Added audio decoding using Haali's splitters
Restructuring
Big API changes
Many bug fixes

Originally committed to SVN as r2972.
This commit is contained in:
Fredrik Mellbin 2009-05-22 21:28:02 +00:00
parent 2377b1455e
commit 4165b7c963
12 changed files with 516 additions and 297 deletions

View file

@ -108,7 +108,8 @@ FFAudio::~FFAudio() {
void FFLAVFAudio::Free(bool CloseCodec) { void FFLAVFAudio::Free(bool CloseCodec) {
if (CloseCodec) if (CloseCodec)
avcodec_close(CodecContext); avcodec_close(CodecContext);
av_close_input_file(FormatContext); if (FormatContext)
av_close_input_file(FormatContext);
} }
FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) {
@ -151,7 +152,7 @@ FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char
// Always try to decode a frame to make sure all required parameters are known // Always try to decode a frame to make sure all required parameters are known
int64_t Dummy; int64_t Dummy;
if (DecodeNextAudioBlock(DecodingBuffer, &Dummy, ErrorMsg, MsgSize) < 0) { if (DecodeNextAudioBlock(&Dummy, ErrorMsg, MsgSize) < 0) {
Free(true); Free(true);
throw ErrorMsg; throw ErrorMsg;
} }
@ -169,10 +170,11 @@ FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50); AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
} }
int FFLAVFAudio::DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, char *ErrorMsg, unsigned MsgSize) { int FFLAVFAudio::DecodeNextAudioBlock(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;
uint8_t *Buf = DecodingBuffer;
AVPacket Packet, TempPacket; AVPacket Packet, TempPacket;
InitNullPacket(&Packet); InitNullPacket(&Packet);
InitNullPacket(&TempPacket); InitNullPacket(&TempPacket);
@ -265,7 +267,7 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs
int64_t DecodeCount; int64_t DecodeCount;
do { do {
int Ret = DecodeNextAudioBlock(DecodingBuffer, &DecodeCount, ErrorMsg, MsgSize); int Ret = DecodeNextAudioBlock(&DecodeCount, ErrorMsg, MsgSize);
if (Ret < 0) { if (Ret < 0) {
// FIXME // FIXME
//Env->ThrowError("Bleh, bad audio decoding"); //Env->ThrowError("Bleh, bad audio decoding");
@ -362,7 +364,7 @@ FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFIndex *Ind
// Always try to decode a frame to make sure all required parameters are known // Always try to decode a frame to make sure all required parameters are known
int64_t Dummy; int64_t Dummy;
if (DecodeNextAudioBlock(DecodingBuffer, &Dummy, Frames[0].FilePos, Frames[0].FrameSize, ErrorMsg, MsgSize) < 0) { if (DecodeNextAudioBlock(&Dummy, 0, ErrorMsg, MsgSize) < 0) {
Free(true); Free(true);
throw ErrorMsg; throw ErrorMsg;
} }
@ -396,7 +398,7 @@ int FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *Err
if (CacheEnd == Start + Count) if (CacheEnd == Start + Count)
return 0; return 0;
size_t CurrentAudioBlock; int CurrentAudioBlock;
// Is seeking required to decode the requested samples? // Is seeking required to decode the requested samples?
// if (!(CurrentSample >= Start && CurrentSample <= CacheEnd)) { // if (!(CurrentSample >= Start && CurrentSample <= CacheEnd)) {
if (CurrentSample != CacheEnd) { if (CurrentSample != CacheEnd) {
@ -410,7 +412,7 @@ int FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *Err
int64_t DecodeCount; int64_t DecodeCount;
do { do {
int Ret = DecodeNextAudioBlock(DecodingBuffer, &DecodeCount, Frames[CurrentAudioBlock].FilePos, Frames[CurrentAudioBlock].FrameSize, ErrorMsg, MsgSize); int Ret = DecodeNextAudioBlock(&DecodeCount, CurrentAudioBlock, ErrorMsg, MsgSize);
if (Ret < 0) { if (Ret < 0) {
// FIXME // FIXME
//Env->ThrowError("Bleh, bad audio decoding"); //Env->ThrowError("Bleh, bad audio decoding");
@ -432,17 +434,21 @@ int FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *Err
return 0; return 0;
} }
int FFMatroskaAudio::DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, uint64_t FilePos, unsigned int FrameSize, char *ErrorMsg, unsigned MsgSize) { int FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count, int AudioBlock, 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;
uint8_t *Buf = DecodingBuffer;
AVPacket TempPacket; AVPacket TempPacket;
InitNullPacket(&TempPacket); InitNullPacket(&TempPacket);
// FIXME check return unsigned int FrameSize = Frames[AudioBlock].FrameSize;
ReadFrame(FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize); if (ReadFrame(Frames[AudioBlock].FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize))
TempPacket.size = FrameSize; return 1;
TempPacket.data = MC.Buffer; TempPacket.data = MC.Buffer;
TempPacket.size = FrameSize;
if (Frames[AudioBlock].KeyFrame)
TempPacket.flags = PKT_FLAG_KEY;
while (TempPacket.size > 0) { while (TempPacket.size > 0) {
int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE; int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
@ -460,6 +466,247 @@ int FFMatroskaAudio::DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, uint64_t
} }
} }
Done:
return 0;
}
#ifdef HAALISOURCE
void FFHaaliAudio::Free(bool CloseCodec) {
if (CloseCodec)
avcodec_close(CodecContext);
av_free(CodecContext);
delete[] CodecPrivate;
}
int FFHaaliAudio::DecodeNextAudioBlock(int64_t *AFirstStartTime, 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;
*AFirstStartTime = -1;
*Count = 0;
uint8_t *Buf = DecodingBuffer;
AVPacket Packet;
InitNullPacket(&Packet);
for (;;) {
CComPtr<IMMFrame> pMMF;
if (pMMC->ReadFrame(NULL, &pMMF) != S_OK)
break;
REFERENCE_TIME Ts, Te;
if (*AFirstStartTime < 0 && SUCCEEDED(pMMF->GetTime(&Ts, &Te)))
*AFirstStartTime = Ts;
if (pMMF->GetTrack() == AudioTrack) {
BYTE *Data = NULL;
if (FAILED(pMMF->GetPointer(&Data)))
goto Done;
Packet.data = Data;
Packet.size = pMMF->GetActualDataLength();
if (pMMF->IsSyncPoint() == S_OK)
Packet.flags = PKT_FLAG_KEY;
else
Packet.flags = 0;
while (Packet.size > 0) {
int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE * 10;
Ret = avcodec_decode_audio3(CodecContext, (int16_t *)Buf, &TempOutputBufSize, &Packet);
if (Ret < 0) {// throw error or something?
goto Done;
}
if (Ret > 0) {
Packet.size -= Ret;
Packet.data += Ret;
Buf += TempOutputBufSize;
if (SizeConst)
*Count += TempOutputBufSize / SizeConst;
}
}
goto Done;
}
}
Done: Done:
return Ret; return Ret;
} }
FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index, int SourceMode, char *ErrorMsg, unsigned MsgSize) {
CodecPrivate = NULL;
AVCodec *Codec = NULL;
CodecContext = NULL;
AudioTrack = Track;
Frames = (*Index)[AudioTrack];
if (Frames.size() == 0) {
_snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?");
throw ErrorMsg;
}
CLSID clsid = HAALI_TS_Parser;
if (SourceMode == 1)
clsid = HAALI_OGM_Parser;
if (FAILED(pMMC.CoCreateInstance(clsid))) {
_snprintf(ErrorMsg, MsgSize, "Can't create parser");
throw ErrorMsg;
}
CComPtr<IMemAlloc> pMA;
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
_snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
throw ErrorMsg;
}
CComPtr<IMMStream> pMS;
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) {
_snprintf(ErrorMsg, MsgSize, "Can't create disk file reader");
throw ErrorMsg;
}
WCHAR WSourceFile[2048];
mbstowcs(WSourceFile, SourceFile, 2000);
CComQIPtr<IMMStreamOpen> pMSO(pMS);
if (FAILED(pMSO->Open(WSourceFile))) {
_snprintf(ErrorMsg, MsgSize, "Can't open file");
throw ErrorMsg;
}
if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
_snprintf(ErrorMsg, MsgSize, "Can't parse file");
throw ErrorMsg;
}
int CodecPrivateSize = 0;
int CurrentTrack = 0;
CComPtr<IEnumUnknown> pEU;
CComQIPtr<IPropertyBag> pBag;
if (SUCCEEDED(pMMC->EnumTracks(&pEU))) {
CComPtr<IUnknown> pU;
while (pEU->Next(1, &pU, NULL) == S_OK) {
if (CurrentTrack++ == Track) {
pBag = pU;
if (pBag) {
CComVariant pV;
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);
Codec = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, CodecPrivate));
}
}
}
pU = NULL;
}
}
CodecContext = avcodec_alloc_context();
CodecContext->extradata = CodecPrivate;
CodecContext->extradata_size = CodecPrivateSize;
if (Codec == NULL) {
Free(false);
_snprintf(ErrorMsg, MsgSize, "Audio codec not found");
throw ErrorMsg;
}
if (avcodec_open(CodecContext, Codec) < 0) {
Free(false);
_snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
throw ErrorMsg;
}
// Always try to decode a frame to make sure all required parameters are known
int64_t Dummy1, Dummy2;
if (DecodeNextAudioBlock(&Dummy1, &Dummy2, ErrorMsg, MsgSize) < 0) {
Free(true);
throw ErrorMsg;
}
pMMC->Seek(Frames[0].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT);
avcodec_flush_buffers(CodecContext);
FillAP(AP, CodecContext, Frames);
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) {
Free(true);
_snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio");
throw ErrorMsg;
}
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
}
FFHaaliAudio::~FFHaaliAudio() {
Free(true);
}
int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) {
const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
memset(Buf, 0, SizeConst * Count);
bool HasSeeked = false;
int PreDecBlocks = 0;
uint8_t *DstBuf = static_cast<uint8_t *>(Buf);
// Fill with everything in the cache
int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
// Was everything in the cache?
if (CacheEnd == Start + Count)
return 0;
int64_t CurrentAudioBlock;
// Is seeking required to decode the requested samples?
// if (!(CurrentSample >= Start && CurrentSample <= CacheEnd)) {
if (CurrentSample != CacheEnd) {
PreDecBlocks = 15;
CurrentAudioBlock = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks - 20, (int64_t)0);
pMMC->Seek(Frames[CurrentAudioBlock].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT);
avcodec_flush_buffers(CodecContext);
HasSeeked = true;
} else {
CurrentAudioBlock = Frames.FindClosestAudioKeyFrame(CurrentSample);
}
int64_t FirstTime, DecodeCount;
do {
int Ret = DecodeNextAudioBlock(&FirstTime, &DecodeCount, ErrorMsg, MsgSize);
if (Ret < 0) {
// FIXME
//Env->ThrowError("Bleh, bad audio decoding");
}
if (HasSeeked) {
CurrentAudioBlock = Frames.ClosestFrameFromDTS(FirstTime);
HasSeeked = false;
}
// Cache the block if enough blocks before it have been decoded to avoid garbage
if (PreDecBlocks == 0) {
AudioCache.CacheBlock(Frames[CurrentAudioBlock].SampleStart, DecodeCount, DecodingBuffer);
CacheEnd = AudioCache.FillRequest(CacheEnd, Start + Count - CacheEnd, DstBuf + (CacheEnd - Start) * SizeConst);
} else {
PreDecBlocks--;
}
CurrentAudioBlock++;
if (CurrentAudioBlock < Frames.size())
CurrentSample = Frames[CurrentAudioBlock].SampleStart;
} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < Frames.size());
return 0;
}
#endif // HAALISOURCE

