FFmpegSource2: fix lots of memory leaks, improve the api, add a progress callback

Originally committed to SVN as r2315.
This commit is contained in:
Fredrik Mellbin 2008-09-03 22:26:27 +00:00
parent b2153dd6cf
commit 19f9172d64
7 changed files with 162 additions and 91 deletions

View file

@ -54,12 +54,14 @@ AVSValue __cdecl CreateFFIndex(AVSValue Args, void* UserData, IScriptEnvironment
// 1: Index generated
// 2: Index forced to be overwritten
FrameIndex *Index = FFMS_CreateFrameIndex();
if (OverWrite || FFMS_ReadIndex(CacheFile, Index, ErrorMsg, MsgSize)) {
if (FFMS_MakeIndex(Source, Index, TrackMask, AudioFile, NULL, ErrorMsg, MsgSize))
FrameIndex *Index;
if (OverWrite || !(Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize))) {
if (!(Index = FFMS_MakeIndex(Source, TrackMask, AudioFile, NULL, NULL, ErrorMsg, MsgSize)))
Env->ThrowError("FFIndex: %s", ErrorMsg);
if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize))
if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize)) {
FFMS_DestroyFrameIndex(Index);
Env->ThrowError("FFIndex: %s", ErrorMsg);
}
FFMS_DestroyFrameIndex(Index);
if (!OverWrite)
return AVSValue(1);
@ -100,28 +102,44 @@ AVSValue __cdecl CreateFFVideoSource(AVSValue Args, void* UserData, IScriptEnvir
if (Threads < 1)
Env->ThrowError("FFVideoSource: Invalid thread count");
if (!_stricmp(Source, Timecodes))
Env->ThrowError("FFVideoSource: Timecodes will overwrite the source");
std::string DefaultCache(Source);
DefaultCache.append(".ffindex");
if (!strcmp(CacheFile, ""))
CacheFile = DefaultCache.c_str();
FrameIndex *Index = FFMS_CreateFrameIndex();
FrameIndex *Index;
if (Cache) {
if (FFMS_ReadIndex(CacheFile, Index, ErrorMsg, MsgSize)) {
if (FFMS_MakeIndex(Source, Index, 0, NULL, NULL, ErrorMsg, MsgSize))
if (!(Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize))) {
if (!(Index = FFMS_MakeIndex(Source, 0, NULL, NULL, NULL, ErrorMsg, MsgSize)))
Env->ThrowError("FFVideoSource: %s", ErrorMsg);
if (Cache)
if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize))
if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize)) {
FFMS_DestroyFrameIndex(Index);
Env->ThrowError("FFVideoSource: %s", ErrorMsg);
}
}
}
AvisynthVideoSource *Filter = new AvisynthVideoSource(Source, Track, Index, PP, Threads, SeekMode, Env, ErrorMsg, MsgSize);
AvisynthVideoSource *Filter;
if (strcmp(Timecodes, ""))
if (FFMS_WriteTimecodes(FFMS_GetTITrackIndex(Index, Filter->GetTrack(), ErrorMsg, MsgSize), Timecodes, ErrorMsg, MsgSize))
try {
Filter = new AvisynthVideoSource(Source, Track, Index, PP, Threads, SeekMode, Env, ErrorMsg, MsgSize);
} catch (...) {
FFMS_DestroyFrameIndex(Index);
throw;
}
if (strcmp(Timecodes, "")) {
if (FFMS_WriteTimecodes(FFMS_GetTITrackIndex(Index, Filter->GetTrack(), ErrorMsg, MsgSize), Timecodes, ErrorMsg, MsgSize)) {
FFMS_DestroyFrameIndex(Index);
delete Filter;
Env->ThrowError("FFVideoSource: %s", ErrorMsg);
}
}
return Filter;
}

View file

