From cd7932d6ee1dc87841ffa7e8c40bc94435211be7 Mon Sep 17 00:00:00 2001 From: Fredrik Mellbin Date: Wed, 20 May 2009 18:57:03 +0000 Subject: [PATCH] FFMS2: Split indexing into a 2 step process so track numbers/types become known in advance Big code cleanup and renaming Originally committed to SVN as r2955. --- aegisub/FFmpegSource2/ffaudiosource.cpp | 42 +-- aegisub/FFmpegSource2/ffaudiosource.h | 14 +- aegisub/FFmpegSource2/ffms.cpp | 104 +++--- aegisub/FFmpegSource2/ffms.h | 41 ++- aegisub/FFmpegSource2/ffms2.html | 6 +- aegisub/FFmpegSource2/ffvideosource.cpp | 44 +-- aegisub/FFmpegSource2/ffvideosource.h | 18 +- aegisub/FFmpegSource2/indexing.cpp | 357 ++++++-------------- aegisub/FFmpegSource2/indexing.h | 75 +++- aegisub/FFmpegSource2/utils.cpp | 205 +++++++++++ aegisub/FFmpegSource2/utils.h | 41 ++- aegisub/src/video_provider_ffmpegsource.cpp | 2 +- 12 files changed, 540 insertions(+), 409 deletions(-) diff --git a/aegisub/FFmpegSource2/ffaudiosource.cpp b/aegisub/FFmpegSource2/ffaudiosource.cpp index eab8a44e7..2ca689b5b 100644 --- a/aegisub/FFmpegSource2/ffaudiosource.cpp +++ b/aegisub/FFmpegSource2/ffaudiosource.cpp @@ -105,23 +105,13 @@ FFAudio::~FFAudio() { delete[] DecodingBuffer; }; -size_t FFAudio::FindClosestAudioKeyFrame(int64_t Sample) { - for (size_t i = 0; i < Frames.size(); i++) { - if (Frames[i].SampleStart == Sample && Frames[i].KeyFrame) - return i; - else if (Frames[i].SampleStart > Sample && Frames[i].KeyFrame) - return i - 1; - } - return Frames.size() - 1; -} - -void FFAudioSource::Free(bool CloseCodec) { +void FFLAVFAudio::Free(bool CloseCodec) { if (CloseCodec) avcodec_close(CodecContext); av_close_input_file(FormatContext); } -FFAudioSource::FFAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { +FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { FormatContext = NULL; AVCodec *Codec = NULL; AudioTrack = Track; @@ -176,10 +166,10 @@ FFAudioSource::FFAudioSource(const char *SourceFile, int Track, FFIndex *Index, throw ErrorMsg; } - AudioCache.Initialize((AP.Channels *AP.BitsPerSample) / 8, 50); + AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50); } -int FFAudioSource::DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, char *ErrorMsg, unsigned MsgSize) { +int FFLAVFAudio::DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, char *ErrorMsg, unsigned MsgSize) { const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; int Ret = -1; *Count = 0; @@ -221,7 +211,7 @@ Done: return Ret; } -int FFAudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) { +int FFLAVFAudio::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, SizeConst * Count); @@ -239,7 +229,7 @@ int FFAudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count, char *Error // if (!(CurrentSample >= Start && CurrentSample <= CacheEnd)) { if (CurrentSample != CacheEnd) { PreDecBlocks = 15; - CurrentAudioBlock = FFMAX((int64_t)FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks - 20, (int64_t)0); + CurrentAudioBlock = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks - 20, (int64_t)0); av_seek_frame(FormatContext, AudioTrack, Frames[CurrentAudioBlock].DTS, AVSEEK_FLAG_BACKWARD); avcodec_flush_buffers(CodecContext); @@ -269,7 +259,7 @@ int FFAudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count, char *Error av_free_packet(&Packet); } } else { - CurrentAudioBlock = FindClosestAudioKeyFrame(CurrentSample); + CurrentAudioBlock = Frames.FindClosestAudioKeyFrame(CurrentSample); } int64_t DecodeCount; @@ -297,11 +287,11 @@ int FFAudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count, char *Error return 0; } -FFAudioSource::~FFAudioSource() { +FFLAVFAudio::~FFLAVFAudio() { Free(true); } -void MatroskaAudioSource::Free(bool CloseCodec) { +void FFMatroskaAudio::Free(bool CloseCodec) { if (CS) cs_Destroy(CS); if (MC.ST.fp) { @@ -313,7 +303,7 @@ void MatroskaAudioSource::Free(bool CloseCodec) { av_free(CodecContext); } -MatroskaAudioSource::MatroskaAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { +FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { CodecContext = NULL; AVCodec *Codec = NULL; TrackInfo *TI = NULL; @@ -386,14 +376,14 @@ MatroskaAudioSource::MatroskaAudioSource(const char *SourceFile, int Track, FFIn throw ErrorMsg; } - AudioCache.Initialize((AP.Channels *AP.BitsPerSample) / 8, 50); + AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50); } -MatroskaAudioSource::~MatroskaAudioSource() { +FFMatroskaAudio::~FFMatroskaAudio() { Free(true); } -int MatroskaAudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) { +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, SizeConst * Count); @@ -411,10 +401,10 @@ int MatroskaAudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count, char // if (!(CurrentSample >= Start && CurrentSample <= CacheEnd)) { if (CurrentSample != CacheEnd) { PreDecBlocks = 15; - CurrentAudioBlock = FFMAX((int64_t)FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks, (int64_t)0); + CurrentAudioBlock = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks, (int64_t)0); avcodec_flush_buffers(CodecContext); } else { - CurrentAudioBlock = FindClosestAudioKeyFrame(CurrentSample); + CurrentAudioBlock = Frames.FindClosestAudioKeyFrame(CurrentSample); } int64_t DecodeCount; @@ -442,7 +432,7 @@ int MatroskaAudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count, char return 0; } -int MatroskaAudioSource::DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, uint64_t FilePos, unsigned int FrameSize, char *ErrorMsg, unsigned MsgSize) { +int FFMatroskaAudio::DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, uint64_t FilePos, unsigned int FrameSize, char *ErrorMsg, unsigned MsgSize) { const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; int Ret = -1; *Count = 0; diff --git a/aegisub/FFmpegSource2/ffaudiosource.h b/aegisub/FFmpegSource2/ffaudiosource.h index dc2d8ec2a..5e9a76962 100644 --- a/aegisub/FFmpegSource2/ffaudiosource.h +++ b/aegisub/FFmpegSource2/ffaudiosource.h @@ -75,8 +75,6 @@ protected: FFTrack Frames; AVCodecContext *CodecContext; TAudioProperties AP; - - size_t FindClosestAudioKeyFrame(int64_t Sample); public: FFAudio(); ~FFAudio(); @@ -85,7 +83,7 @@ public: virtual int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) = 0; }; -class FFAudioSource : public FFAudio { +class FFLAVFAudio : public FFAudio { private: AVFormatContext *FormatContext; int AudioTrack; @@ -93,13 +91,13 @@ private: int DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, char *ErrorMsg, unsigned MsgSize); void Free(bool CloseCodec); public: - FFAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); - ~FFAudioSource(); + 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); }; -class MatroskaAudioSource : public FFAudio { +class FFMatroskaAudio : public FFAudio { private: MatroskaFile *MF; MatroskaReaderContext MC; @@ -109,8 +107,8 @@ private: int DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, uint64_t FilePos, unsigned int FrameSize, char *ErrorMsg, unsigned MsgSize); void Free(bool CloseCodec); public: - MatroskaAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); - ~MatroskaAudioSource(); + 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); }; diff --git a/aegisub/FFmpegSource2/ffms.cpp b/aegisub/FFmpegSource2/ffms.cpp index e54ea6c93..64a742ab5 100644 --- a/aegisub/FFmpegSource2/ffms.cpp +++ b/aegisub/FFmpegSource2/ffms.cpp @@ -31,30 +31,6 @@ extern "C" { #define _snprintf snprintf #endif -TFrameInfo::TFrameInfo(int64_t DTS, bool KeyFrame) { - this->DTS = DTS; - this->SampleStart = 0; - this->FilePos = 0; - this->FrameSize = 0; - this->KeyFrame = KeyFrame; -} - -TFrameInfo::TFrameInfo(int64_t DTS, int64_t SampleStart, bool KeyFrame) { - this->DTS = DTS; - this->SampleStart = SampleStart; - this->FilePos = 0; - this->FrameSize = 0; - this->KeyFrame = KeyFrame; -} - -TFrameInfo::TFrameInfo(int64_t SampleStart, int64_t FilePos, unsigned int FrameSize, bool KeyFrame) { - this->DTS = 0; - this->SampleStart = SampleStart; - this->FilePos = FilePos; - this->FrameSize = FrameSize; - this->KeyFrame = KeyFrame; -} - FFMS_API(void) FFMS_Init() { static bool InitDone = false; if (!InitDone) { @@ -75,11 +51,11 @@ 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) { try { switch (Index->Decoder) { - case 0: return new FFVideoSource(SourceFile, Track, Index, PP, Threads, SeekMode, ErrorMsg, MsgSize); - case 1: return new MatroskaVideoSource(SourceFile, Track, Index, PP, Threads, ErrorMsg, MsgSize); + 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); #ifdef HAALISOURCE - case 2: return new HaaliVideoSource(SourceFile, Track, Index, PP, Threads, 0, ErrorMsg, MsgSize); - case 3: return new HaaliVideoSource(SourceFile, Track, Index, PP, Threads, 1, ErrorMsg, MsgSize); + 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); #endif default: _snprintf(ErrorMsg, MsgSize, "Unsupported format"); @@ -93,8 +69,8 @@ FFMS_API(FFVideo *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FF FFMS_API(FFAudio *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { try { switch (Index->Decoder) { - case 0: return new FFAudioSource(SourceFile, Track, Index, ErrorMsg, MsgSize); - case 1: return new MatroskaAudioSource(SourceFile, Track, Index, ErrorMsg, MsgSize); + case 0: return new FFLAVFAudio(SourceFile, Track, Index, ErrorMsg, MsgSize); + case 1: return new FFMatroskaAudio(SourceFile, Track, Index, ErrorMsg, MsgSize); default: _snprintf(ErrorMsg, MsgSize, "Unsupported format"); return NULL; @@ -145,6 +121,14 @@ FFMS_API(void) FFMS_DestroyFFIndex(FFIndex *Index) { } FFMS_API(int) FFMS_GetFirstTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize) { + 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; +} + +FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize) { for (int i = 0; i < static_cast(Index->size()); i++) if ((*Index)[i].TT == TrackType && (*Index)[i].size() > 0) return i; @@ -156,20 +140,28 @@ FFMS_API(int) FFMS_GetNumTracks(FFIndex *Index) { return Index->size(); } -FFMS_API(int) FFMS_GetTrackType(FFTrack *T) { +FFMS_API(int) FFMS_GetNumTracksI(FFIndexer *Indexer, int Track) { + return Indexer->GetNumberOfTracks(); +} + +FFMS_API(FFMS_TrackType) FFMS_GetTrackType(FFTrack *T) { return T->TT; } +FFMS_API(FFMS_TrackType) FFMS_GetTrackTypeI(FFIndexer *Indexer, int Track) { + return Indexer->GetTrackType(Track); +} + FFMS_API(int) FFMS_GetNumFrames(FFTrack *T) { return T->size(); } -FFMS_API(const TFrameInfo *) FFMS_GetFrameInfo(FFTrack *T, int Frame, char *ErrorMsg, unsigned MsgSize) { +FFMS_API(const FFFrameInfo *) FFMS_GetFrameInfo(FFTrack *T, int Frame, char *ErrorMsg, unsigned MsgSize) { if (Frame < 0 || Frame >= static_cast(T->size())) { _snprintf(ErrorMsg, MsgSize, "Invalid frame specified"); return NULL; } else { - return &(*T)[Frame]; + return reinterpret_cast(&(*T)[Frame]); } } @@ -190,15 +182,6 @@ FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A) { return A->GetFFTrack(); } -FFMS_API(int) FFMS_FindClosestKeyFrame(FFTrack *T, int Frame, char *ErrorMsg, unsigned MsgSize) { - if (Frame < 0 || Frame >= static_cast(T->size())) { - _snprintf(ErrorMsg, MsgSize, "Out of range frame specified"); - return -1; - } else { - return T->FindClosestKeyFrame(Frame); - } -} - FFMS_API(const TTrackTimeBase *) FFMS_GetTimeBase(FFTrack *T) { return &T->TB; } @@ -207,12 +190,36 @@ FFMS_API(int) FFMS_WriteTimecodes(FFTrack *T, const char *TimecodeFile, char *Er return T->WriteTimecodes(TimecodeFile, ErrorMsg, MsgSize); } -FFMS_API(FFIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, TIndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize) { - return MakeIndex(SourceFile, IndexMask, DumpMask, AudioFile, IgnoreDecodeErrors, IP, Private, ErrorMsg, MsgSize); +FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize) { + try { + return FFIndexer::CreateFFIndexer(SourceFile, ErrorMsg, MsgSize); + } catch (...) { + return NULL; + } +} + +FFMS_API(FFIndex *) FFMS_DoIndexing(FFIndexer *Indexer, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, TIndexCallback IC, void *Private, char *ErrorMsg, unsigned MsgSize) { + Indexer->SetIndexMask(IndexMask); + Indexer->SetDumpMask(DumpMask); + Indexer->SetIgnoreDecodeErrors(IgnoreDecodeErrors); + Indexer->SetProgressCallback(IC, Private); + FFIndex *Index = Indexer->DoIndexing(AudioFile, ErrorMsg, MsgSize); + delete Indexer; + return Index; +} + +FFMS_API(void) FFMS_CancelIndexing(FFIndexer *Indexer) { + delete Indexer; } FFMS_API(FFIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) { - return ReadIndex(IndexFile, ErrorMsg, MsgSize); + FFIndex *Index = new FFIndex(); + if (Index->ReadIndex(IndexFile, ErrorMsg, MsgSize)) { + delete Index; + return NULL; + } else { + return Index; + } } FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { @@ -222,3 +229,10 @@ FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFIndex *Index, char *Error FFMS_API(int) FFMS_GetPixFmt(const char *Name) { return avcodec_get_pix_fmt(Name); } + +FFMS_API(FFIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, TIndexCallback IC, void *Private, char *ErrorMsg, unsigned MsgSize) { + FFIndexer *Indexer = FFMS_CreateIndexer(SourceFile, ErrorMsg, MsgSize); + if (!Indexer) + return NULL; + return FFMS_DoIndexing(Indexer, IndexMask, DumpMask, AudioFile, IgnoreDecodeErrors, IC, Private, ErrorMsg, MsgSize); +} diff --git a/aegisub/FFmpegSource2/ffms.h b/aegisub/FFmpegSource2/ffms.h index c2c20970e..b18978950 100644 --- a/aegisub/FFmpegSource2/ffms.h +++ b/aegisub/FFmpegSource2/ffms.h @@ -43,6 +43,7 @@ struct FFVideo; struct FFAudio; +struct FFIndexer; struct FFIndex; struct FFTrack; @@ -55,8 +56,12 @@ enum FFMS_SeekMode { }; enum FFMS_TrackType { - FFMS_TYPE_VIDEO = 0, - FFMS_TYPE_AUDIO = 1, + FFMS_TYPE_UNKNOWN = -1, + FFMS_TYPE_VIDEO, + FFMS_TYPE_AUDIO, + FFMS_TYPE_DATA, + FFMS_TYPE_SUBTITLE, + FFMS_TYPE_ATTACHMENT, }; // This is a subset of the original AVFrame only containing the most used parts. @@ -77,17 +82,10 @@ struct TTrackTimeBase { int64_t Den; }; -struct TFrameInfo { - int64_t DTS; - int64_t SampleStart; - int64_t FilePos; - unsigned int FrameSize; - bool KeyFrame; -#ifdef FFMS_EXPORTS - TFrameInfo(int64_t DTS, bool KeyFrame); - TFrameInfo(int64_t DTS, int64_t SampleStart, bool KeyFrame); - TFrameInfo(int64_t SampleStart, int64_t FilePos, unsigned int FrameSize, bool KeyFrame); -#endif +#define FFMS_FRAMEINFO_COMMON int64_t DTS; bool KeyFrame; + +struct FFFrameInfo { + FFMS_FRAMEINFO_COMMON }; struct TVideoProperties { @@ -138,18 +136,27 @@ FFMS_API(int) FFMS_SetOutputFormat(FFVideo *V, int TargetFormat, int Width, int FFMS_API(void) FFMS_ResetOutputFormat(FFVideo *V); FFMS_API(void) FFMS_DestroyFFIndex(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_GetTrackType(FFTrack *T); +FFMS_API(int) FFMS_GetNumTracksI(FFIndexer *Indexer, int Track); +FFMS_API(FFMS_TrackType) FFMS_GetTrackType(FFTrack *T); +FFMS_API(FFMS_TrackType) FFMS_GetTrackTypeI(FFIndexer *Indexer, int Track); +FFMS_API(const char *) FFMS_CodecName(FFIndexer *Indexer, int Track); FFMS_API(int) FFMS_GetNumFrames(FFTrack *T); -FFMS_API(const TFrameInfo *) FFMS_GetFrameInfo(FFTrack *T, int Frame, char *ErrorMsg, unsigned MsgSize); +FFMS_API(const FFFrameInfo *) FFMS_GetFrameInfo(FFTrack *T, int Frame, char *ErrorMsg, unsigned MsgSize); FFMS_API(FFTrack *) FFMS_GetTrackFromIndex(FFIndex *Index, int Track, char *ErrorMsg, unsigned MsgSize); FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V); FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A); -FFMS_API(int) FFMS_FindClosestKeyFrame(FFTrack *T, int Frame, char *ErrorMsg, unsigned MsgSize); FFMS_API(const TTrackTimeBase *) 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, const char *AudioFile, bool IgnoreDecodeErrors, TIndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize); +FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize); +FFMS_API(FFIndex *) FFMS_DoIndexing(FFIndexer *Indexer, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, TIndexCallback IC, void *Private, 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_WriteIndex(const char *IndexFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); FFMS_API(int) FFMS_GetPixFmt(const char *Name); + +// Deprecated, only provided for compatibility +FFMS_API(FFIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, TIndexCallback IC, void *Private, char *ErrorMsg, unsigned MsgSize); + #endif diff --git a/aegisub/FFmpegSource2/ffms2.html b/aegisub/FFmpegSource2/ffms2.html index 9fd18b74d..25e22cdb9 100644 --- a/aegisub/FFmpegSource2/ffms2.html +++ b/aegisub/FFmpegSource2/ffms2.html @@ -13,6 +13,7 @@ Opens files using ffmpeg and nothing else. May be frame accurate on good days. T