View file

@ -74,6 +74,7 @@ protected:
uint8_t *DecodingBuffer; uint8_t *DecodingBuffer;
FFTrack Frames; FFTrack Frames;
AVCodecContext *CodecContext; AVCodecContext *CodecContext;
int AudioTrack;
TAudioProperties AP; TAudioProperties AP;
public: public:
FFAudio(); FFAudio();
@ -86,14 +87,12 @@ public:
class FFLAVFAudio : public FFAudio { class FFLAVFAudio : public FFAudio {
private: private:
AVFormatContext *FormatContext; AVFormatContext *FormatContext;
int AudioTrack;
int DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, char *ErrorMsg, unsigned MsgSize); int DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned MsgSize);
void Free(bool CloseCodec); void Free(bool CloseCodec);
public: public:
FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
~FFLAVFAudio(); ~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);
}; };
@ -104,27 +103,26 @@ private:
CompressedStream *CS; CompressedStream *CS;
char ErrorMessage[256]; char ErrorMessage[256];
int DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, uint64_t FilePos, unsigned int FrameSize, char *ErrorMsg, unsigned MsgSize); int DecodeNextAudioBlock(int64_t *Count, int AudioBlock, char *ErrorMsg, unsigned MsgSize);
void Free(bool CloseCodec); void Free(bool CloseCodec);
public: public:
FFMatroskaAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); FFMatroskaAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
~FFMatroskaAudio(); ~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);
}; };
#ifdef HAALISOURCE #ifdef HAALISOURCE
class HaaliAudioSource : public FFAudio { class FFHaaliAudio : public FFAudio {
private: private:
CComPtr<IMMContainer> pMMC; CComPtr<IMMContainer> pMMC;
uint8_t * CodecPrivate;
int DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, char *ErrorMsg, unsigned MsgSize);
void Free(bool CloseCodec); void Free(bool CloseCodec);
int DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count, char *ErrorMsg, unsigned MsgSize);
public: public:
HaaliAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index, int SourceMode, char *ErrorMsg, unsigned MsgSize);
~HaaliAudioSource(); ~FFHaaliAudio();
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);
}; };

View file

@ -62,7 +62,7 @@ static AVSValue __cdecl CreateFFIndex(AVSValue Args, void* UserData, IScriptEnvi
FFIndex *Index = NULL; FFIndex *Index = NULL;
if (OverWrite || !(Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize))) { if (OverWrite || !(Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize))) {
if (!(Index = FFMS_MakeIndex(Source, IndexMask, DumpMask, AudioFile, true, NULL, NULL, ErrorMsg, MsgSize))) if (!(Index = FFMS_MakeIndex(Source, IndexMask, DumpMask, FFMS_DefaultAudioFilename, NULL, true, NULL, NULL, ErrorMsg, MsgSize)))
Env->ThrowError("FFIndex: %s", ErrorMsg); Env->ThrowError("FFIndex: %s", ErrorMsg);
if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize)) { if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize)) {
FFMS_DestroyFFIndex(Index); FFMS_DestroyFFIndex(Index);
@ -122,7 +122,7 @@ static AVSValue __cdecl CreateFFVideoSource(AVSValue Args, void* UserData, IScri
if (Cache) if (Cache)
Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize); Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize);
if (!Index) { if (!Index) {
if (!(Index = FFMS_MakeIndex(Source, 0, 0, NULL, true, NULL, NULL, ErrorMsg, MsgSize))) if (!(Index = FFMS_MakeIndex(Source, 0, 0, NULL, NULL, true, NULL, NULL, ErrorMsg, MsgSize)))
Env->ThrowError("FFVideoSource: %s", ErrorMsg); Env->ThrowError("FFVideoSource: %s", ErrorMsg);
if (Cache) if (Cache)
@ -133,7 +133,7 @@ static AVSValue __cdecl CreateFFVideoSource(AVSValue Args, void* UserData, IScri
} }
if (Track == -1) if (Track == -1)
Track = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_VIDEO, ErrorMsg, MsgSize); Track = FFMS_GetFirstIndexedTrackOfType(Index, FFMS_TYPE_VIDEO, ErrorMsg, MsgSize);
if (Track < 0) if (Track < 0)
Env->ThrowError("FFVideoSource: No video track found"); Env->ThrowError("FFVideoSource: No video track found");
@ -183,7 +183,7 @@ static AVSValue __cdecl CreateFFAudioSource(AVSValue Args, void* UserData, IScri
if (Cache) if (Cache)
Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize); Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize);
if (!Index) { if (!Index) {
if (!(Index = FFMS_MakeIndex(Source, -1, 0, CacheFile, true, NULL, NULL, ErrorMsg, MsgSize))) if (!(Index = FFMS_MakeIndex(Source, -1, 0, NULL, NULL, true, NULL, NULL, ErrorMsg, MsgSize)))
Env->ThrowError("FFAudioSource: %s", ErrorMsg); Env->ThrowError("FFAudioSource: %s", ErrorMsg);
if (Cache) if (Cache)
@ -194,7 +194,7 @@ static AVSValue __cdecl CreateFFAudioSource(AVSValue Args, void* UserData, IScri
} }
if (Track == -1) if (Track == -1)
Track = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, ErrorMsg, MsgSize); Track = FFMS_GetFirstIndexedTrackOfType(Index, FFMS_TYPE_AUDIO, ErrorMsg, MsgSize);
if (Track < 0) if (Track < 0)
Env->ThrowError("FFAudioSource: No audio track found"); Env->ThrowError("FFAudioSource: No audio track found");

