Updating ffms2 to r221 (2.12), step 1/3: sources

Originally committed to SVN as r3574.
This commit is contained in:
Karl Blomster 2009-09-26 21:56:15 +00:00
parent 8322981b47
commit 6fe92bce72
21 changed files with 1354 additions and 1139 deletions

View file

@ -21,6 +21,9 @@
#ifndef FFMS_H #ifndef FFMS_H
#define FFMS_H #define FFMS_H
// Version format: major - minor - micro - bump
#define FFMS_VERSION ((2 << 24) | (11 << 16)| (0 << 8) | 5)
#include <stdint.h> #include <stdint.h>
#ifdef __cplusplus #ifdef __cplusplus
@ -43,11 +46,56 @@
# define FFMS_API(ret) EXTERN_C ret FFMS_CC # define FFMS_API(ret) EXTERN_C ret FFMS_CC
#endif #endif
FFMS_CLASS_TYPE FFVideo; struct FFMS_ErrorInfo {
FFMS_CLASS_TYPE FFAudio; int ErrorType;
FFMS_CLASS_TYPE FFIndexer; int SubType;
FFMS_CLASS_TYPE FFIndex; int BufferSize;
FFMS_CLASS_TYPE FFTrack; char *Buffer;
};
FFMS_CLASS_TYPE FFMS_VideoSource;
FFMS_CLASS_TYPE FFMS_AudioSource;
FFMS_CLASS_TYPE FFMS_Indexer;
FFMS_CLASS_TYPE FFMS_Index;
FFMS_CLASS_TYPE FFMS_Track;
enum FFMS_Errors {
// No error
FFMS_ERROR_SUCCESS = 0,
// Main types - where the error occurred
FFMS_ERROR_INDEX = 1,
FFMS_ERROR_INDEXING,
FFMS_ERROR_POSTPROCESSING,
FFMS_ERROR_SCALING,
FFMS_ERROR_DECODING,
FFMS_ERROR_SEEKING,
FFMS_ERROR_PARSER,
FFMS_ERROR_TRACK,
FFMS_ERROR_WAVE_WRITER,
FFMS_ERROR_CANCELLED,
// Subtypes - what caused the error
FFMS_ERROR_UNKNOWN = 20,
FFMS_ERROR_UNSUPPORTED,
FFMS_ERROR_FILE_READ,
FFMS_ERROR_FILE_WRITE,
FFMS_ERROR_NO_FILE,
FFMS_ERROR_VERSION,
FFMS_ERROR_ALLOCATION_FAILED,
FFMS_ERROR_INVALID_ARGUMENT,
FFMS_ERROR_CODEC,
FFMS_ERROR_NOT_AVAILABLE,
FFMS_ERROR_FILE_MISMATCH,
FFMS_ERROR_USER
};
enum FFMS_Sources {
FFMS_SOURCE_LAVF = 0x01,
FFMS_SOURCE_MATROSKA = 0x02,
FFMS_SOURCE_HAALIMPEG = 0x04,
FFMS_SOURCE_HAALIOGG = 0x08
};
enum FFMS_CPUFeatures { enum FFMS_CPUFeatures {
FFMS_CPU_CAPS_MMX = 0x01, FFMS_CPU_CAPS_MMX = 0x01,
@ -65,6 +113,13 @@ enum FFMS_SeekMode {
FFMS_SEEK_AGGRESSIVE = 3 FFMS_SEEK_AGGRESSIVE = 3
}; };
enum FFMS_IndexErrorHandling {
FFMS_IEH_ABORT = 0,
FFMS_IEH_CLEAR_TRACK = 1,
FFMS_IEH_STOP_TRACK = 2,
FFMS_IEH_IGNORE = 3
};
enum FFMS_TrackType { enum FFMS_TrackType {
FFMS_TYPE_UNKNOWN = -1, FFMS_TYPE_UNKNOWN = -1,
FFMS_TYPE_VIDEO, FFMS_TYPE_VIDEO,
@ -106,20 +161,20 @@ enum FFMS_AudioChannel {
}; };
enum FFMS_Resizers { enum FFMS_Resizers {
FFMS_RESIZER_FAST_BILINEAR = 0x01, FFMS_RESIZER_FAST_BILINEAR = 0x0001,
FFMS_RESIZER_BILINEAR = 0x02, FFMS_RESIZER_BILINEAR = 0x0002,
FFMS_RESIZER_BICUBIC = 0x04, FFMS_RESIZER_BICUBIC = 0x0004,
FFMS_RESIZER_X = 0x08, FFMS_RESIZER_X = 0x0008,
FFMS_RESIZER_POINT = 0x10, FFMS_RESIZER_POINT = 0x0010,
FFMS_RESIZER_AREA = 0x20, FFMS_RESIZER_AREA = 0x0020,
FFMS_RESIZER_BICUBLIN = 0x40, FFMS_RESIZER_BICUBLIN = 0x0040,
FFMS_RESIZER_GAUSS = 0x80, FFMS_RESIZER_GAUSS = 0x0080,
FFMS_RESIZER_SINC = 0x100, FFMS_RESIZER_SINC = 0x0100,
FFMS_RESIZER_LANCZOS = 0x200, FFMS_RESIZER_LANCZOS = 0x0200,
FFMS_RESIZER_SPLINE = 0x400 FFMS_RESIZER_SPLINE = 0x0400
}; };
struct FFAVFrame { struct FFMS_Frame {
uint8_t *Data[4]; uint8_t *Data[4];
int Linesize[4]; int Linesize[4];
int EncodedWidth; int EncodedWidth;
@ -135,37 +190,37 @@ struct FFAVFrame {
char PictType; char PictType;
}; };
struct FFTrackTimeBase { struct FFMS_TrackTimeBase {
int64_t Num; int64_t Num;
int64_t Den; int64_t Den;
}; };
#define FFMS_FRAMEINFO_COMMON int64_t DTS; int RepeatPict; bool KeyFrame; #define FFMS_FRAMEINFO_COMMON int64_t DTS; int RepeatPict; bool KeyFrame;
struct FFFrameInfo { struct FFMS_FrameInfo {
FFMS_FRAMEINFO_COMMON FFMS_FRAMEINFO_COMMON
}; };
struct FFVideoProperties { struct FFMS_VideoProperties {
int Width;
int Height;
int FPSDenominator; int FPSDenominator;
int FPSNumerator; int FPSNumerator;
int RFFDenominator; int RFFDenominator;
int RFFNumerator; int RFFNumerator;
int NumFrames; int NumFrames;
int VPixelFormat;
int SARNum; int SARNum;
int SARDen; int SARDen;
int CropTop; int CropTop;
int CropBottom; int CropBottom;
int CropLeft; int CropLeft;
int CropRight; int CropRight;
int TopFieldFirst;
int ColorSpace; // same as in the MPEG-2 specs, see AVColorSpace in avcodec.h
int ColorRange; // 0=unspecified, 1=16-235, 2=0-255
double FirstTime; double FirstTime;
double LastTime; double LastTime;
}; };
struct FFAudioProperties { struct FFMS_AudioProperties {
int SampleFormat; int SampleFormat;
int SampleRate; int SampleRate;
int BitsPerSample; int BitsPerSample;
@ -177,47 +232,51 @@ struct FFAudioProperties {
}; };
typedef int (FFMS_CC *TIndexCallback)(int64_t Current, int64_t Total, void *ICPrivate); typedef int (FFMS_CC *TIndexCallback)(int64_t Current, int64_t Total, void *ICPrivate);
typedef int (FFMS_CC *TAudioNameCallback)(const char *SourceFile, int Track, const FFAudioProperties *AP, char *FileName, int FNSize, void *Private); typedef int (FFMS_CC *TAudioNameCallback)(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, 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 in a graceful way // Functions without error message output can be assumed to never fail in a graceful way
FFMS_API(void) FFMS_Init(int CPUFeatures); FFMS_API(void) FFMS_Init(int CPUFeatures);
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(FFMS_VideoSource *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(FFAudio *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); FFMS_API(FFMS_AudioSource *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(void) FFMS_DestroyVideoSource(FFVideo *V); FFMS_API(void) FFMS_DestroyVideoSource(FFMS_VideoSource *V);
FFMS_API(void) FFMS_DestroyAudioSource(FFAudio *A); FFMS_API(void) FFMS_DestroyAudioSource(FFMS_AudioSource *A);
FFMS_API(const FFVideoProperties *) FFMS_GetVideoProperties(FFVideo *V); FFMS_API(const FFMS_VideoProperties *) FFMS_GetVideoProperties(FFMS_VideoSource *V);
FFMS_API(const FFAudioProperties *) FFMS_GetAudioProperties(FFAudio *A); FFMS_API(const FFMS_AudioProperties *) FFMS_GetAudioProperties(FFMS_AudioSource *A);
FFMS_API(const FFAVFrame *) FFMS_GetFrame(FFVideo *V, int n, char *ErrorMsg, unsigned MsgSize); FFMS_API(const FFMS_Frame *) FFMS_GetFrame(FFMS_VideoSource *V, int n, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(const FFAVFrame *) FFMS_GetFrameByTime(FFVideo *V, double Time, char *ErrorMsg, unsigned MsgSize); FFMS_API(const FFMS_Frame *) FFMS_GetFrameByTime(FFMS_VideoSource *V, double Time, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(int) FFMS_GetAudio(FFAudio *A, void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize); FFMS_API(int) FFMS_GetAudio(FFMS_AudioSource *A, void *Buf, int64_t Start, int64_t Count, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(int) FFMS_SetOutputFormatV(FFVideo *V, int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize); FFMS_API(int) FFMS_SetOutputFormatV(FFMS_VideoSource *V, int64_t TargetFormats, int Width, int Height, int Resizer, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(void) FFMS_ResetOutputFormatV(FFVideo *V); FFMS_API(void) FFMS_ResetOutputFormatV(FFMS_VideoSource *V);
FFMS_API(void) FFMS_DestroyIndex(FFIndex *Index); FFMS_API(int) FFMS_SetPP(FFMS_VideoSource *V, const char *PP, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(int) FFMS_GetFirstTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize); FFMS_API(void) FFMS_ResetPP(FFMS_VideoSource *V);
FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize); FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index);
FFMS_API(int) FFMS_GetNumTracks(FFIndex *Index); FFMS_API(int) FFMS_GetFirstTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(int) FFMS_GetNumTracksI(FFIndexer *Indexer); FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(int) FFMS_GetTrackType(FFTrack *T); FFMS_API(int) FFMS_GetNumTracks(FFMS_Index *Index);
FFMS_API(int) FFMS_GetTrackTypeI(FFIndexer *Indexer, int Track); FFMS_API(int) FFMS_GetNumTracksI(FFMS_Indexer *Indexer);
FFMS_API(const char *) FFMS_GetCodecNameI(FFIndexer *Indexer, int Track); FFMS_API(int) FFMS_GetTrackType(FFMS_Track *T);
FFMS_API(int) FFMS_GetNumFrames(FFTrack *T); FFMS_API(int) FFMS_GetTrackTypeI(FFMS_Indexer *Indexer, int Track);
FFMS_API(const FFFrameInfo *) FFMS_GetFrameInfo(FFTrack *T, int Frame); FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track);
FFMS_API(FFTrack *) FFMS_GetTrackFromIndex(FFIndex *Index, int Track); FFMS_API(int) FFMS_GetNumFrames(FFMS_Track *T);
FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V); FFMS_API(const FFMS_FrameInfo *) FFMS_GetFrameInfo(FFMS_Track *T, int Frame);
FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A); FFMS_API(FFMS_Track *) FFMS_GetTrackFromIndex(FFMS_Index *Index, int Track);
FFMS_API(const FFTrackTimeBase *) FFMS_GetTimeBase(FFTrack *T); FFMS_API(FFMS_Track *) FFMS_GetTrackFromVideo(FFMS_VideoSource *V);
FFMS_API(int) FFMS_WriteTimecodes(FFTrack *T, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize); FFMS_API(FFMS_Track *) FFMS_GetTrackFromAudio(FFMS_AudioSource *A);
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(const FFMS_TrackTimeBase *) FFMS_GetTimeBase(FFMS_Track *T);
FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFAudioProperties *AP, char *FileName, int FNSize, void *Private); FFMS_API(int) FFMS_WriteTimecodes(FFMS_Track *T, const char *TimecodeFile, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize); FFMS_API(FFMS_Index *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo);
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(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, void *Private);
FFMS_API(void) FFMS_CancelIndexing(FFIndexer *Indexer); FFMS_API(FFMS_Indexer *) FFMS_CreateIndexer(const char *SourceFile, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(FFIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize); FFMS_API(FFMS_Index *) FFMS_DoIndexing(FFMS_Indexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(int) FFMS_IndexBelongsToFile(FFIndex *Index, const char *SourceFile, char *ErrorMsg, unsigned MsgSize); FFMS_API(void) FFMS_CancelIndexing(FFMS_Indexer *Indexer);
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); FFMS_API(FFMS_Index *) FFMS_ReadIndex(const char *IndexFile, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(int) FFMS_IndexBelongsToFile(FFMS_Index *Index, const char *SourceFile, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(int) FFMS_GetPixFmt(const char *Name); FFMS_API(int) FFMS_GetPixFmt(const char *Name);
FFMS_API(int) FFMS_GetPresentSources();
FFMS_API(int) FFMS_GetEnabledSources();
#endif #endif

View file

@ -18,7 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#include "ffaudiosource.h" #include "audiosource.h"
/* Audio Cache */ /* Audio Cache */
@ -93,15 +93,32 @@ int64_t TAudioCache::FillRequest(int64_t Start, int64_t Samples, uint8_t *Dst) {
return FFMIN(Ret, Start + Samples); return FFMIN(Ret, Start + Samples);
} }
/* FFAudio base class */ /* FFMS_AudioSource base class */
FFAudio::FFAudio(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 10) { FFMS_AudioSource::FFMS_AudioSource(const char *SourceFile, FFMS_Index *Index, int Track) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 10), CurrentSample(0) {
if (Index->CompareFileSignature(SourceFile, ErrorMsg, MsgSize)) if (Track < 0 || Track >= static_cast<int>(Index->size()))
throw ErrorMsg; throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
"Out of bounds track index selected");
CurrentSample = 0; if (Index->at(Track).TT != FFMS_TYPE_AUDIO)
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
"Not an audio track");
if (Index[Track].size() == 0)
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
"Audio track contains no audio frames");
if (!Index->CompareFileSignature(SourceFile))
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH,
"The index does not match the source file");
} }
FFAudio::~FFAudio() { FFMS_AudioSource::~FFMS_AudioSource() {
} }
void FFMS_AudioSource::GetAudioCheck(int64_t Start, int64_t Count) {
if (Start < 0 || Start + Count > AP.NumSamples)
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_INVALID_ARGUMENT,
"Out of bounds audio samples requested");
}

View file

@ -68,63 +68,68 @@ public:
int64_t FillRequest(int64_t Start, int64_t Samples, uint8_t *Dst); int64_t FillRequest(int64_t Start, int64_t Samples, uint8_t *Dst);
}; };
class FFAudio { class FFMS_AudioSource {
friend class FFSourceResources<FFMS_AudioSource>;
protected: protected:
TAudioCache AudioCache; TAudioCache AudioCache;
int64_t CurrentSample; int64_t CurrentSample;
std::vector<uint8_t> DecodingBuffer; std::vector<uint8_t> DecodingBuffer;
FFTrack Frames; FFMS_Track Frames;
AVCodecContext *CodecContext; AVCodecContext *CodecContext;
int AudioTrack; int AudioTrack;
FFAudioProperties AP; FFMS_AudioProperties AP;
virtual void Free(bool CloseCodec) = 0;
public: public:
FFAudio(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); FFMS_AudioSource(const char *SourceFile, FFMS_Index *Index, int Track);
virtual ~FFAudio(); virtual ~FFMS_AudioSource();
FFTrack *GetFFTrack() { return &Frames; } FFMS_Track *GetTrack() { return &Frames; }
const FFAudioProperties& GetFFAudioProperties() { return AP; } const FFMS_AudioProperties& GetAudioProperties() { return AP; }
virtual int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) = 0; virtual void GetAudio(void *Buf, int64_t Start, int64_t Count) = 0;
void GetAudioCheck(int64_t Start, int64_t Count);
}; };
class FFLAVFAudio : public FFAudio { class FFLAVFAudio : public FFMS_AudioSource {
private: private:
AVFormatContext *FormatContext; AVFormatContext *FormatContext;
FFSourceResources<FFMS_AudioSource> Res;
int DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned MsgSize); void DecodeNextAudioBlock(int64_t *Count);
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, FFMS_Index *Index);
~FFLAVFAudio(); void GetAudio(void *Buf, int64_t Start, int64_t Count);
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
}; };
class FFMatroskaAudio : public FFAudio { class FFMatroskaAudio : public FFMS_AudioSource {
private: private:
MatroskaFile *MF; MatroskaFile *MF;
MatroskaReaderContext MC; MatroskaReaderContext MC;
CompressedStream *CS; CompressedStream *CS;
char ErrorMessage[256]; char ErrorMessage[256];
FFSourceResources<FFMS_AudioSource> Res;
size_t PacketNumber;
int DecodeNextAudioBlock(int64_t *Count, int AudioBlock, char *ErrorMsg, unsigned MsgSize); void DecodeNextAudioBlock(int64_t *Count);
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, FFMS_Index *Index);
~FFMatroskaAudio(); void GetAudio(void *Buf, int64_t Start, int64_t Count);
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
}; };
#ifdef HAALISOURCE #ifdef HAALISOURCE
class FFHaaliAudio : public FFAudio { class FFHaaliAudio : public FFMS_AudioSource {
private: private:
CComPtr<IMMContainer> pMMC; CComPtr<IMMContainer> pMMC;
std::vector<uint8_t> CodecPrivate; std::vector<uint8_t> CodecPrivate;
FFSourceResources<FFMS_AudioSource> Res;
void DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count);
void Free(bool CloseCodec); void Free(bool CloseCodec);
int DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count, char *ErrorMsg, unsigned MsgSize);
public: public:
FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index, int SourceMode, char *ErrorMsg, unsigned MsgSize); FFHaaliAudio(const char *SourceFile, int Track, FFMS_Index *Index, enum FFMS_Sources SourceMode);
~FFHaaliAudio(); void GetAudio(void *Buf, int64_t Start, int64_t Count);
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
}; };
#endif // HAALISOURCE #endif // HAALISOURCE

View file