Known issues

  • Requires Haali's Media Splitter if ogm or mpeg ps/ts is to be opened.
  • +
  • Haali's Media Splitter is used the reported framerate is always 30
  • Avi files with NVOPs (sometimes occurs in xvid and such) will desync when these frames are encountered. Remux to mkv/mp4 before opening to solve it for now.
  • The audio sources still aren't sample accurate and sometimes exhibit many interesting issues. This is however more likely to be an issues when not using a 32bit windows compile. Dumping the audio during indexing is the only workaround.
@@ -239,14 +240,15 @@ Note that --enable-w32threads is required for multithreaded decoding to work.

Changes

  • 2.00 beta 9
      -
    • Now has stricter index checking to detect when different FFmpeg versions were used to create an inded of the same version
    • +
    • How indexing works has been split internally so the track numbers and types are reported, this makes it possible to create an interactive GUI or ask which audio tracks are to be indexed
    • +
    • Now has stricter index checking to detect when different FFmpeg versions were used to create an index of the same version
    • Fixed an access violation occurring when unindexed or empty audio tracks in matroska files were opened
    • Less type conversion/signedness warnings
    • When audio track dumping is performed a custom callback can now be supplied to name the tracks
    • The audio track delay is now exposed in the API in the same way as video tracks
    • A big type and argument name cleanup in the API, many things have been renamed to be clearer and it should be completely C friendly now
    • Removed FFNoLog and replaced it with FFSetLogLevel and FFGetLogLevel, the default logging is now also set to quiet, the magical numbers to supply it can be found in avutil/log.h
    • -
    • Updated FFmpeg to rev X (now with pthreads and faad2 again by popular demand, updated to GCC 4.4.0 for compiling all libraries)
    • +
    • Updated FFmpeg to rev X (now with faad2 again by popular demand, updated to GCC 4.4.0 for compiling all libraries)
  • 2.00 beta 8
      diff --git a/aegisub/FFmpegSource2/ffvideosource.cpp b/aegisub/FFmpegSource2/ffvideosource.cpp index a648c42c0..bad82a1af 100644 --- a/aegisub/FFmpegSource2/ffvideosource.cpp +++ b/aegisub/FFmpegSource2/ffvideosource.cpp @@ -171,7 +171,7 @@ void FFVideo::ResetOutputFormat() { VP.VPixelFormat = CodecContext->pix_fmt; } -void FFVideoSource::Free(bool CloseCodec) { +void FFLAVFVideo::Free(bool CloseCodec) { if (CloseCodec) avcodec_close(CodecContext); av_close_input_file(FormatContext); @@ -179,7 +179,7 @@ void FFVideoSource::Free(bool CloseCodec) { //av_free(FormatContext); } -FFVideoSource::FFVideoSource(const char *SourceFile, int Track, FFIndex *Index, +FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize) { FormatContext = NULL; @@ -275,11 +275,11 @@ FFVideoSource::FFVideoSource(const char *SourceFile, int Track, FFIndex *Index, VP.SARDen = CodecContext->sample_aspect_ratio.den; } -FFVideoSource::~FFVideoSource() { +FFLAVFVideo::~FFLAVFVideo() { Free(true); } -int FFVideoSource::DecodeNextFrame(AVFrame *AFrame, int64_t *AStartTime, char *ErrorMsg, unsigned MsgSize) { +int FFLAVFVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AStartTime, char *ErrorMsg, unsigned MsgSize) { AVPacket Packet; InitNullPacket(&Packet); int FrameFinished = 0; @@ -315,7 +315,7 @@ Done: return 0; } -TAVFrameLite *FFVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { +TAVFrameLite *FFLAVFVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { // PPFrame always holds frame LastFrameNum even if no PP is applied if (LastFrameNum == n) return OutputFrame(DecodeFrame); @@ -325,7 +325,7 @@ TAVFrameLite *FFVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { int ClosestKF = 0; if (SeekMode >= 0) { - ClosestKF = Frames.FindClosestKeyFrame(n); + ClosestKF = Frames.FindClosestVideoKeyFrame(n); if (SeekMode == 0) { if (n < CurrentFrame) { @@ -385,7 +385,7 @@ ReSeek: return OutputFrame(DecodeFrame); } -void MatroskaVideoSource::Free(bool CloseCodec) { +void FFMatroskaVideo::Free(bool CloseCodec) { if (CS) cs_Destroy(CS); if (MC.ST.fp) { @@ -397,7 +397,7 @@ void MatroskaVideoSource::Free(bool CloseCodec) { av_free(CodecContext); } -MatroskaVideoSource::MatroskaVideoSource(const char *SourceFile, int Track, +FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, char *ErrorMsg, unsigned MsgSize) { @@ -506,11 +506,11 @@ MatroskaVideoSource::MatroskaVideoSource(const char *SourceFile, int Track, VP.CropBottom = TI->AV.Video.CropB; } -MatroskaVideoSource::~MatroskaVideoSource() { +FFMatroskaVideo::~FFMatroskaVideo() { Free(true); } -int MatroskaVideoSource::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) { +int FFMatroskaVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) { int FrameFinished = 0; *AFirstStartTime = -1; AVPacket Packet; @@ -553,14 +553,14 @@ Done: return 0; } -TAVFrameLite *MatroskaVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { +TAVFrameLite *FFMatroskaVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { // PPFrame always holds frame LastFrameNum even if no PP is applied if (LastFrameNum == n) return OutputFrame(DecodeFrame); bool HasSeeked = false; - if (n < CurrentFrame || Frames.FindClosestKeyFrame(n) > CurrentFrame) { + if (n < CurrentFrame || Frames.FindClosestVideoKeyFrame(n) > CurrentFrame) { mkv_Seek(MF, Frames[n].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT); avcodec_flush_buffers(CodecContext); HasSeeked = true; @@ -589,14 +589,14 @@ TAVFrameLite *MatroskaVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgS #ifdef HAALISOURCE -void HaaliVideoSource::Free(bool CloseCodec) { +void FFHaaliVideo::Free(bool CloseCodec) { if (CloseCodec) avcodec_close(CodecContext); av_free(CodecContext); delete[] CodecPrivate; } -HaaliVideoSource::HaaliVideoSource(const char *SourceFile, int Track, +FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize) { @@ -723,14 +723,6 @@ HaaliVideoSource::HaaliVideoSource(const char *SourceFile, int Track, throw ErrorMsg; } - // Calculate the average framerate - if (Frames.size() >= 2) { - double DTSDiff = (double)(Frames.back().DTS - Frames.front().DTS); - // FIXME - VP.FPSDenominator = (unsigned int)((DTSDiff * 1000000) / (double)(VP.NumFrames - 1) + 0.5); - VP.FPSNumerator = 1000000; - } - // Output the already decoded frame so it isn't wasted OutputFrame(DecodeFrame); @@ -745,11 +737,11 @@ HaaliVideoSource::HaaliVideoSource(const char *SourceFile, int Track, VP.SARDen = pV.uiVal; } -HaaliVideoSource::~HaaliVideoSource() { +FFHaaliVideo::~FFHaaliVideo() { Free(true); } -int HaaliVideoSource::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) { +int FFHaaliVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) { int FrameFinished = 0; *AFirstStartTime = -1; AVPacket Packet; @@ -798,7 +790,7 @@ Done: return 0; } -TAVFrameLite *HaaliVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { +TAVFrameLite *FFHaaliVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { // PPFrame always holds frame LastFrameNum even if no PP is applied if (LastFrameNum == n) return OutputFrame(DecodeFrame); @@ -806,7 +798,7 @@ TAVFrameLite *HaaliVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSize bool HasSeeked = false; int SeekOffset = 0; - if (n < CurrentFrame || Frames.FindClosestKeyFrame(n) > CurrentFrame + 10) { + if (n < CurrentFrame || Frames.FindClosestVideoKeyFrame(n) > CurrentFrame + 10) { ReSeek: pMMC->Seek(Frames[n + SeekOffset].DTS, MMSF_PREV_KF); avcodec_flush_buffers(CodecContext); diff --git a/aegisub/FFmpegSource2/ffvideosource.h b/aegisub/FFmpegSource2/ffvideosource.h index e7b5e9e24..84e055a72 100644 --- a/aegisub/FFmpegSource2/ffvideosource.h +++ b/aegisub/FFmpegSource2/ffvideosource.h @@ -73,7 +73,7 @@ public: void ResetOutputFormat(); }; -class FFVideoSource : public FFVideo { +class FFLAVFVideo : public FFVideo { private: AVFormatContext *FormatContext; int SeekMode; @@ -81,12 +81,12 @@ private: void Free(bool CloseCodec); int DecodeNextFrame(AVFrame *Frame, int64_t *DTS, char *ErrorMsg, unsigned MsgSize); public: - FFVideoSource(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize); - ~FFVideoSource(); + FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize); + ~FFLAVFVideo(); TAVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize); }; -class MatroskaVideoSource : public FFVideo { +class FFMatroskaVideo : public FFVideo { private: MatroskaFile *MF; MatroskaReaderContext MC; @@ -96,22 +96,22 @@ private: void Free(bool CloseCodec); int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize); public: - MatroskaVideoSource(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, char *ErrorMsg, unsigned MsgSize); - ~MatroskaVideoSource(); + FFMatroskaVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, char *ErrorMsg, unsigned MsgSize); + ~FFMatroskaVideo(); TAVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize); }; #ifdef HAALISOURCE -class HaaliVideoSource : public FFVideo { +class FFHaaliVideo : public FFVideo { private: CComPtr pMMC; uint8_t * CodecPrivate; void Free(bool CloseCodec); int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize); public: - HaaliVideoSource(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize); - ~HaaliVideoSource(); + FFHaaliVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize); + ~FFHaaliVideo(); TAVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize); }; diff --git a/aegisub/FFmpegSource2/indexing.cpp b/aegisub/FFmpegSource2/indexing.cpp index d22a69d4c..b240e8138 100644 --- a/aegisub/FFmpegSource2/indexing.cpp +++ b/aegisub/FFmpegSource2/indexing.cpp @@ -172,69 +172,34 @@ static void SortTrackIndices(FFIndex *Index) { std::sort(Cur->begin(), Cur->end(), DTSComparison); } -int FFIndex::WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) { - std::ofstream 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; - } - - // Write the index file header - IndexHeader IH; - IH.Id = INDEXID; - IH.Version = INDEXVERSION; - IH.Tracks = size(); - IH.Decoder = Decoder; - IH.LAVUVersion = LIBAVUTIL_VERSION_INT; - IH.LAVFVersion = LIBAVFORMAT_VERSION_INT; - IH.LAVCVersion = LIBAVCODEC_VERSION_INT; - IH.LSWSVersion = LIBSWSCALE_VERSION_INT; - IH.LPPVersion = LIBPOSTPROC_VERSION_INT; - - IndexStream.write(reinterpret_cast(&IH), sizeof(IH)); - - for (unsigned int i = 0; i < IH.Tracks; i++) { - int 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)); - size_t Frames = at(i).size(); - IndexStream.write(reinterpret_cast(&Frames), sizeof(Frames)); - - for (size_t j = 0; j < Frames; j++) - IndexStream.write(reinterpret_cast(&(at(i)[j])), sizeof(TFrameInfo)); - } - - return 0; -} - #ifdef HAALISOURCE -static FFIndex *MakeHaaliIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, int SourceMode, TIndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize) { +FFHaaliIndexer::FFHaaliIndexer(const char *SourceFile, int SourceMode, char *ErrorMsg, unsigned MsgSize) { + 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)); ::CoInitializeEx(NULL, COINIT_MULTITHREADED); CLSID clsid = HAALI_TS_Parser; if (SourceMode == 1) clsid = HAALI_OGM_Parser; - CComPtr pMMC; if (FAILED(pMMC.CoCreateInstance(clsid))) { _snprintf(ErrorMsg, MsgSize, "Can't create parser"); - return NULL; + throw ErrorMsg; } CComPtr pMA; if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) { _snprintf(ErrorMsg, MsgSize, "Can't create memory allocator"); - return NULL; + throw ErrorMsg; } CComPtr pMS; if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) { _snprintf(ErrorMsg, MsgSize, "Can't create disk file reader"); - return NULL; + throw ErrorMsg; } WCHAR WSourceFile[2048]; @@ -242,26 +207,51 @@ static FFIndex *MakeHaaliIndex(const char *SourceFile, int IndexMask, int DumpMa CComQIPtr pMSO(pMS); if (FAILED(pMSO->Open(WSourceFile))) { _snprintf(ErrorMsg, MsgSize, "Can't open file"); - return NULL; + throw ErrorMsg; } if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) { _snprintf(ErrorMsg, MsgSize, "Can't parse file"); - return NULL; + throw ErrorMsg; } - int NumTracks = 0; + NumTracks = 0; CComPtr pEU; if (SUCCEEDED(pMMC->EnumTracks(&pEU))) { CComPtr pU; while (pEU->Next(1, &pU, NULL) == S_OK) { - NumTracks++; + CComQIPtr pBag = pU; + + if (pBag) { + CComVariant pV; + + pV.Clear(); + if (SUCCEEDED(pBag->Read(L"Type", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) + TrackType[NumTracks] = HaaliTrackTypeToFFTrackType(pV.uintVal); + + pV.Clear(); + if (SUCCEEDED(pBag->Read(L"CodecPrivate", &pV, NULL))) { + CodecPrivateSize[NumTracks] = vtSize(pV); + CodecPrivate[NumTracks] = new uint8_t[CodecPrivateSize[NumTracks]]; + vtCopy(pV, CodecPrivate[NumTracks]); + } + + 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, CodecPrivate[NumTracks])); + } + } + pU = NULL; + NumTracks++; } } +} +FFIndex *FFHaaliIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize) { // Audio stuff - int16_t *db; MatroskaAudioContext *AudioContexts; HaaliIndexMemory IM = HaaliIndexMemory(NumTracks, db, AudioContexts); @@ -271,68 +261,31 @@ static FFIndex *MakeHaaliIndex(const char *SourceFile, int IndexMask, int DumpMa if (SourceMode == 1) TrackIndices->Decoder = 3; - int TrackTypes[32]; - int CurrentTrack = 0; - pEU = NULL; - if (SUCCEEDED(pMMC->EnumTracks(&pEU))) { - CComPtr pU; - while (pEU->Next(1, &pU, NULL) == S_OK) { - CComQIPtr pBag = pU; - AVCodec *CodecID = NULL; - TrackTypes[CurrentTrack] = -200; - uint8_t * CodecPrivate = NULL; - int CodecPrivateSize = 0; - if (pBag) { - CComVariant pV; + for (int i = 0; i < NumTracks; i++) { + TrackIndices->push_back(FFTrack(1, 1000000000, TrackType[i])); - pV.Clear(); - if (SUCCEEDED(pBag->Read(L"Type", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) - TrackTypes[CurrentTrack] = pV.uintVal; + if (IndexMask & (1 << i) && TrackType[i] == FFMS_TYPE_AUDIO) { + AVCodecContext *AudioCodecContext = avcodec_alloc_context(); + AudioCodecContext->extradata = CodecPrivate[i]; + AudioCodecContext->extradata_size = CodecPrivateSize[i]; + AudioContexts[i].CTX = AudioCodecContext; - pV.Clear(); - if (SUCCEEDED(pBag->Read(L"CodecPrivate", &pV, NULL))) { - CodecPrivateSize = vtSize(pV); - CodecPrivate = new uint8_t[CodecPrivateSize]; - vtCopy(pV, CodecPrivate); - } - - pV.Clear(); - if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) { - char ACodecID[2048]; - wcstombs(ACodecID, pV.bstrVal, 2000); - CodecID = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, CodecPrivate)); - } + if (Codec[i] == NULL) { + av_free(AudioCodecContext); + AudioContexts[i].CTX = NULL; + _snprintf(ErrorMsg, MsgSize, "Audio codec not found"); + return NULL; } - TrackIndices->push_back(FFTrack(1, 1000000000, TrackTypes[CurrentTrack] - 1)); - - if (IndexMask & (1 << CurrentTrack) && TrackTypes[CurrentTrack] == TT_AUDIO) { - AVCodecContext *AudioCodecContext = avcodec_alloc_context(); - AudioCodecContext->extradata = CodecPrivate; - AudioCodecContext->extradata_size = CodecPrivateSize; - AudioContexts[CurrentTrack].CTX = AudioCodecContext; - - AVCodec *AudioCodec = CodecID; - if (AudioCodec == NULL) { - av_free(AudioCodecContext); - AudioContexts[CurrentTrack].CTX = NULL; - _snprintf(ErrorMsg, MsgSize, "Audio codec not found"); - return NULL; - } - - if (avcodec_open(AudioCodecContext, AudioCodec) < 0) { - av_free(AudioCodecContext); - AudioContexts[CurrentTrack].CTX = NULL; - _snprintf(ErrorMsg, MsgSize, "Could not open audio codec"); - return NULL; - } - } else { - IndexMask &= ~(1 << CurrentTrack); + if (avcodec_open(AudioCodecContext, Codec[i]) < 0) { + av_free(AudioCodecContext); + AudioContexts[i].CTX = NULL; + _snprintf(ErrorMsg, MsgSize, "Could not open audio codec"); + return NULL; } - - pU = NULL; - CurrentTrack++; + } else { + IndexMask &= ~(1 << i); } } // @@ -341,8 +294,8 @@ static FFIndex *MakeHaaliIndex(const char *SourceFile, int IndexMask, int DumpMa InitNullPacket(&TempPacket); for (;;) { - if (IP) { - if ((*IP)(0, 1, Private)) { + if (IC) { + if ((*IC)(0, 1, Private)) { _snprintf(ErrorMsg, MsgSize, "Cancelled by user"); delete TrackIndices; return NULL; @@ -359,10 +312,10 @@ static FFIndex *MakeHaaliIndex(const char *SourceFile, int IndexMask, int DumpMa unsigned int CurrentTrack = pMMF->GetTrack(); // Only create index entries for video for now to save space - if (TrackTypes[CurrentTrack] == TT_VIDEO) { + if (TrackType[CurrentTrack] == FFMS_TYPE_VIDEO) { (*TrackIndices)[CurrentTrack].push_back(TFrameInfo(Ts, pMMF->IsSyncPoint() == S_OK)); - } else if (TrackTypes[CurrentTrack] == TT_AUDIO && (IndexMask & (1 << CurrentTrack))) { - (*TrackIndices)[CurrentTrack].push_back(TFrameInfo(AudioContexts[CurrentTrack].CurrentSample, 0 /* FIXME? */, pMMF->GetActualDataLength(), pMMF->IsSyncPoint() == S_OK)); + } else if (TrackType[CurrentTrack] == FFMS_TYPE_AUDIO && (IndexMask & (1 << CurrentTrack))) { + (*TrackIndices)[CurrentTrack].push_back(TFrameInfo(Ts, AudioContexts[CurrentTrack].CurrentSample, 0 /* FIXME? */, pMMF->GetActualDataLength(), pMMF->IsSyncPoint() == S_OK)); AVCodecContext *AudioCodecContext = AudioContexts[CurrentTrack].CTX; pMMF->GetPointer(&TempPacket.data); TempPacket.size = pMMF->GetActualDataLength(); @@ -416,18 +369,14 @@ static FFIndex *MakeHaaliIndex(const char *SourceFile, int IndexMask, int DumpMa } #endif -static FFIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, TIndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize) { - MatroskaFile *MF; +FFMatroskaIndexer::FFMatroskaIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize) { char ErrorMessage[256]; - MatroskaReaderContext MC; - MC.Buffer = NULL; - MC.BufferSize = 0; InitStdIoStream(&MC.ST); MC.ST.fp = fopen(SourceFile, "rb"); if (MC.ST.fp == NULL) { _snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno)); - return NULL; + throw ErrorMsg; } setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE); @@ -436,11 +385,14 @@ static FFIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int Dum if (MF == NULL) { fclose(MC.ST.fp); _snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage); - return NULL; + throw ErrorMsg; } +} + +FFIndex *FFMatroskaIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize) { + char ErrorMessage[256]; // Audio stuff - int16_t *db; MatroskaAudioContext *AudioContexts; MatroskaIndexMemory IM = MatroskaIndexMemory(mkv_GetNumTracks(MF), db, AudioContexts, MF, &MC); @@ -493,7 +445,7 @@ static FFIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int Dum TrackIndices->Decoder = 1; for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) - TrackIndices->push_back(FFTrack(mkv_TruncFloat(mkv_GetTrackInfo(MF, i)->TimecodeScale), 1000000, mkv_GetTrackInfo(MF, i)->Type - 1)); + TrackIndices->push_back(FFTrack(mkv_TruncFloat(mkv_GetTrackInfo(MF, i)->TimecodeScale), 1000000, HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, i)->Type))); ulonglong StartTime, EndTime, FilePos; unsigned int Track, FrameFlags, FrameSize; @@ -502,19 +454,19 @@ static FFIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int Dum while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) { // Update progress - if (IP) { - if ((*IP)(_ftelli64(MC.ST.fp), SourceSize, Private)) { + if (IC) { + if ((*IC)(_ftelli64(MC.ST.fp), SourceSize, Private)) { _snprintf(ErrorMsg, MsgSize, "Cancelled by user"); delete TrackIndices; return NULL; } } - + // Only create index entries for video for now to save space if (mkv_GetTrackInfo(MF, Track)->Type == TT_VIDEO) { - (*TrackIndices)[Track].push_back(TFrameInfo(StartTime, (FrameFlags & FRAME_KF) != 0)); + (*TrackIndices)[Track].push_back(TFrameInfo(StartTime, FilePos, FrameSize, (FrameFlags & FRAME_KF) != 0)); } else if (mkv_GetTrackInfo(MF, Track)->Type == TT_AUDIO && (IndexMask & (1 << Track))) { - (*TrackIndices)[Track].push_back(TFrameInfo(AudioContexts[Track].CurrentSample, FilePos, FrameSize, (FrameFlags & FRAME_KF) != 0)); + (*TrackIndices)[Track].push_back(TFrameInfo(StartTime, AudioContexts[Track].CurrentSample, FilePos, FrameSize, (FrameFlags & FRAME_KF) != 0)); ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC, ErrorMsg, MsgSize); AVCodecContext *AudioCodecContext = AudioContexts[Track].CTX; TempPacket.data = MC.Buffer; @@ -566,42 +518,56 @@ static FFIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int Dum return TrackIndices; } -FFIndex *MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, TIndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize) { +FFIndexer *FFIndexer::CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) { AVFormatContext *FormatContext = NULL; - IndexMask |= DumpMask; - if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) { - _snprintf(ErrorMsg, MsgSize, "Can't open '%s'", SourceFile); + if (av_open_input_file(&FormatContext, Filename, NULL, 0, NULL) != 0) { + _snprintf(ErrorMsg, MsgSize, "Can't open '%s'", Filename); return NULL; } // Do matroska indexing instead? if (!strcmp(FormatContext->iformat->name, "matroska")) { av_close_input_file(FormatContext); - return MakeMatroskaIndex(SourceFile, IndexMask, DumpMask, AudioFile, IgnoreDecodeErrors, IP, Private, ErrorMsg, MsgSize); + return new FFMatroskaIndexer(Filename, ErrorMsg, MsgSize); } #ifdef HAALISOURCE // Do haali ts indexing instead? if (!strcmp(FormatContext->iformat->name, "mpeg") || !strcmp(FormatContext->iformat->name, "mpegts")) { av_close_input_file(FormatContext); - return MakeHaaliIndex(SourceFile, IndexMask, DumpMask, AudioFile, IgnoreDecodeErrors, 0, IP, Private, ErrorMsg, MsgSize); + return new FFHaaliIndexer(Filename, 0, ErrorMsg, MsgSize); } if (!strcmp(FormatContext->iformat->name, "ogg")) { av_close_input_file(FormatContext); - return MakeHaaliIndex(SourceFile, IndexMask, DumpMask, AudioFile, IgnoreDecodeErrors, 1, IP, Private, ErrorMsg, MsgSize); + return new FFHaaliIndexer(Filename, 1, ErrorMsg, MsgSize); } #endif + + return new FFLAVFIndexer(FormatContext, ErrorMsg, MsgSize); +} + +FFLAVFIndexer::FFLAVFIndexer(AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize) { + IsIndexing = false; + this->FormatContext = FormatContext; if (av_find_stream_info(FormatContext) < 0) { av_close_input_file(FormatContext); _snprintf(ErrorMsg, MsgSize, "Couldn't find stream information"); - return NULL; + throw ErrorMsg; } +} + +FFLAVFIndexer::~FFLAVFIndexer() { + if (!IsIndexing) + av_close_input_file(FormatContext); +} + +FFIndex *FFLAVFIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize) { + IsIndexing = true; // Audio stuff - int16_t *db; FFAudioContext *AudioContexts; FFIndexMemory IM = FFIndexMemory(FormatContext->nb_streams, db, AudioContexts, FormatContext); @@ -635,15 +601,15 @@ FFIndex *MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const ch for (unsigned int i = 0; i < FormatContext->nb_streams; i++) TrackIndices->push_back(FFTrack((int64_t)FormatContext->streams[i]->time_base.num * 1000, FormatContext->streams[i]->time_base.den, - FormatContext->streams[i]->codec->codec_type)); + static_cast(FormatContext->streams[i]->codec->codec_type))); AVPacket Packet, TempPacket; InitNullPacket(&Packet); InitNullPacket(&TempPacket); while (av_read_frame(FormatContext, &Packet) >= 0) { // Update progress - if (IP) { - if ((*IP)(FormatContext->pb->pos, FormatContext->file_size, Private)) { + if (IC) { + if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, Private)) { _snprintf(ErrorMsg, MsgSize, "Cancelled by user"); delete TrackIndices; return NULL; @@ -707,122 +673,3 @@ FFIndex *MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const ch SortTrackIndices(TrackIndices); return TrackIndices; } - -FFIndex *ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) { - std::ifstream Index(IndexFile, std::ios::in | std::ios::binary); - - if (!Index.is_open()) { - _snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for reading", IndexFile); - return NULL; - } - - // 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 NULL; - } - - if (IH.Version != INDEXVERSION) { - _snprintf(ErrorMsg, MsgSize, "'%s' is not the expected index version", IndexFile); - return NULL; - } - - if (IH.LAVUVersion != LIBAVUTIL_VERSION_INT || IH.LAVFVersion != LIBAVFORMAT_VERSION_INT || - IH.LAVCVersion != LIBAVCODEC_VERSION_INT || IH.LSWSVersion != LIBSWSCALE_VERSION_INT || - IH.LPPVersion != LIBPOSTPROC_VERSION_INT) { - _snprintf(ErrorMsg, MsgSize, "A different FFmpeg build was used to create this index", IndexFile); - return NULL; - } - - FFIndex *TrackIndices = new FFIndex(); - - try { - - TrackIndices->Decoder = IH.Decoder; - - for (unsigned int i = 0; i < IH.Tracks; i++) { - // Read how many records belong to the current stream - int 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)); - size_t Frames; - Index.read(reinterpret_cast(&Frames), sizeof(Frames)); - TrackIndices->push_back(FFTrack(Num, Den, TT)); - - TFrameInfo FI(0, false); - for (size_t j = 0; j < Frames; j++) { - Index.read(reinterpret_cast(&FI), sizeof(TFrameInfo)); - TrackIndices->at(i).push_back(FI); - } - } - - } catch (...) { - delete TrackIndices; - _snprintf(ErrorMsg, MsgSize, "Unknown error while reading index information in '%s'", IndexFile); - return NULL; - } - - return TrackIndices; -} - -int FFTrack::WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize) { - std::ofstream Timecodes(TimecodeFile, std::ios::out | std::ios::trunc); - - if (!Timecodes.is_open()) { - _snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for writing", TimecodeFile); - return 1; - } - - 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) { - 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 Frame = 0; - int64_t BestDiff = 0xFFFFFFFFFFFFFFLL; // big number - for (int i = 0; i < static_cast(size()); i++) { - int64_t CurrentDiff = FFABS(at(i).DTS - DTS); - if (CurrentDiff < BestDiff) { - BestDiff = CurrentDiff; - Frame = i; - } - } - - return Frame; -} - -int FFTrack::FindClosestKeyFrame(int Frame) { - Frame = FFMIN(FFMAX(Frame, 0), static_cast(size()) - 1); - for (int i = Frame; i > 0; i--) - if (at(i).KeyFrame) - return i; - return 0; -} - -FFTrack::FFTrack() { - this->TT = 0; - this->TB.Num = 0; - this->TB.Den = 0; -} - -FFTrack::FFTrack(int64_t Num, int64_t Den, int TT) { - this->TT = TT; - this->TB.Num = Num; - this->TB.Den = Den; -} diff --git a/aegisub/FFmpegSource2/indexing.h b/aegisub/FFmpegSource2/indexing.h index 3ffada699..36549fcb7 100644 --- a/aegisub/FFmpegSource2/indexing.h +++ b/aegisub/FFmpegSource2/indexing.h @@ -21,11 +21,10 @@ #ifndef INDEXING_H #define INDEXING_H -#include #include "utils.h" #include "ffms.h" -#define INDEXVERSION 21 +#define INDEXVERSION 24 #define INDEXID 0x53920873 struct IndexHeader { @@ -40,27 +39,67 @@ struct IndexHeader { uint32_t LPPVersion; }; -struct FFTrack : public std::vector { +class FFIndexer { +protected: + int IndexMask; + int DumpMask; + bool IgnoreDecodeErrors; + TIndexCallback IC; + void *Private; public: - int TT; - TTrackTimeBase TB; - - int FindClosestKeyFrame(int Frame); - int FrameFromDTS(int64_t DTS); - int ClosestFrameFromDTS(int64_t DTS); - int WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize); - - FFTrack(); - FFTrack(int64_t Num, int64_t Den, int TT); + static FFIndexer *CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize); + virtual ~FFIndexer() { } + void SetIndexMask(int IndexMask) { this->IndexMask = IndexMask; } + void SetDumpMask(int DumpMask) { this->DumpMask = DumpMask; } + void SetIgnoreDecodeErrors(bool IgnoreDecodeErrors) { this->IgnoreDecodeErrors = IgnoreDecodeErrors; } + void SetProgressCallback(TIndexCallback IC, void *Private) { this->IC = IC; this->Private = Private; } + virtual FFIndex *DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize) = 0; + virtual int GetNumberOfTracks() = 0; + virtual FFMS_TrackType GetTrackType(int Track) = 0; + virtual const char *GetTrackCodec(int Track) = 0; }; -struct FFIndex : public std::vector { +class FFLAVFIndexer : public FFIndexer { +private: + bool IsIndexing; + AVFormatContext *FormatContext; public: - int Decoder; - int WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize); + FFLAVFIndexer(AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize); + ~FFLAVFIndexer(); + FFIndex *DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize); + int GetNumberOfTracks() { return FormatContext->nb_streams; } + FFMS_TrackType GetTrackType(int Track) { return static_cast(FormatContext->streams[Track]->codec->codec_type); } + const char *GetTrackCodec(int Track) { return FormatContext->streams[Track]->codec->codec_name; } }; -FFIndex *MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, TIndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize); -FFIndex *ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize); +class FFMatroskaIndexer : public FFIndexer { +private: + MatroskaFile *MF; + MatroskaReaderContext MC; +public: + FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize); + FFIndex *DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize); + int GetNumberOfTracks() { return mkv_GetNumTracks(MF); } + FFMS_TrackType GetTrackType(int Track) { return HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, Track)->Type); } + const char *GetTrackCodec(int Track) { return mkv_GetTrackInfo(MF, Track)->CodecID; } +}; + +class FFHaaliIndexer : public FFIndexer { +private: + int SourceMode; + CComPtr pMMC; + int NumTracks; + FFMS_TrackType TrackType[32]; + AVCodec *Codec[32]; + uint8_t *CodecPrivate[32]; + int CodecPrivateSize[32]; +public: + FFHaaliIndexer(const char *Filename, int SourceMode, char *ErrorMsg, unsigned MsgSize); + ~FFHaaliIndexer() { for (int i = 0; i < 32; i++) delete[] CodecPrivate[i]; } + FFIndex *DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize); + int GetNumberOfTracks() { return NumTracks; } + FFMS_TrackType GetTrackType(int Track) { return TrackType[Track]; } + const char *GetTrackCodec(int Track) { if (Codec[Track]) return Codec[Track]->name; else return "Unsupported codec/Unknown codec name"; } +}; #endif diff --git a/aegisub/FFmpegSource2/utils.cpp b/aegisub/FFmpegSource2/utils.cpp index aa820c322..cb3d1d5ad 100644 --- a/aegisub/FFmpegSource2/utils.cpp +++ b/aegisub/FFmpegSource2/utils.cpp @@ -22,6 +22,8 @@ #include "indexing.h" #include #include +#include +#include #ifdef _MSC_VER # include #endif @@ -32,6 +34,200 @@ #define _snprintf snprintf #endif +TFrameInfo::TFrameInfo(int64_t DTS, bool KeyFrame) { + this->DTS = DTS; + this->KeyFrame = KeyFrame; + this->SampleStart = 0; + this->FilePos = 0; + this->FrameSize = 0; +} + +TFrameInfo::TFrameInfo(int64_t DTS, int64_t FilePos, unsigned int FrameSize, bool KeyFrame) { + this->DTS = DTS; + this->KeyFrame = KeyFrame; + this->SampleStart = 0; + this->FilePos = FilePos; + this->FrameSize = FrameSize; +} + +TFrameInfo::TFrameInfo(int64_t DTS, int64_t SampleStart, bool KeyFrame) { + this->DTS = DTS; + this->KeyFrame = KeyFrame; + this->SampleStart = SampleStart; + this->FilePos = 0; + this->FrameSize = 0; +} + +TFrameInfo::TFrameInfo(int64_t DTS, int64_t SampleStart, int64_t FilePos, unsigned int FrameSize, bool KeyFrame) { + this->DTS = DTS; + this->KeyFrame = KeyFrame; + this->SampleStart = SampleStart; + this->FilePos = FilePos; + this->FrameSize = FrameSize; +} + +int FFTrack::WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize) { + std::ofstream Timecodes(TimecodeFile, std::ios::out | std::ios::trunc); + + if (!Timecodes.is_open()) { + _snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for writing", TimecodeFile); + return 1; + } + + 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) { + 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 Frame = 0; + int64_t BestDiff = 0xFFFFFFFFFFFFFFLL; // big number + for (int i = 0; i < static_cast(size()); i++) { + int64_t CurrentDiff = FFABS(at(i).DTS - DTS); + if (CurrentDiff < BestDiff) { + BestDiff = CurrentDiff; + Frame = i; + } + } + + return Frame; +} + +int FFTrack::FindClosestVideoKeyFrame(int Frame) { + Frame = FFMIN(FFMAX(Frame, 0), static_cast(size()) - 1); + for (int i = Frame; i > 0; i--) + if (at(i).KeyFrame) + return i; + return 0; +} + +int FFTrack::FindClosestAudioKeyFrame(int64_t Sample) { + for (size_t i = 0; i < size(); i++) { + if (at(i).SampleStart == Sample && at(i).KeyFrame) + return i; + else if (at(i).SampleStart > Sample && at(i).KeyFrame) + return i - 1; + } + return size() - 1; +} + +FFTrack::FFTrack() { + this->TT = FFMS_TYPE_UNKNOWN; + this->TB.Num = 0; + this->TB.Den = 0; +} + +FFTrack::FFTrack(int64_t Num, int64_t Den, FFMS_TrackType TT) { + this->TT = TT; + this->TB.Num = Num; + this->TB.Den = Den; +} + +int FFIndex::WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) { + std::ofstream 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; + } + + // Write the index file header + IndexHeader IH; + IH.Id = INDEXID; + IH.Version = INDEXVERSION; + IH.Tracks = size(); + IH.Decoder = Decoder; + IH.LAVUVersion = LIBAVUTIL_VERSION_INT; + IH.LAVFVersion = LIBAVFORMAT_VERSION_INT; + IH.LAVCVersion = LIBAVCODEC_VERSION_INT; + IH.LSWSVersion = LIBSWSCALE_VERSION_INT; + IH.LPPVersion = LIBPOSTPROC_VERSION_INT; + + IndexStream.write(reinterpret_cast(&IH), sizeof(IH)); + + for (unsigned int i = 0; i < IH.Tracks; i++) { + int 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)); + size_t Frames = at(i).size(); + IndexStream.write(reinterpret_cast(&Frames), sizeof(Frames)); + + for (size_t j = 0; j < Frames; j++) + IndexStream.write(reinterpret_cast(&(at(i)[j])), sizeof(TFrameInfo)); + } + + return 0; +} + +int FFIndex::ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) { + std::ifstream Index(IndexFile, std::ios::in | std::ios::binary); + + if (!Index.is_open()) { + _snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for reading", IndexFile); + return NULL; + } + + // 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 NULL; + } + + if (IH.Version != INDEXVERSION) { + _snprintf(ErrorMsg, MsgSize, "'%s' is not the expected index version", IndexFile); + return NULL; + } + + if (IH.LAVUVersion != LIBAVUTIL_VERSION_INT || IH.LAVFVersion != LIBAVFORMAT_VERSION_INT || + IH.LAVCVersion != LIBAVCODEC_VERSION_INT || IH.LSWSVersion != LIBSWSCALE_VERSION_INT || + IH.LPPVersion != LIBPOSTPROC_VERSION_INT) { + _snprintf(ErrorMsg, MsgSize, "A different FFmpeg build was used to create this index", IndexFile); + return NULL; + } + + try { + Decoder = IH.Decoder; + + for (unsigned int i = 0; i < IH.Tracks; i++) { + // Read how many records belong to the current stream + FFMS_TrackType 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)); + size_t Frames; + Index.read(reinterpret_cast(&Frames), sizeof(Frames)); + push_back(FFTrack(Num, Den, TT)); + + TFrameInfo FI(0, false); + for (size_t j = 0; j < Frames; j++) { + Index.read(reinterpret_cast(&FI), sizeof(TFrameInfo)); + at(i).push_back(FI); + } + } + + } catch (...) { + _snprintf(ErrorMsg, MsgSize, "Unknown error while reading index information in '%s'", IndexFile); + return 1; + } +} + int GetCPUFlags() { // FIXME Add proper feature detection when msvc isn't used int Flags = PP_CPU_CAPS_MMX | PP_CPU_CAPS_MMX2; @@ -51,6 +247,15 @@ int GetCPUFlags() { return Flags; } +FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT) { + switch (TT) { + case TT_VIDEO: return FFMS_TYPE_VIDEO; break; + case TT_AUDIO: return FFMS_TYPE_AUDIO; break; + case TT_SUB: return FFMS_TYPE_SUBTITLE; break; + default: return FFMS_TYPE_UNKNOWN; + } +} + int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize) { if (CS) { char CSBuffer[4096]; diff --git a/aegisub/FFmpegSource2/utils.h b/aegisub/FFmpegSource2/utils.h index 9eb2223f0..f1eefad19 100644 --- a/aegisub/FFmpegSource2/utils.h +++ b/aegisub/FFmpegSource2/utils.h @@ -21,6 +21,9 @@ #ifndef UTILS_H #define UTILS_H +#include "ffms.h" +#include + extern "C" { #include #include @@ -31,8 +34,6 @@ extern "C" { #include "stdiostream.h" } -#include "ffms.h" - #ifdef HAALISOURCE # define _WIN32_DCOM # include @@ -44,6 +45,41 @@ extern "C" { # include "guids.h" #endif +struct TFrameInfo { + FFMS_FRAMEINFO_COMMON + int64_t SampleStart; + int64_t FilePos; + unsigned int FrameSize; +#ifdef FFMS_EXPORTS + TFrameInfo(int64_t DTS, bool KeyFrame); + TFrameInfo(int64_t DTS, int64_t FilePos, unsigned int FrameSize, bool KeyFrame); + TFrameInfo(int64_t DTS, int64_t SampleStart, bool KeyFrame); + TFrameInfo(int64_t DTS, int64_t SampleStart, int64_t FilePos, unsigned int FrameSize, bool KeyFrame); +#endif +}; + +struct FFTrack : public std::vector { +public: + FFMS_TrackType TT; + TTrackTimeBase 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); + + FFTrack(); + FFTrack(int64_t Num, int64_t Den, FFMS_TrackType TT); +}; + +struct FFIndex : public std::vector { +public: + int Decoder; + int WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize); + int ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize); +}; + struct MatroskaReaderContext { public: StdIoStream ST; @@ -62,6 +98,7 @@ public: }; int GetCPUFlags(); +FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT); int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize); bool AudioFMTIsFloat(SampleFormat FMT); void InitNullPacket(AVPacket *pkt); diff --git a/aegisub/src/video_provider_ffmpegsource.cpp b/aegisub/src/video_provider_ffmpegsource.cpp index c91e7fb4e..905e0028b 100644 --- a/aegisub/src/video_provider_ffmpegsource.cpp +++ b/aegisub/src/video_provider_ffmpegsource.cpp @@ -159,7 +159,7 @@ void FFmpegSourceVideoProvider::LoadVideo(Aegisub::String filename, double fps) if (TimeBase == NULL) throw _T("FFmpegSource video provider: failed to get track time base"); - const TFrameInfo *CurFrameData; + const FFFrameInfo *CurFrameData; // build list of keyframes and timecodes for (int CurFrameNum = 0; CurFrameNum < VideoInfo->NumFrames; CurFrameNum++) {