View file

@ -31,13 +31,27 @@ extern "C" {
#define _snprintf snprintf #define _snprintf snprintf
#endif #endif
FFMS_API(void) FFMS_Init() { static int InitCount = 0;
static bool InitDone = false;
if (!InitDone) { FFMS_API(int) FFMS_Init() {
if (!InitCount++) {
av_register_all(); av_register_all();
av_log_set_level(AV_LOG_QUIET); av_log_set_level(AV_LOG_QUIET);
#ifdef HAALISOURCE
if (::CoInitializeEx(NULL, COINIT_MULTITHREADED) != S_OK)
return 1;
#endif
}
return 0;
}
FFMS_API(void) FFMS_DeInit() {
if (!--InitCount) {
#ifdef HAALISOURCE
::CoUninitialize();
#endif
} }
InitDone = true;
} }
FFMS_API(int) FFMS_GetLogLevel() { FFMS_API(int) FFMS_GetLogLevel() {
@ -71,6 +85,8 @@ FFMS_API(FFAudio *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FF
switch (Index->Decoder) { switch (Index->Decoder) {
case 0: return new FFLAVFAudio(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); case 1: return new FFMatroskaAudio(SourceFile, Track, Index, ErrorMsg, MsgSize);
case 2: return new FFHaaliAudio(SourceFile, Track, Index, 0, ErrorMsg, MsgSize);
case 3: return new FFHaaliAudio(SourceFile, Track, Index, 1, ErrorMsg, MsgSize);
default: default:
_snprintf(ErrorMsg, MsgSize, "Unsupported format"); _snprintf(ErrorMsg, MsgSize, "Unsupported format");
return NULL; return NULL;
@ -190,6 +206,21 @@ 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, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize) {
FFIndexer *Indexer = FFMS_CreateIndexer(SourceFile, ErrorMsg, MsgSize);
if (!Indexer)
return NULL;
return FFMS_DoIndexing(Indexer, IndexMask, DumpMask, ANC, ANCPrivate, IgnoreDecodeErrors, IC, ICPrivate, ErrorMsg, MsgSize);
}
FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const TAudioProperties *AP, char *FileName, void *Private) {
const char * FormatString = "%s.%d2.w64";
if (FileName == NULL)
return _scprintf(FormatString, SourceFile, Track) + 1;
else
return sprintf(FileName, FormatString, SourceFile, Track) + 1;
}
FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize) { FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize) {
try { try {
return FFIndexer::CreateFFIndexer(SourceFile, ErrorMsg, MsgSize); return FFIndexer::CreateFFIndexer(SourceFile, ErrorMsg, MsgSize);
@ -198,12 +229,13 @@ FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg,
} }
} }
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(FFIndex *) FFMS_DoIndexing(FFIndexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize) {
Indexer->SetIndexMask(IndexMask); Indexer->SetIndexMask(IndexMask);
Indexer->SetDumpMask(DumpMask); Indexer->SetDumpMask(DumpMask);
Indexer->SetIgnoreDecodeErrors(IgnoreDecodeErrors); Indexer->SetIgnoreDecodeErrors(IgnoreDecodeErrors);
Indexer->SetProgressCallback(IC, Private); Indexer->SetProgressCallback(IC, ICPrivate);
FFIndex *Index = Indexer->DoIndexing(AudioFile, ErrorMsg, MsgSize); Indexer->SetAudioNameCallback(ANC, ANCPrivate);
FFIndex *Index = Indexer->DoIndexing(ErrorMsg, MsgSize);
delete Indexer; delete Indexer;
return Index; return Index;
} }
@ -229,10 +261,3 @@ 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);
}

View file