@ -21,21 +21,22 @@
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#include "ffms.h" #include "ffms.h"
#include "ffvideosource.h" #include "videosource.h"
#include "ffaudiosource.h" #include "audiosource.h"
#include "indexing.h" #include "indexing.h"
static bool FFmpegInited = false; static bool FFmpegInited = false;
bool HasHaaliMPEG = false;
bool HasHaaliOGG = false;
int CPUFeatures = 0; int CPUFeatures = 0;
#ifdef FFMS_WIN_DEBUG #ifdef FFMS_WIN_DEBUG
extern "C" int av_log_level; extern "C" int av_log_level;
void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl) void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl) {
{
static int print_prefix=1; static int print_prefix=1;
static int count; static int count;
static char line[1024], prev[1024]; static char line[1024], prev[1024];
@ -56,7 +57,9 @@ void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl)
return; return;
} }
if(count>0){ if(count>0){
fprintf(stderr, " Last message repeated %d times\n", count); std::stringstream ss;
ss << " Last message repeated " << count << " times\n";
OutputDebugStringA(ss.str().c_str());
count=0; count=0;
} }
OutputDebugStringA(line); OutputDebugStringA(line);
@ -75,6 +78,13 @@ FFMS_API(void) FFMS_Init(int CPUFeatures) {
av_log_set_level(AV_LOG_QUIET); av_log_set_level(AV_LOG_QUIET);
#endif #endif
::CPUFeatures = CPUFeatures; ::CPUFeatures = CPUFeatures;
#ifdef HAALISOURCE
CComPtr<IMMContainer> pMMC;
HasHaaliMPEG = !FAILED(pMMC.CoCreateInstance(HAALI_MPEG_PARSER));
pMMC = NULL;
HasHaaliOGG = !FAILED(pMMC.CoCreateInstance(HAALI_OGG_PARSER));
pMMC = NULL;
#endif
FFmpegInited = true; FFmpegInited = true;
} }
} }
@ -87,171 +97,224 @@ FFMS_API(void) FFMS_SetLogLevel(int Level) {
av_log_set_level(Level); av_log_set_level(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(FFMS_VideoSource *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode, FFMS_ErrorInfo *ErrorInfo) {
if (Track < 0 || Track >= static_cast<int>(Index->size())) {
snprintf(ErrorMsg, MsgSize, "Out of bounds track index selected");
return NULL;
}
if (Index->at(Track).TT != FFMS_TYPE_VIDEO) {
snprintf(ErrorMsg, MsgSize, "Not a video track");
return NULL;
}
try { try {
switch (Index->Decoder) { switch (Index->Decoder) {
case 0: return new FFLAVFVideo(SourceFile, Track, Index, PP, Threads, SeekMode, ErrorMsg, MsgSize); case FFMS_SOURCE_LAVF:
case 1: return new FFMatroskaVideo(SourceFile, Track, Index, PP, Threads, ErrorMsg, MsgSize); return new FFLAVFVideo(SourceFile, Track, Index, Threads, SeekMode);
case FFMS_SOURCE_MATROSKA:
return new FFMatroskaVideo(SourceFile, Track, Index, Threads);
#ifdef HAALISOURCE #ifdef HAALISOURCE
case 2: return new FFHaaliVideo(SourceFile, Track, Index, PP, Threads, 0, ErrorMsg, MsgSize); case FFMS_SOURCE_HAALIMPEG:
case 3: return new FFHaaliVideo(SourceFile, Track, Index, PP, Threads, 1, ErrorMsg, MsgSize); if (HasHaaliMPEG)
return new FFHaaliVideo(SourceFile, Track, Index, Threads, FFMS_SOURCE_HAALIMPEG);
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali MPEG/TS source unavailable");
case FFMS_SOURCE_HAALIOGG:
if (HasHaaliOGG)
return new FFHaaliVideo(SourceFile, Track, Index, Threads, FFMS_SOURCE_HAALIOGG);
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali OGG/OGM source unavailable");
#endif #endif
default: default:
snprintf(ErrorMsg, MsgSize, "Unsupported format"); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Unsupported format");
return NULL;
} }
} catch (...) { } catch (FFMS_Exception &e) {
e.CopyOut(ErrorInfo);
return NULL; return NULL;
} }
} }
FFMS_API(FFAudio *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { FFMS_API(FFMS_AudioSource *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo) {
if (Track < 0 || Track >= static_cast<int>(Index->size())) {
snprintf(ErrorMsg, MsgSize, "Out of bounds track index selected");
return NULL;
}
if (Index->at(Track).TT != FFMS_TYPE_AUDIO) {
snprintf(ErrorMsg, MsgSize, "Not an audio track");
return NULL;
}
try { try {
switch (Index->Decoder) { switch (Index->Decoder) {
case 0: return new FFLAVFAudio(SourceFile, Track, Index, ErrorMsg, MsgSize); case FFMS_SOURCE_LAVF:
case 1: return new FFMatroskaAudio(SourceFile, Track, Index, ErrorMsg, MsgSize); return new FFLAVFAudio(SourceFile, Track, Index);
case FFMS_SOURCE_MATROSKA:
return new FFMatroskaAudio(SourceFile, Track, Index);
#ifdef HAALISOURCE #ifdef HAALISOURCE
case 2: return new FFHaaliAudio(SourceFile, Track, Index, 0, ErrorMsg, MsgSize); case FFMS_SOURCE_HAALIMPEG:
case 3: return new FFHaaliAudio(SourceFile, Track, Index, 1, ErrorMsg, MsgSize); if (HasHaaliMPEG)
return new FFHaaliAudio(SourceFile, Track, Index, FFMS_SOURCE_HAALIMPEG);
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali MPEG/TS source unavailable");
case FFMS_SOURCE_HAALIOGG:
if (HasHaaliOGG)
return new FFHaaliAudio(SourceFile, Track, Index, FFMS_SOURCE_HAALIOGG);
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali OGG/OGM source unavailable");
#endif #endif
default: default:
snprintf(ErrorMsg, MsgSize, "Unsupported format"); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Unsupported format");
return NULL;
} }
} catch (...) { } catch (FFMS_Exception &e) {
e.CopyOut(ErrorInfo);
return NULL; return NULL;
} }
} }
FFMS_API(void) FFMS_DestroyVideoSource(FFVideo *V) { FFMS_API(void) FFMS_DestroyVideoSource(FFMS_VideoSource *V) {
delete V; delete V;
} }
FFMS_API(void) FFMS_DestroyAudioSource(FFAudio *A) { FFMS_API(void) FFMS_DestroyAudioSource(FFMS_AudioSource *A) {
delete A; delete A;
} }
FFMS_API(const FFVideoProperties *) FFMS_GetVideoProperties(FFVideo *V) { FFMS_API(const FFMS_VideoProperties *) FFMS_GetVideoProperties(FFMS_VideoSource *V) {
return &V->GetFFVideoProperties(); return &V->GetVideoProperties();
} }
FFMS_API(const FFAudioProperties *) FFMS_GetAudioProperties(FFAudio *A) { FFMS_API(const FFMS_AudioProperties *) FFMS_GetAudioProperties(FFMS_AudioSource *A) {
return &A->GetFFAudioProperties(); return &A->GetAudioProperties();
} }
FFMS_API(const FFAVFrame *) FFMS_GetFrame(FFVideo *V, int n, char *ErrorMsg, unsigned MsgSize) { FFMS_API(const FFMS_Frame *) FFMS_GetFrame(FFMS_VideoSource *V, int n, FFMS_ErrorInfo *ErrorInfo) {
return (FFAVFrame *)V->GetFrame(n, ErrorMsg, MsgSize); ClearErrorInfo(ErrorInfo);
try {
return V->GetFrame(n);
} catch (FFMS_Exception &e) {
e.CopyOut(ErrorInfo);
return NULL;
}
} }
FFMS_API(const FFAVFrame *) FFMS_GetFrameByTime(FFVideo *V, double Time, char *ErrorMsg, unsigned MsgSize) { FFMS_API(const FFMS_Frame *) FFMS_GetFrameByTime(FFMS_VideoSource *V, double Time, FFMS_ErrorInfo *ErrorInfo) {
return (FFAVFrame *)V->GetFrameByTime(Time, ErrorMsg, MsgSize); ClearErrorInfo(ErrorInfo);
try {
return (FFMS_Frame *)V->GetFrameByTime(Time);
} catch (FFMS_Exception &e) {
e.CopyOut(ErrorInfo);
return NULL;
}
} }
FFMS_API(int) FFMS_GetAudio(FFAudio *A, void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) { FFMS_API(int) FFMS_GetAudio(FFMS_AudioSource *A, void *Buf, int64_t Start, int64_t Count, FFMS_ErrorInfo *ErrorInfo) {
return A->GetAudio(Buf, Start, Count, ErrorMsg, MsgSize); ClearErrorInfo(ErrorInfo);
try {
A->GetAudio(Buf, Start, Count);
} catch (FFMS_Exception &e) {
return e.CopyOut(ErrorInfo);
}
return FFMS_ERROR_SUCCESS;
} }
FFMS_API(int) FFMS_SetOutputFormatV(FFVideo *V, int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize) { FFMS_API(int) FFMS_SetOutputFormatV(FFMS_VideoSource *V, int64_t TargetFormats, int Width, int Height, int Resizer, FFMS_ErrorInfo *ErrorInfo) {
return V->SetOutputFormat(TargetFormats, Width, Height, Resizer, ErrorMsg, MsgSize); ClearErrorInfo(ErrorInfo);
try {
V->SetOutputFormat(TargetFormats, Width, Height, Resizer);
} catch (FFMS_Exception &e) {
return e.CopyOut(ErrorInfo);
}
return FFMS_ERROR_SUCCESS;
} }
FFMS_API(void) FFMS_ResetOutputFormatV(FFVideo *V) { FFMS_API(void) FFMS_ResetOutputFormatV(FFMS_VideoSource *V) {
V->ResetOutputFormat(); V->ResetOutputFormat();
} }
FFMS_API(void) FFMS_DestroyIndex(FFIndex *Index) { FFMS_API(int) FFMS_SetPP(FFMS_VideoSource *V, const char *PP, FFMS_ErrorInfo *ErrorInfo) {
ClearErrorInfo(ErrorInfo);
try {
V->SetPP(PP);
} catch (FFMS_Exception &e) {
return e.CopyOut(ErrorInfo);
}
return FFMS_ERROR_SUCCESS;
}
FFMS_API(void) FFMS_ResetPP(FFMS_VideoSource *V) {
V->ResetPP();
}
FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index) {
delete Index; delete Index;
} }
FFMS_API(int) FFMS_GetFirstTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize) { FFMS_API(int) FFMS_GetFirstTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo) {
ClearErrorInfo(ErrorInfo);
for (int i = 0; i < static_cast<int>(Index->size()); i++) for (int i = 0; i < static_cast<int>(Index->size()); i++)
if ((*Index)[i].TT == TrackType) if ((*Index)[i].TT == TrackType)
return i; return i;
snprintf(ErrorMsg, MsgSize, "No suitable, indexed track found");
return -1; try {
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE,
"No suitable, indexed track found");
} catch (FFMS_Exception &e) {
e.CopyOut(ErrorInfo);
return -1;
}
} }
FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize) { FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo) {
ClearErrorInfo(ErrorInfo);
for (int i = 0; i < static_cast<int>(Index->size()); i++) for (int i = 0; i < static_cast<int>(Index->size()); i++)
if ((*Index)[i].TT == TrackType && (*Index)[i].size() > 0) if ((*Index)[i].TT == TrackType && (*Index)[i].size() > 0)
return i; return i;
snprintf(ErrorMsg, MsgSize, "No suitable, indexed track found"); try {
return -1; throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE,
"No suitable, indexed track found");
} catch (FFMS_Exception &e) {
e.CopyOut(ErrorInfo);
return -1;
}
} }
FFMS_API(int) FFMS_GetNumTracks(FFIndex *Index) { FFMS_API(int) FFMS_GetNumTracks(FFMS_Index *Index) {
return Index->size(); return Index->size();
} }
FFMS_API(int) FFMS_GetNumTracksI(FFIndexer *Indexer) { FFMS_API(int) FFMS_GetNumTracksI(FFMS_Indexer *Indexer) {
return Indexer->GetNumberOfTracks(); return Indexer->GetNumberOfTracks();
} }
FFMS_API(int) FFMS_GetTrackType(FFTrack *T) { FFMS_API(int) FFMS_GetTrackType(FFMS_Track *T) {
return T->TT; return T->TT;
} }
FFMS_API(int) FFMS_GetTrackTypeI(FFIndexer *Indexer, int Track) { FFMS_API(int) FFMS_GetTrackTypeI(FFMS_Indexer *Indexer, int Track) {
return Indexer->GetTrackType(Track); return Indexer->GetTrackType(Track);
} }
FFMS_API(const char *) FFMS_GetCodecNameI(FFIndexer *Indexer, int Track) { FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track) {
return Indexer->GetTrackCodec(Track); return Indexer->GetTrackCodec(Track);
} }
FFMS_API(int) FFMS_GetNumFrames(FFTrack *T) { FFMS_API(int) FFMS_GetNumFrames(FFMS_Track *T) {
return T->size(); return T->size();
} }
FFMS_API(const FFFrameInfo *) FFMS_GetFrameInfo(FFTrack *T, int Frame) { FFMS_API(const FFMS_FrameInfo *) FFMS_GetFrameInfo(FFMS_Track *T, int Frame) {
return reinterpret_cast<FFFrameInfo *>(&(*T)[Frame]); return reinterpret_cast<FFMS_FrameInfo *>(&(*T)[Frame]);
} }
FFMS_API(FFTrack *) FFMS_GetTrackFromIndex(FFIndex *Index, int Track) { FFMS_API(FFMS_Track *) FFMS_GetTrackFromIndex(FFMS_Index *Index, int Track) {
return &(*Index)[Track]; return &(*Index)[Track];
} }
FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V) { FFMS_API(FFMS_Track *) FFMS_GetTrackFromVideo(FFMS_VideoSource *V) {
return V->GetFFTrack(); return V->GetTrack();
} }
FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A) { FFMS_API(FFMS_Track *) FFMS_GetTrackFromAudio(FFMS_AudioSource *A) {
return A->GetFFTrack(); return A->GetTrack();
} }
FFMS_API(const FFTrackTimeBase *) FFMS_GetTimeBase(FFTrack *T) { FFMS_API(const FFMS_TrackTimeBase *) FFMS_GetTimeBase(FFMS_Track *T) {
return &T->TB; return &T->TB;
} }
FFMS_API(int) FFMS_WriteTimecodes(FFTrack *T, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize) { FFMS_API(int) FFMS_WriteTimecodes(FFMS_Track *T, const char *TimecodeFile, FFMS_ErrorInfo *ErrorInfo) {
return T->WriteTimecodes(TimecodeFile, ErrorMsg, MsgSize); ClearErrorInfo(ErrorInfo);
try {
T->WriteTimecodes(TimecodeFile);
} catch (FFMS_Exception &e) {
return e.CopyOut(ErrorInfo);
}
return FFMS_ERROR_SUCCESS;
} }
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(FFMS_Index *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo) {
FFIndexer *Indexer = FFMS_CreateIndexer(SourceFile, ErrorMsg, MsgSize); FFMS_Indexer *Indexer = FFMS_CreateIndexer(SourceFile, ErrorInfo);
if (!Indexer) if (!Indexer)
return NULL; return NULL;
return FFMS_DoIndexing(Indexer, IndexMask, DumpMask, ANC, ANCPrivate, IgnoreDecodeErrors, IC, ICPrivate, ErrorMsg, MsgSize); return FFMS_DoIndexing(Indexer, IndexMask, DumpMask, ANC, ANCPrivate, ErrorHandling, IC, ICPrivate, ErrorInfo);
} }
/* Used by FFMS_DefaultAudioFilename */ /* Used by FFMS_DefaultAudioFilename */
@ -270,7 +333,7 @@ static void ReplaceString(std::string &s, std::string from, std::string to) {
s.replace(idx, from.length(), to); s.replace(idx, from.length(), to);
} }
FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFAudioProperties *AP, char *FileName, int FNSize, void *Private) { FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, void *Private) {
std::string s = static_cast<char *>(Private); std::string s = static_cast<char *>(Private);
ReplaceString(s, "%sourcefile%", SourceFile); ReplaceString(s, "%sourcefile%", SourceFile);
@ -281,55 +344,99 @@ FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const
ReplaceString(s, "%bps%", IntToStr(AP->BitsPerSample)); ReplaceString(s, "%bps%", IntToStr(AP->BitsPerSample));
ReplaceString(s, "%delay%", IntToStr(static_cast<int>(AP->FirstTime))); ReplaceString(s, "%delay%", IntToStr(static_cast<int>(AP->FirstTime)));
if (FileName == NULL) { if (FileName != NULL)
return s.length() + 1;
} else {
strcpy(FileName, s.c_str()); strcpy(FileName, s.c_str());
return 0;
} return s.length() + 1;
} }
FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize) { FFMS_API(FFMS_Indexer *) FFMS_CreateIndexer(const char *SourceFile, FFMS_ErrorInfo *ErrorInfo) {
ClearErrorInfo(ErrorInfo);
try { try {
return FFIndexer::CreateFFIndexer(SourceFile, ErrorMsg, MsgSize); return FFMS_Indexer::CreateIndexer(SourceFile);
} catch (...) { } catch (FFMS_Exception &e) {
e.CopyOut(ErrorInfo);
return NULL; return NULL;
} }
} }
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(FFMS_Index *) FFMS_DoIndexing(FFMS_Indexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo) {
ClearErrorInfo(ErrorInfo);
Indexer->SetIndexMask(IndexMask | DumpMask); Indexer->SetIndexMask(IndexMask | DumpMask);
Indexer->SetDumpMask(DumpMask); Indexer->SetDumpMask(DumpMask);
Indexer->SetIgnoreDecodeErrors(IgnoreDecodeErrors); Indexer->SetErrorHandling(ErrorHandling);
Indexer->SetProgressCallback(IC, ICPrivate); Indexer->SetProgressCallback(IC, ICPrivate);
Indexer->SetAudioNameCallback(ANC, ANCPrivate); Indexer->SetAudioNameCallback(ANC, ANCPrivate);
FFIndex *Index = Indexer->DoIndexing(ErrorMsg, MsgSize);
FFMS_Index *Index = NULL;
try {
Index = Indexer->DoIndexing();
} catch (FFMS_Exception &e) {
e.CopyOut(ErrorInfo);
}
delete Indexer; delete Indexer;
return Index; return Index;
} }
FFMS_API(void) FFMS_CancelIndexing(FFIndexer *Indexer) { FFMS_API(void) FFMS_CancelIndexing(FFMS_Indexer *Indexer) {
delete Indexer; delete Indexer;
} }
FFMS_API(FFIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) { FFMS_API(FFMS_Index *) FFMS_ReadIndex(const char *IndexFile, FFMS_ErrorInfo *ErrorInfo) {
FFIndex *Index = new FFIndex(); ClearErrorInfo(ErrorInfo);
if (Index->ReadIndex(IndexFile, ErrorMsg, MsgSize)) { FFMS_Index *Index = new FFMS_Index();
try {
Index->ReadIndex(IndexFile);
} catch (FFMS_Exception &e) {
delete Index; delete Index;
e.CopyOut(ErrorInfo);
return NULL; return NULL;
} else {
return Index;
} }
return Index;
} }
FFMS_API(int) FFMS_IndexBelongsToFile(FFIndex *Index, const char *SourceFile, char *ErrorMsg, unsigned MsgSize) { FFMS_API(int) FFMS_IndexBelongsToFile(FFMS_Index *Index, const char *SourceFile, FFMS_ErrorInfo *ErrorInfo) {
return Index->CompareFileSignature(SourceFile, ErrorMsg, MsgSize); ClearErrorInfo(ErrorInfo);
try {
if (!Index->CompareFileSignature(SourceFile))
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH,
"The index does not belong to the file");
} catch (FFMS_Exception &e) {
return e.CopyOut(ErrorInfo);
}
return FFMS_ERROR_SUCCESS;
} }
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo) {
return Index->WriteIndex(IndexFile, ErrorMsg, MsgSize); ClearErrorInfo(ErrorInfo);
try {
Index->WriteIndex(IndexFile);
} catch (FFMS_Exception &e) {
return e.CopyOut(ErrorInfo);
}
return FFMS_ERROR_SUCCESS;
} }
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(int) FFMS_GetPresentSources() {
int Sources = FFMS_SOURCE_LAVF | FFMS_SOURCE_MATROSKA;
#ifdef HAALISOURCE
Sources |= FFMS_SOURCE_HAALIMPEG | FFMS_SOURCE_HAALIOGG;
#endif
return Sources;
}
FFMS_API(int) FFMS_GetEnabledSources() {
if (!FFmpegInited)
return 0;
int Sources = FFMS_SOURCE_LAVF | FFMS_SOURCE_MATROSKA;
if (HasHaaliMPEG)
Sources |= FFMS_SOURCE_HAALIMPEG;
if (HasHaaliOGG)
Sources |= FFMS_SOURCE_HAALIOGG;
return Sources;
}

View file

@ -116,10 +116,10 @@ DEFINE_GUID(MEDIASUBTYPE_WMV3,
0x33564d57, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71); 0x33564d57, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
// FIXME: move somewhere else? // FIXME: move somewhere else?
DEFINE_GUID(HAALI_TS_Parser, DEFINE_GUID(HAALI_MPEG_PARSER,
0xB841F346, 0x4835, 0x4de8, 0xAA, 0x5E, 0x2E, 0x7C, 0xD2, 0xD4, 0xC4, 0x35); 0xB841F346, 0x4835, 0x4de8, 0xAA, 0x5E, 0x2E, 0x7C, 0xD2, 0xD4, 0xC4, 0x35);
DEFINE_GUID(HAALI_OGM_Parser, DEFINE_GUID(HAALI_OGG_PARSER,
0xDB43B405, 0x43AA, 0x4F01, 0x82, 0xD8, 0xD8, 0x4D, 0x47, 0xE6, 0x01, 0x9C); 0xDB43B405, 0x43AA, 0x4F01, 0x82, 0xD8, 0xD8, 0x4D, 0x47, 0xE6, 0x01, 0x9C);
//DB43B405-43AA-4F01-82D8-D84D47E6019C //DB43B405-43AA-4F01-82D8-D84D47E6019C

View file

@ -20,7 +20,9 @@
#ifdef HAALISOURCE #ifdef HAALISOURCE
#include "ffaudiosource.h" #include "audiosource.h"
void FFHaaliAudio::Free(bool CloseCodec) { void FFHaaliAudio::Free(bool CloseCodec) {
if (CloseCodec) if (CloseCodec)
@ -28,14 +30,14 @@ void FFHaaliAudio::Free(bool CloseCodec) {
av_freep(&CodecContext); av_freep(&CodecContext);
} }
int FFHaaliAudio::DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count, char *ErrorMsg, unsigned MsgSize) { void FFHaaliAudio::DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count) {
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;
*AFirstStartTime = -1; *AFirstStartTime = -1;
*Count = 0; *Count = 0;
uint8_t *Buf = &DecodingBuffer[0]; uint8_t *Buf = &DecodingBuffer[0];
AVPacket Packet; AVPacket Packet;
InitNullPacket(&Packet); InitNullPacket(Packet);
for (;;) { for (;;) {
CComPtr<IMMFrame> pMMF; CComPtr<IMMFrame> pMMF;
@ -79,56 +81,17 @@ int FFHaaliAudio::DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count,
} }
} }
Done: Done:;
return Ret;
} }
FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index, FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFMS_Index *Index, enum FFMS_Sources SourceMode)
int SourceMode, char *ErrorMsg, unsigned MsgSize) : Res(FFSourceResources<FFMS_AudioSource>(this)), FFMS_AudioSource(SourceFile, Index, Track) {
: FFAudio(SourceFile, Index, ErrorMsg, MsgSize) {
AVCodec *Codec = NULL; AVCodec *Codec = NULL;
CodecContext = NULL; CodecContext = NULL;
AudioTrack = Track; AudioTrack = Track;
Frames = (*Index)[AudioTrack]; Frames = (*Index)[AudioTrack];
if (Frames.size() == 0) { pMMC = HaaliOpenFile(SourceFile, SourceMode);
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];
ffms_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 CodecPrivateSize = 0;
int CurrentTrack = 0; int CurrentTrack = 0;
@ -154,7 +117,13 @@ FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index,
if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) { if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) {
char ACodecID[2048]; char ACodecID[2048];
wcstombs(ACodecID, pV.bstrVal, 2000); wcstombs(ACodecID, pV.bstrVal, 2000);
Codec = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, FFMS_GET_VECTOR_PTR(CodecPrivate)));
int BitDepth = 0;
pV.Clear();
if (SUCCEEDED(pBag->Read(L"Audio.BitDepth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
BitDepth = pV.uintVal;
Codec = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, FFMS_GET_VECTOR_PTR(CodecPrivate), 0, BitDepth));
} }
} }
} }
@ -166,45 +135,37 @@ FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index,
CodecContext->extradata = FFMS_GET_VECTOR_PTR(CodecPrivate); CodecContext->extradata = FFMS_GET_VECTOR_PTR(CodecPrivate);
CodecContext->extradata_size = CodecPrivateSize; CodecContext->extradata_size = CodecPrivateSize;
if (Codec == NULL) { if (Codec == NULL)
Free(false); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Audio codec not found"); "Audio codec not found");
throw ErrorMsg;
}
InitializeCodecContextFromHaaliInfo(pBag, CodecContext); InitializeCodecContextFromHaaliInfo(pBag, CodecContext);
if (avcodec_open(CodecContext, Codec) < 0) { if (avcodec_open(CodecContext, Codec) < 0)
Free(false); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Could not open audio codec"); "Could not open audio codec");
throw ErrorMsg;
} Res.CloseCodec(true);
// 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 Dummy1, Dummy2; int64_t Dummy1, Dummy2;
if (DecodeNextAudioBlock(&Dummy1, &Dummy2, ErrorMsg, MsgSize) < 0) { DecodeNextAudioBlock(&Dummy1, &Dummy2);
Free(true);
throw ErrorMsg;
}
pMMC->Seek(Frames[0].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT); pMMC->Seek(Frames[0].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT);
avcodec_flush_buffers(CodecContext); avcodec_flush_buffers(CodecContext);
FillAP(AP, CodecContext, Frames); FillAP(AP, CodecContext, Frames);
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) { if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0)
Free(true); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio"); "Codec returned zero size audio");
throw ErrorMsg;
}
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50); AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
} }
FFHaaliAudio::~FFHaaliAudio() { void FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) {
Free(true); GetAudioCheck(Start, Count);
}
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; const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
memset(Buf, 0, static_cast<size_t>(SizeConst * Count)); memset(Buf, 0, static_cast<size_t>(SizeConst * Count));
bool HasSeeked = false; bool HasSeeked = false;
@ -216,7 +177,7 @@ int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorM
int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf); int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
// Was everything in the cache? // Was everything in the cache?
if (CacheEnd == Start + Count) if (CacheEnd == Start + Count)
return 0; return;
int CurrentAudioBlock; int CurrentAudioBlock;
// Is seeking required to decode the requested samples? // Is seeking required to decode the requested samples?
@ -234,11 +195,7 @@ int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorM
int64_t FirstTime, DecodeCount; int64_t FirstTime, DecodeCount;
do { do {
int Ret = DecodeNextAudioBlock(&FirstTime, &DecodeCount, ErrorMsg, MsgSize); DecodeNextAudioBlock(&FirstTime, &DecodeCount);
if (Ret < 0) {
// FIXME
//Env->ThrowError("Bleh, bad audio decoding");
}
if (HasSeeked) { if (HasSeeked) {
CurrentAudioBlock = Frames.ClosestFrameFromDTS(FirstTime); CurrentAudioBlock = Frames.ClosestFrameFromDTS(FirstTime);
@ -257,8 +214,6 @@ int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorM
if (CurrentAudioBlock < static_cast<int>(Frames.size())) if (CurrentAudioBlock < static_cast<int>(Frames.size()))
CurrentSample = Frames[CurrentAudioBlock].SampleStart; CurrentSample = Frames[CurrentAudioBlock].SampleStart;
} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < static_cast<int>(Frames.size())); } while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < static_cast<int>(Frames.size()));
return 0;
} }
#endif // HAALISOURCE #endif // HAALISOURCE

