diff --git a/aegisub/libffms/src/core/lavfvideo.cpp b/aegisub/libffms/src/core/lavfvideo.cpp index e62a02c7d..07bc40746 100644 --- a/aegisub/libffms/src/core/lavfvideo.cpp +++ b/aegisub/libffms/src/core/lavfvideo.cpp @@ -67,6 +67,12 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index, VP.FPSNumerator = FormatContext->streams[VideoTrack]->time_base.den; VP.RFFDenominator = CodecContext->time_base.num; VP.RFFNumerator = CodecContext->time_base.den; + if (CodecContext->codec_id == CODEC_ID_H264) { + if (VP.RFFNumerator & 1) + VP.RFFDenominator *= 2; + else + VP.RFFNumerator /= 2; + } VP.NumFrames = Frames.size(); VP.TopFieldFirst = DecodeFrame->top_field_first; #ifdef FFMS_HAVE_FFMPEG_COLORSPACE_INFO @@ -115,12 +121,18 @@ void FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime) { if (*AStartTime < 0) *AStartTime = Packet.dts; - if (CodecContext->codec_id == CODEC_ID_MPEG4 && IsNVOP(Packet)) { - av_free_packet(&Packet); - goto Done; - } - avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet); + + if (CodecContext->codec_id == CODEC_ID_MPEG4) { + if (IsPackedFrame(Packet)) { + MPEG4Counter++; + } else if (IsNVOP(Packet) && MPEG4Counter && FrameFinished) { + MPEG4Counter--; + } else if (IsNVOP(Packet) && !MPEG4Counter && !FrameFinished) { + av_free_packet(&Packet); + goto Done; + } + } } av_free_packet(&Packet); @@ -184,6 +196,7 @@ ReSeek: if (HasSeeked) { HasSeeked = false; + MPEG4Counter = 0; // Is the seek destination time known? Does it belong to a frame? if (StartTime < 0 || (CurrentFrame = Frames.FrameFromPTS(StartTime)) < 0) { diff --git a/aegisub/libffms/src/core/matroskavideo.cpp b/aegisub/libffms/src/core/matroskavideo.cpp index d45c95a8f..a815c6fac 100644 --- a/aegisub/libffms/src/core/matroskavideo.cpp +++ b/aegisub/libffms/src/core/matroskavideo.cpp @@ -98,6 +98,12 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track, VP.FPSNumerator = 30; VP.RFFDenominator = CodecContext->time_base.num; VP.RFFNumerator = CodecContext->time_base.den; + if (CodecContext->codec_id == CODEC_ID_H264) { + if (VP.RFFNumerator & 1) + VP.RFFDenominator *= 2; + else + VP.RFFNumerator /= 2; + } VP.NumFrames = Frames.size(); VP.TopFieldFirst = DecodeFrame->top_field_first; #ifdef FFMS_HAVE_FFMPEG_COLORSPACE_INFO @@ -159,11 +165,18 @@ void FFMatroskaVideo::DecodeNextFrame() { PacketNumber++; - if (CodecContext->codec_id == CODEC_ID_MPEG4 && IsNVOP(Packet)) - goto Done; - avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet); + if (CodecContext->codec_id == CODEC_ID_MPEG4) { + if (IsPackedFrame(Packet)) { + MPEG4Counter++; + } else if (IsNVOP(Packet) && MPEG4Counter && FrameFinished) { + MPEG4Counter--; + } else if (IsNVOP(Packet) && !MPEG4Counter && !FrameFinished) { + goto Done; + } + } + if (FrameFinished) goto Done; } @@ -190,6 +203,7 @@ FFMS_Frame *FFMatroskaVideo::GetFrame(int n) { int ClosestKF = Frames.FindClosestVideoKeyFrame(n); if (CurrentFrame > n || ClosestKF > CurrentFrame + 10) { + MPEG4Counter = 0; PacketNumber = ClosestKF; CurrentFrame = ClosestKF; avcodec_flush_buffers(CodecContext); diff --git a/aegisub/libffms/src/core/utils.cpp b/aegisub/libffms/src/core/utils.cpp index 061765bdb..9c0c31a87 100644 --- a/aegisub/libffms/src/core/utils.cpp +++ b/aegisub/libffms/src/core/utils.cpp @@ -202,8 +202,17 @@ void InitNullPacket(AVPacket &pkt) { pkt.size = 0; } +bool IsPackedFrame(AVPacket &pkt) { + for (int i = 0; i < pkt.size - 5; i++) + if (pkt.data[i] == 0x00 && pkt.data[i + 1] == 0x00 && pkt.data[i + 2] == 0x01 && pkt.data[i + 3] == 0xB6 && (pkt.data[i + 4] & 0x40)) + for (i = i + 5; i < pkt.size - 5; i++) + if (pkt.data[i] == 0x00 && pkt.data[i + 1] == 0x00 && pkt.data[i + 2] == 0x01 && pkt.data[i + 3] == 0xB6 && (pkt.data[i + 4] & 0xC0) == 0x80) + return true; + return false; +} + bool IsNVOP(AVPacket &pkt) { - const uint8_t MPEG4NVOP[] = { 0x00, 0x00, 0x01, 0xB6 }; + static const uint8_t MPEG4NVOP[] = { 0x00, 0x00, 0x01, 0xB6 }; return (pkt.size >= 4 && pkt.size <= 8) && !memcmp(pkt.data, MPEG4NVOP, 4); } diff --git a/aegisub/libffms/src/core/utils.h b/aegisub/libffms/src/core/utils.h index 5145c90a9..0e377fe06 100644 --- a/aegisub/libffms/src/core/utils.h +++ b/aegisub/libffms/src/core/utils.h @@ -142,6 +142,7 @@ FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT); void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context); bool AudioFMTIsFloat(SampleFormat FMT); void InitNullPacket(AVPacket &pkt); +bool IsPackedFrame(AVPacket &pkt); bool IsNVOP(AVPacket &pkt); void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames); #ifdef HAALISOURCE diff --git a/aegisub/libffms/src/core/videosource.cpp b/aegisub/libffms/src/core/videosource.cpp index cf03d76cd..45e1230b1 100644 --- a/aegisub/libffms/src/core/videosource.cpp +++ b/aegisub/libffms/src/core/videosource.cpp @@ -162,6 +162,7 @@ FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, in SWS = NULL; LastFrameNum = 0; CurrentFrame = 1; + MPEG4Counter = 0; CodecContext = NULL; LastFrameHeight = -1; LastFrameWidth = -1; diff --git a/aegisub/libffms/src/core/videosource.h b/aegisub/libffms/src/core/videosource.h index 70d25a745..bf09a919a 100644 --- a/aegisub/libffms/src/core/videosource.h +++ b/aegisub/libffms/src/core/videosource.h @@ -73,6 +73,7 @@ protected: FFMS_Track Frames; int VideoTrack; int CurrentFrame; + int MPEG4Counter; AVCodecContext *CodecContext; FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track);