@ -117,12 +117,13 @@ struct TAudioProperties {
double LastTime; double LastTime;
}; };
typedef int (FFMS_CC *TIndexCallback)(int64_t Current, int64_t Total, void *Private); typedef int (FFMS_CC *TIndexCallback)(int64_t Current, int64_t Total, void *ICPrivate);
typedef int (FFMS_CC *TAudioNameCallback)(const char *SourceFile, int Track, const TAudioProperties *AP, char *FileName, unsigned FNSize); typedef int (FFMS_CC *TAudioNameCallback)(const char *SourceFile, int Track, const TAudioProperties *AP, char *FileName, void *Private);
// Most functions return 0 on success // Most functions return 0 on success
// Functions without error message output can be assumed to never fail // Functions without error message output can be assumed to never fail
FFMS_API(void) FFMS_Init(); FFMS_API(int) FFMS_Init();
FFMS_API(void) FFMS_DeInit();
FFMS_API(int) FFMS_GetLogLevel(); FFMS_API(int) FFMS_GetLogLevel();
FFMS_API(void) FFMS_SetLogLevel(int Level); 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);
@ -151,14 +152,13 @@ FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V);
FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A); FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A);
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, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize);
FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const TAudioProperties *AP, char *FileName, void *Private);
FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, 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(FFIndex *) FFMS_DoIndexing(FFIndexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize);
FFMS_API(void) FFMS_CancelIndexing(FFIndexer *Indexer); FFMS_API(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

View file

@ -225,28 +225,31 @@ tn:64:128:256
<p><b>FFmpeg svn</b> from http://ffmpeg.mplayerhq.hu/</p> <p><b>FFmpeg svn</b> from http://ffmpeg.mplayerhq.hu/</p>
<p><b>pthreads</b> only required for ffmpeg-mt compiles</p> <p><b>pthreads</b> only required for FFmpeg-mt compiles</p>
<p><b>Required FFmpeg Configuration:</b> <p><b>Required FFmpeg Configuration:</b>
./configure --enable-memalign-hack --enable-gpl --enable-postproc ./configure --enable-memalign-hack --enable-gpl --enable-postproc
<p><b>Suggested Additional Options:</b> <p><b>Suggested Additional Options:</b>
--enable-pthreads --disable-encoders --disable-muxers --disable-network --disable-debug --enable-libfaad --disable-decoder=aac</p> --enable-w32threads --disable-encoders --disable-muxers --disable-network --disable-debug --enable-libfaad --disable-decoder=aac</p>
<p> <p>
Note that --enable-w32threads is required for multithreaded decoding to work. Note that --enable-w32threads or --enable-pthreads is required for multithreaded decoding to work. For FFmpeg-mt only --enable-pthreads will work.
</p> </p>
<h2>Changes</h2> <h2>Changes</h2>
<ul> <ul>
<li>2.00 beta 9<ul> <li>2.00 beta 9<ul>
<li>Implemented audio decoding using Haali's splitters, FFAudioSource now works on ts, ps and ogm</li>
<li>Can now be compiled with ICL 10.1 (probably other versions too)</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>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>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 memory leaks when audio sources were destroyed and when errors happened during indexing</li>
<li>Fixed access violations occurring when unindexed or empty audio tracks in matroska/lavf read 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 ffms.h 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 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>

View file

@ -23,125 +23,97 @@ extern "C" {
#include <libavutil/md5.h> #include <libavutil/md5.h>
} }
#include <cassert>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include "ffms.h" #include "ffms.h"
using namespace std; using namespace std;
//#define VERBOSE #define VERBOSE
static int FFMS_CC UpdateProgress(int64_t Current, int64_t Total, void *Private) { static int FFMS_CC UpdateProgress(int64_t Current, int64_t Total, void *Private) {
static int LastPercentage = -1; int LastPercentage = (int)Private;
int Percentage = int((double(Current)/double(Total)) * 100); int Percentage = int((double(Current)/double(Total)) * 100);
if (Percentage <= LastPercentage) if (Percentage <= LastPercentage)
return 0; return 0;
LastPercentage = Percentage; Private = (void *)Percentage;
#ifdef VERBOSE #ifdef VERBOSE
cout << "Indexing " << static_cast<char *>(Private) << ", please wait... " << Percentage << "% \r" << flush; cout << "Indexing, please wait... " << Percentage << "% \r" << flush;
#endif #endif
return 0; return 0;
} }
int main(int argc, char *argv[]) { void TestFullDump1(char *SrcFile, bool WithAudio) {
char ErrorMsg[2000]; char ErrorMsg[2000];
AVMD5 *ctx = reinterpret_cast<AVMD5 *>(new uint8_t[av_md5_size]); int ret = FFMS_Init();
uint8_t md5sum[16]; assert(!ret);
if (argc != 2) FFIndexer *FIdx = FFMS_CreateIndexer(SrcFile, ErrorMsg, sizeof(ErrorMsg));
return -1; assert(FIdx);
FFMS_CancelIndexing(FIdx);
FIdx = FFMS_CreateIndexer(SrcFile, ErrorMsg, sizeof(ErrorMsg));
assert(FIdx);
FFMS_Init(); FFIndex *FI = FFMS_DoIndexing(FIdx, -1, -1, FFMS_DefaultAudioFilename, NULL, false, UpdateProgress, NULL, ErrorMsg, sizeof(ErrorMsg));
#ifdef VERBOSE assert(FI);
FFMS_SetLogLevel(AV_LOG_INFO);
#endif
int FMT_YV12A = FFMS_GetPixFmt("yuv420p"); int vtrack = FFMS_GetFirstTrackOfType(FI, FFMS_TYPE_VIDEO, ErrorMsg, sizeof(ErrorMsg));
int FMT_YV12B = FFMS_GetPixFmt("yuvj420p"); assert(vtrack >= 0);
int FMT_YUY2 = FFMS_GetPixFmt("yuv422p"); int atrack = FFMS_GetFirstTrackOfType(FI, FFMS_TYPE_AUDIO, ErrorMsg, sizeof(ErrorMsg));
assert(atrack >= 0);
av_md5_init(ctx); FFVideo *V = FFMS_CreateVideoSource(SrcFile, vtrack, FI, "", 1, 1, ErrorMsg, sizeof(ErrorMsg));
FFIndex *FI = FFMS_MakeIndex(argv[1], -1, 0, NULL, false, UpdateProgress, argv[1], ErrorMsg, sizeof(ErrorMsg)); assert(V);
if (!FI) {
cout << "Indexing error: " << ErrorMsg << endl;
return 1;
}
int track = FFMS_GetFirstTrackOfType(FI, FFMS_TYPE_VIDEO, ErrorMsg, sizeof(ErrorMsg)); if (WithAudio) {
if (track < 0) { uint8_t *DB = new uint8_t[100000];
cout << "No usable track error: " << ErrorMsg << endl; FFAudio *A = FFMS_CreateAudioSource(SrcFile, atrack, FI, ErrorMsg, sizeof(ErrorMsg));
return 2; assert(A);
}
FFVideo *V = FFMS_CreateVideoSource(argv[1], track, FI, "", 1, 1, ErrorMsg, sizeof(ErrorMsg)); const TAudioProperties *AP = FFMS_GetAudioProperties(A);
FFMS_DestroyFFIndex(FI); for (int i = 0; i < AP->NumSamples / 1000; i++) {
if (!V) { ret = FFMS_GetAudio(A, DB, i * 1000, 1000, ErrorMsg, sizeof(ErrorMsg));
cout << "Video source error: " << ErrorMsg << endl; assert(!ret);
return 3; }
FFMS_DestroyAudioSource(A);
delete[] DB;
} }
const TVideoProperties *VP = FFMS_GetVideoProperties(V); const TVideoProperties *VP = FFMS_GetVideoProperties(V);
for (int i = 0; i < VP->NumFrames; i++) { for (int i = 0; i < VP->NumFrames; i++) {
const TAVFrameLite *AVF = FFMS_GetFrame(V, i, ErrorMsg, sizeof(ErrorMsg)); const TAVFrameLite *AVF = FFMS_GetFrame(V, i, ErrorMsg, sizeof(ErrorMsg));
if (!AVF) { assert(AVF);
cout << "Frame request error: " << ErrorMsg << " at frame " << i << endl;
return 4;
}
#ifdef VERBOSE
int LastPercentage = -1;
int Percentage = int((double(i)/double(VP->NumFrames)) * 100);
if (Percentage > LastPercentage) {
LastPercentage = Percentage;
cout << "Requesting frames " << argv[1] << ", please wait... " << Percentage << "% \r" << flush;
}
#endif
uint8_t *Data[3];
Data[0] = AVF->Data[0];
Data[1] = AVF->Data[1];
Data[2] = AVF->Data[2];
if (VP->VPixelFormat == FMT_YV12A || VP->VPixelFormat == FMT_YV12B) {
for (int j = 0; j < VP->Height / 2; j++) {
av_md5_update(ctx, Data[0], VP->Width);
Data[0] += AVF->Linesize[0];
av_md5_update(ctx, Data[0], VP->Width);
Data[0] += AVF->Linesize[0];
av_md5_update(ctx, Data[1], VP->Width / 2);
Data[1] += AVF->Linesize[1];
av_md5_update(ctx, Data[2], VP->Width / 2);
Data[2] += AVF->Linesize[2];
}
} else if (VP->VPixelFormat == FMT_YUY2) {
for (int j = 0; j < VP->Height / 2; j++) {
av_md5_update(ctx, Data[0], VP->Width);
Data[0] += AVF->Linesize[0];
av_md5_update(ctx, Data[0], VP->Width);
Data[0] += AVF->Linesize[0];
av_md5_update(ctx, Data[1], VP->Width / 2);
Data[1] += AVF->Linesize[1];
av_md5_update(ctx, Data[2], VP->Width / 2);
Data[2] += AVF->Linesize[2];
}
}
} }
FFMS_DestroyFFIndex(FI);
FFMS_DestroyVideoSource(V); FFMS_DestroyVideoSource(V);
av_md5_final(ctx, md5sum); FFMS_DeInit();
}
delete[] reinterpret_cast<uint8_t *>(ctx); int main(int argc, char *argv[]) {
char *TestFiles1[10];
TestFiles1[0] = "[FLV1]_The_Melancholy_of_Haruhi_Suzumiya_-_Full_Clean_Ending.flv";
TestFiles1[1] = "jra_jupiter.avi";
TestFiles1[2] = "h264_16-bframes_16-references_pyramid_crash-indexing.mkv";
TestFiles1[3] = "pyramid-adaptive-10-bframes.mkv";
TestFiles1[4] = "negative-timecodes.avi";
TestFiles1[5] = "Zero1_ITV2_TS_Test.ts";
cout << "Test successful, MD5: " << hex; for (int i = 0; i < 5; i++)
for (int i = 0; i < sizeof(md5sum); i++) TestFullDump1(TestFiles1[5], true);
cout << static_cast<unsigned>(md5sum[i]); /*
cout << endl; TestFullDump1(TestFiles1[5], false);
for (int i = 0; i < 5; i++)
TestFullDump1(TestFiles1[i], true);
TestFullDump1(TestFiles1[5], false);
*/
return 0; return 0;
} }

View file

@ -186,7 +186,10 @@ int main(int argc, char *argv[]) {
return 1; return 1;
} }
FFMS_Init(); if (FFMS_Init()) {
std::cout << std::endl << "Error: initialization failed" << std::endl;
return 1;
}
if (Verbose) if (Verbose)
FFMS_SetLogLevel(AV_LOG_INFO); FFMS_SetLogLevel(AV_LOG_INFO);
@ -208,5 +211,6 @@ int main(int argc, char *argv[]) {
} }
FFMS_DestroyFFIndex(Index); FFMS_DestroyFFIndex(Index);
FFMS_DeInit();
return 0; return 0;
} }

View file

