forked from mia/Aegisub
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.
This commit is contained in:
parent
1e52702f0a
commit
cd7932d6ee
12 changed files with 540 additions and 409 deletions
|
@ -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;
|
||||
|
@ -179,7 +169,7 @@ FFAudioSource::FFAudioSource(const char *SourceFile, int Track, FFIndex *Index,
|
|||
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;
|
||||
|
@ -389,11 +379,11 @@ MatroskaAudioSource::MatroskaAudioSource(const char *SourceFile, int Track, FFIn
|
|||
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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
TrackIndices->push_back(FFTrack(1, 1000000000, TrackTypes[CurrentTrack] - 1));
|
||||
|
||||
if (IndexMask & (1 << CurrentTrack) && TrackTypes[CurrentTrack] == TT_AUDIO) {
|
||||
if (IndexMask & (1 << i) && TrackType[i] == FFMS_TYPE_AUDIO) {
|
||||
AVCodecContext *AudioCodecContext = avcodec_alloc_context();
|
||||
AudioCodecContext->extradata = CodecPrivate;
|
||||
AudioCodecContext->extradata_size = CodecPrivateSize;
|
||||
AudioContexts[CurrentTrack].CTX = AudioCodecContext;
|
||||
AudioCodecContext->extradata = CodecPrivate[i];
|
||||
AudioCodecContext->extradata_size = CodecPrivateSize[i];
|
||||
AudioContexts[i].CTX = AudioCodecContext;
|
||||
|
||||
AVCodec *AudioCodec = CodecID;
|
||||
if (AudioCodec == NULL) {
|
||||
if (Codec[i] == NULL) {
|
||||
av_free(AudioCodecContext);
|
||||
AudioContexts[CurrentTrack].CTX = NULL;
|
||||
AudioContexts[i].CTX = NULL;
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
|
||||
if (avcodec_open(AudioCodecContext, Codec[i]) < 0) {
|
||||
av_free(AudioCodecContext);
|
||||
AudioContexts[CurrentTrack].CTX = NULL;
|
||||
AudioContexts[i].CTX = NULL;
|
||||
_snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
IndexMask &= ~(1 << CurrentTrack);
|
||||
}
|
||||
|
||||
pU = NULL;
|
||||
CurrentTrack++;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// Audio stuff
|
||||
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,8 +454,8 @@ 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;
|
||||
|
@ -512,9 +464,9 @@ static FFIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int Dum
|
|||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Audio stuff
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++) {
|
||||
|
|
Loading…
Reference in a new issue