View file

@ -24,51 +24,17 @@
FFHaaliIndexer::FFHaaliIndexer(const char *Filename, int SourceMode, char *ErrorMsg, unsigned MsgSize) : FFIndexer(Filename, ErrorMsg, MsgSize) { FFHaaliIndexer::FFHaaliIndexer(const char *Filename, enum FFMS_Sources SourceMode) : FFMS_Indexer(Filename) {
SourceFile = Filename;
this->SourceMode = SourceMode; this->SourceMode = SourceMode;
memset(TrackType, FFMS_TYPE_UNKNOWN, sizeof(TrackType)); SourceFile = Filename;
memset(Codec, 0, sizeof(Codec));
memset(CodecPrivate, 0, sizeof(CodecPrivate));
memset(CodecPrivateSize, 0, sizeof(CodecPrivateSize));
Duration = 0; Duration = 0;
for (int i = 0; i < 32; i++) {
CLSID clsid = HAALI_TS_Parser; TrackType[i] = FFMS_TYPE_UNKNOWN;
if (SourceMode == 1) Codec[i] = NULL;
clsid = HAALI_OGM_Parser; CodecPrivateSize[i] = 0;
if (FAILED(pMMC.CoCreateInstance(clsid))) {
snprintf(ErrorMsg, MsgSize, "Can't create parser");
throw ErrorMsg;
} }
CComPtr<IMemAlloc> pMA; pMMC = HaaliOpenFile(SourceFile, SourceMode);
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];
ffms_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))) {
if (SourceMode == 0)
snprintf(ErrorMsg, MsgSize, "Can't parse file, most likely a transport stream not cut at packet boundaries");
else if (SourceMode == 1)
snprintf(ErrorMsg, MsgSize, "Can't parse file");
throw ErrorMsg;
}
CComQIPtr<IPropertyBag> pBag2 = pMMC; CComQIPtr<IPropertyBag> pBag2 = pMMC;
CComVariant pV2; CComVariant pV2;
@ -99,14 +65,42 @@ FFHaaliIndexer::FFHaaliIndexer(const char *Filename, int SourceMode, char *Error
} }
pV.Clear(); pV.Clear();
if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) {
FourCC = pV.uintVal; FourCC = pV.uintVal;
// Reconstruct the missing codec private part for VC1
std::vector<uint8_t> bihvect;
bihvect.resize(sizeof(FFMS_BITMAPINFOHEADER));
FFMS_BITMAPINFOHEADER *bih = reinterpret_cast<FFMS_BITMAPINFOHEADER *>(FFMS_GET_VECTOR_PTR(bihvect));
memset(bih, 0, sizeof(FFMS_BITMAPINFOHEADER));
bih->biSize = sizeof(FFMS_BITMAPINFOHEADER) + CodecPrivateSize[NumTracks];
bih->biCompression = FourCC;
bih->biBitCount = 24;
bih->biPlanes = 1;
pV.Clear();
if (SUCCEEDED(pBag->Read(L"Video.PixelWidth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
bih->biWidth = pV.uintVal;
pV.Clear();
if (SUCCEEDED(pBag->Read(L"Video.PixelHeight", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
bih->biHeight = pV.uintVal;
CodecPrivate[NumTracks].insert(CodecPrivate[NumTracks].begin(), bihvect.begin(), bihvect.end());
CodecPrivateSize[NumTracks] += sizeof(FFMS_BITMAPINFOHEADER);
}
pV.Clear(); pV.Clear();
if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) { if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) {
char CodecID[2048]; char CodecStr[2048];
wcstombs(CodecID, pV.bstrVal, 2000); wcstombs(CodecStr, pV.bstrVal, 2000);
Codec[NumTracks] = avcodec_find_decoder(MatroskaToFFCodecID(CodecID, FFMS_GET_VECTOR_PTR(CodecPrivate[NumTracks]), FourCC));
int BitDepth = 0;
pV.Clear();
if (SUCCEEDED(pBag->Read(L"Audio.BitDepth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
BitDepth = pV.uintVal;
Codec[NumTracks] = avcodec_find_decoder(MatroskaToFFCodecID(CodecStr, FFMS_GET_VECTOR_PTR(CodecPrivate[NumTracks]), FourCC, BitDepth));
} }
} }
@ -116,17 +110,17 @@ FFHaaliIndexer::FFHaaliIndexer(const char *Filename, int SourceMode, char *Error
} }
} }
FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { FFMS_Index *FFHaaliIndexer::DoIndexing() {
std::vector<SharedAudioContext> AudioContexts(NumTracks, SharedAudioContext(true)); std::vector<SharedAudioContext> AudioContexts(NumTracks, SharedAudioContext(true));
std::vector<SharedVideoContext> VideoContexts(NumTracks, SharedVideoContext(true)); std::vector<SharedVideoContext> VideoContexts(NumTracks, SharedVideoContext(true));
std::auto_ptr<FFIndex> TrackIndices(new FFIndex(Filesize, Digest)); std::auto_ptr<FFMS_Index> TrackIndices(new FFMS_Index(Filesize, Digest));
TrackIndices->Decoder = 2; TrackIndices->Decoder = FFMS_SOURCE_HAALIMPEG;
if (SourceMode == 1) if (SourceMode == FFMS_SOURCE_HAALIOGG)
TrackIndices->Decoder = 3; TrackIndices->Decoder = FFMS_SOURCE_HAALIOGG;
for (int i = 0; i < NumTracks; i++) { for (int i = 0; i < NumTracks; i++) {
TrackIndices->push_back(FFTrack(1, 1000000, TrackType[i])); TrackIndices->push_back(FFMS_Track(1, 1000000, TrackType[i]));
if (TrackType[i] == FFMS_TYPE_VIDEO && Codec[i] && (VideoContexts[i].Parser = av_parser_init(Codec[i]->id))) { if (TrackType[i] == FFMS_TYPE_VIDEO && Codec[i] && (VideoContexts[i].Parser = av_parser_init(Codec[i]->id))) {
@ -138,8 +132,8 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
if (avcodec_open(CodecContext, Codec[i]) < 0) { if (avcodec_open(CodecContext, Codec[i]) < 0) {
av_freep(&CodecContext); av_freep(&CodecContext);
snprintf(ErrorMsg, MsgSize, "Could not open video codec"); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
return NULL; "Could not open video codec");
} }
VideoContexts[i].CodecContext = CodecContext; VideoContexts[i].CodecContext = CodecContext;
@ -147,10 +141,9 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
} }
if (IndexMask & (1 << i) && TrackType[i] == FFMS_TYPE_AUDIO) { if (IndexMask & (1 << i) && TrackType[i] == FFMS_TYPE_AUDIO) {
if (Codec[i] == NULL) { if (Codec[i] == NULL)
snprintf(ErrorMsg, MsgSize, "Audio codec not found"); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
return NULL; "Audio codec not found");
}
AVCodecContext *CodecContext = avcodec_alloc_context(); AVCodecContext *CodecContext = avcodec_alloc_context();
CodecContext->extradata = FFMS_GET_VECTOR_PTR(CodecPrivate[i]); CodecContext->extradata = FFMS_GET_VECTOR_PTR(CodecPrivate[i]);
@ -160,8 +153,8 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
if (avcodec_open(CodecContext, Codec[i]) < 0) { if (avcodec_open(CodecContext, Codec[i]) < 0) {
av_freep(&CodecContext); av_freep(&CodecContext);
AudioContexts[i].CodecContext = NULL; AudioContexts[i].CodecContext = NULL;
snprintf(ErrorMsg, MsgSize, "Could not open audio codec"); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
return NULL; "Could not open audio codec");
} }
} else { } else {
IndexMask &= ~(1 << i); IndexMask &= ~(1 << i);
@ -170,7 +163,7 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
// //
AVPacket TempPacket; AVPacket TempPacket;
InitNullPacket(&TempPacket); InitNullPacket(TempPacket);
for (;;) { for (;;) {
CComPtr<IMMFrame> pMMF; CComPtr<IMMFrame> pMMF;
@ -183,16 +176,14 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
if (IC) { if (IC) {
if (Duration > 0) { if (Duration > 0) {
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
if ((*IC)(Ts, Duration, ICPrivate)) { if ((*IC)(Ts, Duration, ICPrivate))
snprintf(ErrorMsg, MsgSize, "Cancelled by user"); throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
return NULL; "Cancelled by user");
}
} }
} else { } else {
if ((*IC)(0, 1, ICPrivate)) { if ((*IC)(0, 1, ICPrivate))
snprintf(ErrorMsg, MsgSize, "Cancelled by user"); throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
return NULL; "Cancelled by user");
}
} }
} }
@ -213,7 +204,7 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
(*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(Ts, RepeatPict, pMMF->IsSyncPoint() == S_OK)); (*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(Ts, RepeatPict, pMMF->IsSyncPoint() == S_OK));
} else if (TrackType[Track] == FFMS_TYPE_AUDIO && (IndexMask & (1 << Track))) { } else if (TrackType[Track] == FFMS_TYPE_AUDIO && (IndexMask & (1 << Track))) {
(*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(Ts, AudioContexts[Track].CurrentSample, pMMF->IsSyncPoint() == S_OK)); int64_t StartSample = AudioContexts[Track].CurrentSample;
AVCodecContext *AudioCodecContext = AudioContexts[Track].CodecContext; AVCodecContext *AudioCodecContext = AudioContexts[Track].CodecContext;
if (pMMF->IsSyncPoint() == S_OK) if (pMMF->IsSyncPoint() == S_OK)
@ -225,13 +216,18 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket); int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
if (Ret < 0) { if (Ret < 0) {
if (IgnoreDecodeErrors) { if (ErrorHandling == FFMS_IEH_ABORT) {
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
"Audio decoding error");
} else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) {
(*TrackIndices)[Track].clear(); (*TrackIndices)[Track].clear();
IndexMask &= ~(1 << Track); IndexMask &= ~(1 << Track);
break; break;
} else { } else if (ErrorHandling == FFMS_IEH_STOP_TRACK) {
snprintf(ErrorMsg, MsgSize, "Audio decoding error"); IndexMask &= ~(1 << Track);
return NULL; break;
} else if (ErrorHandling == FFMS_IEH_IGNORE) {
break;
} }
} }
@ -244,8 +240,11 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
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 (DumpMask & (1 << Track)) if (DumpMask & (1 << Track))
WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize, ErrorMsg, MsgSize); WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize);
} }
(*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(Ts, StartSample,
static_cast<unsigned int>(AudioContexts[Track].CurrentSample - StartSample), pMMF->IsSyncPoint() == S_OK));
} }
} }
@ -263,7 +262,7 @@ FFMS_TrackType FFHaaliIndexer::GetTrackType(int Track) {
const char *FFHaaliIndexer::GetTrackCodec(int Track) { const char *FFHaaliIndexer::GetTrackCodec(int Track) {
if (Codec[Track]) if (Codec[Track])
return Codec[Track]->long_name; return Codec[Track]->name;
else else
return "Unsupported codec/Unknown codec name"; return "Unsupported codec/Unknown codec name";
} }

View file

@ -18,10 +18,12 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#include "ffvideosource.h"
#ifdef HAALISOURCE #ifdef HAALISOURCE
#include "videosource.h"
void FFHaaliVideo::Free(bool CloseCodec) { void FFHaaliVideo::Free(bool CloseCodec) {
if (CloseCodec) if (CloseCodec)
avcodec_close(CodecContext); avcodec_close(CodecContext);
@ -31,9 +33,8 @@ void FFHaaliVideo::Free(bool CloseCodec) {
} }
FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track, FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
FFIndex *Index, const char *PP, FFMS_Index *Index, int Threads, enum FFMS_Sources SourceMode)
int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize) : Res(FFSourceResources<FFMS_VideoSource>(this)), FFMS_VideoSource(SourceFile, Index, Track) {
: FFVideo(SourceFile, Index, ErrorMsg, MsgSize) {
BitStreamFilter = NULL; BitStreamFilter = NULL;
AVCodec *Codec = NULL; AVCodec *Codec = NULL;
@ -41,44 +42,7 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
VideoTrack = Track; VideoTrack = Track;
Frames = (*Index)[VideoTrack]; Frames = (*Index)[VideoTrack];
if (Frames.size() == 0) { pMMC = HaaliOpenFile(SourceFile, SourceMode);
snprintf(ErrorMsg, MsgSize, "Video track contains no frames");
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];
ffms_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 CodecPrivateSize = 0;
int CurrentTrack = 0; int CurrentTrack = 0;
@ -102,9 +66,31 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
} }
pV.Clear(); pV.Clear();
if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) {
FourCC = pV.uintVal; FourCC = pV.uintVal;
// Reconstruct the missing codec private part for VC1
std::vector<uint8_t> bihvect;
bihvect.resize(sizeof(FFMS_BITMAPINFOHEADER));
FFMS_BITMAPINFOHEADER *bih = reinterpret_cast<FFMS_BITMAPINFOHEADER *>(FFMS_GET_VECTOR_PTR(bihvect));
memset(bih, 0, sizeof(FFMS_BITMAPINFOHEADER));
bih->biSize = sizeof(FFMS_BITMAPINFOHEADER) + CodecPrivateSize;
bih->biCompression = FourCC;
bih->biBitCount = 24;
bih->biPlanes = 1;
pV.Clear();
if (SUCCEEDED(pBag->Read(L"Video.PixelWidth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
bih->biWidth = pV.uintVal;
pV.Clear();
if (SUCCEEDED(pBag->Read(L"Video.PixelHeight", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
bih->biHeight = pV.uintVal;
CodecPrivate.insert(CodecPrivate.begin(), bihvect.begin(), bihvect.end());
CodecPrivateSize += sizeof(FFMS_BITMAPINFOHEADER);
}
pV.Clear(); pV.Clear();
if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) { if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) {
char ACodecID[2048]; char ACodecID[2048];
@ -122,51 +108,39 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
CodecContext->extradata_size = CodecPrivateSize; CodecContext->extradata_size = CodecPrivateSize;
CodecContext->thread_count = Threads; CodecContext->thread_count = Threads;
if (Codec == NULL) { if (Codec == NULL)
Free(false); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Video codec not found"); "Video codec not found");
throw ErrorMsg;
}
InitializeCodecContextFromHaaliInfo(pBag, CodecContext); InitializeCodecContextFromHaaliInfo(pBag, CodecContext);
if (avcodec_open(CodecContext, Codec) < 0) { if (avcodec_open(CodecContext, Codec) < 0)
Free(false); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Could not open video codec"); "Could not open video codec");
throw ErrorMsg;
}
if (Codec->id == CODEC_ID_H264 && SourceMode == 0) if (Codec->id == CODEC_ID_H264 && SourceMode == FFMS_SOURCE_HAALIMPEG)
BitStreamFilter = av_bitstream_filter_init("h264_mp4toannexb"); BitStreamFilter = av_bitstream_filter_init("h264_mp4toannexb");
Res.CloseCodec(true);
// 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(&Dummy, ErrorMsg, MsgSize)) { DecodeNextFrame(&Dummy);
Free(true);
throw ErrorMsg;
}
VP.Width = CodecContext->width;
VP.Height = CodecContext->height;;
VP.FPSDenominator = 1; VP.FPSDenominator = 1;
VP.FPSNumerator = 30; VP.FPSNumerator = 30;
VP.RFFDenominator = CodecContext->time_base.num; VP.RFFDenominator = CodecContext->time_base.num;
VP.RFFNumerator = CodecContext->time_base.den; VP.RFFNumerator = CodecContext->time_base.den;
VP.NumFrames = Frames.size(); VP.NumFrames = Frames.size();
VP.VPixelFormat = CodecContext->pix_fmt; VP.TopFieldFirst = DecodeFrame->top_field_first;
VP.ColorSpace = CodecContext->colorspace;
VP.ColorRange = CodecContext->color_range;
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
if (VP.Width <= 0 || VP.Height <= 0) { if (CodecContext->width <= 0 || CodecContext->height <= 0)
Free(true); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Codec returned zero size video"); "Codec returned zero size video");
throw ErrorMsg;
}
if (InitPP(PP, ErrorMsg, MsgSize)) {
Free(true);
throw ErrorMsg;
}
// Calculate the average framerate // Calculate the average framerate
if (Frames.size() >= 2) { if (Frames.size() >= 2) {
@ -176,10 +150,7 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
} }
// Output the already decoded frame so it isn't wasted // Output the already decoded frame so it isn't wasted
if (!OutputFrame(DecodeFrame, ErrorMsg, MsgSize)) { OutputFrame(DecodeFrame);
Free(true);
throw ErrorMsg;
}
// Set AR variables // Set AR variables
CComVariant pV; CComVariant pV;
@ -192,15 +163,11 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
VP.SARDen = pV.uiVal; VP.SARDen = pV.uiVal;
} }
FFHaaliVideo::~FFHaaliVideo() { void FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime) {
Free(true);
}
int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) {
int FrameFinished = 0; int FrameFinished = 0;
*AFirstStartTime = -1; *AFirstStartTime = -1;
AVPacket Packet; AVPacket Packet;
InitNullPacket(&Packet); InitNullPacket(Packet);
for (;;) { for (;;) {
CComPtr<IMMFrame> pMMF; CComPtr<IMMFrame> pMMF;
@ -227,6 +194,9 @@ int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsi
av_bitstream_filter_filter(BitStreamFilter, CodecContext, NULL, av_bitstream_filter_filter(BitStreamFilter, CodecContext, NULL,
&Packet.data, &Packet.size, Data, pMMF->GetActualDataLength(), !!Packet.flags); &Packet.data, &Packet.size, Data, pMMF->GetActualDataLength(), !!Packet.flags);
if (CodecContext->codec_id == CODEC_ID_MPEG4 && IsNVOP(Packet))
goto Done;
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
if (FrameFinished) if (FrameFinished)
@ -237,7 +207,7 @@ int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsi
// Flush the last frames // Flush the last frames
if (CodecContext->has_b_frames) { if (CodecContext->has_b_frames) {
AVPacket NullPacket; AVPacket NullPacket;
InitNullPacket(&NullPacket); InitNullPacket(NullPacket);
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
} }
@ -245,14 +215,14 @@ int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsi
goto Error; goto Error;
Error: Error:
Done: Done:;
return 0;
} }
FFAVFrame *FFHaaliVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { FFMS_Frame *FFHaaliVideo::GetFrame(int n) {
// PPFrame always holds frame LastFrameNum even if no PP is applied GetFrameCheck(n);
if (LastFrameNum == n) if (LastFrameNum == n)
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize); return &LocalFrame;
bool HasSeeked = false; bool HasSeeked = false;
int SeekOffset = 0; int SeekOffset = 0;
@ -266,18 +236,16 @@ ReSeek:
do { do {
int64_t StartTime; int64_t StartTime;
if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize)) DecodeNextFrame(&StartTime);
return NULL;
if (HasSeeked) { if (HasSeeked) {
HasSeeked = false; HasSeeked = false;
if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) { if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) {
// No idea where we are so go back a bit further // No idea where we are so go back a bit further
if (n + SeekOffset == 0) { if (n + SeekOffset == 0)
snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file\n"); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
return NULL; "Frame accurate seeking is not possible in this file");
}
SeekOffset -= FFMIN(20, n + SeekOffset); SeekOffset -= FFMIN(20, n + SeekOffset);
goto ReSeek; goto ReSeek;
@ -288,7 +256,7 @@ ReSeek:
} while (CurrentFrame <= n); } while (CurrentFrame <= n);
LastFrameNum = n; LastFrameNum = n;
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize); return OutputFrame(DecodeFrame);
} }
#endif // HAALISOURCE #endif // HAALISOURCE

View file