@ -53,10 +53,6 @@ FFMS_API(const AVFrameLite *) FFMS_GetFrame(VideoBase *VB, int n, char *ErrorMsg
return (AVFrameLite *)VB->GetFrame(n, ErrorMsg, MsgSize);
}
FFMS_API(FrameIndex *) FFMS_CreateFrameIndex() {
return new FrameIndex();
}
FFMS_API(void) FFMS_DestroyFrameIndex(FrameIndex *FI) {
delete FI;
}
@ -116,12 +112,12 @@ FFMS_API(int) FFMS_WriteTimecodes(FrameInfoVector *FIV, const char *TimecodeFile
return FIV->WriteTimecodes(TimecodeFile, ErrorMsg, MsgSize);
}
FFMS_API(int) FFMS_MakeIndex(const char *SourceFile, FrameIndex *TrackIndices, int AudioTrackMask, const char *AudioFile, IndexProgress *IP, char *ErrorMsg, unsigned MsgSize) {
return MakeIndex(SourceFile, TrackIndices, AudioTrackMask, AudioFile, IP, ErrorMsg, MsgSize);
FFMS_API(FrameIndex *) FFMS_MakeIndex(const char *SourceFile, int AudioTrackMask, const char *AudioFile, IndexCallback *IP, void *Private, char *ErrorMsg, unsigned MsgSize) {
return MakeIndex(SourceFile, AudioTrackMask, AudioFile, IP, Private, ErrorMsg, MsgSize);
}
FFMS_API(int) FFMS_ReadIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize) {
return ReadIndex(IndexFile, TrackIndices, ErrorMsg, MsgSize);
FFMS_API(FrameIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) {
return ReadIndex(IndexFile, ErrorMsg, MsgSize);
}
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize) {

View file

@ -31,17 +31,19 @@
# define EXTERN_C
#endif
#define FFMS_CC __stdcall
#ifdef FFMS_EXPORTS
# define FFMS_API(ret) EXTERN_C __declspec(dllexport) ret __stdcall
# define FFMS_API(ret) EXTERN_C __declspec(dllexport) ret FFMS_CC
#else
# define FFMS_API(ret) EXTERN_C __declspec(dllimport) ret __stdcall
# define FFMS_API(ret) EXTERN_C __declspec(dllimport) ret FFMS_CC
#endif
class VideoBase;
class FrameIndex;
class FrameInfoVector;
typedef int (*IndexProgress)(int64_t, int);
typedef int (*IndexCallback)(int State, int64_t Current, int64_t Total, void *Private);
// This is a subset of the original AVFrame only containing the most used parts.
// Even if it might seem like a good idea to cast it back to a full AVFrame to
@ -89,7 +91,6 @@ FFMS_API(void) FFMS_DestroyVideoSource(VideoBase *VB);
FFMS_API(int) FFMS_GetVSTrack(VideoBase *VB);
FFMS_API(const VideoProperties *) FFMS_GetVideoProperties(VideoBase *VB);
FFMS_API(const AVFrameLite *) FFMS_GetFrame(VideoBase *VB, int n, char *ErrorMsg, unsigned MsgSize);
FFMS_API(FrameIndex *) FFMS_CreateFrameIndex();
FFMS_API(void) FFMS_DestroyFrameIndex(FrameIndex *FI);
FFMS_API(int) FFMS_GetNumTracks(FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize);
FFMS_API(int) FFMS_GetNumFrames(FrameInfoVector *FIV, char *ErrorMsg, unsigned MsgSize);
@ -101,8 +102,8 @@ FFMS_API(int) FFMS_FrameFromDTS(FrameInfoVector *FIV, int64_t DTS, char *ErrorMs
FFMS_API(int) FFMS_ClosestFrameFromDTS(FrameInfoVector *FIV, int64_t DTS, char *ErrorMsg, unsigned MsgSize);
FFMS_API(const TrackTimeBase *) FFMS_GetTimeBase(FrameInfoVector *FIV, char *ErrorMsg, unsigned MsgSize);
FFMS_API(int) FFMS_WriteTimecodes(FrameInfoVector *FIV, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize);
FFMS_API(int) FFMS_MakeIndex(const char *SourceFile, FrameIndex *TrackIndices, int AudioTrackMask, const char *AudioFile, IndexProgress *IP, char *ErrorMsg, unsigned MsgSize);
FFMS_API(int) FFMS_ReadIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize);
FFMS_API(FrameIndex *) FFMS_MakeIndex(const char *SourceFile, int AudioTrackMask, const char *AudioFile, IndexCallback *IP, void *Private, char *ErrorMsg, unsigned MsgSize);
FFMS_API(FrameIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize);
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize);
#endif

View file

