Files
Goober5000 e90416e199 Several coverity fixes (#6232)
* coverity - using `std::move` instead of copying, and change some parameters from value to reference

* coverity - fix or mark potential copy-paste mistakes

* coverity - various checking of parameter and return values

* coverity - miscellaneous fixes, and change some 'intentional' markers to 'false positive' markers
2024-07-06 12:51:31 +02:00

139 lines
4.0 KiB
C++

//
//
#include "SubtitleDecoder.h"
namespace {
double getFrameTime(int64_t pts, AVRational time_base) {
return pts * av_q2d(time_base);
}
}
namespace cutscene {
namespace ffmpeg {
SubtitleDecoder::SubtitleDecoder(cutscene::ffmpeg::DecoderStatus* status) : FFMPEGStreamDecoder(status) {
}
SubtitleDecoder::~SubtitleDecoder() {
}
void SubtitleDecoder::decodePacket(AVPacket* packet) {
int finishedFrame = 0;
AVSubtitle subtitle;
auto result = avcodec_decode_subtitle2(m_status->subtitleCodecCtx, &subtitle, &finishedFrame, packet);
if (result >= 0 && finishedFrame) {
pushSubtitleFrame(packet, &subtitle);
avsubtitle_free(&subtitle);
}
}
void SubtitleDecoder::finishDecoding() {
// Handle those decoders that have a delay
AVPacket nullPacket;
memset(&nullPacket, 0, sizeof(nullPacket));
nullPacket.data = nullptr;
nullPacket.size = 0;
AVSubtitle subtitle;
while (true) {
int finishedFrame = 1;
auto err = avcodec_decode_subtitle2(m_status->subtitleCodecCtx, &subtitle, &finishedFrame, &nullPacket);
if (err < 0 || !finishedFrame) {
break;
}
pushSubtitleFrame(&nullPacket, &subtitle);
avsubtitle_free(&subtitle);
}
}
void SubtitleDecoder::pushSubtitleFrame(AVPacket* packet, AVSubtitle* subtitle) {
if (subtitle->format != 1) {
// Non-text subtitles are not supported yet.
mprintf(("FFmpeg: Detected a non-text subtitle! This is not supported yet!\n"));
return;
}
if (subtitle->num_rects < 1) {
return;
}
int64_t pts = subtitle->pts;
if (pts == AV_NOPTS_VALUE) {
pts = packet->pts;
}
if (pts == AV_NOPTS_VALUE) {
// Still no valid timestamp...
return;
}
auto packet_time = getFrameTime(pts, m_status->subtitleStream->time_base);
auto start_time = packet_time + (subtitle->start_display_time / 1000.0);
auto end_time = packet_time + (subtitle->end_display_time / 1000.0);
if (subtitle->end_display_time == 0 && m_status->subtitleStream->time_base.num != 0) {
end_time = packet_time + getFrameTime(packet->duration, m_status->subtitleStream->time_base);
}
SCP_string processed_text;
// For now we only use the first subtitle rectangle
auto subtitle_rect = subtitle->rects[0];
if (subtitle_rect->type == SUBTITLE_BITMAP) {
// Same as above, non-text subtitles are not supported yet.
mprintf(("FFmpeg: Detected a non-text subtitle! This is not supported yet!\n"));
return;
} else if (subtitle_rect->type == SUBTITLE_TEXT) {
// Subtitle does not need to be processed any further
processed_text = subtitle_rect->text;
} else if (subtitle_rect->type == SUBTITLE_ASS) {
SCP_string ass_text = subtitle_rect->ass;
// We are not interested in any of the other information contained in the ASS line but we still need to figure
// out where our text starts
auto comma_pos = ass_text.find(',');
for (auto i = 0; i < 8 && comma_pos != SCP_string::npos; ++i) {
comma_pos = ass_text.find(',', comma_pos + 1);
}
Assertion(comma_pos != SCP_string::npos, "Received an ill-formed ASS line from FFmpeg! Text was '%s'.", subtitle_rect->ass);
// This + 1 is safe since comma_pos points to a valid character so the next character is at worst the end of the string
processed_text = ass_text.substr(comma_pos + 1);
// ASS has a special sequence for new lines that needs to be converted to an actual new line
auto newline_pos = processed_text.find("\\N");
while(newline_pos != SCP_string::npos) {
processed_text.replace(newline_pos, 2, "\n");
newline_pos = processed_text.find("\\N");
}
} else {
mprintf(("FFmpeg: Detected unknown subtitle name in movie!\n"));
return;
}
// Remove \r from string. Some subtitles use \r\n for line breaks and we only support \n
auto ret_pos = processed_text.find('\r');
while(ret_pos != SCP_string::npos) {
processed_text.erase(ret_pos, 1);
ret_pos = processed_text.find('\r');
}
SubtitleFramePtr frame(new SubtitleFrame());
frame->text = std::move(processed_text);
frame->displayStartTime = start_time;
frame->displayEndTime = end_time;
pushFrame(std::move(frame));
}
void SubtitleDecoder::flushBuffers() {
avcodec_flush_buffers(m_status->subtitleCodecCtx);
}
}
}