@ -22,13 +22,15 @@
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <algorithm> #include <algorithm>
#include <stdio.h>
extern "C" { extern "C" {
#include <libavutil/sha1.h> #include <libavutil/sha1.h>
} }
#define INDEXID 0x53920873
extern bool HasHaaliMPEG;
extern bool HasHaaliOGG;
struct IndexHeader { struct IndexHeader {
uint32_t Id; uint32_t Id;
@ -44,6 +46,13 @@ struct IndexHeader {
uint8_t FileSignature[20]; uint8_t FileSignature[20];
}; };
struct TrackHeader {
uint32_t TT;
uint32_t Frames;
int64_t Num;
int64_t Den;
};
SharedVideoContext::SharedVideoContext(bool FreeCodecContext) { SharedVideoContext::SharedVideoContext(bool FreeCodecContext) {
CodecContext = NULL; CodecContext = NULL;
Parser = NULL; Parser = NULL;
@ -85,47 +94,49 @@ SharedAudioContext::~SharedAudioContext() {
cs_Destroy(CS); cs_Destroy(CS);
} }
TFrameInfo::TFrameInfo(int64_t DTS, int64_t SampleStart, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) { TFrameInfo::TFrameInfo() {
}
TFrameInfo::TFrameInfo(int64_t DTS, int64_t SampleStart, unsigned int SampleCount, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) {
this->DTS = DTS; this->DTS = DTS;
this->RepeatPict = RepeatPict; this->RepeatPict = RepeatPict;
this->KeyFrame = KeyFrame; this->KeyFrame = KeyFrame;
this->SampleStart = SampleStart; this->SampleStart = SampleStart;
this->SampleCount = SampleCount;
this->FilePos = FilePos; this->FilePos = FilePos;
this->FrameSize = FrameSize; this->FrameSize = FrameSize;
this->OriginalPos = 0;
} }
TFrameInfo TFrameInfo::VideoFrameInfo(int64_t DTS, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) { TFrameInfo TFrameInfo::VideoFrameInfo(int64_t DTS, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) {
return TFrameInfo(DTS, 0, RepeatPict, KeyFrame, FilePos, FrameSize); return TFrameInfo(DTS, 0, 0, RepeatPict, KeyFrame, FilePos, FrameSize);
} }
TFrameInfo TFrameInfo::AudioFrameInfo(int64_t DTS, int64_t SampleStart, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) { TFrameInfo TFrameInfo::AudioFrameInfo(int64_t DTS, int64_t SampleStart, unsigned int SampleCount, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) {
return TFrameInfo(DTS, SampleStart, 0, KeyFrame, FilePos, FrameSize); return TFrameInfo(DTS, SampleStart, SampleCount, 0, KeyFrame, FilePos, FrameSize);
} }
int FFTrack::WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize) { void FFMS_Track::WriteTimecodes(const char *TimecodeFile) {
ffms_fstream Timecodes(TimecodeFile, std::ios::out | std::ios::trunc); ffms_fstream Timecodes(TimecodeFile, std::ios::out | std::ios::trunc);
if (!Timecodes.is_open()) { if (!Timecodes.is_open())
snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for writing", TimecodeFile); throw FFMS_Exception(FFMS_ERROR_TRACK, FFMS_ERROR_FILE_WRITE,
return 1; boost::format("Failed to open '%1%' for writing") % TimecodeFile);
}
Timecodes << "# timecode format v2\n"; Timecodes << "# timecode format v2\n";
for (iterator Cur=begin(); Cur!=end(); Cur++) for (iterator Cur=begin(); Cur!=end(); Cur++)
Timecodes << std::fixed << ((Cur->DTS * TB.Num) / (double)TB.Den) << "\n"; Timecodes << std::fixed << ((Cur->DTS * TB.Num) / (double)TB.Den) << "\n";
return 0;
} }
int FFTrack::FrameFromDTS(int64_t DTS) { int FFMS_Track::FrameFromDTS(int64_t DTS) {
for (int i = 0; i < static_cast<int>(size()); i++) for (int i = 0; i < static_cast<int>(size()); i++)
if (at(i).DTS == DTS) if (at(i).DTS == DTS)
return i; return i;
return -1; return -1;
} }
int FFTrack::ClosestFrameFromDTS(int64_t DTS) { int FFMS_Track::ClosestFrameFromDTS(int64_t DTS) {
int Frame = 0; int Frame = 0;
int64_t BestDiff = 0xFFFFFFFFFFFFFFLL; // big number int64_t BestDiff = 0xFFFFFFFFFFFFFFLL; // big number
for (int i = 0; i < static_cast<int>(size()); i++) { for (int i = 0; i < static_cast<int>(size()); i++) {
@ -139,7 +150,7 @@ int FFTrack::ClosestFrameFromDTS(int64_t DTS) {
return Frame; return Frame;
} }
int FFTrack::FindClosestVideoKeyFrame(int Frame) { int FFMS_Track::FindClosestVideoKeyFrame(int Frame) {
Frame = FFMIN(FFMAX(Frame, 0), static_cast<int>(size()) - 1); Frame = FFMIN(FFMAX(Frame, 0), static_cast<int>(size()) - 1);
for (int i = Frame; i > 0; i--) for (int i = Frame; i > 0; i--)
if (at(i).KeyFrame) if (at(i).KeyFrame)
@ -147,7 +158,7 @@ int FFTrack::FindClosestVideoKeyFrame(int Frame) {
return 0; return 0;
} }
int FFTrack::FindClosestAudioKeyFrame(int64_t Sample) { int FFMS_Track::FindClosestAudioKeyFrame(int64_t Sample) {
for (size_t i = 0; i < size(); i++) { for (size_t i = 0; i < size(); i++) {
if (at(i).SampleStart == Sample && at(i).KeyFrame) if (at(i).SampleStart == Sample && at(i).KeyFrame)
return i; return i;
@ -157,29 +168,26 @@ int FFTrack::FindClosestAudioKeyFrame(int64_t Sample) {
return size() - 1; return size() - 1;
} }
FFTrack::FFTrack() { FFMS_Track::FFMS_Track() {
this->TT = FFMS_TYPE_UNKNOWN; this->TT = FFMS_TYPE_UNKNOWN;
this->TB.Num = 0; this->TB.Num = 0;
this->TB.Den = 0; this->TB.Den = 0;
} }
FFTrack::FFTrack(int64_t Num, int64_t Den, FFMS_TrackType TT) { FFMS_Track::FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT) {
this->TT = TT; this->TT = TT;
this->TB.Num = Num; this->TB.Num = Num;
this->TB.Den = Den; this->TB.Den = Den;
} }
int FFIndex::CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20], char *ErrorMsg, unsigned MsgSize) { void FFMS_Index::CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20]) {
// use cstdio because Microsoft's implementation of std::fstream doesn't support files >4GB.
// please kill me now.
FILE *SFile = ffms_fopen(Filename,"rb"); FILE *SFile = ffms_fopen(Filename,"rb");
if (SFile == NULL) { if (SFile == NULL)
snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for hashing", Filename); throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ,
return 1; boost::format("Failed to open '%1%' for hashing") % Filename);
}
const int BlockSize = 2*1024*1024; const int BlockSize = 1024*1024;
std::vector<uint8_t> FileBuffer(BlockSize); std::vector<uint8_t> FileBuffer(BlockSize);
std::vector<uint8_t> ctxmem(av_sha1_size); std::vector<uint8_t> ctxmem(av_sha1_size);
AVSHA1 *ctx = (AVSHA1 *)&ctxmem[0]; AVSHA1 *ctx = (AVSHA1 *)&ctxmem[0];
@ -188,10 +196,10 @@ int FFIndex::CalculateFileSignature(const char *Filename, int64_t *Filesize, uin
memset(&FileBuffer[0], 0, BlockSize); memset(&FileBuffer[0], 0, BlockSize);
fread(&FileBuffer[0], 1, BlockSize, SFile); fread(&FileBuffer[0], 1, BlockSize, SFile);
if (ferror(SFile) && !feof(SFile)) { if (ferror(SFile) && !feof(SFile)) {
snprintf(ErrorMsg, MsgSize, "Failed to read from '%s' for hashing", Filename);
av_sha1_final(ctx, Digest); av_sha1_final(ctx, Digest);
fclose(SFile); fclose(SFile);
return 1; throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ,
boost::format("Failed to read '%1%' for hashing") % Filename);
} }
av_sha1_update(ctx, &FileBuffer[0], BlockSize); av_sha1_update(ctx, &FileBuffer[0], BlockSize);
@ -199,61 +207,67 @@ int FFIndex::CalculateFileSignature(const char *Filename, int64_t *Filesize, uin
memset(&FileBuffer[0], 0, BlockSize); memset(&FileBuffer[0], 0, BlockSize);
fread(&FileBuffer[0], 1, BlockSize, SFile); fread(&FileBuffer[0], 1, BlockSize, SFile);
if (ferror(SFile) && !feof(SFile)) { if (ferror(SFile) && !feof(SFile)) {
snprintf(ErrorMsg, MsgSize, "Failed to seek with offset %d from file end in '%s' for hashing", BlockSize, Filename);
av_sha1_final(ctx, Digest); av_sha1_final(ctx, Digest);
fclose(SFile); fclose(SFile);
return 1; throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ,
boost::format("Failed to seek with offset %1% from file end in '%2%' for hashing") % BlockSize % Filename);
} }
av_sha1_update(ctx, &FileBuffer[0], BlockSize); av_sha1_update(ctx, &FileBuffer[0], BlockSize);
fseeko(SFile, 0, SEEK_END); fseeko(SFile, 0, SEEK_END);
if (ferror(SFile)) { if (ferror(SFile)) {
snprintf(ErrorMsg, MsgSize, "Failed to seek to end of '%s' for hashing", Filename);
av_sha1_final(ctx, Digest); av_sha1_final(ctx, Digest);
fclose(SFile); fclose(SFile);
return 1; throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ,
boost::format("Failed to seek to end of '%1%' for hashing") % Filename);
} }
*Filesize = ftello(SFile); *Filesize = ftello(SFile);
fclose(SFile); fclose(SFile);
av_sha1_final(ctx, Digest); av_sha1_final(ctx, Digest);
return 0;
} }
static bool DTSComparison(TFrameInfo FI1, TFrameInfo FI2) { static bool DTSComparison(TFrameInfo FI1, TFrameInfo FI2) {
return FI1.DTS < FI2.DTS; return FI1.DTS < FI2.DTS;
} }
void FFIndex::Sort() { void FFMS_Index::Sort() {
for (FFIndex::iterator Cur=begin(); Cur!=end(); Cur++) for (FFMS_Index::iterator Cur=begin(); Cur!=end(); Cur++) {
for (size_t i = 0; i < Cur->size(); i++)
Cur->at(i).OriginalPos = i;
std::sort(Cur->begin(), Cur->end(), DTSComparison); std::sort(Cur->begin(), Cur->end(), DTSComparison);
std::vector<size_t> ReorderTemp;
ReorderTemp.resize(Cur->size());
for (size_t i = 0; i < Cur->size(); i++)
ReorderTemp[i] = Cur->at(i).OriginalPos;
for (size_t i = 0; i < Cur->size(); i++)
Cur->at(ReorderTemp[i]).OriginalPos = i;
}
} }
int FFIndex::CompareFileSignature(const char *Filename, char *ErrorMsg, unsigned MsgSize) { bool FFMS_Index::CompareFileSignature(const char *Filename) {
int64_t CFilesize; int64_t CFilesize;
uint8_t CDigest[20]; uint8_t CDigest[20];
CalculateFileSignature(Filename, &CFilesize, CDigest, ErrorMsg, MsgSize); CalculateFileSignature(Filename, &CFilesize, CDigest);
return (CFilesize == Filesize && !memcmp(CDigest, Digest, sizeof(Digest)));
if (CFilesize != Filesize || memcmp(CDigest, Digest, sizeof(Digest))) {
snprintf(ErrorMsg, MsgSize, "Index and source file signature mismatch");
return 1;
}
return 0;
} }
int FFIndex::WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) { void FFMS_Index::WriteIndex(const char *IndexFile) {
ffms_fstream IndexStream(IndexFile, std::ios::out | std::ios::binary | std::ios::trunc); ffms_fstream IndexStream(IndexFile, std::ios::out | std::ios::binary | std::ios::trunc);
if (!IndexStream.is_open()) { if (!IndexStream.is_open())
snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for writing", IndexFile); throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_WRITE,
return 1; boost::format("Failed to open '%1%' for writing") % IndexFile);
}
// Write the index file header // Write the index file header
IndexHeader IH; IndexHeader IH;
IH.Id = INDEXID; IH.Id = INDEXID;
IH.Version = INDEXVERSION; IH.Version = FFMS_VERSION;
IH.Tracks = size(); IH.Tracks = size();
IH.Decoder = Decoder; IH.Decoder = Decoder;
IH.LAVUVersion = avutil_version(); IH.LAVUVersion = avutil_version();
@ -267,158 +281,152 @@ int FFIndex::WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize)
IndexStream.write(reinterpret_cast<char *>(&IH), sizeof(IH)); IndexStream.write(reinterpret_cast<char *>(&IH), sizeof(IH));
for (unsigned int i = 0; i < IH.Tracks; i++) { for (unsigned int i = 0; i < IH.Tracks; i++) {
uint32_t TT = at(i).TT; TrackHeader TH;
IndexStream.write(reinterpret_cast<char *>(&TT), sizeof(TT)); TH.TT = at(i).TT;
int64_t Num = at(i).TB.Num; TH.Frames = at(i).size();
IndexStream.write(reinterpret_cast<char *>(&Num), sizeof(Num)); TH.Num = at(i).TB.Num;;
int64_t Den = at(i).TB.Den; TH.Den = at(i).TB.Den;
IndexStream.write(reinterpret_cast<char *>(&Den), sizeof(Den));
int64_t Frames = at(i).size();
IndexStream.write(reinterpret_cast<char *>(&Frames), sizeof(Frames));
for (FFTrack::iterator Cur=at(i).begin(); Cur!=at(i).end(); Cur++) IndexStream.write(reinterpret_cast<char *>(&TH), sizeof(TH));
IndexStream.write(reinterpret_cast<char *>(&*Cur), sizeof(TFrameInfo)); IndexStream.write(reinterpret_cast<char *>(FFMS_GET_VECTOR_PTR(at(i))), TH.Frames * sizeof(TFrameInfo));
} }
return 0;
} }
int FFIndex::ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) { void FFMS_Index::ReadIndex(const char *IndexFile) {
ffms_fstream Index(IndexFile, std::ios::in | std::ios::binary); ffms_fstream Index(IndexFile, std::ios::in | std::ios::binary);
if (!Index.is_open()) { if (!Index.is_open())
snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for reading", IndexFile); throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ,
return 1; boost::format("Failed to open '%1%' for reading") % IndexFile);
}
// Read the index file header // Read the index file header
IndexHeader IH; IndexHeader IH;
Index.read(reinterpret_cast<char *>(&IH), sizeof(IH)); Index.read(reinterpret_cast<char *>(&IH), sizeof(IH));
if (IH.Id != INDEXID) { if (IH.Id != INDEXID)
snprintf(ErrorMsg, MsgSize, "'%s' is not a valid index file", IndexFile); throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
return 2; boost::format("'%1%' is not a valid index file") % IndexFile);
}
if (IH.Version != INDEXVERSION) { if (IH.Version != FFMS_VERSION)
snprintf(ErrorMsg, MsgSize, "'%s' is not the expected index version", IndexFile); throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_VERSION,
return 3; boost::format("'%1%' is not the expected index version") % IndexFile);
}
if (IH.LAVUVersion != avutil_version() || IH.LAVFVersion != avformat_version() || if (IH.LAVUVersion != avutil_version() || IH.LAVFVersion != avformat_version() ||
IH.LAVCVersion != avcodec_version() || IH.LSWSVersion != swscale_version() || IH.LAVCVersion != avcodec_version() || IH.LSWSVersion != swscale_version() ||
IH.LPPVersion != postproc_version()) { IH.LPPVersion != postproc_version())
snprintf(ErrorMsg, MsgSize, "A different FFmpeg build was used to create '%s'", IndexFile); throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_VERSION,
return 4; boost::format("A different FFmpeg build was used to create '%1%'") % IndexFile);
}
if (!(IH.Decoder & FFMS_GetEnabledSources()))
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE,
"The source which this index was created with is not available");
Decoder = IH.Decoder; Decoder = IH.Decoder;
Filesize = IH.FileSize; Filesize = IH.FileSize;
memcpy(Digest, IH.FileSignature, sizeof(Digest)); memcpy(Digest, IH.FileSignature, sizeof(Digest));
try { try {
for (unsigned int i = 0; i < IH.Tracks; i++) { for (unsigned int i = 0; i < IH.Tracks; i++) {
// Read how many records belong to the current stream TrackHeader TH;
uint32_t TT; Index.read(reinterpret_cast<char *>(&TH), sizeof(TH));
Index.read(reinterpret_cast<char *>(&TT), sizeof(TT)); push_back(FFMS_Track(TH.Num, TH.Den, static_cast<FFMS_TrackType>(TH.TT)));
int64_t Num;
Index.read(reinterpret_cast<char *>(&Num), sizeof(Num));
int64_t Den;
Index.read(reinterpret_cast<char *>(&Den), sizeof(Den));
int64_t Frames;
Index.read(reinterpret_cast<char *>(&Frames), sizeof(Frames));
push_back(FFTrack(Num, Den, static_cast<FFMS_TrackType>(TT)));
TFrameInfo FI = TFrameInfo::VideoFrameInfo(0, 0, false); if (TH.Frames) {
for (size_t j = 0; j < Frames; j++) { at(i).resize(TH.Frames);
Index.read(reinterpret_cast<char *>(&FI), sizeof(TFrameInfo)); Index.read(reinterpret_cast<char *>(FFMS_GET_VECTOR_PTR(at(i))), TH.Frames * sizeof(TFrameInfo));
at(i).push_back(FI);
} }
} }
} catch (...) { } catch (...) {
snprintf(ErrorMsg, MsgSize, "Unknown error while reading index information in '%s'", IndexFile); throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_UNKNOWN,
return 5; boost::format("Unknown error while reading index information in '%1%'") % IndexFile);
} }
return 0;
} }
FFIndex::FFIndex() { FFMS_Index::FFMS_Index() {
// this comment documents nothing // this comment documents nothing
} }
FFIndex::FFIndex(int64_t Filesize, uint8_t Digest[20]) { FFMS_Index::FFMS_Index(int64_t Filesize, uint8_t Digest[20]) {
this->Filesize = Filesize; this->Filesize = Filesize;
memcpy(this->Digest, Digest, sizeof(this->Digest)); memcpy(this->Digest, Digest, sizeof(this->Digest));
} }
void FFIndexer::SetIndexMask(int IndexMask) { void FFMS_Indexer::SetIndexMask(int IndexMask) {
this->IndexMask = IndexMask; this->IndexMask = IndexMask;
} }
void FFIndexer::SetDumpMask(int DumpMask) { void FFMS_Indexer::SetDumpMask(int DumpMask) {
this->DumpMask = DumpMask; this->DumpMask = DumpMask;
} }
void FFIndexer::SetIgnoreDecodeErrors(bool IgnoreDecodeErrors) { void FFMS_Indexer::SetErrorHandling(int ErrorHandling) {
this->IgnoreDecodeErrors = IgnoreDecodeErrors; if (ErrorHandling != FFMS_IEH_ABORT && ErrorHandling != FFMS_IEH_CLEAR_TRACK &&
ErrorHandling != FFMS_IEH_STOP_TRACK && ErrorHandling != FFMS_IEH_IGNORE)
throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_INVALID_ARGUMENT,
"Invalid error handling mode specified");
this->ErrorHandling = ErrorHandling;
} }
void FFIndexer::SetProgressCallback(TIndexCallback IC, void *ICPrivate) { void FFMS_Indexer::SetProgressCallback(TIndexCallback IC, void *ICPrivate) {
this->IC = IC; this->IC = IC;
this->ICPrivate = ICPrivate; this->ICPrivate = ICPrivate;
} }
void FFIndexer::SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate) { void FFMS_Indexer::SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate) {
this->ANC = ANC; this->ANC = ANC;
this->ANCPrivate = ANCPrivate; this->ANCPrivate = ANCPrivate;
} }
FFIndexer *FFIndexer::CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) { FFMS_Indexer *FFMS_Indexer::CreateIndexer(const char *Filename) {
AVFormatContext *FormatContext = NULL; AVFormatContext *FormatContext = NULL;
if (av_open_input_file(&FormatContext, Filename, NULL, 0, NULL) != 0) { if (av_open_input_file(&FormatContext, Filename, NULL, 0, NULL) != 0)
snprintf(ErrorMsg, MsgSize, "Can't open '%s'", Filename); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
return NULL; boost::format("Can't open '%1%'") % Filename);
}
// Do matroska indexing instead? // Do matroska indexing instead?
if (!strcmp(FormatContext->iformat->name, "matroska")) { if (!strcmp(FormatContext->iformat->name, "matroska")) {
av_close_input_file(FormatContext); av_close_input_file(FormatContext);
return new FFMatroskaIndexer(Filename, ErrorMsg, MsgSize); return new FFMatroskaIndexer(Filename);
} }
#ifdef HAALISOURCE #ifdef HAALISOURCE
// Do haali ts indexing instead? // Do haali ts indexing instead?
if (!strcmp(FormatContext->iformat->name, "mpeg") || !strcmp(FormatContext->iformat->name, "mpegts")) { if (HasHaaliMPEG && (!strcmp(FormatContext->iformat->name, "mpeg") || !strcmp(FormatContext->iformat->name, "mpegts"))) {
av_close_input_file(FormatContext); av_close_input_file(FormatContext);
return new FFHaaliIndexer(Filename, 0, ErrorMsg, MsgSize); return new FFHaaliIndexer(Filename, FFMS_SOURCE_HAALIMPEG);
} }
if (!strcmp(FormatContext->iformat->name, "ogg")) { if (HasHaaliOGG && !strcmp(FormatContext->iformat->name, "ogg")) {
av_close_input_file(FormatContext); av_close_input_file(FormatContext);
return new FFHaaliIndexer(Filename, 1, ErrorMsg, MsgSize); return new FFHaaliIndexer(Filename, FFMS_SOURCE_HAALIOGG);
} }
#endif #endif
return new FFLAVFIndexer(Filename, FormatContext, ErrorMsg, MsgSize); return new FFLAVFIndexer(Filename, FormatContext);
} }
FFIndexer::FFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 5) { FFMS_Indexer::FFMS_Indexer(const char *Filename) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 5) {
if (FFIndex::CalculateFileSignature(Filename, &Filesize, Digest, ErrorMsg, MsgSize)) IndexMask = 0;
throw ErrorMsg; DumpMask = 0;
ErrorHandling = FFMS_IEH_CLEAR_TRACK;
IC = NULL;
ICPrivate = NULL;
ANC = NULL;
ANCPrivate = NULL;
FFMS_Index::CalculateFileSignature(Filename, &Filesize, Digest);
} }
FFIndexer::~FFIndexer() { FFMS_Indexer::~FFMS_Indexer() {
} }
bool FFIndexer::WriteAudio(SharedAudioContext &AudioContext, FFIndex *Index, int Track, int DBSize, char *ErrorMsg, unsigned MsgSize) { void FFMS_Indexer::WriteAudio(SharedAudioContext &AudioContext, FFMS_Index *Index, int Track, int DBSize) {
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers. // 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 (DBSize > 0) {
if (!AudioContext.W64Writer) { if (!AudioContext.W64Writer) {
FFAudioProperties AP; FFMS_AudioProperties AP;
FillAP(AP, AudioContext.CodecContext, (*Index)[Track]); FillAP(AP, AudioContext.CodecContext, (*Index)[Track]);
int FNSize = (*ANC)(SourceFile, Track, &AP, NULL, 0, ANCPrivate); int FNSize = (*ANC)(SourceFile, Track, &AP, NULL, 0, ANCPrivate);
std::vector<char> WName(FNSize); std::vector<char> WName(FNSize);
@ -428,13 +436,11 @@ bool FFIndexer::WriteAudio(SharedAudioContext &AudioContext, FFIndex *Index, int
AudioContext.W64Writer = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioContext.CodecContext->sample_fmt), AudioContext.W64Writer = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioContext.CodecContext->sample_fmt),
AudioContext.CodecContext->channels, AudioContext.CodecContext->sample_rate, (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_FLT) || (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_DBL)); AudioContext.CodecContext->channels, AudioContext.CodecContext->sample_rate, (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_FLT) || (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_DBL));
} catch (...) { } catch (...) {
snprintf(ErrorMsg, MsgSize, "Failed to write wave data"); throw FFMS_Exception(FFMS_ERROR_WAVE_WRITER, FFMS_ERROR_FILE_WRITE,
return false; "Failed to write wave data");
} }
} }
AudioContext.W64Writer->WriteData(&DecodingBuffer[0], DBSize); AudioContext.W64Writer->WriteData(&DecodingBuffer[0], DBSize);
} }
return true;
} }

