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;
|
delete[] DecodingBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t FFAudio::FindClosestAudioKeyFrame(int64_t Sample) {
|
void FFLAVFAudio::Free(bool CloseCodec) {
|
||||||
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) {
|
|
||||||
if (CloseCodec)
|
if (CloseCodec)
|
||||||
avcodec_close(CodecContext);
|
avcodec_close(CodecContext);
|
||||||
av_close_input_file(FormatContext);
|
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;
|
FormatContext = NULL;
|
||||||
AVCodec *Codec = NULL;
|
AVCodec *Codec = NULL;
|
||||||
AudioTrack = Track;
|
AudioTrack = Track;
|
||||||
|
@ -176,10 +166,10 @@ FFAudioSource::FFAudioSource(const char *SourceFile, int Track, FFIndex *Index,
|
||||||
throw ErrorMsg;
|
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;
|
const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||||
int Ret = -1;
|
int Ret = -1;
|
||||||
*Count = 0;
|
*Count = 0;
|
||||||
|
@ -221,7 +211,7 @@ Done:
|
||||||
return Ret;
|
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;
|
const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||||
memset(Buf, 0, SizeConst * Count);
|
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 >= Start && CurrentSample <= CacheEnd)) {
|
||||||
if (CurrentSample != CacheEnd) {
|
if (CurrentSample != CacheEnd) {
|
||||||
PreDecBlocks = 15;
|
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);
|
av_seek_frame(FormatContext, AudioTrack, Frames[CurrentAudioBlock].DTS, AVSEEK_FLAG_BACKWARD);
|
||||||
avcodec_flush_buffers(CodecContext);
|
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);
|
av_free_packet(&Packet);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
CurrentAudioBlock = FindClosestAudioKeyFrame(CurrentSample);
|
CurrentAudioBlock = Frames.FindClosestAudioKeyFrame(CurrentSample);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t DecodeCount;
|
int64_t DecodeCount;
|
||||||
|
@ -297,11 +287,11 @@ int FFAudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count, char *Error
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFAudioSource::~FFAudioSource() {
|
FFLAVFAudio::~FFLAVFAudio() {
|
||||||
Free(true);
|
Free(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MatroskaAudioSource::Free(bool CloseCodec) {
|
void FFMatroskaAudio::Free(bool CloseCodec) {
|
||||||
if (CS)
|
if (CS)
|
||||||
cs_Destroy(CS);
|
cs_Destroy(CS);
|
||||||
if (MC.ST.fp) {
|
if (MC.ST.fp) {
|
||||||
|
@ -313,7 +303,7 @@ void MatroskaAudioSource::Free(bool CloseCodec) {
|
||||||
av_free(CodecContext);
|
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;
|
CodecContext = NULL;
|
||||||
AVCodec *Codec = NULL;
|
AVCodec *Codec = NULL;
|
||||||
TrackInfo *TI = NULL;
|
TrackInfo *TI = NULL;
|
||||||
|
@ -386,14 +376,14 @@ MatroskaAudioSource::MatroskaAudioSource(const char *SourceFile, int Track, FFIn
|
||||||
throw ErrorMsg;
|
throw ErrorMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioCache.Initialize((AP.Channels *AP.BitsPerSample) / 8, 50);
|
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
MatroskaAudioSource::~MatroskaAudioSource() {
|
FFMatroskaAudio::~FFMatroskaAudio() {
|
||||||
Free(true);
|
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;
|
const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||||
memset(Buf, 0, SizeConst * Count);
|
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 >= Start && CurrentSample <= CacheEnd)) {
|
||||||
if (CurrentSample != CacheEnd) {
|
if (CurrentSample != CacheEnd) {
|
||||||
PreDecBlocks = 15;
|
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);
|
avcodec_flush_buffers(CodecContext);
|
||||||
} else {
|
} else {
|
||||||
CurrentAudioBlock = FindClosestAudioKeyFrame(CurrentSample);
|
CurrentAudioBlock = Frames.FindClosestAudioKeyFrame(CurrentSample);
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t DecodeCount;
|
int64_t DecodeCount;
|
||||||
|
@ -442,7 +432,7 @@ int MatroskaAudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count, char
|
||||||
return 0;
|
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;
|
const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||||
int Ret = -1;
|
int Ret = -1;
|
||||||
*Count = 0;
|
*Count = 0;
|
||||||
|
|
|
@ -75,8 +75,6 @@ protected:
|
||||||
FFTrack Frames;
|
FFTrack Frames;
|
||||||
AVCodecContext *CodecContext;
|
AVCodecContext *CodecContext;
|
||||||
TAudioProperties AP;
|
TAudioProperties AP;
|
||||||
|
|
||||||
size_t FindClosestAudioKeyFrame(int64_t Sample);
|
|
||||||
public:
|
public:
|
||||||
FFAudio();
|
FFAudio();
|
||||||
~FFAudio();
|
~FFAudio();
|
||||||
|
@ -85,7 +83,7 @@ public:
|
||||||
virtual int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) = 0;
|
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:
|
private:
|
||||||
AVFormatContext *FormatContext;
|
AVFormatContext *FormatContext;
|
||||||
int AudioTrack;
|
int AudioTrack;
|
||||||
|
@ -93,13 +91,13 @@ private:
|
||||||
int DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, char *ErrorMsg, unsigned MsgSize);
|
int DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, char *ErrorMsg, unsigned MsgSize);
|
||||||
void Free(bool CloseCodec);
|
void Free(bool CloseCodec);
|
||||||
public:
|
public:
|
||||||
FFAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
||||||
~FFAudioSource();
|
~FFLAVFAudio();
|
||||||
|
|
||||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
class MatroskaAudioSource : public FFAudio {
|
class FFMatroskaAudio : public FFAudio {
|
||||||
private:
|
private:
|
||||||
MatroskaFile *MF;
|
MatroskaFile *MF;
|
||||||
MatroskaReaderContext MC;
|
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);
|
int DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, uint64_t FilePos, unsigned int FrameSize, char *ErrorMsg, unsigned MsgSize);
|
||||||
void Free(bool CloseCodec);
|
void Free(bool CloseCodec);
|
||||||
public:
|
public:
|
||||||
MatroskaAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
FFMatroskaAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
||||||
~MatroskaAudioSource();
|
~FFMatroskaAudio();
|
||||||
|
|
||||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
||||||
};
|
};
|
||||||
|
|
|
@ -31,30 +31,6 @@ extern "C" {
|
||||||
#define _snprintf snprintf
|
#define _snprintf snprintf
|
||||||
#endif
|
#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() {
|
FFMS_API(void) FFMS_Init() {
|
||||||
static bool InitDone = false;
|
static bool InitDone = false;
|
||||||
if (!InitDone) {
|
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) {
|
FFMS_API(FFVideo *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize) {
|
||||||
try {
|
try {
|
||||||
switch (Index->Decoder) {
|
switch (Index->Decoder) {
|
||||||
case 0: return new FFVideoSource(SourceFile, Track, Index, PP, Threads, SeekMode, ErrorMsg, MsgSize);
|
case 0: return new FFLAVFVideo(SourceFile, Track, Index, PP, Threads, SeekMode, ErrorMsg, MsgSize);
|
||||||
case 1: return new MatroskaVideoSource(SourceFile, Track, Index, PP, Threads, ErrorMsg, MsgSize);
|
case 1: return new FFMatroskaVideo(SourceFile, Track, Index, PP, Threads, ErrorMsg, MsgSize);
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
case 2: return new HaaliVideoSource(SourceFile, Track, Index, PP, Threads, 0, ErrorMsg, MsgSize);
|
case 2: return new FFHaaliVideo(SourceFile, Track, Index, PP, Threads, 0, ErrorMsg, MsgSize);
|
||||||
case 3: return new HaaliVideoSource(SourceFile, Track, Index, PP, Threads, 1, ErrorMsg, MsgSize);
|
case 3: return new FFHaaliVideo(SourceFile, Track, Index, PP, Threads, 1, ErrorMsg, MsgSize);
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
_snprintf(ErrorMsg, MsgSize, "Unsupported format");
|
_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) {
|
FFMS_API(FFAudio *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) {
|
||||||
try {
|
try {
|
||||||
switch (Index->Decoder) {
|
switch (Index->Decoder) {
|
||||||
case 0: return new FFAudioSource(SourceFile, Track, Index, ErrorMsg, MsgSize);
|
case 0: return new FFLAVFAudio(SourceFile, Track, Index, ErrorMsg, MsgSize);
|
||||||
case 1: return new MatroskaAudioSource(SourceFile, Track, Index, ErrorMsg, MsgSize);
|
case 1: return new FFMatroskaAudio(SourceFile, Track, Index, ErrorMsg, MsgSize);
|
||||||
default:
|
default:
|
||||||
_snprintf(ErrorMsg, MsgSize, "Unsupported format");
|
_snprintf(ErrorMsg, MsgSize, "Unsupported format");
|
||||||
return NULL;
|
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) {
|
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++)
|
for (int i = 0; i < static_cast<int>(Index->size()); i++)
|
||||||
if ((*Index)[i].TT == TrackType && (*Index)[i].size() > 0)
|
if ((*Index)[i].TT == TrackType && (*Index)[i].size() > 0)
|
||||||
return i;
|
return i;
|
||||||
|
@ -156,20 +140,28 @@ FFMS_API(int) FFMS_GetNumTracks(FFIndex *Index) {
|
||||||
return Index->size();
|
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;
|
return T->TT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FFMS_API(FFMS_TrackType) FFMS_GetTrackTypeI(FFIndexer *Indexer, int Track) {
|
||||||
|
return Indexer->GetTrackType(Track);
|
||||||
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_GetNumFrames(FFTrack *T) {
|
FFMS_API(int) FFMS_GetNumFrames(FFTrack *T) {
|
||||||
return T->size();
|
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())) {
|
if (Frame < 0 || Frame >= static_cast<int>(T->size())) {
|
||||||
_snprintf(ErrorMsg, MsgSize, "Invalid frame specified");
|
_snprintf(ErrorMsg, MsgSize, "Invalid frame specified");
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
} else {
|
||||||
return &(*T)[Frame];
|
return reinterpret_cast<FFFrameInfo *>(&(*T)[Frame]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,15 +182,6 @@ FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A) {
|
||||||
return A->GetFFTrack();
|
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) {
|
FFMS_API(const TTrackTimeBase *) FFMS_GetTimeBase(FFTrack *T) {
|
||||||
return &T->TB;
|
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);
|
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) {
|
FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize) {
|
||||||
return MakeIndex(SourceFile, IndexMask, DumpMask, AudioFile, IgnoreDecodeErrors, IP, Private, ErrorMsg, 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) {
|
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) {
|
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) {
|
FFMS_API(int) FFMS_GetPixFmt(const char *Name) {
|
||||||
return avcodec_get_pix_fmt(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 FFVideo;
|
||||||
struct FFAudio;
|
struct FFAudio;
|
||||||
|
struct FFIndexer;
|
||||||
struct FFIndex;
|
struct FFIndex;
|
||||||
struct FFTrack;
|
struct FFTrack;
|
||||||
|
|
||||||
|
@ -55,8 +56,12 @@ enum FFMS_SeekMode {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FFMS_TrackType {
|
enum FFMS_TrackType {
|
||||||
FFMS_TYPE_VIDEO = 0,
|
FFMS_TYPE_UNKNOWN = -1,
|
||||||
FFMS_TYPE_AUDIO = 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.
|
// This is a subset of the original AVFrame only containing the most used parts.
|
||||||
|
@ -77,17 +82,10 @@ struct TTrackTimeBase {
|
||||||
int64_t Den;
|
int64_t Den;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TFrameInfo {
|
#define FFMS_FRAMEINFO_COMMON int64_t DTS; bool KeyFrame;
|
||||||
int64_t DTS;
|
|
||||||
int64_t SampleStart;
|
struct FFFrameInfo {
|
||||||
int64_t FilePos;
|
FFMS_FRAMEINFO_COMMON
|
||||||
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
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TVideoProperties {
|
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_ResetOutputFormat(FFVideo *V);
|
||||||
FFMS_API(void) FFMS_DestroyFFIndex(FFIndex *Index);
|
FFMS_API(void) FFMS_DestroyFFIndex(FFIndex *Index);
|
||||||
FFMS_API(int) FFMS_GetFirstTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize);
|
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_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(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_GetTrackFromIndex(FFIndex *Index, int Track, char *ErrorMsg, unsigned MsgSize);
|
||||||
FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V);
|
FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V);
|
||||||
FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A);
|
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(const TTrackTimeBase *) FFMS_GetTimeBase(FFTrack *T);
|
||||||
FFMS_API(int) FFMS_WriteTimecodes(FFTrack *T, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize);
|
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(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_WriteIndex(const char *IndexFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
||||||
FFMS_API(int) FFMS_GetPixFmt(const char *Name);
|
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
|
#endif
|
||||||
|
|
|
@ -13,6 +13,7 @@ Opens files using ffmpeg and nothing else. May be frame accurate on good days. T
|
||||||
<h2>Known issues</h2>
|
<h2>Known issues</h2>
|
||||||
<ul>
|
<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>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>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.
|
<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>
|
</ul>
|
||||||
|
@ -239,14 +240,15 @@ Note that --enable-w32threads is required for multithreaded decoding to work.
|
||||||
<h2>Changes</h2>
|
<h2>Changes</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li>2.00 beta 9<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>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>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>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>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>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>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>
|
</ul></li>
|
||||||
|
|
||||||
<li>2.00 beta 8<ul>
|
<li>2.00 beta 8<ul>
|
||||||
|
|
|
@ -171,7 +171,7 @@ void FFVideo::ResetOutputFormat() {
|
||||||
VP.VPixelFormat = CodecContext->pix_fmt;
|
VP.VPixelFormat = CodecContext->pix_fmt;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFVideoSource::Free(bool CloseCodec) {
|
void FFLAVFVideo::Free(bool CloseCodec) {
|
||||||
if (CloseCodec)
|
if (CloseCodec)
|
||||||
avcodec_close(CodecContext);
|
avcodec_close(CodecContext);
|
||||||
av_close_input_file(FormatContext);
|
av_close_input_file(FormatContext);
|
||||||
|
@ -179,7 +179,7 @@ void FFVideoSource::Free(bool CloseCodec) {
|
||||||
//av_free(FormatContext);
|
//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) {
|
const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize) {
|
||||||
|
|
||||||
FormatContext = NULL;
|
FormatContext = NULL;
|
||||||
|
@ -275,11 +275,11 @@ FFVideoSource::FFVideoSource(const char *SourceFile, int Track, FFIndex *Index,
|
||||||
VP.SARDen = CodecContext->sample_aspect_ratio.den;
|
VP.SARDen = CodecContext->sample_aspect_ratio.den;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFVideoSource::~FFVideoSource() {
|
FFLAVFVideo::~FFLAVFVideo() {
|
||||||
Free(true);
|
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;
|
AVPacket Packet;
|
||||||
InitNullPacket(&Packet);
|
InitNullPacket(&Packet);
|
||||||
int FrameFinished = 0;
|
int FrameFinished = 0;
|
||||||
|
@ -315,7 +315,7 @@ Done:
|
||||||
return 0;
|
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
|
// PPFrame always holds frame LastFrameNum even if no PP is applied
|
||||||
if (LastFrameNum == n)
|
if (LastFrameNum == n)
|
||||||
return OutputFrame(DecodeFrame);
|
return OutputFrame(DecodeFrame);
|
||||||
|
@ -325,7 +325,7 @@ TAVFrameLite *FFVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) {
|
||||||
|
|
||||||
int ClosestKF = 0;
|
int ClosestKF = 0;
|
||||||
if (SeekMode >= 0) {
|
if (SeekMode >= 0) {
|
||||||
ClosestKF = Frames.FindClosestKeyFrame(n);
|
ClosestKF = Frames.FindClosestVideoKeyFrame(n);
|
||||||
|
|
||||||
if (SeekMode == 0) {
|
if (SeekMode == 0) {
|
||||||
if (n < CurrentFrame) {
|
if (n < CurrentFrame) {
|
||||||
|
@ -385,7 +385,7 @@ ReSeek:
|
||||||
return OutputFrame(DecodeFrame);
|
return OutputFrame(DecodeFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MatroskaVideoSource::Free(bool CloseCodec) {
|
void FFMatroskaVideo::Free(bool CloseCodec) {
|
||||||
if (CS)
|
if (CS)
|
||||||
cs_Destroy(CS);
|
cs_Destroy(CS);
|
||||||
if (MC.ST.fp) {
|
if (MC.ST.fp) {
|
||||||
|
@ -397,7 +397,7 @@ void MatroskaVideoSource::Free(bool CloseCodec) {
|
||||||
av_free(CodecContext);
|
av_free(CodecContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
MatroskaVideoSource::MatroskaVideoSource(const char *SourceFile, int Track,
|
FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
|
||||||
FFIndex *Index, const char *PP,
|
FFIndex *Index, const char *PP,
|
||||||
int Threads, char *ErrorMsg, unsigned MsgSize) {
|
int Threads, char *ErrorMsg, unsigned MsgSize) {
|
||||||
|
|
||||||
|
@ -506,11 +506,11 @@ MatroskaVideoSource::MatroskaVideoSource(const char *SourceFile, int Track,
|
||||||
VP.CropBottom = TI->AV.Video.CropB;
|
VP.CropBottom = TI->AV.Video.CropB;
|
||||||
}
|
}
|
||||||
|
|
||||||
MatroskaVideoSource::~MatroskaVideoSource() {
|
FFMatroskaVideo::~FFMatroskaVideo() {
|
||||||
Free(true);
|
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;
|
int FrameFinished = 0;
|
||||||
*AFirstStartTime = -1;
|
*AFirstStartTime = -1;
|
||||||
AVPacket Packet;
|
AVPacket Packet;
|
||||||
|
@ -553,14 +553,14 @@ Done:
|
||||||
return 0;
|
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
|
// PPFrame always holds frame LastFrameNum even if no PP is applied
|
||||||
if (LastFrameNum == n)
|
if (LastFrameNum == n)
|
||||||
return OutputFrame(DecodeFrame);
|
return OutputFrame(DecodeFrame);
|
||||||
|
|
||||||
bool HasSeeked = false;
|
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);
|
mkv_Seek(MF, Frames[n].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT);
|
||||||
avcodec_flush_buffers(CodecContext);
|
avcodec_flush_buffers(CodecContext);
|
||||||
HasSeeked = true;
|
HasSeeked = true;
|
||||||
|
@ -589,14 +589,14 @@ TAVFrameLite *MatroskaVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgS
|
||||||
|
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
|
|
||||||
void HaaliVideoSource::Free(bool CloseCodec) {
|
void FFHaaliVideo::Free(bool CloseCodec) {
|
||||||
if (CloseCodec)
|
if (CloseCodec)
|
||||||
avcodec_close(CodecContext);
|
avcodec_close(CodecContext);
|
||||||
av_free(CodecContext);
|
av_free(CodecContext);
|
||||||
delete[] CodecPrivate;
|
delete[] CodecPrivate;
|
||||||
}
|
}
|
||||||
|
|
||||||
HaaliVideoSource::HaaliVideoSource(const char *SourceFile, int Track,
|
FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
||||||
FFIndex *Index, const char *PP,
|
FFIndex *Index, const char *PP,
|
||||||
int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize) {
|
int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize) {
|
||||||
|
|
||||||
|
@ -723,14 +723,6 @@ HaaliVideoSource::HaaliVideoSource(const char *SourceFile, int Track,
|
||||||
throw ErrorMsg;
|
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
|
// Output the already decoded frame so it isn't wasted
|
||||||
OutputFrame(DecodeFrame);
|
OutputFrame(DecodeFrame);
|
||||||
|
|
||||||
|
@ -745,11 +737,11 @@ HaaliVideoSource::HaaliVideoSource(const char *SourceFile, int Track,
|
||||||
VP.SARDen = pV.uiVal;
|
VP.SARDen = pV.uiVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
HaaliVideoSource::~HaaliVideoSource() {
|
FFHaaliVideo::~FFHaaliVideo() {
|
||||||
Free(true);
|
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;
|
int FrameFinished = 0;
|
||||||
*AFirstStartTime = -1;
|
*AFirstStartTime = -1;
|
||||||
AVPacket Packet;
|
AVPacket Packet;
|
||||||
|
@ -798,7 +790,7 @@ Done:
|
||||||
return 0;
|
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
|
// PPFrame always holds frame LastFrameNum even if no PP is applied
|
||||||
if (LastFrameNum == n)
|
if (LastFrameNum == n)
|
||||||
return OutputFrame(DecodeFrame);
|
return OutputFrame(DecodeFrame);
|
||||||
|
@ -806,7 +798,7 @@ TAVFrameLite *HaaliVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSize
|
||||||
bool HasSeeked = false;
|
bool HasSeeked = false;
|
||||||
int SeekOffset = 0;
|
int SeekOffset = 0;
|
||||||
|
|
||||||
if (n < CurrentFrame || Frames.FindClosestKeyFrame(n) > CurrentFrame + 10) {
|
if (n < CurrentFrame || Frames.FindClosestVideoKeyFrame(n) > CurrentFrame + 10) {
|
||||||
ReSeek:
|
ReSeek:
|
||||||
pMMC->Seek(Frames[n + SeekOffset].DTS, MMSF_PREV_KF);
|
pMMC->Seek(Frames[n + SeekOffset].DTS, MMSF_PREV_KF);
|
||||||
avcodec_flush_buffers(CodecContext);
|
avcodec_flush_buffers(CodecContext);
|
||||||
|
|
|
@ -73,7 +73,7 @@ public:
|
||||||
void ResetOutputFormat();
|
void ResetOutputFormat();
|
||||||
};
|
};
|
||||||
|
|
||||||
class FFVideoSource : public FFVideo {
|
class FFLAVFVideo : public FFVideo {
|
||||||
private:
|
private:
|
||||||
AVFormatContext *FormatContext;
|
AVFormatContext *FormatContext;
|
||||||
int SeekMode;
|
int SeekMode;
|
||||||
|
@ -81,12 +81,12 @@ private:
|
||||||
void Free(bool CloseCodec);
|
void Free(bool CloseCodec);
|
||||||
int DecodeNextFrame(AVFrame *Frame, int64_t *DTS, char *ErrorMsg, unsigned MsgSize);
|
int DecodeNextFrame(AVFrame *Frame, int64_t *DTS, char *ErrorMsg, unsigned MsgSize);
|
||||||
public:
|
public:
|
||||||
FFVideoSource(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize);
|
FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize);
|
||||||
~FFVideoSource();
|
~FFLAVFVideo();
|
||||||
TAVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
TAVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
class MatroskaVideoSource : public FFVideo {
|
class FFMatroskaVideo : public FFVideo {
|
||||||
private:
|
private:
|
||||||
MatroskaFile *MF;
|
MatroskaFile *MF;
|
||||||
MatroskaReaderContext MC;
|
MatroskaReaderContext MC;
|
||||||
|
@ -96,22 +96,22 @@ private:
|
||||||
void Free(bool CloseCodec);
|
void Free(bool CloseCodec);
|
||||||
int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
|
int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
|
||||||
public:
|
public:
|
||||||
MatroskaVideoSource(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, char *ErrorMsg, unsigned MsgSize);
|
FFMatroskaVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, char *ErrorMsg, unsigned MsgSize);
|
||||||
~MatroskaVideoSource();
|
~FFMatroskaVideo();
|
||||||
TAVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
TAVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
|
|
||||||
class HaaliVideoSource : public FFVideo {
|
class FFHaaliVideo : public FFVideo {
|
||||||
private:
|
private:
|
||||||
CComPtr<IMMContainer> pMMC;
|
CComPtr<IMMContainer> pMMC;
|
||||||
uint8_t * CodecPrivate;
|
uint8_t * CodecPrivate;
|
||||||
void Free(bool CloseCodec);
|
void Free(bool CloseCodec);
|
||||||
int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
|
int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
|
||||||
public:
|
public:
|
||||||
HaaliVideoSource(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize);
|
FFHaaliVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize);
|
||||||
~HaaliVideoSource();
|
~FFHaaliVideo();
|
||||||
TAVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
TAVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -172,69 +172,34 @@ static void SortTrackIndices(FFIndex *Index) {
|
||||||
std::sort(Cur->begin(), Cur->end(), DTSComparison);
|
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
|
#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);
|
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||||
|
|
||||||
CLSID clsid = HAALI_TS_Parser;
|
CLSID clsid = HAALI_TS_Parser;
|
||||||
if (SourceMode == 1)
|
if (SourceMode == 1)
|
||||||
clsid = HAALI_OGM_Parser;
|
clsid = HAALI_OGM_Parser;
|
||||||
|
|
||||||
CComPtr<IMMContainer> pMMC;
|
|
||||||
if (FAILED(pMMC.CoCreateInstance(clsid))) {
|
if (FAILED(pMMC.CoCreateInstance(clsid))) {
|
||||||
_snprintf(ErrorMsg, MsgSize, "Can't create parser");
|
_snprintf(ErrorMsg, MsgSize, "Can't create parser");
|
||||||
return NULL;
|
throw ErrorMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
CComPtr<IMemAlloc> pMA;
|
CComPtr<IMemAlloc> pMA;
|
||||||
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
|
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
|
||||||
_snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
|
_snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
|
||||||
return NULL;
|
throw ErrorMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
CComPtr<IMMStream> pMS;
|
CComPtr<IMMStream> pMS;
|
||||||
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) {
|
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) {
|
||||||
_snprintf(ErrorMsg, MsgSize, "Can't create disk file reader");
|
_snprintf(ErrorMsg, MsgSize, "Can't create disk file reader");
|
||||||
return NULL;
|
throw ErrorMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
WCHAR WSourceFile[2048];
|
WCHAR WSourceFile[2048];
|
||||||
|
@ -242,26 +207,51 @@ static FFIndex *MakeHaaliIndex(const char *SourceFile, int IndexMask, int DumpMa
|
||||||
CComQIPtr<IMMStreamOpen> pMSO(pMS);
|
CComQIPtr<IMMStreamOpen> pMSO(pMS);
|
||||||
if (FAILED(pMSO->Open(WSourceFile))) {
|
if (FAILED(pMSO->Open(WSourceFile))) {
|
||||||
_snprintf(ErrorMsg, MsgSize, "Can't open file");
|
_snprintf(ErrorMsg, MsgSize, "Can't open file");
|
||||||
return NULL;
|
throw ErrorMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
|
if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
|
||||||
_snprintf(ErrorMsg, MsgSize, "Can't parse file");
|
_snprintf(ErrorMsg, MsgSize, "Can't parse file");
|
||||||
return NULL;
|
throw ErrorMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
int NumTracks = 0;
|
NumTracks = 0;
|
||||||
CComPtr<IEnumUnknown> pEU;
|
CComPtr<IEnumUnknown> pEU;
|
||||||
if (SUCCEEDED(pMMC->EnumTracks(&pEU))) {
|
if (SUCCEEDED(pMMC->EnumTracks(&pEU))) {
|
||||||
CComPtr<IUnknown> pU;
|
CComPtr<IUnknown> pU;
|
||||||
while (pEU->Next(1, &pU, NULL) == S_OK) {
|
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;
|
pU = NULL;
|
||||||
|
NumTracks++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FFIndex *FFHaaliIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize) {
|
||||||
// Audio stuff
|
// Audio stuff
|
||||||
|
|
||||||
int16_t *db;
|
int16_t *db;
|
||||||
MatroskaAudioContext *AudioContexts;
|
MatroskaAudioContext *AudioContexts;
|
||||||
HaaliIndexMemory IM = HaaliIndexMemory(NumTracks, db, AudioContexts);
|
HaaliIndexMemory IM = HaaliIndexMemory(NumTracks, db, AudioContexts);
|
||||||
|
@ -271,68 +261,31 @@ static FFIndex *MakeHaaliIndex(const char *SourceFile, int IndexMask, int DumpMa
|
||||||
if (SourceMode == 1)
|
if (SourceMode == 1)
|
||||||
TrackIndices->Decoder = 3;
|
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) {
|
for (int i = 0; i < NumTracks; i++) {
|
||||||
CComVariant pV;
|
TrackIndices->push_back(FFTrack(1, 1000000000, TrackType[i]));
|
||||||
|
|
||||||
pV.Clear();
|
if (IndexMask & (1 << i) && TrackType[i] == FFMS_TYPE_AUDIO) {
|
||||||
if (SUCCEEDED(pBag->Read(L"Type", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
|
AVCodecContext *AudioCodecContext = avcodec_alloc_context();
|
||||||
TrackTypes[CurrentTrack] = pV.uintVal;
|
AudioCodecContext->extradata = CodecPrivate[i];
|
||||||
|
AudioCodecContext->extradata_size = CodecPrivateSize[i];
|
||||||
|
AudioContexts[i].CTX = AudioCodecContext;
|
||||||
|
|
||||||
pV.Clear();
|
if (Codec[i] == NULL) {
|
||||||
if (SUCCEEDED(pBag->Read(L"CodecPrivate", &pV, NULL))) {
|
av_free(AudioCodecContext);
|
||||||
CodecPrivateSize = vtSize(pV);
|
AudioContexts[i].CTX = NULL;
|
||||||
CodecPrivate = new uint8_t[CodecPrivateSize];
|
_snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
||||||
vtCopy(pV, CodecPrivate);
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
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 (avcodec_open(AudioCodecContext, Codec[i]) < 0) {
|
||||||
|
av_free(AudioCodecContext);
|
||||||
if (IndexMask & (1 << CurrentTrack) && TrackTypes[CurrentTrack] == TT_AUDIO) {
|
AudioContexts[i].CTX = NULL;
|
||||||
AVCodecContext *AudioCodecContext = avcodec_alloc_context();
|
_snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
||||||
AudioCodecContext->extradata = CodecPrivate;
|
return NULL;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
pU = NULL;
|
IndexMask &= ~(1 << i);
|
||||||
CurrentTrack++;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
|
@ -341,8 +294,8 @@ static FFIndex *MakeHaaliIndex(const char *SourceFile, int IndexMask, int DumpMa
|
||||||
InitNullPacket(&TempPacket);
|
InitNullPacket(&TempPacket);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (IP) {
|
if (IC) {
|
||||||
if ((*IP)(0, 1, Private)) {
|
if ((*IC)(0, 1, Private)) {
|
||||||
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
||||||
delete TrackIndices;
|
delete TrackIndices;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -359,10 +312,10 @@ static FFIndex *MakeHaaliIndex(const char *SourceFile, int IndexMask, int DumpMa
|
||||||
unsigned int CurrentTrack = pMMF->GetTrack();
|
unsigned int CurrentTrack = pMMF->GetTrack();
|
||||||
|
|
||||||
// Only create index entries for video for now to save space
|
// 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));
|
(*TrackIndices)[CurrentTrack].push_back(TFrameInfo(Ts, pMMF->IsSyncPoint() == S_OK));
|
||||||
} else if (TrackTypes[CurrentTrack] == TT_AUDIO && (IndexMask & (1 << CurrentTrack))) {
|
} else if (TrackType[CurrentTrack] == FFMS_TYPE_AUDIO && (IndexMask & (1 << CurrentTrack))) {
|
||||||
(*TrackIndices)[CurrentTrack].push_back(TFrameInfo(AudioContexts[CurrentTrack].CurrentSample, 0 /* FIXME? */, pMMF->GetActualDataLength(), pMMF->IsSyncPoint() == S_OK));
|
(*TrackIndices)[CurrentTrack].push_back(TFrameInfo(Ts, AudioContexts[CurrentTrack].CurrentSample, 0 /* FIXME? */, pMMF->GetActualDataLength(), pMMF->IsSyncPoint() == S_OK));
|
||||||
AVCodecContext *AudioCodecContext = AudioContexts[CurrentTrack].CTX;
|
AVCodecContext *AudioCodecContext = AudioContexts[CurrentTrack].CTX;
|
||||||
pMMF->GetPointer(&TempPacket.data);
|
pMMF->GetPointer(&TempPacket.data);
|
||||||
TempPacket.size = pMMF->GetActualDataLength();
|
TempPacket.size = pMMF->GetActualDataLength();
|
||||||
|
@ -416,18 +369,14 @@ static FFIndex *MakeHaaliIndex(const char *SourceFile, int IndexMask, int DumpMa
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static FFIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, TIndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize) {
|
FFMatroskaIndexer::FFMatroskaIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize) {
|
||||||
MatroskaFile *MF;
|
|
||||||
char ErrorMessage[256];
|
char ErrorMessage[256];
|
||||||
MatroskaReaderContext MC;
|
|
||||||
MC.Buffer = NULL;
|
|
||||||
MC.BufferSize = 0;
|
|
||||||
|
|
||||||
InitStdIoStream(&MC.ST);
|
InitStdIoStream(&MC.ST);
|
||||||
MC.ST.fp = fopen(SourceFile, "rb");
|
MC.ST.fp = fopen(SourceFile, "rb");
|
||||||
if (MC.ST.fp == NULL) {
|
if (MC.ST.fp == NULL) {
|
||||||
_snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
|
_snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
|
||||||
return NULL;
|
throw ErrorMsg;
|
||||||
}
|
}
|
||||||
|
|
||||||
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
||||||
|
@ -436,11 +385,14 @@ static FFIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int Dum
|
||||||
if (MF == NULL) {
|
if (MF == NULL) {
|
||||||
fclose(MC.ST.fp);
|
fclose(MC.ST.fp);
|
||||||
_snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage);
|
_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
|
// Audio stuff
|
||||||
|
|
||||||
int16_t *db;
|
int16_t *db;
|
||||||
MatroskaAudioContext *AudioContexts;
|
MatroskaAudioContext *AudioContexts;
|
||||||
MatroskaIndexMemory IM = MatroskaIndexMemory(mkv_GetNumTracks(MF), db, AudioContexts, MF, &MC);
|
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;
|
TrackIndices->Decoder = 1;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++)
|
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;
|
ulonglong StartTime, EndTime, FilePos;
|
||||||
unsigned int Track, FrameFlags, FrameSize;
|
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) {
|
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) {
|
||||||
// Update progress
|
// Update progress
|
||||||
if (IP) {
|
if (IC) {
|
||||||
if ((*IP)(_ftelli64(MC.ST.fp), SourceSize, Private)) {
|
if ((*IC)(_ftelli64(MC.ST.fp), SourceSize, Private)) {
|
||||||
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
||||||
delete TrackIndices;
|
delete TrackIndices;
|
||||||
return NULL;
|
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
|
// Only create index entries for video for now to save space
|
||||||
if (mkv_GetTrackInfo(MF, Track)->Type == TT_VIDEO) {
|
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))) {
|
} 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);
|
ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC, ErrorMsg, MsgSize);
|
||||||
AVCodecContext *AudioCodecContext = AudioContexts[Track].CTX;
|
AVCodecContext *AudioCodecContext = AudioContexts[Track].CTX;
|
||||||
TempPacket.data = MC.Buffer;
|
TempPacket.data = MC.Buffer;
|
||||||
|
@ -566,42 +518,56 @@ static FFIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int Dum
|
||||||
return TrackIndices;
|
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;
|
AVFormatContext *FormatContext = NULL;
|
||||||
IndexMask |= DumpMask;
|
|
||||||
|
|
||||||
if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) {
|
if (av_open_input_file(&FormatContext, Filename, NULL, 0, NULL) != 0) {
|
||||||
_snprintf(ErrorMsg, MsgSize, "Can't open '%s'", SourceFile);
|
_snprintf(ErrorMsg, MsgSize, "Can't open '%s'", Filename);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do matroska indexing instead?
|
// Do matroska indexing instead?
|
||||||
if (!strcmp(FormatContext->iformat->name, "matroska")) {
|
if (!strcmp(FormatContext->iformat->name, "matroska")) {
|
||||||
av_close_input_file(FormatContext);
|
av_close_input_file(FormatContext);
|
||||||
return MakeMatroskaIndex(SourceFile, IndexMask, DumpMask, AudioFile, IgnoreDecodeErrors, IP, Private, ErrorMsg, MsgSize);
|
return new FFMatroskaIndexer(Filename, ErrorMsg, MsgSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
// Do haali ts indexing instead?
|
// Do haali ts indexing instead?
|
||||||
if (!strcmp(FormatContext->iformat->name, "mpeg") || !strcmp(FormatContext->iformat->name, "mpegts")) {
|
if (!strcmp(FormatContext->iformat->name, "mpeg") || !strcmp(FormatContext->iformat->name, "mpegts")) {
|
||||||
av_close_input_file(FormatContext);
|
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")) {
|
if (!strcmp(FormatContext->iformat->name, "ogg")) {
|
||||||
av_close_input_file(FormatContext);
|
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
|
#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) {
|
if (av_find_stream_info(FormatContext) < 0) {
|
||||||
av_close_input_file(FormatContext);
|
av_close_input_file(FormatContext);
|
||||||
_snprintf(ErrorMsg, MsgSize, "Couldn't find stream information");
|
_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
|
// Audio stuff
|
||||||
|
|
||||||
int16_t *db;
|
int16_t *db;
|
||||||
FFAudioContext *AudioContexts;
|
FFAudioContext *AudioContexts;
|
||||||
FFIndexMemory IM = FFIndexMemory(FormatContext->nb_streams, db, AudioContexts, FormatContext);
|
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++)
|
for (unsigned int i = 0; i < FormatContext->nb_streams; i++)
|
||||||
TrackIndices->push_back(FFTrack((int64_t)FormatContext->streams[i]->time_base.num * 1000,
|
TrackIndices->push_back(FFTrack((int64_t)FormatContext->streams[i]->time_base.num * 1000,
|
||||||
FormatContext->streams[i]->time_base.den,
|
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;
|
AVPacket Packet, TempPacket;
|
||||||
InitNullPacket(&Packet);
|
InitNullPacket(&Packet);
|
||||||
InitNullPacket(&TempPacket);
|
InitNullPacket(&TempPacket);
|
||||||
while (av_read_frame(FormatContext, &Packet) >= 0) {
|
while (av_read_frame(FormatContext, &Packet) >= 0) {
|
||||||
// Update progress
|
// Update progress
|
||||||
if (IP) {
|
if (IC) {
|
||||||
if ((*IP)(FormatContext->pb->pos, FormatContext->file_size, Private)) {
|
if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, Private)) {
|
||||||
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
||||||
delete TrackIndices;
|
delete TrackIndices;
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -707,122 +673,3 @@ FFIndex *MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const ch
|
||||||
SortTrackIndices(TrackIndices);
|
SortTrackIndices(TrackIndices);
|
||||||
return 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
|
#ifndef INDEXING_H
|
||||||
#define INDEXING_H
|
#define INDEXING_H
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "ffms.h"
|
#include "ffms.h"
|
||||||
|
|
||||||
#define INDEXVERSION 21
|
#define INDEXVERSION 24
|
||||||
#define INDEXID 0x53920873
|
#define INDEXID 0x53920873
|
||||||
|
|
||||||
struct IndexHeader {
|
struct IndexHeader {
|
||||||
|
@ -40,27 +39,67 @@ struct IndexHeader {
|
||||||
uint32_t LPPVersion;
|
uint32_t LPPVersion;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FFTrack : public std::vector<TFrameInfo> {
|
class FFIndexer {
|
||||||
|
protected:
|
||||||
|
int IndexMask;
|
||||||
|
int DumpMask;
|
||||||
|
bool IgnoreDecodeErrors;
|
||||||
|
TIndexCallback IC;
|
||||||
|
void *Private;
|
||||||
public:
|
public:
|
||||||
int TT;
|
static FFIndexer *CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize);
|
||||||
TTrackTimeBase TB;
|
virtual ~FFIndexer() { }
|
||||||
|
void SetIndexMask(int IndexMask) { this->IndexMask = IndexMask; }
|
||||||
int FindClosestKeyFrame(int Frame);
|
void SetDumpMask(int DumpMask) { this->DumpMask = DumpMask; }
|
||||||
int FrameFromDTS(int64_t DTS);
|
void SetIgnoreDecodeErrors(bool IgnoreDecodeErrors) { this->IgnoreDecodeErrors = IgnoreDecodeErrors; }
|
||||||
int ClosestFrameFromDTS(int64_t DTS);
|
void SetProgressCallback(TIndexCallback IC, void *Private) { this->IC = IC; this->Private = Private; }
|
||||||
int WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize);
|
virtual FFIndex *DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize) = 0;
|
||||||
|
virtual int GetNumberOfTracks() = 0;
|
||||||
FFTrack();
|
virtual FFMS_TrackType GetTrackType(int Track) = 0;
|
||||||
FFTrack(int64_t Num, int64_t Den, int TT);
|
virtual const char *GetTrackCodec(int Track) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FFIndex : public std::vector<FFTrack> {
|
class FFLAVFIndexer : public FFIndexer {
|
||||||
|
private:
|
||||||
|
bool IsIndexing;
|
||||||
|
AVFormatContext *FormatContext;
|
||||||
public:
|
public:
|
||||||
int Decoder;
|
FFLAVFIndexer(AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize);
|
||||||
int WriteIndex(const char *IndexFile, 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);
|
class FFMatroskaIndexer : public FFIndexer {
|
||||||
FFIndex *ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize);
|
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
|
#endif
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
#include "indexing.h"
|
#include "indexing.h"
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# include <intrin.h>
|
# include <intrin.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -32,6 +34,200 @@
|
||||||
#define _snprintf snprintf
|
#define _snprintf snprintf
|
||||||
#endif
|
#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() {
|
int GetCPUFlags() {
|
||||||
// FIXME Add proper feature detection when msvc isn't used
|
// FIXME Add proper feature detection when msvc isn't used
|
||||||
int Flags = PP_CPU_CAPS_MMX | PP_CPU_CAPS_MMX2;
|
int Flags = PP_CPU_CAPS_MMX | PP_CPU_CAPS_MMX2;
|
||||||
|
@ -51,6 +247,15 @@ int GetCPUFlags() {
|
||||||
return Flags;
|
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) {
|
int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize) {
|
||||||
if (CS) {
|
if (CS) {
|
||||||
char CSBuffer[4096];
|
char CSBuffer[4096];
|
||||||
|
|
|
@ -21,6 +21,9 @@
|
||||||
#ifndef UTILS_H
|
#ifndef UTILS_H
|
||||||
#define UTILS_H
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include "ffms.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libavformat/avformat.h>
|
#include <libavformat/avformat.h>
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
|
@ -31,8 +34,6 @@ extern "C" {
|
||||||
#include "stdiostream.h"
|
#include "stdiostream.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "ffms.h"
|
|
||||||
|
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
# define _WIN32_DCOM
|
# define _WIN32_DCOM
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
|
@ -44,6 +45,41 @@ extern "C" {
|
||||||
# include "guids.h"
|
# include "guids.h"
|
||||||
#endif
|
#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 {
|
struct MatroskaReaderContext {
|
||||||
public:
|
public:
|
||||||
StdIoStream ST;
|
StdIoStream ST;
|
||||||
|
@ -62,6 +98,7 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
int GetCPUFlags();
|
int GetCPUFlags();
|
||||||
|
FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT);
|
||||||
int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize);
|
int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize);
|
||||||
bool AudioFMTIsFloat(SampleFormat FMT);
|
bool AudioFMTIsFloat(SampleFormat FMT);
|
||||||
void InitNullPacket(AVPacket *pkt);
|
void InitNullPacket(AVPacket *pkt);
|
||||||
|
|
|
@ -159,7 +159,7 @@ void FFmpegSourceVideoProvider::LoadVideo(Aegisub::String filename, double fps)
|
||||||
if (TimeBase == NULL)
|
if (TimeBase == NULL)
|
||||||
throw _T("FFmpegSource video provider: failed to get track time base");
|
throw _T("FFmpegSource video provider: failed to get track time base");
|
||||||
|
|
||||||
const TFrameInfo *CurFrameData;
|
const FFFrameInfo *CurFrameData;
|
||||||
|
|
||||||
// build list of keyframes and timecodes
|
// build list of keyframes and timecodes
|
||||||
for (int CurFrameNum = 0; CurFrameNum < VideoInfo->NumFrames; CurFrameNum++) {
|
for (int CurFrameNum = 0; CurFrameNum < VideoInfo->NumFrames; CurFrameNum++) {
|
||||||
|
|
Loading…
Reference in a new issue