forked from mia/Aegisub
Updating ffms2 to r221 (2.12), step 1/3: sources
Originally committed to SVN as r3574.
This commit is contained in:
parent
8322981b47
commit
6fe92bce72
21 changed files with 1354 additions and 1139 deletions
|
@ -21,6 +21,9 @@
|
|||
#ifndef FFMS_H
|
||||
#define FFMS_H
|
||||
|
||||
// Version format: major - minor - micro - bump
|
||||
#define FFMS_VERSION ((2 << 24) | (11 << 16)| (0 << 8) | 5)
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -43,11 +46,56 @@
|
|||
# define FFMS_API(ret) EXTERN_C ret FFMS_CC
|
||||
#endif
|
||||
|
||||
FFMS_CLASS_TYPE FFVideo;
|
||||
FFMS_CLASS_TYPE FFAudio;
|
||||
FFMS_CLASS_TYPE FFIndexer;
|
||||
FFMS_CLASS_TYPE FFIndex;
|
||||
FFMS_CLASS_TYPE FFTrack;
|
||||
struct FFMS_ErrorInfo {
|
||||
int ErrorType;
|
||||
int SubType;
|
||||
int BufferSize;
|
||||
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 {
|
||||
FFMS_CPU_CAPS_MMX = 0x01,
|
||||
|
@ -65,6 +113,13 @@ enum FFMS_SeekMode {
|
|||
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 {
|
||||
FFMS_TYPE_UNKNOWN = -1,
|
||||
FFMS_TYPE_VIDEO,
|
||||
|
@ -106,20 +161,20 @@ enum FFMS_AudioChannel {
|
|||
};
|
||||
|
||||
enum FFMS_Resizers {
|
||||
FFMS_RESIZER_FAST_BILINEAR = 0x01,
|
||||
FFMS_RESIZER_BILINEAR = 0x02,
|
||||
FFMS_RESIZER_BICUBIC = 0x04,
|
||||
FFMS_RESIZER_X = 0x08,
|
||||
FFMS_RESIZER_POINT = 0x10,
|
||||
FFMS_RESIZER_AREA = 0x20,
|
||||
FFMS_RESIZER_BICUBLIN = 0x40,
|
||||
FFMS_RESIZER_GAUSS = 0x80,
|
||||
FFMS_RESIZER_SINC = 0x100,
|
||||
FFMS_RESIZER_LANCZOS = 0x200,
|
||||
FFMS_RESIZER_SPLINE = 0x400
|
||||
FFMS_RESIZER_FAST_BILINEAR = 0x0001,
|
||||
FFMS_RESIZER_BILINEAR = 0x0002,
|
||||
FFMS_RESIZER_BICUBIC = 0x0004,
|
||||
FFMS_RESIZER_X = 0x0008,
|
||||
FFMS_RESIZER_POINT = 0x0010,
|
||||
FFMS_RESIZER_AREA = 0x0020,
|
||||
FFMS_RESIZER_BICUBLIN = 0x0040,
|
||||
FFMS_RESIZER_GAUSS = 0x0080,
|
||||
FFMS_RESIZER_SINC = 0x0100,
|
||||
FFMS_RESIZER_LANCZOS = 0x0200,
|
||||
FFMS_RESIZER_SPLINE = 0x0400
|
||||
};
|
||||
|
||||
struct FFAVFrame {
|
||||
struct FFMS_Frame {
|
||||
uint8_t *Data[4];
|
||||
int Linesize[4];
|
||||
int EncodedWidth;
|
||||
|
@ -135,37 +190,37 @@ struct FFAVFrame {
|
|||
char PictType;
|
||||
};
|
||||
|
||||
struct FFTrackTimeBase {
|
||||
struct FFMS_TrackTimeBase {
|
||||
int64_t Num;
|
||||
int64_t Den;
|
||||
};
|
||||
|
||||
#define FFMS_FRAMEINFO_COMMON int64_t DTS; int RepeatPict; bool KeyFrame;
|
||||
|
||||
struct FFFrameInfo {
|
||||
struct FFMS_FrameInfo {
|
||||
FFMS_FRAMEINFO_COMMON
|
||||
};
|
||||
|
||||
struct FFVideoProperties {
|
||||
int Width;
|
||||
int Height;
|
||||
struct FFMS_VideoProperties {
|
||||
int FPSDenominator;
|
||||
int FPSNumerator;
|
||||
int RFFDenominator;
|
||||
int RFFNumerator;
|
||||
int NumFrames;
|
||||
int VPixelFormat;
|
||||
int SARNum;
|
||||
int SARDen;
|
||||
int CropTop;
|
||||
int CropBottom;
|
||||
int CropLeft;
|
||||
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 LastTime;
|
||||
};
|
||||
|
||||
struct FFAudioProperties {
|
||||
struct FFMS_AudioProperties {
|
||||
int SampleFormat;
|
||||
int SampleRate;
|
||||
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 *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
|
||||
// Functions without error message output can be assumed to never fail in a graceful way
|
||||
FFMS_API(void) FFMS_Init(int CPUFeatures);
|
||||
FFMS_API(int) FFMS_GetLogLevel();
|
||||
FFMS_API(void) FFMS_SetLogLevel(int Level);
|
||||
FFMS_API(FFVideo *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(FFAudio *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(void) FFMS_DestroyVideoSource(FFVideo *V);
|
||||
FFMS_API(void) FFMS_DestroyAudioSource(FFAudio *A);
|
||||
FFMS_API(const FFVideoProperties *) FFMS_GetVideoProperties(FFVideo *V);
|
||||
FFMS_API(const FFAudioProperties *) FFMS_GetAudioProperties(FFAudio *A);
|
||||
FFMS_API(const FFAVFrame *) FFMS_GetFrame(FFVideo *V, int n, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(const FFAVFrame *) FFMS_GetFrameByTime(FFVideo *V, double Time, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_GetAudio(FFAudio *A, void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_SetOutputFormatV(FFVideo *V, int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(void) FFMS_ResetOutputFormatV(FFVideo *V);
|
||||
FFMS_API(void) FFMS_DestroyIndex(FFIndex *Index);
|
||||
FFMS_API(int) FFMS_GetFirstTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_GetNumTracks(FFIndex *Index);
|
||||
FFMS_API(int) FFMS_GetNumTracksI(FFIndexer *Indexer);
|
||||
FFMS_API(int) FFMS_GetTrackType(FFTrack *T);
|
||||
FFMS_API(int) FFMS_GetTrackTypeI(FFIndexer *Indexer, int Track);
|
||||
FFMS_API(const char *) FFMS_GetCodecNameI(FFIndexer *Indexer, int Track);
|
||||
FFMS_API(int) FFMS_GetNumFrames(FFTrack *T);
|
||||
FFMS_API(const FFFrameInfo *) FFMS_GetFrameInfo(FFTrack *T, int Frame);
|
||||
FFMS_API(FFTrack *) FFMS_GetTrackFromIndex(FFIndex *Index, int Track);
|
||||
FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V);
|
||||
FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A);
|
||||
FFMS_API(const FFTrackTimeBase *) FFMS_GetTimeBase(FFTrack *T);
|
||||
FFMS_API(int) FFMS_WriteTimecodes(FFTrack *T, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(FFIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFAudioProperties *AP, char *FileName, int FNSize, void *Private);
|
||||
FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(FFIndex *) FFMS_DoIndexing(FFIndexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(void) FFMS_CancelIndexing(FFIndexer *Indexer);
|
||||
FFMS_API(FFIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_IndexBelongsToFile(FFIndex *Index, const char *SourceFile, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFIndex *Index, 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(FFMS_AudioSource *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo);
|
||||
FFMS_API(void) FFMS_DestroyVideoSource(FFMS_VideoSource *V);
|
||||
FFMS_API(void) FFMS_DestroyAudioSource(FFMS_AudioSource *A);
|
||||
FFMS_API(const FFMS_VideoProperties *) FFMS_GetVideoProperties(FFMS_VideoSource *V);
|
||||
FFMS_API(const FFMS_AudioProperties *) FFMS_GetAudioProperties(FFMS_AudioSource *A);
|
||||
FFMS_API(const FFMS_Frame *) FFMS_GetFrame(FFMS_VideoSource *V, int n, FFMS_ErrorInfo *ErrorInfo);
|
||||
FFMS_API(const FFMS_Frame *) FFMS_GetFrameByTime(FFMS_VideoSource *V, double Time, FFMS_ErrorInfo *ErrorInfo);
|
||||
FFMS_API(int) FFMS_GetAudio(FFMS_AudioSource *A, void *Buf, int64_t Start, int64_t Count, FFMS_ErrorInfo *ErrorInfo);
|
||||
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(FFMS_VideoSource *V);
|
||||
FFMS_API(int) FFMS_SetPP(FFMS_VideoSource *V, const char *PP, FFMS_ErrorInfo *ErrorInfo);
|
||||
FFMS_API(void) FFMS_ResetPP(FFMS_VideoSource *V);
|
||||
FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index);
|
||||
FFMS_API(int) FFMS_GetFirstTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo);
|
||||
FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo);
|
||||
FFMS_API(int) FFMS_GetNumTracks(FFMS_Index *Index);
|
||||
FFMS_API(int) FFMS_GetNumTracksI(FFMS_Indexer *Indexer);
|
||||
FFMS_API(int) FFMS_GetTrackType(FFMS_Track *T);
|
||||
FFMS_API(int) FFMS_GetTrackTypeI(FFMS_Indexer *Indexer, int Track);
|
||||
FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track);
|
||||
FFMS_API(int) FFMS_GetNumFrames(FFMS_Track *T);
|
||||
FFMS_API(const FFMS_FrameInfo *) FFMS_GetFrameInfo(FFMS_Track *T, int Frame);
|
||||
FFMS_API(FFMS_Track *) FFMS_GetTrackFromIndex(FFMS_Index *Index, int Track);
|
||||
FFMS_API(FFMS_Track *) FFMS_GetTrackFromVideo(FFMS_VideoSource *V);
|
||||
FFMS_API(FFMS_Track *) FFMS_GetTrackFromAudio(FFMS_AudioSource *A);
|
||||
FFMS_API(const FFMS_TrackTimeBase *) FFMS_GetTimeBase(FFMS_Track *T);
|
||||
FFMS_API(int) FFMS_WriteTimecodes(FFMS_Track *T, const char *TimecodeFile, FFMS_ErrorInfo *ErrorInfo);
|
||||
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(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, void *Private);
|
||||
FFMS_API(FFMS_Indexer *) FFMS_CreateIndexer(const char *SourceFile, FFMS_ErrorInfo *ErrorInfo);
|
||||
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(void) FFMS_CancelIndexing(FFMS_Indexer *Indexer);
|
||||
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_GetPresentSources();
|
||||
FFMS_API(int) FFMS_GetEnabledSources();
|
||||
|
||||
#endif
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "ffaudiosource.h"
|
||||
#include "audiosource.h"
|
||||
|
||||
/* Audio Cache */
|
||||
|
||||
|
@ -93,15 +93,32 @@ int64_t TAudioCache::FillRequest(int64_t Start, int64_t Samples, uint8_t *Dst) {
|
|||
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) {
|
||||
if (Index->CompareFileSignature(SourceFile, ErrorMsg, MsgSize))
|
||||
throw ErrorMsg;
|
||||
FFMS_AudioSource::FFMS_AudioSource(const char *SourceFile, FFMS_Index *Index, int Track) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 10), CurrentSample(0) {
|
||||
if (Track < 0 || Track >= static_cast<int>(Index->size()))
|
||||
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");
|
||||
}
|
|
@ -68,63 +68,68 @@ public:
|
|||
int64_t FillRequest(int64_t Start, int64_t Samples, uint8_t *Dst);
|
||||
};
|
||||
|
||||
class FFAudio {
|
||||
class FFMS_AudioSource {
|
||||
friend class FFSourceResources<FFMS_AudioSource>;
|
||||
protected:
|
||||
TAudioCache AudioCache;
|
||||
int64_t CurrentSample;
|
||||
std::vector<uint8_t> DecodingBuffer;
|
||||
FFTrack Frames;
|
||||
FFMS_Track Frames;
|
||||
AVCodecContext *CodecContext;
|
||||
int AudioTrack;
|
||||
FFAudioProperties AP;
|
||||
FFMS_AudioProperties AP;
|
||||
|
||||
virtual void Free(bool CloseCodec) = 0;
|
||||
public:
|
||||
FFAudio(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
||||
virtual ~FFAudio();
|
||||
FFTrack *GetFFTrack() { return &Frames; }
|
||||
const FFAudioProperties& GetFFAudioProperties() { return AP; }
|
||||
virtual int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) = 0;
|
||||
FFMS_AudioSource(const char *SourceFile, FFMS_Index *Index, int Track);
|
||||
virtual ~FFMS_AudioSource();
|
||||
FFMS_Track *GetTrack() { return &Frames; }
|
||||
const FFMS_AudioProperties& GetAudioProperties() { return AP; }
|
||||
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:
|
||||
AVFormatContext *FormatContext;
|
||||
FFSourceResources<FFMS_AudioSource> Res;
|
||||
|
||||
int DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned MsgSize);
|
||||
void DecodeNextAudioBlock(int64_t *Count);
|
||||
void Free(bool CloseCodec);
|
||||
public:
|
||||
FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFLAVFAudio();
|
||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
||||
FFLAVFAudio(const char *SourceFile, int Track, FFMS_Index *Index);
|
||||
void GetAudio(void *Buf, int64_t Start, int64_t Count);
|
||||
};
|
||||
|
||||
class FFMatroskaAudio : public FFAudio {
|
||||
class FFMatroskaAudio : public FFMS_AudioSource {
|
||||
private:
|
||||
MatroskaFile *MF;
|
||||
MatroskaReaderContext MC;
|
||||
CompressedStream *CS;
|
||||
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);
|
||||
public:
|
||||
FFMatroskaAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFMatroskaAudio();
|
||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index *Index);
|
||||
void GetAudio(void *Buf, int64_t Start, int64_t Count);
|
||||
};
|
||||
|
||||
#ifdef HAALISOURCE
|
||||
|
||||
class FFHaaliAudio : public FFAudio {
|
||||
class FFHaaliAudio : public FFMS_AudioSource {
|
||||
private:
|
||||
CComPtr<IMMContainer> pMMC;
|
||||
std::vector<uint8_t> CodecPrivate;
|
||||
FFSourceResources<FFMS_AudioSource> Res;
|
||||
|
||||
void DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count);
|
||||
void Free(bool CloseCodec);
|
||||
int DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count, char *ErrorMsg, unsigned MsgSize);
|
||||
public:
|
||||
FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index, int SourceMode, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFHaaliAudio();
|
||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
||||
FFHaaliAudio(const char *SourceFile, int Track, FFMS_Index *Index, enum FFMS_Sources SourceMode);
|
||||
void GetAudio(void *Buf, int64_t Start, int64_t Count);
|
||||
};
|
||||
|
||||
#endif // HAALISOURCE
|
|
@ -21,21 +21,22 @@
|
|||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "ffms.h"
|
||||
#include "ffvideosource.h"
|
||||
#include "ffaudiosource.h"
|
||||
#include "videosource.h"
|
||||
#include "audiosource.h"
|
||||
#include "indexing.h"
|
||||
|
||||
|
||||
|
||||
static bool FFmpegInited = false;
|
||||
static bool FFmpegInited = false;
|
||||
bool HasHaaliMPEG = false;
|
||||
bool HasHaaliOGG = false;
|
||||
int CPUFeatures = 0;
|
||||
|
||||
#ifdef FFMS_WIN_DEBUG
|
||||
|
||||
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 count;
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
OutputDebugStringA(line);
|
||||
|
@ -75,6 +78,13 @@ FFMS_API(void) FFMS_Init(int CPUFeatures) {
|
|||
av_log_set_level(AV_LOG_QUIET);
|
||||
#endif
|
||||
::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;
|
||||
}
|
||||
}
|
||||
|
@ -87,171 +97,224 @@ FFMS_API(void) FFMS_SetLogLevel(int 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) {
|
||||
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;
|
||||
}
|
||||
|
||||
FFMS_API(FFMS_VideoSource *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode, FFMS_ErrorInfo *ErrorInfo) {
|
||||
try {
|
||||
switch (Index->Decoder) {
|
||||
case 0: return new FFLAVFVideo(SourceFile, Track, Index, PP, Threads, SeekMode, ErrorMsg, MsgSize);
|
||||
case 1: return new FFMatroskaVideo(SourceFile, Track, Index, PP, Threads, ErrorMsg, MsgSize);
|
||||
case FFMS_SOURCE_LAVF:
|
||||
return new FFLAVFVideo(SourceFile, Track, Index, Threads, SeekMode);
|
||||
case FFMS_SOURCE_MATROSKA:
|
||||
return new FFMatroskaVideo(SourceFile, Track, Index, Threads);
|
||||
#ifdef HAALISOURCE
|
||||
case 2: return new FFHaaliVideo(SourceFile, Track, Index, PP, Threads, 0, ErrorMsg, MsgSize);
|
||||
case 3: return new FFHaaliVideo(SourceFile, Track, Index, PP, Threads, 1, ErrorMsg, MsgSize);
|
||||
case FFMS_SOURCE_HAALIMPEG:
|
||||
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
|
||||
default:
|
||||
snprintf(ErrorMsg, MsgSize, "Unsupported format");
|
||||
return NULL;
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Unsupported format");
|
||||
}
|
||||
} catch (...) {
|
||||
} catch (FFMS_Exception &e) {
|
||||
e.CopyOut(ErrorInfo);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
FFMS_API(FFAudio *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) {
|
||||
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;
|
||||
}
|
||||
|
||||
FFMS_API(FFMS_AudioSource *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo) {
|
||||
try {
|
||||
switch (Index->Decoder) {
|
||||
case 0: return new FFLAVFAudio(SourceFile, Track, Index, ErrorMsg, MsgSize);
|
||||
case 1: return new FFMatroskaAudio(SourceFile, Track, Index, ErrorMsg, MsgSize);
|
||||
case FFMS_SOURCE_LAVF:
|
||||
return new FFLAVFAudio(SourceFile, Track, Index);
|
||||
case FFMS_SOURCE_MATROSKA:
|
||||
return new FFMatroskaAudio(SourceFile, Track, Index);
|
||||
#ifdef HAALISOURCE
|
||||
case 2: return new FFHaaliAudio(SourceFile, Track, Index, 0, ErrorMsg, MsgSize);
|
||||
case 3: return new FFHaaliAudio(SourceFile, Track, Index, 1, ErrorMsg, MsgSize);
|
||||
case FFMS_SOURCE_HAALIMPEG:
|
||||
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
|
||||
default:
|
||||
snprintf(ErrorMsg, MsgSize, "Unsupported format");
|
||||
return NULL;
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Unsupported format");
|
||||
}
|
||||
} catch (...) {
|
||||
} catch (FFMS_Exception &e) {
|
||||
e.CopyOut(ErrorInfo);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
FFMS_API(void) FFMS_DestroyVideoSource(FFVideo *V) {
|
||||
FFMS_API(void) FFMS_DestroyVideoSource(FFMS_VideoSource *V) {
|
||||
delete V;
|
||||
}
|
||||
|
||||
FFMS_API(void) FFMS_DestroyAudioSource(FFAudio *A) {
|
||||
FFMS_API(void) FFMS_DestroyAudioSource(FFMS_AudioSource *A) {
|
||||
delete A;
|
||||
}
|
||||
|
||||
FFMS_API(const FFVideoProperties *) FFMS_GetVideoProperties(FFVideo *V) {
|
||||
return &V->GetFFVideoProperties();
|
||||
FFMS_API(const FFMS_VideoProperties *) FFMS_GetVideoProperties(FFMS_VideoSource *V) {
|
||||
return &V->GetVideoProperties();
|
||||
}
|
||||
|
||||
FFMS_API(const FFAudioProperties *) FFMS_GetAudioProperties(FFAudio *A) {
|
||||
return &A->GetFFAudioProperties();
|
||||
FFMS_API(const FFMS_AudioProperties *) FFMS_GetAudioProperties(FFMS_AudioSource *A) {
|
||||
return &A->GetAudioProperties();
|
||||
}
|
||||
|
||||
FFMS_API(const FFAVFrame *) FFMS_GetFrame(FFVideo *V, int n, char *ErrorMsg, unsigned MsgSize) {
|
||||
return (FFAVFrame *)V->GetFrame(n, ErrorMsg, MsgSize);
|
||||
FFMS_API(const FFMS_Frame *) FFMS_GetFrame(FFMS_VideoSource *V, int n, FFMS_ErrorInfo *ErrorInfo) {
|
||||
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) {
|
||||
return (FFAVFrame *)V->GetFrameByTime(Time, ErrorMsg, MsgSize);
|
||||
FFMS_API(const FFMS_Frame *) FFMS_GetFrameByTime(FFMS_VideoSource *V, double Time, FFMS_ErrorInfo *ErrorInfo) {
|
||||
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) {
|
||||
return A->GetAudio(Buf, Start, Count, ErrorMsg, MsgSize);
|
||||
FFMS_API(int) FFMS_GetAudio(FFMS_AudioSource *A, void *Buf, int64_t Start, int64_t Count, FFMS_ErrorInfo *ErrorInfo) {
|
||||
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) {
|
||||
return V->SetOutputFormat(TargetFormats, Width, Height, Resizer, ErrorMsg, MsgSize);
|
||||
FFMS_API(int) FFMS_SetOutputFormatV(FFMS_VideoSource *V, int64_t TargetFormats, int Width, int Height, int Resizer, FFMS_ErrorInfo *ErrorInfo) {
|
||||
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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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++)
|
||||
if ((*Index)[i].TT == TrackType)
|
||||
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++)
|
||||
if ((*Index)[i].TT == TrackType && (*Index)[i].size() > 0)
|
||||
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_GetNumTracks(FFIndex *Index) {
|
||||
FFMS_API(int) FFMS_GetNumTracks(FFMS_Index *Index) {
|
||||
return Index->size();
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_GetNumTracksI(FFIndexer *Indexer) {
|
||||
FFMS_API(int) FFMS_GetNumTracksI(FFMS_Indexer *Indexer) {
|
||||
return Indexer->GetNumberOfTracks();
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_GetTrackType(FFTrack *T) {
|
||||
FFMS_API(int) FFMS_GetTrackType(FFMS_Track *T) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_GetNumFrames(FFTrack *T) {
|
||||
FFMS_API(int) FFMS_GetNumFrames(FFMS_Track *T) {
|
||||
return T->size();
|
||||
}
|
||||
|
||||
FFMS_API(const FFFrameInfo *) FFMS_GetFrameInfo(FFTrack *T, int Frame) {
|
||||
return reinterpret_cast<FFFrameInfo *>(&(*T)[Frame]);
|
||||
FFMS_API(const FFMS_FrameInfo *) FFMS_GetFrameInfo(FFMS_Track *T, int 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];
|
||||
}
|
||||
|
||||
FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V) {
|
||||
return V->GetFFTrack();
|
||||
FFMS_API(FFMS_Track *) FFMS_GetTrackFromVideo(FFMS_VideoSource *V) {
|
||||
return V->GetTrack();
|
||||
}
|
||||
|
||||
FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A) {
|
||||
return A->GetFFTrack();
|
||||
FFMS_API(FFMS_Track *) FFMS_GetTrackFromAudio(FFMS_AudioSource *A) {
|
||||
return A->GetTrack();
|
||||
}
|
||||
|
||||
FFMS_API(const FFTrackTimeBase *) FFMS_GetTimeBase(FFTrack *T) {
|
||||
FFMS_API(const FFMS_TrackTimeBase *) FFMS_GetTimeBase(FFMS_Track *T) {
|
||||
return &T->TB;
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_WriteTimecodes(FFTrack *T, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize) {
|
||||
return T->WriteTimecodes(TimecodeFile, ErrorMsg, MsgSize);
|
||||
FFMS_API(int) FFMS_WriteTimecodes(FFMS_Track *T, const char *TimecodeFile, FFMS_ErrorInfo *ErrorInfo) {
|
||||
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) {
|
||||
FFIndexer *Indexer = FFMS_CreateIndexer(SourceFile, ErrorMsg, 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_Indexer *Indexer = FFMS_CreateIndexer(SourceFile, ErrorInfo);
|
||||
if (!Indexer)
|
||||
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 */
|
||||
|
@ -270,7 +333,7 @@ static void ReplaceString(std::string &s, std::string from, std::string 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);
|
||||
|
||||
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, "%delay%", IntToStr(static_cast<int>(AP->FirstTime)));
|
||||
|
||||
if (FileName == NULL) {
|
||||
return s.length() + 1;
|
||||
} else {
|
||||
if (FileName != NULL)
|
||||
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 {
|
||||
return FFIndexer::CreateFFIndexer(SourceFile, ErrorMsg, MsgSize);
|
||||
} catch (...) {
|
||||
return FFMS_Indexer::CreateIndexer(SourceFile);
|
||||
} catch (FFMS_Exception &e) {
|
||||
e.CopyOut(ErrorInfo);
|
||||
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->SetDumpMask(DumpMask);
|
||||
Indexer->SetIgnoreDecodeErrors(IgnoreDecodeErrors);
|
||||
Indexer->SetErrorHandling(ErrorHandling);
|
||||
Indexer->SetProgressCallback(IC, ICPrivate);
|
||||
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;
|
||||
return Index;
|
||||
}
|
||||
|
||||
FFMS_API(void) FFMS_CancelIndexing(FFIndexer *Indexer) {
|
||||
FFMS_API(void) FFMS_CancelIndexing(FFMS_Indexer *Indexer) {
|
||||
delete Indexer;
|
||||
}
|
||||
|
||||
FFMS_API(FFIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) {
|
||||
FFIndex *Index = new FFIndex();
|
||||
if (Index->ReadIndex(IndexFile, ErrorMsg, MsgSize)) {
|
||||
FFMS_API(FFMS_Index *) FFMS_ReadIndex(const char *IndexFile, FFMS_ErrorInfo *ErrorInfo) {
|
||||
ClearErrorInfo(ErrorInfo);
|
||||
FFMS_Index *Index = new FFMS_Index();
|
||||
try {
|
||||
Index->ReadIndex(IndexFile);
|
||||
} catch (FFMS_Exception &e) {
|
||||
delete Index;
|
||||
e.CopyOut(ErrorInfo);
|
||||
return NULL;
|
||||
} else {
|
||||
return Index;
|
||||
}
|
||||
return Index;
|
||||
}
|
||||
|
||||
FFMS_API(int) FFMS_IndexBelongsToFile(FFIndex *Index, const char *SourceFile, char *ErrorMsg, unsigned MsgSize) {
|
||||
return Index->CompareFileSignature(SourceFile, ErrorMsg, MsgSize);
|
||||
FFMS_API(int) FFMS_IndexBelongsToFile(FFMS_Index *Index, const char *SourceFile, FFMS_ErrorInfo *ErrorInfo) {
|
||||
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) {
|
||||
return Index->WriteIndex(IndexFile, ErrorMsg, MsgSize);
|
||||
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo) {
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -116,10 +116,10 @@ DEFINE_GUID(MEDIASUBTYPE_WMV3,
|
|||
0x33564d57, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
|
||||
|
||||
// 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);
|
||||
|
||||
DEFINE_GUID(HAALI_OGM_Parser,
|
||||
DEFINE_GUID(HAALI_OGG_PARSER,
|
||||
0xDB43B405, 0x43AA, 0x4F01, 0x82, 0xD8, 0xD8, 0x4D, 0x47, 0xE6, 0x01, 0x9C);
|
||||
|
||||
//DB43B405-43AA-4F01-82D8-D84D47E6019C
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
|
||||
#ifdef HAALISOURCE
|
||||
|
||||
#include "ffaudiosource.h"
|
||||
#include "audiosource.h"
|
||||
|
||||
|
||||
|
||||
void FFHaaliAudio::Free(bool CloseCodec) {
|
||||
if (CloseCodec)
|
||||
|
@ -28,14 +30,14 @@ void FFHaaliAudio::Free(bool CloseCodec) {
|
|||
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;
|
||||
int Ret = -1;
|
||||
*AFirstStartTime = -1;
|
||||
*Count = 0;
|
||||
uint8_t *Buf = &DecodingBuffer[0];
|
||||
AVPacket Packet;
|
||||
InitNullPacket(&Packet);
|
||||
InitNullPacket(Packet);
|
||||
|
||||
for (;;) {
|
||||
CComPtr<IMMFrame> pMMF;
|
||||
|
@ -79,56 +81,17 @@ int FFHaaliAudio::DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count,
|
|||
}
|
||||
}
|
||||
|
||||
Done:
|
||||
return Ret;
|
||||
Done:;
|
||||
}
|
||||
|
||||
FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index,
|
||||
int SourceMode, char *ErrorMsg, unsigned MsgSize)
|
||||
: FFAudio(SourceFile, Index, ErrorMsg, MsgSize) {
|
||||
FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFMS_Index *Index, enum FFMS_Sources SourceMode)
|
||||
: Res(FFSourceResources<FFMS_AudioSource>(this)), FFMS_AudioSource(SourceFile, Index, Track) {
|
||||
AVCodec *Codec = NULL;
|
||||
CodecContext = NULL;
|
||||
AudioTrack = Track;
|
||||
Frames = (*Index)[AudioTrack];
|
||||
|
||||
if (Frames.size() == 0) {
|
||||
snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
CLSID clsid = HAALI_TS_Parser;
|
||||
if (SourceMode == 1)
|
||||
clsid = HAALI_OGM_Parser;
|
||||
|
||||
if (FAILED(pMMC.CoCreateInstance(clsid))) {
|
||||
snprintf(ErrorMsg, MsgSize, "Can't create parser");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
CComPtr<IMemAlloc> pMA;
|
||||
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
|
||||
snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
CComPtr<IMMStream> pMS;
|
||||
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) {
|
||||
snprintf(ErrorMsg, MsgSize, "Can't create disk file reader");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
WCHAR WSourceFile[2048];
|
||||
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;
|
||||
}
|
||||
pMMC = HaaliOpenFile(SourceFile, SourceMode);
|
||||
|
||||
int CodecPrivateSize = 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))) {
|
||||
char ACodecID[2048];
|
||||
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_size = CodecPrivateSize;
|
||||
|
||||
if (Codec == NULL) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (Codec == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Audio codec not found");
|
||||
|
||||
InitializeCodecContextFromHaaliInfo(pBag, CodecContext);
|
||||
|
||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (avcodec_open(CodecContext, Codec) < 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Could not open audio codec");
|
||||
|
||||
Res.CloseCodec(true);
|
||||
|
||||
// Always try to decode a frame to make sure all required parameters are known
|
||||
int64_t Dummy1, Dummy2;
|
||||
if (DecodeNextAudioBlock(&Dummy1, &Dummy2, ErrorMsg, MsgSize) < 0) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
DecodeNextAudioBlock(&Dummy1, &Dummy2);
|
||||
|
||||
pMMC->Seek(Frames[0].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT);
|
||||
avcodec_flush_buffers(CodecContext);
|
||||
|
||||
FillAP(AP, CodecContext, Frames);
|
||||
|
||||
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) {
|
||||
Free(true);
|
||||
snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Codec returned zero size audio");
|
||||
|
||||
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
|
||||
}
|
||||
|
||||
FFHaaliAudio::~FFHaaliAudio() {
|
||||
Free(true);
|
||||
}
|
||||
void FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) {
|
||||
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;
|
||||
memset(Buf, 0, static_cast<size_t>(SizeConst * Count));
|
||||
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);
|
||||
// Was everything in the cache?
|
||||
if (CacheEnd == Start + Count)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
int CurrentAudioBlock;
|
||||
// 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;
|
||||
|
||||
do {
|
||||
int Ret = DecodeNextAudioBlock(&FirstTime, &DecodeCount, ErrorMsg, MsgSize);
|
||||
if (Ret < 0) {
|
||||
// FIXME
|
||||
//Env->ThrowError("Bleh, bad audio decoding");
|
||||
}
|
||||
DecodeNextAudioBlock(&FirstTime, &DecodeCount);
|
||||
|
||||
if (HasSeeked) {
|
||||
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()))
|
||||
CurrentSample = Frames[CurrentAudioBlock].SampleStart;
|
||||
} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < static_cast<int>(Frames.size()));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // HAALISOURCE
|
|
@ -24,51 +24,17 @@
|
|||
|
||||
|
||||
|
||||
FFHaaliIndexer::FFHaaliIndexer(const char *Filename, int SourceMode, char *ErrorMsg, unsigned MsgSize) : FFIndexer(Filename, ErrorMsg, MsgSize) {
|
||||
SourceFile = Filename;
|
||||
FFHaaliIndexer::FFHaaliIndexer(const char *Filename, enum FFMS_Sources SourceMode) : FFMS_Indexer(Filename) {
|
||||
this->SourceMode = SourceMode;
|
||||
memset(TrackType, FFMS_TYPE_UNKNOWN, sizeof(TrackType));
|
||||
memset(Codec, 0, sizeof(Codec));
|
||||
memset(CodecPrivate, 0, sizeof(CodecPrivate));
|
||||
memset(CodecPrivateSize, 0, sizeof(CodecPrivateSize));
|
||||
SourceFile = Filename;
|
||||
Duration = 0;
|
||||
|
||||
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;
|
||||
for (int i = 0; i < 32; i++) {
|
||||
TrackType[i] = FFMS_TYPE_UNKNOWN;
|
||||
Codec[i] = NULL;
|
||||
CodecPrivateSize[i] = 0;
|
||||
}
|
||||
|
||||
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))) {
|
||||
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;
|
||||
}
|
||||
pMMC = HaaliOpenFile(SourceFile, SourceMode);
|
||||
|
||||
CComQIPtr<IPropertyBag> pBag2 = pMMC;
|
||||
CComVariant pV2;
|
||||
|
@ -99,14 +65,42 @@ FFHaaliIndexer::FFHaaliIndexer(const char *Filename, int SourceMode, char *Error
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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();
|
||||
if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) {
|
||||
char CodecID[2048];
|
||||
wcstombs(CodecID, pV.bstrVal, 2000);
|
||||
Codec[NumTracks] = avcodec_find_decoder(MatroskaToFFCodecID(CodecID, FFMS_GET_VECTOR_PTR(CodecPrivate[NumTracks]), FourCC));
|
||||
char CodecStr[2048];
|
||||
wcstombs(CodecStr, pV.bstrVal, 2000);
|
||||
|
||||
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<SharedVideoContext> VideoContexts(NumTracks, SharedVideoContext(true));
|
||||
|
||||
std::auto_ptr<FFIndex> TrackIndices(new FFIndex(Filesize, Digest));
|
||||
TrackIndices->Decoder = 2;
|
||||
if (SourceMode == 1)
|
||||
TrackIndices->Decoder = 3;
|
||||
std::auto_ptr<FFMS_Index> TrackIndices(new FFMS_Index(Filesize, Digest));
|
||||
TrackIndices->Decoder = FFMS_SOURCE_HAALIMPEG;
|
||||
if (SourceMode == FFMS_SOURCE_HAALIOGG)
|
||||
TrackIndices->Decoder = FFMS_SOURCE_HAALIOGG;
|
||||
|
||||
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))) {
|
||||
|
||||
|
@ -138,8 +132,8 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
|||
|
||||
if (avcodec_open(CodecContext, Codec[i]) < 0) {
|
||||
av_freep(&CodecContext);
|
||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
||||
return NULL;
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||
"Could not open video codec");
|
||||
}
|
||||
|
||||
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 (Codec[i] == NULL) {
|
||||
snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
||||
return NULL;
|
||||
}
|
||||
if (Codec[i] == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||
"Audio codec not found");
|
||||
|
||||
AVCodecContext *CodecContext = avcodec_alloc_context();
|
||||
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) {
|
||||
av_freep(&CodecContext);
|
||||
AudioContexts[i].CodecContext = NULL;
|
||||
snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
||||
return NULL;
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||
"Could not open audio codec");
|
||||
}
|
||||
} else {
|
||||
IndexMask &= ~(1 << i);
|
||||
|
@ -170,7 +163,7 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
|||
//
|
||||
|
||||
AVPacket TempPacket;
|
||||
InitNullPacket(&TempPacket);
|
||||
InitNullPacket(TempPacket);
|
||||
|
||||
for (;;) {
|
||||
CComPtr<IMMFrame> pMMF;
|
||||
|
@ -183,16 +176,14 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
|||
if (IC) {
|
||||
if (Duration > 0) {
|
||||
if (SUCCEEDED(hr)) {
|
||||
if ((*IC)(Ts, Duration, ICPrivate)) {
|
||||
snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
||||
return NULL;
|
||||
}
|
||||
if ((*IC)(Ts, Duration, ICPrivate))
|
||||
throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
|
||||
"Cancelled by user");
|
||||
}
|
||||
} else {
|
||||
if ((*IC)(0, 1, ICPrivate)) {
|
||||
snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
||||
return NULL;
|
||||
}
|
||||
if ((*IC)(0, 1, ICPrivate))
|
||||
throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
|
||||
"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));
|
||||
} 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;
|
||||
|
||||
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 Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
|
||||
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();
|
||||
IndexMask &= ~(1 << Track);
|
||||
break;
|
||||
} else {
|
||||
snprintf(ErrorMsg, MsgSize, "Audio decoding error");
|
||||
return NULL;
|
||||
} else if (ErrorHandling == FFMS_IEH_STOP_TRACK) {
|
||||
IndexMask &= ~(1 << Track);
|
||||
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);
|
||||
|
||||
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) {
|
||||
if (Codec[Track])
|
||||
return Codec[Track]->long_name;
|
||||
return Codec[Track]->name;
|
||||
else
|
||||
return "Unsupported codec/Unknown codec name";
|
||||
}
|
|
@ -18,10 +18,12 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "ffvideosource.h"
|
||||
|
||||
#ifdef HAALISOURCE
|
||||
|
||||
#include "videosource.h"
|
||||
|
||||
|
||||
|
||||
void FFHaaliVideo::Free(bool CloseCodec) {
|
||||
if (CloseCodec)
|
||||
avcodec_close(CodecContext);
|
||||
|
@ -31,9 +33,8 @@ void FFHaaliVideo::Free(bool CloseCodec) {
|
|||
}
|
||||
|
||||
FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
||||
FFIndex *Index, const char *PP,
|
||||
int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize)
|
||||
: FFVideo(SourceFile, Index, ErrorMsg, MsgSize) {
|
||||
FFMS_Index *Index, int Threads, enum FFMS_Sources SourceMode)
|
||||
: Res(FFSourceResources<FFMS_VideoSource>(this)), FFMS_VideoSource(SourceFile, Index, Track) {
|
||||
|
||||
BitStreamFilter = NULL;
|
||||
AVCodec *Codec = NULL;
|
||||
|
@ -41,44 +42,7 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
|||
VideoTrack = Track;
|
||||
Frames = (*Index)[VideoTrack];
|
||||
|
||||
if (Frames.size() == 0) {
|
||||
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;
|
||||
}
|
||||
pMMC = HaaliOpenFile(SourceFile, SourceMode);
|
||||
|
||||
int CodecPrivateSize = 0;
|
||||
int CurrentTrack = 0;
|
||||
|
@ -102,8 +66,30 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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();
|
||||
if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) {
|
||||
|
@ -122,51 +108,39 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
|||
CodecContext->extradata_size = CodecPrivateSize;
|
||||
CodecContext->thread_count = Threads;
|
||||
|
||||
if (Codec == NULL) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Video codec not found");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (Codec == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Video codec not found");
|
||||
|
||||
InitializeCodecContextFromHaaliInfo(pBag, CodecContext);
|
||||
|
||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (avcodec_open(CodecContext, Codec) < 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Could not open video codec");
|
||||
|
||||
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");
|
||||
|
||||
Res.CloseCodec(true);
|
||||
|
||||
// Always try to decode a frame to make sure all required parameters are known
|
||||
int64_t Dummy;
|
||||
if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
DecodeNextFrame(&Dummy);
|
||||
|
||||
VP.Width = CodecContext->width;
|
||||
VP.Height = CodecContext->height;;
|
||||
VP.FPSDenominator = 1;
|
||||
VP.FPSNumerator = 30;
|
||||
VP.RFFDenominator = CodecContext->time_base.num;
|
||||
VP.RFFNumerator = CodecContext->time_base.den;
|
||||
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.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||
|
||||
if (VP.Width <= 0 || VP.Height <= 0) {
|
||||
Free(true);
|
||||
snprintf(ErrorMsg, MsgSize, "Codec returned zero size video");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (InitPP(PP, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (CodecContext->width <= 0 || CodecContext->height <= 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Codec returned zero size video");
|
||||
|
||||
// Calculate the average framerate
|
||||
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
|
||||
if (!OutputFrame(DecodeFrame, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
OutputFrame(DecodeFrame);
|
||||
|
||||
// Set AR variables
|
||||
CComVariant pV;
|
||||
|
@ -192,15 +163,11 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
|||
VP.SARDen = pV.uiVal;
|
||||
}
|
||||
|
||||
FFHaaliVideo::~FFHaaliVideo() {
|
||||
Free(true);
|
||||
}
|
||||
|
||||
int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) {
|
||||
void FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime) {
|
||||
int FrameFinished = 0;
|
||||
*AFirstStartTime = -1;
|
||||
AVPacket Packet;
|
||||
InitNullPacket(&Packet);
|
||||
InitNullPacket(Packet);
|
||||
|
||||
for (;;) {
|
||||
CComPtr<IMMFrame> pMMF;
|
||||
|
@ -227,6 +194,9 @@ int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsi
|
|||
av_bitstream_filter_filter(BitStreamFilter, CodecContext, NULL,
|
||||
&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);
|
||||
|
||||
if (FrameFinished)
|
||||
|
@ -237,7 +207,7 @@ int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsi
|
|||
// Flush the last frames
|
||||
if (CodecContext->has_b_frames) {
|
||||
AVPacket NullPacket;
|
||||
InitNullPacket(&NullPacket);
|
||||
InitNullPacket(NullPacket);
|
||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
|
||||
}
|
||||
|
||||
|
@ -245,14 +215,14 @@ int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsi
|
|||
goto Error;
|
||||
|
||||
Error:
|
||||
Done:
|
||||
return 0;
|
||||
Done:;
|
||||
}
|
||||
|
||||
FFAVFrame *FFHaaliVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) {
|
||||
// PPFrame always holds frame LastFrameNum even if no PP is applied
|
||||
FFMS_Frame *FFHaaliVideo::GetFrame(int n) {
|
||||
GetFrameCheck(n);
|
||||
|
||||
if (LastFrameNum == n)
|
||||
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize);
|
||||
return &LocalFrame;
|
||||
|
||||
bool HasSeeked = false;
|
||||
int SeekOffset = 0;
|
||||
|
@ -266,18 +236,16 @@ ReSeek:
|
|||
|
||||
do {
|
||||
int64_t StartTime;
|
||||
if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize))
|
||||
return NULL;
|
||||
DecodeNextFrame(&StartTime);
|
||||
|
||||
if (HasSeeked) {
|
||||
HasSeeked = false;
|
||||
|
||||
if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) {
|
||||
// No idea where we are so go back a bit further
|
||||
if (n + SeekOffset == 0) {
|
||||
snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file\n");
|
||||
return NULL;
|
||||
}
|
||||
if (n + SeekOffset == 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Frame accurate seeking is not possible in this file");
|
||||
|
||||
SeekOffset -= FFMIN(20, n + SeekOffset);
|
||||
goto ReSeek;
|
||||
|
@ -288,7 +256,7 @@ ReSeek:
|
|||
} while (CurrentFrame <= n);
|
||||
|
||||
LastFrameNum = n;
|
||||
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize);
|
||||
return OutputFrame(DecodeFrame);
|
||||
}
|
||||
|
||||
#endif // HAALISOURCE
|
|
@ -22,13 +22,15 @@
|
|||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
#include <libavutil/sha1.h>
|
||||
}
|
||||
|
||||
#define INDEXID 0x53920873
|
||||
|
||||
extern bool HasHaaliMPEG;
|
||||
extern bool HasHaaliOGG;
|
||||
|
||||
struct IndexHeader {
|
||||
uint32_t Id;
|
||||
|
@ -44,6 +46,13 @@ struct IndexHeader {
|
|||
uint8_t FileSignature[20];
|
||||
};
|
||||
|
||||
struct TrackHeader {
|
||||
uint32_t TT;
|
||||
uint32_t Frames;
|
||||
int64_t Num;
|
||||
int64_t Den;
|
||||
};
|
||||
|
||||
SharedVideoContext::SharedVideoContext(bool FreeCodecContext) {
|
||||
CodecContext = NULL;
|
||||
Parser = NULL;
|
||||
|
@ -85,47 +94,49 @@ SharedAudioContext::~SharedAudioContext() {
|
|||
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->RepeatPict = RepeatPict;
|
||||
this->KeyFrame = KeyFrame;
|
||||
this->SampleStart = SampleStart;
|
||||
this->SampleCount = SampleCount;
|
||||
this->FilePos = FilePos;
|
||||
this->FrameSize = FrameSize;
|
||||
this->OriginalPos = 0;
|
||||
}
|
||||
|
||||
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) {
|
||||
return TFrameInfo(DTS, SampleStart, 0, KeyFrame, FilePos, 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, 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);
|
||||
|
||||
if (!Timecodes.is_open()) {
|
||||
snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for writing", TimecodeFile);
|
||||
return 1;
|
||||
}
|
||||
if (!Timecodes.is_open())
|
||||
throw FFMS_Exception(FFMS_ERROR_TRACK, FFMS_ERROR_FILE_WRITE,
|
||||
boost::format("Failed to open '%1%' for writing") % TimecodeFile);
|
||||
|
||||
Timecodes << "# timecode format v2\n";
|
||||
|
||||
for (iterator Cur=begin(); Cur!=end(); Cur++)
|
||||
Timecodes << std::fixed << ((Cur->DTS * TB.Num) / (double)TB.Den) << "\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FFTrack::FrameFromDTS(int64_t DTS) {
|
||||
int FFMS_Track::FrameFromDTS(int64_t DTS) {
|
||||
for (int i = 0; i < static_cast<int>(size()); i++)
|
||||
if (at(i).DTS == DTS)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int FFTrack::ClosestFrameFromDTS(int64_t DTS) {
|
||||
int FFMS_Track::ClosestFrameFromDTS(int64_t DTS) {
|
||||
int Frame = 0;
|
||||
int64_t BestDiff = 0xFFFFFFFFFFFFFFLL; // big number
|
||||
for (int i = 0; i < static_cast<int>(size()); i++) {
|
||||
|
@ -139,7 +150,7 @@ int FFTrack::ClosestFrameFromDTS(int64_t DTS) {
|
|||
return Frame;
|
||||
}
|
||||
|
||||
int FFTrack::FindClosestVideoKeyFrame(int Frame) {
|
||||
int FFMS_Track::FindClosestVideoKeyFrame(int Frame) {
|
||||
Frame = FFMIN(FFMAX(Frame, 0), static_cast<int>(size()) - 1);
|
||||
for (int i = Frame; i > 0; i--)
|
||||
if (at(i).KeyFrame)
|
||||
|
@ -147,7 +158,7 @@ int FFTrack::FindClosestVideoKeyFrame(int Frame) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int FFTrack::FindClosestAudioKeyFrame(int64_t Sample) {
|
||||
int FFMS_Track::FindClosestAudioKeyFrame(int64_t Sample) {
|
||||
for (size_t i = 0; i < size(); i++) {
|
||||
if (at(i).SampleStart == Sample && at(i).KeyFrame)
|
||||
return i;
|
||||
|
@ -157,29 +168,26 @@ int FFTrack::FindClosestAudioKeyFrame(int64_t Sample) {
|
|||
return size() - 1;
|
||||
}
|
||||
|
||||
FFTrack::FFTrack() {
|
||||
FFMS_Track::FFMS_Track() {
|
||||
this->TT = FFMS_TYPE_UNKNOWN;
|
||||
this->TB.Num = 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->TB.Num = Num;
|
||||
this->TB.Den = Den;
|
||||
}
|
||||
|
||||
int FFIndex::CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20], char *ErrorMsg, unsigned MsgSize) {
|
||||
// use cstdio because Microsoft's implementation of std::fstream doesn't support files >4GB.
|
||||
// please kill me now.
|
||||
void FFMS_Index::CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20]) {
|
||||
FILE *SFile = ffms_fopen(Filename,"rb");
|
||||
|
||||
if (SFile == NULL) {
|
||||
snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for hashing", Filename);
|
||||
return 1;
|
||||
}
|
||||
if (SFile == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ,
|
||||
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> ctxmem(av_sha1_size);
|
||||
AVSHA1 *ctx = (AVSHA1 *)&ctxmem[0];
|
||||
|
@ -188,10 +196,10 @@ int FFIndex::CalculateFileSignature(const char *Filename, int64_t *Filesize, uin
|
|||
memset(&FileBuffer[0], 0, BlockSize);
|
||||
fread(&FileBuffer[0], 1, BlockSize, SFile);
|
||||
if (ferror(SFile) && !feof(SFile)) {
|
||||
snprintf(ErrorMsg, MsgSize, "Failed to read from '%s' for hashing", Filename);
|
||||
av_sha1_final(ctx, Digest);
|
||||
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);
|
||||
|
||||
|
@ -199,61 +207,67 @@ int FFIndex::CalculateFileSignature(const char *Filename, int64_t *Filesize, uin
|
|||
memset(&FileBuffer[0], 0, BlockSize);
|
||||
fread(&FileBuffer[0], 1, BlockSize, 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);
|
||||
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);
|
||||
|
||||
fseeko(SFile, 0, SEEK_END);
|
||||
if (ferror(SFile)) {
|
||||
snprintf(ErrorMsg, MsgSize, "Failed to seek to end of '%s' for hashing", Filename);
|
||||
av_sha1_final(ctx, Digest);
|
||||
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);
|
||||
fclose(SFile);
|
||||
|
||||
av_sha1_final(ctx, Digest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool DTSComparison(TFrameInfo FI1, TFrameInfo FI2) {
|
||||
return FI1.DTS < FI2.DTS;
|
||||
}
|
||||
|
||||
void FFIndex::Sort() {
|
||||
for (FFIndex::iterator Cur=begin(); Cur!=end(); Cur++)
|
||||
void FFMS_Index::Sort() {
|
||||
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::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;
|
||||
uint8_t CDigest[20];
|
||||
CalculateFileSignature(Filename, &CFilesize, CDigest, ErrorMsg, MsgSize);
|
||||
|
||||
if (CFilesize != Filesize || memcmp(CDigest, Digest, sizeof(Digest))) {
|
||||
snprintf(ErrorMsg, MsgSize, "Index and source file signature mismatch");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
CalculateFileSignature(Filename, &CFilesize, CDigest);
|
||||
return (CFilesize == Filesize && !memcmp(CDigest, Digest, sizeof(Digest)));
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!IndexStream.is_open()) {
|
||||
snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for writing", IndexFile);
|
||||
return 1;
|
||||
}
|
||||
if (!IndexStream.is_open())
|
||||
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_WRITE,
|
||||
boost::format("Failed to open '%1%' for writing") % IndexFile);
|
||||
|
||||
// Write the index file header
|
||||
IndexHeader IH;
|
||||
IH.Id = INDEXID;
|
||||
IH.Version = INDEXVERSION;
|
||||
IH.Version = FFMS_VERSION;
|
||||
IH.Tracks = size();
|
||||
IH.Decoder = Decoder;
|
||||
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));
|
||||
|
||||
for (unsigned int i = 0; i < IH.Tracks; i++) {
|
||||
uint32_t TT = at(i).TT;
|
||||
IndexStream.write(reinterpret_cast<char *>(&TT), sizeof(TT));
|
||||
int64_t Num = at(i).TB.Num;
|
||||
IndexStream.write(reinterpret_cast<char *>(&Num), sizeof(Num));
|
||||
int64_t Den = at(i).TB.Den;
|
||||
IndexStream.write(reinterpret_cast<char *>(&Den), sizeof(Den));
|
||||
int64_t Frames = at(i).size();
|
||||
IndexStream.write(reinterpret_cast<char *>(&Frames), sizeof(Frames));
|
||||
TrackHeader TH;
|
||||
TH.TT = at(i).TT;
|
||||
TH.Frames = at(i).size();
|
||||
TH.Num = at(i).TB.Num;;
|
||||
TH.Den = at(i).TB.Den;
|
||||
|
||||
for (FFTrack::iterator Cur=at(i).begin(); Cur!=at(i).end(); Cur++)
|
||||
IndexStream.write(reinterpret_cast<char *>(&*Cur), sizeof(TFrameInfo));
|
||||
IndexStream.write(reinterpret_cast<char *>(&TH), sizeof(TH));
|
||||
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);
|
||||
|
||||
if (!Index.is_open()) {
|
||||
snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for reading", IndexFile);
|
||||
return 1;
|
||||
}
|
||||
if (!Index.is_open())
|
||||
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ,
|
||||
boost::format("Failed to open '%1%' for reading") % IndexFile);
|
||||
|
||||
// Read the index file header
|
||||
IndexHeader IH;
|
||||
Index.read(reinterpret_cast<char *>(&IH), sizeof(IH));
|
||||
if (IH.Id != INDEXID) {
|
||||
snprintf(ErrorMsg, MsgSize, "'%s' is not a valid index file", IndexFile);
|
||||
return 2;
|
||||
}
|
||||
if (IH.Id != INDEXID)
|
||||
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
|
||||
boost::format("'%1%' is not a valid index file") % IndexFile);
|
||||
|
||||
if (IH.Version != INDEXVERSION) {
|
||||
snprintf(ErrorMsg, MsgSize, "'%s' is not the expected index version", IndexFile);
|
||||
return 3;
|
||||
}
|
||||
if (IH.Version != FFMS_VERSION)
|
||||
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_VERSION,
|
||||
boost::format("'%1%' is not the expected index version") % IndexFile);
|
||||
|
||||
if (IH.LAVUVersion != avutil_version() || IH.LAVFVersion != avformat_version() ||
|
||||
IH.LAVCVersion != avcodec_version() || IH.LSWSVersion != swscale_version() ||
|
||||
IH.LPPVersion != postproc_version()) {
|
||||
snprintf(ErrorMsg, MsgSize, "A different FFmpeg build was used to create '%s'", IndexFile);
|
||||
return 4;
|
||||
}
|
||||
IH.LPPVersion != postproc_version())
|
||||
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_VERSION,
|
||||
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;
|
||||
Filesize = IH.FileSize;
|
||||
memcpy(Digest, IH.FileSignature, sizeof(Digest));
|
||||
|
||||
try {
|
||||
|
||||
for (unsigned int i = 0; i < IH.Tracks; i++) {
|
||||
// Read how many records belong to the current stream
|
||||
uint32_t TT;
|
||||
Index.read(reinterpret_cast<char *>(&TT), sizeof(TT));
|
||||
int64_t Num;
|
||||
Index.read(reinterpret_cast<char *>(&Num), sizeof(Num));
|
||||
int64_t Den;
|
||||
Index.read(reinterpret_cast<char *>(&Den), sizeof(Den));
|
||||
int64_t Frames;
|
||||
Index.read(reinterpret_cast<char *>(&Frames), sizeof(Frames));
|
||||
push_back(FFTrack(Num, Den, static_cast<FFMS_TrackType>(TT)));
|
||||
TrackHeader TH;
|
||||
Index.read(reinterpret_cast<char *>(&TH), sizeof(TH));
|
||||
push_back(FFMS_Track(TH.Num, TH.Den, static_cast<FFMS_TrackType>(TH.TT)));
|
||||
|
||||
TFrameInfo FI = TFrameInfo::VideoFrameInfo(0, 0, false);
|
||||
for (size_t j = 0; j < Frames; j++) {
|
||||
Index.read(reinterpret_cast<char *>(&FI), sizeof(TFrameInfo));
|
||||
at(i).push_back(FI);
|
||||
if (TH.Frames) {
|
||||
at(i).resize(TH.Frames);
|
||||
Index.read(reinterpret_cast<char *>(FFMS_GET_VECTOR_PTR(at(i))), TH.Frames * sizeof(TFrameInfo));
|
||||
}
|
||||
}
|
||||
|
||||
} catch (...) {
|
||||
snprintf(ErrorMsg, MsgSize, "Unknown error while reading index information in '%s'", IndexFile);
|
||||
return 5;
|
||||
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_UNKNOWN,
|
||||
boost::format("Unknown error while reading index information in '%1%'") % IndexFile);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FFIndex::FFIndex() {
|
||||
FFMS_Index::FFMS_Index() {
|
||||
// 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;
|
||||
memcpy(this->Digest, Digest, sizeof(this->Digest));
|
||||
}
|
||||
|
||||
void FFIndexer::SetIndexMask(int IndexMask) {
|
||||
void FFMS_Indexer::SetIndexMask(int IndexMask) {
|
||||
this->IndexMask = IndexMask;
|
||||
}
|
||||
|
||||
void FFIndexer::SetDumpMask(int DumpMask) {
|
||||
void FFMS_Indexer::SetDumpMask(int DumpMask) {
|
||||
this->DumpMask = DumpMask;
|
||||
}
|
||||
|
||||
void FFIndexer::SetIgnoreDecodeErrors(bool IgnoreDecodeErrors) {
|
||||
this->IgnoreDecodeErrors = IgnoreDecodeErrors;
|
||||
void FFMS_Indexer::SetErrorHandling(int ErrorHandling) {
|
||||
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->ICPrivate = ICPrivate;
|
||||
}
|
||||
|
||||
void FFIndexer::SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate) {
|
||||
void FFMS_Indexer::SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate) {
|
||||
this->ANC = ANC;
|
||||
this->ANCPrivate = ANCPrivate;
|
||||
}
|
||||
|
||||
FFIndexer *FFIndexer::CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) {
|
||||
FFMS_Indexer *FFMS_Indexer::CreateIndexer(const char *Filename) {
|
||||
AVFormatContext *FormatContext = NULL;
|
||||
|
||||
if (av_open_input_file(&FormatContext, Filename, NULL, 0, NULL) != 0) {
|
||||
snprintf(ErrorMsg, MsgSize, "Can't open '%s'", Filename);
|
||||
return NULL;
|
||||
}
|
||||
if (av_open_input_file(&FormatContext, Filename, NULL, 0, NULL) != 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||
boost::format("Can't open '%1%'") % Filename);
|
||||
|
||||
// Do matroska indexing instead?
|
||||
if (!strcmp(FormatContext->iformat->name, "matroska")) {
|
||||
av_close_input_file(FormatContext);
|
||||
return new FFMatroskaIndexer(Filename, ErrorMsg, MsgSize);
|
||||
return new FFMatroskaIndexer(Filename);
|
||||
}
|
||||
|
||||
#ifdef HAALISOURCE
|
||||
// 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);
|
||||
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);
|
||||
return new FFHaaliIndexer(Filename, 1, ErrorMsg, MsgSize);
|
||||
return new FFHaaliIndexer(Filename, FFMS_SOURCE_HAALIOGG);
|
||||
}
|
||||
#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) {
|
||||
if (FFIndex::CalculateFileSignature(Filename, &Filesize, Digest, ErrorMsg, MsgSize))
|
||||
throw ErrorMsg;
|
||||
FFMS_Indexer::FFMS_Indexer(const char *Filename) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 5) {
|
||||
IndexMask = 0;
|
||||
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.
|
||||
if (DBSize > 0) {
|
||||
if (!AudioContext.W64Writer) {
|
||||
FFAudioProperties AP;
|
||||
FFMS_AudioProperties AP;
|
||||
FillAP(AP, AudioContext.CodecContext, (*Index)[Track]);
|
||||
int FNSize = (*ANC)(SourceFile, Track, &AP, NULL, 0, ANCPrivate);
|
||||
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.CodecContext->channels, AudioContext.CodecContext->sample_rate, (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_FLT) || (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_DBL));
|
||||
} catch (...) {
|
||||
snprintf(ErrorMsg, MsgSize, "Failed to write wave data");
|
||||
return false;
|
||||
throw FFMS_Exception(FFMS_ERROR_WAVE_WRITER, FFMS_ERROR_FILE_WRITE,
|
||||
"Failed to write wave data");
|
||||
}
|
||||
}
|
||||
|
||||
AudioContext.W64Writer->WriteData(&DecodingBuffer[0], DBSize);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -37,8 +37,7 @@
|
|||
# include "guids.h"
|
||||
#endif
|
||||
|
||||
#define INDEXVERSION 28
|
||||
#define INDEXID 0x53920873
|
||||
|
||||
|
||||
class SharedVideoContext {
|
||||
private:
|
||||
|
@ -69,52 +68,55 @@ struct TFrameInfo {
|
|||
public:
|
||||
FFMS_FRAMEINFO_COMMON
|
||||
int64_t SampleStart;
|
||||
unsigned int SampleCount;
|
||||
int64_t FilePos;
|
||||
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 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:
|
||||
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:
|
||||
FFMS_TrackType TT;
|
||||
FFTrackTimeBase TB;
|
||||
FFMS_TrackTimeBase TB;
|
||||
|
||||
int FindClosestVideoKeyFrame(int Frame);
|
||||
int FindClosestAudioKeyFrame(int64_t Sample);
|
||||
int FrameFromDTS(int64_t DTS);
|
||||
int ClosestFrameFromDTS(int64_t DTS);
|
||||
int WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize);
|
||||
void WriteTimecodes(const char *TimecodeFile);
|
||||
|
||||
FFTrack();
|
||||
FFTrack(int64_t Num, int64_t Den, FFMS_TrackType TT);
|
||||
FFMS_Track();
|
||||
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:
|
||||
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;
|
||||
int64_t Filesize;
|
||||
uint8_t Digest[20];
|
||||
|
||||
void Sort();
|
||||
int CompareFileSignature(const char *Filename, char *ErrorMsg, unsigned MsgSize);
|
||||
int WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize);
|
||||
int ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize);
|
||||
bool CompareFileSignature(const char *Filename);
|
||||
void WriteIndex(const char *IndexFile);
|
||||
void ReadIndex(const char *IndexFile);
|
||||
|
||||
FFIndex();
|
||||
FFIndex(int64_t Filesize, uint8_t Digest[20]);
|
||||
FFMS_Index();
|
||||
FFMS_Index(int64_t Filesize, uint8_t Digest[20]);
|
||||
};
|
||||
|
||||
class FFIndexer {
|
||||
class FFMS_Indexer {
|
||||
protected:
|
||||
int IndexMask;
|
||||
int DumpMask;
|
||||
bool IgnoreDecodeErrors;
|
||||
int ErrorHandling;
|
||||
TIndexCallback IC;
|
||||
void *ICPrivate;
|
||||
TAudioNameCallback ANC;
|
||||
|
@ -125,43 +127,43 @@ protected:
|
|||
int64_t Filesize;
|
||||
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:
|
||||
static FFIndexer *CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize);
|
||||
FFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize);
|
||||
virtual ~FFIndexer();
|
||||
static FFMS_Indexer *CreateIndexer(const char *Filename);
|
||||
FFMS_Indexer(const char *Filename);
|
||||
virtual ~FFMS_Indexer();
|
||||
void SetIndexMask(int IndexMask);
|
||||
void SetDumpMask(int DumpMask);
|
||||
void SetIgnoreDecodeErrors(bool IgnoreDecodeErrors);
|
||||
void SetErrorHandling(int ErrorHandling);
|
||||
void SetProgressCallback(TIndexCallback IC, void *ICPrivate);
|
||||
void SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate);
|
||||
virtual FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize) = 0;
|
||||
virtual FFMS_Index *DoIndexing() = 0;
|
||||
virtual int GetNumberOfTracks() = 0;
|
||||
virtual FFMS_TrackType GetTrackType(int Track) = 0;
|
||||
virtual const char *GetTrackCodec(int Track) = 0;
|
||||
};
|
||||
|
||||
class FFLAVFIndexer : public FFIndexer {
|
||||
class FFLAVFIndexer : public FFMS_Indexer {
|
||||
private:
|
||||
AVFormatContext *FormatContext;
|
||||
public:
|
||||
FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize);
|
||||
FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext);
|
||||
~FFLAVFIndexer();
|
||||
FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_Index *DoIndexing();
|
||||
int GetNumberOfTracks();
|
||||
FFMS_TrackType GetTrackType(int Track);
|
||||
const char *GetTrackCodec(int Track);
|
||||
};
|
||||
|
||||
class FFMatroskaIndexer : public FFIndexer {
|
||||
class FFMatroskaIndexer : public FFMS_Indexer {
|
||||
private:
|
||||
MatroskaFile *MF;
|
||||
MatroskaReaderContext MC;
|
||||
AVCodec *Codec[32];
|
||||
public:
|
||||
FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMatroskaIndexer(const char *Filename);
|
||||
~FFMatroskaIndexer();
|
||||
FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_Index *DoIndexing();
|
||||
int GetNumberOfTracks();
|
||||
FFMS_TrackType GetTrackType(int Track);
|
||||
const char *GetTrackCodec(int Track);
|
||||
|
@ -169,7 +171,7 @@ public:
|
|||
|
||||
#ifdef HAALISOURCE
|
||||
|
||||
class FFHaaliIndexer : public FFIndexer {
|
||||
class FFHaaliIndexer : public FFMS_Indexer {
|
||||
private:
|
||||
int SourceMode;
|
||||
CComPtr<IMMContainer> pMMC;
|
||||
|
@ -181,8 +183,8 @@ private:
|
|||
CComQIPtr<IPropertyBag> PropertyBags[32];
|
||||
int64_t Duration;
|
||||
public:
|
||||
FFHaaliIndexer(const char *Filename, int SourceMode, char *ErrorMsg, unsigned MsgSize);
|
||||
FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize);
|
||||
FFHaaliIndexer(const char *Filename, enum FFMS_Sources SourceMode);
|
||||
FFMS_Index *DoIndexing();
|
||||
int GetNumberOfTracks();
|
||||
FFMS_TrackType GetTrackType(int Track);
|
||||
const char *GetTrackCodec(int Track);
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "ffaudiosource.h"
|
||||
#include "audiosource.h"
|
||||
|
||||
void FFLAVFAudio::Free(bool CloseCodec) {
|
||||
if (CloseCodec)
|
||||
|
@ -27,75 +27,53 @@ void FFLAVFAudio::Free(bool CloseCodec) {
|
|||
av_close_input_file(FormatContext);
|
||||
}
|
||||
|
||||
FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index,
|
||||
char *ErrorMsg, unsigned MsgSize)
|
||||
: FFAudio(SourceFile, Index, ErrorMsg, MsgSize){
|
||||
FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFMS_Index *Index)
|
||||
: Res(FFSourceResources<FFMS_AudioSource>(this)), FFMS_AudioSource(SourceFile, Index, Track){
|
||||
FormatContext = NULL;
|
||||
AVCodec *Codec = NULL;
|
||||
AudioTrack = Track;
|
||||
Frames = (*Index)[AudioTrack];
|
||||
|
||||
if (Frames.size() == 0) {
|
||||
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;
|
||||
}
|
||||
LAVFOpenFile(SourceFile, FormatContext);
|
||||
|
||||
CodecContext = FormatContext->streams[AudioTrack]->codec;
|
||||
|
||||
Codec = avcodec_find_decoder(CodecContext->codec_id);
|
||||
if (Codec == NULL) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (Codec == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Audio codec not found");
|
||||
|
||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (avcodec_open(CodecContext, Codec) < 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Could not open audio codec");
|
||||
|
||||
Res.CloseCodec(true);
|
||||
|
||||
// Always try to decode a frame to make sure all required parameters are known
|
||||
int64_t Dummy;
|
||||
if (DecodeNextAudioBlock(&Dummy, ErrorMsg, MsgSize) < 0) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
DecodeNextAudioBlock(&Dummy);
|
||||
|
||||
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);
|
||||
avcodec_flush_buffers(CodecContext);
|
||||
|
||||
FillAP(AP, CodecContext, Frames);
|
||||
|
||||
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) {
|
||||
Free(true);
|
||||
snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Codec returned zero size audio");
|
||||
|
||||
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;
|
||||
int Ret = -1;
|
||||
*Count = 0;
|
||||
uint8_t *Buf = &DecodingBuffer[0];
|
||||
AVPacket Packet, TempPacket;
|
||||
InitNullPacket(&Packet);
|
||||
InitNullPacket(&TempPacket);
|
||||
InitNullPacket(Packet);
|
||||
InitNullPacket(TempPacket);
|
||||
|
||||
while (av_read_frame(FormatContext, &Packet) >= 0) {
|
||||
if (Packet.stream_index == AudioTrack) {
|
||||
|
@ -127,11 +105,12 @@ int FFLAVFAudio::DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned M
|
|||
av_free_packet(&Packet);
|
||||
}
|
||||
|
||||
Done:
|
||||
return Ret;
|
||||
Done:;
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
// Was everything in the cache?
|
||||
if (CacheEnd == Start + Count)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
size_t CurrentAudioBlock;
|
||||
// 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);
|
||||
|
||||
AVPacket Packet;
|
||||
InitNullPacket(&Packet);
|
||||
InitNullPacket(Packet);
|
||||
|
||||
// Establish where we actually are
|
||||
// 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;
|
||||
|
||||
do {
|
||||
int Ret = DecodeNextAudioBlock(&DecodeCount, ErrorMsg, MsgSize);
|
||||
if (Ret < 0) {
|
||||
// FIXME
|
||||
//Env->ThrowError("Bleh, bad audio decoding");
|
||||
}
|
||||
DecodeNextAudioBlock(&DecodeCount);
|
||||
|
||||
// Cache the block if enough blocks before it have been decoded to avoid garbage
|
||||
if (PreDecBlocks == 0) {
|
||||
|
@ -207,10 +182,4 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs
|
|||
if (CurrentAudioBlock < Frames.size())
|
||||
CurrentSample = Frames[CurrentAudioBlock].SampleStart;
|
||||
} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < Frames.size());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FFLAVFAudio::~FFLAVFAudio() {
|
||||
Free(true);
|
||||
}
|
|
@ -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;
|
||||
this->FormatContext = FormatContext;
|
||||
|
||||
if (av_find_stream_info(FormatContext) < 0) {
|
||||
av_close_input_file(FormatContext);
|
||||
snprintf(ErrorMsg, MsgSize, "Couldn't find stream information");
|
||||
throw ErrorMsg;
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||
"Couldn't find stream information");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,15 +37,15 @@ FFLAVFIndexer::~FFLAVFIndexer() {
|
|||
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<SharedVideoContext> VideoContexts(FormatContext->nb_streams, SharedVideoContext(false));
|
||||
|
||||
std::auto_ptr<FFIndex> TrackIndices(new FFIndex(Filesize, Digest));
|
||||
TrackIndices->Decoder = 0;
|
||||
std::auto_ptr<FFMS_Index> TrackIndices(new FFMS_Index(Filesize, Digest));
|
||||
TrackIndices->Decoder = FFMS_SOURCE_LAVF;
|
||||
|
||||
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,
|
||||
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))) {
|
||||
|
||||
AVCodec *VideoCodec = avcodec_find_decoder(FormatContext->streams[i]->codec->codec_id);
|
||||
if (VideoCodec == NULL) {
|
||||
snprintf(ErrorMsg, MsgSize, "Vudio codec not found");
|
||||
return NULL;
|
||||
}
|
||||
if (VideoCodec == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||
"Video codec not found");
|
||||
|
||||
if (avcodec_open(FormatContext->streams[i]->codec, VideoCodec) < 0) {
|
||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
||||
return NULL;
|
||||
}
|
||||
if (avcodec_open(FormatContext->streams[i]->codec, VideoCodec) < 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||
"Could not open video codec");
|
||||
|
||||
VideoContexts[i].CodecContext = FormatContext->streams[i]->codec;
|
||||
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;
|
||||
|
||||
AVCodec *AudioCodec = avcodec_find_decoder(AudioCodecContext->codec_id);
|
||||
if (AudioCodec == NULL) {
|
||||
snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
||||
return NULL;
|
||||
}
|
||||
if (AudioCodec == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||
"Audio codec not found");
|
||||
|
||||
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
|
||||
snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
||||
return NULL;
|
||||
}
|
||||
if (avcodec_open(AudioCodecContext, AudioCodec) < 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||
"Could not open audio codec");
|
||||
|
||||
AudioContexts[i].CodecContext = AudioCodecContext;
|
||||
} else {
|
||||
|
@ -90,15 +86,14 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
|||
//
|
||||
|
||||
AVPacket Packet, TempPacket;
|
||||
InitNullPacket(&Packet);
|
||||
InitNullPacket(&TempPacket);
|
||||
InitNullPacket(Packet);
|
||||
InitNullPacket(TempPacket);
|
||||
while (av_read_frame(FormatContext, &Packet) >= 0) {
|
||||
// Update progress
|
||||
if (IC) {
|
||||
if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, ICPrivate)) {
|
||||
snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
||||
return NULL;
|
||||
}
|
||||
if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, ICPrivate))
|
||||
throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
|
||||
"Cancelled by user");
|
||||
}
|
||||
|
||||
// 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));
|
||||
} 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;
|
||||
TempPacket.data = Packet.data;
|
||||
TempPacket.size = Packet.size;
|
||||
|
@ -124,13 +119,18 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
|||
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
|
||||
int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
|
||||
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();
|
||||
IndexMask &= ~(1 << Packet.stream_index);
|
||||
IndexMask &= ~(1 << Packet.stream_index);
|
||||
break;
|
||||
} else if (ErrorHandling == FFMS_IEH_STOP_TRACK) {
|
||||
IndexMask &= ~(1 << Packet.stream_index);
|
||||
break;
|
||||
} else if (ErrorHandling == FFMS_IEH_IGNORE) {
|
||||
break;
|
||||
} else {
|
||||
snprintf(ErrorMsg, MsgSize, "Audio decoding error");
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
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);
|
||||
|
@ -163,5 +166,5 @@ FFMS_TrackType FFLAVFIndexer::GetTrackType(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;
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "ffvideosource.h"
|
||||
#include "videosource.h"
|
||||
|
||||
|
||||
|
||||
|
@ -28,9 +28,9 @@ void FFLAVFVideo::Free(bool CloseCodec) {
|
|||
av_close_input_file(FormatContext);
|
||||
}
|
||||
|
||||
FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index,
|
||||
const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize)
|
||||
: FFVideo(SourceFile, Index, ErrorMsg, MsgSize) {
|
||||
FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index,
|
||||
int Threads, int SeekMode)
|
||||
: Res(FFSourceResources<FFMS_VideoSource>(this)), FFMS_VideoSource(SourceFile, Index, Track) {
|
||||
|
||||
FormatContext = NULL;
|
||||
AVCodec *Codec = NULL;
|
||||
|
@ -38,68 +38,45 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index,
|
|||
VideoTrack = Track;
|
||||
Frames = (*Index)[VideoTrack];
|
||||
|
||||
if (Frames.size() == 0) {
|
||||
snprintf(ErrorMsg, MsgSize, "Video track contains no frames");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
LAVFOpenFile(SourceFile, FormatContext);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
if (SeekMode >= 0 && av_seek_frame(FormatContext, VideoTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Video track is unseekable");
|
||||
|
||||
CodecContext = FormatContext->streams[VideoTrack]->codec;
|
||||
CodecContext->thread_count = Threads;
|
||||
|
||||
Codec = avcodec_find_decoder(CodecContext->codec_id);
|
||||
if (Codec == NULL) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Video codec not found");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (Codec == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Video codec not found");
|
||||
|
||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (avcodec_open(CodecContext, Codec) < 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Could not open video codec");
|
||||
|
||||
Res.CloseCodec(true);
|
||||
|
||||
// Always try to decode a frame to make sure all required parameters are known
|
||||
int64_t Dummy;
|
||||
if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
DecodeNextFrame(&Dummy);
|
||||
|
||||
//VP.image_type = VideoInfo::IT_TFF;
|
||||
VP.Width = CodecContext->width;
|
||||
VP.Height = CodecContext->height;
|
||||
VP.FPSDenominator = FormatContext->streams[VideoTrack]->time_base.num;
|
||||
VP.FPSNumerator = FormatContext->streams[VideoTrack]->time_base.den;
|
||||
VP.RFFDenominator = CodecContext->time_base.num;
|
||||
VP.RFFNumerator = CodecContext->time_base.den;
|
||||
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.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||
|
||||
if (VP.Width <= 0 || VP.Height <= 0) {
|
||||
Free(true);
|
||||
snprintf(ErrorMsg, MsgSize, "Codec returned zero size video");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (CodecContext->width <= 0 || CodecContext->height <= 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Codec returned zero size video");
|
||||
|
||||
// sanity check framerate
|
||||
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;
|
||||
}
|
||||
|
||||
if (InitPP(PP, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
// Adjust framerate to match the duration of the first frame
|
||||
if (Frames.size() >= 2) {
|
||||
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
|
||||
// This is the additional mess required for seekmode=-1 to work in a reasonable way
|
||||
if (!OutputFrame(DecodeFrame, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
OutputFrame(DecodeFrame);
|
||||
|
||||
// Set AR variables
|
||||
VP.SARNum = CodecContext->sample_aspect_ratio.num;
|
||||
VP.SARDen = CodecContext->sample_aspect_ratio.den;
|
||||
}
|
||||
|
||||
FFLAVFVideo::~FFLAVFVideo() {
|
||||
Free(true);
|
||||
}
|
||||
|
||||
int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned MsgSize) {
|
||||
void FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime) {
|
||||
AVPacket Packet;
|
||||
InitNullPacket(&Packet);
|
||||
InitNullPacket(Packet);
|
||||
int FrameFinished = 0;
|
||||
*AStartTime = -1;
|
||||
|
||||
|
@ -145,6 +110,11 @@ int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned M
|
|||
if (*AStartTime < 0)
|
||||
*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);
|
||||
}
|
||||
|
||||
|
@ -157,23 +127,22 @@ int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned M
|
|||
// Flush the last frames
|
||||
if (CodecContext->has_b_frames) {
|
||||
AVPacket NullPacket;
|
||||
InitNullPacket(&NullPacket);
|
||||
InitNullPacket(NullPacket);
|
||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
|
||||
}
|
||||
|
||||
if (!FrameFinished)
|
||||
goto Error;
|
||||
|
||||
// Ignore errors for now
|
||||
Error:
|
||||
Done:
|
||||
return 0;
|
||||
Done:;
|
||||
}
|
||||
|
||||
FFAVFrame *FFLAVFVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) {
|
||||
// PPFrame always holds frame LastFrameNum even if no PP is applied
|
||||
FFMS_Frame *FFLAVFVideo::GetFrame(int n) {
|
||||
GetFrameCheck(n);
|
||||
|
||||
if (LastFrameNum == n)
|
||||
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize);
|
||||
return &LocalFrame;
|
||||
|
||||
bool HasSeeked = false;
|
||||
int SeekOffset = 0;
|
||||
|
@ -200,14 +169,13 @@ ReSeek:
|
|||
}
|
||||
}
|
||||
} else if (n < CurrentFrame) {
|
||||
snprintf(ErrorMsg, MsgSize, "Non-linear access attempted");
|
||||
return NULL;
|
||||
throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_INVALID_ARGUMENT,
|
||||
"Non-linear access attempted");
|
||||
}
|
||||
|
||||
do {
|
||||
int64_t StartTime;
|
||||
if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize))
|
||||
return NULL;
|
||||
DecodeNextFrame(&StartTime);
|
||||
|
||||
if (HasSeeked) {
|
||||
HasSeeked = false;
|
||||
|
@ -217,10 +185,10 @@ ReSeek:
|
|||
switch (SeekMode) {
|
||||
case 1:
|
||||
// No idea where we are so go back a bit further
|
||||
if (ClosestKF + SeekOffset == 0) {
|
||||
snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file\n");
|
||||
return NULL;
|
||||
}
|
||||
if (ClosestKF + SeekOffset == 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Frame accurate seeking is not possible in this file");
|
||||
|
||||
|
||||
SeekOffset -= FFMIN(10, ClosestKF + SeekOffset);
|
||||
goto ReSeek;
|
||||
|
@ -229,8 +197,8 @@ ReSeek:
|
|||
CurrentFrame = Frames.ClosestFrameFromDTS(StartTime);
|
||||
break;
|
||||
default:
|
||||
snprintf(ErrorMsg, MsgSize, "Failed assertion");
|
||||
return NULL;
|
||||
throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_UNKNOWN,
|
||||
"Failed assertion");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -239,5 +207,5 @@ ReSeek:
|
|||
} while (CurrentFrame <= n);
|
||||
|
||||
LastFrameNum = n;
|
||||
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize);
|
||||
return OutputFrame(DecodeFrame);
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "ffaudiosource.h"
|
||||
#include "audiosource.h"
|
||||
|
||||
void FFMatroskaAudio::Free(bool CloseCodec) {
|
||||
if (CS)
|
||||
|
@ -32,89 +32,74 @@ void FFMatroskaAudio::Free(bool CloseCodec) {
|
|||
av_freep(&CodecContext);
|
||||
}
|
||||
|
||||
FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track,
|
||||
FFIndex *Index, char *ErrorMsg, unsigned MsgSize)
|
||||
: FFAudio(SourceFile, Index, ErrorMsg, MsgSize) {
|
||||
FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index *Index)
|
||||
: Res(FFSourceResources<FFMS_AudioSource>(this)), FFMS_AudioSource(SourceFile, Index, Track) {
|
||||
CodecContext = NULL;
|
||||
AVCodec *Codec = NULL;
|
||||
TrackInfo *TI = NULL;
|
||||
CS = NULL;
|
||||
PacketNumber = 0;
|
||||
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");
|
||||
if (MC.ST.fp == NULL) {
|
||||
snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (MC.ST.fp == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||
boost::format("Can't open '%1%': %2%") % SourceFile % strerror(errno));
|
||||
|
||||
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
||||
|
||||
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
|
||||
if (MF == NULL) {
|
||||
fclose(MC.ST.fp);
|
||||
snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage);
|
||||
throw ErrorMsg;
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||
boost::format("Can't parse Matroska file: %1%") % ErrorMessage);
|
||||
}
|
||||
|
||||
mkv_SetTrackMask(MF, ~(1 << Track));
|
||||
|
||||
TI = mkv_GetTrackInfo(MF, Track);
|
||||
|
||||
if (TI->CompEnabled) {
|
||||
CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage));
|
||||
if (CS == NULL) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
|
||||
throw ErrorMsg;
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||
boost::format("Can't create decompressor: %1%") % ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
CodecContext = avcodec_alloc_context();
|
||||
|
||||
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
|
||||
if (Codec == NULL) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Video codec not found");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (Codec == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Audio codec not found");
|
||||
|
||||
InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext);
|
||||
|
||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (avcodec_open(CodecContext, Codec) < 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Could not open audio codec");
|
||||
|
||||
Res.CloseCodec(true);
|
||||
|
||||
// Always try to decode a frame to make sure all required parameters are known
|
||||
int64_t Dummy;
|
||||
if (DecodeNextAudioBlock(&Dummy, 0, ErrorMsg, MsgSize) < 0) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
DecodeNextAudioBlock(&Dummy);
|
||||
|
||||
avcodec_flush_buffers(CodecContext);
|
||||
|
||||
FillAP(AP, CodecContext, Frames);
|
||||
|
||||
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) {
|
||||
Free(true);
|
||||
snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Codec returned zero size audio");
|
||||
|
||||
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
|
||||
}
|
||||
|
||||
FFMatroskaAudio::~FFMatroskaAudio() {
|
||||
Free(true);
|
||||
}
|
||||
void FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) {
|
||||
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;
|
||||
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);
|
||||
// Was everything in the cache?
|
||||
if (CacheEnd == Start + Count)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
int CurrentAudioBlock;
|
||||
// Is seeking required to decode the requested samples?
|
||||
// if (!(CurrentSample >= Start && CurrentSample <= CacheEnd)) {
|
||||
if (CurrentSample != CacheEnd) {
|
||||
PreDecBlocks = 15;
|
||||
CurrentAudioBlock = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks, (int64_t)0);
|
||||
PacketNumber = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks, (int64_t)0);;
|
||||
avcodec_flush_buffers(CodecContext);
|
||||
} else {
|
||||
CurrentAudioBlock = Frames.FindClosestAudioKeyFrame(CurrentSample);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t DecodeCount;
|
||||
|
||||
do {
|
||||
int Ret = DecodeNextAudioBlock(&DecodeCount, CurrentAudioBlock, ErrorMsg, MsgSize);
|
||||
if (Ret < 0) {
|
||||
// FIXME
|
||||
//Env->ThrowError("Bleh, bad audio decoding");
|
||||
}
|
||||
const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos];
|
||||
DecodeNextAudioBlock(&DecodeCount);
|
||||
|
||||
// Cache the block if enough blocks before it have been decoded to avoid garbage
|
||||
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);
|
||||
} else {
|
||||
PreDecBlocks--;
|
||||
}
|
||||
|
||||
CurrentAudioBlock++;
|
||||
if (CurrentAudioBlock < static_cast<int>(Frames.size()))
|
||||
CurrentSample = Frames[CurrentAudioBlock].SampleStart;
|
||||
} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < static_cast<int>(Frames.size()));
|
||||
|
||||
return 0;
|
||||
} while (Start + Count - CacheEnd > 0 && PacketNumber < Frames.size());
|
||||
}
|
||||
|
||||
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;
|
||||
int Ret = -1;
|
||||
*Count = 0;
|
||||
uint8_t *Buf = &DecodingBuffer[0];
|
||||
AVPacket TempPacket;
|
||||
InitNullPacket(&TempPacket);
|
||||
InitNullPacket(TempPacket);
|
||||
|
||||
unsigned int FrameSize = Frames[AudioBlock].FrameSize;
|
||||
if (ReadFrame(Frames[AudioBlock].FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize))
|
||||
return 1;
|
||||
const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos];
|
||||
unsigned int FrameSize = FI.FrameSize;
|
||||
CurrentSample = FI.SampleStart + FI.SampleCount;
|
||||
PacketNumber++;
|
||||
|
||||
ReadFrame(FI.FilePos, FrameSize, CS, MC);
|
||||
TempPacket.data = MC.Buffer;
|
||||
TempPacket.size = FrameSize;
|
||||
if (Frames[AudioBlock].KeyFrame)
|
||||
if (FI.KeyFrame)
|
||||
TempPacket.flags = AV_PKT_FLAG_KEY;
|
||||
else
|
||||
TempPacket.flags = 0;
|
||||
|
@ -197,6 +174,5 @@ int FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count, int AudioBlock, char *
|
|||
}
|
||||
}
|
||||
|
||||
Done:
|
||||
return 0;
|
||||
Done:;
|
||||
}
|
|
@ -23,25 +23,26 @@
|
|||
|
||||
|
||||
|
||||
FFMatroskaIndexer::FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) : FFIndexer(Filename, ErrorMsg, MsgSize) {
|
||||
memset(Codec, 0, sizeof(Codec));
|
||||
FFMatroskaIndexer::FFMatroskaIndexer(const char *Filename) : FFMS_Indexer(Filename) {
|
||||
SourceFile = Filename;
|
||||
char ErrorMessage[256];
|
||||
for (int i = 0; i < 32; i++) {
|
||||
Codec[i] = NULL;
|
||||
}
|
||||
|
||||
InitStdIoStream(&MC.ST);
|
||||
MC.ST.fp = ffms_fopen(SourceFile, "rb");
|
||||
if (MC.ST.fp == NULL) {
|
||||
snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (MC.ST.fp == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||
boost::format("Can't open '%1%': %2%") % SourceFile % strerror(errno));
|
||||
|
||||
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
||||
|
||||
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
|
||||
if (MF == NULL) {
|
||||
fclose(MC.ST.fp);
|
||||
snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage);
|
||||
throw ErrorMsg;
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||
boost::format("Can't parse Matroska file: %1%") % ErrorMessage);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) {
|
||||
|
@ -55,17 +56,17 @@ FFMatroskaIndexer::~FFMatroskaIndexer() {
|
|||
fclose(MC.ST.fp);
|
||||
}
|
||||
|
||||
FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||
FFMS_Index *FFMatroskaIndexer::DoIndexing() {
|
||||
char ErrorMessage[256];
|
||||
std::vector<SharedAudioContext> AudioContexts(mkv_GetNumTracks(MF), SharedAudioContext(true));
|
||||
std::vector<SharedVideoContext> VideoContexts(mkv_GetNumTracks(MF), SharedVideoContext(true));
|
||||
|
||||
std::auto_ptr<FFIndex> TrackIndices(new FFIndex(Filesize, Digest));
|
||||
TrackIndices->Decoder = 1;
|
||||
std::auto_ptr<FFMS_Index> TrackIndices(new FFMS_Index(Filesize, Digest));
|
||||
TrackIndices->Decoder = FFMS_SOURCE_MATROSKA;
|
||||
|
||||
for (unsigned int i = 0; i < mkv_GetNumTracks(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))) {
|
||||
|
||||
|
@ -75,16 +76,15 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
|||
|
||||
if (avcodec_open(CodecContext, Codec[i]) < 0) {
|
||||
av_freep(&CodecContext);
|
||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
||||
return NULL;
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||
"Could not open video codec");
|
||||
}
|
||||
|
||||
if (TI->CompEnabled) {
|
||||
VideoContexts[i].CS = cs_Create(MF, i, ErrorMessage, sizeof(ErrorMessage));
|
||||
if (VideoContexts[i].CS == NULL) {
|
||||
snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
|
||||
return NULL;
|
||||
}
|
||||
if (VideoContexts[i].CS == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||
boost::format("Can't create decompressor: %1%") % ErrorMessage);
|
||||
}
|
||||
|
||||
VideoContexts[i].CodecContext = CodecContext;
|
||||
|
@ -102,8 +102,8 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
|||
if (AudioContexts[i].CS == NULL) {
|
||||
av_freep(&AudioCodecContext);
|
||||
AudioContexts[i].CodecContext = NULL;
|
||||
snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
|
||||
return NULL;
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||
boost::format("Can't create decompressor: %1%") % ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,15 +111,15 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
|||
if (AudioCodec == NULL) {
|
||||
av_freep(&AudioCodecContext);
|
||||
AudioContexts[i].CodecContext = NULL;
|
||||
snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
||||
return NULL;
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||
"Audio codec not found");
|
||||
}
|
||||
|
||||
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
|
||||
av_freep(&AudioCodecContext);
|
||||
AudioContexts[i].CodecContext = NULL;
|
||||
snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
||||
return NULL;
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||
"Could not open audio codec");
|
||||
}
|
||||
} else {
|
||||
IndexMask &= ~(1 << i);
|
||||
|
@ -131,15 +131,14 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
|||
ulonglong StartTime, EndTime, FilePos;
|
||||
unsigned int Track, FrameFlags, FrameSize;
|
||||
AVPacket TempPacket;
|
||||
InitNullPacket(&TempPacket);
|
||||
InitNullPacket(TempPacket);
|
||||
|
||||
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) {
|
||||
// Update progress
|
||||
if (IC) {
|
||||
if ((*IC)(ftello(MC.ST.fp), Filesize, ICPrivate)) {
|
||||
snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
||||
return NULL;
|
||||
}
|
||||
if ((*IC)(ftello(MC.ST.fp), Filesize, ICPrivate))
|
||||
throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
|
||||
"Cancelled by user");
|
||||
}
|
||||
|
||||
// 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));
|
||||
} 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));
|
||||
ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC, ErrorMsg, MsgSize);
|
||||
int64_t StartSample = AudioContexts[Track].CurrentSample;
|
||||
unsigned int CompressedFrameSize = FrameSize;
|
||||
AVCodecContext *AudioCodecContext = AudioContexts[Track].CodecContext;
|
||||
ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC);
|
||||
TempPacket.data = MC.Buffer;
|
||||
TempPacket.size = FrameSize;
|
||||
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 Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
|
||||
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();
|
||||
IndexMask &= ~(1 << Track);
|
||||
break;
|
||||
} else {
|
||||
snprintf(ErrorMsg, MsgSize, "Audio decoding error");
|
||||
return NULL;
|
||||
} else if (ErrorHandling == FFMS_IEH_STOP_TRACK) {
|
||||
IndexMask &= ~(1 << Track);
|
||||
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);
|
||||
|
||||
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) {
|
||||
if (Codec[Track])
|
||||
return Codec[Track]->long_name;
|
||||
return Codec[Track]->name;
|
||||
else
|
||||
return "Unsupported codec/Unknown codec name";
|
||||
}
|
|
@ -18,7 +18,9 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "ffvideosource.h"
|
||||
#include "videosource.h"
|
||||
|
||||
|
||||
|
||||
void FFMatroskaVideo::Free(bool CloseCodec) {
|
||||
if (CS)
|
||||
|
@ -33,46 +35,39 @@ void FFMatroskaVideo::Free(bool CloseCodec) {
|
|||
}
|
||||
|
||||
FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
|
||||
FFIndex *Index, const char *PP,
|
||||
int Threads, char *ErrorMsg, unsigned MsgSize)
|
||||
: FFVideo(SourceFile, Index, ErrorMsg, MsgSize) {
|
||||
FFMS_Index *Index, int Threads)
|
||||
: Res(FFSourceResources<FFMS_VideoSource>(this)), FFMS_VideoSource(SourceFile, Index, Track) {
|
||||
|
||||
AVCodec *Codec = NULL;
|
||||
CodecContext = NULL;
|
||||
TrackInfo *TI = NULL;
|
||||
CS = NULL;
|
||||
PacketNumber = 0;
|
||||
VideoTrack = Track;
|
||||
Frames = (*Index)[VideoTrack];
|
||||
|
||||
if (Frames.size() == 0) {
|
||||
snprintf(ErrorMsg, MsgSize, "Video track contains no frames");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
MC.ST.fp = ffms_fopen(SourceFile, "rb");
|
||||
if (MC.ST.fp == NULL) {
|
||||
snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (MC.ST.fp == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||
boost::format("Can't open '%1%': %2%") % SourceFile % strerror(errno));
|
||||
|
||||
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
||||
|
||||
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
|
||||
if (MF == NULL) {
|
||||
fclose(MC.ST.fp);
|
||||
snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage);
|
||||
throw ErrorMsg;
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||
boost::format("Can't parse Matroska file: %1%") % ErrorMessage);
|
||||
}
|
||||
|
||||
mkv_SetTrackMask(MF, ~(1 << VideoTrack));
|
||||
TI = mkv_GetTrackInfo(MF, VideoTrack);
|
||||
|
||||
if (TI->CompEnabled) {
|
||||
CS = cs_Create(MF, VideoTrack, ErrorMessage, sizeof(ErrorMessage));
|
||||
if (CS == NULL) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
|
||||
throw ErrorMsg;
|
||||
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||
boost::format("Can't create decompressor: %1%") % ErrorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,48 +75,35 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
|
|||
CodecContext->thread_count = Threads;
|
||||
|
||||
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
|
||||
if (Codec == NULL) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Video codec not found");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (Codec == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Video codec not found");
|
||||
|
||||
InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext);
|
||||
|
||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
||||
Free(false);
|
||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (avcodec_open(CodecContext, Codec) < 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Could not open video codec");
|
||||
|
||||
Res.CloseCodec(true);
|
||||
|
||||
// Always try to decode a frame to make sure all required parameters are known
|
||||
int64_t Dummy;
|
||||
if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
DecodeNextFrame();
|
||||
|
||||
VP.Width = CodecContext->width;
|
||||
VP.Height = CodecContext->height;;
|
||||
VP.FPSDenominator = 1;
|
||||
VP.FPSNumerator = 30;
|
||||
VP.RFFDenominator = CodecContext->time_base.num;
|
||||
VP.RFFNumerator = CodecContext->time_base.den;
|
||||
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.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||
|
||||
if (VP.Width <= 0 || VP.Height <= 0) {
|
||||
Free(true);
|
||||
snprintf(ErrorMsg, MsgSize, "Codec returned zero size video");
|
||||
throw ErrorMsg;
|
||||
}
|
||||
|
||||
if (InitPP(PP, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
if (CodecContext->width <= 0 || CodecContext->height <= 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||
"Codec returned zero size video");
|
||||
|
||||
// Calculate the average framerate
|
||||
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
|
||||
if (!OutputFrame(DecodeFrame, ErrorMsg, MsgSize)) {
|
||||
Free(true);
|
||||
throw ErrorMsg;
|
||||
}
|
||||
OutputFrame(DecodeFrame);
|
||||
|
||||
// Set AR variables
|
||||
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;
|
||||
}
|
||||
|
||||
FFMatroskaVideo::~FFMatroskaVideo() {
|
||||
Free(true);
|
||||
}
|
||||
|
||||
int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) {
|
||||
void FFMatroskaVideo::DecodeNextFrame() {
|
||||
int FrameFinished = 0;
|
||||
*AFirstStartTime = -1;
|
||||
AVPacket Packet;
|
||||
InitNullPacket(&Packet);
|
||||
InitNullPacket(Packet);
|
||||
|
||||
ulonglong StartTime, EndTime, FilePos;
|
||||
unsigned int Track, FrameFlags, FrameSize;
|
||||
unsigned int FrameSize;
|
||||
|
||||
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) {
|
||||
if (*AFirstStartTime < 0)
|
||||
*AFirstStartTime = StartTime;
|
||||
|
||||
if (ReadFrame(FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize))
|
||||
return 1;
|
||||
while (PacketNumber < Frames.size()) {
|
||||
// The additional indirection is because the packets are stored in
|
||||
// presentation order and not decoding order, this is unnoticable
|
||||
// in the other sources where less is done manually
|
||||
const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos];
|
||||
FrameSize = FI.FrameSize;
|
||||
ReadFrame(FI.FilePos, FrameSize, CS, MC);
|
||||
|
||||
Packet.data = MC.Buffer;
|
||||
Packet.size = FrameSize;
|
||||
if (FrameFlags & FRAME_KF)
|
||||
if (FI.KeyFrame)
|
||||
Packet.flags = AV_PKT_FLAG_KEY;
|
||||
else
|
||||
Packet.flags = 0;
|
||||
|
||||
PacketNumber++;
|
||||
|
||||
if (CodecContext->codec_id == CODEC_ID_MPEG4 && IsNVOP(Packet))
|
||||
goto Done;
|
||||
|
||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
|
||||
|
||||
if (FrameFinished)
|
||||
|
@ -183,7 +162,7 @@ int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, u
|
|||
// Flush the last frames
|
||||
if (CodecContext->has_b_frames) {
|
||||
AVPacket NullPacket;
|
||||
InitNullPacket(&NullPacket);
|
||||
InitNullPacket(NullPacket);
|
||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
|
||||
}
|
||||
|
||||
|
@ -191,40 +170,27 @@ int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, u
|
|||
goto Error;
|
||||
|
||||
Error:
|
||||
Done:
|
||||
return 0;
|
||||
Done:;
|
||||
}
|
||||
|
||||
FFAVFrame *FFMatroskaVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) {
|
||||
// PPFrame always holds frame LastFrameNum even if no PP is applied
|
||||
FFMS_Frame *FFMatroskaVideo::GetFrame(int n) {
|
||||
GetFrameCheck(n);
|
||||
|
||||
if (LastFrameNum == n)
|
||||
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize);
|
||||
return &LocalFrame;
|
||||
|
||||
bool HasSeeked = false;
|
||||
|
||||
if (n < CurrentFrame || Frames.FindClosestVideoKeyFrame(n) > CurrentFrame) {
|
||||
mkv_Seek(MF, Frames[n].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT);
|
||||
int ClosestKF = Frames.FindClosestVideoKeyFrame(n);
|
||||
if (CurrentFrame > n || ClosestKF > CurrentFrame + 10) {
|
||||
PacketNumber = ClosestKF;
|
||||
CurrentFrame = ClosestKF;
|
||||
avcodec_flush_buffers(CodecContext);
|
||||
HasSeeked = true;
|
||||
}
|
||||
|
||||
do {
|
||||
int64_t StartTime;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
DecodeNextFrame();
|
||||
CurrentFrame++;
|
||||
} while (CurrentFrame <= n);
|
||||
|
||||
LastFrameNum = n;
|
||||
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize);
|
||||
return OutputFrame(DecodeFrame);
|
||||
}
|
|
@ -104,13 +104,13 @@ longlong StdIoGetFileSize(StdIoStream *st) {
|
|||
|
||||
void InitStdIoStream(StdIoStream *st) {
|
||||
memset(st,0,sizeof(StdIoStream));
|
||||
st->base.read = StdIoRead;
|
||||
st->base.scan = StdIoScan;
|
||||
st->base.getcachesize = StdIoGetCacheSize;
|
||||
st->base.geterror = StdIoGetLastError;
|
||||
st->base.memalloc = StdIoMalloc;
|
||||
st->base.memrealloc = StdIoRealloc;
|
||||
st->base.memfree = StdIoFree;
|
||||
st->base.progress = StdIoProgress;
|
||||
st->base.getfilesize = StdIoGetFileSize;
|
||||
st->base.read = (int (*)(InputStream *,ulonglong,void *,int))StdIoRead;
|
||||
st->base.scan = (longlong (*)(InputStream *,ulonglong,unsigned int))StdIoScan;
|
||||
st->base.getcachesize = (unsigned int (*)(InputStream *))StdIoGetCacheSize;
|
||||
st->base.geterror = (const char *(*)(InputStream *))StdIoGetLastError;
|
||||
st->base.memalloc = (void *(*)(InputStream *,size_t))StdIoMalloc;
|
||||
st->base.memrealloc = (void *(*)(InputStream *,void *,size_t))StdIoRealloc;
|
||||
st->base.memfree = (void (*)(InputStream *,void *))StdIoFree;
|
||||
st->base.progress = (int (*)(InputStream *,ulonglong,ulonglong))StdIoProgress;
|
||||
st->base.getfilesize = (longlong (*)(InputStream *))StdIoGetFileSize;
|
||||
}
|
||||
|
|
|
@ -46,6 +46,38 @@ extern const AVCodecTag ff_codec_wav_tags[];
|
|||
|
||||
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 Flags = 0;
|
||||
|
||||
|
@ -78,6 +110,16 @@ int GetPPCPUFlags() {
|
|||
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) {
|
||||
switch (TT) {
|
||||
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) {
|
||||
char CSBuffer[4096];
|
||||
|
||||
unsigned int DecompressedFrameSize = 0;
|
||||
|
||||
cs_NextFrame(CS, FilePos, FrameSize);
|
||||
|
||||
for (;;) {
|
||||
int ReadBytes = cs_ReadData(CS, CSBuffer, sizeof(CSBuffer));
|
||||
if (ReadBytes < 0) {
|
||||
snprintf(ErrorMsg, MsgSize, "Error decompressing data: %s", cs_GetLastError(CS));
|
||||
return 1;
|
||||
}
|
||||
int ReadBytes = cs_ReadData(CS, Context.CSBuffer, sizeof(Context.CSBuffer));
|
||||
if (ReadBytes < 0)
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||
boost::format("Error decompressing data: %1%") % cs_GetLastError(CS));
|
||||
|
||||
if (ReadBytes == 0) {
|
||||
FrameSize = DecompressedFrameSize;
|
||||
return 0;
|
||||
memset(Context.Buffer + DecompressedFrameSize, 0,
|
||||
Context.BufferSize + FF_INPUT_BUFFER_PADDING_SIZE - DecompressedFrameSize);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Context.BufferSize < DecompressedFrameSize + ReadBytes) {
|
||||
Context.BufferSize = FrameSize;
|
||||
Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16);
|
||||
if (Context.Buffer == NULL) {
|
||||
snprintf(ErrorMsg, MsgSize, "Out of memory");
|
||||
return 2;
|
||||
}
|
||||
Context.BufferSize = DecompressedFrameSize + ReadBytes;
|
||||
Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||
if (Context.Buffer == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
|
||||
"Out of memory");
|
||||
}
|
||||
|
||||
memcpy(Context.Buffer + DecompressedFrameSize, CSBuffer, ReadBytes);
|
||||
memcpy(Context.Buffer + DecompressedFrameSize, Context.CSBuffer, ReadBytes);
|
||||
DecompressedFrameSize += ReadBytes;
|
||||
}
|
||||
} else {
|
||||
if (fseeko(Context.ST.fp, FilePos, SEEK_SET)) {
|
||||
snprintf(ErrorMsg, MsgSize, "fseek(): %s", strerror(errno));
|
||||
return 3;
|
||||
}
|
||||
if (fseeko(Context.ST.fp, FilePos, SEEK_SET))
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_SEEKING,
|
||||
boost::format("fseek(): %1%") % strerror(errno));
|
||||
|
||||
if (Context.BufferSize < FrameSize) {
|
||||
Context.BufferSize = FrameSize;
|
||||
Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16);
|
||||
if (Context.Buffer == NULL) {
|
||||
snprintf(ErrorMsg, MsgSize, "Out of memory");
|
||||
return 4;
|
||||
}
|
||||
if (Context.Buffer == NULL)
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
|
||||
"Out of memory");
|
||||
}
|
||||
|
||||
size_t ReadBytes = fread(Context.Buffer, 1, FrameSize, Context.ST.fp);
|
||||
if (ReadBytes != FrameSize) {
|
||||
if (ReadBytes == 0) {
|
||||
if (feof(Context.ST.fp)) {
|
||||
snprintf(ErrorMsg, MsgSize, "Unexpected EOF while reading frame");
|
||||
return 5;
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||
"Unexpected EOF while reading frame");
|
||||
} else {
|
||||
snprintf(ErrorMsg, MsgSize, "Error reading frame: %s", strerror(errno));
|
||||
return 6;
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_SEEKING,
|
||||
boost::format("Error reading frame: %1%") % strerror(errno));
|
||||
}
|
||||
} else {
|
||||
snprintf(ErrorMsg, MsgSize, "Short read while reading frame");
|
||||
return 7;
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||
"Short read while reading frame");
|
||||
}
|
||||
snprintf(ErrorMsg, MsgSize, "Unknown read error");
|
||||
return 8;
|
||||
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||
"Unknown read error");
|
||||
}
|
||||
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void InitNullPacket(AVPacket *pkt) {
|
||||
av_init_packet(pkt);
|
||||
pkt->data = NULL;
|
||||
pkt->size = 0;
|
||||
void InitNullPacket(AVPacket &pkt) {
|
||||
av_init_packet(&pkt);
|
||||
pkt.data = NULL;
|
||||
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.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.Channels = CTX->channels;;
|
||||
AP.ChannelLayout = CTX->channel_layout;
|
||||
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.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
|
||||
|
||||
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 */
|
||||
for(int i = 0; ff_mkv_codec_tags[i].id != CODEC_ID_NONE; i++){
|
||||
if(!strncmp(ff_mkv_codec_tags[i].str, Codec,
|
||||
strlen(ff_mkv_codec_tags[i].str))){
|
||||
return ff_mkv_codec_tags[i].id;
|
||||
strlen(ff_mkv_codec_tags[i].str))) {
|
||||
|
||||
// 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 };
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@
|
|||
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
#include <boost/format.hpp>
|
||||
#include "ffms.h"
|
||||
#include "matroskaparser.h"
|
||||
|
||||
|
@ -50,17 +51,73 @@ extern "C" {
|
|||
# include "guids.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#define FFMS_GET_VECTOR_PTR(v) (((v).size() ? &(v)[0] : NULL))
|
||||
|
||||
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 {
|
||||
public:
|
||||
StdIoStream ST;
|
||||
uint8_t *Buffer;
|
||||
unsigned int BufferSize;
|
||||
char CSBuffer[4096];
|
||||
|
||||
MatroskaReaderContext() {
|
||||
InitStdIoStream(&ST);
|
||||
|
@ -81,19 +138,25 @@ public:
|
|||
|
||||
int GetSWSCPUFlags();
|
||||
int GetPPCPUFlags();
|
||||
void ClearErrorInfo(FFMS_ErrorInfo *ErrorInfo);
|
||||
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);
|
||||
void InitNullPacket(AVPacket *pkt);
|
||||
void FillAP(FFAudioProperties &AP, AVCodecContext *CTX, FFTrack &Frames);
|
||||
void InitNullPacket(AVPacket &pkt);
|
||||
bool IsNVOP(AVPacket &pkt);
|
||||
void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames);
|
||||
#ifdef HAALISOURCE
|
||||
unsigned vtSize(VARIANT &vt);
|
||||
void vtCopy(VARIANT& vt,void *dest);
|
||||
void InitializeCodecContextFromHaaliInfo(CComQIPtr<IPropertyBag> pBag, AVCodecContext *CodecContext);
|
||||
#endif
|
||||
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);
|
||||
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
|
||||
|
|
|
@ -18,24 +18,52 @@
|
|||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#include "ffvideosource.h"
|
||||
#include "videosource.h"
|
||||
|
||||
int FFVideo::InitPP(const char *PP, char *ErrorMsg, unsigned MsgSize) {
|
||||
if (PP == NULL || !strcmp(PP, ""))
|
||||
return 0;
|
||||
|
||||
PPMode = pp_get_mode_by_name_and_quality(PP, PP_QUALITY_MAX);
|
||||
if (!PPMode) {
|
||||
snprintf(ErrorMsg, MsgSize, "Invalid postprocesing settings");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
void FFMS_VideoSource::GetFrameCheck(int n) {
|
||||
if (n < 0 || n >= VP.NumFrames)
|
||||
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_INVALID_ARGUMENT,
|
||||
"Out of bounds frame requested");
|
||||
}
|
||||
|
||||
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)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
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_YUV444P: Flags |= PP_FORMAT_444; break;
|
||||
default:
|
||||
snprintf(ErrorMsg, MsgSize, "Input format is not supported for postprocessing");
|
||||
return 1;
|
||||
ResetPP();
|
||||
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);
|
||||
|
||||
avpicture_free(&PPFrame);
|
||||
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++) {
|
||||
Dst.Data[i] = Picture.data[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 (ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height, ErrorMsg, MsgSize))
|
||||
return NULL;
|
||||
ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height);
|
||||
|
||||
if (TargetHeight > 0 && TargetWidth > 0 && TargetPixelFormats != 0)
|
||||
if (ReAdjustOutputFormat(TargetPixelFormats, TargetWidth, TargetHeight, TargetResizer, ErrorMsg, MsgSize))
|
||||
return NULL;
|
||||
ReAdjustOutputFormat(TargetPixelFormats, TargetWidth, TargetHeight, TargetResizer);
|
||||
}
|
||||
|
||||
if (PPMode) {
|
||||
|
@ -99,9 +123,9 @@ FFAVFrame *FFVideo::OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize
|
|||
LocalFrame.EncodedWidth = CodecContext->width;
|
||||
LocalFrame.EncodedHeight = CodecContext->height;
|
||||
LocalFrame.EncodedPixelFormat = CodecContext->pix_fmt;
|
||||
LocalFrame.ScaledWidth = VP.Width;
|
||||
LocalFrame.ScaledHeight = VP.Height;
|
||||
LocalFrame.ConvertedPixelFormat = VP.VPixelFormat;
|
||||
LocalFrame.ScaledWidth = TargetWidth;
|
||||
LocalFrame.ScaledHeight = TargetHeight;
|
||||
LocalFrame.ConvertedPixelFormat = OutputFormat;
|
||||
LocalFrame.KeyFrame = Frame->key_frame;
|
||||
LocalFrame.PictType = av_get_pict_type_char(Frame->pict_type);
|
||||
LocalFrame.RepeatPict = Frame->repeat_pict;
|
||||
|
@ -115,9 +139,22 @@ FFAVFrame *FFVideo::OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize
|
|||
return &LocalFrame;
|
||||
}
|
||||
|
||||
FFVideo::FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) {
|
||||
if (Index->CompareFileSignature(SourceFile, ErrorMsg, MsgSize))
|
||||
throw ErrorMsg;
|
||||
FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track) {
|
||||
if (Track < 0 || Track >= static_cast<int>(Index->size()))
|
||||
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));
|
||||
PPContext = NULL;
|
||||
|
@ -133,6 +170,7 @@ FFVideo::FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigne
|
|||
TargetWidth = -1;
|
||||
TargetPixelFormats = 0;
|
||||
TargetResizer = 0;
|
||||
OutputFormat = PIX_FMT_NONE;
|
||||
DecodeFrame = avcodec_alloc_frame();
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
FFVideo::~FFVideo() {
|
||||
FFMS_VideoSource::~FFMS_VideoSource() {
|
||||
if (PPMode)
|
||||
pp_free_mode(PPMode);
|
||||
|
||||
|
@ -155,32 +193,33 @@ FFVideo::~FFVideo() {
|
|||
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));
|
||||
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->TargetHeight = Height;
|
||||
this->TargetPixelFormats = TargetFormats;
|
||||
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) {
|
||||
sws_freeContext(SWS);
|
||||
SWS = NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
if (OutputFormat == PIX_FMT_NONE) {
|
||||
ResetOutputFormat();
|
||||
snprintf(ErrorMsg, MsgSize, "No suitable output format found");
|
||||
return 1;
|
||||
throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT,
|
||||
"No suitable output format found");
|
||||
}
|
||||
|
||||
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);
|
||||
if (SWS == NULL) {
|
||||
ResetOutputFormat();
|
||||
snprintf(ErrorMsg, MsgSize, "Failed to allocate SWScale context");
|
||||
return 1;
|
||||
throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT,
|
||||
"Failed to allocate SWScale context");
|
||||
}
|
||||
}
|
||||
|
||||
VP.VPixelFormat = OutputFormat;
|
||||
VP.Height = Height;
|
||||
VP.Width = Width;
|
||||
|
||||
avpicture_free(&SWSFrame);
|
||||
avpicture_alloc(&SWSFrame, OutputFormat, Width, Height);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FFVideo::ResetOutputFormat() {
|
||||
void FFMS_VideoSource::ResetOutputFormat() {
|
||||
if (SWS) {
|
||||
sws_freeContext(SWS);
|
||||
SWS = NULL;
|
||||
|
@ -212,8 +245,7 @@ void FFVideo::ResetOutputFormat() {
|
|||
TargetWidth = -1;
|
||||
TargetHeight = -1;
|
||||
TargetPixelFormats = 0;
|
||||
|
||||
VP.Height = CodecContext->height;
|
||||
VP.Width = CodecContext->width;
|
||||
VP.VPixelFormat = CodecContext->pix_fmt;
|
||||
OutputFormat = PIX_FMT_NONE;
|
||||
|
||||
OutputFrame(DecodeFrame);
|
||||
}
|
|
@ -45,7 +45,8 @@ extern "C" {
|
|||
# include "guids.h"
|
||||
#endif
|
||||
|
||||
class FFVideo {
|
||||
class FFMS_VideoSource {
|
||||
friend class FFSourceResources<FFMS_VideoSource>;
|
||||
private:
|
||||
pp_context_t *PPContext;
|
||||
pp_mode_t *PPMode;
|
||||
|
@ -57,75 +58,83 @@ private:
|
|||
int TargetWidth;
|
||||
int64_t TargetPixelFormats;
|
||||
int TargetResizer;
|
||||
PixelFormat OutputFormat;
|
||||
AVPicture PPFrame;
|
||||
AVPicture SWSFrame;
|
||||
protected:
|
||||
FFVideoProperties VP;
|
||||
FFAVFrame LocalFrame;
|
||||
FFMS_VideoProperties VP;
|
||||
FFMS_Frame LocalFrame;
|
||||
AVFrame *DecodeFrame;
|
||||
int LastFrameNum;
|
||||
FFTrack Frames;
|
||||
FFMS_Track Frames;
|
||||
int VideoTrack;
|
||||
int CurrentFrame;
|
||||
AVCodecContext *CodecContext;
|
||||
|
||||
FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
||||
int InitPP(const char *PP, char *ErrorMsg, unsigned MsgSize);
|
||||
int ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height, char *ErrorMsg, unsigned MsgSize);
|
||||
FFAVFrame *OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track);
|
||||
void ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height);
|
||||
void ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer);
|
||||
FFMS_Frame *OutputFrame(AVFrame *Frame);
|
||||
virtual void Free(bool CloseCodec) = 0;
|
||||
public:
|
||||
virtual ~FFVideo();
|
||||
const FFVideoProperties& GetFFVideoProperties() { return VP; }
|
||||
FFTrack *GetFFTrack() { return &Frames; }
|
||||
virtual FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize) = 0;
|
||||
FFAVFrame *GetFrameByTime(double Time, char *ErrorMsg, unsigned MsgSize);
|
||||
int SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize);
|
||||
int ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize);
|
||||
virtual ~FFMS_VideoSource();
|
||||
const FFMS_VideoProperties& GetVideoProperties() { return VP; }
|
||||
FFMS_Track *GetTrack() { return &Frames; }
|
||||
virtual FFMS_Frame *GetFrame(int n) = 0;
|
||||
void GetFrameCheck(int n);
|
||||
FFMS_Frame *GetFrameByTime(double Time);
|
||||
void SetPP(const char *PP);
|
||||
void ResetPP();
|
||||
void SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer);
|
||||
void ResetOutputFormat();
|
||||
};
|
||||
|
||||
class FFLAVFVideo : public FFVideo {
|
||||
class FFLAVFVideo : public FFMS_VideoSource {
|
||||
private:
|
||||
AVFormatContext *FormatContext;
|
||||
int SeekMode;
|
||||
FFSourceResources<FFMS_VideoSource> Res;
|
||||
|
||||
void DecodeNextFrame(int64_t *DTS);
|
||||
protected:
|
||||
void Free(bool CloseCodec);
|
||||
int DecodeNextFrame(int64_t *DTS, char *ErrorMsg, unsigned MsgSize);
|
||||
public:
|
||||
FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFLAVFVideo();
|
||||
FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
||||
FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode);
|
||||
FFMS_Frame *GetFrame(int n);
|
||||
};
|
||||
|
||||
class FFMatroskaVideo : public FFVideo {
|
||||
class FFMatroskaVideo : public FFMS_VideoSource {
|
||||
private:
|
||||
MatroskaFile *MF;
|
||||
MatroskaReaderContext MC;
|
||||
CompressedStream *CS;
|
||||
char ErrorMessage[256];
|
||||
FFSourceResources<FFMS_VideoSource> Res;
|
||||
size_t PacketNumber;
|
||||
|
||||
void DecodeNextFrame();
|
||||
protected:
|
||||
void Free(bool CloseCodec);
|
||||
int DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
|
||||
public:
|
||||
FFMatroskaVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFMatroskaVideo();
|
||||
FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
||||
FFMatroskaVideo(const char *SourceFile, int Track, FFMS_Index *Index, int Threads);
|
||||
FFMS_Frame *GetFrame(int n);
|
||||
};
|
||||
|
||||
#ifdef HAALISOURCE
|
||||
|
||||
class FFHaaliVideo : public FFVideo {
|
||||
class FFHaaliVideo : public FFMS_VideoSource {
|
||||
private:
|
||||
CComPtr<IMMContainer> pMMC;
|
||||
std::vector<uint8_t> CodecPrivate;
|
||||
AVBitStreamFilterContext *BitStreamFilter;
|
||||
FFSourceResources<FFMS_VideoSource> Res;
|
||||
|
||||
void DecodeNextFrame(int64_t *AFirstStartTime);
|
||||
protected:
|
||||
void Free(bool CloseCodec);
|
||||
int DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
|
||||
public:
|
||||
FFHaaliVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize);
|
||||
~FFHaaliVideo();
|
||||
FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
||||
FFHaaliVideo(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, enum FFMS_Sources SourceMode);
|
||||
FFMS_Frame *GetFrame(int n);
|
||||
};
|
||||
|
||||
#endif // HAALISOURCE
|
Loading…
Reference in a new issue