forked from mia/Aegisub
FFMS2:
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:
parent
2377b1455e
commit
4165b7c963
12 changed files with 516 additions and 297 deletions
|
@ -108,7 +108,8 @@ FFAudio::~FFAudio() {
|
|||
void FFLAVFAudio::Free(bool CloseCodec) {
|
||||
if (CloseCodec)
|
||||
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) {
|
||||
|
@ -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
|
||||
int64_t Dummy;
|
||||
if (DecodeNextAudioBlock(DecodingBuffer, &Dummy, ErrorMsg, MsgSize) < 0) {
|
||||
if (DecodeNextAudioBlock(&Dummy, ErrorMsg, MsgSize) < 0) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
@ -169,10 +170,11 @@ FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char
|
|||
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;
|
||||
int Ret = -1;
|
||||
*Count = 0;
|
||||
uint8_t *Buf = DecodingBuffer;
|
||||
AVPacket Packet, TempPacket;
|
||||
InitNullPacket(&Packet);
|
||||
InitNullPacket(&TempPacket);
|
||||
|
@ -265,7 +267,7 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs
|
|||
int64_t DecodeCount;
|
||||
|
||||
do {
|
||||
int Ret = DecodeNextAudioBlock(DecodingBuffer, &DecodeCount, ErrorMsg, MsgSize);
|
||||
int Ret = DecodeNextAudioBlock(&DecodeCount, ErrorMsg, MsgSize);
|
||||
if (Ret < 0) {
|
||||
// FIXME
|
||||
//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
|
||||
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);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
@ -396,7 +398,7 @@ int FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *Err
|
|||
if (CacheEnd == Start + Count)
|
||||
return 0;
|
||||
|
||||
size_t CurrentAudioBlock;
|
||||
int CurrentAudioBlock;
|
||||
// Is seeking required to decode the requested samples?
|
||||
// if (!(CurrentSample >= Start && 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;
|
||||
|
||||
do {
|
||||
int Ret = DecodeNextAudioBlock(DecodingBuffer, &DecodeCount, Frames[CurrentAudioBlock].FilePos, Frames[CurrentAudioBlock].FrameSize, ErrorMsg, MsgSize);
|
||||
int Ret = DecodeNextAudioBlock(&DecodeCount, CurrentAudioBlock, ErrorMsg, MsgSize);
|
||||
if (Ret < 0) {
|
||||
// FIXME
|
||||
//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;
|
||||
}
|
||||
|
||||
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;
|
||||
int Ret = -1;
|
||||
*Count = 0;
|
||||
uint8_t *Buf = DecodingBuffer;
|
||||
AVPacket TempPacket;
|
||||
InitNullPacket(&TempPacket);
|
||||
|
||||
// FIXME check return
|
||||
ReadFrame(FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize);
|
||||
TempPacket.size = FrameSize;
|
||||
unsigned int FrameSize = Frames[AudioBlock].FrameSize;
|
||||
if (ReadFrame(Frames[AudioBlock].FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize))
|
||||
return 1;
|
||||
TempPacket.data = MC.Buffer;
|
||||
TempPacket.size = FrameSize;
|
||||
if (Frames[AudioBlock].KeyFrame)
|
||||
TempPacket.flags = PKT_FLAG_KEY;
|
||||
|
||||
while (TempPacket.size > 0) {
|
||||
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:
|
||||
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
|
|
@ -74,6 +74,7 @@ protected:
|
|||
uint8_t *DecodingBuffer;
|
||||
FFTrack Frames;
|
||||
AVCodecContext *CodecContext;
|
||||
int AudioTrack;
|
||||
TAudioProperties AP;
|
||||
public:
|
||||
FFAudio();
|
||||
|
@ -86,14 +87,12 @@ public:
|
|||
class FFLAVFAudio : public FFAudio {
|
||||
private:
|
||||
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);
|
||||
public:
|
||||
FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFLAVFAudio();
|
||||
|
||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
||||
};
|
||||
|
||||
|
@ -104,27 +103,26 @@ private:
|
|||
CompressedStream *CS;
|
||||
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);
|
||||
public:
|
||||
FFMatroskaAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFMatroskaAudio();
|
||||
|
||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
||||
};
|
||||
|
||||
#ifdef HAALISOURCE
|
||||
|
||||
class HaaliAudioSource : public FFAudio {
|
||||
class FFHaaliAudio : public FFAudio {
|
||||
private:
|
||||
CComPtr<IMMContainer> pMMC;
|
||||
uint8_t * CodecPrivate;
|
||||
|
||||
int DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, char *ErrorMsg, unsigned MsgSize);
|
||||
void Free(bool CloseCodec);
|
||||
int DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count, char *ErrorMsg, unsigned MsgSize);
|
||||
public:
|
||||
HaaliAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
||||
~HaaliAudioSource();
|
||||
|
||||
FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index, int SourceMode, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFHaaliAudio();
|
||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
||||
};
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ static AVSValue __cdecl CreateFFIndex(AVSValue Args, void* UserData, IScriptEnvi
|
|||
|
||||
FFIndex *Index = NULL;
|
||||
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);
|
||||
if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize)) {
|
||||
FFMS_DestroyFFIndex(Index);
|
||||
|
@ -122,7 +122,7 @@ static AVSValue __cdecl CreateFFVideoSource(AVSValue Args, void* UserData, IScri
|
|||
if (Cache)
|
||||
Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize);
|
||||
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);
|
||||
|
||||
if (Cache)
|
||||
|
@ -133,7 +133,7 @@ static AVSValue __cdecl CreateFFVideoSource(AVSValue Args, void* UserData, IScri
|
|||
}
|
||||
|
||||
if (Track == -1)
|
||||
Track = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_VIDEO, ErrorMsg, MsgSize);
|
||||
Track = FFMS_GetFirstIndexedTrackOfType(Index, FFMS_TYPE_VIDEO, ErrorMsg, MsgSize);
|
||||
if (Track < 0)
|
||||
Env->ThrowError("FFVideoSource: No video track found");
|
||||
|
||||
|
@ -183,7 +183,7 @@ static AVSValue __cdecl CreateFFAudioSource(AVSValue Args, void* UserData, IScri
|
|||
if (Cache)
|
||||
Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize);
|
||||
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);
|
||||
|
||||
if (Cache)
|
||||
|
@ -194,7 +194,7 @@ static AVSValue __cdecl CreateFFAudioSource(AVSValue Args, void* UserData, IScri
|
|||
}
|
||||
|
||||
if (Track == -1)
|
||||
Track = FFMS_GetFirstTrackOfType(Index, FFMS_TYPE_AUDIO, ErrorMsg, MsgSize);
|
||||
Track = FFMS_GetFirstIndexedTrackOfType(Index, FFMS_TYPE_AUDIO, ErrorMsg, MsgSize);
|
||||
if (Track < 0)
|
||||
Env->ThrowError("FFAudioSource: No audio track found");
|
||||
|
||||
|
|
|
@ -31,13 +31,27 @@ extern "C" {
|
|||
#define _snprintf snprintf
|
||||
#endif
|
||||
|
||||
FFMS_API(void) FFMS_Init() {
|
||||
static bool InitDone = false;
|
||||
if (!InitDone) {
|
||||
static int InitCount = 0;
|
||||
|
||||
FFMS_API(int) FFMS_Init() {
|
||||
if (!InitCount++) {
|
||||
av_register_all();
|
||||
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() {
|
||||
|
@ -71,6 +85,8 @@ FFMS_API(FFAudio *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FF
|
|||
switch (Index->Decoder) {
|
||||
case 0: return new FFLAVFAudio(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:
|
||||
_snprintf(ErrorMsg, MsgSize, "Unsupported format");
|
||||
return NULL;
|
||||
|
@ -190,6 +206,21 @@ FFMS_API(int) FFMS_WriteTimecodes(FFTrack *T, const char *TimecodeFile, char *Er
|
|||
return T->WriteTimecodes(TimecodeFile, ErrorMsg, MsgSize);
|
||||
}
|
||||
|
||||
FFMS_API(FFIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, 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) {
|
||||
try {
|
||||
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->SetDumpMask(DumpMask);
|
||||
Indexer->SetIgnoreDecodeErrors(IgnoreDecodeErrors);
|
||||
Indexer->SetProgressCallback(IC, Private);
|
||||
FFIndex *Index = Indexer->DoIndexing(AudioFile, ErrorMsg, MsgSize);
|
||||
Indexer->SetProgressCallback(IC, ICPrivate);
|
||||
Indexer->SetAudioNameCallback(ANC, ANCPrivate);
|
||||
FFIndex *Index = Indexer->DoIndexing(ErrorMsg, MsgSize);
|
||||
delete Indexer;
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -117,12 +117,13 @@ struct TAudioProperties {
|
|||
double LastTime;
|
||||
};
|
||||
|
||||
typedef int (FFMS_CC *TIndexCallback)(int64_t Current, int64_t Total, void *Private);
|
||||
typedef int (FFMS_CC *TAudioNameCallback)(const char *SourceFile, int Track, const TAudioProperties *AP, char *FileName, unsigned FNSize);
|
||||
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, void *Private);
|
||||
|
||||
// Most functions return 0 on success
|
||||
// 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(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);
|
||||
|
@ -151,14 +152,13 @@ FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V);
|
|||
FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A);
|
||||
FFMS_API(const TTrackTimeBase *) FFMS_GetTimeBase(FFTrack *T);
|
||||
FFMS_API(int) FFMS_WriteTimecodes(FFTrack *T, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(FFIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, 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(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(FFIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_GetPixFmt(const char *Name);
|
||||
|
||||
// Deprecated, only provided for compatibility
|
||||
FFMS_API(FFIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, TIndexCallback IC, void *Private, char *ErrorMsg, unsigned MsgSize);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -225,28 +225,31 @@ tn:64:128:256
|
|||
|
||||
<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>
|
||||
./configure --enable-memalign-hack --enable-gpl --enable-postproc
|
||||
|
||||
<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>
|
||||
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>
|
||||
|
||||
<h2>Changes</h2>
|
||||
<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>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>When audio track dumping is performed a custom callback can now be supplied to name the tracks</li>
|
||||
<li>The audio track delay is now exposed in the API in the same way as video tracks</li>
|
||||
<li>A big type and argument name cleanup in the API, many things have been renamed to be clearer and it should be completely C friendly now</li>
|
||||
<li>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>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>
|
||||
|
|
|
@ -23,125 +23,97 @@ extern "C" {
|
|||
#include <libavutil/md5.h>
|
||||
}
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "ffms.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
//#define VERBOSE
|
||||
#define VERBOSE
|
||||
|
||||
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);
|
||||
|
||||
if (Percentage <= LastPercentage)
|
||||
return 0;
|
||||
|
||||
LastPercentage = Percentage;
|
||||
Private = (void *)Percentage;
|
||||
|
||||
#ifdef VERBOSE
|
||||
cout << "Indexing " << static_cast<char *>(Private) << ", please wait... " << Percentage << "% \r" << flush;
|
||||
cout << "Indexing, please wait... " << Percentage << "% \r" << flush;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
void TestFullDump1(char *SrcFile, bool WithAudio) {
|
||||
char ErrorMsg[2000];
|
||||
AVMD5 *ctx = reinterpret_cast<AVMD5 *>(new uint8_t[av_md5_size]);
|
||||
uint8_t md5sum[16];
|
||||
int ret = FFMS_Init();
|
||||
assert(!ret);
|
||||
|
||||
if (argc != 2)
|
||||
return -1;
|
||||
FFIndexer *FIdx = FFMS_CreateIndexer(SrcFile, ErrorMsg, sizeof(ErrorMsg));
|
||||
assert(FIdx);
|
||||
FFMS_CancelIndexing(FIdx);
|
||||
FIdx = FFMS_CreateIndexer(SrcFile, ErrorMsg, sizeof(ErrorMsg));
|
||||
assert(FIdx);
|
||||
|
||||
FFMS_Init();
|
||||
#ifdef VERBOSE
|
||||
FFMS_SetLogLevel(AV_LOG_INFO);
|
||||
#endif
|
||||
FFIndex *FI = FFMS_DoIndexing(FIdx, -1, -1, FFMS_DefaultAudioFilename, NULL, false, UpdateProgress, NULL, ErrorMsg, sizeof(ErrorMsg));
|
||||
assert(FI);
|
||||
|
||||
int FMT_YV12A = FFMS_GetPixFmt("yuv420p");
|
||||
int FMT_YV12B = FFMS_GetPixFmt("yuvj420p");
|
||||
int FMT_YUY2 = FFMS_GetPixFmt("yuv422p");
|
||||
int vtrack = FFMS_GetFirstTrackOfType(FI, FFMS_TYPE_VIDEO, ErrorMsg, sizeof(ErrorMsg));
|
||||
assert(vtrack >= 0);
|
||||
int atrack = FFMS_GetFirstTrackOfType(FI, FFMS_TYPE_AUDIO, ErrorMsg, sizeof(ErrorMsg));
|
||||
assert(atrack >= 0);
|
||||
|
||||
av_md5_init(ctx);
|
||||
FFIndex *FI = FFMS_MakeIndex(argv[1], -1, 0, NULL, false, UpdateProgress, argv[1], ErrorMsg, sizeof(ErrorMsg));
|
||||
if (!FI) {
|
||||
cout << "Indexing error: " << ErrorMsg << endl;
|
||||
return 1;
|
||||
}
|
||||
FFVideo *V = FFMS_CreateVideoSource(SrcFile, vtrack, FI, "", 1, 1, ErrorMsg, sizeof(ErrorMsg));
|
||||
assert(V);
|
||||
|
||||
int track = FFMS_GetFirstTrackOfType(FI, FFMS_TYPE_VIDEO, ErrorMsg, sizeof(ErrorMsg));
|
||||
if (track < 0) {
|
||||
cout << "No usable track error: " << ErrorMsg << endl;
|
||||
return 2;
|
||||
}
|
||||
if (WithAudio) {
|
||||
uint8_t *DB = new uint8_t[100000];
|
||||
FFAudio *A = FFMS_CreateAudioSource(SrcFile, atrack, FI, ErrorMsg, sizeof(ErrorMsg));
|
||||
assert(A);
|
||||
|
||||
FFVideo *V = FFMS_CreateVideoSource(argv[1], track, FI, "", 1, 1, ErrorMsg, sizeof(ErrorMsg));
|
||||
FFMS_DestroyFFIndex(FI);
|
||||
if (!V) {
|
||||
cout << "Video source error: " << ErrorMsg << endl;
|
||||
return 3;
|
||||
const TAudioProperties *AP = FFMS_GetAudioProperties(A);
|
||||
for (int i = 0; i < AP->NumSamples / 1000; i++) {
|
||||
ret = FFMS_GetAudio(A, DB, i * 1000, 1000, ErrorMsg, sizeof(ErrorMsg));
|
||||
assert(!ret);
|
||||
}
|
||||
|
||||
FFMS_DestroyAudioSource(A);
|
||||
delete[] DB;
|
||||
}
|
||||
|
||||
const TVideoProperties *VP = FFMS_GetVideoProperties(V);
|
||||
for (int i = 0; i < VP->NumFrames; i++) {
|
||||
const TAVFrameLite *AVF = FFMS_GetFrame(V, i, ErrorMsg, sizeof(ErrorMsg));
|
||||
if (!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];
|
||||
}
|
||||
}
|
||||
assert(AVF);
|
||||
}
|
||||
|
||||
FFMS_DestroyFFIndex(FI);
|
||||
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 < sizeof(md5sum); i++)
|
||||
cout << static_cast<unsigned>(md5sum[i]);
|
||||
cout << endl;
|
||||
for (int i = 0; i < 5; i++)
|
||||
TestFullDump1(TestFiles1[5], true);
|
||||
/*
|
||||
TestFullDump1(TestFiles1[5], false);
|
||||
|
||||
for (int i = 0; i < 5; i++)
|
||||
TestFullDump1(TestFiles1[i], true);
|
||||
TestFullDump1(TestFiles1[5], false);
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -186,7 +186,10 @@ int main(int argc, char *argv[]) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
FFMS_Init();
|
||||
if (FFMS_Init()) {
|
||||
std::cout << std::endl << "Error: initialization failed" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (Verbose)
|
||||
FFMS_SetLogLevel(AV_LOG_INFO);
|
||||
|
@ -208,5 +211,6 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
FFMS_DestroyFFIndex(Index);
|
||||
FFMS_DeInit();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
int64_t Dummy;
|
||||
if (DecodeNextFrame(DecodeFrame, &Dummy, ErrorMsg, MsgSize)) {
|
||||
if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
@ -279,7 +279,7 @@ FFLAVFVideo::~FFLAVFVideo() {
|
|||
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;
|
||||
InitNullPacket(&Packet);
|
||||
int FrameFinished = 0;
|
||||
|
@ -290,7 +290,7 @@ int FFLAVFVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AStartTime, char *Err
|
|||
if (*AStartTime < 0)
|
||||
*AStartTime = Packet.dts;
|
||||
|
||||
avcodec_decode_video2(CodecContext, AFrame, &FrameFinished, &Packet);
|
||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
|
||||
}
|
||||
|
||||
av_free_packet(&Packet);
|
||||
|
@ -303,7 +303,7 @@ int FFLAVFVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AStartTime, char *Err
|
|||
if (CodecContext->has_b_frames) {
|
||||
AVPacket NullPacket;
|
||||
InitNullPacket(&NullPacket);
|
||||
avcodec_decode_video2(CodecContext, AFrame, &FrameFinished, &NullPacket);
|
||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
|
||||
}
|
||||
|
||||
if (!FrameFinished)
|
||||
|
@ -349,7 +349,7 @@ ReSeek:
|
|||
|
||||
do {
|
||||
int64_t StartTime;
|
||||
if (DecodeNextFrame(DecodeFrame, &StartTime, ErrorMsg, MsgSize))
|
||||
if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize))
|
||||
return NULL;
|
||||
|
||||
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
|
||||
int64_t Dummy;
|
||||
if (DecodeNextFrame(DecodeFrame, &Dummy, ErrorMsg, MsgSize)) {
|
||||
if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
@ -510,7 +510,7 @@ FFMatroskaVideo::~FFMatroskaVideo() {
|
|||
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;
|
||||
*AFirstStartTime = -1;
|
||||
AVPacket Packet;
|
||||
|
@ -532,7 +532,7 @@ int FFMatroskaVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime,
|
|||
Packet.flags = PKT_FLAG_KEY;
|
||||
else
|
||||
Packet.flags = 0;
|
||||
avcodec_decode_video2(CodecContext, AFrame, &FrameFinished, &Packet);
|
||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
|
||||
|
||||
if (FrameFinished)
|
||||
goto Done;
|
||||
|
@ -542,7 +542,7 @@ int FFMatroskaVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime,
|
|||
if (CodecContext->has_b_frames) {
|
||||
AVPacket NullPacket;
|
||||
InitNullPacket(&NullPacket);
|
||||
avcodec_decode_video2(CodecContext, AFrame, &FrameFinished, &NullPacket);
|
||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
|
||||
}
|
||||
|
||||
if (!FrameFinished)
|
||||
|
@ -568,7 +568,7 @@ TAVFrameLite *FFMatroskaVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize)
|
|||
|
||||
do {
|
||||
int64_t StartTime;
|
||||
if (DecodeNextFrame(DecodeFrame, &StartTime, ErrorMsg, MsgSize))
|
||||
if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize))
|
||||
return NULL;
|
||||
|
||||
if (HasSeeked) {
|
||||
|
@ -600,6 +600,7 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
|||
FFIndex *Index, const char *PP,
|
||||
int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize) {
|
||||
|
||||
CodecPrivate = NULL;
|
||||
AVCodec *Codec = NULL;
|
||||
CodecContext = NULL;
|
||||
VideoTrack = Track;
|
||||
|
@ -610,8 +611,6 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
|||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
|
||||
CLSID clsid = HAALI_TS_Parser;
|
||||
if (SourceMode == 1)
|
||||
clsid = HAALI_OGM_Parser;
|
||||
|
@ -646,7 +645,6 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
|||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
CodecPrivate = NULL;
|
||||
int CodecPrivateSize = 0;
|
||||
int CurrentTrack = 0;
|
||||
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
|
||||
int64_t Dummy;
|
||||
if (DecodeNextFrame(DecodeFrame, &Dummy, ErrorMsg, MsgSize)) {
|
||||
if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
@ -741,7 +739,7 @@ FFHaaliVideo::~FFHaaliVideo() {
|
|||
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;
|
||||
*AFirstStartTime = -1;
|
||||
AVPacket Packet;
|
||||
|
@ -768,7 +766,7 @@ int FFHaaliVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, cha
|
|||
else
|
||||
Packet.flags = 0;
|
||||
|
||||
avcodec_decode_video2(CodecContext, AFrame, &FrameFinished, &Packet);
|
||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
|
||||
|
||||
if (FrameFinished)
|
||||
goto Done;
|
||||
|
@ -779,7 +777,7 @@ int FFHaaliVideo::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, cha
|
|||
if (CodecContext->has_b_frames) {
|
||||
AVPacket NullPacket;
|
||||
InitNullPacket(&NullPacket);
|
||||
avcodec_decode_video2(CodecContext, AFrame, &FrameFinished, &NullPacket);
|
||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
|
||||
}
|
||||
|
||||
if (!FrameFinished)
|
||||
|
@ -807,7 +805,7 @@ ReSeek:
|
|||
|
||||
do {
|
||||
int64_t StartTime;
|
||||
if (DecodeNextFrame(DecodeFrame, &StartTime, ErrorMsg, MsgSize))
|
||||
if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize))
|
||||
return NULL;
|
||||
|
||||
if (HasSeeked) {
|
||||
|
|
|
@ -79,7 +79,7 @@ private:
|
|||
int SeekMode;
|
||||
|
||||
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:
|
||||
FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFLAVFVideo();
|
||||
|
@ -94,7 +94,7 @@ private:
|
|||
char ErrorMessage[256];
|
||||
|
||||
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:
|
||||
FFMatroskaVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFMatroskaVideo();
|
||||
|
@ -107,8 +107,9 @@ class FFHaaliVideo : public FFVideo {
|
|||
private:
|
||||
CComPtr<IMMContainer> pMMC;
|
||||
uint8_t * CodecPrivate;
|
||||
|
||||
void Free(bool CloseCodec);
|
||||
int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
|
||||
int DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
|
||||
public:
|
||||
FFHaaliVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFHaaliVideo();
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include <memory>
|
||||
#include <errno.h>
|
||||
#include "indexing.h"
|
||||
#include "wave64writer.h"
|
||||
|
||||
#ifdef __UNIX__
|
||||
#define _fseeki64 fseeko
|
||||
|
@ -51,48 +50,30 @@ extern "C" {
|
|||
# include "guids.h"
|
||||
#endif
|
||||
|
||||
class MatroskaAudioContext {
|
||||
class MatroskaAudioContext : public SharedAudioContext {
|
||||
public:
|
||||
Wave64Writer *W64W;
|
||||
AVCodecContext *CTX;
|
||||
CompressedStream *CS;
|
||||
int64_t CurrentSample;
|
||||
uint8_t *CodecPrivate;
|
||||
|
||||
MatroskaAudioContext() {
|
||||
W64W = NULL;
|
||||
CTX = NULL;
|
||||
CS = NULL;
|
||||
CurrentSample = 0;
|
||||
CodecPrivate = NULL;
|
||||
}
|
||||
|
||||
~MatroskaAudioContext() {
|
||||
delete[] CodecPrivate;
|
||||
delete W64W;
|
||||
if (CTX) {
|
||||
avcodec_close(CTX);
|
||||
av_free(CTX);
|
||||
}
|
||||
if (CS)
|
||||
cs_Destroy(CS);
|
||||
delete[] CodecPrivate;
|
||||
}
|
||||
};
|
||||
|
||||
class FFAudioContext {
|
||||
class FFAudioContext : public SharedAudioContext {
|
||||
public:
|
||||
Wave64Writer *W64W;
|
||||
AVCodecContext *CTX;
|
||||
int64_t CurrentSample;
|
||||
|
||||
FFAudioContext() {
|
||||
W64W = NULL;
|
||||
CTX = NULL;
|
||||
CurrentSample = 0;
|
||||
}
|
||||
|
||||
~FFAudioContext() {
|
||||
delete W64W;
|
||||
if (CTX)
|
||||
avcodec_close(CTX);
|
||||
}
|
||||
|
@ -101,42 +82,34 @@ public:
|
|||
#ifdef HAALISOURCE
|
||||
class HaaliIndexMemory {
|
||||
private:
|
||||
int16_t *DecodingBuffer;
|
||||
MatroskaAudioContext *AudioContexts;
|
||||
public:
|
||||
HaaliIndexMemory(int Tracks, int16_t *&DecodingBuffer, MatroskaAudioContext *&AudioContexts) {
|
||||
DecodingBuffer = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
|
||||
HaaliIndexMemory(int Tracks, MatroskaAudioContext *&AudioContexts) {
|
||||
AudioContexts = new MatroskaAudioContext[Tracks];
|
||||
this->DecodingBuffer = DecodingBuffer;
|
||||
this->AudioContexts = AudioContexts;
|
||||
}
|
||||
|
||||
~HaaliIndexMemory() {
|
||||
delete [] DecodingBuffer;
|
||||
delete [] AudioContexts;
|
||||
delete[] AudioContexts;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
class MatroskaIndexMemory {
|
||||
private:
|
||||
int16_t *DecodingBuffer;
|
||||
MatroskaAudioContext *AudioContexts;
|
||||
MatroskaFile *MF;
|
||||
MatroskaReaderContext *MC;
|
||||
public:
|
||||
MatroskaIndexMemory(int Tracks, int16_t *&DecodingBuffer, MatroskaAudioContext *&AudioContexts, MatroskaFile *MF, MatroskaReaderContext *MC) {
|
||||
DecodingBuffer = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
|
||||
MatroskaIndexMemory(int Tracks, MatroskaAudioContext *&AudioContexts, MatroskaFile *MF, MatroskaReaderContext *MC) {
|
||||
AudioContexts = new MatroskaAudioContext[Tracks];
|
||||
this->DecodingBuffer = DecodingBuffer;
|
||||
this->AudioContexts = AudioContexts;
|
||||
this->MF = MF;
|
||||
this->MC = MC;
|
||||
}
|
||||
|
||||
~MatroskaIndexMemory() {
|
||||
delete [] DecodingBuffer;
|
||||
delete [] AudioContexts;
|
||||
delete[] AudioContexts;
|
||||
mkv_Close(MF);
|
||||
fclose(MC->ST.fp);
|
||||
}
|
||||
|
@ -144,21 +117,17 @@ public:
|
|||
|
||||
class FFIndexMemory {
|
||||
private:
|
||||
int16_t *DecodingBuffer;
|
||||
FFAudioContext *AudioContexts;
|
||||
AVFormatContext *FormatContext;
|
||||
public:
|
||||
FFIndexMemory(int Tracks, int16_t *&DecodingBuffer, FFAudioContext *&AudioContexts, AVFormatContext *&FormatContext) {
|
||||
DecodingBuffer = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
|
||||
FFIndexMemory(int Tracks, FFAudioContext *&AudioContexts, AVFormatContext *&FormatContext) {
|
||||
AudioContexts = new FFAudioContext[Tracks];
|
||||
this->DecodingBuffer = DecodingBuffer;
|
||||
this->AudioContexts = AudioContexts;
|
||||
this->FormatContext = FormatContext;
|
||||
}
|
||||
|
||||
~FFIndexMemory() {
|
||||
delete [] DecodingBuffer;
|
||||
delete [] AudioContexts;
|
||||
delete[] AudioContexts;
|
||||
av_close_input_file(FormatContext);
|
||||
}
|
||||
};
|
||||
|
@ -167,19 +136,44 @@ static bool DTSComparison(TFrameInfo FI1, TFrameInfo FI2) {
|
|||
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++)
|
||||
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
|
||||
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;
|
||||
memset(TrackType, FFMS_TYPE_UNKNOWN, sizeof(TrackType));
|
||||
memset(Codec, 0, sizeof(Codec));
|
||||
memset(CodecPrivate, 0, sizeof(CodecPrivate));
|
||||
memset(CodecPrivateSize, 0, sizeof(CodecPrivateSize));
|
||||
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
|
||||
CLSID clsid = HAALI_TS_Parser;
|
||||
if (SourceMode == 1)
|
||||
|
@ -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
|
||||
int16_t *db;
|
||||
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;
|
||||
if (SourceMode == 1)
|
||||
TrackIndices->Decoder = 3;
|
||||
|
@ -295,9 +288,8 @@ FFIndex *FFHaaliIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsig
|
|||
|
||||
for (;;) {
|
||||
if (IC) {
|
||||
if ((*IC)(0, 1, Private)) {
|
||||
if ((*IC)(0, 1, ICPrivate)) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
||||
delete TrackIndices;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -309,28 +301,27 @@ FFIndex *FFHaaliIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsig
|
|||
REFERENCE_TIME 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
|
||||
if (TrackType[CurrentTrack] == FFMS_TYPE_VIDEO) {
|
||||
(*TrackIndices)[CurrentTrack].push_back(TFrameInfo(Ts, pMMF->IsSyncPoint() == S_OK));
|
||||
} else if (TrackType[CurrentTrack] == FFMS_TYPE_AUDIO && (IndexMask & (1 << CurrentTrack))) {
|
||||
(*TrackIndices)[CurrentTrack].push_back(TFrameInfo(Ts, AudioContexts[CurrentTrack].CurrentSample, 0 /* FIXME? */, pMMF->GetActualDataLength(), pMMF->IsSyncPoint() == S_OK));
|
||||
AVCodecContext *AudioCodecContext = AudioContexts[CurrentTrack].CTX;
|
||||
if (TrackType[Track] == FFMS_TYPE_VIDEO) {
|
||||
(*TrackIndices)[Track].push_back(TFrameInfo(Ts, pMMF->IsSyncPoint() == S_OK));
|
||||
} else if (TrackType[Track] == FFMS_TYPE_AUDIO && (IndexMask & (1 << Track))) {
|
||||
(*TrackIndices)[Track].push_back(TFrameInfo(Ts, AudioContexts[Track].CurrentSample, 0 /* FIXME? */, pMMF->GetActualDataLength(), pMMF->IsSyncPoint() == S_OK));
|
||||
AVCodecContext *AudioCodecContext = AudioContexts[Track].CTX;
|
||||
pMMF->GetPointer(&TempPacket.data);
|
||||
TempPacket.size = pMMF->GetActualDataLength();
|
||||
|
||||
while (TempPacket.size > 0) {
|
||||
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 (IgnoreDecodeErrors) {
|
||||
(*TrackIndices)[CurrentTrack].clear();
|
||||
IndexMask &= ~(1 << CurrentTrack);
|
||||
(*TrackIndices)[Track].clear();
|
||||
IndexMask &= ~(1 << Track);
|
||||
break;
|
||||
} else {
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio decoding error");
|
||||
delete TrackIndices;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -341,33 +332,21 @@ FFIndex *FFHaaliIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsig
|
|||
}
|
||||
|
||||
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))) {
|
||||
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers.
|
||||
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);
|
||||
}
|
||||
if (DumpMask & (1 << Track))
|
||||
WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize, ErrorMsg, MsgSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SortTrackIndices(TrackIndices);
|
||||
return TrackIndices;
|
||||
SortTrackIndex(TrackIndices.get());
|
||||
return TrackIndices.release();
|
||||
}
|
||||
#endif
|
||||
|
||||
FFMatroskaIndexer::FFMatroskaIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize) {
|
||||
FFMatroskaIndexer::FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) {
|
||||
SourceFile = Filename;
|
||||
char ErrorMessage[256];
|
||||
|
||||
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];
|
||||
|
||||
// Audio stuff
|
||||
int16_t *db;
|
||||
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++) {
|
||||
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);
|
||||
_fseeki64(MC.ST.fp, CurrentPos, SEEK_SET);
|
||||
|
||||
FFIndex *TrackIndices = new FFIndex();
|
||||
std::auto_ptr<FFIndex> TrackIndices(new FFIndex());
|
||||
TrackIndices->Decoder = 1;
|
||||
|
||||
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) {
|
||||
// Update progress
|
||||
if (IC) {
|
||||
if ((*IC)(_ftelli64(MC.ST.fp), SourceSize, Private)) {
|
||||
if ((*IC)(_ftelli64(MC.ST.fp), SourceSize, ICPrivate)) {
|
||||
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
||||
delete TrackIndices;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -472,7 +449,7 @@ FFIndex *FFMatroskaIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, un
|
|||
|
||||
while (TempPacket.size > 0) {
|
||||
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 (IgnoreDecodeErrors) {
|
||||
(*TrackIndices)[Track].clear();
|
||||
|
@ -480,7 +457,6 @@ FFIndex *FFMatroskaIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, un
|
|||
break;
|
||||
} else {
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio decoding error");
|
||||
delete TrackIndices;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -493,27 +469,14 @@ FFIndex *FFMatroskaIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, un
|
|||
if (dbsize > 0)
|
||||
AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
|
||||
|
||||
if (dbsize > 0 && (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.
|
||||
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);
|
||||
}
|
||||
if (DumpMask & (1 << Track))
|
||||
WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize, ErrorMsg, MsgSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SortTrackIndices(TrackIndices);
|
||||
return TrackIndices;
|
||||
SortTrackIndex(TrackIndices.get());
|
||||
return TrackIndices.release();
|
||||
}
|
||||
|
||||
FFIndexer *FFIndexer::CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) {
|
||||
|
@ -543,12 +506,13 @@ FFIndexer *FFIndexer::CreateFFIndexer(const char *Filename, char *ErrorMsg, unsi
|
|||
}
|
||||
#endif
|
||||
|
||||
return new FFLAVFIndexer(FormatContext, ErrorMsg, MsgSize);
|
||||
return new FFLAVFIndexer(Filename, FormatContext, ErrorMsg, MsgSize);
|
||||
}
|
||||
|
||||
FFLAVFIndexer::FFLAVFIndexer(AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize) {
|
||||
IsIndexing = false;
|
||||
FFLAVFIndexer::FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize) {
|
||||
SourceFile = Filename;
|
||||
this->FormatContext = FormatContext;
|
||||
IsIndexing = false;
|
||||
|
||||
if (av_find_stream_info(FormatContext) < 0) {
|
||||
av_close_input_file(FormatContext);
|
||||
|
@ -562,13 +526,12 @@ FFLAVFIndexer::~FFLAVFIndexer() {
|
|||
av_close_input_file(FormatContext);
|
||||
}
|
||||
|
||||
FFIndex *FFLAVFIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize) {
|
||||
FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||
IsIndexing = true;
|
||||
|
||||
// Audio stuff
|
||||
int16_t *db;
|
||||
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++) {
|
||||
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;
|
||||
|
||||
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) {
|
||||
// Update progress
|
||||
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");
|
||||
delete TrackIndices;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -625,7 +587,7 @@ FFIndex *FFLAVFIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsign
|
|||
|
||||
while (TempPacket.size > 0) {
|
||||
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 (IgnoreDecodeErrors) {
|
||||
(*TrackIndices)[Packet.stream_index].clear();
|
||||
|
@ -633,7 +595,6 @@ FFIndex *FFLAVFIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsign
|
|||
break;
|
||||
} else {
|
||||
_snprintf(ErrorMsg, MsgSize, "Audio decoding error");
|
||||
delete TrackIndices;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
@ -646,28 +607,14 @@ FFIndex *FFLAVFIndexer::DoIndexing(const char *AudioFile, char *ErrorMsg, unsign
|
|||
if (dbsize > 0)
|
||||
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))) {
|
||||
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers.
|
||||
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);
|
||||
}
|
||||
if (DumpMask & (1 << Packet.stream_index))
|
||||
WriteAudio(AudioContexts[Packet.stream_index], TrackIndices.get(), Packet.stream_index, dbsize, ErrorMsg, MsgSize);
|
||||
}
|
||||
}
|
||||
|
||||
av_free_packet(&Packet);
|
||||
}
|
||||
|
||||
SortTrackIndices(TrackIndices);
|
||||
return TrackIndices;
|
||||
SortTrackIndex(TrackIndices.get());
|
||||
return TrackIndices.release();
|
||||
}
|
||||
|
|
|
@ -22,9 +22,9 @@
|
|||
#define INDEXING_H
|
||||
|
||||
#include "utils.h"
|
||||
#include "ffms.h"
|
||||
#include "wave64writer.h"
|
||||
|
||||
#define INDEXVERSION 24
|
||||
#define INDEXVERSION 25
|
||||
#define INDEXID 0x53920873
|
||||
|
||||
struct IndexHeader {
|
||||
|
@ -39,21 +39,45 @@ struct IndexHeader {
|
|||
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 {
|
||||
protected:
|
||||
int IndexMask;
|
||||
int DumpMask;
|
||||
bool IgnoreDecodeErrors;
|
||||
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:
|
||||
static FFIndexer *CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize);
|
||||
virtual ~FFIndexer() { }
|
||||
void SetIndexMask(int IndexMask) { this->IndexMask = IndexMask; }
|
||||
void SetDumpMask(int DumpMask) { this->DumpMask = DumpMask; }
|
||||
void SetIgnoreDecodeErrors(bool IgnoreDecodeErrors) { this->IgnoreDecodeErrors = IgnoreDecodeErrors; }
|
||||
void SetProgressCallback(TIndexCallback IC, void *Private) { this->IC = IC; this->Private = Private; }
|
||||
virtual FFIndex *DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize) = 0;
|
||||
void SetProgressCallback(TIndexCallback IC, void *ICPrivate) { this->IC = IC; this->ICPrivate = ICPrivate; }
|
||||
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 FFMS_TrackType GetTrackType(int Track) = 0;
|
||||
virtual const char *GetTrackCodec(int Track) = 0;
|
||||
|
@ -64,9 +88,9 @@ private:
|
|||
bool IsIndexing;
|
||||
AVFormatContext *FormatContext;
|
||||
public:
|
||||
FFLAVFIndexer(AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize);
|
||||
FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFLAVFIndexer();
|
||||
FFIndex *DoIndexing(const char *AudioFile, char *ErrorMsg, unsigned MsgSize);
|
||||
FFIndex *DoIndexing(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; }
|
||||
|
@ -78,7 +102,7 @@ private:
|
|||
MatroskaReaderContext MC;
|
||||
public:
|
||||
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); }
|
||||
FFMS_TrackType GetTrackType(int Track) { return HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, Track)->Type); }
|
||||
const char *GetTrackCodec(int Track) { return mkv_GetTrackInfo(MF, Track)->CodecID; }
|
||||
|
@ -98,7 +122,7 @@ private:
|
|||
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);
|
||||
FFIndex *DoIndexing(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"; }
|
||||
|
|
Loading…
Reference in a new issue