View file

@ -37,8 +37,7 @@
# include "guids.h" # include "guids.h"
#endif #endif
#define INDEXVERSION 28
#define INDEXID 0x53920873
class SharedVideoContext { class SharedVideoContext {
private: private:
@ -69,52 +68,55 @@ struct TFrameInfo {
public: public:
FFMS_FRAMEINFO_COMMON FFMS_FRAMEINFO_COMMON
int64_t SampleStart; int64_t SampleStart;
unsigned int SampleCount;
int64_t FilePos; int64_t FilePos;
unsigned int FrameSize; unsigned int FrameSize;
size_t OriginalPos;
TFrameInfo();
static TFrameInfo VideoFrameInfo(int64_t DTS, int RepeatPict, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0); static TFrameInfo VideoFrameInfo(int64_t DTS, int RepeatPict, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0);
static TFrameInfo AudioFrameInfo(int64_t DTS, int64_t SampleStart, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0); static TFrameInfo AudioFrameInfo(int64_t DTS, int64_t SampleStart, unsigned int SampleCount, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0);
private: private:
TFrameInfo(int64_t DTS, int64_t SampleStart, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize); TFrameInfo(int64_t DTS, int64_t SampleStart, unsigned int SampleCount, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize);
}; };
class FFTrack : public std::vector<TFrameInfo> { class FFMS_Track : public std::vector<TFrameInfo> {
public: public:
FFMS_TrackType TT; FFMS_TrackType TT;
FFTrackTimeBase TB; FFMS_TrackTimeBase TB;
int FindClosestVideoKeyFrame(int Frame); int FindClosestVideoKeyFrame(int Frame);
int FindClosestAudioKeyFrame(int64_t Sample); int FindClosestAudioKeyFrame(int64_t Sample);
int FrameFromDTS(int64_t DTS); int FrameFromDTS(int64_t DTS);
int ClosestFrameFromDTS(int64_t DTS); int ClosestFrameFromDTS(int64_t DTS);
int WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize); void WriteTimecodes(const char *TimecodeFile);
FFTrack(); FFMS_Track();
FFTrack(int64_t Num, int64_t Den, FFMS_TrackType TT); FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT);
}; };
class FFIndex : public std::vector<FFTrack> { class FFMS_Index : public std::vector<FFMS_Track> {
public: public:
static int CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20], char *ErrorMsg, unsigned MsgSize); static void CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20]);
int Decoder; int Decoder;
int64_t Filesize; int64_t Filesize;
uint8_t Digest[20]; uint8_t Digest[20];
void Sort(); void Sort();
int CompareFileSignature(const char *Filename, char *ErrorMsg, unsigned MsgSize); bool CompareFileSignature(const char *Filename);
int WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize); void WriteIndex(const char *IndexFile);
int ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize); void ReadIndex(const char *IndexFile);
FFIndex(); FFMS_Index();
FFIndex(int64_t Filesize, uint8_t Digest[20]); FFMS_Index(int64_t Filesize, uint8_t Digest[20]);
}; };
class FFIndexer { class FFMS_Indexer {
protected: protected:
int IndexMask; int IndexMask;
int DumpMask; int DumpMask;
bool IgnoreDecodeErrors; int ErrorHandling;
TIndexCallback IC; TIndexCallback IC;
void *ICPrivate; void *ICPrivate;
TAudioNameCallback ANC; TAudioNameCallback ANC;
@ -125,43 +127,43 @@ protected:
int64_t Filesize; int64_t Filesize;
uint8_t Digest[20]; uint8_t Digest[20];
bool WriteAudio(SharedAudioContext &AudioContext, FFIndex *Index, int Track, int DBSize, char *ErrorMsg, unsigned MsgSize); void WriteAudio(SharedAudioContext &AudioContext, FFMS_Index *Index, int Track, int DBSize);
public: public:
static FFIndexer *CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize); static FFMS_Indexer *CreateIndexer(const char *Filename);
FFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize); FFMS_Indexer(const char *Filename);
virtual ~FFIndexer(); virtual ~FFMS_Indexer();
void SetIndexMask(int IndexMask); void SetIndexMask(int IndexMask);
void SetDumpMask(int DumpMask); void SetDumpMask(int DumpMask);
void SetIgnoreDecodeErrors(bool IgnoreDecodeErrors); void SetErrorHandling(int ErrorHandling);
void SetProgressCallback(TIndexCallback IC, void *ICPrivate); void SetProgressCallback(TIndexCallback IC, void *ICPrivate);
void SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate); void SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate);
virtual FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize) = 0; virtual FFMS_Index *DoIndexing() = 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;
}; };
class FFLAVFIndexer : public FFIndexer { class FFLAVFIndexer : public FFMS_Indexer {
private: private:
AVFormatContext *FormatContext; AVFormatContext *FormatContext;
public: public:
FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize); FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext);
~FFLAVFIndexer(); ~FFLAVFIndexer();
FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize); FFMS_Index *DoIndexing();
int GetNumberOfTracks(); int GetNumberOfTracks();
FFMS_TrackType GetTrackType(int Track); FFMS_TrackType GetTrackType(int Track);
const char *GetTrackCodec(int Track); const char *GetTrackCodec(int Track);
}; };
class FFMatroskaIndexer : public FFIndexer { class FFMatroskaIndexer : public FFMS_Indexer {
private: private:
MatroskaFile *MF; MatroskaFile *MF;
MatroskaReaderContext MC; MatroskaReaderContext MC;
AVCodec *Codec[32]; AVCodec *Codec[32];
public: public:
FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize); FFMatroskaIndexer(const char *Filename);
~FFMatroskaIndexer(); ~FFMatroskaIndexer();
FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize); FFMS_Index *DoIndexing();
int GetNumberOfTracks(); int GetNumberOfTracks();
FFMS_TrackType GetTrackType(int Track); FFMS_TrackType GetTrackType(int Track);
const char *GetTrackCodec(int Track); const char *GetTrackCodec(int Track);
@ -169,7 +171,7 @@ public:
#ifdef HAALISOURCE #ifdef HAALISOURCE
class FFHaaliIndexer : public FFIndexer { class FFHaaliIndexer : public FFMS_Indexer {
private: private:
int SourceMode; int SourceMode;
CComPtr<IMMContainer> pMMC; CComPtr<IMMContainer> pMMC;
@ -181,8 +183,8 @@ private:
CComQIPtr<IPropertyBag> PropertyBags[32]; CComQIPtr<IPropertyBag> PropertyBags[32];
int64_t Duration; int64_t Duration;
public: public:
FFHaaliIndexer(const char *Filename, int SourceMode, char *ErrorMsg, unsigned MsgSize); FFHaaliIndexer(const char *Filename, enum FFMS_Sources SourceMode);
FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize); FFMS_Index *DoIndexing();
int GetNumberOfTracks(); int GetNumberOfTracks();
FFMS_TrackType GetTrackType(int Track); FFMS_TrackType GetTrackType(int Track);
const char *GetTrackCodec(int Track); const char *GetTrackCodec(int Track);

View file

@ -18,7 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#include "ffaudiosource.h" #include "audiosource.h"
void FFLAVFAudio::Free(bool CloseCodec) { void FFLAVFAudio::Free(bool CloseCodec) {
if (CloseCodec) if (CloseCodec)
@ -27,75 +27,53 @@ void FFLAVFAudio::Free(bool CloseCodec) {
av_close_input_file(FormatContext); av_close_input_file(FormatContext);
} }
FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFMS_Index *Index)
char *ErrorMsg, unsigned MsgSize) : Res(FFSourceResources<FFMS_AudioSource>(this)), FFMS_AudioSource(SourceFile, Index, Track){
: FFAudio(SourceFile, Index, ErrorMsg, MsgSize){
FormatContext = NULL; FormatContext = NULL;
AVCodec *Codec = NULL; AVCodec *Codec = NULL;
AudioTrack = Track; AudioTrack = Track;
Frames = (*Index)[AudioTrack]; Frames = (*Index)[AudioTrack];
if (Frames.size() == 0) { LAVFOpenFile(SourceFile, FormatContext);
Free(false);
snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?");
throw ErrorMsg;
}
if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) {
snprintf(ErrorMsg, MsgSize, "Couldn't open '%s'", SourceFile);
throw ErrorMsg;
}
if (av_find_stream_info(FormatContext) < 0) {
Free(false);
snprintf(ErrorMsg, MsgSize, "Couldn't find stream information");
throw ErrorMsg;
}
CodecContext = FormatContext->streams[AudioTrack]->codec; CodecContext = FormatContext->streams[AudioTrack]->codec;
Codec = avcodec_find_decoder(CodecContext->codec_id); Codec = avcodec_find_decoder(CodecContext->codec_id);
if (Codec == NULL) { if (Codec == NULL)
Free(false); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Audio codec not found"); "Audio codec not found");
throw ErrorMsg;
}
if (avcodec_open(CodecContext, Codec) < 0) { if (avcodec_open(CodecContext, Codec) < 0)
Free(false); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Could not open audio codec"); "Could not open audio codec");
throw ErrorMsg;
} Res.CloseCodec(true);
// 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(&Dummy, ErrorMsg, MsgSize) < 0) { DecodeNextAudioBlock(&Dummy);
Free(true);
throw ErrorMsg;
}
if (av_seek_frame(FormatContext, AudioTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0) if (av_seek_frame(FormatContext, AudioTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0)
av_seek_frame(FormatContext, AudioTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY); av_seek_frame(FormatContext, AudioTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY);
avcodec_flush_buffers(CodecContext); avcodec_flush_buffers(CodecContext);
FillAP(AP, CodecContext, Frames); FillAP(AP, CodecContext, Frames);
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) { if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0)
Free(true); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio"); "Codec returned zero size audio");
throw ErrorMsg;
}
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50); AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
} }
int FFLAVFAudio::DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned MsgSize) { void FFLAVFAudio::DecodeNextAudioBlock(int64_t *Count) {
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[0]; uint8_t *Buf = &DecodingBuffer[0];
AVPacket Packet, TempPacket; AVPacket Packet, TempPacket;
InitNullPacket(&Packet); InitNullPacket(Packet);
InitNullPacket(&TempPacket); InitNullPacket(TempPacket);
while (av_read_frame(FormatContext, &Packet) >= 0) { while (av_read_frame(FormatContext, &Packet) >= 0) {
if (Packet.stream_index == AudioTrack) { if (Packet.stream_index == AudioTrack) {
@ -127,11 +105,12 @@ int FFLAVFAudio::DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned M
av_free_packet(&Packet); av_free_packet(&Packet);
} }
Done: Done:;
return Ret;
} }
int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) { void FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) {
GetAudioCheck(Start, Count);
const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
memset(Buf, 0, static_cast<size_t>(SizeConst * Count)); memset(Buf, 0, static_cast<size_t>(SizeConst * Count));
@ -142,7 +121,7 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs
int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf); int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
// Was everything in the cache? // Was everything in the cache?
if (CacheEnd == Start + Count) if (CacheEnd == Start + Count)
return 0; return;
size_t CurrentAudioBlock; size_t CurrentAudioBlock;
// Is seeking required to decode the requested samples? // Is seeking required to decode the requested samples?
@ -158,7 +137,7 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs
avcodec_flush_buffers(CodecContext); avcodec_flush_buffers(CodecContext);
AVPacket Packet; AVPacket Packet;
InitNullPacket(&Packet); InitNullPacket(Packet);
// Establish where we actually are // Establish where we actually are
// Trigger on packet dts difference since groups can otherwise be indistinguishable // Trigger on packet dts difference since groups can otherwise be indistinguishable
@ -189,11 +168,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(&DecodeCount, ErrorMsg, MsgSize); DecodeNextAudioBlock(&DecodeCount);
if (Ret < 0) {
// FIXME
//Env->ThrowError("Bleh, bad audio decoding");
}
// Cache the block if enough blocks before it have been decoded to avoid garbage // Cache the block if enough blocks before it have been decoded to avoid garbage
if (PreDecBlocks == 0) { if (PreDecBlocks == 0) {
@ -207,10 +182,4 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs
if (CurrentAudioBlock < Frames.size()) if (CurrentAudioBlock < Frames.size())
CurrentSample = Frames[CurrentAudioBlock].SampleStart; CurrentSample = Frames[CurrentAudioBlock].SampleStart;
} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < Frames.size()); } while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < Frames.size());
return 0;
}
FFLAVFAudio::~FFLAVFAudio() {
Free(true);
} }

View file

@ -22,14 +22,14 @@
FFLAVFIndexer::FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize) : FFIndexer(Filename, ErrorMsg, MsgSize) { FFLAVFIndexer::FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext) : FFMS_Indexer(Filename) {
SourceFile = Filename; SourceFile = Filename;
this->FormatContext = FormatContext; this->FormatContext = FormatContext;
if (av_find_stream_info(FormatContext) < 0) { if (av_find_stream_info(FormatContext) < 0) {
av_close_input_file(FormatContext); av_close_input_file(FormatContext);
snprintf(ErrorMsg, MsgSize, "Couldn't find stream information"); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
throw ErrorMsg; "Couldn't find stream information");
} }
} }
@ -37,15 +37,15 @@ FFLAVFIndexer::~FFLAVFIndexer() {
av_close_input_file(FormatContext); av_close_input_file(FormatContext);
} }
FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { FFMS_Index *FFLAVFIndexer::DoIndexing() {
std::vector<SharedAudioContext> AudioContexts(FormatContext->nb_streams, SharedAudioContext(false)); std::vector<SharedAudioContext> AudioContexts(FormatContext->nb_streams, SharedAudioContext(false));
std::vector<SharedVideoContext> VideoContexts(FormatContext->nb_streams, SharedVideoContext(false)); std::vector<SharedVideoContext> VideoContexts(FormatContext->nb_streams, SharedVideoContext(false));
std::auto_ptr<FFIndex> TrackIndices(new FFIndex(Filesize, Digest)); std::auto_ptr<FFMS_Index> TrackIndices(new FFMS_Index(Filesize, Digest));
TrackIndices->Decoder = 0; TrackIndices->Decoder = FFMS_SOURCE_LAVF;
for (unsigned int i = 0; i < FormatContext->nb_streams; i++) { for (unsigned int i = 0; i < FormatContext->nb_streams; i++) {
TrackIndices->push_back(FFTrack((int64_t)FormatContext->streams[i]->time_base.num * 1000, TrackIndices->push_back(FFMS_Track((int64_t)FormatContext->streams[i]->time_base.num * 1000,
FormatContext->streams[i]->time_base.den, FormatContext->streams[i]->time_base.den,
static_cast<FFMS_TrackType>(FormatContext->streams[i]->codec->codec_type))); static_cast<FFMS_TrackType>(FormatContext->streams[i]->codec->codec_type)));
@ -53,15 +53,13 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
(VideoContexts[i].Parser = av_parser_init(FormatContext->streams[i]->codec->codec_id))) { (VideoContexts[i].Parser = av_parser_init(FormatContext->streams[i]->codec->codec_id))) {
AVCodec *VideoCodec = avcodec_find_decoder(FormatContext->streams[i]->codec->codec_id); AVCodec *VideoCodec = avcodec_find_decoder(FormatContext->streams[i]->codec->codec_id);
if (VideoCodec == NULL) { if (VideoCodec == NULL)
snprintf(ErrorMsg, MsgSize, "Vudio codec not found"); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
return NULL; "Video codec not found");
}
if (avcodec_open(FormatContext->streams[i]->codec, VideoCodec) < 0) { if (avcodec_open(FormatContext->streams[i]->codec, VideoCodec) < 0)
snprintf(ErrorMsg, MsgSize, "Could not open video codec"); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
return NULL; "Could not open video codec");
}
VideoContexts[i].CodecContext = FormatContext->streams[i]->codec; VideoContexts[i].CodecContext = FormatContext->streams[i]->codec;
VideoContexts[i].Parser->flags = PARSER_FLAG_COMPLETE_FRAMES; VideoContexts[i].Parser->flags = PARSER_FLAG_COMPLETE_FRAMES;
@ -71,15 +69,13 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
AVCodecContext *AudioCodecContext = FormatContext->streams[i]->codec; AVCodecContext *AudioCodecContext = FormatContext->streams[i]->codec;
AVCodec *AudioCodec = avcodec_find_decoder(AudioCodecContext->codec_id); AVCodec *AudioCodec = avcodec_find_decoder(AudioCodecContext->codec_id);
if (AudioCodec == NULL) { if (AudioCodec == NULL)
snprintf(ErrorMsg, MsgSize, "Audio codec not found"); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
return NULL; "Audio codec not found");
}
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) { if (avcodec_open(AudioCodecContext, AudioCodec) < 0)
snprintf(ErrorMsg, MsgSize, "Could not open audio codec"); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
return NULL; "Could not open audio codec");
}
AudioContexts[i].CodecContext = AudioCodecContext; AudioContexts[i].CodecContext = AudioCodecContext;
} else { } else {
@ -90,15 +86,14 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
// //
AVPacket Packet, TempPacket; AVPacket Packet, TempPacket;
InitNullPacket(&Packet); InitNullPacket(Packet);
InitNullPacket(&TempPacket); InitNullPacket(TempPacket);
while (av_read_frame(FormatContext, &Packet) >= 0) { while (av_read_frame(FormatContext, &Packet) >= 0) {
// Update progress // Update progress
if (IC) { if (IC) {
if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, ICPrivate)) { if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, ICPrivate))
snprintf(ErrorMsg, MsgSize, "Cancelled by user"); throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
return NULL; "Cancelled by user");
}
} }
// Only create index entries for video for now to save space // Only create index entries for video for now to save space
@ -114,7 +109,7 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
(*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::VideoFrameInfo(Packet.dts, RepeatPict, (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0)); (*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::VideoFrameInfo(Packet.dts, RepeatPict, (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0));
} else if (FormatContext->streams[Packet.stream_index]->codec->codec_type == CODEC_TYPE_AUDIO && (IndexMask & (1 << Packet.stream_index))) { } else if (FormatContext->streams[Packet.stream_index]->codec->codec_type == CODEC_TYPE_AUDIO && (IndexMask & (1 << Packet.stream_index))) {
(*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::AudioFrameInfo(Packet.dts, AudioContexts[Packet.stream_index].CurrentSample, (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0)); int64_t StartSample = AudioContexts[Packet.stream_index].CurrentSample;
AVCodecContext *AudioCodecContext = FormatContext->streams[Packet.stream_index]->codec; AVCodecContext *AudioCodecContext = FormatContext->streams[Packet.stream_index]->codec;
TempPacket.data = Packet.data; TempPacket.data = Packet.data;
TempPacket.size = Packet.size; TempPacket.size = Packet.size;
@ -124,13 +119,18 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket); int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
if (Ret < 0) { if (Ret < 0) {
if (IgnoreDecodeErrors) { if (ErrorHandling == FFMS_IEH_ABORT) {
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
"Audio decoding error");
} else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) {
(*TrackIndices)[Packet.stream_index].clear(); (*TrackIndices)[Packet.stream_index].clear();
IndexMask &= ~(1 << Packet.stream_index); IndexMask &= ~(1 << Packet.stream_index);
break; break;
} else { } else if (ErrorHandling == FFMS_IEH_STOP_TRACK) {
snprintf(ErrorMsg, MsgSize, "Audio decoding error"); IndexMask &= ~(1 << Packet.stream_index);
return NULL; break;
} else if (ErrorHandling == FFMS_IEH_IGNORE) {
break;
} }
} }
@ -143,8 +143,11 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
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 (DumpMask & (1 << Packet.stream_index)) if (DumpMask & (1 << Packet.stream_index))
WriteAudio(AudioContexts[Packet.stream_index], TrackIndices.get(), Packet.stream_index, dbsize, ErrorMsg, MsgSize); WriteAudio(AudioContexts[Packet.stream_index], TrackIndices.get(), Packet.stream_index, dbsize);
} }
(*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::AudioFrameInfo(Packet.dts, StartSample,
static_cast<unsigned int>(AudioContexts[Packet.stream_index].CurrentSample - StartSample), (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0));
} }
av_free_packet(&Packet); av_free_packet(&Packet);
@ -163,5 +166,5 @@ FFMS_TrackType FFLAVFIndexer::GetTrackType(int Track) {
} }
const char *FFLAVFIndexer::GetTrackCodec(int Track) { const char *FFLAVFIndexer::GetTrackCodec(int Track) {
return (avcodec_find_decoder(FormatContext->streams[Track]->codec->codec_id))->long_name; return (avcodec_find_decoder(FormatContext->streams[Track]->codec->codec_id))->name;
} }

