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.
This commit is contained in:
Fredrik Mellbin 2009-05-20 18:57:03 +00:00
parent 083e2ee91c
commit 34c50c8cd9
12 changed files with 540 additions and 409 deletions

View file

@ -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;

View file

@ -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);
};

View file

@ -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<int>(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<int>(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<int>(T->size())) {
_snprintf(ErrorMsg, MsgSize, "Invalid frame specified");
return NULL;
} else {
return &(*T)[Frame];
return reinterpret_cast<FFFrameInfo *>(&(*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<int>(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);
}

View file

@ -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

View file

@ -13,6 +13,7 @@ Opens files using ffmpeg and nothing else. May be frame accurate on good days. T
<h2>Known issues</h2>
<ul>
<li>Requires <a href='http://haali.cs.msu.ru/mkv/'>Haali's Media Splitter</a> if ogm or mpeg ps/ts is to be opened.</li>
<li>Haali's Media Splitter is used the reported framerate is always 30</li>
<li>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.</li>
<li>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.
</ul>
@ -239,14 +240,15 @@ Note that --enable-w32threads is required for multithreaded decoding to work.
<h2>Changes</h2>
<ul>
<li>2.00 beta 9<ul>
<li>Now has stricter index checking to detect when different FFmpeg versions were used to create an inded of the same version</li>
<li>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</li>
<li>Now has stricter index checking to detect when different FFmpeg versions were used to create an index of the same version</li>
<li>Fixed an access violation occurring when unindexed or empty audio tracks in matroska files were opened</li>
<li>Less type conversion/signedness warnings</li>
<li>When audio track dumping is performed a custom callback can now be supplied to name the tracks</li>
<li>The audio track delay is now exposed in the API in the same way as video tracks</li>
<li>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</li>
<li>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</li>
<li>Updated FFmpeg to rev X (now with pthreads and faad2 again by popular demand, updated to GCC 4.4.0 for compiling all libraries)</li>
<li>Updated FFmpeg to rev X (now with faad2 again by popular demand, updated to GCC 4.4.0 for compiling all libraries)</li>
</ul></li>
<li>2.00 beta 8<ul>

View file

@ -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);

View file

@ -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<IMMContainer> 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);
};

View file

@ -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<char *>(&IH), sizeof(IH));
for (unsigned int i = 0; i < IH.Tracks; i++) {
int TT = at(i).TT;
IndexStream.write(reinterpret_cast<char *>(&TT), sizeof(TT));
int64_t Num = at(i).TB.Num;
IndexStream.write(reinterpret_cast<char *>(&Num), sizeof(Num));
int64_t Den = at(i).TB.Den;
IndexStream.write(reinterpret_cast<char *>(&Den), sizeof(Den));
size_t Frames = at(i).size();
IndexStream.write(reinterpret_cast<char *>(&Frames), sizeof(Frames));
for (size_t j = 0; j < Frames; j++)
IndexStream.write(reinterpret_cast<char *>(&(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<IMMContainer> pMMC;
if (FAILED(pMMC.CoCreateInstance(clsid))) {
_snprintf(ErrorMsg, MsgSize, "Can't create parser");
return NULL;
throw ErrorMsg;
}
CComPtr<IMemAlloc> pMA;
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
_snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
return NULL;
throw ErrorMsg;
}
CComPtr<IMMStream> 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<IMMStreamOpen> 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<IEnumUnknown> pEU;
if (SUCCEEDED(pMMC->EnumTracks(&pEU))) {
CComPtr<IUnknown> pU;
while (pEU->Next(1, &pU, NULL) == S_OK) {
NumTracks++;
CComQIPtr<IPropertyBag> 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<IUnknown> pU;
while (pEU->Next(1, &pU, NULL) == S_OK) {
CComQIPtr<IPropertyBag> 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<FFMS_TrackType>(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<char *>(&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<char *>(&TT), sizeof(TT));
int64_t Num;
Index.read(reinterpret_cast<char *>(&Num), sizeof(Num));
int64_t Den;
Index.read(reinterpret_cast<char *>(&Den), sizeof(Den));
size_t Frames;
Index.read(reinterpret_cast<char *>(&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<char *>(&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<int>(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<int>(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<int>(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;
}

View file

@ -21,11 +21,10 @@
#ifndef INDEXING_H
#define INDEXING_H
#include <vector>
#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<TFrameInfo> {
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<FFTrack> {
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<FFMS_TrackType>(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<IMMContainer> 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

View file

@ -22,6 +22,8 @@
#include "indexing.h"
#include <string.h>
#include <errno.h>
#include <iostream>
#include <fstream>
#ifdef _MSC_VER
# include <intrin.h>
#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<int>(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<int>(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<int>(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<char *>(&IH), sizeof(IH));
for (unsigned int i = 0; i < IH.Tracks; i++) {
int TT = at(i).TT;
IndexStream.write(reinterpret_cast<char *>(&TT), sizeof(TT));
int64_t Num = at(i).TB.Num;
IndexStream.write(reinterpret_cast<char *>(&Num), sizeof(Num));
int64_t Den = at(i).TB.Den;
IndexStream.write(reinterpret_cast<char *>(&Den), sizeof(Den));
size_t Frames = at(i).size();
IndexStream.write(reinterpret_cast<char *>(&Frames), sizeof(Frames));
for (size_t j = 0; j < Frames; j++)
IndexStream.write(reinterpret_cast<char *>(&(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<char *>(&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<char *>(&TT), sizeof(TT));
int64_t Num;
Index.read(reinterpret_cast<char *>(&Num), sizeof(Num));
int64_t Den;
Index.read(reinterpret_cast<char *>(&Den), sizeof(Den));
size_t Frames;
Index.read(reinterpret_cast<char *>(&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<char *>(&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];

View file

@ -21,6 +21,9 @@
#ifndef UTILS_H
#define UTILS_H
#include "ffms.h"
#include <vector>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
@ -31,8 +34,6 @@ extern "C" {
#include "stdiostream.h"
}
#include "ffms.h"
#ifdef HAALISOURCE
# define _WIN32_DCOM
# include <windows.h>
@ -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<TFrameInfo> {
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<FFTrack> {
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);

View file

@ -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++) {