From 6fe92bce72f9c732927c99780f2e37f8f84bcab3 Mon Sep 17 00:00:00 2001 From: Karl Blomster Date: Sat, 26 Sep 2009 21:56:15 +0000 Subject: [PATCH] Updating ffms2 to r221 (2.12), step 1/3: sources Originally committed to SVN as r3574. --- aegisub/libffms/include/ffms.h | 177 ++++++---- .../{ffaudiosource.cpp => audiosource.cpp} | 31 +- .../core/{ffaudiosource.h => audiosource.h} | 51 +-- aegisub/libffms/src/core/ffms.cpp | 317 ++++++++++++------ aegisub/libffms/src/core/guids.h | 4 +- .../core/{ffhaaliaudio.cpp => haaliaudio.cpp} | 111 ++---- .../{ffhaaliindexer.cpp => haaliindexer.cpp} | 149 ++++---- .../core/{ffhaalivideo.cpp => haalivideo.cpp} | 158 ++++----- aegisub/libffms/src/core/indexing.cpp | 260 +++++++------- aegisub/libffms/src/core/indexing.h | 68 ++-- .../core/{fflavfaudio.cpp => lavfaudio.cpp} | 85 ++--- .../{fflavfindexer.cpp => lavfindexer.cpp} | 77 +++-- .../core/{fflavfvideo.cpp => lavfvideo.cpp} | 126 +++---- ...{ffmatroskaaudio.cpp => matroskaaudio.cpp} | 112 +++---- ...atroskaindexer.cpp => matroskaindexer.cpp} | 82 +++-- ...{ffmatroskavideo.cpp => matroskavideo.cpp} | 148 ++++---- aegisub/libffms/src/core/stdiostream.c | 18 +- aegisub/libffms/src/core/utils.cpp | 233 +++++++++---- aegisub/libffms/src/core/utils.h | 77 ++++- .../{ffvideosource.cpp => videosource.cpp} | 140 +++++--- .../core/{ffvideosource.h => videosource.h} | 69 ++-- 21 files changed, 1354 insertions(+), 1139 deletions(-) rename aegisub/libffms/src/core/{ffaudiosource.cpp => audiosource.cpp} (74%) rename aegisub/libffms/src/core/{ffaudiosource.h => audiosource.h} (66%) rename aegisub/libffms/src/core/{ffhaaliaudio.cpp => haaliaudio.cpp} (72%) rename aegisub/libffms/src/core/{ffhaaliindexer.cpp => haaliindexer.cpp} (66%) rename aegisub/libffms/src/core/{ffhaalivideo.cpp => haalivideo.cpp} (67%) rename aegisub/libffms/src/core/{fflavfaudio.cpp => lavfaudio.cpp} (75%) rename aegisub/libffms/src/core/{fflavfindexer.cpp => lavfindexer.cpp} (73%) rename aegisub/libffms/src/core/{fflavfvideo.cpp => lavfvideo.cpp} (67%) rename aegisub/libffms/src/core/{ffmatroskaaudio.cpp => matroskaaudio.cpp} (62%) rename aegisub/libffms/src/core/{ffmatroskaindexer.cpp => matroskaindexer.cpp} (71%) rename aegisub/libffms/src/core/{ffmatroskavideo.cpp => matroskavideo.cpp} (58%) rename aegisub/libffms/src/core/{ffvideosource.cpp => videosource.cpp} (62%) rename aegisub/libffms/src/core/{ffvideosource.h => videosource.h} (56%) diff --git a/aegisub/libffms/include/ffms.h b/aegisub/libffms/include/ffms.h index e9aa3aacf..9eb7b071e 100644 --- a/aegisub/libffms/include/ffms.h +++ b/aegisub/libffms/include/ffms.h @@ -21,6 +21,9 @@ #ifndef FFMS_H #define FFMS_H +// Version format: major - minor - micro - bump +#define FFMS_VERSION ((2 << 24) | (11 << 16)| (0 << 8) | 5) + #include #ifdef __cplusplus @@ -43,11 +46,56 @@ # define FFMS_API(ret) EXTERN_C ret FFMS_CC #endif -FFMS_CLASS_TYPE FFVideo; -FFMS_CLASS_TYPE FFAudio; -FFMS_CLASS_TYPE FFIndexer; -FFMS_CLASS_TYPE FFIndex; -FFMS_CLASS_TYPE FFTrack; +struct FFMS_ErrorInfo { + int ErrorType; + int SubType; + int BufferSize; + char *Buffer; +}; + +FFMS_CLASS_TYPE FFMS_VideoSource; +FFMS_CLASS_TYPE FFMS_AudioSource; +FFMS_CLASS_TYPE FFMS_Indexer; +FFMS_CLASS_TYPE FFMS_Index; +FFMS_CLASS_TYPE FFMS_Track; + +enum FFMS_Errors { + // No error + FFMS_ERROR_SUCCESS = 0, + + // Main types - where the error occurred + FFMS_ERROR_INDEX = 1, + FFMS_ERROR_INDEXING, + FFMS_ERROR_POSTPROCESSING, + FFMS_ERROR_SCALING, + FFMS_ERROR_DECODING, + FFMS_ERROR_SEEKING, + FFMS_ERROR_PARSER, + FFMS_ERROR_TRACK, + FFMS_ERROR_WAVE_WRITER, + FFMS_ERROR_CANCELLED, + + // Subtypes - what caused the error + FFMS_ERROR_UNKNOWN = 20, + FFMS_ERROR_UNSUPPORTED, + FFMS_ERROR_FILE_READ, + FFMS_ERROR_FILE_WRITE, + FFMS_ERROR_NO_FILE, + FFMS_ERROR_VERSION, + FFMS_ERROR_ALLOCATION_FAILED, + FFMS_ERROR_INVALID_ARGUMENT, + FFMS_ERROR_CODEC, + FFMS_ERROR_NOT_AVAILABLE, + FFMS_ERROR_FILE_MISMATCH, + FFMS_ERROR_USER +}; + +enum FFMS_Sources { + FFMS_SOURCE_LAVF = 0x01, + FFMS_SOURCE_MATROSKA = 0x02, + FFMS_SOURCE_HAALIMPEG = 0x04, + FFMS_SOURCE_HAALIOGG = 0x08 +}; enum FFMS_CPUFeatures { FFMS_CPU_CAPS_MMX = 0x01, @@ -65,6 +113,13 @@ enum FFMS_SeekMode { FFMS_SEEK_AGGRESSIVE = 3 }; +enum FFMS_IndexErrorHandling { + FFMS_IEH_ABORT = 0, + FFMS_IEH_CLEAR_TRACK = 1, + FFMS_IEH_STOP_TRACK = 2, + FFMS_IEH_IGNORE = 3 +}; + enum FFMS_TrackType { FFMS_TYPE_UNKNOWN = -1, FFMS_TYPE_VIDEO, @@ -106,20 +161,20 @@ enum FFMS_AudioChannel { }; enum FFMS_Resizers { - FFMS_RESIZER_FAST_BILINEAR = 0x01, - FFMS_RESIZER_BILINEAR = 0x02, - FFMS_RESIZER_BICUBIC = 0x04, - FFMS_RESIZER_X = 0x08, - FFMS_RESIZER_POINT = 0x10, - FFMS_RESIZER_AREA = 0x20, - FFMS_RESIZER_BICUBLIN = 0x40, - FFMS_RESIZER_GAUSS = 0x80, - FFMS_RESIZER_SINC = 0x100, - FFMS_RESIZER_LANCZOS = 0x200, - FFMS_RESIZER_SPLINE = 0x400 + FFMS_RESIZER_FAST_BILINEAR = 0x0001, + FFMS_RESIZER_BILINEAR = 0x0002, + FFMS_RESIZER_BICUBIC = 0x0004, + FFMS_RESIZER_X = 0x0008, + FFMS_RESIZER_POINT = 0x0010, + FFMS_RESIZER_AREA = 0x0020, + FFMS_RESIZER_BICUBLIN = 0x0040, + FFMS_RESIZER_GAUSS = 0x0080, + FFMS_RESIZER_SINC = 0x0100, + FFMS_RESIZER_LANCZOS = 0x0200, + FFMS_RESIZER_SPLINE = 0x0400 }; -struct FFAVFrame { +struct FFMS_Frame { uint8_t *Data[4]; int Linesize[4]; int EncodedWidth; @@ -135,37 +190,37 @@ struct FFAVFrame { char PictType; }; -struct FFTrackTimeBase { +struct FFMS_TrackTimeBase { int64_t Num; int64_t Den; }; #define FFMS_FRAMEINFO_COMMON int64_t DTS; int RepeatPict; bool KeyFrame; -struct FFFrameInfo { +struct FFMS_FrameInfo { FFMS_FRAMEINFO_COMMON }; -struct FFVideoProperties { - int Width; - int Height; +struct FFMS_VideoProperties { int FPSDenominator; int FPSNumerator; int RFFDenominator; int RFFNumerator; int NumFrames; - int VPixelFormat; int SARNum; int SARDen; int CropTop; int CropBottom; int CropLeft; int CropRight; + int TopFieldFirst; + int ColorSpace; // same as in the MPEG-2 specs, see AVColorSpace in avcodec.h + int ColorRange; // 0=unspecified, 1=16-235, 2=0-255 double FirstTime; double LastTime; }; -struct FFAudioProperties { +struct FFMS_AudioProperties { int SampleFormat; int SampleRate; int BitsPerSample; @@ -177,47 +232,51 @@ struct FFAudioProperties { }; typedef int (FFMS_CC *TIndexCallback)(int64_t Current, int64_t Total, void *ICPrivate); -typedef int (FFMS_CC *TAudioNameCallback)(const char *SourceFile, int Track, const FFAudioProperties *AP, char *FileName, int FNSize, void *Private); +typedef int (FFMS_CC *TAudioNameCallback)(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, void *Private); // Most functions return 0 on success // Functions without error message output can be assumed to never fail in a graceful way FFMS_API(void) FFMS_Init(int CPUFeatures); FFMS_API(int) FFMS_GetLogLevel(); FFMS_API(void) FFMS_SetLogLevel(int Level); -FFMS_API(FFVideo *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize); -FFMS_API(FFAudio *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); -FFMS_API(void) FFMS_DestroyVideoSource(FFVideo *V); -FFMS_API(void) FFMS_DestroyAudioSource(FFAudio *A); -FFMS_API(const FFVideoProperties *) FFMS_GetVideoProperties(FFVideo *V); -FFMS_API(const FFAudioProperties *) FFMS_GetAudioProperties(FFAudio *A); -FFMS_API(const FFAVFrame *) FFMS_GetFrame(FFVideo *V, int n, char *ErrorMsg, unsigned MsgSize); -FFMS_API(const FFAVFrame *) FFMS_GetFrameByTime(FFVideo *V, double Time, char *ErrorMsg, unsigned MsgSize); -FFMS_API(int) FFMS_GetAudio(FFAudio *A, void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize); -FFMS_API(int) FFMS_SetOutputFormatV(FFVideo *V, int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize); -FFMS_API(void) FFMS_ResetOutputFormatV(FFVideo *V); -FFMS_API(void) FFMS_DestroyIndex(FFIndex *Index); -FFMS_API(int) FFMS_GetFirstTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize); -FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize); -FFMS_API(int) FFMS_GetNumTracks(FFIndex *Index); -FFMS_API(int) FFMS_GetNumTracksI(FFIndexer *Indexer); -FFMS_API(int) FFMS_GetTrackType(FFTrack *T); -FFMS_API(int) FFMS_GetTrackTypeI(FFIndexer *Indexer, int Track); -FFMS_API(const char *) FFMS_GetCodecNameI(FFIndexer *Indexer, int Track); -FFMS_API(int) FFMS_GetNumFrames(FFTrack *T); -FFMS_API(const FFFrameInfo *) FFMS_GetFrameInfo(FFTrack *T, int Frame); -FFMS_API(FFTrack *) FFMS_GetTrackFromIndex(FFIndex *Index, int Track); -FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V); -FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A); -FFMS_API(const FFTrackTimeBase *) FFMS_GetTimeBase(FFTrack *T); -FFMS_API(int) FFMS_WriteTimecodes(FFTrack *T, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize); -FFMS_API(FFIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize); -FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFAudioProperties *AP, char *FileName, int FNSize, void *Private); -FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize); -FFMS_API(FFIndex *) FFMS_DoIndexing(FFIndexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize); -FFMS_API(void) FFMS_CancelIndexing(FFIndexer *Indexer); -FFMS_API(FFIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize); -FFMS_API(int) FFMS_IndexBelongsToFile(FFIndex *Index, const char *SourceFile, char *ErrorMsg, unsigned MsgSize); -FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); +FFMS_API(FFMS_VideoSource *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode, FFMS_ErrorInfo *ErrorInfo); +FFMS_API(FFMS_AudioSource *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo); +FFMS_API(void) FFMS_DestroyVideoSource(FFMS_VideoSource *V); +FFMS_API(void) FFMS_DestroyAudioSource(FFMS_AudioSource *A); +FFMS_API(const FFMS_VideoProperties *) FFMS_GetVideoProperties(FFMS_VideoSource *V); +FFMS_API(const FFMS_AudioProperties *) FFMS_GetAudioProperties(FFMS_AudioSource *A); +FFMS_API(const FFMS_Frame *) FFMS_GetFrame(FFMS_VideoSource *V, int n, FFMS_ErrorInfo *ErrorInfo); +FFMS_API(const FFMS_Frame *) FFMS_GetFrameByTime(FFMS_VideoSource *V, double Time, FFMS_ErrorInfo *ErrorInfo); +FFMS_API(int) FFMS_GetAudio(FFMS_AudioSource *A, void *Buf, int64_t Start, int64_t Count, FFMS_ErrorInfo *ErrorInfo); +FFMS_API(int) FFMS_SetOutputFormatV(FFMS_VideoSource *V, int64_t TargetFormats, int Width, int Height, int Resizer, FFMS_ErrorInfo *ErrorInfo); +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_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(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); +FFMS_API(FFMS_Track *) FFMS_GetTrackFromVideo(FFMS_VideoSource *V); +FFMS_API(FFMS_Track *) FFMS_GetTrackFromAudio(FFMS_AudioSource *A); +FFMS_API(const FFMS_TrackTimeBase *) FFMS_GetTimeBase(FFMS_Track *T); +FFMS_API(int) FFMS_WriteTimecodes(FFMS_Track *T, const char *TimecodeFile, FFMS_ErrorInfo *ErrorInfo); +FFMS_API(FFMS_Index *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo); +FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, void *Private); +FFMS_API(FFMS_Indexer *) FFMS_CreateIndexer(const char *SourceFile, FFMS_ErrorInfo *ErrorInfo); +FFMS_API(FFMS_Index *) FFMS_DoIndexing(FFMS_Indexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo); +FFMS_API(void) FFMS_CancelIndexing(FFMS_Indexer *Indexer); +FFMS_API(FFMS_Index *) FFMS_ReadIndex(const char *IndexFile, FFMS_ErrorInfo *ErrorInfo); +FFMS_API(int) FFMS_IndexBelongsToFile(FFMS_Index *Index, const char *SourceFile, FFMS_ErrorInfo *ErrorInfo); +FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo); FFMS_API(int) FFMS_GetPixFmt(const char *Name); +FFMS_API(int) FFMS_GetPresentSources(); +FFMS_API(int) FFMS_GetEnabledSources(); #endif diff --git a/aegisub/libffms/src/core/ffaudiosource.cpp b/aegisub/libffms/src/core/audiosource.cpp similarity index 74% rename from aegisub/libffms/src/core/ffaudiosource.cpp rename to aegisub/libffms/src/core/audiosource.cpp index f8a6ad0c5..169329216 100644 --- a/aegisub/libffms/src/core/ffaudiosource.cpp +++ b/aegisub/libffms/src/core/audiosource.cpp @@ -18,7 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include "ffaudiosource.h" +#include "audiosource.h" /* Audio Cache */ @@ -93,15 +93,32 @@ int64_t TAudioCache::FillRequest(int64_t Start, int64_t Samples, uint8_t *Dst) { return FFMIN(Ret, Start + Samples); } -/* FFAudio base class */ +/* FFMS_AudioSource base class */ -FFAudio::FFAudio(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 10) { - if (Index->CompareFileSignature(SourceFile, ErrorMsg, MsgSize)) - throw ErrorMsg; +FFMS_AudioSource::FFMS_AudioSource(const char *SourceFile, FFMS_Index *Index, int Track) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 10), CurrentSample(0) { + if (Track < 0 || Track >= static_cast(Index->size())) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, + "Out of bounds track index selected"); - CurrentSample = 0; + if (Index->at(Track).TT != FFMS_TYPE_AUDIO) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, + "Not an audio track"); + + if (Index[Track].size() == 0) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, + "Audio track contains no audio frames"); + + if (!Index->CompareFileSignature(SourceFile)) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH, + "The index does not match the source file"); } -FFAudio::~FFAudio() { +FFMS_AudioSource::~FFMS_AudioSource() { } + +void FFMS_AudioSource::GetAudioCheck(int64_t Start, int64_t Count) { + if (Start < 0 || Start + Count > AP.NumSamples) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_INVALID_ARGUMENT, + "Out of bounds audio samples requested"); +} diff --git a/aegisub/libffms/src/core/ffaudiosource.h b/aegisub/libffms/src/core/audiosource.h similarity index 66% rename from aegisub/libffms/src/core/ffaudiosource.h rename to aegisub/libffms/src/core/audiosource.h index e9bc6a41a..de6b9ee23 100644 --- a/aegisub/libffms/src/core/ffaudiosource.h +++ b/aegisub/libffms/src/core/audiosource.h @@ -68,63 +68,68 @@ public: int64_t FillRequest(int64_t Start, int64_t Samples, uint8_t *Dst); }; -class FFAudio { +class FFMS_AudioSource { +friend class FFSourceResources; protected: TAudioCache AudioCache; int64_t CurrentSample; std::vector DecodingBuffer; - FFTrack Frames; + FFMS_Track Frames; AVCodecContext *CodecContext; int AudioTrack; - FFAudioProperties AP; + FFMS_AudioProperties AP; + + virtual void Free(bool CloseCodec) = 0; public: - FFAudio(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); - virtual ~FFAudio(); - FFTrack *GetFFTrack() { return &Frames; } - const FFAudioProperties& GetFFAudioProperties() { return AP; } - virtual int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) = 0; + FFMS_AudioSource(const char *SourceFile, FFMS_Index *Index, int Track); + virtual ~FFMS_AudioSource(); + FFMS_Track *GetTrack() { return &Frames; } + const FFMS_AudioProperties& GetAudioProperties() { return AP; } + virtual void GetAudio(void *Buf, int64_t Start, int64_t Count) = 0; + void GetAudioCheck(int64_t Start, int64_t Count); }; -class FFLAVFAudio : public FFAudio { +class FFLAVFAudio : public FFMS_AudioSource { private: AVFormatContext *FormatContext; + FFSourceResources Res; - int DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned MsgSize); + void DecodeNextAudioBlock(int64_t *Count); void Free(bool CloseCodec); public: - FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); - ~FFLAVFAudio(); - int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize); + FFLAVFAudio(const char *SourceFile, int Track, FFMS_Index *Index); + void GetAudio(void *Buf, int64_t Start, int64_t Count); }; -class FFMatroskaAudio : public FFAudio { +class FFMatroskaAudio : public FFMS_AudioSource { private: MatroskaFile *MF; MatroskaReaderContext MC; CompressedStream *CS; char ErrorMessage[256]; + FFSourceResources Res; + size_t PacketNumber; - int DecodeNextAudioBlock(int64_t *Count, int AudioBlock, char *ErrorMsg, unsigned MsgSize); + void DecodeNextAudioBlock(int64_t *Count); void Free(bool CloseCodec); public: - FFMatroskaAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); - ~FFMatroskaAudio(); - int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize); + FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index *Index); + void GetAudio(void *Buf, int64_t Start, int64_t Count); }; #ifdef HAALISOURCE -class FFHaaliAudio : public FFAudio { +class FFHaaliAudio : public FFMS_AudioSource { private: CComPtr pMMC; std::vector CodecPrivate; + FFSourceResources Res; + void DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count); void Free(bool CloseCodec); - int DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count, char *ErrorMsg, unsigned MsgSize); public: - FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index, int SourceMode, char *ErrorMsg, unsigned MsgSize); - ~FFHaaliAudio(); - int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize); + FFHaaliAudio(const char *SourceFile, int Track, FFMS_Index *Index, enum FFMS_Sources SourceMode); + void GetAudio(void *Buf, int64_t Start, int64_t Count); }; #endif // HAALISOURCE diff --git a/aegisub/libffms/src/core/ffms.cpp b/aegisub/libffms/src/core/ffms.cpp index 50836c845..ab2e27d53 100644 --- a/aegisub/libffms/src/core/ffms.cpp +++ b/aegisub/libffms/src/core/ffms.cpp @@ -21,21 +21,22 @@ #include #include #include "ffms.h" -#include "ffvideosource.h" -#include "ffaudiosource.h" +#include "videosource.h" +#include "audiosource.h" #include "indexing.h" -static bool FFmpegInited = false; +static bool FFmpegInited = false; +bool HasHaaliMPEG = false; +bool HasHaaliOGG = false; int CPUFeatures = 0; #ifdef FFMS_WIN_DEBUG extern "C" int av_log_level; -void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl) -{ +void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl) { static int print_prefix=1; static int count; static char line[1024], prev[1024]; @@ -56,7 +57,9 @@ void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl) return; } if(count>0){ - fprintf(stderr, " Last message repeated %d times\n", count); + std::stringstream ss; + ss << " Last message repeated " << count << " times\n"; + OutputDebugStringA(ss.str().c_str()); count=0; } OutputDebugStringA(line); @@ -75,6 +78,13 @@ FFMS_API(void) FFMS_Init(int CPUFeatures) { av_log_set_level(AV_LOG_QUIET); #endif ::CPUFeatures = CPUFeatures; +#ifdef HAALISOURCE + CComPtr pMMC; + HasHaaliMPEG = !FAILED(pMMC.CoCreateInstance(HAALI_MPEG_PARSER)); + pMMC = NULL; + HasHaaliOGG = !FAILED(pMMC.CoCreateInstance(HAALI_OGG_PARSER)); + pMMC = NULL; +#endif FFmpegInited = true; } } @@ -87,171 +97,224 @@ FFMS_API(void) FFMS_SetLogLevel(int Level) { av_log_set_level(Level); } -FFMS_API(FFVideo *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize) { - if (Track < 0 || Track >= static_cast(Index->size())) { - snprintf(ErrorMsg, MsgSize, "Out of bounds track index selected"); - return NULL; - } - - if (Index->at(Track).TT != FFMS_TYPE_VIDEO) { - snprintf(ErrorMsg, MsgSize, "Not a video track"); - return NULL; - } - +FFMS_API(FFMS_VideoSource *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode, FFMS_ErrorInfo *ErrorInfo) { try { switch (Index->Decoder) { - case 0: return new FFLAVFVideo(SourceFile, Track, Index, PP, Threads, SeekMode, ErrorMsg, MsgSize); - case 1: return new FFMatroskaVideo(SourceFile, Track, Index, PP, Threads, ErrorMsg, MsgSize); + case FFMS_SOURCE_LAVF: + return new FFLAVFVideo(SourceFile, Track, Index, Threads, SeekMode); + case FFMS_SOURCE_MATROSKA: + return new FFMatroskaVideo(SourceFile, Track, Index, Threads); #ifdef HAALISOURCE - case 2: return new FFHaaliVideo(SourceFile, Track, Index, PP, Threads, 0, ErrorMsg, MsgSize); - case 3: return new FFHaaliVideo(SourceFile, Track, Index, PP, Threads, 1, ErrorMsg, MsgSize); + case FFMS_SOURCE_HAALIMPEG: + if (HasHaaliMPEG) + return new FFHaaliVideo(SourceFile, Track, Index, Threads, FFMS_SOURCE_HAALIMPEG); + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali MPEG/TS source unavailable"); + case FFMS_SOURCE_HAALIOGG: + if (HasHaaliOGG) + return new FFHaaliVideo(SourceFile, Track, Index, Threads, FFMS_SOURCE_HAALIOGG); + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali OGG/OGM source unavailable"); #endif default: - snprintf(ErrorMsg, MsgSize, "Unsupported format"); - return NULL; + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Unsupported format"); } - } catch (...) { + } catch (FFMS_Exception &e) { + e.CopyOut(ErrorInfo); return NULL; } } -FFMS_API(FFAudio *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { - if (Track < 0 || Track >= static_cast(Index->size())) { - snprintf(ErrorMsg, MsgSize, "Out of bounds track index selected"); - return NULL; - } - - if (Index->at(Track).TT != FFMS_TYPE_AUDIO) { - snprintf(ErrorMsg, MsgSize, "Not an audio track"); - return NULL; - } - +FFMS_API(FFMS_AudioSource *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo) { try { switch (Index->Decoder) { - case 0: return new FFLAVFAudio(SourceFile, Track, Index, ErrorMsg, MsgSize); - case 1: return new FFMatroskaAudio(SourceFile, Track, Index, ErrorMsg, MsgSize); + case FFMS_SOURCE_LAVF: + return new FFLAVFAudio(SourceFile, Track, Index); + case FFMS_SOURCE_MATROSKA: + return new FFMatroskaAudio(SourceFile, Track, Index); #ifdef HAALISOURCE - case 2: return new FFHaaliAudio(SourceFile, Track, Index, 0, ErrorMsg, MsgSize); - case 3: return new FFHaaliAudio(SourceFile, Track, Index, 1, ErrorMsg, MsgSize); + case FFMS_SOURCE_HAALIMPEG: + if (HasHaaliMPEG) + return new FFHaaliAudio(SourceFile, Track, Index, FFMS_SOURCE_HAALIMPEG); + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali MPEG/TS source unavailable"); + case FFMS_SOURCE_HAALIOGG: + if (HasHaaliOGG) + return new FFHaaliAudio(SourceFile, Track, Index, FFMS_SOURCE_HAALIOGG); + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali OGG/OGM source unavailable"); #endif default: - snprintf(ErrorMsg, MsgSize, "Unsupported format"); - return NULL; + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Unsupported format"); } - } catch (...) { + } catch (FFMS_Exception &e) { + e.CopyOut(ErrorInfo); return NULL; } } -FFMS_API(void) FFMS_DestroyVideoSource(FFVideo *V) { +FFMS_API(void) FFMS_DestroyVideoSource(FFMS_VideoSource *V) { delete V; } -FFMS_API(void) FFMS_DestroyAudioSource(FFAudio *A) { +FFMS_API(void) FFMS_DestroyAudioSource(FFMS_AudioSource *A) { delete A; } -FFMS_API(const FFVideoProperties *) FFMS_GetVideoProperties(FFVideo *V) { - return &V->GetFFVideoProperties(); +FFMS_API(const FFMS_VideoProperties *) FFMS_GetVideoProperties(FFMS_VideoSource *V) { + return &V->GetVideoProperties(); } -FFMS_API(const FFAudioProperties *) FFMS_GetAudioProperties(FFAudio *A) { - return &A->GetFFAudioProperties(); +FFMS_API(const FFMS_AudioProperties *) FFMS_GetAudioProperties(FFMS_AudioSource *A) { + return &A->GetAudioProperties(); } -FFMS_API(const FFAVFrame *) FFMS_GetFrame(FFVideo *V, int n, char *ErrorMsg, unsigned MsgSize) { - return (FFAVFrame *)V->GetFrame(n, ErrorMsg, MsgSize); +FFMS_API(const FFMS_Frame *) FFMS_GetFrame(FFMS_VideoSource *V, int n, FFMS_ErrorInfo *ErrorInfo) { + ClearErrorInfo(ErrorInfo); + try { + return V->GetFrame(n); + } catch (FFMS_Exception &e) { + e.CopyOut(ErrorInfo); + return NULL; + } } -FFMS_API(const FFAVFrame *) FFMS_GetFrameByTime(FFVideo *V, double Time, char *ErrorMsg, unsigned MsgSize) { - return (FFAVFrame *)V->GetFrameByTime(Time, ErrorMsg, MsgSize); +FFMS_API(const FFMS_Frame *) FFMS_GetFrameByTime(FFMS_VideoSource *V, double Time, FFMS_ErrorInfo *ErrorInfo) { + ClearErrorInfo(ErrorInfo); + try { + return (FFMS_Frame *)V->GetFrameByTime(Time); + } catch (FFMS_Exception &e) { + e.CopyOut(ErrorInfo); + return NULL; + } } -FFMS_API(int) FFMS_GetAudio(FFAudio *A, void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) { - return A->GetAudio(Buf, Start, Count, ErrorMsg, MsgSize); +FFMS_API(int) FFMS_GetAudio(FFMS_AudioSource *A, void *Buf, int64_t Start, int64_t Count, FFMS_ErrorInfo *ErrorInfo) { + ClearErrorInfo(ErrorInfo); + try { + A->GetAudio(Buf, Start, Count); + } catch (FFMS_Exception &e) { + return e.CopyOut(ErrorInfo); + } + return FFMS_ERROR_SUCCESS; } -FFMS_API(int) FFMS_SetOutputFormatV(FFVideo *V, int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize) { - return V->SetOutputFormat(TargetFormats, Width, Height, Resizer, ErrorMsg, MsgSize); +FFMS_API(int) FFMS_SetOutputFormatV(FFMS_VideoSource *V, int64_t TargetFormats, int Width, int Height, int Resizer, FFMS_ErrorInfo *ErrorInfo) { + ClearErrorInfo(ErrorInfo); + try { + V->SetOutputFormat(TargetFormats, Width, Height, Resizer); + } catch (FFMS_Exception &e) { + return e.CopyOut(ErrorInfo); + } + return FFMS_ERROR_SUCCESS; } -FFMS_API(void) FFMS_ResetOutputFormatV(FFVideo *V) { +FFMS_API(void) FFMS_ResetOutputFormatV(FFMS_VideoSource *V) { V->ResetOutputFormat(); } -FFMS_API(void) FFMS_DestroyIndex(FFIndex *Index) { +FFMS_API(int) FFMS_SetPP(FFMS_VideoSource *V, const char *PP, FFMS_ErrorInfo *ErrorInfo) { + ClearErrorInfo(ErrorInfo); + try { + V->SetPP(PP); + } catch (FFMS_Exception &e) { + return e.CopyOut(ErrorInfo); + } + return FFMS_ERROR_SUCCESS; +} + +FFMS_API(void) FFMS_ResetPP(FFMS_VideoSource *V) { + V->ResetPP(); +} + +FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index) { delete Index; } -FFMS_API(int) FFMS_GetFirstTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize) { +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++) if ((*Index)[i].TT == TrackType) return i; - snprintf(ErrorMsg, MsgSize, "No suitable, indexed track found"); - return -1; + + try { + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE, + "No suitable, indexed track found"); + } catch (FFMS_Exception &e) { + e.CopyOut(ErrorInfo); + return -1; + } } -FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize) { +FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo) { + ClearErrorInfo(ErrorInfo); for (int i = 0; i < static_cast(Index->size()); i++) if ((*Index)[i].TT == TrackType && (*Index)[i].size() > 0) return i; - snprintf(ErrorMsg, MsgSize, "No suitable, indexed track found"); - return -1; + try { + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE, + "No suitable, indexed track found"); + } catch (FFMS_Exception &e) { + e.CopyOut(ErrorInfo); + return -1; + } } -FFMS_API(int) FFMS_GetNumTracks(FFIndex *Index) { +FFMS_API(int) FFMS_GetNumTracks(FFMS_Index *Index) { return Index->size(); } -FFMS_API(int) FFMS_GetNumTracksI(FFIndexer *Indexer) { +FFMS_API(int) FFMS_GetNumTracksI(FFMS_Indexer *Indexer) { return Indexer->GetNumberOfTracks(); } -FFMS_API(int) FFMS_GetTrackType(FFTrack *T) { +FFMS_API(int) FFMS_GetTrackType(FFMS_Track *T) { return T->TT; } -FFMS_API(int) FFMS_GetTrackTypeI(FFIndexer *Indexer, int Track) { +FFMS_API(int) FFMS_GetTrackTypeI(FFMS_Indexer *Indexer, int Track) { return Indexer->GetTrackType(Track); } -FFMS_API(const char *) FFMS_GetCodecNameI(FFIndexer *Indexer, int Track) { +FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track) { return Indexer->GetTrackCodec(Track); } -FFMS_API(int) FFMS_GetNumFrames(FFTrack *T) { +FFMS_API(int) FFMS_GetNumFrames(FFMS_Track *T) { return T->size(); } -FFMS_API(const FFFrameInfo *) FFMS_GetFrameInfo(FFTrack *T, int Frame) { - return reinterpret_cast(&(*T)[Frame]); +FFMS_API(const FFMS_FrameInfo *) FFMS_GetFrameInfo(FFMS_Track *T, int Frame) { + return reinterpret_cast(&(*T)[Frame]); } -FFMS_API(FFTrack *) FFMS_GetTrackFromIndex(FFIndex *Index, int Track) { +FFMS_API(FFMS_Track *) FFMS_GetTrackFromIndex(FFMS_Index *Index, int Track) { return &(*Index)[Track]; } -FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V) { - return V->GetFFTrack(); +FFMS_API(FFMS_Track *) FFMS_GetTrackFromVideo(FFMS_VideoSource *V) { + return V->GetTrack(); } -FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A) { - return A->GetFFTrack(); +FFMS_API(FFMS_Track *) FFMS_GetTrackFromAudio(FFMS_AudioSource *A) { + return A->GetTrack(); } -FFMS_API(const FFTrackTimeBase *) FFMS_GetTimeBase(FFTrack *T) { +FFMS_API(const FFMS_TrackTimeBase *) FFMS_GetTimeBase(FFMS_Track *T) { return &T->TB; } -FFMS_API(int) FFMS_WriteTimecodes(FFTrack *T, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize) { - return T->WriteTimecodes(TimecodeFile, ErrorMsg, MsgSize); +FFMS_API(int) FFMS_WriteTimecodes(FFMS_Track *T, const char *TimecodeFile, FFMS_ErrorInfo *ErrorInfo) { + ClearErrorInfo(ErrorInfo); + try { + T->WriteTimecodes(TimecodeFile); + } catch (FFMS_Exception &e) { + return e.CopyOut(ErrorInfo); + } + return FFMS_ERROR_SUCCESS; } -FFMS_API(FFIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize) { - FFIndexer *Indexer = FFMS_CreateIndexer(SourceFile, ErrorMsg, MsgSize); +FFMS_API(FFMS_Index *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo) { + FFMS_Indexer *Indexer = FFMS_CreateIndexer(SourceFile, ErrorInfo); if (!Indexer) return NULL; - return FFMS_DoIndexing(Indexer, IndexMask, DumpMask, ANC, ANCPrivate, IgnoreDecodeErrors, IC, ICPrivate, ErrorMsg, MsgSize); + return FFMS_DoIndexing(Indexer, IndexMask, DumpMask, ANC, ANCPrivate, ErrorHandling, IC, ICPrivate, ErrorInfo); } /* Used by FFMS_DefaultAudioFilename */ @@ -270,7 +333,7 @@ static void ReplaceString(std::string &s, std::string from, std::string to) { s.replace(idx, from.length(), to); } -FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFAudioProperties *AP, char *FileName, int FNSize, void *Private) { +FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, void *Private) { std::string s = static_cast(Private); ReplaceString(s, "%sourcefile%", SourceFile); @@ -281,55 +344,99 @@ FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const ReplaceString(s, "%bps%", IntToStr(AP->BitsPerSample)); ReplaceString(s, "%delay%", IntToStr(static_cast(AP->FirstTime))); - if (FileName == NULL) { - return s.length() + 1; - } else { + if (FileName != NULL) strcpy(FileName, s.c_str()); - return 0; - } + + return s.length() + 1; } -FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize) { +FFMS_API(FFMS_Indexer *) FFMS_CreateIndexer(const char *SourceFile, FFMS_ErrorInfo *ErrorInfo) { + ClearErrorInfo(ErrorInfo); try { - return FFIndexer::CreateFFIndexer(SourceFile, ErrorMsg, MsgSize); - } catch (...) { + return FFMS_Indexer::CreateIndexer(SourceFile); + } catch (FFMS_Exception &e) { + e.CopyOut(ErrorInfo); return NULL; } } -FFMS_API(FFIndex *) FFMS_DoIndexing(FFIndexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize) { +FFMS_API(FFMS_Index *) FFMS_DoIndexing(FFMS_Indexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo) { + ClearErrorInfo(ErrorInfo); + Indexer->SetIndexMask(IndexMask | DumpMask); Indexer->SetDumpMask(DumpMask); - Indexer->SetIgnoreDecodeErrors(IgnoreDecodeErrors); + Indexer->SetErrorHandling(ErrorHandling); Indexer->SetProgressCallback(IC, ICPrivate); Indexer->SetAudioNameCallback(ANC, ANCPrivate); - FFIndex *Index = Indexer->DoIndexing(ErrorMsg, MsgSize); + + FFMS_Index *Index = NULL; + try { + Index = Indexer->DoIndexing(); + } catch (FFMS_Exception &e) { + e.CopyOut(ErrorInfo); + } delete Indexer; return Index; } -FFMS_API(void) FFMS_CancelIndexing(FFIndexer *Indexer) { +FFMS_API(void) FFMS_CancelIndexing(FFMS_Indexer *Indexer) { delete Indexer; } -FFMS_API(FFIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) { - FFIndex *Index = new FFIndex(); - if (Index->ReadIndex(IndexFile, ErrorMsg, MsgSize)) { +FFMS_API(FFMS_Index *) FFMS_ReadIndex(const char *IndexFile, FFMS_ErrorInfo *ErrorInfo) { + ClearErrorInfo(ErrorInfo); + FFMS_Index *Index = new FFMS_Index(); + try { + Index->ReadIndex(IndexFile); + } catch (FFMS_Exception &e) { delete Index; + e.CopyOut(ErrorInfo); return NULL; - } else { - return Index; } + return Index; } -FFMS_API(int) FFMS_IndexBelongsToFile(FFIndex *Index, const char *SourceFile, char *ErrorMsg, unsigned MsgSize) { - return Index->CompareFileSignature(SourceFile, ErrorMsg, MsgSize); +FFMS_API(int) FFMS_IndexBelongsToFile(FFMS_Index *Index, const char *SourceFile, FFMS_ErrorInfo *ErrorInfo) { + ClearErrorInfo(ErrorInfo); + try { + if (!Index->CompareFileSignature(SourceFile)) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH, + "The index does not belong to the file"); + } catch (FFMS_Exception &e) { + return e.CopyOut(ErrorInfo); + } + return FFMS_ERROR_SUCCESS; } -FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { - return Index->WriteIndex(IndexFile, ErrorMsg, MsgSize); +FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo) { + ClearErrorInfo(ErrorInfo); + try { + Index->WriteIndex(IndexFile); + } catch (FFMS_Exception &e) { + return e.CopyOut(ErrorInfo); + } + return FFMS_ERROR_SUCCESS; } FFMS_API(int) FFMS_GetPixFmt(const char *Name) { return avcodec_get_pix_fmt(Name); } + +FFMS_API(int) FFMS_GetPresentSources() { + int Sources = FFMS_SOURCE_LAVF | FFMS_SOURCE_MATROSKA; +#ifdef HAALISOURCE + Sources |= FFMS_SOURCE_HAALIMPEG | FFMS_SOURCE_HAALIOGG; +#endif + return Sources; +} + +FFMS_API(int) FFMS_GetEnabledSources() { + if (!FFmpegInited) + return 0; + int Sources = FFMS_SOURCE_LAVF | FFMS_SOURCE_MATROSKA; + if (HasHaaliMPEG) + Sources |= FFMS_SOURCE_HAALIMPEG; + if (HasHaaliOGG) + Sources |= FFMS_SOURCE_HAALIOGG; + return Sources; +} diff --git a/aegisub/libffms/src/core/guids.h b/aegisub/libffms/src/core/guids.h index 3958aa328..5e4e0aa2a 100644 --- a/aegisub/libffms/src/core/guids.h +++ b/aegisub/libffms/src/core/guids.h @@ -116,10 +116,10 @@ DEFINE_GUID(MEDIASUBTYPE_WMV3, 0x33564d57, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); // FIXME: move somewhere else? -DEFINE_GUID(HAALI_TS_Parser, +DEFINE_GUID(HAALI_MPEG_PARSER, 0xB841F346, 0x4835, 0x4de8, 0xAA, 0x5E, 0x2E, 0x7C, 0xD2, 0xD4, 0xC4, 0x35); -DEFINE_GUID(HAALI_OGM_Parser, +DEFINE_GUID(HAALI_OGG_PARSER, 0xDB43B405, 0x43AA, 0x4F01, 0x82, 0xD8, 0xD8, 0x4D, 0x47, 0xE6, 0x01, 0x9C); //DB43B405-43AA-4F01-82D8-D84D47E6019C diff --git a/aegisub/libffms/src/core/ffhaaliaudio.cpp b/aegisub/libffms/src/core/haaliaudio.cpp similarity index 72% rename from aegisub/libffms/src/core/ffhaaliaudio.cpp rename to aegisub/libffms/src/core/haaliaudio.cpp index 45a1cb0f4..304cdbdb2 100644 --- a/aegisub/libffms/src/core/ffhaaliaudio.cpp +++ b/aegisub/libffms/src/core/haaliaudio.cpp @@ -20,7 +20,9 @@ #ifdef HAALISOURCE -#include "ffaudiosource.h" +#include "audiosource.h" + + void FFHaaliAudio::Free(bool CloseCodec) { if (CloseCodec) @@ -28,14 +30,14 @@ void FFHaaliAudio::Free(bool CloseCodec) { av_freep(&CodecContext); } -int FFHaaliAudio::DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count, char *ErrorMsg, unsigned MsgSize) { +void FFHaaliAudio::DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count) { const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; int Ret = -1; *AFirstStartTime = -1; *Count = 0; uint8_t *Buf = &DecodingBuffer[0]; AVPacket Packet; - InitNullPacket(&Packet); + InitNullPacket(Packet); for (;;) { CComPtr pMMF; @@ -79,56 +81,17 @@ int FFHaaliAudio::DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count, } } -Done: - return Ret; +Done:; } -FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index, - int SourceMode, char *ErrorMsg, unsigned MsgSize) - : FFAudio(SourceFile, Index, ErrorMsg, MsgSize) { +FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFMS_Index *Index, enum FFMS_Sources SourceMode) + : Res(FFSourceResources(this)), FFMS_AudioSource(SourceFile, Index, Track) { AVCodec *Codec = NULL; CodecContext = NULL; AudioTrack = Track; Frames = (*Index)[AudioTrack]; - if (Frames.size() == 0) { - snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?"); - throw ErrorMsg; - } - - CLSID clsid = HAALI_TS_Parser; - if (SourceMode == 1) - clsid = HAALI_OGM_Parser; - - if (FAILED(pMMC.CoCreateInstance(clsid))) { - snprintf(ErrorMsg, MsgSize, "Can't create parser"); - throw ErrorMsg; - } - - CComPtr pMA; - if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) { - snprintf(ErrorMsg, MsgSize, "Can't create memory allocator"); - throw ErrorMsg; - } - - CComPtr pMS; - if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) { - snprintf(ErrorMsg, MsgSize, "Can't create disk file reader"); - throw ErrorMsg; - } - - WCHAR WSourceFile[2048]; - ffms_mbstowcs(WSourceFile, SourceFile, 2000); - CComQIPtr pMSO(pMS); - if (FAILED(pMSO->Open(WSourceFile))) { - snprintf(ErrorMsg, MsgSize, "Can't open file"); - throw ErrorMsg; - } - - if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) { - snprintf(ErrorMsg, MsgSize, "Can't parse file"); - throw ErrorMsg; - } + pMMC = HaaliOpenFile(SourceFile, SourceMode); int CodecPrivateSize = 0; int CurrentTrack = 0; @@ -154,7 +117,13 @@ FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index, if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) { char ACodecID[2048]; wcstombs(ACodecID, pV.bstrVal, 2000); - Codec = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, FFMS_GET_VECTOR_PTR(CodecPrivate))); + + int BitDepth = 0; + pV.Clear(); + if (SUCCEEDED(pBag->Read(L"Audio.BitDepth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) + BitDepth = pV.uintVal; + + Codec = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, FFMS_GET_VECTOR_PTR(CodecPrivate), 0, BitDepth)); } } } @@ -166,45 +135,37 @@ FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index, CodecContext->extradata = FFMS_GET_VECTOR_PTR(CodecPrivate); CodecContext->extradata_size = CodecPrivateSize; - if (Codec == NULL) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Audio codec not found"); - throw ErrorMsg; - } + if (Codec == NULL) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Audio codec not found"); InitializeCodecContextFromHaaliInfo(pBag, CodecContext); - if (avcodec_open(CodecContext, Codec) < 0) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Could not open audio codec"); - throw ErrorMsg; - } + if (avcodec_open(CodecContext, Codec) < 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Could not open audio codec"); + + Res.CloseCodec(true); // Always try to decode a frame to make sure all required parameters are known int64_t Dummy1, Dummy2; - if (DecodeNextAudioBlock(&Dummy1, &Dummy2, ErrorMsg, MsgSize) < 0) { - Free(true); - throw ErrorMsg; - } + DecodeNextAudioBlock(&Dummy1, &Dummy2); + pMMC->Seek(Frames[0].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT); avcodec_flush_buffers(CodecContext); FillAP(AP, CodecContext, Frames); - if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) { - Free(true); - snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio"); - throw ErrorMsg; - } + if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Codec returned zero size audio"); AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50); } -FFHaaliAudio::~FFHaaliAudio() { - Free(true); -} +void FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) { + GetAudioCheck(Start, Count); -int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) { const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; memset(Buf, 0, static_cast(SizeConst * Count)); bool HasSeeked = false; @@ -216,7 +177,7 @@ int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorM int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf); // Was everything in the cache? if (CacheEnd == Start + Count) - return 0; + return; int CurrentAudioBlock; // Is seeking required to decode the requested samples? @@ -234,11 +195,7 @@ int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorM int64_t FirstTime, DecodeCount; do { - int Ret = DecodeNextAudioBlock(&FirstTime, &DecodeCount, ErrorMsg, MsgSize); - if (Ret < 0) { - // FIXME - //Env->ThrowError("Bleh, bad audio decoding"); - } + DecodeNextAudioBlock(&FirstTime, &DecodeCount); if (HasSeeked) { CurrentAudioBlock = Frames.ClosestFrameFromDTS(FirstTime); @@ -257,8 +214,6 @@ int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorM if (CurrentAudioBlock < static_cast(Frames.size())) CurrentSample = Frames[CurrentAudioBlock].SampleStart; } while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < static_cast(Frames.size())); - - return 0; } #endif // HAALISOURCE diff --git a/aegisub/libffms/src/core/ffhaaliindexer.cpp b/aegisub/libffms/src/core/haaliindexer.cpp similarity index 66% rename from aegisub/libffms/src/core/ffhaaliindexer.cpp rename to aegisub/libffms/src/core/haaliindexer.cpp index 2c8eaaa4d..92f6f0290 100644 --- a/aegisub/libffms/src/core/ffhaaliindexer.cpp +++ b/aegisub/libffms/src/core/haaliindexer.cpp @@ -24,51 +24,17 @@ -FFHaaliIndexer::FFHaaliIndexer(const char *Filename, int SourceMode, char *ErrorMsg, unsigned MsgSize) : FFIndexer(Filename, ErrorMsg, MsgSize) { - SourceFile = Filename; +FFHaaliIndexer::FFHaaliIndexer(const char *Filename, enum FFMS_Sources SourceMode) : FFMS_Indexer(Filename) { this->SourceMode = SourceMode; - memset(TrackType, FFMS_TYPE_UNKNOWN, sizeof(TrackType)); - memset(Codec, 0, sizeof(Codec)); - memset(CodecPrivate, 0, sizeof(CodecPrivate)); - memset(CodecPrivateSize, 0, sizeof(CodecPrivateSize)); + SourceFile = Filename; Duration = 0; - - CLSID clsid = HAALI_TS_Parser; - if (SourceMode == 1) - clsid = HAALI_OGM_Parser; - - if (FAILED(pMMC.CoCreateInstance(clsid))) { - snprintf(ErrorMsg, MsgSize, "Can't create parser"); - throw ErrorMsg; + for (int i = 0; i < 32; i++) { + TrackType[i] = FFMS_TYPE_UNKNOWN; + Codec[i] = NULL; + CodecPrivateSize[i] = 0; } - CComPtr pMA; - if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) { - snprintf(ErrorMsg, MsgSize, "Can't create memory allocator"); - throw ErrorMsg; - } - - CComPtr pMS; - if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) { - snprintf(ErrorMsg, MsgSize, "Can't create disk file reader"); - throw ErrorMsg; - } - - WCHAR WSourceFile[2048]; - ffms_mbstowcs(WSourceFile, SourceFile, 2000); - CComQIPtr pMSO(pMS); - if (FAILED(pMSO->Open(WSourceFile))) { - snprintf(ErrorMsg, MsgSize, "Can't open file"); - throw ErrorMsg; - } - - if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) { - if (SourceMode == 0) - snprintf(ErrorMsg, MsgSize, "Can't parse file, most likely a transport stream not cut at packet boundaries"); - else if (SourceMode == 1) - snprintf(ErrorMsg, MsgSize, "Can't parse file"); - throw ErrorMsg; - } + pMMC = HaaliOpenFile(SourceFile, SourceMode); CComQIPtr pBag2 = pMMC; CComVariant pV2; @@ -99,14 +65,42 @@ FFHaaliIndexer::FFHaaliIndexer(const char *Filename, int SourceMode, char *Error } pV.Clear(); - if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) + if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) { FourCC = pV.uintVal; + + // Reconstruct the missing codec private part for VC1 + std::vector bihvect; + bihvect.resize(sizeof(FFMS_BITMAPINFOHEADER)); + FFMS_BITMAPINFOHEADER *bih = reinterpret_cast(FFMS_GET_VECTOR_PTR(bihvect)); + memset(bih, 0, sizeof(FFMS_BITMAPINFOHEADER)); + bih->biSize = sizeof(FFMS_BITMAPINFOHEADER) + CodecPrivateSize[NumTracks]; + bih->biCompression = FourCC; + bih->biBitCount = 24; + bih->biPlanes = 1; + + pV.Clear(); + if (SUCCEEDED(pBag->Read(L"Video.PixelWidth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) + bih->biWidth = pV.uintVal; + + pV.Clear(); + if (SUCCEEDED(pBag->Read(L"Video.PixelHeight", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) + bih->biHeight = pV.uintVal; + + CodecPrivate[NumTracks].insert(CodecPrivate[NumTracks].begin(), bihvect.begin(), bihvect.end()); + CodecPrivateSize[NumTracks] += sizeof(FFMS_BITMAPINFOHEADER); + } pV.Clear(); if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) { - char CodecID[2048]; - wcstombs(CodecID, pV.bstrVal, 2000); - Codec[NumTracks] = avcodec_find_decoder(MatroskaToFFCodecID(CodecID, FFMS_GET_VECTOR_PTR(CodecPrivate[NumTracks]), FourCC)); + char CodecStr[2048]; + wcstombs(CodecStr, pV.bstrVal, 2000); + + int BitDepth = 0; + pV.Clear(); + if (SUCCEEDED(pBag->Read(L"Audio.BitDepth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) + BitDepth = pV.uintVal; + + Codec[NumTracks] = avcodec_find_decoder(MatroskaToFFCodecID(CodecStr, FFMS_GET_VECTOR_PTR(CodecPrivate[NumTracks]), FourCC, BitDepth)); } } @@ -116,17 +110,17 @@ FFHaaliIndexer::FFHaaliIndexer(const char *Filename, int SourceMode, char *Error } } -FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { +FFMS_Index *FFHaaliIndexer::DoIndexing() { std::vector AudioContexts(NumTracks, SharedAudioContext(true)); std::vector VideoContexts(NumTracks, SharedVideoContext(true)); - std::auto_ptr TrackIndices(new FFIndex(Filesize, Digest)); - TrackIndices->Decoder = 2; - if (SourceMode == 1) - TrackIndices->Decoder = 3; + std::auto_ptr TrackIndices(new FFMS_Index(Filesize, Digest)); + TrackIndices->Decoder = FFMS_SOURCE_HAALIMPEG; + if (SourceMode == FFMS_SOURCE_HAALIOGG) + TrackIndices->Decoder = FFMS_SOURCE_HAALIOGG; for (int i = 0; i < NumTracks; i++) { - TrackIndices->push_back(FFTrack(1, 1000000, TrackType[i])); + TrackIndices->push_back(FFMS_Track(1, 1000000, TrackType[i])); if (TrackType[i] == FFMS_TYPE_VIDEO && Codec[i] && (VideoContexts[i].Parser = av_parser_init(Codec[i]->id))) { @@ -138,8 +132,8 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { if (avcodec_open(CodecContext, Codec[i]) < 0) { av_freep(&CodecContext); - snprintf(ErrorMsg, MsgSize, "Could not open video codec"); - return NULL; + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, + "Could not open video codec"); } VideoContexts[i].CodecContext = CodecContext; @@ -147,10 +141,9 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { } if (IndexMask & (1 << i) && TrackType[i] == FFMS_TYPE_AUDIO) { - if (Codec[i] == NULL) { - snprintf(ErrorMsg, MsgSize, "Audio codec not found"); - return NULL; - } + if (Codec[i] == NULL) + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, + "Audio codec not found"); AVCodecContext *CodecContext = avcodec_alloc_context(); CodecContext->extradata = FFMS_GET_VECTOR_PTR(CodecPrivate[i]); @@ -160,8 +153,8 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { if (avcodec_open(CodecContext, Codec[i]) < 0) { av_freep(&CodecContext); AudioContexts[i].CodecContext = NULL; - snprintf(ErrorMsg, MsgSize, "Could not open audio codec"); - return NULL; + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, + "Could not open audio codec"); } } else { IndexMask &= ~(1 << i); @@ -170,7 +163,7 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { // AVPacket TempPacket; - InitNullPacket(&TempPacket); + InitNullPacket(TempPacket); for (;;) { CComPtr pMMF; @@ -183,16 +176,14 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { if (IC) { if (Duration > 0) { if (SUCCEEDED(hr)) { - if ((*IC)(Ts, Duration, ICPrivate)) { - snprintf(ErrorMsg, MsgSize, "Cancelled by user"); - return NULL; - } + if ((*IC)(Ts, Duration, ICPrivate)) + throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER, + "Cancelled by user"); } } else { - if ((*IC)(0, 1, ICPrivate)) { - snprintf(ErrorMsg, MsgSize, "Cancelled by user"); - return NULL; - } + if ((*IC)(0, 1, ICPrivate)) + throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER, + "Cancelled by user"); } } @@ -213,7 +204,7 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { (*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(Ts, RepeatPict, pMMF->IsSyncPoint() == S_OK)); } else if (TrackType[Track] == FFMS_TYPE_AUDIO && (IndexMask & (1 << Track))) { - (*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(Ts, AudioContexts[Track].CurrentSample, pMMF->IsSyncPoint() == S_OK)); + int64_t StartSample = AudioContexts[Track].CurrentSample; AVCodecContext *AudioCodecContext = AudioContexts[Track].CodecContext; if (pMMF->IsSyncPoint() == S_OK) @@ -225,13 +216,18 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket); if (Ret < 0) { - if (IgnoreDecodeErrors) { + if (ErrorHandling == FFMS_IEH_ABORT) { + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, + "Audio decoding error"); + } else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) { (*TrackIndices)[Track].clear(); IndexMask &= ~(1 << Track); break; - } else { - snprintf(ErrorMsg, MsgSize, "Audio decoding error"); - return NULL; + } else if (ErrorHandling == FFMS_IEH_STOP_TRACK) { + IndexMask &= ~(1 << Track); + break; + } else if (ErrorHandling == FFMS_IEH_IGNORE) { + break; } } @@ -244,8 +240,11 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels); if (DumpMask & (1 << Track)) - WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize, ErrorMsg, MsgSize); + WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize); } + + (*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(Ts, StartSample, + static_cast(AudioContexts[Track].CurrentSample - StartSample), pMMF->IsSyncPoint() == S_OK)); } } @@ -263,7 +262,7 @@ FFMS_TrackType FFHaaliIndexer::GetTrackType(int Track) { const char *FFHaaliIndexer::GetTrackCodec(int Track) { if (Codec[Track]) - return Codec[Track]->long_name; + return Codec[Track]->name; else return "Unsupported codec/Unknown codec name"; } diff --git a/aegisub/libffms/src/core/ffhaalivideo.cpp b/aegisub/libffms/src/core/haalivideo.cpp similarity index 67% rename from aegisub/libffms/src/core/ffhaalivideo.cpp rename to aegisub/libffms/src/core/haalivideo.cpp index f6ee50f6d..a63e8ada8 100644 --- a/aegisub/libffms/src/core/ffhaalivideo.cpp +++ b/aegisub/libffms/src/core/haalivideo.cpp @@ -18,10 +18,12 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include "ffvideosource.h" - #ifdef HAALISOURCE +#include "videosource.h" + + + void FFHaaliVideo::Free(bool CloseCodec) { if (CloseCodec) avcodec_close(CodecContext); @@ -31,9 +33,8 @@ void FFHaaliVideo::Free(bool CloseCodec) { } FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track, - FFIndex *Index, const char *PP, - int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize) - : FFVideo(SourceFile, Index, ErrorMsg, MsgSize) { + FFMS_Index *Index, int Threads, enum FFMS_Sources SourceMode) + : Res(FFSourceResources(this)), FFMS_VideoSource(SourceFile, Index, Track) { BitStreamFilter = NULL; AVCodec *Codec = NULL; @@ -41,44 +42,7 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track, VideoTrack = Track; Frames = (*Index)[VideoTrack]; - if (Frames.size() == 0) { - snprintf(ErrorMsg, MsgSize, "Video track contains no frames"); - throw ErrorMsg; - } - - CLSID clsid = HAALI_TS_Parser; - if (SourceMode == 1) - clsid = HAALI_OGM_Parser; - - if (FAILED(pMMC.CoCreateInstance(clsid))) { - snprintf(ErrorMsg, MsgSize, "Can't create parser"); - throw ErrorMsg; - } - - CComPtr pMA; - if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) { - snprintf(ErrorMsg, MsgSize, "Can't create memory allocator"); - throw ErrorMsg; - } - - CComPtr pMS; - if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) { - snprintf(ErrorMsg, MsgSize, "Can't create disk file reader"); - throw ErrorMsg; - } - - WCHAR WSourceFile[2048]; - ffms_mbstowcs(WSourceFile, SourceFile, 2000); - CComQIPtr pMSO(pMS); - if (FAILED(pMSO->Open(WSourceFile))) { - snprintf(ErrorMsg, MsgSize, "Can't open file"); - throw ErrorMsg; - } - - if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) { - snprintf(ErrorMsg, MsgSize, "Can't parse file"); - throw ErrorMsg; - } + pMMC = HaaliOpenFile(SourceFile, SourceMode); int CodecPrivateSize = 0; int CurrentTrack = 0; @@ -102,8 +66,30 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track, } pV.Clear(); - if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) + if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) { FourCC = pV.uintVal; + + // Reconstruct the missing codec private part for VC1 + std::vector bihvect; + bihvect.resize(sizeof(FFMS_BITMAPINFOHEADER)); + FFMS_BITMAPINFOHEADER *bih = reinterpret_cast(FFMS_GET_VECTOR_PTR(bihvect)); + memset(bih, 0, sizeof(FFMS_BITMAPINFOHEADER)); + bih->biSize = sizeof(FFMS_BITMAPINFOHEADER) + CodecPrivateSize; + bih->biCompression = FourCC; + bih->biBitCount = 24; + bih->biPlanes = 1; + + pV.Clear(); + if (SUCCEEDED(pBag->Read(L"Video.PixelWidth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) + bih->biWidth = pV.uintVal; + + pV.Clear(); + if (SUCCEEDED(pBag->Read(L"Video.PixelHeight", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) + bih->biHeight = pV.uintVal; + + CodecPrivate.insert(CodecPrivate.begin(), bihvect.begin(), bihvect.end()); + CodecPrivateSize += sizeof(FFMS_BITMAPINFOHEADER); + } pV.Clear(); if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) { @@ -122,51 +108,39 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track, CodecContext->extradata_size = CodecPrivateSize; CodecContext->thread_count = Threads; - if (Codec == NULL) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Video codec not found"); - throw ErrorMsg; - } + if (Codec == NULL) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Video codec not found"); InitializeCodecContextFromHaaliInfo(pBag, CodecContext); - if (avcodec_open(CodecContext, Codec) < 0) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Could not open video codec"); - throw ErrorMsg; - } + if (avcodec_open(CodecContext, Codec) < 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Could not open video codec"); - if (Codec->id == CODEC_ID_H264 && SourceMode == 0) + if (Codec->id == CODEC_ID_H264 && SourceMode == FFMS_SOURCE_HAALIMPEG) BitStreamFilter = av_bitstream_filter_init("h264_mp4toannexb"); + Res.CloseCodec(true); + // Always try to decode a frame to make sure all required parameters are known int64_t Dummy; - if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) { - Free(true); - throw ErrorMsg; - } + DecodeNextFrame(&Dummy); - VP.Width = CodecContext->width; - VP.Height = CodecContext->height;; VP.FPSDenominator = 1; VP.FPSNumerator = 30; VP.RFFDenominator = CodecContext->time_base.num; VP.RFFNumerator = CodecContext->time_base.den; VP.NumFrames = Frames.size(); - VP.VPixelFormat = CodecContext->pix_fmt; + VP.TopFieldFirst = DecodeFrame->top_field_first; + VP.ColorSpace = CodecContext->colorspace; + VP.ColorRange = CodecContext->color_range; VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; - if (VP.Width <= 0 || VP.Height <= 0) { - Free(true); - snprintf(ErrorMsg, MsgSize, "Codec returned zero size video"); - throw ErrorMsg; - } - - if (InitPP(PP, ErrorMsg, MsgSize)) { - Free(true); - throw ErrorMsg; - } + if (CodecContext->width <= 0 || CodecContext->height <= 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Codec returned zero size video"); // Calculate the average framerate if (Frames.size() >= 2) { @@ -176,10 +150,7 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track, } // Output the already decoded frame so it isn't wasted - if (!OutputFrame(DecodeFrame, ErrorMsg, MsgSize)) { - Free(true); - throw ErrorMsg; - } + OutputFrame(DecodeFrame); // Set AR variables CComVariant pV; @@ -192,15 +163,11 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track, VP.SARDen = pV.uiVal; } -FFHaaliVideo::~FFHaaliVideo() { - Free(true); -} - -int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) { +void FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime) { int FrameFinished = 0; *AFirstStartTime = -1; AVPacket Packet; - InitNullPacket(&Packet); + InitNullPacket(Packet); for (;;) { CComPtr pMMF; @@ -227,6 +194,9 @@ int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsi av_bitstream_filter_filter(BitStreamFilter, CodecContext, NULL, &Packet.data, &Packet.size, Data, pMMF->GetActualDataLength(), !!Packet.flags); + if (CodecContext->codec_id == CODEC_ID_MPEG4 && IsNVOP(Packet)) + goto Done; + avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet); if (FrameFinished) @@ -237,7 +207,7 @@ int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsi // Flush the last frames if (CodecContext->has_b_frames) { AVPacket NullPacket; - InitNullPacket(&NullPacket); + InitNullPacket(NullPacket); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket); } @@ -245,14 +215,14 @@ int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsi goto Error; Error: -Done: - return 0; +Done:; } -FFAVFrame *FFHaaliVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { - // PPFrame always holds frame LastFrameNum even if no PP is applied +FFMS_Frame *FFHaaliVideo::GetFrame(int n) { + GetFrameCheck(n); + if (LastFrameNum == n) - return OutputFrame(DecodeFrame, ErrorMsg, MsgSize); + return &LocalFrame; bool HasSeeked = false; int SeekOffset = 0; @@ -266,18 +236,16 @@ ReSeek: do { int64_t StartTime; - if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize)) - return NULL; + DecodeNextFrame(&StartTime); if (HasSeeked) { HasSeeked = false; if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) { // No idea where we are so go back a bit further - if (n + SeekOffset == 0) { - snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file\n"); - return NULL; - } + if (n + SeekOffset == 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Frame accurate seeking is not possible in this file"); SeekOffset -= FFMIN(20, n + SeekOffset); goto ReSeek; @@ -288,7 +256,7 @@ ReSeek: } while (CurrentFrame <= n); LastFrameNum = n; - return OutputFrame(DecodeFrame, ErrorMsg, MsgSize); + return OutputFrame(DecodeFrame); } #endif // HAALISOURCE diff --git a/aegisub/libffms/src/core/indexing.cpp b/aegisub/libffms/src/core/indexing.cpp index a8f2edcbb..a183b0a03 100644 --- a/aegisub/libffms/src/core/indexing.cpp +++ b/aegisub/libffms/src/core/indexing.cpp @@ -22,13 +22,15 @@ #include #include #include -#include extern "C" { #include } +#define INDEXID 0x53920873 +extern bool HasHaaliMPEG; +extern bool HasHaaliOGG; struct IndexHeader { uint32_t Id; @@ -44,6 +46,13 @@ struct IndexHeader { uint8_t FileSignature[20]; }; +struct TrackHeader { + uint32_t TT; + uint32_t Frames; + int64_t Num; + int64_t Den; +}; + SharedVideoContext::SharedVideoContext(bool FreeCodecContext) { CodecContext = NULL; Parser = NULL; @@ -85,47 +94,49 @@ SharedAudioContext::~SharedAudioContext() { cs_Destroy(CS); } -TFrameInfo::TFrameInfo(int64_t DTS, int64_t SampleStart, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) { +TFrameInfo::TFrameInfo() { +} + +TFrameInfo::TFrameInfo(int64_t DTS, int64_t SampleStart, unsigned int SampleCount, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) { this->DTS = DTS; this->RepeatPict = RepeatPict; this->KeyFrame = KeyFrame; this->SampleStart = SampleStart; + this->SampleCount = SampleCount; this->FilePos = FilePos; this->FrameSize = FrameSize; + this->OriginalPos = 0; } TFrameInfo TFrameInfo::VideoFrameInfo(int64_t DTS, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) { - return TFrameInfo(DTS, 0, RepeatPict, KeyFrame, FilePos, FrameSize); + return TFrameInfo(DTS, 0, 0, RepeatPict, KeyFrame, FilePos, FrameSize); } -TFrameInfo TFrameInfo::AudioFrameInfo(int64_t DTS, int64_t SampleStart, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) { - return TFrameInfo(DTS, SampleStart, 0, KeyFrame, FilePos, FrameSize); +TFrameInfo TFrameInfo::AudioFrameInfo(int64_t DTS, int64_t SampleStart, unsigned int SampleCount, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) { + return TFrameInfo(DTS, SampleStart, SampleCount, 0, KeyFrame, FilePos, FrameSize); } -int FFTrack::WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize) { +void FFMS_Track::WriteTimecodes(const char *TimecodeFile) { ffms_fstream Timecodes(TimecodeFile, std::ios::out | std::ios::trunc); - if (!Timecodes.is_open()) { - snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for writing", TimecodeFile); - return 1; - } + if (!Timecodes.is_open()) + throw FFMS_Exception(FFMS_ERROR_TRACK, FFMS_ERROR_FILE_WRITE, + boost::format("Failed to open '%1%' for writing") % TimecodeFile); Timecodes << "# timecode format v2\n"; for (iterator Cur=begin(); Cur!=end(); Cur++) Timecodes << std::fixed << ((Cur->DTS * TB.Num) / (double)TB.Den) << "\n"; - - return 0; } -int FFTrack::FrameFromDTS(int64_t DTS) { +int FFMS_Track::FrameFromDTS(int64_t DTS) { for (int i = 0; i < static_cast(size()); i++) if (at(i).DTS == DTS) return i; return -1; } -int FFTrack::ClosestFrameFromDTS(int64_t DTS) { +int FFMS_Track::ClosestFrameFromDTS(int64_t DTS) { int Frame = 0; int64_t BestDiff = 0xFFFFFFFFFFFFFFLL; // big number for (int i = 0; i < static_cast(size()); i++) { @@ -139,7 +150,7 @@ int FFTrack::ClosestFrameFromDTS(int64_t DTS) { return Frame; } -int FFTrack::FindClosestVideoKeyFrame(int Frame) { +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) @@ -147,7 +158,7 @@ int FFTrack::FindClosestVideoKeyFrame(int Frame) { return 0; } -int FFTrack::FindClosestAudioKeyFrame(int64_t Sample) { +int FFMS_Track::FindClosestAudioKeyFrame(int64_t Sample) { for (size_t i = 0; i < size(); i++) { if (at(i).SampleStart == Sample && at(i).KeyFrame) return i; @@ -157,29 +168,26 @@ int FFTrack::FindClosestAudioKeyFrame(int64_t Sample) { return size() - 1; } -FFTrack::FFTrack() { +FFMS_Track::FFMS_Track() { this->TT = FFMS_TYPE_UNKNOWN; this->TB.Num = 0; this->TB.Den = 0; } -FFTrack::FFTrack(int64_t Num, int64_t Den, FFMS_TrackType TT) { +FFMS_Track::FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT) { this->TT = TT; this->TB.Num = Num; this->TB.Den = Den; } -int FFIndex::CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20], char *ErrorMsg, unsigned MsgSize) { - // use cstdio because Microsoft's implementation of std::fstream doesn't support files >4GB. - // please kill me now. +void FFMS_Index::CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20]) { FILE *SFile = ffms_fopen(Filename,"rb"); - if (SFile == NULL) { - snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for hashing", Filename); - return 1; - } + if (SFile == NULL) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ, + boost::format("Failed to open '%1%' for hashing") % Filename); - const int BlockSize = 2*1024*1024; + const int BlockSize = 1024*1024; std::vector FileBuffer(BlockSize); std::vector ctxmem(av_sha1_size); AVSHA1 *ctx = (AVSHA1 *)&ctxmem[0]; @@ -188,10 +196,10 @@ int FFIndex::CalculateFileSignature(const char *Filename, int64_t *Filesize, uin memset(&FileBuffer[0], 0, BlockSize); fread(&FileBuffer[0], 1, BlockSize, SFile); if (ferror(SFile) && !feof(SFile)) { - snprintf(ErrorMsg, MsgSize, "Failed to read from '%s' for hashing", Filename); av_sha1_final(ctx, Digest); fclose(SFile); - return 1; + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ, + boost::format("Failed to read '%1%' for hashing") % Filename); } av_sha1_update(ctx, &FileBuffer[0], BlockSize); @@ -199,61 +207,67 @@ int FFIndex::CalculateFileSignature(const char *Filename, int64_t *Filesize, uin memset(&FileBuffer[0], 0, BlockSize); fread(&FileBuffer[0], 1, BlockSize, SFile); if (ferror(SFile) && !feof(SFile)) { - snprintf(ErrorMsg, MsgSize, "Failed to seek with offset %d from file end in '%s' for hashing", BlockSize, Filename); av_sha1_final(ctx, Digest); fclose(SFile); - return 1; + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ, + boost::format("Failed to seek with offset %1% from file end in '%2%' for hashing") % BlockSize % Filename); } av_sha1_update(ctx, &FileBuffer[0], BlockSize); fseeko(SFile, 0, SEEK_END); if (ferror(SFile)) { - snprintf(ErrorMsg, MsgSize, "Failed to seek to end of '%s' for hashing", Filename); av_sha1_final(ctx, Digest); fclose(SFile); - return 1; + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ, + boost::format("Failed to seek to end of '%1%' for hashing") % Filename); } *Filesize = ftello(SFile); fclose(SFile); av_sha1_final(ctx, Digest); - return 0; } static bool DTSComparison(TFrameInfo FI1, TFrameInfo FI2) { return FI1.DTS < FI2.DTS; } -void FFIndex::Sort() { - for (FFIndex::iterator Cur=begin(); Cur!=end(); Cur++) +void FFMS_Index::Sort() { + for (FFMS_Index::iterator Cur=begin(); Cur!=end(); Cur++) { + + for (size_t i = 0; i < Cur->size(); i++) + Cur->at(i).OriginalPos = i; + std::sort(Cur->begin(), Cur->end(), DTSComparison); + + std::vector ReorderTemp; + ReorderTemp.resize(Cur->size()); + + for (size_t i = 0; i < Cur->size(); i++) + ReorderTemp[i] = Cur->at(i).OriginalPos; + + for (size_t i = 0; i < Cur->size(); i++) + Cur->at(ReorderTemp[i]).OriginalPos = i; + } } -int FFIndex::CompareFileSignature(const char *Filename, char *ErrorMsg, unsigned MsgSize) { +bool FFMS_Index::CompareFileSignature(const char *Filename) { int64_t CFilesize; uint8_t CDigest[20]; - CalculateFileSignature(Filename, &CFilesize, CDigest, ErrorMsg, MsgSize); - - if (CFilesize != Filesize || memcmp(CDigest, Digest, sizeof(Digest))) { - snprintf(ErrorMsg, MsgSize, "Index and source file signature mismatch"); - return 1; - } - - return 0; + CalculateFileSignature(Filename, &CFilesize, CDigest); + return (CFilesize == Filesize && !memcmp(CDigest, Digest, sizeof(Digest))); } -int FFIndex::WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) { +void FFMS_Index::WriteIndex(const char *IndexFile) { ffms_fstream IndexStream(IndexFile, std::ios::out | std::ios::binary | std::ios::trunc); - if (!IndexStream.is_open()) { - snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for writing", IndexFile); - return 1; - } + if (!IndexStream.is_open()) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_WRITE, + boost::format("Failed to open '%1%' for writing") % IndexFile); // Write the index file header IndexHeader IH; IH.Id = INDEXID; - IH.Version = INDEXVERSION; + IH.Version = FFMS_VERSION; IH.Tracks = size(); IH.Decoder = Decoder; IH.LAVUVersion = avutil_version(); @@ -267,158 +281,152 @@ int FFIndex::WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) IndexStream.write(reinterpret_cast(&IH), sizeof(IH)); for (unsigned int i = 0; i < IH.Tracks; i++) { - uint32_t TT = at(i).TT; - IndexStream.write(reinterpret_cast(&TT), sizeof(TT)); - int64_t Num = at(i).TB.Num; - IndexStream.write(reinterpret_cast(&Num), sizeof(Num)); - int64_t Den = at(i).TB.Den; - IndexStream.write(reinterpret_cast(&Den), sizeof(Den)); - int64_t Frames = at(i).size(); - IndexStream.write(reinterpret_cast(&Frames), sizeof(Frames)); + TrackHeader TH; + TH.TT = at(i).TT; + TH.Frames = at(i).size(); + TH.Num = at(i).TB.Num;; + TH.Den = at(i).TB.Den; - for (FFTrack::iterator Cur=at(i).begin(); Cur!=at(i).end(); Cur++) - IndexStream.write(reinterpret_cast(&*Cur), sizeof(TFrameInfo)); + IndexStream.write(reinterpret_cast(&TH), sizeof(TH)); + IndexStream.write(reinterpret_cast(FFMS_GET_VECTOR_PTR(at(i))), TH.Frames * sizeof(TFrameInfo)); } - - return 0; } -int FFIndex::ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) { +void FFMS_Index::ReadIndex(const char *IndexFile) { ffms_fstream Index(IndexFile, std::ios::in | std::ios::binary); - if (!Index.is_open()) { - snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for reading", IndexFile); - return 1; - } + if (!Index.is_open()) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ, + boost::format("Failed to open '%1%' for reading") % IndexFile); // Read the index file header IndexHeader IH; Index.read(reinterpret_cast(&IH), sizeof(IH)); - if (IH.Id != INDEXID) { - snprintf(ErrorMsg, MsgSize, "'%s' is not a valid index file", IndexFile); - return 2; - } + if (IH.Id != INDEXID) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, + boost::format("'%1%' is not a valid index file") % IndexFile); - if (IH.Version != INDEXVERSION) { - snprintf(ErrorMsg, MsgSize, "'%s' is not the expected index version", IndexFile); - return 3; - } + if (IH.Version != FFMS_VERSION) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_VERSION, + boost::format("'%1%' is not the expected index version") % IndexFile); if (IH.LAVUVersion != avutil_version() || IH.LAVFVersion != avformat_version() || IH.LAVCVersion != avcodec_version() || IH.LSWSVersion != swscale_version() || - IH.LPPVersion != postproc_version()) { - snprintf(ErrorMsg, MsgSize, "A different FFmpeg build was used to create '%s'", IndexFile); - return 4; - } + IH.LPPVersion != postproc_version()) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_VERSION, + boost::format("A different FFmpeg build was used to create '%1%'") % IndexFile); + + if (!(IH.Decoder & FFMS_GetEnabledSources())) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE, + "The source which this index was created with is not available"); Decoder = IH.Decoder; Filesize = IH.FileSize; memcpy(Digest, IH.FileSignature, sizeof(Digest)); try { - for (unsigned int i = 0; i < IH.Tracks; i++) { - // Read how many records belong to the current stream - uint32_t TT; - Index.read(reinterpret_cast(&TT), sizeof(TT)); - int64_t Num; - Index.read(reinterpret_cast(&Num), sizeof(Num)); - int64_t Den; - Index.read(reinterpret_cast(&Den), sizeof(Den)); - int64_t Frames; - Index.read(reinterpret_cast(&Frames), sizeof(Frames)); - push_back(FFTrack(Num, Den, static_cast(TT))); + TrackHeader TH; + Index.read(reinterpret_cast(&TH), sizeof(TH)); + push_back(FFMS_Track(TH.Num, TH.Den, static_cast(TH.TT))); - TFrameInfo FI = TFrameInfo::VideoFrameInfo(0, 0, false); - for (size_t j = 0; j < Frames; j++) { - Index.read(reinterpret_cast(&FI), sizeof(TFrameInfo)); - at(i).push_back(FI); + if (TH.Frames) { + at(i).resize(TH.Frames); + Index.read(reinterpret_cast(FFMS_GET_VECTOR_PTR(at(i))), TH.Frames * sizeof(TFrameInfo)); } } } catch (...) { - snprintf(ErrorMsg, MsgSize, "Unknown error while reading index information in '%s'", IndexFile); - return 5; + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_UNKNOWN, + boost::format("Unknown error while reading index information in '%1%'") % IndexFile); } - - return 0; } -FFIndex::FFIndex() { +FFMS_Index::FFMS_Index() { // this comment documents nothing } -FFIndex::FFIndex(int64_t Filesize, uint8_t Digest[20]) { +FFMS_Index::FFMS_Index(int64_t Filesize, uint8_t Digest[20]) { this->Filesize = Filesize; memcpy(this->Digest, Digest, sizeof(this->Digest)); } -void FFIndexer::SetIndexMask(int IndexMask) { +void FFMS_Indexer::SetIndexMask(int IndexMask) { this->IndexMask = IndexMask; } -void FFIndexer::SetDumpMask(int DumpMask) { +void FFMS_Indexer::SetDumpMask(int DumpMask) { this->DumpMask = DumpMask; } -void FFIndexer::SetIgnoreDecodeErrors(bool IgnoreDecodeErrors) { - this->IgnoreDecodeErrors = IgnoreDecodeErrors; +void FFMS_Indexer::SetErrorHandling(int ErrorHandling) { + if (ErrorHandling != FFMS_IEH_ABORT && ErrorHandling != FFMS_IEH_CLEAR_TRACK && + ErrorHandling != FFMS_IEH_STOP_TRACK && ErrorHandling != FFMS_IEH_IGNORE) + throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_INVALID_ARGUMENT, + "Invalid error handling mode specified"); + this->ErrorHandling = ErrorHandling; } -void FFIndexer::SetProgressCallback(TIndexCallback IC, void *ICPrivate) { +void FFMS_Indexer::SetProgressCallback(TIndexCallback IC, void *ICPrivate) { this->IC = IC; this->ICPrivate = ICPrivate; } -void FFIndexer::SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate) { +void FFMS_Indexer::SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate) { this->ANC = ANC; this->ANCPrivate = ANCPrivate; } -FFIndexer *FFIndexer::CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) { +FFMS_Indexer *FFMS_Indexer::CreateIndexer(const char *Filename) { AVFormatContext *FormatContext = NULL; - if (av_open_input_file(&FormatContext, Filename, NULL, 0, NULL) != 0) { - snprintf(ErrorMsg, MsgSize, "Can't open '%s'", Filename); - return NULL; - } + if (av_open_input_file(&FormatContext, Filename, NULL, 0, NULL) != 0) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + boost::format("Can't open '%1%'") % Filename); // Do matroska indexing instead? if (!strcmp(FormatContext->iformat->name, "matroska")) { av_close_input_file(FormatContext); - return new FFMatroskaIndexer(Filename, ErrorMsg, MsgSize); + return new FFMatroskaIndexer(Filename); } #ifdef HAALISOURCE // Do haali ts indexing instead? - if (!strcmp(FormatContext->iformat->name, "mpeg") || !strcmp(FormatContext->iformat->name, "mpegts")) { + if (HasHaaliMPEG && (!strcmp(FormatContext->iformat->name, "mpeg") || !strcmp(FormatContext->iformat->name, "mpegts"))) { av_close_input_file(FormatContext); - return new FFHaaliIndexer(Filename, 0, ErrorMsg, MsgSize); + return new FFHaaliIndexer(Filename, FFMS_SOURCE_HAALIMPEG); } - if (!strcmp(FormatContext->iformat->name, "ogg")) { + if (HasHaaliOGG && !strcmp(FormatContext->iformat->name, "ogg")) { av_close_input_file(FormatContext); - return new FFHaaliIndexer(Filename, 1, ErrorMsg, MsgSize); + return new FFHaaliIndexer(Filename, FFMS_SOURCE_HAALIOGG); } #endif - return new FFLAVFIndexer(Filename, FormatContext, ErrorMsg, MsgSize); + return new FFLAVFIndexer(Filename, FormatContext); } -FFIndexer::FFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 5) { - if (FFIndex::CalculateFileSignature(Filename, &Filesize, Digest, ErrorMsg, MsgSize)) - throw ErrorMsg; +FFMS_Indexer::FFMS_Indexer(const char *Filename) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 5) { + IndexMask = 0; + DumpMask = 0; + ErrorHandling = FFMS_IEH_CLEAR_TRACK; + IC = NULL; + ICPrivate = NULL; + ANC = NULL; + ANCPrivate = NULL; + + FFMS_Index::CalculateFileSignature(Filename, &Filesize, Digest); } -FFIndexer::~FFIndexer() { +FFMS_Indexer::~FFMS_Indexer() { } -bool FFIndexer::WriteAudio(SharedAudioContext &AudioContext, FFIndex *Index, int Track, int DBSize, char *ErrorMsg, unsigned MsgSize) { +void FFMS_Indexer::WriteAudio(SharedAudioContext &AudioContext, FFMS_Index *Index, int Track, int DBSize) { // Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers. if (DBSize > 0) { if (!AudioContext.W64Writer) { - FFAudioProperties AP; + FFMS_AudioProperties AP; FillAP(AP, AudioContext.CodecContext, (*Index)[Track]); int FNSize = (*ANC)(SourceFile, Track, &AP, NULL, 0, ANCPrivate); std::vector WName(FNSize); @@ -428,13 +436,11 @@ bool FFIndexer::WriteAudio(SharedAudioContext &AudioContext, FFIndex *Index, int AudioContext.W64Writer = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioContext.CodecContext->sample_fmt), AudioContext.CodecContext->channels, AudioContext.CodecContext->sample_rate, (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_FLT) || (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_DBL)); } catch (...) { - snprintf(ErrorMsg, MsgSize, "Failed to write wave data"); - return false; + throw FFMS_Exception(FFMS_ERROR_WAVE_WRITER, FFMS_ERROR_FILE_WRITE, + "Failed to write wave data"); } } AudioContext.W64Writer->WriteData(&DecodingBuffer[0], DBSize); } - - return true; } diff --git a/aegisub/libffms/src/core/indexing.h b/aegisub/libffms/src/core/indexing.h index e7e70385d..c1e921922 100644 --- a/aegisub/libffms/src/core/indexing.h +++ b/aegisub/libffms/src/core/indexing.h @@ -37,8 +37,7 @@ # include "guids.h" #endif -#define INDEXVERSION 28 -#define INDEXID 0x53920873 + class SharedVideoContext { private: @@ -69,52 +68,55 @@ struct TFrameInfo { public: FFMS_FRAMEINFO_COMMON int64_t SampleStart; + unsigned int SampleCount; int64_t FilePos; unsigned int FrameSize; + size_t OriginalPos; + TFrameInfo(); static TFrameInfo VideoFrameInfo(int64_t DTS, int RepeatPict, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0); - static TFrameInfo AudioFrameInfo(int64_t DTS, int64_t SampleStart, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0); + static TFrameInfo AudioFrameInfo(int64_t DTS, int64_t SampleStart, unsigned int SampleCount, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0); private: - TFrameInfo(int64_t DTS, int64_t SampleStart, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize); + TFrameInfo(int64_t DTS, int64_t SampleStart, unsigned int SampleCount, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize); }; -class FFTrack : public std::vector { +class FFMS_Track : public std::vector { public: FFMS_TrackType TT; - FFTrackTimeBase TB; + FFMS_TrackTimeBase TB; int FindClosestVideoKeyFrame(int Frame); int FindClosestAudioKeyFrame(int64_t Sample); int FrameFromDTS(int64_t DTS); int ClosestFrameFromDTS(int64_t DTS); - int WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize); + void WriteTimecodes(const char *TimecodeFile); - FFTrack(); - FFTrack(int64_t Num, int64_t Den, FFMS_TrackType TT); + FFMS_Track(); + FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT); }; -class FFIndex : public std::vector { +class FFMS_Index : public std::vector { public: - static int CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20], char *ErrorMsg, unsigned MsgSize); + static void CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20]); int Decoder; int64_t Filesize; uint8_t Digest[20]; void Sort(); - int CompareFileSignature(const char *Filename, char *ErrorMsg, unsigned MsgSize); - int WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize); - int ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize); + bool CompareFileSignature(const char *Filename); + void WriteIndex(const char *IndexFile); + void ReadIndex(const char *IndexFile); - FFIndex(); - FFIndex(int64_t Filesize, uint8_t Digest[20]); + FFMS_Index(); + FFMS_Index(int64_t Filesize, uint8_t Digest[20]); }; -class FFIndexer { +class FFMS_Indexer { protected: int IndexMask; int DumpMask; - bool IgnoreDecodeErrors; + int ErrorHandling; TIndexCallback IC; void *ICPrivate; TAudioNameCallback ANC; @@ -125,43 +127,43 @@ protected: int64_t Filesize; uint8_t Digest[20]; - bool WriteAudio(SharedAudioContext &AudioContext, FFIndex *Index, int Track, int DBSize, char *ErrorMsg, unsigned MsgSize); + void WriteAudio(SharedAudioContext &AudioContext, FFMS_Index *Index, int Track, int DBSize); public: - static FFIndexer *CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize); - FFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize); - virtual ~FFIndexer(); + static FFMS_Indexer *CreateIndexer(const char *Filename); + FFMS_Indexer(const char *Filename); + virtual ~FFMS_Indexer(); void SetIndexMask(int IndexMask); void SetDumpMask(int DumpMask); - void SetIgnoreDecodeErrors(bool IgnoreDecodeErrors); + void SetErrorHandling(int ErrorHandling); void SetProgressCallback(TIndexCallback IC, void *ICPrivate); void SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate); - virtual FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize) = 0; + virtual FFMS_Index *DoIndexing() = 0; virtual int GetNumberOfTracks() = 0; virtual FFMS_TrackType GetTrackType(int Track) = 0; virtual const char *GetTrackCodec(int Track) = 0; }; -class FFLAVFIndexer : public FFIndexer { +class FFLAVFIndexer : public FFMS_Indexer { private: AVFormatContext *FormatContext; public: - FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize); + FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext); ~FFLAVFIndexer(); - FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize); + FFMS_Index *DoIndexing(); int GetNumberOfTracks(); FFMS_TrackType GetTrackType(int Track); const char *GetTrackCodec(int Track); }; -class FFMatroskaIndexer : public FFIndexer { +class FFMatroskaIndexer : public FFMS_Indexer { private: MatroskaFile *MF; MatroskaReaderContext MC; AVCodec *Codec[32]; public: - FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize); + FFMatroskaIndexer(const char *Filename); ~FFMatroskaIndexer(); - FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize); + FFMS_Index *DoIndexing(); int GetNumberOfTracks(); FFMS_TrackType GetTrackType(int Track); const char *GetTrackCodec(int Track); @@ -169,7 +171,7 @@ public: #ifdef HAALISOURCE -class FFHaaliIndexer : public FFIndexer { +class FFHaaliIndexer : public FFMS_Indexer { private: int SourceMode; CComPtr pMMC; @@ -181,8 +183,8 @@ private: CComQIPtr PropertyBags[32]; int64_t Duration; public: - FFHaaliIndexer(const char *Filename, int SourceMode, char *ErrorMsg, unsigned MsgSize); - FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize); + FFHaaliIndexer(const char *Filename, enum FFMS_Sources SourceMode); + FFMS_Index *DoIndexing(); int GetNumberOfTracks(); FFMS_TrackType GetTrackType(int Track); const char *GetTrackCodec(int Track); diff --git a/aegisub/libffms/src/core/fflavfaudio.cpp b/aegisub/libffms/src/core/lavfaudio.cpp similarity index 75% rename from aegisub/libffms/src/core/fflavfaudio.cpp rename to aegisub/libffms/src/core/lavfaudio.cpp index e030d3735..22eb470ce 100644 --- a/aegisub/libffms/src/core/fflavfaudio.cpp +++ b/aegisub/libffms/src/core/lavfaudio.cpp @@ -18,7 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include "ffaudiosource.h" +#include "audiosource.h" void FFLAVFAudio::Free(bool CloseCodec) { if (CloseCodec) @@ -27,75 +27,53 @@ void FFLAVFAudio::Free(bool CloseCodec) { av_close_input_file(FormatContext); } -FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, - char *ErrorMsg, unsigned MsgSize) - : FFAudio(SourceFile, Index, ErrorMsg, MsgSize){ +FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFMS_Index *Index) + : Res(FFSourceResources(this)), FFMS_AudioSource(SourceFile, Index, Track){ FormatContext = NULL; AVCodec *Codec = NULL; AudioTrack = Track; Frames = (*Index)[AudioTrack]; - if (Frames.size() == 0) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?"); - throw ErrorMsg; - } - - if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) { - snprintf(ErrorMsg, MsgSize, "Couldn't open '%s'", SourceFile); - throw ErrorMsg; - } - - if (av_find_stream_info(FormatContext) < 0) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Couldn't find stream information"); - throw ErrorMsg; - } + LAVFOpenFile(SourceFile, FormatContext); CodecContext = FormatContext->streams[AudioTrack]->codec; Codec = avcodec_find_decoder(CodecContext->codec_id); - if (Codec == NULL) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Audio codec not found"); - throw ErrorMsg; - } + if (Codec == NULL) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Audio codec not found"); - if (avcodec_open(CodecContext, Codec) < 0) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Could not open audio codec"); - throw ErrorMsg; - } + if (avcodec_open(CodecContext, Codec) < 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Could not open audio codec"); + + Res.CloseCodec(true); // Always try to decode a frame to make sure all required parameters are known int64_t Dummy; - if (DecodeNextAudioBlock(&Dummy, ErrorMsg, MsgSize) < 0) { - Free(true); - throw ErrorMsg; - } + DecodeNextAudioBlock(&Dummy); + if (av_seek_frame(FormatContext, AudioTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0) av_seek_frame(FormatContext, AudioTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY); avcodec_flush_buffers(CodecContext); FillAP(AP, CodecContext, Frames); - if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) { - Free(true); - snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio"); - throw ErrorMsg; - } + if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Codec returned zero size audio"); AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50); } -int FFLAVFAudio::DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned MsgSize) { +void FFLAVFAudio::DecodeNextAudioBlock(int64_t *Count) { const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; int Ret = -1; *Count = 0; uint8_t *Buf = &DecodingBuffer[0]; AVPacket Packet, TempPacket; - InitNullPacket(&Packet); - InitNullPacket(&TempPacket); + InitNullPacket(Packet); + InitNullPacket(TempPacket); while (av_read_frame(FormatContext, &Packet) >= 0) { if (Packet.stream_index == AudioTrack) { @@ -127,11 +105,12 @@ int FFLAVFAudio::DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned M av_free_packet(&Packet); } -Done: - return Ret; +Done:; } -int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) { +void FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) { + GetAudioCheck(Start, Count); + const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; memset(Buf, 0, static_cast(SizeConst * Count)); @@ -142,7 +121,7 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf); // Was everything in the cache? if (CacheEnd == Start + Count) - return 0; + return; size_t CurrentAudioBlock; // Is seeking required to decode the requested samples? @@ -158,7 +137,7 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs avcodec_flush_buffers(CodecContext); AVPacket Packet; - InitNullPacket(&Packet); + InitNullPacket(Packet); // Establish where we actually are // Trigger on packet dts difference since groups can otherwise be indistinguishable @@ -189,11 +168,7 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs int64_t DecodeCount; do { - int Ret = DecodeNextAudioBlock(&DecodeCount, ErrorMsg, MsgSize); - if (Ret < 0) { - // FIXME - //Env->ThrowError("Bleh, bad audio decoding"); - } + DecodeNextAudioBlock(&DecodeCount); // Cache the block if enough blocks before it have been decoded to avoid garbage if (PreDecBlocks == 0) { @@ -207,10 +182,4 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs if (CurrentAudioBlock < Frames.size()) CurrentSample = Frames[CurrentAudioBlock].SampleStart; } while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < Frames.size()); - - return 0; -} - -FFLAVFAudio::~FFLAVFAudio() { - Free(true); } diff --git a/aegisub/libffms/src/core/fflavfindexer.cpp b/aegisub/libffms/src/core/lavfindexer.cpp similarity index 73% rename from aegisub/libffms/src/core/fflavfindexer.cpp rename to aegisub/libffms/src/core/lavfindexer.cpp index d851eecee..fc65376de 100644 --- a/aegisub/libffms/src/core/fflavfindexer.cpp +++ b/aegisub/libffms/src/core/lavfindexer.cpp @@ -22,14 +22,14 @@ -FFLAVFIndexer::FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize) : FFIndexer(Filename, ErrorMsg, MsgSize) { +FFLAVFIndexer::FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext) : FFMS_Indexer(Filename) { SourceFile = Filename; this->FormatContext = FormatContext; if (av_find_stream_info(FormatContext) < 0) { av_close_input_file(FormatContext); - snprintf(ErrorMsg, MsgSize, "Couldn't find stream information"); - throw ErrorMsg; + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + "Couldn't find stream information"); } } @@ -37,15 +37,15 @@ FFLAVFIndexer::~FFLAVFIndexer() { av_close_input_file(FormatContext); } -FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { +FFMS_Index *FFLAVFIndexer::DoIndexing() { std::vector AudioContexts(FormatContext->nb_streams, SharedAudioContext(false)); std::vector VideoContexts(FormatContext->nb_streams, SharedVideoContext(false)); - std::auto_ptr TrackIndices(new FFIndex(Filesize, Digest)); - TrackIndices->Decoder = 0; + std::auto_ptr TrackIndices(new FFMS_Index(Filesize, Digest)); + TrackIndices->Decoder = FFMS_SOURCE_LAVF; for (unsigned int i = 0; i < FormatContext->nb_streams; i++) { - TrackIndices->push_back(FFTrack((int64_t)FormatContext->streams[i]->time_base.num * 1000, + TrackIndices->push_back(FFMS_Track((int64_t)FormatContext->streams[i]->time_base.num * 1000, FormatContext->streams[i]->time_base.den, static_cast(FormatContext->streams[i]->codec->codec_type))); @@ -53,15 +53,13 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { (VideoContexts[i].Parser = av_parser_init(FormatContext->streams[i]->codec->codec_id))) { AVCodec *VideoCodec = avcodec_find_decoder(FormatContext->streams[i]->codec->codec_id); - if (VideoCodec == NULL) { - snprintf(ErrorMsg, MsgSize, "Vudio codec not found"); - return NULL; - } + if (VideoCodec == NULL) + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, + "Video codec not found"); - if (avcodec_open(FormatContext->streams[i]->codec, VideoCodec) < 0) { - snprintf(ErrorMsg, MsgSize, "Could not open video codec"); - return NULL; - } + if (avcodec_open(FormatContext->streams[i]->codec, VideoCodec) < 0) + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, + "Could not open video codec"); VideoContexts[i].CodecContext = FormatContext->streams[i]->codec; VideoContexts[i].Parser->flags = PARSER_FLAG_COMPLETE_FRAMES; @@ -71,15 +69,13 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { AVCodecContext *AudioCodecContext = FormatContext->streams[i]->codec; AVCodec *AudioCodec = avcodec_find_decoder(AudioCodecContext->codec_id); - if (AudioCodec == NULL) { - snprintf(ErrorMsg, MsgSize, "Audio codec not found"); - return NULL; - } + if (AudioCodec == NULL) + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, + "Audio codec not found"); - if (avcodec_open(AudioCodecContext, AudioCodec) < 0) { - snprintf(ErrorMsg, MsgSize, "Could not open audio codec"); - return NULL; - } + if (avcodec_open(AudioCodecContext, AudioCodec) < 0) + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, + "Could not open audio codec"); AudioContexts[i].CodecContext = AudioCodecContext; } else { @@ -90,15 +86,14 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { // AVPacket Packet, TempPacket; - InitNullPacket(&Packet); - InitNullPacket(&TempPacket); + InitNullPacket(Packet); + InitNullPacket(TempPacket); while (av_read_frame(FormatContext, &Packet) >= 0) { // Update progress if (IC) { - if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, ICPrivate)) { - snprintf(ErrorMsg, MsgSize, "Cancelled by user"); - return NULL; - } + if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, ICPrivate)) + throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER, + "Cancelled by user"); } // Only create index entries for video for now to save space @@ -114,7 +109,7 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { (*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::VideoFrameInfo(Packet.dts, 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))) { - (*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::AudioFrameInfo(Packet.dts, AudioContexts[Packet.stream_index].CurrentSample, (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0)); + int64_t StartSample = AudioContexts[Packet.stream_index].CurrentSample; AVCodecContext *AudioCodecContext = FormatContext->streams[Packet.stream_index]->codec; TempPacket.data = Packet.data; TempPacket.size = Packet.size; @@ -124,13 +119,18 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket); if (Ret < 0) { - if (IgnoreDecodeErrors) { + if (ErrorHandling == FFMS_IEH_ABORT) { + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, + "Audio decoding error"); + } else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) { (*TrackIndices)[Packet.stream_index].clear(); - IndexMask &= ~(1 << Packet.stream_index); + IndexMask &= ~(1 << Packet.stream_index); + break; + } else if (ErrorHandling == FFMS_IEH_STOP_TRACK) { + IndexMask &= ~(1 << Packet.stream_index); + break; + } else if (ErrorHandling == FFMS_IEH_IGNORE) { break; - } else { - snprintf(ErrorMsg, MsgSize, "Audio decoding error"); - return NULL; } } @@ -143,8 +143,11 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { AudioContexts[Packet.stream_index].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels); if (DumpMask & (1 << Packet.stream_index)) - WriteAudio(AudioContexts[Packet.stream_index], TrackIndices.get(), Packet.stream_index, dbsize, ErrorMsg, MsgSize); + WriteAudio(AudioContexts[Packet.stream_index], TrackIndices.get(), Packet.stream_index, dbsize); } + + (*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::AudioFrameInfo(Packet.dts, StartSample, + static_cast(AudioContexts[Packet.stream_index].CurrentSample - StartSample), (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0)); } av_free_packet(&Packet); @@ -163,5 +166,5 @@ FFMS_TrackType FFLAVFIndexer::GetTrackType(int Track) { } const char *FFLAVFIndexer::GetTrackCodec(int Track) { - return (avcodec_find_decoder(FormatContext->streams[Track]->codec->codec_id))->long_name; + return (avcodec_find_decoder(FormatContext->streams[Track]->codec->codec_id))->name; } diff --git a/aegisub/libffms/src/core/fflavfvideo.cpp b/aegisub/libffms/src/core/lavfvideo.cpp similarity index 67% rename from aegisub/libffms/src/core/fflavfvideo.cpp rename to aegisub/libffms/src/core/lavfvideo.cpp index 805069e73..ab0be9b63 100644 --- a/aegisub/libffms/src/core/fflavfvideo.cpp +++ b/aegisub/libffms/src/core/lavfvideo.cpp @@ -18,7 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include "ffvideosource.h" +#include "videosource.h" @@ -28,9 +28,9 @@ void FFLAVFVideo::Free(bool CloseCodec) { av_close_input_file(FormatContext); } -FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index, - const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize) - : FFVideo(SourceFile, Index, ErrorMsg, MsgSize) { +FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index, + int Threads, int SeekMode) + : Res(FFSourceResources(this)), FFMS_VideoSource(SourceFile, Index, Track) { FormatContext = NULL; AVCodec *Codec = NULL; @@ -38,68 +38,45 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index, VideoTrack = Track; Frames = (*Index)[VideoTrack]; - if (Frames.size() == 0) { - snprintf(ErrorMsg, MsgSize, "Video track contains no frames"); - throw ErrorMsg; - } + LAVFOpenFile(SourceFile, FormatContext); - if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) { - snprintf(ErrorMsg, MsgSize, "Couldn't open '%s'", SourceFile); - throw ErrorMsg; - } - - if (av_find_stream_info(FormatContext) < 0) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Couldn't find stream information"); - throw ErrorMsg; - } - - if (SeekMode >= 0 && av_seek_frame(FormatContext, VideoTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Video track is unseekable"); - throw ErrorMsg; - } + if (SeekMode >= 0 && av_seek_frame(FormatContext, VideoTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Video track is unseekable"); CodecContext = FormatContext->streams[VideoTrack]->codec; CodecContext->thread_count = Threads; Codec = avcodec_find_decoder(CodecContext->codec_id); - if (Codec == NULL) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Video codec not found"); - throw ErrorMsg; - } + if (Codec == NULL) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Video codec not found"); - if (avcodec_open(CodecContext, Codec) < 0) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Could not open video codec"); - throw ErrorMsg; - } + if (avcodec_open(CodecContext, Codec) < 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Could not open video codec"); + + Res.CloseCodec(true); // Always try to decode a frame to make sure all required parameters are known int64_t Dummy; - if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) { - Free(true); - throw ErrorMsg; - } + DecodeNextFrame(&Dummy); //VP.image_type = VideoInfo::IT_TFF; - VP.Width = CodecContext->width; - VP.Height = CodecContext->height; VP.FPSDenominator = FormatContext->streams[VideoTrack]->time_base.num; VP.FPSNumerator = FormatContext->streams[VideoTrack]->time_base.den; VP.RFFDenominator = CodecContext->time_base.num; VP.RFFNumerator = CodecContext->time_base.den; VP.NumFrames = Frames.size(); - VP.VPixelFormat = CodecContext->pix_fmt; + VP.TopFieldFirst = DecodeFrame->top_field_first; + VP.ColorSpace = CodecContext->colorspace; + VP.ColorRange = CodecContext->color_range; VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; - if (VP.Width <= 0 || VP.Height <= 0) { - Free(true); - snprintf(ErrorMsg, MsgSize, "Codec returned zero size video"); - throw ErrorMsg; - } + if (CodecContext->width <= 0 || CodecContext->height <= 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Codec returned zero size video"); // sanity check framerate if (VP.FPSDenominator > VP.FPSNumerator || VP.FPSDenominator <= 0 || VP.FPSNumerator <= 0) { @@ -107,11 +84,6 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index, VP.FPSNumerator = 30; } - if (InitPP(PP, ErrorMsg, MsgSize)) { - Free(true); - throw ErrorMsg; - } - // Adjust framerate to match the duration of the first frame if (Frames.size() >= 2) { unsigned int DTSDiff = (unsigned int)FFMAX(Frames[1].DTS - Frames[0].DTS, 1); @@ -120,23 +92,16 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index, // Cannot "output" to PPFrame without doing all other initialization // This is the additional mess required for seekmode=-1 to work in a reasonable way - if (!OutputFrame(DecodeFrame, ErrorMsg, MsgSize)) { - Free(true); - throw ErrorMsg; - } + OutputFrame(DecodeFrame); // Set AR variables VP.SARNum = CodecContext->sample_aspect_ratio.num; VP.SARDen = CodecContext->sample_aspect_ratio.den; } -FFLAVFVideo::~FFLAVFVideo() { - Free(true); -} - -int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned MsgSize) { +void FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime) { AVPacket Packet; - InitNullPacket(&Packet); + InitNullPacket(Packet); int FrameFinished = 0; *AStartTime = -1; @@ -145,6 +110,11 @@ int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned M 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); } @@ -157,23 +127,22 @@ int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned M // Flush the last frames if (CodecContext->has_b_frames) { AVPacket NullPacket; - InitNullPacket(&NullPacket); + InitNullPacket(NullPacket); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket); } if (!FrameFinished) goto Error; -// Ignore errors for now Error: -Done: - return 0; +Done:; } -FFAVFrame *FFLAVFVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { - // PPFrame always holds frame LastFrameNum even if no PP is applied +FFMS_Frame *FFLAVFVideo::GetFrame(int n) { + GetFrameCheck(n); + if (LastFrameNum == n) - return OutputFrame(DecodeFrame, ErrorMsg, MsgSize); + return &LocalFrame; bool HasSeeked = false; int SeekOffset = 0; @@ -200,14 +169,13 @@ ReSeek: } } } else if (n < CurrentFrame) { - snprintf(ErrorMsg, MsgSize, "Non-linear access attempted"); - return NULL; + throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_INVALID_ARGUMENT, + "Non-linear access attempted"); } do { int64_t StartTime; - if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize)) - return NULL; + DecodeNextFrame(&StartTime); if (HasSeeked) { HasSeeked = false; @@ -217,10 +185,10 @@ ReSeek: switch (SeekMode) { case 1: // No idea where we are so go back a bit further - if (ClosestKF + SeekOffset == 0) { - snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file\n"); - return NULL; - } + if (ClosestKF + SeekOffset == 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Frame accurate seeking is not possible in this file"); + SeekOffset -= FFMIN(10, ClosestKF + SeekOffset); goto ReSeek; @@ -229,8 +197,8 @@ ReSeek: CurrentFrame = Frames.ClosestFrameFromDTS(StartTime); break; default: - snprintf(ErrorMsg, MsgSize, "Failed assertion"); - return NULL; + throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_UNKNOWN, + "Failed assertion"); } } } @@ -239,5 +207,5 @@ ReSeek: } while (CurrentFrame <= n); LastFrameNum = n; - return OutputFrame(DecodeFrame, ErrorMsg, MsgSize); + return OutputFrame(DecodeFrame); } diff --git a/aegisub/libffms/src/core/ffmatroskaaudio.cpp b/aegisub/libffms/src/core/matroskaaudio.cpp similarity index 62% rename from aegisub/libffms/src/core/ffmatroskaaudio.cpp rename to aegisub/libffms/src/core/matroskaaudio.cpp index 63659abd2..56e861da0 100644 --- a/aegisub/libffms/src/core/ffmatroskaaudio.cpp +++ b/aegisub/libffms/src/core/matroskaaudio.cpp @@ -18,7 +18,7 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include "ffaudiosource.h" +#include "audiosource.h" void FFMatroskaAudio::Free(bool CloseCodec) { if (CS) @@ -32,89 +32,74 @@ void FFMatroskaAudio::Free(bool CloseCodec) { av_freep(&CodecContext); } -FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, - FFIndex *Index, char *ErrorMsg, unsigned MsgSize) - : FFAudio(SourceFile, Index, ErrorMsg, MsgSize) { +FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index *Index) + : Res(FFSourceResources(this)), FFMS_AudioSource(SourceFile, Index, Track) { CodecContext = NULL; AVCodec *Codec = NULL; TrackInfo *TI = NULL; CS = NULL; + PacketNumber = 0; Frames = (*Index)[Track]; - if (Frames.size() == 0) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?"); - throw ErrorMsg; - } - MC.ST.fp = ffms_fopen(SourceFile, "rb"); - if (MC.ST.fp == NULL) { - snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno)); - throw ErrorMsg; - } + if (MC.ST.fp == NULL) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + boost::format("Can't open '%1%': %2%") % SourceFile % strerror(errno)); setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE); MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage)); if (MF == NULL) { fclose(MC.ST.fp); - snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage); - throw ErrorMsg; + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + boost::format("Can't parse Matroska file: %1%") % ErrorMessage); } - mkv_SetTrackMask(MF, ~(1 << Track)); + TI = mkv_GetTrackInfo(MF, Track); if (TI->CompEnabled) { CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage)); if (CS == NULL) { Free(false); - snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage); - throw ErrorMsg; + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, + boost::format("Can't create decompressor: %1%") % ErrorMessage); } } CodecContext = avcodec_alloc_context(); Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate)); - if (Codec == NULL) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Video codec not found"); - throw ErrorMsg; - } + if (Codec == NULL) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Audio codec not found"); InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext); - if (avcodec_open(CodecContext, Codec) < 0) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Could not open video codec"); - throw ErrorMsg; - } + if (avcodec_open(CodecContext, Codec) < 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Could not open audio codec"); + + Res.CloseCodec(true); // Always try to decode a frame to make sure all required parameters are known int64_t Dummy; - if (DecodeNextAudioBlock(&Dummy, 0, ErrorMsg, MsgSize) < 0) { - Free(true); - throw ErrorMsg; - } + DecodeNextAudioBlock(&Dummy); + avcodec_flush_buffers(CodecContext); FillAP(AP, CodecContext, Frames); - if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) { - Free(true); - snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio"); - throw ErrorMsg; - } + if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Codec returned zero size audio"); AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50); } -FFMatroskaAudio::~FFMatroskaAudio() { - Free(true); -} +void FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) { + GetAudioCheck(Start, Count); -int FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) { const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; memset(Buf, 0, static_cast(SizeConst * Count)); @@ -125,58 +110,50 @@ int FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *Err int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf); // Was everything in the cache? if (CacheEnd == Start + Count) - return 0; + return; - int CurrentAudioBlock; // Is seeking required to decode the requested samples? // if (!(CurrentSample >= Start && CurrentSample <= CacheEnd)) { if (CurrentSample != CacheEnd) { PreDecBlocks = 15; - CurrentAudioBlock = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks, (int64_t)0); + PacketNumber = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks, (int64_t)0);; avcodec_flush_buffers(CodecContext); - } else { - CurrentAudioBlock = Frames.FindClosestAudioKeyFrame(CurrentSample); - } + } int64_t DecodeCount; do { - int Ret = DecodeNextAudioBlock(&DecodeCount, CurrentAudioBlock, ErrorMsg, MsgSize); - if (Ret < 0) { - // FIXME - //Env->ThrowError("Bleh, bad audio decoding"); - } + const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos]; + DecodeNextAudioBlock(&DecodeCount); // Cache the block if enough blocks before it have been decoded to avoid garbage if (PreDecBlocks == 0) { - AudioCache.CacheBlock(Frames[CurrentAudioBlock].SampleStart, DecodeCount, &DecodingBuffer[0]); + AudioCache.CacheBlock(FI.SampleStart, DecodeCount, &DecodingBuffer[0]); CacheEnd = AudioCache.FillRequest(CacheEnd, Start + Count - CacheEnd, DstBuf + (CacheEnd - Start) * SizeConst); } else { PreDecBlocks--; } - CurrentAudioBlock++; - if (CurrentAudioBlock < static_cast(Frames.size())) - CurrentSample = Frames[CurrentAudioBlock].SampleStart; - } while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < static_cast(Frames.size())); - - return 0; + } while (Start + Count - CacheEnd > 0 && PacketNumber < Frames.size()); } -int FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count, int AudioBlock, char *ErrorMsg, unsigned MsgSize) { +void FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count) { const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; int Ret = -1; *Count = 0; uint8_t *Buf = &DecodingBuffer[0]; AVPacket TempPacket; - InitNullPacket(&TempPacket); + InitNullPacket(TempPacket); - unsigned int FrameSize = Frames[AudioBlock].FrameSize; - if (ReadFrame(Frames[AudioBlock].FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize)) - return 1; + const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos]; + unsigned int FrameSize = FI.FrameSize; + CurrentSample = FI.SampleStart + FI.SampleCount; + PacketNumber++; + + ReadFrame(FI.FilePos, FrameSize, CS, MC); TempPacket.data = MC.Buffer; TempPacket.size = FrameSize; - if (Frames[AudioBlock].KeyFrame) + if (FI.KeyFrame) TempPacket.flags = AV_PKT_FLAG_KEY; else TempPacket.flags = 0; @@ -197,6 +174,5 @@ int FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count, int AudioBlock, char * } } -Done: - return 0; +Done:; } diff --git a/aegisub/libffms/src/core/ffmatroskaindexer.cpp b/aegisub/libffms/src/core/matroskaindexer.cpp similarity index 71% rename from aegisub/libffms/src/core/ffmatroskaindexer.cpp rename to aegisub/libffms/src/core/matroskaindexer.cpp index 86bd8b5a5..d9087d9d4 100644 --- a/aegisub/libffms/src/core/ffmatroskaindexer.cpp +++ b/aegisub/libffms/src/core/matroskaindexer.cpp @@ -23,25 +23,26 @@ -FFMatroskaIndexer::FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) : FFIndexer(Filename, ErrorMsg, MsgSize) { - memset(Codec, 0, sizeof(Codec)); +FFMatroskaIndexer::FFMatroskaIndexer(const char *Filename) : FFMS_Indexer(Filename) { SourceFile = Filename; char ErrorMessage[256]; + for (int i = 0; i < 32; i++) { + Codec[i] = NULL; + } InitStdIoStream(&MC.ST); MC.ST.fp = ffms_fopen(SourceFile, "rb"); - if (MC.ST.fp == NULL) { - snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno)); - throw ErrorMsg; - } + if (MC.ST.fp == NULL) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + boost::format("Can't open '%1%': %2%") % SourceFile % strerror(errno)); setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE); MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage)); if (MF == NULL) { fclose(MC.ST.fp); - snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage); - throw ErrorMsg; + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + boost::format("Can't parse Matroska file: %1%") % ErrorMessage); } for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) { @@ -55,17 +56,17 @@ FFMatroskaIndexer::~FFMatroskaIndexer() { fclose(MC.ST.fp); } -FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { +FFMS_Index *FFMatroskaIndexer::DoIndexing() { char ErrorMessage[256]; std::vector AudioContexts(mkv_GetNumTracks(MF), SharedAudioContext(true)); std::vector VideoContexts(mkv_GetNumTracks(MF), SharedVideoContext(true)); - std::auto_ptr TrackIndices(new FFIndex(Filesize, Digest)); - TrackIndices->Decoder = 1; + std::auto_ptr TrackIndices(new FFMS_Index(Filesize, Digest)); + TrackIndices->Decoder = FFMS_SOURCE_MATROSKA; for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) { TrackInfo *TI = mkv_GetTrackInfo(MF, i); - TrackIndices->push_back(FFTrack(mkv_TruncFloat(mkv_GetTrackInfo(MF, i)->TimecodeScale), 1000000, HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, i)->Type))); + TrackIndices->push_back(FFMS_Track(mkv_TruncFloat(mkv_GetTrackInfo(MF, i)->TimecodeScale), 1000000, HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, i)->Type))); if (HaaliTrackTypeToFFTrackType(TI->Type) == FFMS_TYPE_VIDEO && Codec[i] && (VideoContexts[i].Parser = av_parser_init(Codec[i]->id))) { @@ -75,16 +76,15 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { if (avcodec_open(CodecContext, Codec[i]) < 0) { av_freep(&CodecContext); - snprintf(ErrorMsg, MsgSize, "Could not open video codec"); - return NULL; + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, + "Could not open video codec"); } if (TI->CompEnabled) { VideoContexts[i].CS = cs_Create(MF, i, ErrorMessage, sizeof(ErrorMessage)); - if (VideoContexts[i].CS == NULL) { - snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage); - return NULL; - } + if (VideoContexts[i].CS == NULL) + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, + boost::format("Can't create decompressor: %1%") % ErrorMessage); } VideoContexts[i].CodecContext = CodecContext; @@ -102,8 +102,8 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { if (AudioContexts[i].CS == NULL) { av_freep(&AudioCodecContext); AudioContexts[i].CodecContext = NULL; - snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage); - return NULL; + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, + boost::format("Can't create decompressor: %1%") % ErrorMessage); } } @@ -111,15 +111,15 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { if (AudioCodec == NULL) { av_freep(&AudioCodecContext); AudioContexts[i].CodecContext = NULL; - snprintf(ErrorMsg, MsgSize, "Audio codec not found"); - return NULL; + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, + "Audio codec not found"); } if (avcodec_open(AudioCodecContext, AudioCodec) < 0) { av_freep(&AudioCodecContext); AudioContexts[i].CodecContext = NULL; - snprintf(ErrorMsg, MsgSize, "Could not open audio codec"); - return NULL; + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, + "Could not open audio codec"); } } else { IndexMask &= ~(1 << i); @@ -131,15 +131,14 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { ulonglong StartTime, EndTime, FilePos; unsigned int Track, FrameFlags, FrameSize; AVPacket TempPacket; - InitNullPacket(&TempPacket); + InitNullPacket(TempPacket); while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) { // Update progress if (IC) { - if ((*IC)(ftello(MC.ST.fp), Filesize, ICPrivate)) { - snprintf(ErrorMsg, MsgSize, "Cancelled by user"); - return NULL; - } + if ((*IC)(ftello(MC.ST.fp), Filesize, ICPrivate)) + throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER, + "Cancelled by user"); } // Only create index entries for video for now to save space @@ -155,9 +154,10 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { (*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(StartTime, RepeatPict, (FrameFlags & FRAME_KF) != 0, FilePos, FrameSize)); } else if (mkv_GetTrackInfo(MF, Track)->Type == TT_AUDIO && (IndexMask & (1 << Track))) { - (*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(StartTime, AudioContexts[Track].CurrentSample, (FrameFlags & FRAME_KF) != 0, FilePos, FrameSize)); - ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC, ErrorMsg, MsgSize); + int64_t StartSample = AudioContexts[Track].CurrentSample; + unsigned int CompressedFrameSize = FrameSize; AVCodecContext *AudioCodecContext = AudioContexts[Track].CodecContext; + ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC); TempPacket.data = MC.Buffer; TempPacket.size = FrameSize; if ((FrameFlags & FRAME_KF) != 0) @@ -169,13 +169,18 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket); if (Ret < 0) { - if (IgnoreDecodeErrors) { + if (ErrorHandling == FFMS_IEH_ABORT) { + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING, + "Audio decoding error"); + } else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) { (*TrackIndices)[Track].clear(); IndexMask &= ~(1 << Track); break; - } else { - snprintf(ErrorMsg, MsgSize, "Audio decoding error"); - return NULL; + } else if (ErrorHandling == FFMS_IEH_STOP_TRACK) { + IndexMask &= ~(1 << Track); + break; + } else if (ErrorHandling == FFMS_IEH_IGNORE) { + break; } } @@ -188,8 +193,11 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels); if (DumpMask & (1 << Track)) - WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize, ErrorMsg, MsgSize); + WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize); } + + (*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(StartTime, StartSample, + static_cast(AudioContexts[Track].CurrentSample - StartSample), (FrameFlags & FRAME_KF) != 0, FilePos, CompressedFrameSize)); } } @@ -207,7 +215,7 @@ FFMS_TrackType FFMatroskaIndexer::GetTrackType(int Track) { const char *FFMatroskaIndexer::GetTrackCodec(int Track) { if (Codec[Track]) - return Codec[Track]->long_name; + return Codec[Track]->name; else return "Unsupported codec/Unknown codec name"; } diff --git a/aegisub/libffms/src/core/ffmatroskavideo.cpp b/aegisub/libffms/src/core/matroskavideo.cpp similarity index 58% rename from aegisub/libffms/src/core/ffmatroskavideo.cpp rename to aegisub/libffms/src/core/matroskavideo.cpp index db428da91..ede35a6a8 100644 --- a/aegisub/libffms/src/core/ffmatroskavideo.cpp +++ b/aegisub/libffms/src/core/matroskavideo.cpp @@ -18,7 +18,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include "ffvideosource.h" +#include "videosource.h" + + void FFMatroskaVideo::Free(bool CloseCodec) { if (CS) @@ -33,46 +35,39 @@ void FFMatroskaVideo::Free(bool CloseCodec) { } FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track, - FFIndex *Index, const char *PP, - int Threads, char *ErrorMsg, unsigned MsgSize) - : FFVideo(SourceFile, Index, ErrorMsg, MsgSize) { + FFMS_Index *Index, int Threads) + : Res(FFSourceResources(this)), FFMS_VideoSource(SourceFile, Index, Track) { AVCodec *Codec = NULL; CodecContext = NULL; TrackInfo *TI = NULL; CS = NULL; + PacketNumber = 0; VideoTrack = Track; Frames = (*Index)[VideoTrack]; - if (Frames.size() == 0) { - snprintf(ErrorMsg, MsgSize, "Video track contains no frames"); - throw ErrorMsg; - } - MC.ST.fp = ffms_fopen(SourceFile, "rb"); - if (MC.ST.fp == NULL) { - snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno)); - throw ErrorMsg; - } + if (MC.ST.fp == NULL) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + boost::format("Can't open '%1%': %2%") % SourceFile % strerror(errno)); setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE); MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage)); if (MF == NULL) { fclose(MC.ST.fp); - snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage); - throw ErrorMsg; + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + boost::format("Can't parse Matroska file: %1%") % ErrorMessage); } - mkv_SetTrackMask(MF, ~(1 << VideoTrack)); TI = mkv_GetTrackInfo(MF, VideoTrack); if (TI->CompEnabled) { CS = cs_Create(MF, VideoTrack, ErrorMessage, sizeof(ErrorMessage)); if (CS == NULL) { Free(false); - snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage); - throw ErrorMsg; + throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED, + boost::format("Can't create decompressor: %1%") % ErrorMessage); } } @@ -80,48 +75,35 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track, CodecContext->thread_count = Threads; Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate)); - if (Codec == NULL) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Video codec not found"); - throw ErrorMsg; - } + if (Codec == NULL) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Video codec not found"); InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext); - if (avcodec_open(CodecContext, Codec) < 0) { - Free(false); - snprintf(ErrorMsg, MsgSize, "Could not open video codec"); - throw ErrorMsg; - } + if (avcodec_open(CodecContext, Codec) < 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Could not open video codec"); + + Res.CloseCodec(true); // Always try to decode a frame to make sure all required parameters are known - int64_t Dummy; - if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) { - Free(true); - throw ErrorMsg; - } + DecodeNextFrame(); - VP.Width = CodecContext->width; - VP.Height = CodecContext->height;; VP.FPSDenominator = 1; VP.FPSNumerator = 30; VP.RFFDenominator = CodecContext->time_base.num; VP.RFFNumerator = CodecContext->time_base.den; VP.NumFrames = Frames.size(); - VP.VPixelFormat = CodecContext->pix_fmt; + VP.TopFieldFirst = DecodeFrame->top_field_first; + VP.ColorSpace = CodecContext->colorspace; + VP.ColorRange = CodecContext->color_range; VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; - if (VP.Width <= 0 || VP.Height <= 0) { - Free(true); - snprintf(ErrorMsg, MsgSize, "Codec returned zero size video"); - throw ErrorMsg; - } - - if (InitPP(PP, ErrorMsg, MsgSize)) { - Free(true); - throw ErrorMsg; - } + if (CodecContext->width <= 0 || CodecContext->height <= 0) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC, + "Codec returned zero size video"); // Calculate the average framerate if (Frames.size() >= 2) { @@ -131,10 +113,7 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track, } // Output the already decoded frame so it isn't wasted - if (!OutputFrame(DecodeFrame, ErrorMsg, MsgSize)) { - Free(true); - throw ErrorMsg; - } + OutputFrame(DecodeFrame); // Set AR variables VP.SARNum = TI->AV.Video.DisplayWidth * TI->AV.Video.PixelHeight; @@ -147,33 +126,33 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track, VP.CropBottom = TI->AV.Video.CropB; } -FFMatroskaVideo::~FFMatroskaVideo() { - Free(true); -} - -int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) { +void FFMatroskaVideo::DecodeNextFrame() { int FrameFinished = 0; - *AFirstStartTime = -1; AVPacket Packet; - InitNullPacket(&Packet); + InitNullPacket(Packet); - ulonglong StartTime, EndTime, FilePos; - unsigned int Track, FrameFlags, FrameSize; + unsigned int FrameSize; - while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) { - if (*AFirstStartTime < 0) - *AFirstStartTime = StartTime; - - if (ReadFrame(FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize)) - return 1; + while (PacketNumber < Frames.size()) { + // The additional indirection is because the packets are stored in + // presentation order and not decoding order, this is unnoticable + // in the other sources where less is done manually + const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos]; + FrameSize = FI.FrameSize; + ReadFrame(FI.FilePos, FrameSize, CS, MC); Packet.data = MC.Buffer; Packet.size = FrameSize; - if (FrameFlags & FRAME_KF) + if (FI.KeyFrame) Packet.flags = AV_PKT_FLAG_KEY; else Packet.flags = 0; + PacketNumber++; + + if (CodecContext->codec_id == CODEC_ID_MPEG4 && IsNVOP(Packet)) + goto Done; + avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet); if (FrameFinished) @@ -183,7 +162,7 @@ int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, u // Flush the last frames if (CodecContext->has_b_frames) { AVPacket NullPacket; - InitNullPacket(&NullPacket); + InitNullPacket(NullPacket); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket); } @@ -191,40 +170,27 @@ int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, u goto Error; Error: -Done: - return 0; +Done:; } -FFAVFrame *FFMatroskaVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { - // PPFrame always holds frame LastFrameNum even if no PP is applied +FFMS_Frame *FFMatroskaVideo::GetFrame(int n) { + GetFrameCheck(n); + if (LastFrameNum == n) - return OutputFrame(DecodeFrame, ErrorMsg, MsgSize); + return &LocalFrame; - bool HasSeeked = false; - - if (n < CurrentFrame || Frames.FindClosestVideoKeyFrame(n) > CurrentFrame) { - mkv_Seek(MF, Frames[n].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT); + int ClosestKF = Frames.FindClosestVideoKeyFrame(n); + if (CurrentFrame > n || ClosestKF > CurrentFrame + 10) { + PacketNumber = ClosestKF; + CurrentFrame = ClosestKF; avcodec_flush_buffers(CodecContext); - HasSeeked = true; } do { - int64_t StartTime; - if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize)) - return NULL; - - if (HasSeeked) { - HasSeeked = false; - - if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) { - snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file"); - return NULL; - } - } - + DecodeNextFrame(); CurrentFrame++; } while (CurrentFrame <= n); LastFrameNum = n; - return OutputFrame(DecodeFrame, ErrorMsg, MsgSize); + return OutputFrame(DecodeFrame); } diff --git a/aegisub/libffms/src/core/stdiostream.c b/aegisub/libffms/src/core/stdiostream.c index c61058971..9fdde8b15 100644 --- a/aegisub/libffms/src/core/stdiostream.c +++ b/aegisub/libffms/src/core/stdiostream.c @@ -104,13 +104,13 @@ longlong StdIoGetFileSize(StdIoStream *st) { void InitStdIoStream(StdIoStream *st) { memset(st,0,sizeof(StdIoStream)); - st->base.read = StdIoRead; - st->base.scan = StdIoScan; - st->base.getcachesize = StdIoGetCacheSize; - st->base.geterror = StdIoGetLastError; - st->base.memalloc = StdIoMalloc; - st->base.memrealloc = StdIoRealloc; - st->base.memfree = StdIoFree; - st->base.progress = StdIoProgress; - st->base.getfilesize = StdIoGetFileSize; + st->base.read = (int (*)(InputStream *,ulonglong,void *,int))StdIoRead; + st->base.scan = (longlong (*)(InputStream *,ulonglong,unsigned int))StdIoScan; + st->base.getcachesize = (unsigned int (*)(InputStream *))StdIoGetCacheSize; + st->base.geterror = (const char *(*)(InputStream *))StdIoGetLastError; + st->base.memalloc = (void *(*)(InputStream *,size_t))StdIoMalloc; + st->base.memrealloc = (void *(*)(InputStream *,void *,size_t))StdIoRealloc; + st->base.memfree = (void (*)(InputStream *,void *))StdIoFree; + st->base.progress = (int (*)(InputStream *,ulonglong,ulonglong))StdIoProgress; + st->base.getfilesize = (longlong (*)(InputStream *))StdIoGetFileSize; } diff --git a/aegisub/libffms/src/core/utils.cpp b/aegisub/libffms/src/core/utils.cpp index 5922216c1..6566b1b53 100644 --- a/aegisub/libffms/src/core/utils.cpp +++ b/aegisub/libffms/src/core/utils.cpp @@ -46,6 +46,38 @@ extern const AVCodecTag ff_codec_wav_tags[]; extern int CPUFeatures; + + +FFMS_Exception::FFMS_Exception(int ErrorType, int SubType, const char *Message) : _ErrorType(ErrorType), _SubType(SubType), _Message(Message) { +} + +FFMS_Exception::FFMS_Exception(int ErrorType, int SubType, const std::string &Message) : _ErrorType(ErrorType), _SubType(SubType), _Message(Message) { +} + +FFMS_Exception::FFMS_Exception(int ErrorType, int SubType, const boost::format &Message) : _ErrorType(ErrorType), _SubType(SubType), _Message(Message.str()) { +} + +FFMS_Exception::~FFMS_Exception() throw () { +} + +const std::string &FFMS_Exception::GetErrorMessage() const { + return _Message; +} + +int FFMS_Exception::CopyOut(FFMS_ErrorInfo *ErrorInfo) const { + if (ErrorInfo) { + ErrorInfo->ErrorType = _ErrorType; + ErrorInfo->SubType = _SubType; + + if (ErrorInfo->BufferSize > 0) { + memset(ErrorInfo->Buffer, 0, ErrorInfo->BufferSize); + _Message.copy(ErrorInfo->Buffer, ErrorInfo->BufferSize - 1); + } + } + + return (_ErrorType << 16) | _SubType; +} + int GetSWSCPUFlags() { int Flags = 0; @@ -78,6 +110,16 @@ int GetPPCPUFlags() { return Flags; } +void ClearErrorInfo(FFMS_ErrorInfo *ErrorInfo) { + if (ErrorInfo) { + ErrorInfo->ErrorType = FFMS_ERROR_SUCCESS; + ErrorInfo->SubType = FFMS_ERROR_SUCCESS; + + if (ErrorInfo->BufferSize > 0) + ErrorInfo->Buffer[0] = 0; + } +} + FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT) { switch (TT) { case TT_VIDEO: return FFMS_TYPE_VIDEO; break; @@ -87,90 +129,92 @@ FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT) { } } -int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize) { +void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context) { if (CS) { - char CSBuffer[4096]; - unsigned int DecompressedFrameSize = 0; cs_NextFrame(CS, FilePos, FrameSize); for (;;) { - int ReadBytes = cs_ReadData(CS, CSBuffer, sizeof(CSBuffer)); - if (ReadBytes < 0) { - snprintf(ErrorMsg, MsgSize, "Error decompressing data: %s", cs_GetLastError(CS)); - return 1; - } + int ReadBytes = cs_ReadData(CS, Context.CSBuffer, sizeof(Context.CSBuffer)); + if (ReadBytes < 0) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + boost::format("Error decompressing data: %1%") % cs_GetLastError(CS)); + if (ReadBytes == 0) { FrameSize = DecompressedFrameSize; - return 0; + memset(Context.Buffer + DecompressedFrameSize, 0, + Context.BufferSize + FF_INPUT_BUFFER_PADDING_SIZE - DecompressedFrameSize); + return; } if (Context.BufferSize < DecompressedFrameSize + ReadBytes) { - Context.BufferSize = FrameSize; - Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16); - if (Context.Buffer == NULL) { - snprintf(ErrorMsg, MsgSize, "Out of memory"); - return 2; - } + Context.BufferSize = DecompressedFrameSize + ReadBytes; + Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + FF_INPUT_BUFFER_PADDING_SIZE); + if (Context.Buffer == NULL) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, + "Out of memory"); } - memcpy(Context.Buffer + DecompressedFrameSize, CSBuffer, ReadBytes); + memcpy(Context.Buffer + DecompressedFrameSize, Context.CSBuffer, ReadBytes); DecompressedFrameSize += ReadBytes; } } else { - if (fseeko(Context.ST.fp, FilePos, SEEK_SET)) { - snprintf(ErrorMsg, MsgSize, "fseek(): %s", strerror(errno)); - return 3; - } + if (fseeko(Context.ST.fp, FilePos, SEEK_SET)) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_SEEKING, + boost::format("fseek(): %1%") % strerror(errno)); if (Context.BufferSize < FrameSize) { Context.BufferSize = FrameSize; Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16); - if (Context.Buffer == NULL) { - snprintf(ErrorMsg, MsgSize, "Out of memory"); - return 4; - } + if (Context.Buffer == NULL) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, + "Out of memory"); } size_t ReadBytes = fread(Context.Buffer, 1, FrameSize, Context.ST.fp); if (ReadBytes != FrameSize) { if (ReadBytes == 0) { if (feof(Context.ST.fp)) { - snprintf(ErrorMsg, MsgSize, "Unexpected EOF while reading frame"); - return 5; + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + "Unexpected EOF while reading frame"); } else { - snprintf(ErrorMsg, MsgSize, "Error reading frame: %s", strerror(errno)); - return 6; + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_SEEKING, + boost::format("Error reading frame: %1%") % strerror(errno)); } } else { - snprintf(ErrorMsg, MsgSize, "Short read while reading frame"); - return 7; + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + "Short read while reading frame"); } - snprintf(ErrorMsg, MsgSize, "Unknown read error"); - return 8; + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + "Unknown read error"); } - return 0; + return; } } -void InitNullPacket(AVPacket *pkt) { - av_init_packet(pkt); - pkt->data = NULL; - pkt->size = 0; +void InitNullPacket(AVPacket &pkt) { + av_init_packet(&pkt); + pkt.data = NULL; + pkt.size = 0; } -void FillAP(FFAudioProperties &AP, AVCodecContext *CTX, FFTrack &Frames) { +bool IsNVOP(AVPacket &pkt) { + const uint8_t MPEG4NVOP[] = { 0x00, 0x00, 0x01, 0xB6 }; + return pkt.size == 7 && !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); - if (CTX->sample_fmt == SAMPLE_FMT_S32) + if (CTX->sample_fmt == SAMPLE_FMT_S32 && CTX->bits_per_raw_sample) AP.BitsPerSample = CTX->bits_per_raw_sample; AP.Channels = CTX->channels;; AP.ChannelLayout = CTX->channel_layout; AP.SampleRate = CTX->sample_rate; - AP.NumSamples = (Frames.back()).SampleStart; + AP.NumSamples = (Frames.back()).SampleStart + (Frames.back()).SampleCount; AP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; AP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; } @@ -198,35 +242,37 @@ void vtCopy(VARIANT& vt,void *dest) { } } -#else - -// used for matroska<->ffmpeg codec ID mapping to avoid Win32 dependency -typedef struct BITMAPINFOHEADER { - uint32_t biSize; - int32_t biWidth; - int32_t biHeight; - uint16_t biPlanes; - uint16_t biBitCount; - uint32_t biCompression; - uint32_t biSizeImage; - int32_t biXPelsPerMeter; - int32_t biYPelsPerMeter; - uint32_t biClrUsed; - uint32_t biClrImportant; -} BITMAPINFOHEADER; - -#define MAKEFOURCC(ch0, ch1, ch2, ch3)\ - ((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) |\ - ((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 )) - #endif -CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC) { +CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC, unsigned int BitsPerSample) { /* Look up native codecs */ for(int i = 0; ff_mkv_codec_tags[i].id != CODEC_ID_NONE; i++){ if(!strncmp(ff_mkv_codec_tags[i].str, Codec, - strlen(ff_mkv_codec_tags[i].str))){ - return ff_mkv_codec_tags[i].id; + strlen(ff_mkv_codec_tags[i].str))) { + + // Uncompressed and exotic format fixup + // This list is incomplete + CodecID CID = ff_mkv_codec_tags[i].id; + switch (CID) { + case CODEC_ID_PCM_S16LE: + switch (BitsPerSample) { + case 8: CID = CODEC_ID_PCM_S8; break; + case 16: CID = CODEC_ID_PCM_S16LE; break; + case 24: CID = CODEC_ID_PCM_S24LE; break; + case 32: CID = CODEC_ID_PCM_S32LE; break; + } + break; + case CODEC_ID_PCM_S16BE: + switch (BitsPerSample) { + case 8: CID = CODEC_ID_PCM_S8; break; + case 16: CID = CODEC_ID_PCM_S16BE; break; + case 24: CID = CODEC_ID_PCM_S24BE; break; + case 32: CID = CODEC_ID_PCM_S32BE; break; + } + break; + } + + return CID; } } @@ -234,7 +280,7 @@ CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC const AVCodecTag *const tags[] = { ff_codec_bmp_tags, 0 }; if (!strcmp(Codec, "V_MS/VFW/FOURCC")) { - BITMAPINFOHEADER *b = reinterpret_cast(CodecPrivate); + FFMS_BITMAPINFOHEADER *b = reinterpret_cast(CodecPrivate); return av_codec_get_id(tags, b->biCompression); } @@ -362,3 +408,60 @@ void ffms_fstream::open(const char *filename, std::ios_base::openmode mode) { ffms_fstream::ffms_fstream(const char *filename, std::ios_base::openmode mode) { open(filename, mode); } + +#ifdef HAALISOURCE + +CComPtr HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode) { + CComPtr pMMC; + + CLSID clsid = HAALI_MPEG_PARSER; + if (SourceMode == FFMS_SOURCE_HAALIOGG) + clsid = HAALI_OGG_PARSER; + + if (FAILED(pMMC.CoCreateInstance(clsid))) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, + "Can't create parser"); + + CComPtr pMA; + if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, + "Can't create memory allocator"); + + CComPtr pMS; + if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, + "Can't create disk file reader"); + + WCHAR WSourceFile[2048]; + ffms_mbstowcs(WSourceFile, SourceFile, 2000); + CComQIPtr pMSO(pMS); + if (FAILED(pMSO->Open(WSourceFile))) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + "Can't open file"); + + if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) { + if (SourceMode == FFMS_SOURCE_HAALIMPEG) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_INVALID_ARGUMENT, + "Can't parse file, most likely a transport stream not cut at packet boundaries"); + else + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_INVALID_ARGUMENT, + "Can't parse file"); + } + + return pMMC; +} + +#endif + +void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext) { + if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + boost::format("Couldn't open '%1'") % SourceFile); + + if (av_find_stream_info(FormatContext) < 0) { + av_close_input_file(FormatContext); + FormatContext = NULL; + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + "Couldn't find stream information"); + } +} diff --git a/aegisub/libffms/src/core/utils.h b/aegisub/libffms/src/core/utils.h index cd17e50ac..7d1a3ae72 100644 --- a/aegisub/libffms/src/core/utils.h +++ b/aegisub/libffms/src/core/utils.h @@ -23,7 +23,8 @@ #include #include -#include +#include +#include #include "ffms.h" #include "matroskaparser.h" @@ -50,17 +51,73 @@ extern "C" { # include "guids.h" #endif - - #define FFMS_GET_VECTOR_PTR(v) (((v).size() ? &(v)[0] : NULL)) const int64_t ffms_av_nopts_value = static_cast(1) << 63; +// used for matroska<->ffmpeg codec ID mapping to avoid Win32 dependency +typedef struct FFMS_BITMAPINFOHEADER { + uint32_t biSize; + int32_t biWidth; + int32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; +} FFMS_BITMAPINFOHEADER; + +class FFMS_Exception : public std::exception { +private: + std::string _Message; + int _ErrorType; + int _SubType; +public: + FFMS_Exception(int ErrorType, int SubType, const char *Message = ""); + FFMS_Exception(int ErrorType, int SubType, const std::string &Message); + FFMS_Exception(int ErrorType, int SubType, const boost::format &Message); + ~FFMS_Exception() throw (); + const std::string &GetErrorMessage() const; + int CopyOut(FFMS_ErrorInfo *ErrorInfo) const; +}; + +template +class FFSourceResources { +private: + T *_PrivClass; + bool _Enabled; + bool _Arg; +public: + FFSourceResources(T *Target) : _PrivClass(Target), _Enabled(true), _Arg(false) { + } + + ~FFSourceResources() { + if (_Enabled) + _PrivClass->Free(_Arg); + } + + void SetEnabled(bool Enabled) { + _Enabled = Enabled; + } + + void SetArg(bool Arg) { + _Arg = Arg; + } + + void CloseCodec(bool Arg) { + _Arg = Arg; + } +}; + struct MatroskaReaderContext { public: StdIoStream ST; uint8_t *Buffer; unsigned int BufferSize; + char CSBuffer[4096]; MatroskaReaderContext() { InitStdIoStream(&ST); @@ -81,19 +138,25 @@ public: int GetSWSCPUFlags(); int GetPPCPUFlags(); +void ClearErrorInfo(FFMS_ErrorInfo *ErrorInfo); FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT); -int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize); +void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context); bool AudioFMTIsFloat(SampleFormat FMT); -void InitNullPacket(AVPacket *pkt); -void FillAP(FFAudioProperties &AP, AVCodecContext *CTX, FFTrack &Frames); +void InitNullPacket(AVPacket &pkt); +bool IsNVOP(AVPacket &pkt); +void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames); #ifdef HAALISOURCE unsigned vtSize(VARIANT &vt); void vtCopy(VARIANT& vt,void *dest); void InitializeCodecContextFromHaaliInfo(CComQIPtr pBag, AVCodecContext *CodecContext); #endif void InitializeCodecContextFromMatroskaTrackInfo(TrackInfo *TI, AVCodecContext *CodecContext); -CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC = 0); +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); +#ifdef HAALISOURCE +CComPtr HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode); +#endif +void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext); #endif diff --git a/aegisub/libffms/src/core/ffvideosource.cpp b/aegisub/libffms/src/core/videosource.cpp similarity index 62% rename from aegisub/libffms/src/core/ffvideosource.cpp rename to aegisub/libffms/src/core/videosource.cpp index 40027ecb9..1405a3534 100644 --- a/aegisub/libffms/src/core/ffvideosource.cpp +++ b/aegisub/libffms/src/core/videosource.cpp @@ -18,24 +18,52 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. -#include "ffvideosource.h" +#include "videosource.h" -int FFVideo::InitPP(const char *PP, char *ErrorMsg, unsigned MsgSize) { - if (PP == NULL || !strcmp(PP, "")) - return 0; - - PPMode = pp_get_mode_by_name_and_quality(PP, PP_QUALITY_MAX); - if (!PPMode) { - snprintf(ErrorMsg, MsgSize, "Invalid postprocesing settings"); - return 1; - } - - return 0; +void FFMS_VideoSource::GetFrameCheck(int n) { + if (n < 0 || n >= VP.NumFrames) + throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_INVALID_ARGUMENT, + "Out of bounds frame requested"); } -int FFVideo::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height, char *ErrorMsg, unsigned MsgSize) { +void FFMS_VideoSource::SetPP(const char *PP) { + if (PPMode) + pp_free_mode(PPMode); + PPMode = NULL; + + if (PP != NULL && strcmp(PP, "")) { + PPMode = pp_get_mode_by_name_and_quality(PP, PP_QUALITY_MAX); + if (!PPMode) { + ResetPP(); + throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_INVALID_ARGUMENT, + "Invalid postprocesing settings"); + } + + } + + ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height); + OutputFrame(DecodeFrame); +} + +void FFMS_VideoSource::ResetPP() { + if (PPContext) + pp_free_context(PPContext); + PPContext = NULL; + + if (PPMode) + pp_free_mode(PPMode); + PPMode = NULL; + + OutputFrame(DecodeFrame); +} + +void FFMS_VideoSource::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height) { + if (PPContext) + pp_free_context(PPContext); + PPContext = NULL; + if (!PPMode) - return 0; + return; int Flags = GetPPCPUFlags(); @@ -45,34 +73,30 @@ int FFVideo::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height, char *E case PIX_FMT_YUV411P: Flags |= PP_FORMAT_411; break; case PIX_FMT_YUV444P: Flags |= PP_FORMAT_444; break; default: - snprintf(ErrorMsg, MsgSize, "Input format is not supported for postprocessing"); - return 1; + ResetPP(); + throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_UNSUPPORTED, + "The video does not have a colorspace suitable for postprocessing"); } - if (PPContext) - pp_free_context(PPContext); PPContext = pp_get_context(Width, Height, Flags); avpicture_free(&PPFrame); avpicture_alloc(&PPFrame, VPixelFormat, Width, Height); - - return 0; } -static void CopyAVPictureFields(AVPicture &Picture, FFAVFrame &Dst) { +static void CopyAVPictureFields(AVPicture &Picture, FFMS_Frame &Dst) { for (int i = 0; i < 4; i++) { Dst.Data[i] = Picture.data[i]; Dst.Linesize[i] = Picture.linesize[i]; } } -FFAVFrame *FFVideo::OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize) { +FFMS_Frame *FFMS_VideoSource::OutputFrame(AVFrame *Frame) { if (LastFrameWidth != CodecContext->width || LastFrameHeight != CodecContext->height || LastFramePixelFormat != CodecContext->pix_fmt) { - if (ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height, ErrorMsg, MsgSize)) - return NULL; + ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height); + if (TargetHeight > 0 && TargetWidth > 0 && TargetPixelFormats != 0) - if (ReAdjustOutputFormat(TargetPixelFormats, TargetWidth, TargetHeight, TargetResizer, ErrorMsg, MsgSize)) - return NULL; + ReAdjustOutputFormat(TargetPixelFormats, TargetWidth, TargetHeight, TargetResizer); } if (PPMode) { @@ -99,9 +123,9 @@ FFAVFrame *FFVideo::OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize LocalFrame.EncodedWidth = CodecContext->width; LocalFrame.EncodedHeight = CodecContext->height; LocalFrame.EncodedPixelFormat = CodecContext->pix_fmt; - LocalFrame.ScaledWidth = VP.Width; - LocalFrame.ScaledHeight = VP.Height; - LocalFrame.ConvertedPixelFormat = VP.VPixelFormat; + LocalFrame.ScaledWidth = TargetWidth; + LocalFrame.ScaledHeight = TargetHeight; + LocalFrame.ConvertedPixelFormat = OutputFormat; LocalFrame.KeyFrame = Frame->key_frame; LocalFrame.PictType = av_get_pict_type_char(Frame->pict_type); LocalFrame.RepeatPict = Frame->repeat_pict; @@ -115,9 +139,22 @@ FFAVFrame *FFVideo::OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize return &LocalFrame; } -FFVideo::FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { - if (Index->CompareFileSignature(SourceFile, ErrorMsg, MsgSize)) - throw ErrorMsg; +FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track) { + if (Track < 0 || Track >= static_cast(Index->size())) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, + "Out of bounds track index selected"); + + if (Index->at(Track).TT != FFMS_TYPE_VIDEO) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, + "Not a video track"); + + if (Index[Track].size() == 0) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT, + "Video track contains no frames"); + + if (!Index->CompareFileSignature(SourceFile)) + throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH, + "The index does not match the source file"); memset(&VP, 0, sizeof(VP)); PPContext = NULL; @@ -133,6 +170,7 @@ FFVideo::FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigne TargetWidth = -1; TargetPixelFormats = 0; TargetResizer = 0; + OutputFormat = PIX_FMT_NONE; DecodeFrame = avcodec_alloc_frame(); // Dummy allocations so the unallocated case doesn't have to be handled later @@ -140,7 +178,7 @@ FFVideo::FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigne avpicture_alloc(&SWSFrame, PIX_FMT_GRAY8, 16, 16); } -FFVideo::~FFVideo() { +FFMS_VideoSource::~FFMS_VideoSource() { if (PPMode) pp_free_mode(PPMode); @@ -155,32 +193,33 @@ FFVideo::~FFVideo() { av_freep(&DecodeFrame); } -FFAVFrame *FFVideo::GetFrameByTime(double Time, char *ErrorMsg, unsigned MsgSize) { +FFMS_Frame *FFMS_VideoSource::GetFrameByTime(double Time) { int Frame = Frames.ClosestFrameFromDTS(static_cast((Time * 1000 * Frames.TB.Den) / Frames.TB.Num)); - return GetFrame(Frame, ErrorMsg, MsgSize); + return GetFrame(Frame); } -int FFVideo::SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize) { +void FFMS_VideoSource::SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer) { this->TargetWidth = Width; this->TargetHeight = Height; this->TargetPixelFormats = TargetFormats; this->TargetResizer = Resizer; - return ReAdjustOutputFormat(TargetFormats, Width, Height, Resizer, ErrorMsg, MsgSize); + ReAdjustOutputFormat(TargetFormats, Width, Height, Resizer); + OutputFrame(DecodeFrame); } -int FFVideo::ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize) { +void FFMS_VideoSource::ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer) { if (SWS) { sws_freeContext(SWS); SWS = NULL; } int Loss; - PixelFormat OutputFormat = avcodec_find_best_pix_fmt(TargetFormats, + OutputFormat = avcodec_find_best_pix_fmt(TargetFormats, CodecContext->pix_fmt, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss); if (OutputFormat == PIX_FMT_NONE) { ResetOutputFormat(); - snprintf(ErrorMsg, MsgSize, "No suitable output format found"); - return 1; + throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT, + "No suitable output format found"); } if (CodecContext->pix_fmt != OutputFormat || Width != CodecContext->width || Height != CodecContext->height) { @@ -188,22 +227,16 @@ int FFVideo::ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, OutputFormat, GetSWSCPUFlags() | Resizer, NULL, NULL, NULL); if (SWS == NULL) { ResetOutputFormat(); - snprintf(ErrorMsg, MsgSize, "Failed to allocate SWScale context"); - return 1; + throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT, + "Failed to allocate SWScale context"); } } - VP.VPixelFormat = OutputFormat; - VP.Height = Height; - VP.Width = Width; - avpicture_free(&SWSFrame); avpicture_alloc(&SWSFrame, OutputFormat, Width, Height); - - return 0; } -void FFVideo::ResetOutputFormat() { +void FFMS_VideoSource::ResetOutputFormat() { if (SWS) { sws_freeContext(SWS); SWS = NULL; @@ -212,8 +245,7 @@ void FFVideo::ResetOutputFormat() { TargetWidth = -1; TargetHeight = -1; TargetPixelFormats = 0; - - VP.Height = CodecContext->height; - VP.Width = CodecContext->width; - VP.VPixelFormat = CodecContext->pix_fmt; + OutputFormat = PIX_FMT_NONE; + + OutputFrame(DecodeFrame); } diff --git a/aegisub/libffms/src/core/ffvideosource.h b/aegisub/libffms/src/core/videosource.h similarity index 56% rename from aegisub/libffms/src/core/ffvideosource.h rename to aegisub/libffms/src/core/videosource.h index b20ce804b..417622726 100644 --- a/aegisub/libffms/src/core/ffvideosource.h +++ b/aegisub/libffms/src/core/videosource.h @@ -45,7 +45,8 @@ extern "C" { # include "guids.h" #endif -class FFVideo { +class FFMS_VideoSource { +friend class FFSourceResources; private: pp_context_t *PPContext; pp_mode_t *PPMode; @@ -57,75 +58,83 @@ private: int TargetWidth; int64_t TargetPixelFormats; int TargetResizer; + PixelFormat OutputFormat; AVPicture PPFrame; AVPicture SWSFrame; protected: - FFVideoProperties VP; - FFAVFrame LocalFrame; + FFMS_VideoProperties VP; + FFMS_Frame LocalFrame; AVFrame *DecodeFrame; int LastFrameNum; - FFTrack Frames; + FFMS_Track Frames; int VideoTrack; int CurrentFrame; AVCodecContext *CodecContext; - FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); - int InitPP(const char *PP, char *ErrorMsg, unsigned MsgSize); - int ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height, char *ErrorMsg, unsigned MsgSize); - FFAVFrame *OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize); + FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track); + void ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height); + void ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer); + FFMS_Frame *OutputFrame(AVFrame *Frame); + virtual void Free(bool CloseCodec) = 0; public: - virtual ~FFVideo(); - const FFVideoProperties& GetFFVideoProperties() { return VP; } - FFTrack *GetFFTrack() { return &Frames; } - virtual FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize) = 0; - FFAVFrame *GetFrameByTime(double Time, char *ErrorMsg, unsigned MsgSize); - int SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize); - int ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize); + virtual ~FFMS_VideoSource(); + const FFMS_VideoProperties& GetVideoProperties() { return VP; } + FFMS_Track *GetTrack() { return &Frames; } + virtual FFMS_Frame *GetFrame(int n) = 0; + void GetFrameCheck(int n); + FFMS_Frame *GetFrameByTime(double Time); + void SetPP(const char *PP); + void ResetPP(); + void SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer); void ResetOutputFormat(); }; -class FFLAVFVideo : public FFVideo { +class FFLAVFVideo : public FFMS_VideoSource { private: AVFormatContext *FormatContext; int SeekMode; + FFSourceResources Res; + void DecodeNextFrame(int64_t *DTS); +protected: void Free(bool CloseCodec); - int DecodeNextFrame(int64_t *DTS, char *ErrorMsg, unsigned MsgSize); public: - FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize); - ~FFLAVFVideo(); - FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize); + FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode); + FFMS_Frame *GetFrame(int n); }; -class FFMatroskaVideo : public FFVideo { +class FFMatroskaVideo : public FFMS_VideoSource { private: MatroskaFile *MF; MatroskaReaderContext MC; CompressedStream *CS; char ErrorMessage[256]; + FFSourceResources Res; + size_t PacketNumber; + void DecodeNextFrame(); +protected: void Free(bool CloseCodec); - int DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize); public: - FFMatroskaVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, char *ErrorMsg, unsigned MsgSize); - ~FFMatroskaVideo(); - FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize); + FFMatroskaVideo(const char *SourceFile, int Track, FFMS_Index *Index, int Threads); + FFMS_Frame *GetFrame(int n); }; #ifdef HAALISOURCE -class FFHaaliVideo : public FFVideo { +class FFHaaliVideo : public FFMS_VideoSource { private: CComPtr pMMC; std::vector CodecPrivate; AVBitStreamFilterContext *BitStreamFilter; + FFSourceResources Res; + void DecodeNextFrame(int64_t *AFirstStartTime); +protected: void Free(bool CloseCodec); - int DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize); public: - FFHaaliVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize); - ~FFHaaliVideo(); - FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize); + FFHaaliVideo(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, enum FFMS_Sources SourceMode); + FFMS_Frame *GetFrame(int n); }; #endif // HAALISOURCE