View file

@ -18,7 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#include "ffvideosource.h" #include "videosource.h"
@ -28,9 +28,9 @@ void FFLAVFVideo::Free(bool CloseCodec) {
av_close_input_file(FormatContext); av_close_input_file(FormatContext);
} }
FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index, FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index,
const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize) int Threads, int SeekMode)
: FFVideo(SourceFile, Index, ErrorMsg, MsgSize) { : Res(FFSourceResources<FFMS_VideoSource>(this)), FFMS_VideoSource(SourceFile, Index, Track) {
FormatContext = NULL; FormatContext = NULL;
AVCodec *Codec = NULL; AVCodec *Codec = NULL;
@ -38,68 +38,45 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index,
VideoTrack = Track; VideoTrack = Track;
Frames = (*Index)[VideoTrack]; Frames = (*Index)[VideoTrack];
if (Frames.size() == 0) { LAVFOpenFile(SourceFile, FormatContext);
snprintf(ErrorMsg, MsgSize, "Video track contains no frames");
throw ErrorMsg;
}
if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) { if (SeekMode >= 0 && av_seek_frame(FormatContext, VideoTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0)
snprintf(ErrorMsg, MsgSize, "Couldn't open '%s'", SourceFile); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
throw ErrorMsg; "Video track is unseekable");
}
if (av_find_stream_info(FormatContext) < 0) {
Free(false);
snprintf(ErrorMsg, MsgSize, "Couldn't find stream information");
throw ErrorMsg;
}
if (SeekMode >= 0 && av_seek_frame(FormatContext, VideoTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0) {
Free(false);
snprintf(ErrorMsg, MsgSize, "Video track is unseekable");
throw ErrorMsg;
}
CodecContext = FormatContext->streams[VideoTrack]->codec; CodecContext = FormatContext->streams[VideoTrack]->codec;
CodecContext->thread_count = Threads; CodecContext->thread_count = Threads;
Codec = avcodec_find_decoder(CodecContext->codec_id); Codec = avcodec_find_decoder(CodecContext->codec_id);
if (Codec == NULL) { if (Codec == NULL)
Free(false); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Video codec not found"); "Video codec not found");
throw ErrorMsg;
}
if (avcodec_open(CodecContext, Codec) < 0) { if (avcodec_open(CodecContext, Codec) < 0)
Free(false); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Could not open video codec"); "Could not open video codec");
throw ErrorMsg;
} Res.CloseCodec(true);
// 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(&Dummy, ErrorMsg, MsgSize)) { DecodeNextFrame(&Dummy);
Free(true);
throw ErrorMsg;
}
//VP.image_type = VideoInfo::IT_TFF; //VP.image_type = VideoInfo::IT_TFF;
VP.Width = CodecContext->width;
VP.Height = CodecContext->height;
VP.FPSDenominator = FormatContext->streams[VideoTrack]->time_base.num; VP.FPSDenominator = FormatContext->streams[VideoTrack]->time_base.num;
VP.FPSNumerator = FormatContext->streams[VideoTrack]->time_base.den; VP.FPSNumerator = FormatContext->streams[VideoTrack]->time_base.den;
VP.RFFDenominator = CodecContext->time_base.num; VP.RFFDenominator = CodecContext->time_base.num;
VP.RFFNumerator = CodecContext->time_base.den; VP.RFFNumerator = CodecContext->time_base.den;
VP.NumFrames = Frames.size(); VP.NumFrames = Frames.size();
VP.VPixelFormat = CodecContext->pix_fmt; VP.TopFieldFirst = DecodeFrame->top_field_first;
VP.ColorSpace = CodecContext->colorspace;
VP.ColorRange = CodecContext->color_range;
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
if (VP.Width <= 0 || VP.Height <= 0) { if (CodecContext->width <= 0 || CodecContext->height <= 0)
Free(true); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Codec returned zero size video"); "Codec returned zero size video");
throw ErrorMsg;
}
// sanity check framerate // sanity check framerate
if (VP.FPSDenominator > VP.FPSNumerator || VP.FPSDenominator <= 0 || VP.FPSNumerator <= 0) { if (VP.FPSDenominator > VP.FPSNumerator || VP.FPSDenominator <= 0 || VP.FPSNumerator <= 0) {
@ -107,11 +84,6 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index,
VP.FPSNumerator = 30; VP.FPSNumerator = 30;
} }
if (InitPP(PP, ErrorMsg, MsgSize)) {
Free(true);
throw ErrorMsg;
}
// Adjust framerate to match the duration of the first frame // Adjust framerate to match the duration of the first frame
if (Frames.size() >= 2) { if (Frames.size() >= 2) {
unsigned int DTSDiff = (unsigned int)FFMAX(Frames[1].DTS - Frames[0].DTS, 1); unsigned int DTSDiff = (unsigned int)FFMAX(Frames[1].DTS - Frames[0].DTS, 1);
@ -120,23 +92,16 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index,
// Cannot "output" to PPFrame without doing all other initialization // Cannot "output" to PPFrame without doing all other initialization
// This is the additional mess required for seekmode=-1 to work in a reasonable way // This is the additional mess required for seekmode=-1 to work in a reasonable way
if (!OutputFrame(DecodeFrame, ErrorMsg, MsgSize)) { OutputFrame(DecodeFrame);
Free(true);
throw ErrorMsg;
}
// Set AR variables // Set AR variables
VP.SARNum = CodecContext->sample_aspect_ratio.num; VP.SARNum = CodecContext->sample_aspect_ratio.num;
VP.SARDen = CodecContext->sample_aspect_ratio.den; VP.SARDen = CodecContext->sample_aspect_ratio.den;
} }
FFLAVFVideo::~FFLAVFVideo() { void FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime) {
Free(true);
}
int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned MsgSize) {
AVPacket Packet; AVPacket Packet;
InitNullPacket(&Packet); InitNullPacket(Packet);
int FrameFinished = 0; int FrameFinished = 0;
*AStartTime = -1; *AStartTime = -1;
@ -145,6 +110,11 @@ int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned M
if (*AStartTime < 0) if (*AStartTime < 0)
*AStartTime = Packet.dts; *AStartTime = Packet.dts;
if (CodecContext->codec_id == CODEC_ID_MPEG4 && IsNVOP(Packet)) {
av_free_packet(&Packet);
goto Done;
}
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
} }
@ -157,23 +127,22 @@ int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned M
// Flush the last frames // Flush the last frames
if (CodecContext->has_b_frames) { if (CodecContext->has_b_frames) {
AVPacket NullPacket; AVPacket NullPacket;
InitNullPacket(&NullPacket); InitNullPacket(NullPacket);
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
} }
if (!FrameFinished) if (!FrameFinished)
goto Error; goto Error;
// Ignore errors for now
Error: Error:
Done: Done:;
return 0;
} }
FFAVFrame *FFLAVFVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { FFMS_Frame *FFLAVFVideo::GetFrame(int n) {
// PPFrame always holds frame LastFrameNum even if no PP is applied GetFrameCheck(n);
if (LastFrameNum == n) if (LastFrameNum == n)
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize); return &LocalFrame;
bool HasSeeked = false; bool HasSeeked = false;
int SeekOffset = 0; int SeekOffset = 0;
@ -200,14 +169,13 @@ ReSeek:
} }
} }
} else if (n < CurrentFrame) { } else if (n < CurrentFrame) {
snprintf(ErrorMsg, MsgSize, "Non-linear access attempted"); throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_INVALID_ARGUMENT,
return NULL; "Non-linear access attempted");
} }
do { do {
int64_t StartTime; int64_t StartTime;
if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize)) DecodeNextFrame(&StartTime);
return NULL;
if (HasSeeked) { if (HasSeeked) {
HasSeeked = false; HasSeeked = false;
@ -217,10 +185,10 @@ ReSeek:
switch (SeekMode) { switch (SeekMode) {
case 1: case 1:
// No idea where we are so go back a bit further // No idea where we are so go back a bit further
if (ClosestKF + SeekOffset == 0) { if (ClosestKF + SeekOffset == 0)
snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file\n"); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
return NULL; "Frame accurate seeking is not possible in this file");
}
SeekOffset -= FFMIN(10, ClosestKF + SeekOffset); SeekOffset -= FFMIN(10, ClosestKF + SeekOffset);
goto ReSeek; goto ReSeek;
@ -229,8 +197,8 @@ ReSeek:
CurrentFrame = Frames.ClosestFrameFromDTS(StartTime); CurrentFrame = Frames.ClosestFrameFromDTS(StartTime);
break; break;
default: default:
snprintf(ErrorMsg, MsgSize, "Failed assertion"); throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_UNKNOWN,
return NULL; "Failed assertion");
} }
} }
} }
@ -239,5 +207,5 @@ ReSeek:
} while (CurrentFrame <= n); } while (CurrentFrame <= n);
LastFrameNum = n; LastFrameNum = n;
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize); return OutputFrame(DecodeFrame);
} }

View file

@ -18,7 +18,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#include "ffaudiosource.h" #include "audiosource.h"
void FFMatroskaAudio::Free(bool CloseCodec) { void FFMatroskaAudio::Free(bool CloseCodec) {
if (CS) if (CS)
@ -32,89 +32,74 @@ void FFMatroskaAudio::Free(bool CloseCodec) {
av_freep(&CodecContext); av_freep(&CodecContext);
} }
FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index *Index)
FFIndex *Index, char *ErrorMsg, unsigned MsgSize) : Res(FFSourceResources<FFMS_AudioSource>(this)), FFMS_AudioSource(SourceFile, Index, Track) {
: FFAudio(SourceFile, Index, ErrorMsg, MsgSize) {
CodecContext = NULL; CodecContext = NULL;
AVCodec *Codec = NULL; AVCodec *Codec = NULL;
TrackInfo *TI = NULL; TrackInfo *TI = NULL;
CS = NULL; CS = NULL;
PacketNumber = 0;
Frames = (*Index)[Track]; Frames = (*Index)[Track];
if (Frames.size() == 0) {
Free(false);
snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?");
throw ErrorMsg;
}
MC.ST.fp = ffms_fopen(SourceFile, "rb"); MC.ST.fp = ffms_fopen(SourceFile, "rb");
if (MC.ST.fp == NULL) { if (MC.ST.fp == NULL)
snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno)); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
throw ErrorMsg; boost::format("Can't open '%1%': %2%") % SourceFile % strerror(errno));
}
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE); setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage)); MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
if (MF == NULL) { if (MF == NULL) {
fclose(MC.ST.fp); fclose(MC.ST.fp);
snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
throw ErrorMsg; boost::format("Can't parse Matroska file: %1%") % ErrorMessage);
} }
mkv_SetTrackMask(MF, ~(1 << Track));
TI = mkv_GetTrackInfo(MF, Track); TI = mkv_GetTrackInfo(MF, Track);
if (TI->CompEnabled) { if (TI->CompEnabled) {
CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage)); CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage));
if (CS == NULL) { if (CS == NULL) {
Free(false); Free(false);
snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
throw ErrorMsg; boost::format("Can't create decompressor: %1%") % ErrorMessage);
} }
} }
CodecContext = avcodec_alloc_context(); CodecContext = avcodec_alloc_context();
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate)); Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
if (Codec == NULL) { if (Codec == NULL)
Free(false); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Video codec not found"); "Audio codec not found");
throw ErrorMsg;
}
InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext); InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext);
if (avcodec_open(CodecContext, Codec) < 0) { if (avcodec_open(CodecContext, Codec) < 0)
Free(false); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Could not open video codec"); "Could not open audio codec");
throw ErrorMsg;
} Res.CloseCodec(true);
// 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(&Dummy, 0, ErrorMsg, MsgSize) < 0) { DecodeNextAudioBlock(&Dummy);
Free(true);
throw ErrorMsg;
}
avcodec_flush_buffers(CodecContext); avcodec_flush_buffers(CodecContext);
FillAP(AP, CodecContext, Frames); FillAP(AP, CodecContext, Frames);
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) { if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0)
Free(true); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio"); "Codec returned zero size audio");
throw ErrorMsg;
}
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50); AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
} }
FFMatroskaAudio::~FFMatroskaAudio() { void FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) {
Free(true); GetAudioCheck(Start, Count);
}
int FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) {
const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
memset(Buf, 0, static_cast<size_t>(SizeConst * Count)); memset(Buf, 0, static_cast<size_t>(SizeConst * Count));
@ -125,58 +110,50 @@ int FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *Err
int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf); int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
// Was everything in the cache? // Was everything in the cache?
if (CacheEnd == Start + Count) if (CacheEnd == Start + Count)
return 0; return;
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) {
PreDecBlocks = 15; PreDecBlocks = 15;
CurrentAudioBlock = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks, (int64_t)0); PacketNumber = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks, (int64_t)0);;
avcodec_flush_buffers(CodecContext); avcodec_flush_buffers(CodecContext);
} else {
CurrentAudioBlock = Frames.FindClosestAudioKeyFrame(CurrentSample);
} }
int64_t DecodeCount; int64_t DecodeCount;
do { do {
int Ret = DecodeNextAudioBlock(&DecodeCount, CurrentAudioBlock, ErrorMsg, MsgSize); const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos];
if (Ret < 0) { DecodeNextAudioBlock(&DecodeCount);
// FIXME
//Env->ThrowError("Bleh, bad audio decoding");
}
// Cache the block if enough blocks before it have been decoded to avoid garbage // Cache the block if enough blocks before it have been decoded to avoid garbage
if (PreDecBlocks == 0) { if (PreDecBlocks == 0) {
AudioCache.CacheBlock(Frames[CurrentAudioBlock].SampleStart, DecodeCount, &DecodingBuffer[0]); AudioCache.CacheBlock(FI.SampleStart, DecodeCount, &DecodingBuffer[0]);
CacheEnd = AudioCache.FillRequest(CacheEnd, Start + Count - CacheEnd, DstBuf + (CacheEnd - Start) * SizeConst); CacheEnd = AudioCache.FillRequest(CacheEnd, Start + Count - CacheEnd, DstBuf + (CacheEnd - Start) * SizeConst);
} else { } else {
PreDecBlocks--; PreDecBlocks--;
} }
CurrentAudioBlock++; } while (Start + Count - CacheEnd > 0 && PacketNumber < Frames.size());
if (CurrentAudioBlock < static_cast<int>(Frames.size()))
CurrentSample = Frames[CurrentAudioBlock].SampleStart;
} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < static_cast<int>(Frames.size()));
return 0;
} }
int FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count, int AudioBlock, char *ErrorMsg, unsigned MsgSize) { void FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count) {
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[0]; uint8_t *Buf = &DecodingBuffer[0];
AVPacket TempPacket; AVPacket TempPacket;
InitNullPacket(&TempPacket); InitNullPacket(TempPacket);
unsigned int FrameSize = Frames[AudioBlock].FrameSize; const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos];
if (ReadFrame(Frames[AudioBlock].FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize)) unsigned int FrameSize = FI.FrameSize;
return 1; CurrentSample = FI.SampleStart + FI.SampleCount;
PacketNumber++;
ReadFrame(FI.FilePos, FrameSize, CS, MC);
TempPacket.data = MC.Buffer; TempPacket.data = MC.Buffer;
TempPacket.size = FrameSize; TempPacket.size = FrameSize;
if (Frames[AudioBlock].KeyFrame) if (FI.KeyFrame)
TempPacket.flags = AV_PKT_FLAG_KEY; TempPacket.flags = AV_PKT_FLAG_KEY;
else else
TempPacket.flags = 0; TempPacket.flags = 0;
@ -197,6 +174,5 @@ int FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count, int AudioBlock, char *
} }
} }
Done: Done:;
return 0;
} }

View file

