From ea6dac551e1afd521c14700e8ac398e2f6d4885c Mon Sep 17 00:00:00 2001 From: Karl Blomster Date: Sat, 12 Jun 2010 14:51:33 +0000 Subject: [PATCH] Update ffms2 to r312. Fixes a bunch of rather critical bugs. Originally committed to SVN as r4487. --- aegisub/libffms/include/ffms.h | 15 +- aegisub/libffms/include/ffmscompat.h | 11 +- aegisub/libffms/src/core/audiosource.cpp | 2 +- aegisub/libffms/src/core/coparser.h | 121 -------------- aegisub/libffms/src/core/ffms.cpp | 13 +- aegisub/libffms/src/core/guids.h | 143 ---------------- aegisub/libffms/src/core/indexing.cpp | 141 ++++++++++++++-- aegisub/libffms/src/core/indexing.h | 3 +- aegisub/libffms/src/core/lavfaudio.cpp | 6 +- aegisub/libffms/src/core/lavfindexer.cpp | 34 +++- aegisub/libffms/src/core/lavfvideo.cpp | 61 +++++-- aegisub/libffms/src/core/matroskaparser.c | 33 ++-- aegisub/libffms/src/core/matroskavideo.cpp | 40 +++-- aegisub/libffms/src/core/utils.cpp | 185 +++++++++++++++++---- aegisub/libffms/src/core/utils.h | 7 +- aegisub/libffms/src/core/videosource.cpp | 9 +- aegisub/libffms/src/core/videosource.h | 3 +- 17 files changed, 445 insertions(+), 382 deletions(-) delete mode 100644 aegisub/libffms/src/core/coparser.h delete mode 100644 aegisub/libffms/src/core/guids.h diff --git a/aegisub/libffms/include/ffms.h b/aegisub/libffms/include/ffms.h index 0168497e4..deed40bfb 100644 --- a/aegisub/libffms/include/ffms.h +++ b/aegisub/libffms/include/ffms.h @@ -22,7 +22,7 @@ #define FFMS_H // Version format: major - minor - micro - bump -#define FFMS_VERSION ((2 << 24) | (13 << 16)| (0 << 8) | 1) +#define FFMS_VERSION ((2 << 24) | (13 << 16) | (1 << 8) | 0) #include @@ -36,10 +36,14 @@ #ifdef _WIN32 # define FFMS_CC __stdcall -# ifdef FFMS_EXPORTS -# define FFMS_API(ret) EXTERN_C __declspec(dllexport) ret FFMS_CC +# ifdef _MSC_VER +# ifdef FFMS_EXPORTS +# define FFMS_API(ret) EXTERN_C __declspec(dllexport) ret FFMS_CC +# else +# define FFMS_API(ret) EXTERN_C __declspec(dllimport) ret FFMS_CC +# endif # else -# define FFMS_API(ret) EXTERN_C __declspec(dllimport) ret FFMS_CC +# define FFMS_API(ret) EXTERN_C ret FFMS_CC # endif #else # define FFMS_CC @@ -253,13 +257,14 @@ FFMS_API(void) FFMS_ResetOutputFormatV(FFMS_VideoSource *V); FFMS_API(int) FFMS_SetPP(FFMS_VideoSource *V, const char *PP, FFMS_ErrorInfo *ErrorInfo); FFMS_API(void) FFMS_ResetPP(FFMS_VideoSource *V); FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index); +FFMS_API(int) FFMS_GetSourceType(FFMS_Index *Index); FFMS_API(int) FFMS_GetFirstTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo); FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo); FFMS_API(int) FFMS_GetNumTracks(FFMS_Index *Index); FFMS_API(int) FFMS_GetNumTracksI(FFMS_Indexer *Indexer); FFMS_API(int) FFMS_GetTrackType(FFMS_Track *T); FFMS_API(int) FFMS_GetTrackTypeI(FFMS_Indexer *Indexer, int Track); -FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track); +FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track); FFMS_API(int) FFMS_GetNumFrames(FFMS_Track *T); FFMS_API(const FFMS_FrameInfo *) FFMS_GetFrameInfo(FFMS_Track *T, int Frame); FFMS_API(FFMS_Track *) FFMS_GetTrackFromIndex(FFMS_Index *Index, int Track); diff --git a/aegisub/libffms/include/ffmscompat.h b/aegisub/libffms/include/ffmscompat.h index ba3c2625f..0d78da0a2 100644 --- a/aegisub/libffms/include/ffmscompat.h +++ b/aegisub/libffms/include/ffmscompat.h @@ -51,10 +51,17 @@ # warning "Your FFmpeg is too old to support reporting colorspace and luma range information. The corresponding fields of FFMS_VideoProperties will be set to 0. Please update FFmpeg to get rid of this warning." # endif # endif +# if (LIBAVCODEC_VERSION_INT) < (AV_VERSION_INT(52,30,2)) +# define AV_PKT_FLAG_KEY PKT_FLAG_KEY +# endif #endif -#ifndef AV_PKT_FLAG_KEY -# define AV_PKT_FLAG_KEY PKT_FLAG_KEY +#ifdef LIBSWSCALE_VERSION_INT +# if (LIBSWSCALE_VERSION_INT) < (AV_VERSION_INT(0,8,0)) +# define FFMS_SWS_CONST_PARAM +# else +# define FFMS_SWS_CONST_PARAM const +# endif #endif diff --git a/aegisub/libffms/src/core/audiosource.cpp b/aegisub/libffms/src/core/audiosource.cpp index 169329216..8ac3c348f 100644 --- a/aegisub/libffms/src/core/audiosource.cpp +++ b/aegisub/libffms/src/core/audiosource.cpp @@ -104,7 +104,7 @@ FFMS_AudioSource::FFMS_AudioSource(const char *SourceFile, FFMS_Index *Index, in throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Not an audio track"); - if (Index[Track].size() == 0) + if (Index->at(Track).size() == 0) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Audio track contains no audio frames"); diff --git a/aegisub/libffms/src/core/coparser.h b/aegisub/libffms/src/core/coparser.h deleted file mode 100644 index 913af8f2c..000000000 --- a/aegisub/libffms/src/core/coparser.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2004-2008 Mike Matsnev. All Rights Reserved. - * - * $Id: CoParser.h,v 1.21 2008/03/29 15:41:28 mike Exp $ - * - */ - -#ifndef COPARSER_H -#define COPARSER_H - -// Generic multimedia container parsers support - -// random access stream, this will be provided by -// IMMContainer's client -interface __declspec(uuid("8E192E9F-E536-4027-8D46-664CC7A102C5")) IMMStream; -interface IMMStream : public IUnknown { - // read count bytes starting at position - STDMETHOD(Read)( unsigned long long position, - void *buffer, - unsigned int *count) = 0; - - // scan the file starting at position for the signature - // signature can't have zero bytes - STDMETHOD(Scan)( unsigned long long *position, - unsigned int signature) = 0; -}; - -interface __declspec(uuid("A237C873-C6AD-422E-90DB-7CB4627DCFD9")) IMMStreamOpen; -interface IMMStreamOpen : public IUnknown { - STDMETHOD(Open)(LPCWSTR name) = 0; -}; - -interface __declspec(uuid("D8FF7213-6E09-4256-A2E5-5872C798B128")) IMMFrame; -interface IMMFrame : public IMediaSample2 { - // track number must be the same as returned by - // IMMContainer->EnumTracks() iterator - STDMETHOD_(unsigned, GetTrack)() = 0; - STDMETHOD(SetTrack)(unsigned) = 0; - - STDMETHOD_(unsigned, GetPre)() = 0; - STDMETHOD(SetPre)(unsigned) = 0; -}; - -interface __declspec(uuid("B8324E2A-21A9-46A1-8922-70C55D06311A")) IMMErrorInfo; -interface IMMErrorInfo : public IUnknown { - STDMETHOD(LogError)(BSTR message) = 0; // message is owned by the caller - STDMETHOD(LogWarning)(BSTR message) = 0; -}; - -interface __declspec(uuid("C7120EDB-528C-4ebe-BB53-DA8E70E618EE")) IMemAlloc; -interface IMemAlloc : public IUnknown { - STDMETHOD(GetBuffer)(HANDLE hAbortEvt, DWORD size, IMMFrame **pS) = 0; -}; - -// container itself should support IPropertyBag and return -// at least Duration[UI8] (in ns) property -// if a containter supports multiple segments in the same -// physical file it should return SegmentTop[UI8] property -// that return the offset of the first byte after this -// segment's end -interface __declspec(uuid("A369001B-F292-45f7-A942-84F9C8C0718A")) IMMContainer; -interface IMMContainer : public IUnknown { - STDMETHOD(Open)( IMMStream *stream, - unsigned long long position, - IMMErrorInfo *einfo, - IMemAlloc *alloc) = 0; - STDMETHOD(GetProgress)(unsigned long long *cur,unsigned long long *max) = 0; - STDMETHOD(AbortOpen)() = 0; - - // pu->Next() returns objects supporting IPropertyBag interface - STDMETHOD(EnumTracks)(IEnumUnknown **pu) = 0; - - // pu->Next() returns objects supporting IProperyBag and IEnumUnknown interfaces - STDMETHOD(EnumEditions)(IEnumUnknown **pu) = 0; - - // pu->Next() returns objects supporting IProperyBag and IMMStream interfaces - STDMETHOD(EnumAttachments)(IEnumUnknown **pu) = 0; - - // S_FALSE is end of stream, S_OK next valid frame returned, E_ABORT wait aborted - STDMETHOD(ReadFrame)(HANDLE hAbortEvt, IMMFrame **frame) = 0; - - // seeking - STDMETHOD(Seek)(unsigned long long timecode,unsigned flags) = 0; -}; - -/* FIXME: duplicated in matroska parser -enum { // Track type - TT_VIDEO = 1, - TT_AUDIO = 2, - TT_SUBS = 17, - - TT_INTERLEAVED = 0x10001, -}; -*/ - -enum { // Seek flags - MMSF_PREV_KF = 1, - MMSF_NEXT_KF = 2 -}; - -/* Track properties [IPropertyBag from EnumTracks->Next()] - Name Type Optional - DefaultDuration UI8 Yes in ns - Video.Interlaced BOOL Yes - Video.DisplayWidth UI4 Yes - Video.DisplayHeight UI4 Yes - Video.PixelWidth UI4 No - Video.PixelHeight UI4 No - CodecID BSTR No - Type UI4 No TT_* enumeration - CodecPrivate ARRAY|UI1 Yes - Audio.Channels UI4 No - Audio.BitDepth UI4 Yes - Audio.SamplingFreq UI4 No - Audio.OutputSamplingFreq UI4 Yes - Language BSTR Yes - Name BSTR Yes - FOURCC UI4 Yes -*/ - -#endif diff --git a/aegisub/libffms/src/core/ffms.cpp b/aegisub/libffms/src/core/ffms.cpp index ab2e27d53..c3c640721 100644 --- a/aegisub/libffms/src/core/ffms.cpp +++ b/aegisub/libffms/src/core/ffms.cpp @@ -25,7 +25,9 @@ #include "audiosource.h" #include "indexing.h" - +#ifdef FFMS_WIN_DEBUG +# include +#endif static bool FFmpegInited = false; bool HasHaaliMPEG = false; @@ -71,6 +73,9 @@ void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl) FFMS_API(void) FFMS_Init(int CPUFeatures) { if (!FFmpegInited) { av_register_all(); +#if defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS) + ffms_patch_lavf_file_open(); +#endif #ifdef FFMS_WIN_DEBUG av_log_set_callback(av_log_windebug_callback); av_log_set_level(AV_LOG_INFO); @@ -98,6 +103,8 @@ FFMS_API(void) FFMS_SetLogLevel(int Level) { } FFMS_API(FFMS_VideoSource *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode, FFMS_ErrorInfo *ErrorInfo) { + if (Threads < 1) + Threads = 1; try { switch (Index->Decoder) { case FFMS_SOURCE_LAVF: @@ -227,6 +234,10 @@ FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index) { delete Index; } +FFMS_API(int) FFMS_GetSourceType(FFMS_Index *Index) { + return Index->Decoder; +} + FFMS_API(int) FFMS_GetFirstTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo) { ClearErrorInfo(ErrorInfo); for (int i = 0; i < static_cast(Index->size()); i++) diff --git a/aegisub/libffms/src/core/guids.h b/aegisub/libffms/src/core/guids.h deleted file mode 100644 index 5e4e0aa2a..000000000 --- a/aegisub/libffms/src/core/guids.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (c) 2004-2008 Mike Matsnev. All Rights Reserved. - * - * $Id: guids.h,v 1.16 2008/03/29 15:41:30 mike Exp $ - * - */ - -#ifndef GUIDS_H -#define GUIDS_H - -// {941A4793-A705-4312-8DFC-C11CA05F397E} -DEFINE_GUID(CLSID_RealAudioDecoder, -0x941A4793, 0xA705, 0x4312, 0x8D, 0xFC, 0xC1, 0x1C, 0xA0, 0x5F, 0x39, 0x7E); - -// {B8CBBAD8-AA1A-4cea-B95E-730041A55EF0} -DEFINE_GUID(MEDIASUBTYPE_WavpackHybrid, -0xb8cbbad8, 0xaa1a, 0x4cea, 0xb9, 0x5e, 0x73, 0x0, 0x41, 0xa5, 0x5e, 0xf0); - -// {1541C5C0-CDDF-477d-BC0A-86F8AE7F8354} -DEFINE_GUID(MEDIASUBTYPE_FLAC_FRAMED, -0x1541c5c0, 0xcddf, 0x477d, 0xbc, 0xa, 0x86, 0xf8, 0xae, 0x7f, 0x83, 0x54); - -// {8D2FD10B-5841-4a6b-8905-588FEC1ADED9} -DEFINE_GUID(MEDIASUBTYPE_Vorbis2, -0x8d2fd10b, 0x5841, 0x4a6b, 0x89, 0x5, 0x58, 0x8f, 0xec, 0x1a, 0xde, 0xd9); - -// {B36E107F-A938-4387-93C7-55E966757473} -DEFINE_GUID(FORMAT_VorbisFormat2, -0xb36e107f, 0xa938, 0x4387, 0x93, 0xc7, 0x55, 0xe9, 0x66, 0x75, 0x74, 0x73); - -// {E487EB08-6B26-4be9-9DD3-993434D313FD} -DEFINE_GUID(MEDIATYPE_Subtitle, -0xe487eb08, 0x6b26, 0x4be9, 0x9d, 0xd3, 0x99, 0x34, 0x34, 0xd3, 0x13, 0xfd); - -// {87C0B230-03A8-4fdf-8010-B27A5848200D} -DEFINE_GUID(MEDIASUBTYPE_UTF8, -0x87c0b230, 0x3a8, 0x4fdf, 0x80, 0x10, 0xb2, 0x7a, 0x58, 0x48, 0x20, 0xd); - -// {3020560F-255A-4ddc-806E-6C5CC6DCD70A} -DEFINE_GUID(MEDIASUBTYPE_SSA, -0x3020560f, 0x255a, 0x4ddc, 0x80, 0x6e, 0x6c, 0x5c, 0xc6, 0xdc, 0xd7, 0xa); - -// {326444F7-686F-47ff-A4B2-C8C96307B4C2} -DEFINE_GUID(MEDIASUBTYPE_ASS, -0x326444f7, 0x686f, 0x47ff, 0xa4, 0xb2, 0xc8, 0xc9, 0x63, 0x7, 0xb4, 0xc2); - -// {370689E7-B226-4f67-978D-F10BC1A9C6AE} -DEFINE_GUID(MEDIASUBTYPE_ASS2, -0x370689e7, 0xb226, 0x4f67, 0x97, 0x8d, 0xf1, 0xb, 0xc1, 0xa9, 0xc6, 0xae); - -// {B753B29A-0A96-45be-985F-68351D9CAB90} -DEFINE_GUID(MEDIASUBTYPE_USF, -0xb753b29a, 0xa96, 0x45be, 0x98, 0x5f, 0x68, 0x35, 0x1d, 0x9c, 0xab, 0x90); - -// {F7239E31-9599-4e43-8DD5-FBAF75CF37F1} -DEFINE_GUID(MEDIASUBTYPE_VOBSUB, -0xf7239e31, 0x9599, 0x4e43, 0x8d, 0xd5, 0xfb, 0xaf, 0x75, 0xcf, 0x37, 0xf1); - -// {0B10E53F-ABF9-4581-BE9C-2C9A5EC6F2E0} -DEFINE_GUID(MEDIASUBTYPE_VOBSUB2, -0xb10e53f, 0xabf9, 0x4581, 0xbe, 0x9c, 0x2c, 0x9a, 0x5e, 0xc6, 0xf2, 0xe0); - -// {5965E924-63F9-4a64-B71E-F75188FD6384} -DEFINE_GUID(MEDIASUBTYPE_DXRSub, -0x5965e924, 0x63f9, 0x4a64, 0xb7, 0x1e, 0xf7, 0x51, 0x88, 0xfd, 0x63, 0x84); - -// {A33D2F7D-96BC-4337-B23B-A8B9FBC295E9} -DEFINE_GUID(FORMAT_SubtitleInfo, -0xa33d2f7d, 0x96bc, 0x4337, 0xb2, 0x3b, 0xa8, 0xb9, 0xfb, 0xc2, 0x95, 0xe9); - -DEFINE_GUID(MEDIASUBTYPE_AC3, -0x00002000, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); - -DEFINE_GUID(MEDIASUBTYPE_EAC3, -0x0000EAC3, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); - -DEFINE_GUID(MEDIASUBTYPE_DTS, -0x00002001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); - -// {B855F837-194F-4d9a-9358-E31ED6B49D03} -DEFINE_GUID(MEDIASUBTYPE_VC1, -0xb855f837, 0x194f, 0x4d9a, 0x93, 0x58, 0xe3, 0x1e, 0xd6, 0xb4, 0x9d, 0x3); - -// {93A22E7A-5091-45ef-BA61-6DA26156A5D0} -DEFINE_GUID(CLSID_DirectVobSubFilter, -0x93a22e7a, 0x5091, 0x45ef, 0xba, 0x61, 0x6d, 0xa2, 0x61, 0x56, 0xa5, 0xd0); - -// {9852A670-F845-491b-9BE6-EBD841B8A613} -DEFINE_GUID(CLSID_DirectVobSubFilter2, -0x9852a670, 0xf845, 0x491b, 0x9b, 0xe6, 0xeb, 0xd8, 0x41, 0xb8, 0xa6, 0x13); - -// {F6D90F11-9C73-11D3-B32E-00C04F990BB4} -DEFINE_GUID(CLSID_MSXML2, -0xF6D90F11, 0x9C73, 0x11D3, 0xB3, 0x2E, 0x00, 0xC0, 0x4F, 0x99, 0x0B, 0xB4); - -// {9F062738-CD84-4F54-A3C4-BD5EB44F416B} -DEFINE_GUID(CLSID_SonicAudioDec, -0x9F062738, 0xCD84, 0x4F54, 0xA3, 0xC4, 0xBD, 0x5E, 0xB4, 0x4F, 0x41, 0x6B); - -// {53D9DE0B-FC61-4650-9773-74D13CC7E582} -DEFINE_GUID(CLSID_DiskFile, -0x53d9de0b, 0xfc61, 0x4650, 0x97, 0x73, 0x74, 0xd1, 0x3c, 0xc7, 0xe5, 0x82); - -// {BD4FB4BE-809D-487b-ADD6-F7D164247E52} -DEFINE_GUID(CLSID_HTTPStream, -0xbd4fb4be, 0x809d, 0x487b, 0xad, 0xd6, 0xf7, 0xd1, 0x64, 0x24, 0x7e, 0x52); - -// {90C7D10E-CE9A-479b-A238-1A0F2396DE43} -DEFINE_GUID(CLSID_MemAlloc, -0x90c7d10e, 0xce9a, 0x479b, 0xa2, 0x38, 0x1a, 0xf, 0x23, 0x96, 0xde, 0x43); - -DEFINE_GUID(MEDIASUBTYPE_WVC1, -0x31435657, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); - -DEFINE_GUID(MEDIASUBTYPE_WMV3, -0x33564d57, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); - -// FIXME: move somewhere else? -DEFINE_GUID(HAALI_MPEG_PARSER, -0xB841F346, 0x4835, 0x4de8, 0xAA, 0x5E, 0x2E, 0x7C, 0xD2, 0xD4, 0xC4, 0x35); - -DEFINE_GUID(HAALI_OGG_PARSER, -0xDB43B405, 0x43AA, 0x4F01, 0x82, 0xD8, 0xD8, 0x4D, 0x47, 0xE6, 0x01, 0x9C); - -//DB43B405-43AA-4F01-82D8-D84D47E6019C - -typedef struct tagVORBISFORMAT2 -{ - DWORD Channels; - DWORD SamplesPerSec; - DWORD BitsPerSample; - DWORD HeaderSize[3]; // 0: Identification, 1: Comment, 2: Setup -} VORBISFORMAT2, *PVORBISFORMAT2, FAR *LPVORBISFORMAT2; - -#pragma pack(push, 1) -typedef struct { - DWORD dwOffset; - CHAR IsoLang[4]; // three letter lang code + terminating zero - WCHAR TrackName[256]; // 256 bytes ought to be enough for everyone :) -} SUBTITLEINFO; -#pragma pack(pop) - -#endif diff --git a/aegisub/libffms/src/core/indexing.cpp b/aegisub/libffms/src/core/indexing.cpp index dc823ebc3..373f17f3a 100644 --- a/aegisub/libffms/src/core/indexing.cpp +++ b/aegisub/libffms/src/core/indexing.cpp @@ -25,6 +25,7 @@ extern "C" { #include +#include } #define INDEXID 0x53920873 @@ -51,6 +52,7 @@ struct TrackHeader { uint32_t Frames; int64_t Num; int64_t Den; + uint32_t UseDTS; }; SharedVideoContext::SharedVideoContext(bool FreeCodecContext) { @@ -155,8 +157,11 @@ int FFMS_Track::ClosestFrameFromPTS(int64_t PTS) { int FFMS_Track::FindClosestVideoKeyFrame(int Frame) { Frame = FFMIN(FFMAX(Frame, 0), static_cast(size()) - 1); for (int i = Frame; i > 0; i--) - if (at(i).KeyFrame) - return i; + if (at(i).KeyFrame) { + for (int j = i; j >= 0; j--) + if (at(at(j).OriginalPos).KeyFrame) + return j; + } return 0; } @@ -174,12 +179,14 @@ FFMS_Track::FFMS_Track() { this->TT = FFMS_TYPE_UNKNOWN; this->TB.Num = 0; this->TB.Den = 0; + this->UseDTS = false; } -FFMS_Track::FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT) { +FFMS_Track::FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT, bool UseDTS) { this->TT = TT; this->TB.Num = Num; this->TB.Den = Den; + this->UseDTS = UseDTS; } void FFMS_Index::CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20]) { @@ -264,6 +271,31 @@ bool FFMS_Index::CompareFileSignature(const char *Filename) { return (CFilesize == Filesize && !memcmp(CDigest, Digest, sizeof(Digest))); } +#define CHUNK 65536 + +static unsigned int z_def(ffms_fstream *IndexStream, z_stream *stream, void *in, size_t in_sz, int finish) { + unsigned int total = 0, have; + int ret; + char out[CHUNK]; + + if (!finish && (in_sz == 0 || in == NULL)) return 0; + + stream->next_in = (Bytef*) in; + stream->avail_in = in_sz; + do { + do { + stream->avail_out = CHUNK; + stream->next_out = (Bytef*) out; + ret = deflate(stream, finish ? Z_FINISH : Z_NO_FLUSH); + have = CHUNK - stream->avail_out; + if (have) IndexStream->write(out, have); + total += have; + } while (stream->avail_out == 0); + } while (finish && ret != Z_STREAM_END); + if (finish) deflateEnd(stream); + return total; +} + void FFMS_Index::WriteIndex(const char *IndexFile) { ffms_fstream IndexStream(IndexFile, std::ios::out | std::ios::binary | std::ios::trunc); @@ -273,6 +305,12 @@ void FFMS_Index::WriteIndex(const char *IndexFile) { throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); } + z_stream stream; + memset(&stream, 0, sizeof(z_stream)); + if (deflateInit(&stream, 9) != Z_OK) { + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to initialize zlib"); + } + // Write the index file header IndexHeader IH; IH.Id = INDEXID; @@ -287,18 +325,72 @@ void FFMS_Index::WriteIndex(const char *IndexFile) { IH.FileSize = Filesize; memcpy(IH.FileSignature, Digest, sizeof(Digest)); - IndexStream.write(reinterpret_cast(&IH), sizeof(IH)); + z_def(&IndexStream, &stream, &IH, sizeof(IndexHeader), 0); for (unsigned int i = 0; i < IH.Tracks; i++) { + FFMS_Track &ctrack = at(i); TrackHeader TH; - TH.TT = at(i).TT; - TH.Frames = at(i).size(); - TH.Num = at(i).TB.Num;; - TH.Den = at(i).TB.Den; + TH.TT = ctrack.TT; + TH.Frames = ctrack.size(); + TH.Num = ctrack.TB.Num;; + TH.Den = ctrack.TB.Den; + TH.UseDTS = ctrack.UseDTS; - IndexStream.write(reinterpret_cast(&TH), sizeof(TH)); - IndexStream.write(reinterpret_cast(FFMS_GET_VECTOR_PTR(at(i))), TH.Frames * sizeof(TFrameInfo)); + FFMS_Track temptrack; + temptrack.resize(TH.Frames); + + if (TH.Frames) + temptrack[0] = ctrack[0]; + + for (size_t j = 1; j < ctrack.size(); j++) { + temptrack[j] = ctrack[j]; + temptrack[j].FilePos = ctrack[j].FilePos - ctrack[j - 1].FilePos; + temptrack[j].OriginalPos = ctrack[j].OriginalPos - ctrack[j - 1].OriginalPos; + temptrack[j].PTS = ctrack[j].PTS - ctrack[j - 1].PTS; + temptrack[j].SampleStart = ctrack[j].SampleStart - ctrack[j - 1].SampleStart; + } + + z_def(&IndexStream, &stream, &TH, sizeof(TrackHeader), 0); + if (TH.Frames) + z_def(&IndexStream, &stream, FFMS_GET_VECTOR_PTR(temptrack), TH.Frames * sizeof(TFrameInfo), 0); } + z_def(&IndexStream, &stream, NULL, 0, 1); +} + +static unsigned int z_inf(ffms_fstream *Index, z_stream *stream, void *in, size_t in_sz, void *out, size_t out_sz) { + int ret; + + if (out_sz == 0 || out == 0) return 0; + stream->next_out = (Bytef*) out; + stream->avail_out = out_sz; + + do { + if (stream->avail_in) memmove(in, stream->next_in, stream->avail_in); + Index->read(((char*)in) + stream->avail_in, in_sz - stream->avail_in); + stream->next_in = (Bytef*) in; + stream->avail_in += Index->gcount(); + + ret = inflate(stream, Z_SYNC_FLUSH); + switch (ret) { + case Z_NEED_DICT: + inflateEnd(stream); + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Dictionary error."); + break; + case Z_DATA_ERROR: + inflateEnd(stream); + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Data error."); + break; + case Z_MEM_ERROR: + inflateEnd(stream); + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Memory error."); + break; + case Z_STREAM_END: + inflateEnd(stream); + return out_sz - stream->avail_out; + } + + } while (stream->avail_out); + return out_sz; } void FFMS_Index::ReadIndex(const char *IndexFile) { @@ -309,10 +401,17 @@ void FFMS_Index::ReadIndex(const char *IndexFile) { buf << "Failed to open '" << IndexFile << "' for reading"; throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); } + + z_stream stream; + memset(&stream, 0, sizeof(z_stream)); + unsigned char in[CHUNK]; + if (inflateInit(&stream) != Z_OK) { + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to initialize zlib"); + } // Read the index file header IndexHeader IH; - Index.read(reinterpret_cast(&IH), sizeof(IH)); + z_inf(&Index, &stream, &in, CHUNK, &IH, sizeof(IndexHeader)); if (IH.Id != INDEXID) { std::ostringstream buf; buf << "'" << IndexFile << "' is not a valid index file"; @@ -340,16 +439,24 @@ void FFMS_Index::ReadIndex(const char *IndexFile) { Decoder = IH.Decoder; Filesize = IH.FileSize; memcpy(Digest, IH.FileSignature, sizeof(Digest)); - + try { for (unsigned int i = 0; i < IH.Tracks; i++) { TrackHeader TH; - Index.read(reinterpret_cast(&TH), sizeof(TH)); - push_back(FFMS_Track(TH.Num, TH.Den, static_cast(TH.TT))); + z_inf(&Index, &stream, &in, CHUNK, &TH, sizeof(TrackHeader)); + push_back(FFMS_Track(TH.Num, TH.Den, static_cast(TH.TT), TH.UseDTS != 0)); + FFMS_Track &ctrack = at(i); if (TH.Frames) { - at(i).resize(TH.Frames); - Index.read(reinterpret_cast(FFMS_GET_VECTOR_PTR(at(i))), TH.Frames * sizeof(TFrameInfo)); + ctrack.resize(TH.Frames); + z_inf(&Index, &stream, &in, CHUNK, FFMS_GET_VECTOR_PTR(ctrack), TH.Frames * sizeof(TFrameInfo)); + } + + for (size_t j = 1; j < ctrack.size(); j++) { + ctrack[j].FilePos = ctrack[j].FilePos + ctrack[j - 1].FilePos; + ctrack[j].OriginalPos = ctrack[j].OriginalPos + ctrack[j - 1].OriginalPos; + ctrack[j].PTS = ctrack[j].PTS + ctrack[j - 1].PTS; + ctrack[j].SampleStart = ctrack[j].SampleStart + ctrack[j - 1].SampleStart; } } @@ -361,7 +468,7 @@ void FFMS_Index::ReadIndex(const char *IndexFile) { } FFMS_Index::FFMS_Index() { - // this comment documents nothing + } FFMS_Index::FFMS_Index(int64_t Filesize, uint8_t Digest[20]) { diff --git a/aegisub/libffms/src/core/indexing.h b/aegisub/libffms/src/core/indexing.h index 52e71c7ed..1fbbeabbd 100644 --- a/aegisub/libffms/src/core/indexing.h +++ b/aegisub/libffms/src/core/indexing.h @@ -84,6 +84,7 @@ class FFMS_Track : public std::vector { public: FFMS_TrackType TT; FFMS_TrackTimeBase TB; + bool UseDTS; int FindClosestVideoKeyFrame(int Frame); int FindClosestAudioKeyFrame(int64_t Sample); @@ -92,7 +93,7 @@ public: void WriteTimecodes(const char *TimecodeFile); FFMS_Track(); - FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT); + FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT, bool UseDTS = false); }; class FFMS_Index : public std::vector { diff --git a/aegisub/libffms/src/core/lavfaudio.cpp b/aegisub/libffms/src/core/lavfaudio.cpp index 7d4d2551e..0df72cd41 100644 --- a/aegisub/libffms/src/core/lavfaudio.cpp +++ b/aegisub/libffms/src/core/lavfaudio.cpp @@ -152,10 +152,10 @@ void FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) { while (av_read_frame(FormatContext, &Packet) >= 0) { if (Packet.stream_index == AudioTrack) { if (LastPTS < 0) { - LastPTS = Packet.dts; - } else if (LastPTS != Packet.dts) { + LastPTS = Packet.pts; + } else if (LastPTS != Packet.pts) { for (size_t i = 0; i < Frames.size(); i++) - if (Frames[i].PTS == Packet.dts) { + if (Frames[i].PTS == Packet.pts) { // The current match was consumed CurrentAudioBlock = i + 1; break; diff --git a/aegisub/libffms/src/core/lavfindexer.cpp b/aegisub/libffms/src/core/lavfindexer.cpp index fc65376de..b758ae082 100644 --- a/aegisub/libffms/src/core/lavfindexer.cpp +++ b/aegisub/libffms/src/core/lavfindexer.cpp @@ -88,9 +88,13 @@ FFMS_Index *FFLAVFIndexer::DoIndexing() { AVPacket Packet, TempPacket; InitNullPacket(Packet); InitNullPacket(TempPacket); + std::vector LastValidTS; + LastValidTS.resize(FormatContext->nb_streams, ffms_av_nopts_value); + while (av_read_frame(FormatContext, &Packet) >= 0) { // Update progress - if (IC) { + // FormatContext->pb can apparently be NULL when opening images. + if (IC && FormatContext->pb) { if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, ICPrivate)) throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER, "Cancelled by user"); @@ -102,12 +106,24 @@ FFMS_Index *FFLAVFIndexer::DoIndexing() { int OBSize; int RepeatPict = -1; + // Duplicated code + if (!(*TrackIndices)[Packet.stream_index].UseDTS && Packet.pts != ffms_av_nopts_value) + LastValidTS[Packet.stream_index] = Packet.pts; + if (LastValidTS[Packet.stream_index] == ffms_av_nopts_value) + (*TrackIndices)[Packet.stream_index].UseDTS = true; + if ((*TrackIndices)[Packet.stream_index].UseDTS && Packet.dts != ffms_av_nopts_value) + LastValidTS[Packet.stream_index] = Packet.dts; + if (LastValidTS[Packet.stream_index] == ffms_av_nopts_value) + throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_PARSER, + "Invalid initial pts and dts"); + // + if (VideoContexts[Packet.stream_index].Parser) { av_parser_parse2(VideoContexts[Packet.stream_index].Parser, VideoContexts[Packet.stream_index].CodecContext, &OB, &OBSize, Packet.data, Packet.size, Packet.pts, Packet.dts, Packet.pos); RepeatPict = VideoContexts[Packet.stream_index].Parser->repeat_pict; } - (*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::VideoFrameInfo(Packet.dts, RepeatPict, (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0)); + (*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::VideoFrameInfo(LastValidTS[Packet.stream_index], RepeatPict, (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0)); } else if (FormatContext->streams[Packet.stream_index]->codec->codec_type == CODEC_TYPE_AUDIO && (IndexMask & (1 << Packet.stream_index))) { int64_t StartSample = AudioContexts[Packet.stream_index].CurrentSample; AVCodecContext *AudioCodecContext = FormatContext->streams[Packet.stream_index]->codec; @@ -115,6 +131,18 @@ FFMS_Index *FFLAVFIndexer::DoIndexing() { TempPacket.size = Packet.size; TempPacket.flags = Packet.flags; + // Duplicated code + if (!(*TrackIndices)[Packet.stream_index].UseDTS && Packet.pts != ffms_av_nopts_value) + LastValidTS[Packet.stream_index] = Packet.pts; + if (LastValidTS[Packet.stream_index] == ffms_av_nopts_value) + (*TrackIndices)[Packet.stream_index].UseDTS = true; + if ((*TrackIndices)[Packet.stream_index].UseDTS && Packet.dts != ffms_av_nopts_value) + LastValidTS[Packet.stream_index] = Packet.dts; + if (LastValidTS[Packet.stream_index] == ffms_av_nopts_value) + throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_PARSER, + "Invalid initial pts and dts"); + // + while (TempPacket.size > 0) { int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket); @@ -146,7 +174,7 @@ FFMS_Index *FFLAVFIndexer::DoIndexing() { WriteAudio(AudioContexts[Packet.stream_index], TrackIndices.get(), Packet.stream_index, dbsize); } - (*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::AudioFrameInfo(Packet.dts, StartSample, + (*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::AudioFrameInfo(LastValidTS[Packet.stream_index], StartSample, static_cast(AudioContexts[Packet.stream_index].CurrentSample - StartSample), (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0)); } diff --git a/aegisub/libffms/src/core/lavfvideo.cpp b/aegisub/libffms/src/core/lavfvideo.cpp index 07bc40746..0b9d56a4c 100644 --- a/aegisub/libffms/src/core/lavfvideo.cpp +++ b/aegisub/libffms/src/core/lavfvideo.cpp @@ -45,7 +45,8 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index, "Video track is unseekable"); CodecContext = FormatContext->streams[VideoTrack]->codec; - CodecContext->thread_count = Threads; + if (avcodec_thread_init(CodecContext, Threads)) + CodecContext->thread_count = 1; Codec = avcodec_find_decoder(CodecContext->codec_id); if (Codec == NULL) @@ -96,11 +97,20 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index, } // Adjust framerate to match the duration of the first frame - if (Frames.size() >= 2) { - unsigned int PTSDiff = (unsigned int)FFMAX(Frames[1].PTS - Frames[0].PTS, 1); - VP.FPSDenominator *= PTSDiff; + for (size_t i = 1; i < Frames.size(); i++) { + if (Frames[i].PTS != Frames[0].PTS) { + unsigned int PTSDiff = (unsigned int)(Frames[i].PTS - Frames[0].PTS); + VP.FPSDenominator *= PTSDiff; + VP.FPSDenominator /= i; + break; + } } + // attempt to correct framerate to the proper NTSC fraction, if applicable + CorrectNTSCRationalFramerate(&VP.FPSNumerator, &VP.FPSDenominator); + // correct the timebase, if necessary + CorrectTimebase(&VP, &Frames.TB); + // Cannot "output" to PPFrame without doing all other initialization // This is the additional mess required for seekmode=-1 to work in a reasonable way OutputFrame(DecodeFrame); @@ -116,22 +126,31 @@ void FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime) { int FrameFinished = 0; *AStartTime = -1; + if (InitialDecode == -1) { + if (DelayCounter > CodecContext->has_b_frames) { + DelayCounter--; + goto Done; + } else { + InitialDecode = 0; + } + } + while (av_read_frame(FormatContext, &Packet) >= 0) { if (Packet.stream_index == VideoTrack) { - if (*AStartTime < 0) - *AStartTime = Packet.dts; + if (*AStartTime < 0) { + if (Frames.UseDTS) + *AStartTime = Packet.dts; + else + *AStartTime = Packet.pts; + } 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; - } + if (!FrameFinished) + DelayCounter++; + if (DelayCounter > CodecContext->has_b_frames && !InitialDecode) { + av_free_packet(&Packet); + goto Done; } } @@ -152,7 +171,8 @@ void FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime) { goto Error; Error: -Done:; +Done: + if (InitialDecode == 1) InitialDecode = -1; } FFMS_Frame *FFLAVFVideo::GetFrame(int n) { @@ -173,6 +193,8 @@ FFMS_Frame *FFLAVFVideo::GetFrame(int n) { av_seek_frame(FormatContext, VideoTrack, Frames[0].PTS, AVSEEK_FLAG_BACKWARD); avcodec_flush_buffers(CodecContext); CurrentFrame = 0; + DelayCounter = 0; + InitialDecode = 1; } } else { // 10 frames is used as a margin to prevent excessive seeking since the predicted best keyframe isn't always selected by avformat @@ -183,6 +205,8 @@ ReSeek: AVSEEK_FLAG_BACKWARD); avcodec_flush_buffers(CodecContext); HasSeeked = true; + DelayCounter = 0; + InitialDecode = 1; } } } else if (n < CurrentFrame) { @@ -192,11 +216,14 @@ ReSeek: do { int64_t StartTime; + if (CurrentFrame+CodecContext->has_b_frames >= n) + CodecContext->skip_frame = AVDISCARD_DEFAULT; + else + CodecContext->skip_frame = AVDISCARD_NONREF; DecodeNextFrame(&StartTime); 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/matroskaparser.c b/aegisub/libffms/src/core/matroskaparser.c index dcf9fb76c..d556907f7 100644 --- a/aegisub/libffms/src/core/matroskaparser.c +++ b/aegisub/libffms/src/core/matroskaparser.c @@ -27,6 +27,10 @@ * */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + #include #include #include @@ -34,11 +38,19 @@ #include #ifdef _WIN32 +#ifdef _MSC_VER // MS names some functions differently #define alloca _alloca #define inline __inline - +#else /* _MSC_VER */ +// support for building with MinGW on Windows +#include +#endif /* _MSC_VER */ #include +#endif /* _WIN32 */ + +#ifdef HAVE_ALLOCA_H +#include #endif #ifndef EVCBUG @@ -286,7 +298,8 @@ static void myvsnprintf_int(char **pdest,char *de,int width,int zero, static void myvsnprintf(char *dest,unsigned dsize,const char *fmt,va_list ap) { // s,d,x,u,ll char *de = dest + dsize - 1; - int state = 0, width, zero, neg, ll; + int state, width, zero, neg, ll; + state = width = zero = neg = ll = 0; if (dsize <= 1) { if (dsize > 0) @@ -904,16 +917,6 @@ static void readLangCC(MatroskaFile *mf, ulonglong len, char lcc[4]) { #define STRGETA(f,v,len) STRGETF(f,v,len,myalloca) #define STRGETM(f,v,len) STRGETF(f,v,len,f->cache->memalloc) -static int IsWritingApp(MatroskaFile *mf,const char *str) { - const char *cp = mf->Seg.WritingApp; - if (!cp) - return 0; - - while (*str && *str++==*cp++) ; - - return !*str; -} - static void parseEBML(MatroskaFile *mf,ulonglong toplen) { ulonglong v; char buf[32]; @@ -1212,10 +1215,6 @@ static void parseAudioInfo(MatroskaFile *mf,ulonglong toplen,struct TrackInfo *t break; case 0x6264: // BitDepth v = readUInt(mf,(unsigned)len); -#if 0 - if ((v<1 || v>255) && !IsWritingApp(mf,"AVI-Mux GUI")) - errorjmp(mf,"Invalid BitDepth: %d",(int)v); -#endif ti->AV.Audio.BitDepth = (unsigned char)v; break; ENDFOR(mf); @@ -1245,7 +1244,7 @@ static void parseTrackEntry(MatroskaFile *mf,ulonglong toplen) { ulonglong v; char *cp = NULL, *cs = NULL; size_t cplen = 0, cslen = 0, cpadd = 0; - unsigned CompScope, num_comp = 0; + unsigned CompScope = 0, num_comp = 0; if (mf->nTracks >= MAX_TRACKS) errorjmp(mf,"Too many tracks."); diff --git a/aegisub/libffms/src/core/matroskavideo.cpp b/aegisub/libffms/src/core/matroskavideo.cpp index a815c6fac..32b9d181f 100644 --- a/aegisub/libffms/src/core/matroskavideo.cpp +++ b/aegisub/libffms/src/core/matroskavideo.cpp @@ -76,7 +76,8 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track, } CodecContext = avcodec_alloc_context(); - CodecContext->thread_count = Threads; + if (avcodec_thread_init(CodecContext, Threads)) + CodecContext->thread_count = 1; Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate)); if (Codec == NULL) @@ -127,6 +128,11 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track, VP.FPSNumerator = 1000000; } + // attempt to correct framerate to the proper NTSC fraction, if applicable + CorrectNTSCRationalFramerate(&VP.FPSNumerator, &VP.FPSDenominator); + // correct the timebase, if necessary + CorrectTimebase(&VP, &Frames.TB); + // Output the already decoded frame so it isn't wasted OutputFrame(DecodeFrame); @@ -147,6 +153,15 @@ void FFMatroskaVideo::DecodeNextFrame() { InitNullPacket(Packet); unsigned int FrameSize; + + if (InitialDecode == -1) { + if (DelayCounter > CodecContext->has_b_frames) { + DelayCounter--; + goto Done; + } else { + InitialDecode = 0; + } + } while (PacketNumber < Frames.size()) { // The additional indirection is because the packets are stored in @@ -167,15 +182,10 @@ void FFMatroskaVideo::DecodeNextFrame() { 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) + DelayCounter++; + if (DelayCounter > CodecContext->has_b_frames && !InitialDecode) + goto Done; if (FrameFinished) goto Done; @@ -192,7 +202,8 @@ void FFMatroskaVideo::DecodeNextFrame() { goto Error; Error: -Done:; +Done: + if (InitialDecode == 1) InitialDecode = -1; } FFMS_Frame *FFMatroskaVideo::GetFrame(int n) { @@ -203,13 +214,18 @@ FFMS_Frame *FFMatroskaVideo::GetFrame(int n) { int ClosestKF = Frames.FindClosestVideoKeyFrame(n); if (CurrentFrame > n || ClosestKF > CurrentFrame + 10) { - MPEG4Counter = 0; + DelayCounter = 0; + InitialDecode = 1; PacketNumber = ClosestKF; CurrentFrame = ClosestKF; avcodec_flush_buffers(CodecContext); } do { + if (CurrentFrame+CodecContext->has_b_frames >= n) + CodecContext->skip_frame = AVDISCARD_DEFAULT; + else + CodecContext->skip_frame = AVDISCARD_NONREF; DecodeNextFrame(); CurrentFrame++; } while (CurrentFrame <= n); diff --git a/aegisub/libffms/src/core/utils.cpp b/aegisub/libffms/src/core/utils.cpp index 9c0c31a87..20ecbbf8b 100644 --- a/aegisub/libffms/src/core/utils.cpp +++ b/aegisub/libffms/src/core/utils.cpp @@ -20,13 +20,19 @@ #include #include - #include "utils.h" #include "indexing.h" -#ifdef FFMS_USE_UTF8_PATHS +#ifdef _WIN32 # define WIN32_LEAN_AND_MEAN # include +#ifdef FFMS_USE_UTF8_PATHS +# include +# include +extern "C" { +# include "libavutil/avstring.h" +} +#endif #endif @@ -202,20 +208,6 @@ 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) { - static const uint8_t MPEG4NVOP[] = { 0x00, 0x00, 0x01, 0xB6 }; - return (pkt.size >= 4 && pkt.size <= 8) && !memcmp(pkt.data, MPEG4NVOP, 4); -} - void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames) { AP.SampleFormat = static_cast(CTX->sample_fmt); AP.BitsPerSample = av_get_bits_per_sample_format(CTX->sample_fmt); @@ -281,6 +273,8 @@ CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC case 32: CID = CODEC_ID_PCM_S32BE; break; } break; + default: + break; } return CID; @@ -367,24 +361,52 @@ void InitializeCodecContextFromHaaliInfo(CComQIPtr pBag, AVCodecCo #endif + +// All this filename chikanery that follows is supposed to make sure both local +// codepage (used by avisynth etc) and UTF8 (potentially used by API users) strings +// work correctly on Win32. +// It's a really ugly hack, and I blame Microsoft for it. +#ifdef _WIN32 +static wchar_t *dup_char_to_wchar(const char *s, unsigned int cp) { + wchar_t *w; + int l; + if (!(l = MultiByteToWideChar(cp, MB_ERR_INVALID_CHARS, s, -1, NULL, 0))) + return NULL; + if (!(w = (wchar_t *)malloc(l * sizeof(wchar_t)))) + return NULL; + if (MultiByteToWideChar(cp, MB_ERR_INVALID_CHARS, s, -1 , w, l) <= 0) { + free(w); + w = NULL; + } + return w; +} +#endif + FILE *ffms_fopen(const char *filename, const char *mode) { +#ifdef _WIN32 + int codepage = CP_ACP; #ifdef FFMS_USE_UTF8_PATHS - // Hack: support utf8-in-char* filenames on windows - wchar_t filename_wide[MAX_PATH*2]; - // 64 characters of mode string ought to be more than enough for everyone - wchar_t mode_wide[64]; - if ((MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, filename_wide, MAX_PATH) > 0) - && (MultiByteToWideChar(CP_ACP, NULL, mode, -1, mode_wide, 60) > 0)) - return _wfopen(filename_wide, mode_wide); + codepage = CP_UTF8; +#endif + FILE *ret; + wchar_t *filename_wide = dup_char_to_wchar(filename, codepage); + wchar_t *mode_wide = dup_char_to_wchar(mode, codepage); + if (filename_wide && mode_wide) + ret = _wfopen(filename_wide, mode_wide); else - return fopen(filename, mode); + ret = fopen(filename, mode); + + free(filename_wide); + free(mode_wide); + + return ret; #else return fopen(filename, mode); -#endif +#endif /* _WIN32 */ } size_t ffms_mbstowcs (wchar_t *wcstr, const char *mbstr, size_t max) { -#ifdef FFMS_USE_UTF8_PATHS +#if defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS) // try utf8 first int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mbstr, -1, NULL, 0); if (len > 0) { @@ -404,22 +426,85 @@ size_t ffms_mbstowcs (wchar_t *wcstr, const char *mbstr, size_t max) { // ffms_fstream stuff void ffms_fstream::open(const char *filename, std::ios_base::openmode mode) { -#ifdef FFMS_USE_UTF8_PATHS - // Hack: support utf8-in-char* filenames on windows - wchar_t filename_wide[MAX_PATH*2]; - if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, filename_wide, MAX_PATH) > 0) + // Unlike MSVC, mingw's iostream library doesn't have an fstream overload + // that takes a wchar_t* filename, which means you can't open unicode + // filenames with it on Windows. gg. +#if defined(_WIN32) && !defined(__MINGW32__) + unsigned int codepage = CP_ACP; +#if defined(FFMS_USE_UTF8_PATHS) + codepage = CP_UTF8; +#endif /* defined(FFMS_USE_UTF8_PATHS) */ + wchar_t *filename_wide = dup_char_to_wchar(filename, codepage); + if (filename_wide) std::fstream::open(filename_wide, mode); else std::fstream::open(filename, mode); -#else + + free(filename_wide); +#else /* defined(_WIN32) && !defined(__MINGW32__) */ std::fstream::open(filename, mode); -#endif +#endif /* defined(_WIN32) && !defined(__MINGW32__) */ } ffms_fstream::ffms_fstream(const char *filename, std::ios_base::openmode mode) { open(filename, mode); } + +#if defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS) +int ffms_wchar_open(const char *fname, int oflags, int pmode) { + wchar_t *wfname = dup_char_to_wchar(fname, CP_UTF8); + if (wfname) { + int ret = _wopen(wfname, oflags, pmode); + av_free(wfname); + return ret; + } + return -1; +} + +static int ffms_lavf_file_open(URLContext *h, const char *filename, int flags) { + int access; + int fd; + + av_strstart(filename, "file:", &filename); + + if (flags & URL_RDWR) { + access = _O_CREAT | _O_TRUNC | _O_RDWR; + } else if (flags & URL_WRONLY) { + access = _O_CREAT | _O_TRUNC | _O_WRONLY; + } else { + access = _O_RDONLY; + } +#ifdef _O_BINARY + access |= _O_BINARY; +#endif + fd = ffms_wchar_open(filename, access, 0666); + if (fd == -1) + return AVERROR(ENOENT); + h->priv_data = (void *) (intptr_t) fd; + return 0; +} + +// Hijack lavf's file protocol handler's open function and use our own instead. +// Hack by nielsm. +void ffms_patch_lavf_file_open() { + extern URLProtocol *first_protocol; + URLProtocol *proto = first_protocol; + while (proto != NULL) { + if (strcmp("file", proto->name) == 0) { + break; + } + proto = proto->next; + } + if (proto != NULL) { + proto->url_open = &ffms_lavf_file_open; + } +} +#endif /* defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS) */ + +// End of filename hackery. + + #ifdef HAALISOURCE CComPtr HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode) { @@ -478,3 +563,39 @@ void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext) { "Couldn't find stream information"); } } + + +// attempt to correct framerate to the proper NTSC fraction, if applicable +// code stolen from Perian +void CorrectNTSCRationalFramerate(int *Num, int *Den) { + AVRational TempFPS; + TempFPS.den = *Num; // not a typo + TempFPS.num = *Den; // still not a typo + + av_reduce(&TempFPS.num, &TempFPS.den, TempFPS.num, TempFPS.den, INT_MAX); + + if (TempFPS.num == 1) { + *Num = TempFPS.den; + *Den = TempFPS.num; + } + else { + double FTimebase = av_q2d(TempFPS); + double NearestNTSC = floor(FTimebase * 1001.0 + 0.5) / 1001.0; + const double SmallInterval = 1.0/120.0; + + if (fabs(FTimebase - NearestNTSC) < SmallInterval) { + *Num = int((1001.0 / FTimebase) + 0.5); + *Den = 1001; + } + } +} + +// correct the timebase if it is invalid +void CorrectTimebase(FFMS_VideoProperties *VP, FFMS_TrackTimeBase *TTimebase) { + double Timebase = (double)TTimebase->Num / TTimebase->Den; + double FPS = (double)VP->FPSNumerator / VP->FPSDenominator; + if ((1000/Timebase) / FPS < 1) { + TTimebase->Den = VP->FPSNumerator; + TTimebase->Num = (int64_t)VP->FPSDenominator * 1000; + } +} diff --git a/aegisub/libffms/src/core/utils.h b/aegisub/libffms/src/core/utils.h index 0e377fe06..672c2b941 100644 --- a/aegisub/libffms/src/core/utils.h +++ b/aegisub/libffms/src/core/utils.h @@ -142,8 +142,6 @@ 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 unsigned vtSize(VARIANT &vt); @@ -154,9 +152,14 @@ void InitializeCodecContextFromMatroskaTrackInfo(TrackInfo *TI, AVCodecContext * CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC = 0, unsigned int BitsPerSample = 0); FILE *ffms_fopen(const char *filename, const char *mode); size_t ffms_mbstowcs (wchar_t *wcstr, const char *mbstr, size_t max); +#if defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS) +void ffms_patch_lavf_file_open(); +#endif #ifdef HAALISOURCE CComPtr HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode); #endif void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext); +void CorrectNTSCRationalFramerate(int *Num, int *Den); +void CorrectTimebase(FFMS_VideoProperties *VP, FFMS_TrackTimeBase *TTimebase); #endif diff --git a/aegisub/libffms/src/core/videosource.cpp b/aegisub/libffms/src/core/videosource.cpp index 45e1230b1..a65a169d9 100644 --- a/aegisub/libffms/src/core/videosource.cpp +++ b/aegisub/libffms/src/core/videosource.cpp @@ -102,14 +102,14 @@ FFMS_Frame *FFMS_VideoSource::OutputFrame(AVFrame *Frame) { if (PPMode) { pp_postprocess(const_cast(Frame->data), Frame->linesize, PPFrame.data, PPFrame.linesize, CodecContext->width, CodecContext->height, Frame->qscale_table, Frame->qstride, PPMode, PPContext, Frame->pict_type | (Frame->qscale_type ? PP_PICT_TYPE_QP2 : 0)); if (SWS) { - sws_scale(SWS, PPFrame.data, PPFrame.linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize); + sws_scale(SWS, const_cast(PPFrame.data), PPFrame.linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize); CopyAVPictureFields(SWSFrame, LocalFrame); } else { CopyAVPictureFields(PPFrame, LocalFrame); } } else { if (SWS) { - sws_scale(SWS, Frame->data, Frame->linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize); + sws_scale(SWS, const_cast(Frame->data), Frame->linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize); CopyAVPictureFields(SWSFrame, LocalFrame); } else { // Special case to avoid ugly casts @@ -148,7 +148,7 @@ FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, in throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Not a video track"); - if (Index[Track].size() == 0) + if (Index->at(Track).size() == 0) throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, "Video track contains no frames"); @@ -162,7 +162,8 @@ FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, in SWS = NULL; LastFrameNum = 0; CurrentFrame = 1; - MPEG4Counter = 0; + DelayCounter = 0; + InitialDecode = 1; CodecContext = NULL; LastFrameHeight = -1; LastFrameWidth = -1; diff --git a/aegisub/libffms/src/core/videosource.h b/aegisub/libffms/src/core/videosource.h index bf09a919a..7836810f4 100644 --- a/aegisub/libffms/src/core/videosource.h +++ b/aegisub/libffms/src/core/videosource.h @@ -73,7 +73,8 @@ protected: FFMS_Track Frames; int VideoTrack; int CurrentFrame; - int MPEG4Counter; + int DelayCounter; + int InitialDecode; AVCodecContext *CodecContext; FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track);