@ -21,7 +21,7 @@
#include "ffvideosource.h"
int VideoBase::InitPP(const char *PP, int PixelFormat, char *ErrorMsg, unsigned MsgSize) {
if (!strcmp(PP, ""))
if (PP == NULL || !strcmp(PP, ""))
return 0;
PPMode = pp_get_mode_by_name_and_quality(PP, PP_QUALITY_MAX);

View file

@ -77,11 +77,10 @@ public:
class MatroskaVideoSource : public VideoBase {
private:
unsigned int BufferSize;
CompressedStream *CS;
MatroskaFile *MF;
char ErrorMessage[256];
MatroskaReaderContext MC;
CompressedStream *CS;
char ErrorMessage[256];
int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
int GetTrackIndex(int &Index, char *ErrorMsg, unsigned MsgSize);

View file

@ -22,6 +22,7 @@
#include <fstream>
#include <set>
#include <algorithm>
#include <memory>
#include "indexing.h"
#include "wave64writer.h"
@ -48,6 +49,41 @@ public:
}
};
class IndexMemory {
private:
int16_t *DecodingBuffer;
AudioContext *AudioContexts;
public:
IndexMemory(int Tracks, int16_t *&DecodingBuffer, AudioContext *&AudioContexts) {
DecodingBuffer = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
AudioContexts = new AudioContext[Tracks];
this->DecodingBuffer = DecodingBuffer;
this->AudioContexts = AudioContexts;
}
~IndexMemory() {
delete [] DecodingBuffer;
delete [] AudioContexts;
}
};
class MatroskaMemory {
private:
MatroskaFile *MF;
MatroskaReaderContext *MC;
public:
MatroskaMemory(MatroskaFile *MF, MatroskaReaderContext *MC) {
this->MF = MF;
this->MC = MC;
}
~MatroskaMemory() {
mkv_Close(MF);
fclose(MC->ST.fp);
}
};
static bool DTSComparison(FrameInfo FI1, FrameInfo FI2) {
return FI1.DTS < FI2.DTS;
}
@ -86,14 +122,10 @@ int WriteIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg,
Index.write(reinterpret_cast<char *>(&(TrackIndices->at(i)[j])), sizeof(FrameInfo));
}
Index.close();
return 0;
}
static int MakeMatroskaIndex(const char *SourceFile, FrameIndex *TrackIndices, int AudioTrackMask, const char *AudioFile, IndexProgress *IP, char *ErrorMsg, unsigned MsgSize) {
TrackIndices->Decoder = 1;
static FrameIndex *MakeMatroskaIndex(const char *SourceFile, int AudioTrackMask, const char *AudioFile, IndexCallback *IP, void *Private, char *ErrorMsg, unsigned MsgSize) {
MatroskaFile *MF;
char ErrorMessage[256];
MatroskaReaderContext MC;
@ -104,7 +136,7 @@ static int MakeMatroskaIndex(const char *SourceFile, FrameIndex *TrackIndices, i
MC.ST.fp = fopen(SourceFile, "rb");
if (MC.ST.fp == NULL) {
_snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
return 1;
return NULL;
}
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
@ -113,12 +145,16 @@ static int MakeMatroskaIndex(const char *SourceFile, FrameIndex *TrackIndices, i
if (MF == NULL) {
fclose(MC.ST.fp);
_snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage);
return 2;
return NULL;
}
MatroskaMemory MM = MatroskaMemory(MF, &MC);
// Audio stuff
AudioContext *AudioContexts = new AudioContext[mkv_GetNumTracks(MF)];
int16_t *db;
AudioContext *AudioContexts;
IndexMemory IM = IndexMemory(mkv_GetNumTracks(MF), db, AudioContexts);
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) {
if (AudioTrackMask & (1 << i) && mkv_GetTrackInfo(MF, i)->Type == TT_AUDIO) {
@ -131,19 +167,19 @@ static int MakeMatroskaIndex(const char *SourceFile, FrameIndex *TrackIndices, i
AudioContexts[i].CS = cs_Create(MF, i, ErrorMessage, sizeof(ErrorMessage));
if (AudioContexts[i].CS == NULL) {
_snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
return 3;
return NULL;
}
}
AVCodec *AudioCodec = avcodec_find_decoder(MatroskaToFFCodecID(mkv_GetTrackInfo(MF, i)));
if (AudioCodec == NULL) {
_snprintf(ErrorMsg, MsgSize, "Audio codec not found");
return 4;
return NULL;
}
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
_snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
return 5;
return NULL;
}
} else {
AudioTrackMask &= ~(1 << i);
@ -152,15 +188,29 @@ static int MakeMatroskaIndex(const char *SourceFile, FrameIndex *TrackIndices, i
//
int64_t CurrentPos = _ftelli64(MC.ST.fp);
_fseeki64(MC.ST.fp, 0, SEEK_END);
int64_t SourceSize = _ftelli64(MC.ST.fp);
_fseeki64(MC.ST.fp, CurrentPos, SEEK_SET);
FrameIndex *TrackIndices = new FrameIndex();
TrackIndices->Decoder = 1;
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++)
TrackIndices->push_back(FrameInfoVector(mkv_TruncFloat(mkv_GetTrackInfo(MF, i)->TimecodeScale), 1000000));
uint64_t StartTime, EndTime, FilePos;
unsigned int Track, FrameFlags, FrameSize;
int16_t *db = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) {
// Update progress
if (IP) {
if ((*IP)(0, _ftelli64(MC.ST.fp), SourceSize, Private)) {
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
return NULL;
}
}
// Only create index entries for video for now to save space
if (mkv_GetTrackInfo(MF, Track)->Type == TT_VIDEO)
(*TrackIndices)[Track].push_back(FrameInfo(StartTime, (FrameFlags & FRAME_KF) != 0));
@ -177,7 +227,7 @@ static int MakeMatroskaIndex(const char *SourceFile, FrameIndex *TrackIndices, i
int Ret = avcodec_decode_audio2(AudioCodecContext, db, &dbsize, Data, Size);
if (Ret < 0) {
_snprintf(ErrorMsg, MsgSize, "Audio decoding error");
return 5;
return NULL;
}
if (Ret > 0) {
@ -203,42 +253,35 @@ static int MakeMatroskaIndex(const char *SourceFile, FrameIndex *TrackIndices, i
}
}
delete [] db;
delete [] AudioContexts;
mkv_Close(MF);
fclose(MC.ST.fp);
SortTrackIndices(TrackIndices);
return 0;
return TrackIndices;
}
int MakeIndex(const char *SourceFile, FrameIndex *TrackIndices, int AudioTrackMask, const char *AudioFile, IndexProgress *IP, char *ErrorMsg, unsigned MsgSize) {
TrackIndices->Decoder = 0;
TrackIndices->clear();
FrameIndex *MakeIndex(const char *SourceFile, int AudioTrackMask, const char *AudioFile, IndexCallback *IP, void *Private, char *ErrorMsg, unsigned MsgSize) {
AVFormatContext *FormatContext = NULL;
if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) {
_snprintf(ErrorMsg, MsgSize, "Can't open '%s'", SourceFile);
return 1;
return NULL;
}
// Do matroska indexing instead?
if (!strcmp(FormatContext->iformat->name, "matroska")) {
av_close_input_file(FormatContext);
return MakeMatroskaIndex(SourceFile, TrackIndices, AudioTrackMask, AudioFile, IP, ErrorMsg, MsgSize);
return MakeMatroskaIndex(SourceFile, AudioTrackMask, AudioFile, IP, Private, ErrorMsg, MsgSize);
}
if (av_find_stream_info(FormatContext) < 0) {
av_close_input_file(FormatContext);
_snprintf(ErrorMsg, MsgSize, "Couldn't find stream information");
return 2;
return NULL;
}
// Audio stuff
AudioContext *AudioContexts = new AudioContext[FormatContext->nb_streams];
int16_t *db;
AudioContext *AudioContexts;
IndexMemory IM = IndexMemory(FormatContext->nb_streams, db, AudioContexts);
for (unsigned int i = 0; i < FormatContext->nb_streams; i++) {
if (AudioTrackMask & (1 << i) && FormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO) {
@ -247,12 +290,12 @@ int MakeIndex(const char *SourceFile, FrameIndex *TrackIndices, int AudioTrackMa
AVCodec *AudioCodec = avcodec_find_decoder(AudioCodecContext->codec_id);
if (AudioCodec == NULL) {
_snprintf(ErrorMsg, MsgSize, "Audio codec not found");
return 3;
return NULL;
}
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
_snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
return 4;
return NULL;
}
} else {
AudioTrackMask &= ~(1 << i);
@ -261,14 +304,23 @@ int MakeIndex(const char *SourceFile, FrameIndex *TrackIndices, int AudioTrackMa
//
FrameIndex *TrackIndices = new FrameIndex();
TrackIndices->Decoder = 0;
for (unsigned int i = 0; i < FormatContext->nb_streams; i++)
TrackIndices->push_back(FrameInfoVector(FormatContext->streams[i]->time_base.den,
FormatContext->streams[i]->time_base.num * 1000));
int16_t *db = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
AVPacket Packet;
while (av_read_frame(FormatContext, &Packet) >= 0) {
// Update progress
if (IP) {
if ((*IP)(0, FormatContext->pb->pos, FormatContext->file_size, Private)) {
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
return NULL;
}
}
// Only create index entries for video for now to save space
if (FormatContext->streams[Packet.stream_index]->codec->codec_type == CODEC_TYPE_VIDEO)
(*TrackIndices)[Packet.stream_index].push_back(FrameInfo(Packet.dts, (Packet.flags & PKT_FLAG_KEY) ? 1 : 0));
@ -283,7 +335,7 @@ int MakeIndex(const char *SourceFile, FrameIndex *TrackIndices, int AudioTrackMa
int Ret = avcodec_decode_audio2(AudioCodecContext, db, &dbsize, Data, Size);
if (Ret < 0) {
_snprintf(ErrorMsg, MsgSize, "Audio decoding error");
return 5;
return NULL;
}
if (Ret > 0) {
@ -313,21 +365,18 @@ int MakeIndex(const char *SourceFile, FrameIndex *TrackIndices, int AudioTrackMa
av_free_packet(&Packet);
}
delete [] db;
delete [] AudioContexts;
av_close_input_file(FormatContext);
SortTrackIndices(TrackIndices);
return 0;
return TrackIndices;
}
int ReadIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize) {
FrameIndex *ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) {
std::ifstream Index(IndexFile, std::ios::in | std::ios::binary);
if (!Index.is_open()) {
_snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for reading", IndexFile);
return 1;
return NULL;
}
// Read the index file header
@ -335,37 +384,44 @@ int ReadIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg, u
Index.read(reinterpret_cast<char *>(&IH), sizeof(IH));
if (IH.Id != INDEXID) {
_snprintf(ErrorMsg, MsgSize, "'%s' is not a valid index file", IndexFile);
return 2;
return NULL;
}
if (IH.Version != INDEXVERSION) {
_snprintf(ErrorMsg, MsgSize, "'%s' is not the expected index version", IndexFile);
return 3;
return NULL;
}
TrackIndices->Decoder = IH.Decoder;
TrackIndices->clear();
FrameIndex *TrackIndices = new FrameIndex();
for (unsigned int i = 0; i < IH.Tracks; i++) {
// Read how many records belong to the current stream
size_t Frames;
Index.read(reinterpret_cast<char *>(&Frames), sizeof(Frames));
int Num;
Index.read(reinterpret_cast<char *>(&Num), sizeof(Num));
int Den;
Index.read(reinterpret_cast<char *>(&Den), sizeof(Den));
TrackIndices->push_back(FrameInfoVector(Num, Den));
try {
FrameInfo FI(0, false);
for (size_t j = 0; j < Frames; j++) {
Index.read(reinterpret_cast<char *>(&FI), sizeof(FrameInfo));
TrackIndices->at(i).push_back(FI);
TrackIndices->Decoder = IH.Decoder;
for (unsigned int i = 0; i < IH.Tracks; i++) {
// Read how many records belong to the current stream
size_t Frames;
Index.read(reinterpret_cast<char *>(&Frames), sizeof(Frames));
int Num;
Index.read(reinterpret_cast<char *>(&Num), sizeof(Num));
int Den;
Index.read(reinterpret_cast<char *>(&Den), sizeof(Den));
TrackIndices->push_back(FrameInfoVector(Num, Den));
FrameInfo FI(0, false);
for (size_t j = 0; j < Frames; j++) {
Index.read(reinterpret_cast<char *>(&FI), sizeof(FrameInfo));
TrackIndices->at(i).push_back(FI);
}
}
} catch (...) {
delete TrackIndices;
_snprintf(ErrorMsg, MsgSize, "Unknown error while reading index information in '%s'", IndexFile);
return NULL;
}
Index.close();
return 0;
return TrackIndices;
}
FrameInfo::FrameInfo(int64_t DTS, bool KeyFrame) {
@ -410,6 +466,7 @@ int FrameInfoVector::ClosestFrameFromDTS(int64_t DTS) {
}
int FrameInfoVector::FindClosestKeyFrame(int Frame) {
Frame = FFMIN(FFMAX(Frame, 0), size() - 1);
for (int i = Frame; i > 0; i--)
if (at(i).KeyFrame)
return i;

View file

@ -61,8 +61,8 @@ public:
int Decoder;
};
int MakeIndex(const char *SourceFile, FrameIndex *TrackIndices, int AudioTrackMask, const char *AudioFile, IndexProgress *IP, char *ErrorMsg, unsigned MsgSize);
int ReadIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize);
FrameIndex *MakeIndex(const char *SourceFile, int AudioTrackMask, const char *AudioFile, IndexCallback *IP, void *Private, char *ErrorMsg, unsigned MsgSize);
FrameIndex *ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize);
int WriteIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize);
#endif