@ -23,25 +23,26 @@
FFMatroskaIndexer::FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) : FFIndexer(Filename, ErrorMsg, MsgSize) { FFMatroskaIndexer::FFMatroskaIndexer(const char *Filename) : FFMS_Indexer(Filename) {
memset(Codec, 0, sizeof(Codec));
SourceFile = Filename; SourceFile = Filename;
char ErrorMessage[256]; char ErrorMessage[256];
for (int i = 0; i < 32; i++) {
Codec[i] = NULL;
}
InitStdIoStream(&MC.ST); InitStdIoStream(&MC.ST);
MC.ST.fp = ffms_fopen(SourceFile, "rb"); MC.ST.fp = ffms_fopen(SourceFile, "rb");
if (MC.ST.fp == NULL) { if (MC.ST.fp == NULL)
snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno)); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
throw ErrorMsg; boost::format("Can't open '%1%': %2%") % SourceFile % strerror(errno));
}
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE); setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage)); MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
if (MF == NULL) { if (MF == NULL) {
fclose(MC.ST.fp); fclose(MC.ST.fp);
snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
throw ErrorMsg; boost::format("Can't parse Matroska file: %1%") % ErrorMessage);
} }
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) { for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) {
@ -55,17 +56,17 @@ FFMatroskaIndexer::~FFMatroskaIndexer() {
fclose(MC.ST.fp); fclose(MC.ST.fp);
} }
FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) { FFMS_Index *FFMatroskaIndexer::DoIndexing() {
char ErrorMessage[256]; char ErrorMessage[256];
std::vector<SharedAudioContext> AudioContexts(mkv_GetNumTracks(MF), SharedAudioContext(true)); std::vector<SharedAudioContext> AudioContexts(mkv_GetNumTracks(MF), SharedAudioContext(true));
std::vector<SharedVideoContext> VideoContexts(mkv_GetNumTracks(MF), SharedVideoContext(true)); std::vector<SharedVideoContext> VideoContexts(mkv_GetNumTracks(MF), SharedVideoContext(true));
std::auto_ptr<FFIndex> TrackIndices(new FFIndex(Filesize, Digest)); std::auto_ptr<FFMS_Index> TrackIndices(new FFMS_Index(Filesize, Digest));
TrackIndices->Decoder = 1; TrackIndices->Decoder = FFMS_SOURCE_MATROSKA;
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);
TrackIndices->push_back(FFTrack(mkv_TruncFloat(mkv_GetTrackInfo(MF, i)->TimecodeScale), 1000000, HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, i)->Type))); TrackIndices->push_back(FFMS_Track(mkv_TruncFloat(mkv_GetTrackInfo(MF, i)->TimecodeScale), 1000000, HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, i)->Type)));
if (HaaliTrackTypeToFFTrackType(TI->Type) == FFMS_TYPE_VIDEO && Codec[i] && (VideoContexts[i].Parser = av_parser_init(Codec[i]->id))) { if (HaaliTrackTypeToFFTrackType(TI->Type) == FFMS_TYPE_VIDEO && Codec[i] && (VideoContexts[i].Parser = av_parser_init(Codec[i]->id))) {
@ -75,16 +76,15 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
if (avcodec_open(CodecContext, Codec[i]) < 0) { if (avcodec_open(CodecContext, Codec[i]) < 0) {
av_freep(&CodecContext); av_freep(&CodecContext);
snprintf(ErrorMsg, MsgSize, "Could not open video codec"); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
return NULL; "Could not open video codec");
} }
if (TI->CompEnabled) { if (TI->CompEnabled) {
VideoContexts[i].CS = cs_Create(MF, i, ErrorMessage, sizeof(ErrorMessage)); VideoContexts[i].CS = cs_Create(MF, i, ErrorMessage, sizeof(ErrorMessage));
if (VideoContexts[i].CS == NULL) { if (VideoContexts[i].CS == NULL)
snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
return NULL; boost::format("Can't create decompressor: %1%") % ErrorMessage);
}
} }
VideoContexts[i].CodecContext = CodecContext; VideoContexts[i].CodecContext = CodecContext;
@ -102,8 +102,8 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
if (AudioContexts[i].CS == NULL) { if (AudioContexts[i].CS == NULL) {
av_freep(&AudioCodecContext); av_freep(&AudioCodecContext);
AudioContexts[i].CodecContext = NULL; AudioContexts[i].CodecContext = NULL;
snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
return NULL; boost::format("Can't create decompressor: %1%") % ErrorMessage);
} }
} }
@ -111,15 +111,15 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
if (AudioCodec == NULL) { if (AudioCodec == NULL) {
av_freep(&AudioCodecContext); av_freep(&AudioCodecContext);
AudioContexts[i].CodecContext = NULL; AudioContexts[i].CodecContext = NULL;
snprintf(ErrorMsg, MsgSize, "Audio codec not found"); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
return NULL; "Audio codec not found");
} }
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) { if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
av_freep(&AudioCodecContext); av_freep(&AudioCodecContext);
AudioContexts[i].CodecContext = NULL; AudioContexts[i].CodecContext = NULL;
snprintf(ErrorMsg, MsgSize, "Could not open audio codec"); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
return NULL; "Could not open audio codec");
} }
} else { } else {
IndexMask &= ~(1 << i); IndexMask &= ~(1 << i);
@ -131,15 +131,14 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
ulonglong StartTime, EndTime, FilePos; ulonglong StartTime, EndTime, FilePos;
unsigned int Track, FrameFlags, FrameSize; unsigned int Track, FrameFlags, FrameSize;
AVPacket TempPacket; AVPacket TempPacket;
InitNullPacket(&TempPacket); InitNullPacket(TempPacket);
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)(ftello(MC.ST.fp), Filesize, ICPrivate)) { if ((*IC)(ftello(MC.ST.fp), Filesize, ICPrivate))
snprintf(ErrorMsg, MsgSize, "Cancelled by user"); throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
return NULL; "Cancelled by user");
}
} }
// Only create index entries for video for now to save space // Only create index entries for video for now to save space
@ -155,9 +154,10 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
(*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(StartTime, RepeatPict, (FrameFlags & FRAME_KF) != 0, FilePos, FrameSize)); (*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(StartTime, RepeatPict, (FrameFlags & FRAME_KF) != 0, FilePos, FrameSize));
} else if (mkv_GetTrackInfo(MF, Track)->Type == TT_AUDIO && (IndexMask & (1 << Track))) { } else if (mkv_GetTrackInfo(MF, Track)->Type == TT_AUDIO && (IndexMask & (1 << Track))) {
(*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(StartTime, AudioContexts[Track].CurrentSample, (FrameFlags & FRAME_KF) != 0, FilePos, FrameSize)); int64_t StartSample = AudioContexts[Track].CurrentSample;
ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC, ErrorMsg, MsgSize); unsigned int CompressedFrameSize = FrameSize;
AVCodecContext *AudioCodecContext = AudioContexts[Track].CodecContext; AVCodecContext *AudioCodecContext = AudioContexts[Track].CodecContext;
ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC);
TempPacket.data = MC.Buffer; TempPacket.data = MC.Buffer;
TempPacket.size = FrameSize; TempPacket.size = FrameSize;
if ((FrameFlags & FRAME_KF) != 0) if ((FrameFlags & FRAME_KF) != 0)
@ -169,13 +169,18 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket); int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
if (Ret < 0) { if (Ret < 0) {
if (IgnoreDecodeErrors) { if (ErrorHandling == FFMS_IEH_ABORT) {
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
"Audio decoding error");
} else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) {
(*TrackIndices)[Track].clear(); (*TrackIndices)[Track].clear();
IndexMask &= ~(1 << Track); IndexMask &= ~(1 << Track);
break; break;
} else { } else if (ErrorHandling == FFMS_IEH_STOP_TRACK) {
snprintf(ErrorMsg, MsgSize, "Audio decoding error"); IndexMask &= ~(1 << Track);
return NULL; break;
} else if (ErrorHandling == FFMS_IEH_IGNORE) {
break;
} }
} }
@ -188,8 +193,11 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
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 (DumpMask & (1 << Track)) if (DumpMask & (1 << Track))
WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize, ErrorMsg, MsgSize); WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize);
} }
(*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(StartTime, StartSample,
static_cast<unsigned int>(AudioContexts[Track].CurrentSample - StartSample), (FrameFlags & FRAME_KF) != 0, FilePos, CompressedFrameSize));
} }
} }
@ -207,7 +215,7 @@ FFMS_TrackType FFMatroskaIndexer::GetTrackType(int Track) {
const char *FFMatroskaIndexer::GetTrackCodec(int Track) { const char *FFMatroskaIndexer::GetTrackCodec(int Track) {
if (Codec[Track]) if (Codec[Track])
return Codec[Track]->long_name; return Codec[Track]->name;
else else
return "Unsupported codec/Unknown codec name"; return "Unsupported codec/Unknown codec name";
} }

View file

@ -18,7 +18,9 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#include "ffvideosource.h" #include "videosource.h"
void FFMatroskaVideo::Free(bool CloseCodec) { void FFMatroskaVideo::Free(bool CloseCodec) {
if (CS) if (CS)
@ -33,46 +35,39 @@ void FFMatroskaVideo::Free(bool CloseCodec) {
} }
FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track, FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
FFIndex *Index, const char *PP, FFMS_Index *Index, int Threads)
int Threads, char *ErrorMsg, unsigned MsgSize) : Res(FFSourceResources<FFMS_VideoSource>(this)), FFMS_VideoSource(SourceFile, Index, Track) {
: FFVideo(SourceFile, Index, ErrorMsg, MsgSize) {
AVCodec *Codec = NULL; AVCodec *Codec = NULL;
CodecContext = NULL; CodecContext = NULL;
TrackInfo *TI = NULL; TrackInfo *TI = NULL;
CS = NULL; CS = NULL;
PacketNumber = 0;
VideoTrack = Track; VideoTrack = Track;
Frames = (*Index)[VideoTrack]; Frames = (*Index)[VideoTrack];
if (Frames.size() == 0) {
snprintf(ErrorMsg, MsgSize, "Video track contains no frames");
throw ErrorMsg;
}
MC.ST.fp = ffms_fopen(SourceFile, "rb"); MC.ST.fp = ffms_fopen(SourceFile, "rb");
if (MC.ST.fp == NULL) { if (MC.ST.fp == NULL)
snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno)); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
throw ErrorMsg; boost::format("Can't open '%1%': %2%") % SourceFile % strerror(errno));
}
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE); setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage)); MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
if (MF == NULL) { if (MF == NULL) {
fclose(MC.ST.fp); fclose(MC.ST.fp);
snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
throw ErrorMsg; boost::format("Can't parse Matroska file: %1%") % ErrorMessage);
} }
mkv_SetTrackMask(MF, ~(1 << VideoTrack));
TI = mkv_GetTrackInfo(MF, VideoTrack); TI = mkv_GetTrackInfo(MF, VideoTrack);
if (TI->CompEnabled) { if (TI->CompEnabled) {
CS = cs_Create(MF, VideoTrack, ErrorMessage, sizeof(ErrorMessage)); CS = cs_Create(MF, VideoTrack, ErrorMessage, sizeof(ErrorMessage));
if (CS == NULL) { if (CS == NULL) {
Free(false); Free(false);
snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage); throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
throw ErrorMsg; boost::format("Can't create decompressor: %1%") % ErrorMessage);
} }
} }
@ -80,48 +75,35 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
CodecContext->thread_count = Threads; CodecContext->thread_count = Threads;
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate)); Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
if (Codec == NULL) { if (Codec == NULL)
Free(false); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Video codec not found"); "Video codec not found");
throw ErrorMsg;
}
InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext); InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext);
if (avcodec_open(CodecContext, Codec) < 0) { if (avcodec_open(CodecContext, Codec) < 0)
Free(false); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Could not open video codec"); "Could not open video codec");
throw ErrorMsg;
} Res.CloseCodec(true);
// 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; DecodeNextFrame();
if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
Free(true);
throw ErrorMsg;
}
VP.Width = CodecContext->width;
VP.Height = CodecContext->height;;
VP.FPSDenominator = 1; VP.FPSDenominator = 1;
VP.FPSNumerator = 30; VP.FPSNumerator = 30;
VP.RFFDenominator = CodecContext->time_base.num; VP.RFFDenominator = CodecContext->time_base.num;
VP.RFFNumerator = CodecContext->time_base.den; VP.RFFNumerator = CodecContext->time_base.den;
VP.NumFrames = Frames.size(); VP.NumFrames = Frames.size();
VP.VPixelFormat = CodecContext->pix_fmt; VP.TopFieldFirst = DecodeFrame->top_field_first;
VP.ColorSpace = CodecContext->colorspace;
VP.ColorRange = CodecContext->color_range;
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
if (VP.Width <= 0 || VP.Height <= 0) { if (CodecContext->width <= 0 || CodecContext->height <= 0)
Free(true); throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
snprintf(ErrorMsg, MsgSize, "Codec returned zero size video"); "Codec returned zero size video");
throw ErrorMsg;
}
if (InitPP(PP, ErrorMsg, MsgSize)) {
Free(true);
throw ErrorMsg;
}
// Calculate the average framerate // Calculate the average framerate
if (Frames.size() >= 2) { if (Frames.size() >= 2) {
@ -131,10 +113,7 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
} }
// Output the already decoded frame so it isn't wasted // Output the already decoded frame so it isn't wasted
if (!OutputFrame(DecodeFrame, ErrorMsg, MsgSize)) { OutputFrame(DecodeFrame);
Free(true);
throw ErrorMsg;
}
// Set AR variables // Set AR variables
VP.SARNum = TI->AV.Video.DisplayWidth * TI->AV.Video.PixelHeight; VP.SARNum = TI->AV.Video.DisplayWidth * TI->AV.Video.PixelHeight;
@ -147,33 +126,33 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
VP.CropBottom = TI->AV.Video.CropB; VP.CropBottom = TI->AV.Video.CropB;
} }
FFMatroskaVideo::~FFMatroskaVideo() { void FFMatroskaVideo::DecodeNextFrame() {
Free(true);
}
int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) {
int FrameFinished = 0; int FrameFinished = 0;
*AFirstStartTime = -1;
AVPacket Packet; AVPacket Packet;
InitNullPacket(&Packet); InitNullPacket(Packet);
ulonglong StartTime, EndTime, FilePos; unsigned int FrameSize;
unsigned int Track, FrameFlags, FrameSize;
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) { while (PacketNumber < Frames.size()) {
if (*AFirstStartTime < 0) // The additional indirection is because the packets are stored in
*AFirstStartTime = StartTime; // presentation order and not decoding order, this is unnoticable
// in the other sources where less is done manually
if (ReadFrame(FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize)) const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos];
return 1; FrameSize = FI.FrameSize;
ReadFrame(FI.FilePos, FrameSize, CS, MC);
Packet.data = MC.Buffer; Packet.data = MC.Buffer;
Packet.size = FrameSize; Packet.size = FrameSize;
if (FrameFlags & FRAME_KF) if (FI.KeyFrame)
Packet.flags = AV_PKT_FLAG_KEY; Packet.flags = AV_PKT_FLAG_KEY;
else else
Packet.flags = 0; Packet.flags = 0;
PacketNumber++;
if (CodecContext->codec_id == CODEC_ID_MPEG4 && IsNVOP(Packet))
goto Done;
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
if (FrameFinished) if (FrameFinished)
@ -183,7 +162,7 @@ int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, u
// Flush the last frames // Flush the last frames
if (CodecContext->has_b_frames) { if (CodecContext->has_b_frames) {
AVPacket NullPacket; AVPacket NullPacket;
InitNullPacket(&NullPacket); InitNullPacket(NullPacket);
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket); avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
} }
@ -191,40 +170,27 @@ int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, u
goto Error; goto Error;
Error: Error:
Done: Done:;
return 0;
} }
FFAVFrame *FFMatroskaVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) { FFMS_Frame *FFMatroskaVideo::GetFrame(int n) {
// PPFrame always holds frame LastFrameNum even if no PP is applied GetFrameCheck(n);
if (LastFrameNum == n) if (LastFrameNum == n)
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize); return &LocalFrame;
bool HasSeeked = false; int ClosestKF = Frames.FindClosestVideoKeyFrame(n);
if (CurrentFrame > n || ClosestKF > CurrentFrame + 10) {
if (n < CurrentFrame || Frames.FindClosestVideoKeyFrame(n) > CurrentFrame) { PacketNumber = ClosestKF;
mkv_Seek(MF, Frames[n].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT); CurrentFrame = ClosestKF;
avcodec_flush_buffers(CodecContext); avcodec_flush_buffers(CodecContext);
HasSeeked = true;
} }
do { do {
int64_t StartTime; DecodeNextFrame();
if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize))
return NULL;
if (HasSeeked) {
HasSeeked = false;
if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) {
snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file");
return NULL;
}
}
CurrentFrame++; CurrentFrame++;
} while (CurrentFrame <= n); } while (CurrentFrame <= n);
LastFrameNum = n; LastFrameNum = n;
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize); return OutputFrame(DecodeFrame);
} }

View file

@ -104,13 +104,13 @@ longlong StdIoGetFileSize(StdIoStream *st) {
void InitStdIoStream(StdIoStream *st) { void InitStdIoStream(StdIoStream *st) {
memset(st,0,sizeof(StdIoStream)); memset(st,0,sizeof(StdIoStream));
st->base.read = StdIoRead; st->base.read = (int (*)(InputStream *,ulonglong,void *,int))StdIoRead;
st->base.scan = StdIoScan; st->base.scan = (longlong (*)(InputStream *,ulonglong,unsigned int))StdIoScan;
st->base.getcachesize = StdIoGetCacheSize; st->base.getcachesize = (unsigned int (*)(InputStream *))StdIoGetCacheSize;
st->base.geterror = StdIoGetLastError; st->base.geterror = (const char *(*)(InputStream *))StdIoGetLastError;
st->base.memalloc = StdIoMalloc; st->base.memalloc = (void *(*)(InputStream *,size_t))StdIoMalloc;
st->base.memrealloc = StdIoRealloc; st->base.memrealloc = (void *(*)(InputStream *,void *,size_t))StdIoRealloc;
st->base.memfree = StdIoFree; st->base.memfree = (void (*)(InputStream *,void *))StdIoFree;
st->base.progress = StdIoProgress; st->base.progress = (int (*)(InputStream *,ulonglong,ulonglong))StdIoProgress;
st->base.getfilesize = StdIoGetFileSize; st->base.getfilesize = (longlong (*)(InputStream *))StdIoGetFileSize;
} }

View file

@ -46,6 +46,38 @@ extern const AVCodecTag ff_codec_wav_tags[];
extern int CPUFeatures; extern int CPUFeatures;
FFMS_Exception::FFMS_Exception(int ErrorType, int SubType, const char *Message) : _ErrorType(ErrorType), _SubType(SubType), _Message(Message) {
}
FFMS_Exception::FFMS_Exception(int ErrorType, int SubType, const std::string &Message) : _ErrorType(ErrorType), _SubType(SubType), _Message(Message) {
}
FFMS_Exception::FFMS_Exception(int ErrorType, int SubType, const boost::format &Message) : _ErrorType(ErrorType), _SubType(SubType), _Message(Message.str()) {
}
FFMS_Exception::~FFMS_Exception() throw () {
}
const std::string &FFMS_Exception::GetErrorMessage() const {
return _Message;
}
int FFMS_Exception::CopyOut(FFMS_ErrorInfo *ErrorInfo) const {
if (ErrorInfo) {
ErrorInfo->ErrorType = _ErrorType;
ErrorInfo->SubType = _SubType;
if (ErrorInfo->BufferSize > 0) {
memset(ErrorInfo->Buffer, 0, ErrorInfo->BufferSize);
_Message.copy(ErrorInfo->Buffer, ErrorInfo->BufferSize - 1);
}
}
return (_ErrorType << 16) | _SubType;
}
int GetSWSCPUFlags() { int GetSWSCPUFlags() {
int Flags = 0; int Flags = 0;
@ -78,6 +110,16 @@ int GetPPCPUFlags() {
return Flags; return Flags;
} }
void ClearErrorInfo(FFMS_ErrorInfo *ErrorInfo) {
if (ErrorInfo) {
ErrorInfo->ErrorType = FFMS_ERROR_SUCCESS;
ErrorInfo->SubType = FFMS_ERROR_SUCCESS;
if (ErrorInfo->BufferSize > 0)
ErrorInfo->Buffer[0] = 0;
}
}
FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT) { FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT) {
switch (TT) { switch (TT) {
case TT_VIDEO: return FFMS_TYPE_VIDEO; break; case TT_VIDEO: return FFMS_TYPE_VIDEO; break;
@ -87,90 +129,92 @@ FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT) {
} }
} }
int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize) { void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context) {
if (CS) { if (CS) {
char CSBuffer[4096];
unsigned int DecompressedFrameSize = 0; unsigned int DecompressedFrameSize = 0;
cs_NextFrame(CS, FilePos, FrameSize); cs_NextFrame(CS, FilePos, FrameSize);
for (;;) { for (;;) {
int ReadBytes = cs_ReadData(CS, CSBuffer, sizeof(CSBuffer)); int ReadBytes = cs_ReadData(CS, Context.CSBuffer, sizeof(Context.CSBuffer));
if (ReadBytes < 0) { if (ReadBytes < 0)
snprintf(ErrorMsg, MsgSize, "Error decompressing data: %s", cs_GetLastError(CS)); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
return 1; boost::format("Error decompressing data: %1%") % cs_GetLastError(CS));
}
if (ReadBytes == 0) { if (ReadBytes == 0) {
FrameSize = DecompressedFrameSize; FrameSize = DecompressedFrameSize;
return 0; memset(Context.Buffer + DecompressedFrameSize, 0,
Context.BufferSize + FF_INPUT_BUFFER_PADDING_SIZE - DecompressedFrameSize);
return;
} }
if (Context.BufferSize < DecompressedFrameSize + ReadBytes) { if (Context.BufferSize < DecompressedFrameSize + ReadBytes) {
Context.BufferSize = FrameSize; Context.BufferSize = DecompressedFrameSize + ReadBytes;
Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16); Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + FF_INPUT_BUFFER_PADDING_SIZE);
if (Context.Buffer == NULL) { if (Context.Buffer == NULL)
snprintf(ErrorMsg, MsgSize, "Out of memory"); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
return 2; "Out of memory");
}
} }
memcpy(Context.Buffer + DecompressedFrameSize, CSBuffer, ReadBytes); memcpy(Context.Buffer + DecompressedFrameSize, Context.CSBuffer, ReadBytes);
DecompressedFrameSize += ReadBytes; DecompressedFrameSize += ReadBytes;
} }
} else { } else {
if (fseeko(Context.ST.fp, FilePos, SEEK_SET)) { if (fseeko(Context.ST.fp, FilePos, SEEK_SET))
snprintf(ErrorMsg, MsgSize, "fseek(): %s", strerror(errno)); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_SEEKING,
return 3; boost::format("fseek(): %1%") % strerror(errno));
}
if (Context.BufferSize < FrameSize) { if (Context.BufferSize < FrameSize) {
Context.BufferSize = FrameSize; Context.BufferSize = FrameSize;
Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16); Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16);
if (Context.Buffer == NULL) { if (Context.Buffer == NULL)
snprintf(ErrorMsg, MsgSize, "Out of memory"); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
return 4; "Out of memory");
}
} }
size_t ReadBytes = fread(Context.Buffer, 1, FrameSize, Context.ST.fp); size_t ReadBytes = fread(Context.Buffer, 1, FrameSize, Context.ST.fp);
if (ReadBytes != FrameSize) { if (ReadBytes != FrameSize) {
if (ReadBytes == 0) { if (ReadBytes == 0) {
if (feof(Context.ST.fp)) { if (feof(Context.ST.fp)) {
snprintf(ErrorMsg, MsgSize, "Unexpected EOF while reading frame"); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
return 5; "Unexpected EOF while reading frame");
} else { } else {
snprintf(ErrorMsg, MsgSize, "Error reading frame: %s", strerror(errno)); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_SEEKING,
return 6; boost::format("Error reading frame: %1%") % strerror(errno));
} }
} else { } else {
snprintf(ErrorMsg, MsgSize, "Short read while reading frame"); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
return 7; "Short read while reading frame");
} }
snprintf(ErrorMsg, MsgSize, "Unknown read error"); throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
return 8; "Unknown read error");
} }
return 0; return;
} }
} }
void InitNullPacket(AVPacket *pkt) { void InitNullPacket(AVPacket &pkt) {
av_init_packet(pkt); av_init_packet(&pkt);
pkt->data = NULL; pkt.data = NULL;
pkt->size = 0; pkt.size = 0;
} }
void FillAP(FFAudioProperties &AP, AVCodecContext *CTX, FFTrack &Frames) { bool IsNVOP(AVPacket &pkt) {
const uint8_t MPEG4NVOP[] = { 0x00, 0x00, 0x01, 0xB6 };
return pkt.size == 7 && !memcmp(pkt.data, MPEG4NVOP, 4);
}
void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames) {
AP.SampleFormat = static_cast<FFMS_SampleFormat>(CTX->sample_fmt); AP.SampleFormat = static_cast<FFMS_SampleFormat>(CTX->sample_fmt);
AP.BitsPerSample = av_get_bits_per_sample_format(CTX->sample_fmt); AP.BitsPerSample = av_get_bits_per_sample_format(CTX->sample_fmt);
if (CTX->sample_fmt == SAMPLE_FMT_S32) if (CTX->sample_fmt == SAMPLE_FMT_S32 && CTX->bits_per_raw_sample)
AP.BitsPerSample = CTX->bits_per_raw_sample; AP.BitsPerSample = CTX->bits_per_raw_sample;
AP.Channels = CTX->channels;; AP.Channels = CTX->channels;;
AP.ChannelLayout = CTX->channel_layout; AP.ChannelLayout = CTX->channel_layout;
AP.SampleRate = CTX->sample_rate; AP.SampleRate = CTX->sample_rate;
AP.NumSamples = (Frames.back()).SampleStart; AP.NumSamples = (Frames.back()).SampleStart + (Frames.back()).SampleCount;
AP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; AP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
AP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000; AP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
} }
@ -198,35 +242,37 @@ void vtCopy(VARIANT& vt,void *dest) {
} }
} }
#else
// used for matroska<->ffmpeg codec ID mapping to avoid Win32 dependency
typedef struct BITMAPINFOHEADER {
uint32_t biSize;
int32_t biWidth;
int32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
} BITMAPINFOHEADER;
#define MAKEFOURCC(ch0, ch1, ch2, ch3)\
((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) |\
((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 ))
#endif #endif
CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC) { CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC, unsigned int BitsPerSample) {
/* Look up native codecs */ /* Look up native codecs */
for(int i = 0; ff_mkv_codec_tags[i].id != CODEC_ID_NONE; i++){ for(int i = 0; ff_mkv_codec_tags[i].id != CODEC_ID_NONE; i++){
if(!strncmp(ff_mkv_codec_tags[i].str, Codec, if(!strncmp(ff_mkv_codec_tags[i].str, Codec,
strlen(ff_mkv_codec_tags[i].str))){ strlen(ff_mkv_codec_tags[i].str))) {
return ff_mkv_codec_tags[i].id;
// Uncompressed and exotic format fixup
// This list is incomplete
CodecID CID = ff_mkv_codec_tags[i].id;
switch (CID) {
case CODEC_ID_PCM_S16LE:
switch (BitsPerSample) {
case 8: CID = CODEC_ID_PCM_S8; break;
case 16: CID = CODEC_ID_PCM_S16LE; break;
case 24: CID = CODEC_ID_PCM_S24LE; break;
case 32: CID = CODEC_ID_PCM_S32LE; break;
}
break;
case CODEC_ID_PCM_S16BE:
switch (BitsPerSample) {
case 8: CID = CODEC_ID_PCM_S8; break;
case 16: CID = CODEC_ID_PCM_S16BE; break;
case 24: CID = CODEC_ID_PCM_S24BE; break;
case 32: CID = CODEC_ID_PCM_S32BE; break;
}
break;
}
return CID;
} }
} }
@ -234,7 +280,7 @@ CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC
const AVCodecTag *const tags[] = { ff_codec_bmp_tags, 0 }; const AVCodecTag *const tags[] = { ff_codec_bmp_tags, 0 };
if (!strcmp(Codec, "V_MS/VFW/FOURCC")) { if (!strcmp(Codec, "V_MS/VFW/FOURCC")) {
BITMAPINFOHEADER *b = reinterpret_cast<BITMAPINFOHEADER *>(CodecPrivate); FFMS_BITMAPINFOHEADER *b = reinterpret_cast<FFMS_BITMAPINFOHEADER *>(CodecPrivate);
return av_codec_get_id(tags, b->biCompression); return av_codec_get_id(tags, b->biCompression);
} }
@ -362,3 +408,60 @@ void ffms_fstream::open(const char *filename, std::ios_base::openmode mode) {
ffms_fstream::ffms_fstream(const char *filename, std::ios_base::openmode mode) { ffms_fstream::ffms_fstream(const char *filename, std::ios_base::openmode mode) {
open(filename, mode); open(filename, mode);
} }
#ifdef HAALISOURCE
CComPtr<IMMContainer> HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode) {
CComPtr<IMMContainer> pMMC;
CLSID clsid = HAALI_MPEG_PARSER;
if (SourceMode == FFMS_SOURCE_HAALIOGG)
clsid = HAALI_OGG_PARSER;
if (FAILED(pMMC.CoCreateInstance(clsid)))
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
"Can't create parser");
CComPtr<IMemAlloc> pMA;
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc)))
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
"Can't create memory allocator");
CComPtr<IMMStream> pMS;
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile)))
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
"Can't create disk file reader");
WCHAR WSourceFile[2048];
ffms_mbstowcs(WSourceFile, SourceFile, 2000);
CComQIPtr<IMMStreamOpen> pMSO(pMS);
if (FAILED(pMSO->Open(WSourceFile)))
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
"Can't open file");
if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
if (SourceMode == FFMS_SOURCE_HAALIMPEG)
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_INVALID_ARGUMENT,
"Can't parse file, most likely a transport stream not cut at packet boundaries");
else
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_INVALID_ARGUMENT,
"Can't parse file");
}
return pMMC;
}
#endif
void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext) {
if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0)
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
boost::format("Couldn't open '%1'") % SourceFile);
if (av_find_stream_info(FormatContext) < 0) {
av_close_input_file(FormatContext);
FormatContext = NULL;
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
"Couldn't find stream information");
}
}