@ -228,7 +228,7 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index,
// Always try to decode a frame to make sure all required parameters are known // Always try to decode a frame to make sure all required parameters are known
int64_t Dummy; int64_t Dummy;
if (DecodeNextFrame(DecodeFrame, &Dummy, ErrorMsg, MsgSize)) { if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
Free(true); Free(true);
throw ErrorMsg; throw ErrorMsg;
} }
@ -279,7 +279,7 @@ FFLAVFVideo::~FFLAVFVideo() {
Free(true); Free(true);
} }
int FFLAVFVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AStartTime, char *ErrorMsg, unsigned MsgSize) { int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned MsgSize) {
AVPacket Packet; AVPacket Packet;
InitNullPacket(&Packet); InitNullPacket(&Packet);
int FrameFinished = 0; int FrameFinished = 0;
@ -290,7 +290,7 @@ int FFLAVFVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AStartTime, char *Err
if (*AStartTime < 0) if (*AStartTime < 0)
*AStartTime = Packet.dts; *AStartTime = Packet.dts;
avcodec_decode_video2(CodecContext, AFrame, &FrameFinished, &Packet); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
} }
av_free_packet(&Packet); av_free_packet(&Packet);
@ -303,7 +303,7 @@ int FFLAVFVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AStartTime, char *Err
if (CodecContext->has_b_frames) { if (CodecContext->has_b_frames) {
AVPacket NullPacket; AVPacket NullPacket;
InitNullPacket(&NullPacket); InitNullPacket(&NullPacket);
avcodec_decode_video2(CodecContext, AFrame, &FrameFinished, &NullPacket); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
} }
if (!FrameFinished) if (!FrameFinished)
@ -349,7 +349,7 @@ ReSeek:
do { do {
int64_t StartTime; int64_t StartTime;
if (DecodeNextFrame(DecodeFrame, &StartTime, ErrorMsg, MsgSize)) if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize))
return NULL; return NULL;
if (HasSeeked) { if (HasSeeked) {
@ -460,7 +460,7 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
// Always try to decode a frame to make sure all required parameters are known // Always try to decode a frame to make sure all required parameters are known
int64_t Dummy; int64_t Dummy;
if (DecodeNextFrame(DecodeFrame, &Dummy, ErrorMsg, MsgSize)) { if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
Free(true); Free(true);
throw ErrorMsg; throw ErrorMsg;
} }
@ -510,7 +510,7 @@ FFMatroskaVideo::~FFMatroskaVideo() {
Free(true); Free(true);
} }
int FFMatroskaVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) { int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) {
int FrameFinished = 0; int FrameFinished = 0;
*AFirstStartTime = -1; *AFirstStartTime = -1;
AVPacket Packet; AVPacket Packet;
@ -532,7 +532,7 @@ int FFMatroskaVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime,
Packet.flags = PKT_FLAG_KEY; Packet.flags = PKT_FLAG_KEY;
else else
Packet.flags = 0; Packet.flags = 0;
avcodec_decode_video2(CodecContext, AFrame, &FrameFinished, &Packet); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
if (FrameFinished) if (FrameFinished)
goto Done; goto Done;
@ -542,7 +542,7 @@ int FFMatroskaVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime,
if (CodecContext->has_b_frames) { if (CodecContext->has_b_frames) {
AVPacket NullPacket; AVPacket NullPacket;
InitNullPacket(&NullPacket); InitNullPacket(&NullPacket);
avcodec_decode_video2(CodecContext, AFrame, &FrameFinished, &NullPacket); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
} }
if (!FrameFinished) if (!FrameFinished)
@ -568,7 +568,7 @@ TAVFrameLite *FFMatroskaVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize)
do { do {
int64_t StartTime; int64_t StartTime;
if (DecodeNextFrame(DecodeFrame, &StartTime, ErrorMsg, MsgSize)) if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize))
return NULL; return NULL;
if (HasSeeked) { if (HasSeeked) {
@ -600,6 +600,7 @@ 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) {
CodecPrivate = NULL;
AVCodec *Codec = NULL; AVCodec *Codec = NULL;
CodecContext = NULL; CodecContext = NULL;
VideoTrack = Track; VideoTrack = Track;
@ -610,8 +611,6 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
throw ErrorMsg; throw ErrorMsg;
} }
::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;
@ -646,7 +645,6 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
throw ErrorMsg; throw ErrorMsg;
} }
CodecPrivate = NULL;
int CodecPrivateSize = 0; int CodecPrivateSize = 0;
int CurrentTrack = 0; int CurrentTrack = 0;
CComPtr<IEnumUnknown> pEU; CComPtr<IEnumUnknown> pEU;
@ -698,7 +696,7 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
// Always try to decode a frame to make sure all required parameters are known // Always try to decode a frame to make sure all required parameters are known
int64_t Dummy; int64_t Dummy;
if (DecodeNextFrame(DecodeFrame, &Dummy, ErrorMsg, MsgSize)) { if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
Free(true); Free(true);
throw ErrorMsg; throw ErrorMsg;
} }
@ -741,7 +739,7 @@ FFHaaliVideo::~FFHaaliVideo() {
Free(true); Free(true);
} }
int FFHaaliVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) { int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) {
int FrameFinished = 0; int FrameFinished = 0;
*AFirstStartTime = -1; *AFirstStartTime = -1;
AVPacket Packet; AVPacket Packet;
@ -768,7 +766,7 @@ int FFHaaliVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, cha
else else
Packet.flags = 0; Packet.flags = 0;
avcodec_decode_video2(CodecContext, AFrame, &FrameFinished, &Packet); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
if (FrameFinished) if (FrameFinished)
goto Done; goto Done;
@ -779,7 +777,7 @@ int FFHaaliVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, cha
if (CodecContext->has_b_frames) { if (CodecContext->has_b_frames) {
AVPacket NullPacket; AVPacket NullPacket;
InitNullPacket(&NullPacket); InitNullPacket(&NullPacket);
avcodec_decode_video2(CodecContext, AFrame, &FrameFinished, &NullPacket); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
} }
if (!FrameFinished) if (!FrameFinished)
@ -807,7 +805,7 @@ ReSeek:
do { do {
int64_t StartTime; int64_t StartTime;
if (DecodeNextFrame(DecodeFrame, &StartTime, ErrorMsg, MsgSize)) if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize))
return NULL; return NULL;
if (HasSeeked) { if (HasSeeked) {

View file

@ -79,7 +79,7 @@ private:
int SeekMode; int SeekMode;
void Free(bool CloseCodec); void Free(bool CloseCodec);
int DecodeNextFrame(AVFrame *Frame, int64_t *DTS, char *ErrorMsg, unsigned MsgSize); int DecodeNextFrame(int64_t *DTS, char *ErrorMsg, unsigned MsgSize);
public: public:
FFLAVFVideo(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);
~FFLAVFVideo(); ~FFLAVFVideo();
@ -94,7 +94,7 @@ private:
char ErrorMessage[256]; char ErrorMessage[256];
void Free(bool CloseCodec); void Free(bool CloseCodec);
int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize); int DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
public: public:
FFMatroskaVideo(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);
~FFMatroskaVideo(); ~FFMatroskaVideo();
@ -107,8 +107,9 @@ 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(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
public: public:
FFHaaliVideo(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);
~FFHaaliVideo(); ~FFHaaliVideo();

View file

@ -25,7 +25,6 @@
#include <memory> #include <memory>
#include <errno.h> #include <errno.h>
#include "indexing.h" #include "indexing.h"
#include "wave64writer.h"
#ifdef __UNIX__ #ifdef __UNIX__
#define _fseeki64 fseeko #define _fseeki64 fseeko
@ -51,48 +50,30 @@ extern "C" {
# include "guids.h" # include "guids.h"
#endif #endif
class MatroskaAudioContext { class MatroskaAudioContext : public SharedAudioContext {
public: public:
Wave64Writer *W64W;
AVCodecContext *CTX;
CompressedStream *CS; CompressedStream *CS;
int64_t CurrentSample;
uint8_t *CodecPrivate; uint8_t *CodecPrivate;
MatroskaAudioContext() { MatroskaAudioContext() {
W64W = NULL;
CTX = NULL;
CS = NULL; CS = NULL;
CurrentSample = 0;
CodecPrivate = NULL; CodecPrivate = NULL;
} }
~MatroskaAudioContext() { ~MatroskaAudioContext() {
delete[] CodecPrivate;
delete W64W;
if (CTX) { if (CTX) {
avcodec_close(CTX); avcodec_close(CTX);
av_free(CTX); av_free(CTX);
} }
if (CS) if (CS)
cs_Destroy(CS); cs_Destroy(CS);
delete[] CodecPrivate;
} }
}; };
class FFAudioContext { class FFAudioContext : public SharedAudioContext {
public: public:
Wave64Writer *W64W;
AVCodecContext *CTX;
int64_t CurrentSample;
FFAudioContext() {
W64W = NULL;
CTX = NULL;
CurrentSample = 0;
}
~FFAudioContext() { ~FFAudioContext() {
delete W64W;
if (CTX) if (CTX)
avcodec_close(CTX); avcodec_close(CTX);
} }
@ -101,42 +82,34 @@ public:
#ifdef HAALISOURCE #ifdef HAALISOURCE
class HaaliIndexMemory { class HaaliIndexMemory {
private: private:
int16_t *DecodingBuffer;
MatroskaAudioContext *AudioContexts; MatroskaAudioContext *AudioContexts;
public: public:
HaaliIndexMemory(int Tracks, int16_t *&DecodingBuffer, MatroskaAudioContext *&AudioContexts) { HaaliIndexMemory(int Tracks, MatroskaAudioContext *&AudioContexts) {
DecodingBuffer = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
AudioContexts = new MatroskaAudioContext[Tracks]; AudioContexts = new MatroskaAudioContext[Tracks];
this->DecodingBuffer = DecodingBuffer;
this->AudioContexts = AudioContexts; this->AudioContexts = AudioContexts;
} }
~HaaliIndexMemory() { ~HaaliIndexMemory() {
delete [] DecodingBuffer; delete[] AudioContexts;
delete [] AudioContexts;
} }
}; };
#endif #endif
class MatroskaIndexMemory { class MatroskaIndexMemory {
private: private:
int16_t *DecodingBuffer;
MatroskaAudioContext *AudioContexts; MatroskaAudioContext *AudioContexts;
MatroskaFile *MF; MatroskaFile *MF;
MatroskaReaderContext *MC; MatroskaReaderContext *MC;
public: public:
MatroskaIndexMemory(int Tracks, int16_t *&DecodingBuffer, MatroskaAudioContext *&AudioContexts, MatroskaFile *MF, MatroskaReaderContext *MC) { MatroskaIndexMemory(int Tracks, MatroskaAudioContext *&AudioContexts, MatroskaFile *MF, MatroskaReaderContext *MC) {
DecodingBuffer = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
AudioContexts = new MatroskaAudioContext[Tracks]; AudioContexts = new MatroskaAudioContext[Tracks];
this->DecodingBuffer = DecodingBuffer;
this->AudioContexts = AudioContexts; this->AudioContexts = AudioContexts;
this->MF = MF; this->MF = MF;
this->MC = MC; this->MC = MC;
} }
~MatroskaIndexMemory() { ~MatroskaIndexMemory() {
delete [] DecodingBuffer; delete[] AudioContexts;
delete [] AudioContexts;
mkv_Close(MF); mkv_Close(MF);
fclose(MC->ST.fp); fclose(MC->ST.fp);
} }
@ -144,21 +117,17 @@ public:
class FFIndexMemory { class FFIndexMemory {
private: private:
int16_t *DecodingBuffer;
FFAudioContext *AudioContexts; FFAudioContext *AudioContexts;
AVFormatContext *FormatContext; AVFormatContext *FormatContext;
public: public:
FFIndexMemory(int Tracks, int16_t *&DecodingBuffer, FFAudioContext *&AudioContexts, AVFormatContext *&FormatContext) { FFIndexMemory(int Tracks, FFAudioContext *&AudioContexts, AVFormatContext *&FormatContext) {
DecodingBuffer = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
AudioContexts = new FFAudioContext[Tracks]; AudioContexts = new FFAudioContext[Tracks];
this->DecodingBuffer = DecodingBuffer;
this->AudioContexts = AudioContexts; this->AudioContexts = AudioContexts;
this->FormatContext = FormatContext; this->FormatContext = FormatContext;
} }
~FFIndexMemory() { ~FFIndexMemory() {
delete [] DecodingBuffer; delete[] AudioContexts;
delete [] AudioContexts;
av_close_input_file(FormatContext); av_close_input_file(FormatContext);
} }
}; };
@ -167,19 +136,44 @@ static bool DTSComparison(TFrameInfo FI1, TFrameInfo FI2) {
return FI1.DTS < FI2.DTS; return FI1.DTS < FI2.DTS;
} }
static void SortTrackIndices(FFIndex *Index) { static void SortTrackIndex(FFIndex *Index) {
for (FFIndex::iterator Cur=Index->begin(); Cur!=Index->end(); Cur++) for (FFIndex::iterator Cur=Index->begin(); Cur!=Index->end(); Cur++)
std::sort(Cur->begin(), Cur->end(), DTSComparison); std::sort(Cur->begin(), Cur->end(), DTSComparison);
} }
bool FFIndexer::WriteAudio(SharedAudioContext &AudioContext, FFIndex *Index, int Track, int DBSize, char *ErrorMsg, unsigned MsgSize) {
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers.
if (DBSize > 0) {
if (!AudioContext.W64W) {
TAudioProperties AP;
FillAP(AP, AudioContext.CTX, (*Index)[Track]);
char *WName = new char[(*ANC)(SourceFile, Track, &AP, NULL, ANCPrivate)];
(*ANC)(SourceFile, Track, &AP, WName, ANCPrivate);
std::string WN(WName);
delete[] WName;
try {
AudioContext.W64W = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioContext.CTX->sample_fmt),
AudioContext.CTX->channels, AudioContext.CTX->sample_rate, AudioFMTIsFloat(AudioContext.CTX->sample_fmt));
} catch (...) {
_snprintf(ErrorMsg, MsgSize, "Failed to write wave data");
return false;
}
}
AudioContext.W64W->WriteData(DecodingBuffer, DBSize);
}
return true;
}
#ifdef HAALISOURCE #ifdef HAALISOURCE
FFHaaliIndexer::FFHaaliIndexer(const char *SourceFile, int SourceMode, char *ErrorMsg, unsigned MsgSize) { FFHaaliIndexer::FFHaaliIndexer(const char *Filename, int SourceMode, char *ErrorMsg, unsigned MsgSize) {
SourceFile = Filename;
this->SourceMode = SourceMode; this->SourceMode = SourceMode;
memset(TrackType, FFMS_TYPE_UNKNOWN, sizeof(TrackType)); memset(TrackType, FFMS_TYPE_UNKNOWN, sizeof(TrackType));
memset(Codec, 0, sizeof(Codec)); memset(Codec, 0, sizeof(Codec));
memset(CodecPrivate, 0, sizeof(CodecPrivate)); memset(CodecPrivate, 0, sizeof(CodecPrivate));
memset(CodecPrivateSize, 0, sizeof(CodecPrivateSize)); memset(CodecPrivateSize, 0, sizeof(CodecPrivateSize));
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
CLSID clsid = HAALI_TS_Parser; CLSID clsid = HAALI_TS_Parser;
if (SourceMode == 1) if (SourceMode == 1)
@ -250,13 +244,12 @@ FFHaaliIndexer::FFHaaliIndexer(const char *SourceFile, int SourceMode, char *Err
} }
} }
FFIndex *FFHaaliIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize) { FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
// Audio stuff // Audio stuff
int16_t *db;
MatroskaAudioContext *AudioContexts; MatroskaAudioContext *AudioContexts;
HaaliIndexMemory IM = HaaliIndexMemory(NumTracks, db, AudioContexts); HaaliIndexMemory IM = HaaliIndexMemory(NumTracks, AudioContexts);
FFIndex *TrackIndices = new FFIndex(); std::auto_ptr<FFIndex> TrackIndices(new FFIndex());
TrackIndices->Decoder = 2; TrackIndices->Decoder = 2;
if (SourceMode == 1) if (SourceMode == 1)
TrackIndices->Decoder = 3; TrackIndices->Decoder = 3;
@ -295,9 +288,8 @@ FFIndex *FFHaaliIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsig
for (;;) { for (;;) {
if (IC) { if (IC) {
if ((*IC)(0, 1, Private)) { if ((*IC)(0, 1, ICPrivate)) {
_snprintf(ErrorMsg, MsgSize, "Cancelled by user"); _snprintf(ErrorMsg, MsgSize, "Cancelled by user");
delete TrackIndices;
return NULL; return NULL;
} }
} }
@ -309,28 +301,27 @@ FFIndex *FFHaaliIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsig
REFERENCE_TIME Ts, Te; REFERENCE_TIME Ts, Te;
HRESULT hr = pMMF->GetTime(&Ts, &Te); HRESULT hr = pMMF->GetTime(&Ts, &Te);
unsigned int CurrentTrack = pMMF->GetTrack(); unsigned int Track = 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 (TrackType[CurrentTrack] == FFMS_TYPE_VIDEO) { if (TrackType[Track] == FFMS_TYPE_VIDEO) {
(*TrackIndices)[CurrentTrack].push_back(TFrameInfo(Ts, pMMF->IsSyncPoint() == S_OK)); (*TrackIndices)[Track].push_back(TFrameInfo(Ts, pMMF->IsSyncPoint() == S_OK));
} else if (TrackType[CurrentTrack] == FFMS_TYPE_AUDIO && (IndexMask & (1 << CurrentTrack))) { } else if (TrackType[Track] == FFMS_TYPE_AUDIO && (IndexMask & (1 << Track))) {
(*TrackIndices)[CurrentTrack].push_back(TFrameInfo(Ts, AudioContexts[CurrentTrack].CurrentSample, 0 /* FIXME? */, pMMF->GetActualDataLength(), pMMF->IsSyncPoint() == S_OK)); (*TrackIndices)[Track].push_back(TFrameInfo(Ts, AudioContexts[Track].CurrentSample, 0 /* FIXME? */, pMMF->GetActualDataLength(), pMMF->IsSyncPoint() == S_OK));
AVCodecContext *AudioCodecContext = AudioContexts[CurrentTrack].CTX; AVCodecContext *AudioCodecContext = AudioContexts[Track].CTX;
pMMF->GetPointer(&TempPacket.data); pMMF->GetPointer(&TempPacket.data);
TempPacket.size = pMMF->GetActualDataLength(); TempPacket.size = pMMF->GetActualDataLength();
while (TempPacket.size > 0) { while (TempPacket.size > 0) {
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
int Ret = avcodec_decode_audio3(AudioCodecContext, db, &dbsize, &TempPacket); int Ret = avcodec_decode_audio3(AudioCodecContext, DecodingBuffer, &dbsize, &TempPacket);
if (Ret < 0) { if (Ret < 0) {
if (IgnoreDecodeErrors) { if (IgnoreDecodeErrors) {
(*TrackIndices)[CurrentTrack].clear(); (*TrackIndices)[Track].clear();
IndexMask &= ~(1 << CurrentTrack); IndexMask &= ~(1 << Track);
break; break;
} else { } else {
_snprintf(ErrorMsg, MsgSize, "Audio decoding error"); _snprintf(ErrorMsg, MsgSize, "Audio decoding error");
delete TrackIndices;
return NULL; return NULL;
} }
} }
@ -341,33 +332,21 @@ FFIndex *FFHaaliIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsig
} }
if (dbsize > 0) if (dbsize > 0)
AudioContexts[CurrentTrack].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels); AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
if (dbsize > 0 && (DumpMask & (1 << CurrentTrack))) { if (DumpMask & (1 << Track))
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers. WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize, ErrorMsg, MsgSize);
if (!AudioContexts[CurrentTrack].W64W) {
AVCodecContext *CTX = AudioContexts[CurrentTrack].CTX;
char ABuf[50];
std::string WN(AudioFile);
_snprintf(ABuf, sizeof(ABuf), ".%02d.delay.%d.w64", CurrentTrack, 0);
WN += ABuf;
AudioContexts[CurrentTrack].W64W = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioCodecContext->sample_fmt),
AudioCodecContext->channels, AudioCodecContext->sample_rate, AudioFMTIsFloat(AudioCodecContext->sample_fmt));
}
AudioContexts[CurrentTrack].W64W->WriteData(db, dbsize);
}
} }
} }
} }
SortTrackIndices(TrackIndices); SortTrackIndex(TrackIndices.get());
return TrackIndices; return TrackIndices.release();
} }
#endif #endif
FFMatroskaIndexer::FFMatroskaIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize) { FFMatroskaIndexer::FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) {
SourceFile = Filename;
char ErrorMessage[256]; char ErrorMessage[256];
InitStdIoStream(&MC.ST); InitStdIoStream(&MC.ST);
@ -387,13 +366,12 @@ FFMatroskaIndexer::FFMatroskaIndexer(const char *SourceFile, char *ErrorMsg, uns
} }
} }
FFIndex *FFMatroskaIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize) { FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
char ErrorMessage[256]; char ErrorMessage[256];
// Audio stuff // Audio stuff
int16_t *db;
MatroskaAudioContext *AudioContexts; MatroskaAudioContext *AudioContexts;
MatroskaIndexMemory IM = MatroskaIndexMemory(mkv_GetNumTracks(MF), db, AudioContexts, MF, &MC); MatroskaIndexMemory IM = MatroskaIndexMemory(mkv_GetNumTracks(MF), AudioContexts, MF, &MC);
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) { for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) {
TrackInfo *TI = mkv_GetTrackInfo(MF, i); TrackInfo *TI = mkv_GetTrackInfo(MF, i);
@ -439,7 +417,7 @@ FFIndex *FFMatroskaIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, un
int64_t SourceSize = _ftelli64(MC.ST.fp); int64_t SourceSize = _ftelli64(MC.ST.fp);
_fseeki64(MC.ST.fp, CurrentPos, SEEK_SET); _fseeki64(MC.ST.fp, CurrentPos, SEEK_SET);
FFIndex *TrackIndices = new FFIndex(); std::auto_ptr<FFIndex> TrackIndices(new FFIndex());
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++)
@ -453,9 +431,8 @@ FFIndex *FFMatroskaIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, un
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 (IC) { if (IC) {
if ((*IC)(_ftelli64(MC.ST.fp), SourceSize, Private)) { if ((*IC)(_ftelli64(MC.ST.fp), SourceSize, ICPrivate)) {
_snprintf(ErrorMsg, MsgSize, "Cancelled by user"); _snprintf(ErrorMsg, MsgSize, "Cancelled by user");
delete TrackIndices;
return NULL; return NULL;
} }
} }
@ -472,7 +449,7 @@ FFIndex *FFMatroskaIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, un
while (TempPacket.size > 0) { while (TempPacket.size > 0) {
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
int Ret = avcodec_decode_audio3(AudioCodecContext, db, &dbsize, &TempPacket); int Ret = avcodec_decode_audio3(AudioCodecContext, DecodingBuffer, &dbsize, &TempPacket);
if (Ret < 0) { if (Ret < 0) {
if (IgnoreDecodeErrors) { if (IgnoreDecodeErrors) {
(*TrackIndices)[Track].clear(); (*TrackIndices)[Track].clear();
@ -480,7 +457,6 @@ FFIndex *FFMatroskaIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, un
break; break;
} else { } else {
_snprintf(ErrorMsg, MsgSize, "Audio decoding error"); _snprintf(ErrorMsg, MsgSize, "Audio decoding error");
delete TrackIndices;
return NULL; return NULL;
} }
} }
@ -493,27 +469,14 @@ FFIndex *FFMatroskaIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, un
if (dbsize > 0) if (dbsize > 0)
AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels); AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
if (dbsize > 0 && (DumpMask & (1 << Track))) { if (DumpMask & (1 << Track))
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers. WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize, ErrorMsg, MsgSize);
if (!AudioContexts[Track].W64W) {
char ABuf[50];
std::string WN(AudioFile);
int Offset = StartTime * mkv_TruncFloat(mkv_GetTrackInfo(MF, Track)->TimecodeScale) / (double)1000000;
_snprintf(ABuf, sizeof(ABuf), ".%02d.delay.%d.w64", Track, Offset);
WN += ABuf;
AudioContexts[Track].W64W = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioCodecContext->sample_fmt),
AudioCodecContext->channels, AudioCodecContext->sample_rate, AudioFMTIsFloat(AudioCodecContext->sample_fmt));
}
AudioContexts[Track].W64W->WriteData(db, dbsize);
}
} }
} }
} }
SortTrackIndices(TrackIndices); SortTrackIndex(TrackIndices.get());
return TrackIndices; return TrackIndices.release();
} }
FFIndexer *FFIndexer::CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) { FFIndexer *FFIndexer::CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) {
@ -543,12 +506,13 @@ FFIndexer *FFIndexer::CreateFFIndexer(const char *Filename, char *ErrorMsg, unsi
} }
#endif #endif
return new FFLAVFIndexer(FormatContext, ErrorMsg, MsgSize); return new FFLAVFIndexer(Filename, FormatContext, ErrorMsg, MsgSize);
} }
FFLAVFIndexer::FFLAVFIndexer(AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize) { FFLAVFIndexer::FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize) {
IsIndexing = false; SourceFile = Filename;
this->FormatContext = FormatContext; this->FormatContext = FormatContext;
IsIndexing = false;
if (av_find_stream_info(FormatContext) < 0) { if (av_find_stream_info(FormatContext) < 0) {
av_close_input_file(FormatContext); av_close_input_file(FormatContext);
@ -562,13 +526,12 @@ FFLAVFIndexer::~FFLAVFIndexer() {
av_close_input_file(FormatContext); av_close_input_file(FormatContext);
} }
FFIndex *FFLAVFIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize) { FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
IsIndexing = true; IsIndexing = true;
// Audio stuff // Audio stuff
int16_t *db;
FFAudioContext *AudioContexts; FFAudioContext *AudioContexts;
FFIndexMemory IM = FFIndexMemory(FormatContext->nb_streams, db, AudioContexts, FormatContext); FFIndexMemory IM = FFIndexMemory(FormatContext->nb_streams, AudioContexts, FormatContext);
for (unsigned int i = 0; i < FormatContext->nb_streams; i++) { for (unsigned int i = 0; i < FormatContext->nb_streams; i++) {
if (IndexMask & (1 << i) && FormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) { if (IndexMask & (1 << i) && FormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) {
@ -593,7 +556,7 @@ FFIndex *FFLAVFIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsign
// //
FFIndex *TrackIndices = new FFIndex(); std::auto_ptr<FFIndex> TrackIndices(new FFIndex());
TrackIndices->Decoder = 0; TrackIndices->Decoder = 0;
for (unsigned int i = 0; i < FormatContext->nb_streams; i++) for (unsigned int i = 0; i < FormatContext->nb_streams; i++)
@ -607,9 +570,8 @@ FFIndex *FFLAVFIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsign
while (av_read_frame(FormatContext, &Packet) >= 0) { while (av_read_frame(FormatContext, &Packet) >= 0) {
// Update progress // Update progress
if (IC) { if (IC) {
if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, Private)) { if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, ICPrivate)) {
_snprintf(ErrorMsg, MsgSize, "Cancelled by user"); _snprintf(ErrorMsg, MsgSize, "Cancelled by user");
delete TrackIndices;
return NULL; return NULL;
} }
} }
@ -625,7 +587,7 @@ FFIndex *FFLAVFIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsign
while (TempPacket.size > 0) { while (TempPacket.size > 0) {
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
int Ret = avcodec_decode_audio3(AudioCodecContext, db, &dbsize, &TempPacket); int Ret = avcodec_decode_audio3(AudioCodecContext, DecodingBuffer, &dbsize, &TempPacket);
if (Ret < 0) { if (Ret < 0) {
if (IgnoreDecodeErrors) { if (IgnoreDecodeErrors) {
(*TrackIndices)[Packet.stream_index].clear(); (*TrackIndices)[Packet.stream_index].clear();
@ -633,7 +595,6 @@ FFIndex *FFLAVFIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsign
break; break;
} else { } else {
_snprintf(ErrorMsg, MsgSize, "Audio decoding error"); _snprintf(ErrorMsg, MsgSize, "Audio decoding error");
delete TrackIndices;
return NULL; return NULL;
} }
} }
@ -646,28 +607,14 @@ FFIndex *FFLAVFIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsign
if (dbsize > 0) if (dbsize > 0)
AudioContexts[Packet.stream_index].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels); AudioContexts[Packet.stream_index].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
if (dbsize > 0 && (DumpMask & (1 << Packet.stream_index))) { if (DumpMask & (1 << Packet.stream_index))
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers. WriteAudio(AudioContexts[Packet.stream_index], TrackIndices.get(), Packet.stream_index, dbsize, ErrorMsg, MsgSize);
if (!AudioContexts[Packet.stream_index].W64W) {
char ABuf[50];
std::string WN(AudioFile);
int Offset = (Packet.dts * FormatContext->streams[Packet.stream_index]->time_base.num)
/ (double)(FormatContext->streams[Packet.stream_index]->time_base.den * 1000);
_snprintf(ABuf, sizeof(ABuf), ".%02d.delay.%d.w64", Packet.stream_index, Offset);
WN += ABuf;
AudioContexts[Packet.stream_index].W64W = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioCodecContext->sample_fmt),
AudioCodecContext->channels, AudioCodecContext->sample_rate, AudioFMTIsFloat(AudioCodecContext->sample_fmt));
}
AudioContexts[Packet.stream_index].W64W->WriteData(db, dbsize);
}
} }
} }
av_free_packet(&Packet); av_free_packet(&Packet);
} }
SortTrackIndices(TrackIndices); SortTrackIndex(TrackIndices.get());
return TrackIndices; return TrackIndices.release();
} }

