mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
* 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
139 lines
4.0 KiB
C++
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);
|
|
}
|
|
|
|
}
|
|
}
|
|
|