View file

@ -23,7 +23,8 @@
#include <vector> #include <vector>
#include <fstream> #include <fstream>
#include <stdio.h> #include <cstdio>
#include <boost/format.hpp>
#include "ffms.h" #include "ffms.h"
#include "matroskaparser.h" #include "matroskaparser.h"
@ -50,17 +51,73 @@ extern "C" {
# include "guids.h" # include "guids.h"
#endif #endif
#define FFMS_GET_VECTOR_PTR(v) (((v).size() ? &(v)[0] : NULL)) #define FFMS_GET_VECTOR_PTR(v) (((v).size() ? &(v)[0] : NULL))
const int64_t ffms_av_nopts_value = static_cast<int64_t>(1) << 63; const int64_t ffms_av_nopts_value = static_cast<int64_t>(1) << 63;
// used for matroska<->ffmpeg codec ID mapping to avoid Win32 dependency
typedef struct FFMS_BITMAPINFOHEADER {
uint32_t biSize;
int32_t biWidth;
int32_t biHeight;
uint16_t biPlanes;
uint16_t biBitCount;
uint32_t biCompression;
uint32_t biSizeImage;
int32_t biXPelsPerMeter;
int32_t biYPelsPerMeter;
uint32_t biClrUsed;
uint32_t biClrImportant;
} FFMS_BITMAPINFOHEADER;
class FFMS_Exception : public std::exception {
private:
std::string _Message;
int _ErrorType;
int _SubType;
public:
FFMS_Exception(int ErrorType, int SubType, const char *Message = "");
FFMS_Exception(int ErrorType, int SubType, const std::string &Message);
FFMS_Exception(int ErrorType, int SubType, const boost::format &Message);
~FFMS_Exception() throw ();
const std::string &GetErrorMessage() const;
int CopyOut(FFMS_ErrorInfo *ErrorInfo) const;
};
template<class T>
class FFSourceResources {
private:
T *_PrivClass;
bool _Enabled;
bool _Arg;
public:
FFSourceResources(T *Target) : _PrivClass(Target), _Enabled(true), _Arg(false) {
}
~FFSourceResources() {
if (_Enabled)
_PrivClass->Free(_Arg);
}
void SetEnabled(bool Enabled) {
_Enabled = Enabled;
}
void SetArg(bool Arg) {
_Arg = Arg;
}
void CloseCodec(bool Arg) {
_Arg = Arg;
}
};
struct MatroskaReaderContext { struct MatroskaReaderContext {
public: public:
StdIoStream ST; StdIoStream ST;
uint8_t *Buffer; uint8_t *Buffer;
unsigned int BufferSize; unsigned int BufferSize;
char CSBuffer[4096];
MatroskaReaderContext() { MatroskaReaderContext() {
InitStdIoStream(&ST); InitStdIoStream(&ST);
@ -81,19 +138,25 @@ public:
int GetSWSCPUFlags(); int GetSWSCPUFlags();
int GetPPCPUFlags(); int GetPPCPUFlags();
void ClearErrorInfo(FFMS_ErrorInfo *ErrorInfo);
FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT); FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT);
int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize); void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context);
bool AudioFMTIsFloat(SampleFormat FMT); bool AudioFMTIsFloat(SampleFormat FMT);
void InitNullPacket(AVPacket *pkt); void InitNullPacket(AVPacket &pkt);
void FillAP(FFAudioProperties &AP, AVCodecContext *CTX, FFTrack &Frames); bool IsNVOP(AVPacket &pkt);
void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames);
#ifdef HAALISOURCE #ifdef HAALISOURCE
unsigned vtSize(VARIANT &vt); unsigned vtSize(VARIANT &vt);
void vtCopy(VARIANT& vt,void *dest); void vtCopy(VARIANT& vt,void *dest);
void InitializeCodecContextFromHaaliInfo(CComQIPtr<IPropertyBag> pBag, AVCodecContext *CodecContext); void InitializeCodecContextFromHaaliInfo(CComQIPtr<IPropertyBag> pBag, AVCodecContext *CodecContext);
#endif #endif
void InitializeCodecContextFromMatroskaTrackInfo(TrackInfo *TI, AVCodecContext *CodecContext); void InitializeCodecContextFromMatroskaTrackInfo(TrackInfo *TI, AVCodecContext *CodecContext);
CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC = 0); CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC = 0, unsigned int BitsPerSample = 0);
FILE *ffms_fopen(const char *filename, const char *mode); FILE *ffms_fopen(const char *filename, const char *mode);
size_t ffms_mbstowcs (wchar_t *wcstr, const char *mbstr, size_t max); size_t ffms_mbstowcs (wchar_t *wcstr, const char *mbstr, size_t max);
#ifdef HAALISOURCE
CComPtr<IMMContainer> HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode);
#endif
void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext);
#endif #endif

View file

@ -18,24 +18,52 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE. // THE SOFTWARE.
#include "ffvideosource.h" #include "videosource.h"
int FFVideo::InitPP(const char *PP, char *ErrorMsg, unsigned MsgSize) { void FFMS_VideoSource::GetFrameCheck(int n) {
if (PP == NULL || !strcmp(PP, "")) if (n < 0 || n >= VP.NumFrames)
return 0; throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_INVALID_ARGUMENT,
"Out of bounds frame requested");
PPMode = pp_get_mode_by_name_and_quality(PP, PP_QUALITY_MAX);
if (!PPMode) {
snprintf(ErrorMsg, MsgSize, "Invalid postprocesing settings");
return 1;
}
return 0;
} }
int FFVideo::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height, char *ErrorMsg, unsigned MsgSize) { void FFMS_VideoSource::SetPP(const char *PP) {
if (PPMode)
pp_free_mode(PPMode);
PPMode = NULL;
if (PP != NULL && strcmp(PP, "")) {
PPMode = pp_get_mode_by_name_and_quality(PP, PP_QUALITY_MAX);
if (!PPMode) {
ResetPP();
throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_INVALID_ARGUMENT,
"Invalid postprocesing settings");
}
}
ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height);
OutputFrame(DecodeFrame);
}
void FFMS_VideoSource::ResetPP() {
if (PPContext)
pp_free_context(PPContext);
PPContext = NULL;
if (PPMode)
pp_free_mode(PPMode);
PPMode = NULL;
OutputFrame(DecodeFrame);
}
void FFMS_VideoSource::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height) {
if (PPContext)
pp_free_context(PPContext);
PPContext = NULL;
if (!PPMode) if (!PPMode)
return 0; return;
int Flags = GetPPCPUFlags(); int Flags = GetPPCPUFlags();
@ -45,34 +73,30 @@ int FFVideo::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height, char *E
case PIX_FMT_YUV411P: Flags |= PP_FORMAT_411; break; case PIX_FMT_YUV411P: Flags |= PP_FORMAT_411; break;
case PIX_FMT_YUV444P: Flags |= PP_FORMAT_444; break; case PIX_FMT_YUV444P: Flags |= PP_FORMAT_444; break;
default: default:
snprintf(ErrorMsg, MsgSize, "Input format is not supported for postprocessing"); ResetPP();
return 1; throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_UNSUPPORTED,
"The video does not have a colorspace suitable for postprocessing");
} }
if (PPContext)
pp_free_context(PPContext);
PPContext = pp_get_context(Width, Height, Flags); PPContext = pp_get_context(Width, Height, Flags);
avpicture_free(&PPFrame); avpicture_free(&PPFrame);
avpicture_alloc(&PPFrame, VPixelFormat, Width, Height); avpicture_alloc(&PPFrame, VPixelFormat, Width, Height);
return 0;
} }
static void CopyAVPictureFields(AVPicture &Picture, FFAVFrame &Dst) { static void CopyAVPictureFields(AVPicture &Picture, FFMS_Frame &Dst) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
Dst.Data[i] = Picture.data[i]; Dst.Data[i] = Picture.data[i];
Dst.Linesize[i] = Picture.linesize[i]; Dst.Linesize[i] = Picture.linesize[i];
} }
} }
FFAVFrame *FFVideo::OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize) { FFMS_Frame *FFMS_VideoSource::OutputFrame(AVFrame *Frame) {
if (LastFrameWidth != CodecContext->width || LastFrameHeight != CodecContext->height || LastFramePixelFormat != CodecContext->pix_fmt) { if (LastFrameWidth != CodecContext->width || LastFrameHeight != CodecContext->height || LastFramePixelFormat != CodecContext->pix_fmt) {
if (ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height, ErrorMsg, MsgSize)) ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height);
return NULL;
if (TargetHeight > 0 && TargetWidth > 0 && TargetPixelFormats != 0) if (TargetHeight > 0 && TargetWidth > 0 && TargetPixelFormats != 0)
if (ReAdjustOutputFormat(TargetPixelFormats, TargetWidth, TargetHeight, TargetResizer, ErrorMsg, MsgSize)) ReAdjustOutputFormat(TargetPixelFormats, TargetWidth, TargetHeight, TargetResizer);
return NULL;
} }
if (PPMode) { if (PPMode) {
@ -99,9 +123,9 @@ FFAVFrame *FFVideo::OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize
LocalFrame.EncodedWidth = CodecContext->width; LocalFrame.EncodedWidth = CodecContext->width;
LocalFrame.EncodedHeight = CodecContext->height; LocalFrame.EncodedHeight = CodecContext->height;
LocalFrame.EncodedPixelFormat = CodecContext->pix_fmt; LocalFrame.EncodedPixelFormat = CodecContext->pix_fmt;
LocalFrame.ScaledWidth = VP.Width; LocalFrame.ScaledWidth = TargetWidth;
LocalFrame.ScaledHeight = VP.Height; LocalFrame.ScaledHeight = TargetHeight;
LocalFrame.ConvertedPixelFormat = VP.VPixelFormat; LocalFrame.ConvertedPixelFormat = OutputFormat;
LocalFrame.KeyFrame = Frame->key_frame; LocalFrame.KeyFrame = Frame->key_frame;
LocalFrame.PictType = av_get_pict_type_char(Frame->pict_type); LocalFrame.PictType = av_get_pict_type_char(Frame->pict_type);
LocalFrame.RepeatPict = Frame->repeat_pict; LocalFrame.RepeatPict = Frame->repeat_pict;
@ -115,9 +139,22 @@ FFAVFrame *FFVideo::OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize
return &LocalFrame; return &LocalFrame;
} }
FFVideo::FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) { FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track) {
if (Index->CompareFileSignature(SourceFile, ErrorMsg, MsgSize)) if (Track < 0 || Track >= static_cast<int>(Index->size()))
throw ErrorMsg; throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
"Out of bounds track index selected");
if (Index->at(Track).TT != FFMS_TYPE_VIDEO)
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
"Not a video track");
if (Index[Track].size() == 0)
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
"Video track contains no frames");
if (!Index->CompareFileSignature(SourceFile))
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH,
"The index does not match the source file");
memset(&VP, 0, sizeof(VP)); memset(&VP, 0, sizeof(VP));
PPContext = NULL; PPContext = NULL;
@ -133,6 +170,7 @@ FFVideo::FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigne
TargetWidth = -1; TargetWidth = -1;
TargetPixelFormats = 0; TargetPixelFormats = 0;
TargetResizer = 0; TargetResizer = 0;
OutputFormat = PIX_FMT_NONE;
DecodeFrame = avcodec_alloc_frame(); DecodeFrame = avcodec_alloc_frame();
// Dummy allocations so the unallocated case doesn't have to be handled later // Dummy allocations so the unallocated case doesn't have to be handled later
@ -140,7 +178,7 @@ FFVideo::FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigne
avpicture_alloc(&SWSFrame, PIX_FMT_GRAY8, 16, 16); avpicture_alloc(&SWSFrame, PIX_FMT_GRAY8, 16, 16);
} }
FFVideo::~FFVideo() { FFMS_VideoSource::~FFMS_VideoSource() {
if (PPMode) if (PPMode)
pp_free_mode(PPMode); pp_free_mode(PPMode);
@ -155,32 +193,33 @@ FFVideo::~FFVideo() {
av_freep(&DecodeFrame); av_freep(&DecodeFrame);
} }
FFAVFrame *FFVideo::GetFrameByTime(double Time, char *ErrorMsg, unsigned MsgSize) { FFMS_Frame *FFMS_VideoSource::GetFrameByTime(double Time) {
int Frame = Frames.ClosestFrameFromDTS(static_cast<int64_t>((Time * 1000 * Frames.TB.Den) / Frames.TB.Num)); int Frame = Frames.ClosestFrameFromDTS(static_cast<int64_t>((Time * 1000 * Frames.TB.Den) / Frames.TB.Num));
return GetFrame(Frame, ErrorMsg, MsgSize); return GetFrame(Frame);
} }
int FFVideo::SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize) { void FFMS_VideoSource::SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer) {
this->TargetWidth = Width; this->TargetWidth = Width;
this->TargetHeight = Height; this->TargetHeight = Height;
this->TargetPixelFormats = TargetFormats; this->TargetPixelFormats = TargetFormats;
this->TargetResizer = Resizer; this->TargetResizer = Resizer;
return ReAdjustOutputFormat(TargetFormats, Width, Height, Resizer, ErrorMsg, MsgSize); ReAdjustOutputFormat(TargetFormats, Width, Height, Resizer);
OutputFrame(DecodeFrame);
} }
int FFVideo::ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize) { void FFMS_VideoSource::ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer) {
if (SWS) { if (SWS) {
sws_freeContext(SWS); sws_freeContext(SWS);
SWS = NULL; SWS = NULL;
} }
int Loss; int Loss;
PixelFormat OutputFormat = avcodec_find_best_pix_fmt(TargetFormats, OutputFormat = avcodec_find_best_pix_fmt(TargetFormats,
CodecContext->pix_fmt, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss); CodecContext->pix_fmt, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss);
if (OutputFormat == PIX_FMT_NONE) { if (OutputFormat == PIX_FMT_NONE) {
ResetOutputFormat(); ResetOutputFormat();
snprintf(ErrorMsg, MsgSize, "No suitable output format found"); throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT,
return 1; "No suitable output format found");
} }
if (CodecContext->pix_fmt != OutputFormat || Width != CodecContext->width || Height != CodecContext->height) { if (CodecContext->pix_fmt != OutputFormat || Width != CodecContext->width || Height != CodecContext->height) {
@ -188,22 +227,16 @@ int FFVideo::ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height,
OutputFormat, GetSWSCPUFlags() | Resizer, NULL, NULL, NULL); OutputFormat, GetSWSCPUFlags() | Resizer, NULL, NULL, NULL);
if (SWS == NULL) { if (SWS == NULL) {
ResetOutputFormat(); ResetOutputFormat();
snprintf(ErrorMsg, MsgSize, "Failed to allocate SWScale context"); throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT,
return 1; "Failed to allocate SWScale context");
} }
} }
VP.VPixelFormat = OutputFormat;
VP.Height = Height;
VP.Width = Width;
avpicture_free(&SWSFrame); avpicture_free(&SWSFrame);
avpicture_alloc(&SWSFrame, OutputFormat, Width, Height); avpicture_alloc(&SWSFrame, OutputFormat, Width, Height);
return 0;
} }
void FFVideo::ResetOutputFormat() { void FFMS_VideoSource::ResetOutputFormat() {
if (SWS) { if (SWS) {
sws_freeContext(SWS); sws_freeContext(SWS);
SWS = NULL; SWS = NULL;
@ -212,8 +245,7 @@ void FFVideo::ResetOutputFormat() {
TargetWidth = -1; TargetWidth = -1;
TargetHeight = -1; TargetHeight = -1;
TargetPixelFormats = 0; TargetPixelFormats = 0;
OutputFormat = PIX_FMT_NONE;
VP.Height = CodecContext->height; OutputFrame(DecodeFrame);
VP.Width = CodecContext->width;
VP.VPixelFormat = CodecContext->pix_fmt;
} }

View file

@ -45,7 +45,8 @@ extern "C" {
# include "guids.h" # include "guids.h"
#endif #endif
class FFVideo { class FFMS_VideoSource {
friend class FFSourceResources<FFMS_VideoSource>;
private: private:
pp_context_t *PPContext; pp_context_t *PPContext;
pp_mode_t *PPMode; pp_mode_t *PPMode;
@ -57,75 +58,83 @@ private:
int TargetWidth; int TargetWidth;
int64_t TargetPixelFormats; int64_t TargetPixelFormats;
int TargetResizer; int TargetResizer;
PixelFormat OutputFormat;
AVPicture PPFrame; AVPicture PPFrame;
AVPicture SWSFrame; AVPicture SWSFrame;
protected: protected:
FFVideoProperties VP; FFMS_VideoProperties VP;
FFAVFrame LocalFrame; FFMS_Frame LocalFrame;
AVFrame *DecodeFrame; AVFrame *DecodeFrame;
int LastFrameNum; int LastFrameNum;
FFTrack Frames; FFMS_Track Frames;
int VideoTrack; int VideoTrack;
int CurrentFrame; int CurrentFrame;
AVCodecContext *CodecContext; AVCodecContext *CodecContext;
FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize); FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track);
int InitPP(const char *PP, char *ErrorMsg, unsigned MsgSize); void ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height);
int ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height, char *ErrorMsg, unsigned MsgSize); void ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer);
FFAVFrame *OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize); FFMS_Frame *OutputFrame(AVFrame *Frame);
virtual void Free(bool CloseCodec) = 0;
public: public:
virtual ~FFVideo(); virtual ~FFMS_VideoSource();
const FFVideoProperties& GetFFVideoProperties() { return VP; } const FFMS_VideoProperties& GetVideoProperties() { return VP; }
FFTrack *GetFFTrack() { return &Frames; } FFMS_Track *GetTrack() { return &Frames; }
virtual FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize) = 0; virtual FFMS_Frame *GetFrame(int n) = 0;
FFAVFrame *GetFrameByTime(double Time, char *ErrorMsg, unsigned MsgSize); void GetFrameCheck(int n);
int SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize); FFMS_Frame *GetFrameByTime(double Time);
int ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize); void SetPP(const char *PP);
void ResetPP();
void SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer);
void ResetOutputFormat(); void ResetOutputFormat();
}; };
class FFLAVFVideo : public FFVideo { class FFLAVFVideo : public FFMS_VideoSource {
private: private:
AVFormatContext *FormatContext; AVFormatContext *FormatContext;
int SeekMode; int SeekMode;
FFSourceResources<FFMS_VideoSource> Res;
void DecodeNextFrame(int64_t *DTS);
protected:
void Free(bool CloseCodec); void Free(bool CloseCodec);
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, FFMS_Index *Index, int Threads, int SeekMode);
~FFLAVFVideo(); FFMS_Frame *GetFrame(int n);
FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
}; };
class FFMatroskaVideo : public FFVideo { class FFMatroskaVideo : public FFMS_VideoSource {
private: private:
MatroskaFile *MF; MatroskaFile *MF;
MatroskaReaderContext MC; MatroskaReaderContext MC;
CompressedStream *CS; CompressedStream *CS;
char ErrorMessage[256]; char ErrorMessage[256];
FFSourceResources<FFMS_VideoSource> Res;
size_t PacketNumber;
void DecodeNextFrame();
protected:
void Free(bool CloseCodec); void Free(bool CloseCodec);
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, FFMS_Index *Index, int Threads);
~FFMatroskaVideo(); FFMS_Frame *GetFrame(int n);
FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
}; };
#ifdef HAALISOURCE #ifdef HAALISOURCE
class FFHaaliVideo : public FFVideo { class FFHaaliVideo : public FFMS_VideoSource {
private: private:
CComPtr<IMMContainer> pMMC; CComPtr<IMMContainer> pMMC;
std::vector<uint8_t> CodecPrivate; std::vector<uint8_t> CodecPrivate;
AVBitStreamFilterContext *BitStreamFilter; AVBitStreamFilterContext *BitStreamFilter;
FFSourceResources<FFMS_VideoSource> Res;
void DecodeNextFrame(int64_t *AFirstStartTime);
protected:
void Free(bool CloseCodec); void Free(bool CloseCodec);
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, FFMS_Index *Index, int Threads, enum FFMS_Sources SourceMode);
~FFHaaliVideo(); FFMS_Frame *GetFrame(int n);
FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
}; };
#endif // HAALISOURCE #endif // HAALISOURCE