View file

@ -22,9 +22,9 @@
#define INDEXING_H #define INDEXING_H
#include "utils.h" #include "utils.h"
#include "ffms.h" #include "wave64writer.h"
#define INDEXVERSION 24 #define INDEXVERSION 25
#define INDEXID 0x53920873 #define INDEXID 0x53920873
struct IndexHeader { struct IndexHeader {
@ -39,21 +39,45 @@ struct IndexHeader {
uint32_t LPPVersion; uint32_t LPPVersion;
}; };
class SharedAudioContext {
public:
Wave64Writer *W64W;
AVCodecContext *CTX;
int64_t CurrentSample;
SharedAudioContext() {
W64W = NULL;
CTX = NULL;
CurrentSample = 0;
}
~SharedAudioContext() {
delete W64W;
}
};
class FFIndexer { class FFIndexer {
protected: protected:
int IndexMask; int IndexMask;
int DumpMask; int DumpMask;
bool IgnoreDecodeErrors; bool IgnoreDecodeErrors;
TIndexCallback IC; TIndexCallback IC;
void *Private; void *ICPrivate;
TAudioNameCallback ANC;
void *ANCPrivate;
const char *SourceFile;
int16_t DecodingBuffer[AVCODEC_MAX_AUDIO_FRAME_SIZE * 5];
bool WriteAudio(SharedAudioContext &AudioContext, FFIndex *Index, int Track, int DBSize, char *ErrorMsg, unsigned MsgSize);
public: public:
static FFIndexer *CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize); static FFIndexer *CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize);
virtual ~FFIndexer() { } virtual ~FFIndexer() { }
void SetIndexMask(int IndexMask) { this->IndexMask = IndexMask; } void SetIndexMask(int IndexMask) { this->IndexMask = IndexMask; }
void SetDumpMask(int DumpMask) { this->DumpMask = DumpMask; } void SetDumpMask(int DumpMask) { this->DumpMask = DumpMask; }
void SetIgnoreDecodeErrors(bool IgnoreDecodeErrors) { this->IgnoreDecodeErrors = IgnoreDecodeErrors; } void SetIgnoreDecodeErrors(bool IgnoreDecodeErrors) { this->IgnoreDecodeErrors = IgnoreDecodeErrors; }
void SetProgressCallback(TIndexCallback IC, void *Private) { this->IC = IC; this->Private = Private; } void SetProgressCallback(TIndexCallback IC, void *ICPrivate) { this->IC = IC; this->ICPrivate = ICPrivate; }
virtual FFIndex *DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize) = 0; void SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate) { this->ANC = ANC; this->ANCPrivate = ANCPrivate; }
virtual FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize) = 0;
virtual int GetNumberOfTracks() = 0; virtual int GetNumberOfTracks() = 0;
virtual FFMS_TrackType GetTrackType(int Track) = 0; virtual FFMS_TrackType GetTrackType(int Track) = 0;
virtual const char *GetTrackCodec(int Track) = 0; virtual const char *GetTrackCodec(int Track) = 0;
@ -64,9 +88,9 @@ private:
bool IsIndexing; bool IsIndexing;
AVFormatContext *FormatContext; AVFormatContext *FormatContext;
public: public:
FFLAVFIndexer(AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize); FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize);
~FFLAVFIndexer(); ~FFLAVFIndexer();
FFIndex *DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize); FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize);
int GetNumberOfTracks() { return FormatContext->nb_streams; } int GetNumberOfTracks() { return FormatContext->nb_streams; }
FFMS_TrackType GetTrackType(int Track) { return static_cast<FFMS_TrackType>(FormatContext->streams[Track]->codec->codec_type); } 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; } const char *GetTrackCodec(int Track) { return FormatContext->streams[Track]->codec->codec_name; }
@ -78,7 +102,7 @@ private:
MatroskaReaderContext MC; MatroskaReaderContext MC;
public: public:
FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize); FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize);
FFIndex *DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize); FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize);
int GetNumberOfTracks() { return mkv_GetNumTracks(MF); } int GetNumberOfTracks() { return mkv_GetNumTracks(MF); }
FFMS_TrackType GetTrackType(int Track) { return HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, Track)->Type); } FFMS_TrackType GetTrackType(int Track) { return HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, Track)->Type); }
const char *GetTrackCodec(int Track) { return mkv_GetTrackInfo(MF, Track)->CodecID; } const char *GetTrackCodec(int Track) { return mkv_GetTrackInfo(MF, Track)->CodecID; }
@ -98,7 +122,7 @@ private:
public: public:
FFHaaliIndexer(const char *Filename, int SourceMode, char *ErrorMsg, unsigned MsgSize); FFHaaliIndexer(const char *Filename, int SourceMode, char *ErrorMsg, unsigned MsgSize);
~FFHaaliIndexer() { for (int i = 0; i < 32; i++) delete[] CodecPrivate[i]; } ~FFHaaliIndexer() { for (int i = 0; i < 32; i++) delete[] CodecPrivate[i]; }
FFIndex *DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize); FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize);
int GetNumberOfTracks() { return NumTracks; } int GetNumberOfTracks() { return NumTracks; }
FFMS_TrackType GetTrackType(int Track) { return TrackType[Track]; } 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"; } const char *GetTrackCodec(int Track) { if (Codec[Track]) return Codec[Track]->name; else return "Unsupported codec/Unknown codec name"; }