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
|
#ifndef FFMS_H
|
||||||
#define FFMS_H
|
#define FFMS_H
|
||||||
|
|
||||||
|
// Version format: major - minor - micro - bump
|
||||||
|
#define FFMS_VERSION ((2 << 24) | (11 << 16)| (0 << 8) | 5)
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -43,11 +46,56 @@
|
||||||
# define FFMS_API(ret) EXTERN_C ret FFMS_CC
|
# define FFMS_API(ret) EXTERN_C ret FFMS_CC
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FFMS_CLASS_TYPE FFVideo;
|
struct FFMS_ErrorInfo {
|
||||||
FFMS_CLASS_TYPE FFAudio;
|
int ErrorType;
|
||||||
FFMS_CLASS_TYPE FFIndexer;
|
int SubType;
|
||||||
FFMS_CLASS_TYPE FFIndex;
|
int BufferSize;
|
||||||
FFMS_CLASS_TYPE FFTrack;
|
char *Buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
FFMS_CLASS_TYPE FFMS_VideoSource;
|
||||||
|
FFMS_CLASS_TYPE FFMS_AudioSource;
|
||||||
|
FFMS_CLASS_TYPE FFMS_Indexer;
|
||||||
|
FFMS_CLASS_TYPE FFMS_Index;
|
||||||
|
FFMS_CLASS_TYPE FFMS_Track;
|
||||||
|
|
||||||
|
enum FFMS_Errors {
|
||||||
|
// No error
|
||||||
|
FFMS_ERROR_SUCCESS = 0,
|
||||||
|
|
||||||
|
// Main types - where the error occurred
|
||||||
|
FFMS_ERROR_INDEX = 1,
|
||||||
|
FFMS_ERROR_INDEXING,
|
||||||
|
FFMS_ERROR_POSTPROCESSING,
|
||||||
|
FFMS_ERROR_SCALING,
|
||||||
|
FFMS_ERROR_DECODING,
|
||||||
|
FFMS_ERROR_SEEKING,
|
||||||
|
FFMS_ERROR_PARSER,
|
||||||
|
FFMS_ERROR_TRACK,
|
||||||
|
FFMS_ERROR_WAVE_WRITER,
|
||||||
|
FFMS_ERROR_CANCELLED,
|
||||||
|
|
||||||
|
// Subtypes - what caused the error
|
||||||
|
FFMS_ERROR_UNKNOWN = 20,
|
||||||
|
FFMS_ERROR_UNSUPPORTED,
|
||||||
|
FFMS_ERROR_FILE_READ,
|
||||||
|
FFMS_ERROR_FILE_WRITE,
|
||||||
|
FFMS_ERROR_NO_FILE,
|
||||||
|
FFMS_ERROR_VERSION,
|
||||||
|
FFMS_ERROR_ALLOCATION_FAILED,
|
||||||
|
FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
FFMS_ERROR_CODEC,
|
||||||
|
FFMS_ERROR_NOT_AVAILABLE,
|
||||||
|
FFMS_ERROR_FILE_MISMATCH,
|
||||||
|
FFMS_ERROR_USER
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FFMS_Sources {
|
||||||
|
FFMS_SOURCE_LAVF = 0x01,
|
||||||
|
FFMS_SOURCE_MATROSKA = 0x02,
|
||||||
|
FFMS_SOURCE_HAALIMPEG = 0x04,
|
||||||
|
FFMS_SOURCE_HAALIOGG = 0x08
|
||||||
|
};
|
||||||
|
|
||||||
enum FFMS_CPUFeatures {
|
enum FFMS_CPUFeatures {
|
||||||
FFMS_CPU_CAPS_MMX = 0x01,
|
FFMS_CPU_CAPS_MMX = 0x01,
|
||||||
|
@ -65,6 +113,13 @@ enum FFMS_SeekMode {
|
||||||
FFMS_SEEK_AGGRESSIVE = 3
|
FFMS_SEEK_AGGRESSIVE = 3
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum FFMS_IndexErrorHandling {
|
||||||
|
FFMS_IEH_ABORT = 0,
|
||||||
|
FFMS_IEH_CLEAR_TRACK = 1,
|
||||||
|
FFMS_IEH_STOP_TRACK = 2,
|
||||||
|
FFMS_IEH_IGNORE = 3
|
||||||
|
};
|
||||||
|
|
||||||
enum FFMS_TrackType {
|
enum FFMS_TrackType {
|
||||||
FFMS_TYPE_UNKNOWN = -1,
|
FFMS_TYPE_UNKNOWN = -1,
|
||||||
FFMS_TYPE_VIDEO,
|
FFMS_TYPE_VIDEO,
|
||||||
|
@ -106,20 +161,20 @@ enum FFMS_AudioChannel {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FFMS_Resizers {
|
enum FFMS_Resizers {
|
||||||
FFMS_RESIZER_FAST_BILINEAR = 0x01,
|
FFMS_RESIZER_FAST_BILINEAR = 0x0001,
|
||||||
FFMS_RESIZER_BILINEAR = 0x02,
|
FFMS_RESIZER_BILINEAR = 0x0002,
|
||||||
FFMS_RESIZER_BICUBIC = 0x04,
|
FFMS_RESIZER_BICUBIC = 0x0004,
|
||||||
FFMS_RESIZER_X = 0x08,
|
FFMS_RESIZER_X = 0x0008,
|
||||||
FFMS_RESIZER_POINT = 0x10,
|
FFMS_RESIZER_POINT = 0x0010,
|
||||||
FFMS_RESIZER_AREA = 0x20,
|
FFMS_RESIZER_AREA = 0x0020,
|
||||||
FFMS_RESIZER_BICUBLIN = 0x40,
|
FFMS_RESIZER_BICUBLIN = 0x0040,
|
||||||
FFMS_RESIZER_GAUSS = 0x80,
|
FFMS_RESIZER_GAUSS = 0x0080,
|
||||||
FFMS_RESIZER_SINC = 0x100,
|
FFMS_RESIZER_SINC = 0x0100,
|
||||||
FFMS_RESIZER_LANCZOS = 0x200,
|
FFMS_RESIZER_LANCZOS = 0x0200,
|
||||||
FFMS_RESIZER_SPLINE = 0x400
|
FFMS_RESIZER_SPLINE = 0x0400
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FFAVFrame {
|
struct FFMS_Frame {
|
||||||
uint8_t *Data[4];
|
uint8_t *Data[4];
|
||||||
int Linesize[4];
|
int Linesize[4];
|
||||||
int EncodedWidth;
|
int EncodedWidth;
|
||||||
|
@ -135,37 +190,37 @@ struct FFAVFrame {
|
||||||
char PictType;
|
char PictType;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FFTrackTimeBase {
|
struct FFMS_TrackTimeBase {
|
||||||
int64_t Num;
|
int64_t Num;
|
||||||
int64_t Den;
|
int64_t Den;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define FFMS_FRAMEINFO_COMMON int64_t DTS; int RepeatPict; bool KeyFrame;
|
#define FFMS_FRAMEINFO_COMMON int64_t DTS; int RepeatPict; bool KeyFrame;
|
||||||
|
|
||||||
struct FFFrameInfo {
|
struct FFMS_FrameInfo {
|
||||||
FFMS_FRAMEINFO_COMMON
|
FFMS_FRAMEINFO_COMMON
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FFVideoProperties {
|
struct FFMS_VideoProperties {
|
||||||
int Width;
|
|
||||||
int Height;
|
|
||||||
int FPSDenominator;
|
int FPSDenominator;
|
||||||
int FPSNumerator;
|
int FPSNumerator;
|
||||||
int RFFDenominator;
|
int RFFDenominator;
|
||||||
int RFFNumerator;
|
int RFFNumerator;
|
||||||
int NumFrames;
|
int NumFrames;
|
||||||
int VPixelFormat;
|
|
||||||
int SARNum;
|
int SARNum;
|
||||||
int SARDen;
|
int SARDen;
|
||||||
int CropTop;
|
int CropTop;
|
||||||
int CropBottom;
|
int CropBottom;
|
||||||
int CropLeft;
|
int CropLeft;
|
||||||
int CropRight;
|
int CropRight;
|
||||||
|
int TopFieldFirst;
|
||||||
|
int ColorSpace; // same as in the MPEG-2 specs, see AVColorSpace in avcodec.h
|
||||||
|
int ColorRange; // 0=unspecified, 1=16-235, 2=0-255
|
||||||
double FirstTime;
|
double FirstTime;
|
||||||
double LastTime;
|
double LastTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FFAudioProperties {
|
struct FFMS_AudioProperties {
|
||||||
int SampleFormat;
|
int SampleFormat;
|
||||||
int SampleRate;
|
int SampleRate;
|
||||||
int BitsPerSample;
|
int BitsPerSample;
|
||||||
|
@ -177,47 +232,51 @@ struct FFAudioProperties {
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef int (FFMS_CC *TIndexCallback)(int64_t Current, int64_t Total, void *ICPrivate);
|
typedef int (FFMS_CC *TIndexCallback)(int64_t Current, int64_t Total, void *ICPrivate);
|
||||||
typedef int (FFMS_CC *TAudioNameCallback)(const char *SourceFile, int Track, const FFAudioProperties *AP, char *FileName, int FNSize, void *Private);
|
typedef int (FFMS_CC *TAudioNameCallback)(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, void *Private);
|
||||||
|
|
||||||
// Most functions return 0 on success
|
// Most functions return 0 on success
|
||||||
// Functions without error message output can be assumed to never fail in a graceful way
|
// Functions without error message output can be assumed to never fail in a graceful way
|
||||||
FFMS_API(void) FFMS_Init(int CPUFeatures);
|
FFMS_API(void) FFMS_Init(int CPUFeatures);
|
||||||
FFMS_API(int) FFMS_GetLogLevel();
|
FFMS_API(int) FFMS_GetLogLevel();
|
||||||
FFMS_API(void) FFMS_SetLogLevel(int Level);
|
FFMS_API(void) FFMS_SetLogLevel(int Level);
|
||||||
FFMS_API(FFVideo *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(FFMS_VideoSource *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(FFAudio *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(FFMS_AudioSource *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(void) FFMS_DestroyVideoSource(FFVideo *V);
|
FFMS_API(void) FFMS_DestroyVideoSource(FFMS_VideoSource *V);
|
||||||
FFMS_API(void) FFMS_DestroyAudioSource(FFAudio *A);
|
FFMS_API(void) FFMS_DestroyAudioSource(FFMS_AudioSource *A);
|
||||||
FFMS_API(const FFVideoProperties *) FFMS_GetVideoProperties(FFVideo *V);
|
FFMS_API(const FFMS_VideoProperties *) FFMS_GetVideoProperties(FFMS_VideoSource *V);
|
||||||
FFMS_API(const FFAudioProperties *) FFMS_GetAudioProperties(FFAudio *A);
|
FFMS_API(const FFMS_AudioProperties *) FFMS_GetAudioProperties(FFMS_AudioSource *A);
|
||||||
FFMS_API(const FFAVFrame *) FFMS_GetFrame(FFVideo *V, int n, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(const FFMS_Frame *) FFMS_GetFrame(FFMS_VideoSource *V, int n, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(const FFAVFrame *) FFMS_GetFrameByTime(FFVideo *V, double Time, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(const FFMS_Frame *) FFMS_GetFrameByTime(FFMS_VideoSource *V, double Time, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(int) FFMS_GetAudio(FFAudio *A, void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(int) FFMS_GetAudio(FFMS_AudioSource *A, void *Buf, int64_t Start, int64_t Count, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(int) FFMS_SetOutputFormatV(FFVideo *V, int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(int) FFMS_SetOutputFormatV(FFMS_VideoSource *V, int64_t TargetFormats, int Width, int Height, int Resizer, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(void) FFMS_ResetOutputFormatV(FFVideo *V);
|
FFMS_API(void) FFMS_ResetOutputFormatV(FFMS_VideoSource *V);
|
||||||
FFMS_API(void) FFMS_DestroyIndex(FFIndex *Index);
|
FFMS_API(int) FFMS_SetPP(FFMS_VideoSource *V, const char *PP, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(int) FFMS_GetFirstTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(void) FFMS_ResetPP(FFMS_VideoSource *V);
|
||||||
FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index);
|
||||||
FFMS_API(int) FFMS_GetNumTracks(FFIndex *Index);
|
FFMS_API(int) FFMS_GetFirstTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(int) FFMS_GetNumTracksI(FFIndexer *Indexer);
|
FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(int) FFMS_GetTrackType(FFTrack *T);
|
FFMS_API(int) FFMS_GetNumTracks(FFMS_Index *Index);
|
||||||
FFMS_API(int) FFMS_GetTrackTypeI(FFIndexer *Indexer, int Track);
|
FFMS_API(int) FFMS_GetNumTracksI(FFMS_Indexer *Indexer);
|
||||||
FFMS_API(const char *) FFMS_GetCodecNameI(FFIndexer *Indexer, int Track);
|
FFMS_API(int) FFMS_GetTrackType(FFMS_Track *T);
|
||||||
FFMS_API(int) FFMS_GetNumFrames(FFTrack *T);
|
FFMS_API(int) FFMS_GetTrackTypeI(FFMS_Indexer *Indexer, int Track);
|
||||||
FFMS_API(const FFFrameInfo *) FFMS_GetFrameInfo(FFTrack *T, int Frame);
|
FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track);
|
||||||
FFMS_API(FFTrack *) FFMS_GetTrackFromIndex(FFIndex *Index, int Track);
|
FFMS_API(int) FFMS_GetNumFrames(FFMS_Track *T);
|
||||||
FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V);
|
FFMS_API(const FFMS_FrameInfo *) FFMS_GetFrameInfo(FFMS_Track *T, int Frame);
|
||||||
FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A);
|
FFMS_API(FFMS_Track *) FFMS_GetTrackFromIndex(FFMS_Index *Index, int Track);
|
||||||
FFMS_API(const FFTrackTimeBase *) FFMS_GetTimeBase(FFTrack *T);
|
FFMS_API(FFMS_Track *) FFMS_GetTrackFromVideo(FFMS_VideoSource *V);
|
||||||
FFMS_API(int) FFMS_WriteTimecodes(FFTrack *T, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(FFMS_Track *) FFMS_GetTrackFromAudio(FFMS_AudioSource *A);
|
||||||
FFMS_API(FFIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(const FFMS_TrackTimeBase *) FFMS_GetTimeBase(FFMS_Track *T);
|
||||||
FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFAudioProperties *AP, char *FileName, int FNSize, void *Private);
|
FFMS_API(int) FFMS_WriteTimecodes(FFMS_Track *T, const char *TimecodeFile, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(FFMS_Index *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(FFIndex *) FFMS_DoIndexing(FFIndexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, void *Private);
|
||||||
FFMS_API(void) FFMS_CancelIndexing(FFIndexer *Indexer);
|
FFMS_API(FFMS_Indexer *) FFMS_CreateIndexer(const char *SourceFile, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(FFIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(FFMS_Index *) FFMS_DoIndexing(FFMS_Indexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(int) FFMS_IndexBelongsToFile(FFIndex *Index, const char *SourceFile, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(void) FFMS_CancelIndexing(FFMS_Indexer *Indexer);
|
||||||
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
FFMS_API(FFMS_Index *) FFMS_ReadIndex(const char *IndexFile, FFMS_ErrorInfo *ErrorInfo);
|
||||||
|
FFMS_API(int) FFMS_IndexBelongsToFile(FFMS_Index *Index, const char *SourceFile, FFMS_ErrorInfo *ErrorInfo);
|
||||||
|
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_API(int) FFMS_GetPixFmt(const char *Name);
|
FFMS_API(int) FFMS_GetPixFmt(const char *Name);
|
||||||
|
FFMS_API(int) FFMS_GetPresentSources();
|
||||||
|
FFMS_API(int) FFMS_GetEnabledSources();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#include "ffaudiosource.h"
|
#include "audiosource.h"
|
||||||
|
|
||||||
/* Audio Cache */
|
/* Audio Cache */
|
||||||
|
|
||||||
|
@ -93,15 +93,32 @@ int64_t TAudioCache::FillRequest(int64_t Start, int64_t Samples, uint8_t *Dst) {
|
||||||
return FFMIN(Ret, Start + Samples);
|
return FFMIN(Ret, Start + Samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FFAudio base class */
|
/* FFMS_AudioSource base class */
|
||||||
|
|
||||||
FFAudio::FFAudio(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 10) {
|
FFMS_AudioSource::FFMS_AudioSource(const char *SourceFile, FFMS_Index *Index, int Track) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 10), CurrentSample(0) {
|
||||||
if (Index->CompareFileSignature(SourceFile, ErrorMsg, MsgSize))
|
if (Track < 0 || Track >= static_cast<int>(Index->size()))
|
||||||
throw ErrorMsg;
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Out of bounds track index selected");
|
||||||
|
|
||||||
CurrentSample = 0;
|
if (Index->at(Track).TT != FFMS_TYPE_AUDIO)
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Not an audio track");
|
||||||
|
|
||||||
|
if (Index[Track].size() == 0)
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Audio track contains no audio frames");
|
||||||
|
|
||||||
|
if (!Index->CompareFileSignature(SourceFile))
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH,
|
||||||
|
"The index does not match the source file");
|
||||||
}
|
}
|
||||||
|
|
||||||
FFAudio::~FFAudio() {
|
FFMS_AudioSource::~FFMS_AudioSource() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FFMS_AudioSource::GetAudioCheck(int64_t Start, int64_t Count) {
|
||||||
|
if (Start < 0 || Start + Count > AP.NumSamples)
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Out of bounds audio samples requested");
|
||||||
|
}
|
|
@ -68,63 +68,68 @@ public:
|
||||||
int64_t FillRequest(int64_t Start, int64_t Samples, uint8_t *Dst);
|
int64_t FillRequest(int64_t Start, int64_t Samples, uint8_t *Dst);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FFAudio {
|
class FFMS_AudioSource {
|
||||||
|
friend class FFSourceResources<FFMS_AudioSource>;
|
||||||
protected:
|
protected:
|
||||||
TAudioCache AudioCache;
|
TAudioCache AudioCache;
|
||||||
int64_t CurrentSample;
|
int64_t CurrentSample;
|
||||||
std::vector<uint8_t> DecodingBuffer;
|
std::vector<uint8_t> DecodingBuffer;
|
||||||
FFTrack Frames;
|
FFMS_Track Frames;
|
||||||
AVCodecContext *CodecContext;
|
AVCodecContext *CodecContext;
|
||||||
int AudioTrack;
|
int AudioTrack;
|
||||||
FFAudioProperties AP;
|
FFMS_AudioProperties AP;
|
||||||
|
|
||||||
|
virtual void Free(bool CloseCodec) = 0;
|
||||||
public:
|
public:
|
||||||
FFAudio(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
FFMS_AudioSource(const char *SourceFile, FFMS_Index *Index, int Track);
|
||||||
virtual ~FFAudio();
|
virtual ~FFMS_AudioSource();
|
||||||
FFTrack *GetFFTrack() { return &Frames; }
|
FFMS_Track *GetTrack() { return &Frames; }
|
||||||
const FFAudioProperties& GetFFAudioProperties() { return AP; }
|
const FFMS_AudioProperties& GetAudioProperties() { return AP; }
|
||||||
virtual int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) = 0;
|
virtual void GetAudio(void *Buf, int64_t Start, int64_t Count) = 0;
|
||||||
|
void GetAudioCheck(int64_t Start, int64_t Count);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FFLAVFAudio : public FFAudio {
|
class FFLAVFAudio : public FFMS_AudioSource {
|
||||||
private:
|
private:
|
||||||
AVFormatContext *FormatContext;
|
AVFormatContext *FormatContext;
|
||||||
|
FFSourceResources<FFMS_AudioSource> Res;
|
||||||
|
|
||||||
int DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned MsgSize);
|
void DecodeNextAudioBlock(int64_t *Count);
|
||||||
void Free(bool CloseCodec);
|
void Free(bool CloseCodec);
|
||||||
public:
|
public:
|
||||||
FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
FFLAVFAudio(const char *SourceFile, int Track, FFMS_Index *Index);
|
||||||
~FFLAVFAudio();
|
void GetAudio(void *Buf, int64_t Start, int64_t Count);
|
||||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class FFMatroskaAudio : public FFAudio {
|
class FFMatroskaAudio : public FFMS_AudioSource {
|
||||||
private:
|
private:
|
||||||
MatroskaFile *MF;
|
MatroskaFile *MF;
|
||||||
MatroskaReaderContext MC;
|
MatroskaReaderContext MC;
|
||||||
CompressedStream *CS;
|
CompressedStream *CS;
|
||||||
char ErrorMessage[256];
|
char ErrorMessage[256];
|
||||||
|
FFSourceResources<FFMS_AudioSource> Res;
|
||||||
|
size_t PacketNumber;
|
||||||
|
|
||||||
int DecodeNextAudioBlock(int64_t *Count, int AudioBlock, char *ErrorMsg, unsigned MsgSize);
|
void DecodeNextAudioBlock(int64_t *Count);
|
||||||
void Free(bool CloseCodec);
|
void Free(bool CloseCodec);
|
||||||
public:
|
public:
|
||||||
FFMatroskaAudio(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index *Index);
|
||||||
~FFMatroskaAudio();
|
void GetAudio(void *Buf, int64_t Start, int64_t Count);
|
||||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
|
|
||||||
class FFHaaliAudio : public FFAudio {
|
class FFHaaliAudio : public FFMS_AudioSource {
|
||||||
private:
|
private:
|
||||||
CComPtr<IMMContainer> pMMC;
|
CComPtr<IMMContainer> pMMC;
|
||||||
std::vector<uint8_t> CodecPrivate;
|
std::vector<uint8_t> CodecPrivate;
|
||||||
|
FFSourceResources<FFMS_AudioSource> Res;
|
||||||
|
|
||||||
|
void DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count);
|
||||||
void Free(bool CloseCodec);
|
void Free(bool CloseCodec);
|
||||||
int DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count, char *ErrorMsg, unsigned MsgSize);
|
|
||||||
public:
|
public:
|
||||||
FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index, int SourceMode, char *ErrorMsg, unsigned MsgSize);
|
FFHaaliAudio(const char *SourceFile, int Track, FFMS_Index *Index, enum FFMS_Sources SourceMode);
|
||||||
~FFHaaliAudio();
|
void GetAudio(void *Buf, int64_t Start, int64_t Count);
|
||||||
int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HAALISOURCE
|
#endif // HAALISOURCE
|
|
@ -21,21 +21,22 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include "ffms.h"
|
#include "ffms.h"
|
||||||
#include "ffvideosource.h"
|
#include "videosource.h"
|
||||||
#include "ffaudiosource.h"
|
#include "audiosource.h"
|
||||||
#include "indexing.h"
|
#include "indexing.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static bool FFmpegInited = false;
|
static bool FFmpegInited = false;
|
||||||
|
bool HasHaaliMPEG = false;
|
||||||
|
bool HasHaaliOGG = false;
|
||||||
int CPUFeatures = 0;
|
int CPUFeatures = 0;
|
||||||
|
|
||||||
#ifdef FFMS_WIN_DEBUG
|
#ifdef FFMS_WIN_DEBUG
|
||||||
|
|
||||||
extern "C" int av_log_level;
|
extern "C" int av_log_level;
|
||||||
|
|
||||||
void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl)
|
void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl) {
|
||||||
{
|
|
||||||
static int print_prefix=1;
|
static int print_prefix=1;
|
||||||
static int count;
|
static int count;
|
||||||
static char line[1024], prev[1024];
|
static char line[1024], prev[1024];
|
||||||
|
@ -56,7 +57,9 @@ void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(count>0){
|
if(count>0){
|
||||||
fprintf(stderr, " Last message repeated %d times\n", count);
|
std::stringstream ss;
|
||||||
|
ss << " Last message repeated " << count << " times\n";
|
||||||
|
OutputDebugStringA(ss.str().c_str());
|
||||||
count=0;
|
count=0;
|
||||||
}
|
}
|
||||||
OutputDebugStringA(line);
|
OutputDebugStringA(line);
|
||||||
|
@ -75,6 +78,13 @@ FFMS_API(void) FFMS_Init(int CPUFeatures) {
|
||||||
av_log_set_level(AV_LOG_QUIET);
|
av_log_set_level(AV_LOG_QUIET);
|
||||||
#endif
|
#endif
|
||||||
::CPUFeatures = CPUFeatures;
|
::CPUFeatures = CPUFeatures;
|
||||||
|
#ifdef HAALISOURCE
|
||||||
|
CComPtr<IMMContainer> pMMC;
|
||||||
|
HasHaaliMPEG = !FAILED(pMMC.CoCreateInstance(HAALI_MPEG_PARSER));
|
||||||
|
pMMC = NULL;
|
||||||
|
HasHaaliOGG = !FAILED(pMMC.CoCreateInstance(HAALI_OGG_PARSER));
|
||||||
|
pMMC = NULL;
|
||||||
|
#endif
|
||||||
FFmpegInited = true;
|
FFmpegInited = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,171 +97,224 @@ FFMS_API(void) FFMS_SetLogLevel(int Level) {
|
||||||
av_log_set_level(Level);
|
av_log_set_level(Level);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(FFVideo *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(FFMS_VideoSource *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
if (Track < 0 || Track >= static_cast<int>(Index->size())) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Out of bounds track index selected");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Index->at(Track).TT != FFMS_TYPE_VIDEO) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Not a video track");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (Index->Decoder) {
|
switch (Index->Decoder) {
|
||||||
case 0: return new FFLAVFVideo(SourceFile, Track, Index, PP, Threads, SeekMode, ErrorMsg, MsgSize);
|
case FFMS_SOURCE_LAVF:
|
||||||
case 1: return new FFMatroskaVideo(SourceFile, Track, Index, PP, Threads, ErrorMsg, MsgSize);
|
return new FFLAVFVideo(SourceFile, Track, Index, Threads, SeekMode);
|
||||||
|
case FFMS_SOURCE_MATROSKA:
|
||||||
|
return new FFMatroskaVideo(SourceFile, Track, Index, Threads);
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
case 2: return new FFHaaliVideo(SourceFile, Track, Index, PP, Threads, 0, ErrorMsg, MsgSize);
|
case FFMS_SOURCE_HAALIMPEG:
|
||||||
case 3: return new FFHaaliVideo(SourceFile, Track, Index, PP, Threads, 1, ErrorMsg, MsgSize);
|
if (HasHaaliMPEG)
|
||||||
|
return new FFHaaliVideo(SourceFile, Track, Index, Threads, FFMS_SOURCE_HAALIMPEG);
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali MPEG/TS source unavailable");
|
||||||
|
case FFMS_SOURCE_HAALIOGG:
|
||||||
|
if (HasHaaliOGG)
|
||||||
|
return new FFHaaliVideo(SourceFile, Track, Index, Threads, FFMS_SOURCE_HAALIOGG);
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali OGG/OGM source unavailable");
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
snprintf(ErrorMsg, MsgSize, "Unsupported format");
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Unsupported format");
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (FFMS_Exception &e) {
|
||||||
|
e.CopyOut(ErrorInfo);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(FFAudio *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(FFMS_AudioSource *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
if (Track < 0 || Track >= static_cast<int>(Index->size())) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Out of bounds track index selected");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Index->at(Track).TT != FFMS_TYPE_AUDIO) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Not an audio track");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
switch (Index->Decoder) {
|
switch (Index->Decoder) {
|
||||||
case 0: return new FFLAVFAudio(SourceFile, Track, Index, ErrorMsg, MsgSize);
|
case FFMS_SOURCE_LAVF:
|
||||||
case 1: return new FFMatroskaAudio(SourceFile, Track, Index, ErrorMsg, MsgSize);
|
return new FFLAVFAudio(SourceFile, Track, Index);
|
||||||
|
case FFMS_SOURCE_MATROSKA:
|
||||||
|
return new FFMatroskaAudio(SourceFile, Track, Index);
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
case 2: return new FFHaaliAudio(SourceFile, Track, Index, 0, ErrorMsg, MsgSize);
|
case FFMS_SOURCE_HAALIMPEG:
|
||||||
case 3: return new FFHaaliAudio(SourceFile, Track, Index, 1, ErrorMsg, MsgSize);
|
if (HasHaaliMPEG)
|
||||||
|
return new FFHaaliAudio(SourceFile, Track, Index, FFMS_SOURCE_HAALIMPEG);
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali MPEG/TS source unavailable");
|
||||||
|
case FFMS_SOURCE_HAALIOGG:
|
||||||
|
if (HasHaaliOGG)
|
||||||
|
return new FFHaaliAudio(SourceFile, Track, Index, FFMS_SOURCE_HAALIOGG);
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_NOT_AVAILABLE, "Haali OGG/OGM source unavailable");
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
snprintf(ErrorMsg, MsgSize, "Unsupported format");
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Unsupported format");
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
} catch (...) {
|
} catch (FFMS_Exception &e) {
|
||||||
|
e.CopyOut(ErrorInfo);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(void) FFMS_DestroyVideoSource(FFVideo *V) {
|
FFMS_API(void) FFMS_DestroyVideoSource(FFMS_VideoSource *V) {
|
||||||
delete V;
|
delete V;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(void) FFMS_DestroyAudioSource(FFAudio *A) {
|
FFMS_API(void) FFMS_DestroyAudioSource(FFMS_AudioSource *A) {
|
||||||
delete A;
|
delete A;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(const FFVideoProperties *) FFMS_GetVideoProperties(FFVideo *V) {
|
FFMS_API(const FFMS_VideoProperties *) FFMS_GetVideoProperties(FFMS_VideoSource *V) {
|
||||||
return &V->GetFFVideoProperties();
|
return &V->GetVideoProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(const FFAudioProperties *) FFMS_GetAudioProperties(FFAudio *A) {
|
FFMS_API(const FFMS_AudioProperties *) FFMS_GetAudioProperties(FFMS_AudioSource *A) {
|
||||||
return &A->GetFFAudioProperties();
|
return &A->GetAudioProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(const FFAVFrame *) FFMS_GetFrame(FFVideo *V, int n, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(const FFMS_Frame *) FFMS_GetFrame(FFMS_VideoSource *V, int n, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
return (FFAVFrame *)V->GetFrame(n, ErrorMsg, MsgSize);
|
ClearErrorInfo(ErrorInfo);
|
||||||
|
try {
|
||||||
|
return V->GetFrame(n);
|
||||||
|
} catch (FFMS_Exception &e) {
|
||||||
|
e.CopyOut(ErrorInfo);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(const FFAVFrame *) FFMS_GetFrameByTime(FFVideo *V, double Time, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(const FFMS_Frame *) FFMS_GetFrameByTime(FFMS_VideoSource *V, double Time, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
return (FFAVFrame *)V->GetFrameByTime(Time, ErrorMsg, MsgSize);
|
ClearErrorInfo(ErrorInfo);
|
||||||
|
try {
|
||||||
|
return (FFMS_Frame *)V->GetFrameByTime(Time);
|
||||||
|
} catch (FFMS_Exception &e) {
|
||||||
|
e.CopyOut(ErrorInfo);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_GetAudio(FFAudio *A, void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(int) FFMS_GetAudio(FFMS_AudioSource *A, void *Buf, int64_t Start, int64_t Count, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
return A->GetAudio(Buf, Start, Count, ErrorMsg, MsgSize);
|
ClearErrorInfo(ErrorInfo);
|
||||||
|
try {
|
||||||
|
A->GetAudio(Buf, Start, Count);
|
||||||
|
} catch (FFMS_Exception &e) {
|
||||||
|
return e.CopyOut(ErrorInfo);
|
||||||
|
}
|
||||||
|
return FFMS_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_SetOutputFormatV(FFVideo *V, int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(int) FFMS_SetOutputFormatV(FFMS_VideoSource *V, int64_t TargetFormats, int Width, int Height, int Resizer, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
return V->SetOutputFormat(TargetFormats, Width, Height, Resizer, ErrorMsg, MsgSize);
|
ClearErrorInfo(ErrorInfo);
|
||||||
|
try {
|
||||||
|
V->SetOutputFormat(TargetFormats, Width, Height, Resizer);
|
||||||
|
} catch (FFMS_Exception &e) {
|
||||||
|
return e.CopyOut(ErrorInfo);
|
||||||
|
}
|
||||||
|
return FFMS_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(void) FFMS_ResetOutputFormatV(FFVideo *V) {
|
FFMS_API(void) FFMS_ResetOutputFormatV(FFMS_VideoSource *V) {
|
||||||
V->ResetOutputFormat();
|
V->ResetOutputFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(void) FFMS_DestroyIndex(FFIndex *Index) {
|
FFMS_API(int) FFMS_SetPP(FFMS_VideoSource *V, const char *PP, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
|
ClearErrorInfo(ErrorInfo);
|
||||||
|
try {
|
||||||
|
V->SetPP(PP);
|
||||||
|
} catch (FFMS_Exception &e) {
|
||||||
|
return e.CopyOut(ErrorInfo);
|
||||||
|
}
|
||||||
|
return FFMS_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
FFMS_API(void) FFMS_ResetPP(FFMS_VideoSource *V) {
|
||||||
|
V->ResetPP();
|
||||||
|
}
|
||||||
|
|
||||||
|
FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index) {
|
||||||
delete Index;
|
delete Index;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_GetFirstTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(int) FFMS_GetFirstTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
|
ClearErrorInfo(ErrorInfo);
|
||||||
for (int i = 0; i < static_cast<int>(Index->size()); i++)
|
for (int i = 0; i < static_cast<int>(Index->size()); i++)
|
||||||
if ((*Index)[i].TT == TrackType)
|
if ((*Index)[i].TT == TrackType)
|
||||||
return i;
|
return i;
|
||||||
snprintf(ErrorMsg, MsgSize, "No suitable, indexed track found");
|
|
||||||
return -1;
|
try {
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE,
|
||||||
|
"No suitable, indexed track found");
|
||||||
|
} catch (FFMS_Exception &e) {
|
||||||
|
e.CopyOut(ErrorInfo);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFIndex *Index, int TrackType, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
|
ClearErrorInfo(ErrorInfo);
|
||||||
for (int i = 0; i < static_cast<int>(Index->size()); i++)
|
for (int i = 0; i < static_cast<int>(Index->size()); i++)
|
||||||
if ((*Index)[i].TT == TrackType && (*Index)[i].size() > 0)
|
if ((*Index)[i].TT == TrackType && (*Index)[i].size() > 0)
|
||||||
return i;
|
return i;
|
||||||
snprintf(ErrorMsg, MsgSize, "No suitable, indexed track found");
|
try {
|
||||||
return -1;
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE,
|
||||||
|
"No suitable, indexed track found");
|
||||||
|
} catch (FFMS_Exception &e) {
|
||||||
|
e.CopyOut(ErrorInfo);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_GetNumTracks(FFIndex *Index) {
|
FFMS_API(int) FFMS_GetNumTracks(FFMS_Index *Index) {
|
||||||
return Index->size();
|
return Index->size();
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_GetNumTracksI(FFIndexer *Indexer) {
|
FFMS_API(int) FFMS_GetNumTracksI(FFMS_Indexer *Indexer) {
|
||||||
return Indexer->GetNumberOfTracks();
|
return Indexer->GetNumberOfTracks();
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_GetTrackType(FFTrack *T) {
|
FFMS_API(int) FFMS_GetTrackType(FFMS_Track *T) {
|
||||||
return T->TT;
|
return T->TT;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_GetTrackTypeI(FFIndexer *Indexer, int Track) {
|
FFMS_API(int) FFMS_GetTrackTypeI(FFMS_Indexer *Indexer, int Track) {
|
||||||
return Indexer->GetTrackType(Track);
|
return Indexer->GetTrackType(Track);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(const char *) FFMS_GetCodecNameI(FFIndexer *Indexer, int Track) {
|
FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track) {
|
||||||
return Indexer->GetTrackCodec(Track);
|
return Indexer->GetTrackCodec(Track);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_GetNumFrames(FFTrack *T) {
|
FFMS_API(int) FFMS_GetNumFrames(FFMS_Track *T) {
|
||||||
return T->size();
|
return T->size();
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(const FFFrameInfo *) FFMS_GetFrameInfo(FFTrack *T, int Frame) {
|
FFMS_API(const FFMS_FrameInfo *) FFMS_GetFrameInfo(FFMS_Track *T, int Frame) {
|
||||||
return reinterpret_cast<FFFrameInfo *>(&(*T)[Frame]);
|
return reinterpret_cast<FFMS_FrameInfo *>(&(*T)[Frame]);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(FFTrack *) FFMS_GetTrackFromIndex(FFIndex *Index, int Track) {
|
FFMS_API(FFMS_Track *) FFMS_GetTrackFromIndex(FFMS_Index *Index, int Track) {
|
||||||
return &(*Index)[Track];
|
return &(*Index)[Track];
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(FFTrack *) FFMS_GetTrackFromVideo(FFVideo *V) {
|
FFMS_API(FFMS_Track *) FFMS_GetTrackFromVideo(FFMS_VideoSource *V) {
|
||||||
return V->GetFFTrack();
|
return V->GetTrack();
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(FFTrack *) FFMS_GetTrackFromAudio(FFAudio *A) {
|
FFMS_API(FFMS_Track *) FFMS_GetTrackFromAudio(FFMS_AudioSource *A) {
|
||||||
return A->GetFFTrack();
|
return A->GetTrack();
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(const FFTrackTimeBase *) FFMS_GetTimeBase(FFTrack *T) {
|
FFMS_API(const FFMS_TrackTimeBase *) FFMS_GetTimeBase(FFMS_Track *T) {
|
||||||
return &T->TB;
|
return &T->TB;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_WriteTimecodes(FFTrack *T, const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(int) FFMS_WriteTimecodes(FFMS_Track *T, const char *TimecodeFile, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
return T->WriteTimecodes(TimecodeFile, ErrorMsg, MsgSize);
|
ClearErrorInfo(ErrorInfo);
|
||||||
|
try {
|
||||||
|
T->WriteTimecodes(TimecodeFile);
|
||||||
|
} catch (FFMS_Exception &e) {
|
||||||
|
return e.CopyOut(ErrorInfo);
|
||||||
|
}
|
||||||
|
return FFMS_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(FFIndex *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(FFMS_Index *) FFMS_MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
FFIndexer *Indexer = FFMS_CreateIndexer(SourceFile, ErrorMsg, MsgSize);
|
FFMS_Indexer *Indexer = FFMS_CreateIndexer(SourceFile, ErrorInfo);
|
||||||
if (!Indexer)
|
if (!Indexer)
|
||||||
return NULL;
|
return NULL;
|
||||||
return FFMS_DoIndexing(Indexer, IndexMask, DumpMask, ANC, ANCPrivate, IgnoreDecodeErrors, IC, ICPrivate, ErrorMsg, MsgSize);
|
return FFMS_DoIndexing(Indexer, IndexMask, DumpMask, ANC, ANCPrivate, ErrorHandling, IC, ICPrivate, ErrorInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Used by FFMS_DefaultAudioFilename */
|
/* Used by FFMS_DefaultAudioFilename */
|
||||||
|
@ -270,7 +333,7 @@ static void ReplaceString(std::string &s, std::string from, std::string to) {
|
||||||
s.replace(idx, from.length(), to);
|
s.replace(idx, from.length(), to);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFAudioProperties *AP, char *FileName, int FNSize, void *Private) {
|
FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const FFMS_AudioProperties *AP, char *FileName, int FNSize, void *Private) {
|
||||||
std::string s = static_cast<char *>(Private);
|
std::string s = static_cast<char *>(Private);
|
||||||
|
|
||||||
ReplaceString(s, "%sourcefile%", SourceFile);
|
ReplaceString(s, "%sourcefile%", SourceFile);
|
||||||
|
@ -281,55 +344,99 @@ FFMS_API(int) FFMS_DefaultAudioFilename(const char *SourceFile, int Track, const
|
||||||
ReplaceString(s, "%bps%", IntToStr(AP->BitsPerSample));
|
ReplaceString(s, "%bps%", IntToStr(AP->BitsPerSample));
|
||||||
ReplaceString(s, "%delay%", IntToStr(static_cast<int>(AP->FirstTime)));
|
ReplaceString(s, "%delay%", IntToStr(static_cast<int>(AP->FirstTime)));
|
||||||
|
|
||||||
if (FileName == NULL) {
|
if (FileName != NULL)
|
||||||
return s.length() + 1;
|
|
||||||
} else {
|
|
||||||
strcpy(FileName, s.c_str());
|
strcpy(FileName, s.c_str());
|
||||||
return 0;
|
|
||||||
}
|
return s.length() + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(FFIndexer *) FFMS_CreateIndexer(const char *SourceFile, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(FFMS_Indexer *) FFMS_CreateIndexer(const char *SourceFile, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
|
ClearErrorInfo(ErrorInfo);
|
||||||
try {
|
try {
|
||||||
return FFIndexer::CreateFFIndexer(SourceFile, ErrorMsg, MsgSize);
|
return FFMS_Indexer::CreateIndexer(SourceFile);
|
||||||
} catch (...) {
|
} catch (FFMS_Exception &e) {
|
||||||
|
e.CopyOut(ErrorInfo);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(FFIndex *) FFMS_DoIndexing(FFIndexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, bool IgnoreDecodeErrors, TIndexCallback IC, void *ICPrivate, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(FFMS_Index *) FFMS_DoIndexing(FFMS_Indexer *Indexer, int IndexMask, int DumpMask, TAudioNameCallback ANC, void *ANCPrivate, int ErrorHandling, TIndexCallback IC, void *ICPrivate, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
|
ClearErrorInfo(ErrorInfo);
|
||||||
|
|
||||||
Indexer->SetIndexMask(IndexMask | DumpMask);
|
Indexer->SetIndexMask(IndexMask | DumpMask);
|
||||||
Indexer->SetDumpMask(DumpMask);
|
Indexer->SetDumpMask(DumpMask);
|
||||||
Indexer->SetIgnoreDecodeErrors(IgnoreDecodeErrors);
|
Indexer->SetErrorHandling(ErrorHandling);
|
||||||
Indexer->SetProgressCallback(IC, ICPrivate);
|
Indexer->SetProgressCallback(IC, ICPrivate);
|
||||||
Indexer->SetAudioNameCallback(ANC, ANCPrivate);
|
Indexer->SetAudioNameCallback(ANC, ANCPrivate);
|
||||||
FFIndex *Index = Indexer->DoIndexing(ErrorMsg, MsgSize);
|
|
||||||
|
FFMS_Index *Index = NULL;
|
||||||
|
try {
|
||||||
|
Index = Indexer->DoIndexing();
|
||||||
|
} catch (FFMS_Exception &e) {
|
||||||
|
e.CopyOut(ErrorInfo);
|
||||||
|
}
|
||||||
delete Indexer;
|
delete Indexer;
|
||||||
return Index;
|
return Index;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(void) FFMS_CancelIndexing(FFIndexer *Indexer) {
|
FFMS_API(void) FFMS_CancelIndexing(FFMS_Indexer *Indexer) {
|
||||||
delete Indexer;
|
delete Indexer;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(FFIndex *) FFMS_ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(FFMS_Index *) FFMS_ReadIndex(const char *IndexFile, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
FFIndex *Index = new FFIndex();
|
ClearErrorInfo(ErrorInfo);
|
||||||
if (Index->ReadIndex(IndexFile, ErrorMsg, MsgSize)) {
|
FFMS_Index *Index = new FFMS_Index();
|
||||||
|
try {
|
||||||
|
Index->ReadIndex(IndexFile);
|
||||||
|
} catch (FFMS_Exception &e) {
|
||||||
delete Index;
|
delete Index;
|
||||||
|
e.CopyOut(ErrorInfo);
|
||||||
return NULL;
|
return NULL;
|
||||||
} else {
|
|
||||||
return Index;
|
|
||||||
}
|
}
|
||||||
|
return Index;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_IndexBelongsToFile(FFIndex *Index, const char *SourceFile, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(int) FFMS_IndexBelongsToFile(FFMS_Index *Index, const char *SourceFile, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
return Index->CompareFileSignature(SourceFile, ErrorMsg, MsgSize);
|
ClearErrorInfo(ErrorInfo);
|
||||||
|
try {
|
||||||
|
if (!Index->CompareFileSignature(SourceFile))
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH,
|
||||||
|
"The index does not belong to the file");
|
||||||
|
} catch (FFMS_Exception &e) {
|
||||||
|
return e.CopyOut(ErrorInfo);
|
||||||
|
}
|
||||||
|
return FFMS_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_API(int) FFMS_WriteIndex(const char *IndexFile, FFMS_Index *Index, FFMS_ErrorInfo *ErrorInfo) {
|
||||||
return Index->WriteIndex(IndexFile, ErrorMsg, MsgSize);
|
ClearErrorInfo(ErrorInfo);
|
||||||
|
try {
|
||||||
|
Index->WriteIndex(IndexFile);
|
||||||
|
} catch (FFMS_Exception &e) {
|
||||||
|
return e.CopyOut(ErrorInfo);
|
||||||
|
}
|
||||||
|
return FFMS_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMS_API(int) FFMS_GetPixFmt(const char *Name) {
|
FFMS_API(int) FFMS_GetPixFmt(const char *Name) {
|
||||||
return avcodec_get_pix_fmt(Name);
|
return avcodec_get_pix_fmt(Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FFMS_API(int) FFMS_GetPresentSources() {
|
||||||
|
int Sources = FFMS_SOURCE_LAVF | FFMS_SOURCE_MATROSKA;
|
||||||
|
#ifdef HAALISOURCE
|
||||||
|
Sources |= FFMS_SOURCE_HAALIMPEG | FFMS_SOURCE_HAALIOGG;
|
||||||
|
#endif
|
||||||
|
return Sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
FFMS_API(int) FFMS_GetEnabledSources() {
|
||||||
|
if (!FFmpegInited)
|
||||||
|
return 0;
|
||||||
|
int Sources = FFMS_SOURCE_LAVF | FFMS_SOURCE_MATROSKA;
|
||||||
|
if (HasHaaliMPEG)
|
||||||
|
Sources |= FFMS_SOURCE_HAALIMPEG;
|
||||||
|
if (HasHaaliOGG)
|
||||||
|
Sources |= FFMS_SOURCE_HAALIOGG;
|
||||||
|
return Sources;
|
||||||
|
}
|
||||||
|
|
|
@ -116,10 +116,10 @@ DEFINE_GUID(MEDIASUBTYPE_WMV3,
|
||||||
0x33564d57, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
|
0x33564d57, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
|
||||||
|
|
||||||
// FIXME: move somewhere else?
|
// FIXME: move somewhere else?
|
||||||
DEFINE_GUID(HAALI_TS_Parser,
|
DEFINE_GUID(HAALI_MPEG_PARSER,
|
||||||
0xB841F346, 0x4835, 0x4de8, 0xAA, 0x5E, 0x2E, 0x7C, 0xD2, 0xD4, 0xC4, 0x35);
|
0xB841F346, 0x4835, 0x4de8, 0xAA, 0x5E, 0x2E, 0x7C, 0xD2, 0xD4, 0xC4, 0x35);
|
||||||
|
|
||||||
DEFINE_GUID(HAALI_OGM_Parser,
|
DEFINE_GUID(HAALI_OGG_PARSER,
|
||||||
0xDB43B405, 0x43AA, 0x4F01, 0x82, 0xD8, 0xD8, 0x4D, 0x47, 0xE6, 0x01, 0x9C);
|
0xDB43B405, 0x43AA, 0x4F01, 0x82, 0xD8, 0xD8, 0x4D, 0x47, 0xE6, 0x01, 0x9C);
|
||||||
|
|
||||||
//DB43B405-43AA-4F01-82D8-D84D47E6019C
|
//DB43B405-43AA-4F01-82D8-D84D47E6019C
|
||||||
|
|
|
@ -20,7 +20,9 @@
|
||||||
|
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
|
|
||||||
#include "ffaudiosource.h"
|
#include "audiosource.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void FFHaaliAudio::Free(bool CloseCodec) {
|
void FFHaaliAudio::Free(bool CloseCodec) {
|
||||||
if (CloseCodec)
|
if (CloseCodec)
|
||||||
|
@ -28,14 +30,14 @@ void FFHaaliAudio::Free(bool CloseCodec) {
|
||||||
av_freep(&CodecContext);
|
av_freep(&CodecContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFHaaliAudio::DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count, char *ErrorMsg, unsigned MsgSize) {
|
void FFHaaliAudio::DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count) {
|
||||||
const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||||
int Ret = -1;
|
int Ret = -1;
|
||||||
*AFirstStartTime = -1;
|
*AFirstStartTime = -1;
|
||||||
*Count = 0;
|
*Count = 0;
|
||||||
uint8_t *Buf = &DecodingBuffer[0];
|
uint8_t *Buf = &DecodingBuffer[0];
|
||||||
AVPacket Packet;
|
AVPacket Packet;
|
||||||
InitNullPacket(&Packet);
|
InitNullPacket(Packet);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
CComPtr<IMMFrame> pMMF;
|
CComPtr<IMMFrame> pMMF;
|
||||||
|
@ -79,56 +81,17 @@ int FFHaaliAudio::DecodeNextAudioBlock(int64_t *AFirstStartTime, int64_t *Count,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Done:
|
Done:;
|
||||||
return Ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index,
|
FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFMS_Index *Index, enum FFMS_Sources SourceMode)
|
||||||
int SourceMode, char *ErrorMsg, unsigned MsgSize)
|
: Res(FFSourceResources<FFMS_AudioSource>(this)), FFMS_AudioSource(SourceFile, Index, Track) {
|
||||||
: FFAudio(SourceFile, Index, ErrorMsg, MsgSize) {
|
|
||||||
AVCodec *Codec = NULL;
|
AVCodec *Codec = NULL;
|
||||||
CodecContext = NULL;
|
CodecContext = NULL;
|
||||||
AudioTrack = Track;
|
AudioTrack = Track;
|
||||||
Frames = (*Index)[AudioTrack];
|
Frames = (*Index)[AudioTrack];
|
||||||
|
|
||||||
if (Frames.size() == 0) {
|
pMMC = HaaliOpenFile(SourceFile, SourceMode);
|
||||||
snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
CLSID clsid = HAALI_TS_Parser;
|
|
||||||
if (SourceMode == 1)
|
|
||||||
clsid = HAALI_OGM_Parser;
|
|
||||||
|
|
||||||
if (FAILED(pMMC.CoCreateInstance(clsid))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create parser");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
CComPtr<IMemAlloc> pMA;
|
|
||||||
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
CComPtr<IMMStream> pMS;
|
|
||||||
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create disk file reader");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
WCHAR WSourceFile[2048];
|
|
||||||
ffms_mbstowcs(WSourceFile, SourceFile, 2000);
|
|
||||||
CComQIPtr<IMMStreamOpen> pMSO(pMS);
|
|
||||||
if (FAILED(pMSO->Open(WSourceFile))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't open file");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't parse file");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CodecPrivateSize = 0;
|
int CodecPrivateSize = 0;
|
||||||
int CurrentTrack = 0;
|
int CurrentTrack = 0;
|
||||||
|
@ -154,7 +117,13 @@ FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index,
|
||||||
if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) {
|
if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) {
|
||||||
char ACodecID[2048];
|
char ACodecID[2048];
|
||||||
wcstombs(ACodecID, pV.bstrVal, 2000);
|
wcstombs(ACodecID, pV.bstrVal, 2000);
|
||||||
Codec = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, FFMS_GET_VECTOR_PTR(CodecPrivate)));
|
|
||||||
|
int BitDepth = 0;
|
||||||
|
pV.Clear();
|
||||||
|
if (SUCCEEDED(pBag->Read(L"Audio.BitDepth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
|
||||||
|
BitDepth = pV.uintVal;
|
||||||
|
|
||||||
|
Codec = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, FFMS_GET_VECTOR_PTR(CodecPrivate), 0, BitDepth));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,45 +135,37 @@ FFHaaliAudio::FFHaaliAudio(const char *SourceFile, int Track, FFIndex *Index,
|
||||||
CodecContext->extradata = FFMS_GET_VECTOR_PTR(CodecPrivate);
|
CodecContext->extradata = FFMS_GET_VECTOR_PTR(CodecPrivate);
|
||||||
CodecContext->extradata_size = CodecPrivateSize;
|
CodecContext->extradata_size = CodecPrivateSize;
|
||||||
|
|
||||||
if (Codec == NULL) {
|
if (Codec == NULL)
|
||||||
Free(false);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
"Audio codec not found");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
InitializeCodecContextFromHaaliInfo(pBag, CodecContext);
|
InitializeCodecContextFromHaaliInfo(pBag, CodecContext);
|
||||||
|
|
||||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
if (avcodec_open(CodecContext, Codec) < 0)
|
||||||
Free(false);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
"Could not open audio codec");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
Res.CloseCodec(true);
|
||||||
|
|
||||||
// Always try to decode a frame to make sure all required parameters are known
|
// Always try to decode a frame to make sure all required parameters are known
|
||||||
int64_t Dummy1, Dummy2;
|
int64_t Dummy1, Dummy2;
|
||||||
if (DecodeNextAudioBlock(&Dummy1, &Dummy2, ErrorMsg, MsgSize) < 0) {
|
DecodeNextAudioBlock(&Dummy1, &Dummy2);
|
||||||
Free(true);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
pMMC->Seek(Frames[0].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT);
|
pMMC->Seek(Frames[0].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT);
|
||||||
avcodec_flush_buffers(CodecContext);
|
avcodec_flush_buffers(CodecContext);
|
||||||
|
|
||||||
FillAP(AP, CodecContext, Frames);
|
FillAP(AP, CodecContext, Frames);
|
||||||
|
|
||||||
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) {
|
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0)
|
||||||
Free(true);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio");
|
"Codec returned zero size audio");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
|
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFHaaliAudio::~FFHaaliAudio() {
|
void FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) {
|
||||||
Free(true);
|
GetAudioCheck(Start, Count);
|
||||||
}
|
|
||||||
|
|
||||||
int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) {
|
|
||||||
const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||||
memset(Buf, 0, static_cast<size_t>(SizeConst * Count));
|
memset(Buf, 0, static_cast<size_t>(SizeConst * Count));
|
||||||
bool HasSeeked = false;
|
bool HasSeeked = false;
|
||||||
|
@ -216,7 +177,7 @@ int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorM
|
||||||
int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
|
int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
|
||||||
// Was everything in the cache?
|
// Was everything in the cache?
|
||||||
if (CacheEnd == Start + Count)
|
if (CacheEnd == Start + Count)
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
int CurrentAudioBlock;
|
int CurrentAudioBlock;
|
||||||
// Is seeking required to decode the requested samples?
|
// Is seeking required to decode the requested samples?
|
||||||
|
@ -234,11 +195,7 @@ int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorM
|
||||||
int64_t FirstTime, DecodeCount;
|
int64_t FirstTime, DecodeCount;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int Ret = DecodeNextAudioBlock(&FirstTime, &DecodeCount, ErrorMsg, MsgSize);
|
DecodeNextAudioBlock(&FirstTime, &DecodeCount);
|
||||||
if (Ret < 0) {
|
|
||||||
// FIXME
|
|
||||||
//Env->ThrowError("Bleh, bad audio decoding");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HasSeeked) {
|
if (HasSeeked) {
|
||||||
CurrentAudioBlock = Frames.ClosestFrameFromDTS(FirstTime);
|
CurrentAudioBlock = Frames.ClosestFrameFromDTS(FirstTime);
|
||||||
|
@ -257,8 +214,6 @@ int FFHaaliAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorM
|
||||||
if (CurrentAudioBlock < static_cast<int>(Frames.size()))
|
if (CurrentAudioBlock < static_cast<int>(Frames.size()))
|
||||||
CurrentSample = Frames[CurrentAudioBlock].SampleStart;
|
CurrentSample = Frames[CurrentAudioBlock].SampleStart;
|
||||||
} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < static_cast<int>(Frames.size()));
|
} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < static_cast<int>(Frames.size()));
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAALISOURCE
|
#endif // HAALISOURCE
|
|
@ -24,51 +24,17 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FFHaaliIndexer::FFHaaliIndexer(const char *Filename, int SourceMode, char *ErrorMsg, unsigned MsgSize) : FFIndexer(Filename, ErrorMsg, MsgSize) {
|
FFHaaliIndexer::FFHaaliIndexer(const char *Filename, enum FFMS_Sources SourceMode) : FFMS_Indexer(Filename) {
|
||||||
SourceFile = Filename;
|
|
||||||
this->SourceMode = SourceMode;
|
this->SourceMode = SourceMode;
|
||||||
memset(TrackType, FFMS_TYPE_UNKNOWN, sizeof(TrackType));
|
SourceFile = Filename;
|
||||||
memset(Codec, 0, sizeof(Codec));
|
|
||||||
memset(CodecPrivate, 0, sizeof(CodecPrivate));
|
|
||||||
memset(CodecPrivateSize, 0, sizeof(CodecPrivateSize));
|
|
||||||
Duration = 0;
|
Duration = 0;
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
CLSID clsid = HAALI_TS_Parser;
|
TrackType[i] = FFMS_TYPE_UNKNOWN;
|
||||||
if (SourceMode == 1)
|
Codec[i] = NULL;
|
||||||
clsid = HAALI_OGM_Parser;
|
CodecPrivateSize[i] = 0;
|
||||||
|
|
||||||
if (FAILED(pMMC.CoCreateInstance(clsid))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create parser");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CComPtr<IMemAlloc> pMA;
|
pMMC = HaaliOpenFile(SourceFile, SourceMode);
|
||||||
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
CComPtr<IMMStream> pMS;
|
|
||||||
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create disk file reader");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
WCHAR WSourceFile[2048];
|
|
||||||
ffms_mbstowcs(WSourceFile, SourceFile, 2000);
|
|
||||||
CComQIPtr<IMMStreamOpen> pMSO(pMS);
|
|
||||||
if (FAILED(pMSO->Open(WSourceFile))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't open file");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
|
|
||||||
if (SourceMode == 0)
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't parse file, most likely a transport stream not cut at packet boundaries");
|
|
||||||
else if (SourceMode == 1)
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't parse file");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
CComQIPtr<IPropertyBag> pBag2 = pMMC;
|
CComQIPtr<IPropertyBag> pBag2 = pMMC;
|
||||||
CComVariant pV2;
|
CComVariant pV2;
|
||||||
|
@ -99,14 +65,42 @@ FFHaaliIndexer::FFHaaliIndexer(const char *Filename, int SourceMode, char *Error
|
||||||
}
|
}
|
||||||
|
|
||||||
pV.Clear();
|
pV.Clear();
|
||||||
if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
|
if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) {
|
||||||
FourCC = pV.uintVal;
|
FourCC = pV.uintVal;
|
||||||
|
|
||||||
|
// Reconstruct the missing codec private part for VC1
|
||||||
|
std::vector<uint8_t> bihvect;
|
||||||
|
bihvect.resize(sizeof(FFMS_BITMAPINFOHEADER));
|
||||||
|
FFMS_BITMAPINFOHEADER *bih = reinterpret_cast<FFMS_BITMAPINFOHEADER *>(FFMS_GET_VECTOR_PTR(bihvect));
|
||||||
|
memset(bih, 0, sizeof(FFMS_BITMAPINFOHEADER));
|
||||||
|
bih->biSize = sizeof(FFMS_BITMAPINFOHEADER) + CodecPrivateSize[NumTracks];
|
||||||
|
bih->biCompression = FourCC;
|
||||||
|
bih->biBitCount = 24;
|
||||||
|
bih->biPlanes = 1;
|
||||||
|
|
||||||
|
pV.Clear();
|
||||||
|
if (SUCCEEDED(pBag->Read(L"Video.PixelWidth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
|
||||||
|
bih->biWidth = pV.uintVal;
|
||||||
|
|
||||||
|
pV.Clear();
|
||||||
|
if (SUCCEEDED(pBag->Read(L"Video.PixelHeight", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
|
||||||
|
bih->biHeight = pV.uintVal;
|
||||||
|
|
||||||
|
CodecPrivate[NumTracks].insert(CodecPrivate[NumTracks].begin(), bihvect.begin(), bihvect.end());
|
||||||
|
CodecPrivateSize[NumTracks] += sizeof(FFMS_BITMAPINFOHEADER);
|
||||||
|
}
|
||||||
|
|
||||||
pV.Clear();
|
pV.Clear();
|
||||||
if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) {
|
if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) {
|
||||||
char CodecID[2048];
|
char CodecStr[2048];
|
||||||
wcstombs(CodecID, pV.bstrVal, 2000);
|
wcstombs(CodecStr, pV.bstrVal, 2000);
|
||||||
Codec[NumTracks] = avcodec_find_decoder(MatroskaToFFCodecID(CodecID, FFMS_GET_VECTOR_PTR(CodecPrivate[NumTracks]), FourCC));
|
|
||||||
|
int BitDepth = 0;
|
||||||
|
pV.Clear();
|
||||||
|
if (SUCCEEDED(pBag->Read(L"Audio.BitDepth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
|
||||||
|
BitDepth = pV.uintVal;
|
||||||
|
|
||||||
|
Codec[NumTracks] = avcodec_find_decoder(MatroskaToFFCodecID(CodecStr, FFMS_GET_VECTOR_PTR(CodecPrivate[NumTracks]), FourCC, BitDepth));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,17 +110,17 @@ FFHaaliIndexer::FFHaaliIndexer(const char *Filename, int SourceMode, char *Error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
FFMS_Index *FFHaaliIndexer::DoIndexing() {
|
||||||
std::vector<SharedAudioContext> AudioContexts(NumTracks, SharedAudioContext(true));
|
std::vector<SharedAudioContext> AudioContexts(NumTracks, SharedAudioContext(true));
|
||||||
std::vector<SharedVideoContext> VideoContexts(NumTracks, SharedVideoContext(true));
|
std::vector<SharedVideoContext> VideoContexts(NumTracks, SharedVideoContext(true));
|
||||||
|
|
||||||
std::auto_ptr<FFIndex> TrackIndices(new FFIndex(Filesize, Digest));
|
std::auto_ptr<FFMS_Index> TrackIndices(new FFMS_Index(Filesize, Digest));
|
||||||
TrackIndices->Decoder = 2;
|
TrackIndices->Decoder = FFMS_SOURCE_HAALIMPEG;
|
||||||
if (SourceMode == 1)
|
if (SourceMode == FFMS_SOURCE_HAALIOGG)
|
||||||
TrackIndices->Decoder = 3;
|
TrackIndices->Decoder = FFMS_SOURCE_HAALIOGG;
|
||||||
|
|
||||||
for (int i = 0; i < NumTracks; i++) {
|
for (int i = 0; i < NumTracks; i++) {
|
||||||
TrackIndices->push_back(FFTrack(1, 1000000, TrackType[i]));
|
TrackIndices->push_back(FFMS_Track(1, 1000000, TrackType[i]));
|
||||||
|
|
||||||
if (TrackType[i] == FFMS_TYPE_VIDEO && Codec[i] && (VideoContexts[i].Parser = av_parser_init(Codec[i]->id))) {
|
if (TrackType[i] == FFMS_TYPE_VIDEO && Codec[i] && (VideoContexts[i].Parser = av_parser_init(Codec[i]->id))) {
|
||||||
|
|
||||||
|
@ -138,8 +132,8 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
|
|
||||||
if (avcodec_open(CodecContext, Codec[i]) < 0) {
|
if (avcodec_open(CodecContext, Codec[i]) < 0) {
|
||||||
av_freep(&CodecContext);
|
av_freep(&CodecContext);
|
||||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||||
return NULL;
|
"Could not open video codec");
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoContexts[i].CodecContext = CodecContext;
|
VideoContexts[i].CodecContext = CodecContext;
|
||||||
|
@ -147,10 +141,9 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IndexMask & (1 << i) && TrackType[i] == FFMS_TYPE_AUDIO) {
|
if (IndexMask & (1 << i) && TrackType[i] == FFMS_TYPE_AUDIO) {
|
||||||
if (Codec[i] == NULL) {
|
if (Codec[i] == NULL)
|
||||||
snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||||
return NULL;
|
"Audio codec not found");
|
||||||
}
|
|
||||||
|
|
||||||
AVCodecContext *CodecContext = avcodec_alloc_context();
|
AVCodecContext *CodecContext = avcodec_alloc_context();
|
||||||
CodecContext->extradata = FFMS_GET_VECTOR_PTR(CodecPrivate[i]);
|
CodecContext->extradata = FFMS_GET_VECTOR_PTR(CodecPrivate[i]);
|
||||||
|
@ -160,8 +153,8 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
if (avcodec_open(CodecContext, Codec[i]) < 0) {
|
if (avcodec_open(CodecContext, Codec[i]) < 0) {
|
||||||
av_freep(&CodecContext);
|
av_freep(&CodecContext);
|
||||||
AudioContexts[i].CodecContext = NULL;
|
AudioContexts[i].CodecContext = NULL;
|
||||||
snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||||
return NULL;
|
"Could not open audio codec");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
IndexMask &= ~(1 << i);
|
IndexMask &= ~(1 << i);
|
||||||
|
@ -170,7 +163,7 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
//
|
//
|
||||||
|
|
||||||
AVPacket TempPacket;
|
AVPacket TempPacket;
|
||||||
InitNullPacket(&TempPacket);
|
InitNullPacket(TempPacket);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
CComPtr<IMMFrame> pMMF;
|
CComPtr<IMMFrame> pMMF;
|
||||||
|
@ -183,16 +176,14 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
if (IC) {
|
if (IC) {
|
||||||
if (Duration > 0) {
|
if (Duration > 0) {
|
||||||
if (SUCCEEDED(hr)) {
|
if (SUCCEEDED(hr)) {
|
||||||
if ((*IC)(Ts, Duration, ICPrivate)) {
|
if ((*IC)(Ts, Duration, ICPrivate))
|
||||||
snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
|
||||||
return NULL;
|
"Cancelled by user");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ((*IC)(0, 1, ICPrivate)) {
|
if ((*IC)(0, 1, ICPrivate))
|
||||||
snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
|
||||||
return NULL;
|
"Cancelled by user");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,7 +204,7 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
|
|
||||||
(*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(Ts, RepeatPict, pMMF->IsSyncPoint() == S_OK));
|
(*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(Ts, RepeatPict, pMMF->IsSyncPoint() == S_OK));
|
||||||
} else if (TrackType[Track] == FFMS_TYPE_AUDIO && (IndexMask & (1 << Track))) {
|
} else if (TrackType[Track] == FFMS_TYPE_AUDIO && (IndexMask & (1 << Track))) {
|
||||||
(*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(Ts, AudioContexts[Track].CurrentSample, pMMF->IsSyncPoint() == S_OK));
|
int64_t StartSample = AudioContexts[Track].CurrentSample;
|
||||||
AVCodecContext *AudioCodecContext = AudioContexts[Track].CodecContext;
|
AVCodecContext *AudioCodecContext = AudioContexts[Track].CodecContext;
|
||||||
|
|
||||||
if (pMMF->IsSyncPoint() == S_OK)
|
if (pMMF->IsSyncPoint() == S_OK)
|
||||||
|
@ -225,13 +216,18 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
|
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
|
||||||
int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
|
int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
|
||||||
if (Ret < 0) {
|
if (Ret < 0) {
|
||||||
if (IgnoreDecodeErrors) {
|
if (ErrorHandling == FFMS_IEH_ABORT) {
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||||
|
"Audio decoding error");
|
||||||
|
} else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) {
|
||||||
(*TrackIndices)[Track].clear();
|
(*TrackIndices)[Track].clear();
|
||||||
IndexMask &= ~(1 << Track);
|
IndexMask &= ~(1 << Track);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else if (ErrorHandling == FFMS_IEH_STOP_TRACK) {
|
||||||
snprintf(ErrorMsg, MsgSize, "Audio decoding error");
|
IndexMask &= ~(1 << Track);
|
||||||
return NULL;
|
break;
|
||||||
|
} else if (ErrorHandling == FFMS_IEH_IGNORE) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,8 +240,11 @@ FFIndex *FFHaaliIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
|
AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
|
||||||
|
|
||||||
if (DumpMask & (1 << Track))
|
if (DumpMask & (1 << Track))
|
||||||
WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize, ErrorMsg, MsgSize);
|
WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(Ts, StartSample,
|
||||||
|
static_cast<unsigned int>(AudioContexts[Track].CurrentSample - StartSample), pMMF->IsSyncPoint() == S_OK));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,7 +262,7 @@ FFMS_TrackType FFHaaliIndexer::GetTrackType(int Track) {
|
||||||
|
|
||||||
const char *FFHaaliIndexer::GetTrackCodec(int Track) {
|
const char *FFHaaliIndexer::GetTrackCodec(int Track) {
|
||||||
if (Codec[Track])
|
if (Codec[Track])
|
||||||
return Codec[Track]->long_name;
|
return Codec[Track]->name;
|
||||||
else
|
else
|
||||||
return "Unsupported codec/Unknown codec name";
|
return "Unsupported codec/Unknown codec name";
|
||||||
}
|
}
|
|
@ -18,10 +18,12 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#include "ffvideosource.h"
|
|
||||||
|
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
|
|
||||||
|
#include "videosource.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void FFHaaliVideo::Free(bool CloseCodec) {
|
void FFHaaliVideo::Free(bool CloseCodec) {
|
||||||
if (CloseCodec)
|
if (CloseCodec)
|
||||||
avcodec_close(CodecContext);
|
avcodec_close(CodecContext);
|
||||||
|
@ -31,9 +33,8 @@ void FFHaaliVideo::Free(bool CloseCodec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
||||||
FFIndex *Index, const char *PP,
|
FFMS_Index *Index, int Threads, enum FFMS_Sources SourceMode)
|
||||||
int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize)
|
: Res(FFSourceResources<FFMS_VideoSource>(this)), FFMS_VideoSource(SourceFile, Index, Track) {
|
||||||
: FFVideo(SourceFile, Index, ErrorMsg, MsgSize) {
|
|
||||||
|
|
||||||
BitStreamFilter = NULL;
|
BitStreamFilter = NULL;
|
||||||
AVCodec *Codec = NULL;
|
AVCodec *Codec = NULL;
|
||||||
|
@ -41,44 +42,7 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
||||||
VideoTrack = Track;
|
VideoTrack = Track;
|
||||||
Frames = (*Index)[VideoTrack];
|
Frames = (*Index)[VideoTrack];
|
||||||
|
|
||||||
if (Frames.size() == 0) {
|
pMMC = HaaliOpenFile(SourceFile, SourceMode);
|
||||||
snprintf(ErrorMsg, MsgSize, "Video track contains no frames");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
CLSID clsid = HAALI_TS_Parser;
|
|
||||||
if (SourceMode == 1)
|
|
||||||
clsid = HAALI_OGM_Parser;
|
|
||||||
|
|
||||||
if (FAILED(pMMC.CoCreateInstance(clsid))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create parser");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
CComPtr<IMemAlloc> pMA;
|
|
||||||
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
CComPtr<IMMStream> pMS;
|
|
||||||
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create disk file reader");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
WCHAR WSourceFile[2048];
|
|
||||||
ffms_mbstowcs(WSourceFile, SourceFile, 2000);
|
|
||||||
CComQIPtr<IMMStreamOpen> pMSO(pMS);
|
|
||||||
if (FAILED(pMSO->Open(WSourceFile))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't open file");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't parse file");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CodecPrivateSize = 0;
|
int CodecPrivateSize = 0;
|
||||||
int CurrentTrack = 0;
|
int CurrentTrack = 0;
|
||||||
|
@ -102,8 +66,30 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
||||||
}
|
}
|
||||||
|
|
||||||
pV.Clear();
|
pV.Clear();
|
||||||
if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
|
if (SUCCEEDED(pBag->Read(L"FOURCC", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4))) {
|
||||||
FourCC = pV.uintVal;
|
FourCC = pV.uintVal;
|
||||||
|
|
||||||
|
// Reconstruct the missing codec private part for VC1
|
||||||
|
std::vector<uint8_t> bihvect;
|
||||||
|
bihvect.resize(sizeof(FFMS_BITMAPINFOHEADER));
|
||||||
|
FFMS_BITMAPINFOHEADER *bih = reinterpret_cast<FFMS_BITMAPINFOHEADER *>(FFMS_GET_VECTOR_PTR(bihvect));
|
||||||
|
memset(bih, 0, sizeof(FFMS_BITMAPINFOHEADER));
|
||||||
|
bih->biSize = sizeof(FFMS_BITMAPINFOHEADER) + CodecPrivateSize;
|
||||||
|
bih->biCompression = FourCC;
|
||||||
|
bih->biBitCount = 24;
|
||||||
|
bih->biPlanes = 1;
|
||||||
|
|
||||||
|
pV.Clear();
|
||||||
|
if (SUCCEEDED(pBag->Read(L"Video.PixelWidth", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
|
||||||
|
bih->biWidth = pV.uintVal;
|
||||||
|
|
||||||
|
pV.Clear();
|
||||||
|
if (SUCCEEDED(pBag->Read(L"Video.PixelHeight", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_UI4)))
|
||||||
|
bih->biHeight = pV.uintVal;
|
||||||
|
|
||||||
|
CodecPrivate.insert(CodecPrivate.begin(), bihvect.begin(), bihvect.end());
|
||||||
|
CodecPrivateSize += sizeof(FFMS_BITMAPINFOHEADER);
|
||||||
|
}
|
||||||
|
|
||||||
pV.Clear();
|
pV.Clear();
|
||||||
if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) {
|
if (SUCCEEDED(pBag->Read(L"CodecID", &pV, NULL)) && SUCCEEDED(pV.ChangeType(VT_BSTR))) {
|
||||||
|
@ -122,51 +108,39 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
||||||
CodecContext->extradata_size = CodecPrivateSize;
|
CodecContext->extradata_size = CodecPrivateSize;
|
||||||
CodecContext->thread_count = Threads;
|
CodecContext->thread_count = Threads;
|
||||||
|
|
||||||
if (Codec == NULL) {
|
if (Codec == NULL)
|
||||||
Free(false);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Video codec not found");
|
"Video codec not found");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
InitializeCodecContextFromHaaliInfo(pBag, CodecContext);
|
InitializeCodecContextFromHaaliInfo(pBag, CodecContext);
|
||||||
|
|
||||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
if (avcodec_open(CodecContext, Codec) < 0)
|
||||||
Free(false);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
"Could not open video codec");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Codec->id == CODEC_ID_H264 && SourceMode == 0)
|
if (Codec->id == CODEC_ID_H264 && SourceMode == FFMS_SOURCE_HAALIMPEG)
|
||||||
BitStreamFilter = av_bitstream_filter_init("h264_mp4toannexb");
|
BitStreamFilter = av_bitstream_filter_init("h264_mp4toannexb");
|
||||||
|
|
||||||
|
Res.CloseCodec(true);
|
||||||
|
|
||||||
// Always try to decode a frame to make sure all required parameters are known
|
// Always try to decode a frame to make sure all required parameters are known
|
||||||
int64_t Dummy;
|
int64_t Dummy;
|
||||||
if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
|
DecodeNextFrame(&Dummy);
|
||||||
Free(true);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
VP.Width = CodecContext->width;
|
|
||||||
VP.Height = CodecContext->height;;
|
|
||||||
VP.FPSDenominator = 1;
|
VP.FPSDenominator = 1;
|
||||||
VP.FPSNumerator = 30;
|
VP.FPSNumerator = 30;
|
||||||
VP.RFFDenominator = CodecContext->time_base.num;
|
VP.RFFDenominator = CodecContext->time_base.num;
|
||||||
VP.RFFNumerator = CodecContext->time_base.den;
|
VP.RFFNumerator = CodecContext->time_base.den;
|
||||||
VP.NumFrames = Frames.size();
|
VP.NumFrames = Frames.size();
|
||||||
VP.VPixelFormat = CodecContext->pix_fmt;
|
VP.TopFieldFirst = DecodeFrame->top_field_first;
|
||||||
|
VP.ColorSpace = CodecContext->colorspace;
|
||||||
|
VP.ColorRange = CodecContext->color_range;
|
||||||
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||||
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||||
|
|
||||||
if (VP.Width <= 0 || VP.Height <= 0) {
|
if (CodecContext->width <= 0 || CodecContext->height <= 0)
|
||||||
Free(true);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Codec returned zero size video");
|
"Codec returned zero size video");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (InitPP(PP, ErrorMsg, MsgSize)) {
|
|
||||||
Free(true);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the average framerate
|
// Calculate the average framerate
|
||||||
if (Frames.size() >= 2) {
|
if (Frames.size() >= 2) {
|
||||||
|
@ -176,10 +150,7 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output the already decoded frame so it isn't wasted
|
// Output the already decoded frame so it isn't wasted
|
||||||
if (!OutputFrame(DecodeFrame, ErrorMsg, MsgSize)) {
|
OutputFrame(DecodeFrame);
|
||||||
Free(true);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set AR variables
|
// Set AR variables
|
||||||
CComVariant pV;
|
CComVariant pV;
|
||||||
|
@ -192,15 +163,11 @@ FFHaaliVideo::FFHaaliVideo(const char *SourceFile, int Track,
|
||||||
VP.SARDen = pV.uiVal;
|
VP.SARDen = pV.uiVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFHaaliVideo::~FFHaaliVideo() {
|
void FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime) {
|
||||||
Free(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) {
|
|
||||||
int FrameFinished = 0;
|
int FrameFinished = 0;
|
||||||
*AFirstStartTime = -1;
|
*AFirstStartTime = -1;
|
||||||
AVPacket Packet;
|
AVPacket Packet;
|
||||||
InitNullPacket(&Packet);
|
InitNullPacket(Packet);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
CComPtr<IMMFrame> pMMF;
|
CComPtr<IMMFrame> pMMF;
|
||||||
|
@ -227,6 +194,9 @@ int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsi
|
||||||
av_bitstream_filter_filter(BitStreamFilter, CodecContext, NULL,
|
av_bitstream_filter_filter(BitStreamFilter, CodecContext, NULL,
|
||||||
&Packet.data, &Packet.size, Data, pMMF->GetActualDataLength(), !!Packet.flags);
|
&Packet.data, &Packet.size, Data, pMMF->GetActualDataLength(), !!Packet.flags);
|
||||||
|
|
||||||
|
if (CodecContext->codec_id == CODEC_ID_MPEG4 && IsNVOP(Packet))
|
||||||
|
goto Done;
|
||||||
|
|
||||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
|
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
|
||||||
|
|
||||||
if (FrameFinished)
|
if (FrameFinished)
|
||||||
|
@ -237,7 +207,7 @@ int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsi
|
||||||
// Flush the last frames
|
// Flush the last frames
|
||||||
if (CodecContext->has_b_frames) {
|
if (CodecContext->has_b_frames) {
|
||||||
AVPacket NullPacket;
|
AVPacket NullPacket;
|
||||||
InitNullPacket(&NullPacket);
|
InitNullPacket(NullPacket);
|
||||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
|
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,14 +215,14 @@ int FFHaaliVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsi
|
||||||
goto Error;
|
goto Error;
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
Done:
|
Done:;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FFAVFrame *FFHaaliVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_Frame *FFHaaliVideo::GetFrame(int n) {
|
||||||
// PPFrame always holds frame LastFrameNum even if no PP is applied
|
GetFrameCheck(n);
|
||||||
|
|
||||||
if (LastFrameNum == n)
|
if (LastFrameNum == n)
|
||||||
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize);
|
return &LocalFrame;
|
||||||
|
|
||||||
bool HasSeeked = false;
|
bool HasSeeked = false;
|
||||||
int SeekOffset = 0;
|
int SeekOffset = 0;
|
||||||
|
@ -266,18 +236,16 @@ ReSeek:
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int64_t StartTime;
|
int64_t StartTime;
|
||||||
if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize))
|
DecodeNextFrame(&StartTime);
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (HasSeeked) {
|
if (HasSeeked) {
|
||||||
HasSeeked = false;
|
HasSeeked = false;
|
||||||
|
|
||||||
if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) {
|
if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) {
|
||||||
// No idea where we are so go back a bit further
|
// No idea where we are so go back a bit further
|
||||||
if (n + SeekOffset == 0) {
|
if (n + SeekOffset == 0)
|
||||||
snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file\n");
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
return NULL;
|
"Frame accurate seeking is not possible in this file");
|
||||||
}
|
|
||||||
|
|
||||||
SeekOffset -= FFMIN(20, n + SeekOffset);
|
SeekOffset -= FFMIN(20, n + SeekOffset);
|
||||||
goto ReSeek;
|
goto ReSeek;
|
||||||
|
@ -288,7 +256,7 @@ ReSeek:
|
||||||
} while (CurrentFrame <= n);
|
} while (CurrentFrame <= n);
|
||||||
|
|
||||||
LastFrameNum = n;
|
LastFrameNum = n;
|
||||||
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize);
|
return OutputFrame(DecodeFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // HAALISOURCE
|
#endif // HAALISOURCE
|
|
@ -22,13 +22,15 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <libavutil/sha1.h>
|
#include <libavutil/sha1.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define INDEXID 0x53920873
|
||||||
|
|
||||||
|
extern bool HasHaaliMPEG;
|
||||||
|
extern bool HasHaaliOGG;
|
||||||
|
|
||||||
struct IndexHeader {
|
struct IndexHeader {
|
||||||
uint32_t Id;
|
uint32_t Id;
|
||||||
|
@ -44,6 +46,13 @@ struct IndexHeader {
|
||||||
uint8_t FileSignature[20];
|
uint8_t FileSignature[20];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TrackHeader {
|
||||||
|
uint32_t TT;
|
||||||
|
uint32_t Frames;
|
||||||
|
int64_t Num;
|
||||||
|
int64_t Den;
|
||||||
|
};
|
||||||
|
|
||||||
SharedVideoContext::SharedVideoContext(bool FreeCodecContext) {
|
SharedVideoContext::SharedVideoContext(bool FreeCodecContext) {
|
||||||
CodecContext = NULL;
|
CodecContext = NULL;
|
||||||
Parser = NULL;
|
Parser = NULL;
|
||||||
|
@ -85,47 +94,49 @@ SharedAudioContext::~SharedAudioContext() {
|
||||||
cs_Destroy(CS);
|
cs_Destroy(CS);
|
||||||
}
|
}
|
||||||
|
|
||||||
TFrameInfo::TFrameInfo(int64_t DTS, int64_t SampleStart, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) {
|
TFrameInfo::TFrameInfo() {
|
||||||
|
}
|
||||||
|
|
||||||
|
TFrameInfo::TFrameInfo(int64_t DTS, int64_t SampleStart, unsigned int SampleCount, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) {
|
||||||
this->DTS = DTS;
|
this->DTS = DTS;
|
||||||
this->RepeatPict = RepeatPict;
|
this->RepeatPict = RepeatPict;
|
||||||
this->KeyFrame = KeyFrame;
|
this->KeyFrame = KeyFrame;
|
||||||
this->SampleStart = SampleStart;
|
this->SampleStart = SampleStart;
|
||||||
|
this->SampleCount = SampleCount;
|
||||||
this->FilePos = FilePos;
|
this->FilePos = FilePos;
|
||||||
this->FrameSize = FrameSize;
|
this->FrameSize = FrameSize;
|
||||||
|
this->OriginalPos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
TFrameInfo TFrameInfo::VideoFrameInfo(int64_t DTS, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) {
|
TFrameInfo TFrameInfo::VideoFrameInfo(int64_t DTS, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) {
|
||||||
return TFrameInfo(DTS, 0, RepeatPict, KeyFrame, FilePos, FrameSize);
|
return TFrameInfo(DTS, 0, 0, RepeatPict, KeyFrame, FilePos, FrameSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
TFrameInfo TFrameInfo::AudioFrameInfo(int64_t DTS, int64_t SampleStart, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) {
|
TFrameInfo TFrameInfo::AudioFrameInfo(int64_t DTS, int64_t SampleStart, unsigned int SampleCount, bool KeyFrame, int64_t FilePos, unsigned int FrameSize) {
|
||||||
return TFrameInfo(DTS, SampleStart, 0, KeyFrame, FilePos, FrameSize);
|
return TFrameInfo(DTS, SampleStart, SampleCount, 0, KeyFrame, FilePos, FrameSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFTrack::WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize) {
|
void FFMS_Track::WriteTimecodes(const char *TimecodeFile) {
|
||||||
ffms_fstream Timecodes(TimecodeFile, std::ios::out | std::ios::trunc);
|
ffms_fstream Timecodes(TimecodeFile, std::ios::out | std::ios::trunc);
|
||||||
|
|
||||||
if (!Timecodes.is_open()) {
|
if (!Timecodes.is_open())
|
||||||
snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for writing", TimecodeFile);
|
throw FFMS_Exception(FFMS_ERROR_TRACK, FFMS_ERROR_FILE_WRITE,
|
||||||
return 1;
|
boost::format("Failed to open '%1%' for writing") % TimecodeFile);
|
||||||
}
|
|
||||||
|
|
||||||
Timecodes << "# timecode format v2\n";
|
Timecodes << "# timecode format v2\n";
|
||||||
|
|
||||||
for (iterator Cur=begin(); Cur!=end(); Cur++)
|
for (iterator Cur=begin(); Cur!=end(); Cur++)
|
||||||
Timecodes << std::fixed << ((Cur->DTS * TB.Num) / (double)TB.Den) << "\n";
|
Timecodes << std::fixed << ((Cur->DTS * TB.Num) / (double)TB.Den) << "\n";
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFTrack::FrameFromDTS(int64_t DTS) {
|
int FFMS_Track::FrameFromDTS(int64_t DTS) {
|
||||||
for (int i = 0; i < static_cast<int>(size()); i++)
|
for (int i = 0; i < static_cast<int>(size()); i++)
|
||||||
if (at(i).DTS == DTS)
|
if (at(i).DTS == DTS)
|
||||||
return i;
|
return i;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFTrack::ClosestFrameFromDTS(int64_t DTS) {
|
int FFMS_Track::ClosestFrameFromDTS(int64_t DTS) {
|
||||||
int Frame = 0;
|
int Frame = 0;
|
||||||
int64_t BestDiff = 0xFFFFFFFFFFFFFFLL; // big number
|
int64_t BestDiff = 0xFFFFFFFFFFFFFFLL; // big number
|
||||||
for (int i = 0; i < static_cast<int>(size()); i++) {
|
for (int i = 0; i < static_cast<int>(size()); i++) {
|
||||||
|
@ -139,7 +150,7 @@ int FFTrack::ClosestFrameFromDTS(int64_t DTS) {
|
||||||
return Frame;
|
return Frame;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFTrack::FindClosestVideoKeyFrame(int Frame) {
|
int FFMS_Track::FindClosestVideoKeyFrame(int Frame) {
|
||||||
Frame = FFMIN(FFMAX(Frame, 0), static_cast<int>(size()) - 1);
|
Frame = FFMIN(FFMAX(Frame, 0), static_cast<int>(size()) - 1);
|
||||||
for (int i = Frame; i > 0; i--)
|
for (int i = Frame; i > 0; i--)
|
||||||
if (at(i).KeyFrame)
|
if (at(i).KeyFrame)
|
||||||
|
@ -147,7 +158,7 @@ int FFTrack::FindClosestVideoKeyFrame(int Frame) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFTrack::FindClosestAudioKeyFrame(int64_t Sample) {
|
int FFMS_Track::FindClosestAudioKeyFrame(int64_t Sample) {
|
||||||
for (size_t i = 0; i < size(); i++) {
|
for (size_t i = 0; i < size(); i++) {
|
||||||
if (at(i).SampleStart == Sample && at(i).KeyFrame)
|
if (at(i).SampleStart == Sample && at(i).KeyFrame)
|
||||||
return i;
|
return i;
|
||||||
|
@ -157,29 +168,26 @@ int FFTrack::FindClosestAudioKeyFrame(int64_t Sample) {
|
||||||
return size() - 1;
|
return size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFTrack::FFTrack() {
|
FFMS_Track::FFMS_Track() {
|
||||||
this->TT = FFMS_TYPE_UNKNOWN;
|
this->TT = FFMS_TYPE_UNKNOWN;
|
||||||
this->TB.Num = 0;
|
this->TB.Num = 0;
|
||||||
this->TB.Den = 0;
|
this->TB.Den = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFTrack::FFTrack(int64_t Num, int64_t Den, FFMS_TrackType TT) {
|
FFMS_Track::FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT) {
|
||||||
this->TT = TT;
|
this->TT = TT;
|
||||||
this->TB.Num = Num;
|
this->TB.Num = Num;
|
||||||
this->TB.Den = Den;
|
this->TB.Den = Den;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFIndex::CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20], char *ErrorMsg, unsigned MsgSize) {
|
void FFMS_Index::CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20]) {
|
||||||
// use cstdio because Microsoft's implementation of std::fstream doesn't support files >4GB.
|
|
||||||
// please kill me now.
|
|
||||||
FILE *SFile = ffms_fopen(Filename,"rb");
|
FILE *SFile = ffms_fopen(Filename,"rb");
|
||||||
|
|
||||||
if (SFile == NULL) {
|
if (SFile == NULL)
|
||||||
snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for hashing", Filename);
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ,
|
||||||
return 1;
|
boost::format("Failed to open '%1%' for hashing") % Filename);
|
||||||
}
|
|
||||||
|
|
||||||
const int BlockSize = 2*1024*1024;
|
const int BlockSize = 1024*1024;
|
||||||
std::vector<uint8_t> FileBuffer(BlockSize);
|
std::vector<uint8_t> FileBuffer(BlockSize);
|
||||||
std::vector<uint8_t> ctxmem(av_sha1_size);
|
std::vector<uint8_t> ctxmem(av_sha1_size);
|
||||||
AVSHA1 *ctx = (AVSHA1 *)&ctxmem[0];
|
AVSHA1 *ctx = (AVSHA1 *)&ctxmem[0];
|
||||||
|
@ -188,10 +196,10 @@ int FFIndex::CalculateFileSignature(const char *Filename, int64_t *Filesize, uin
|
||||||
memset(&FileBuffer[0], 0, BlockSize);
|
memset(&FileBuffer[0], 0, BlockSize);
|
||||||
fread(&FileBuffer[0], 1, BlockSize, SFile);
|
fread(&FileBuffer[0], 1, BlockSize, SFile);
|
||||||
if (ferror(SFile) && !feof(SFile)) {
|
if (ferror(SFile) && !feof(SFile)) {
|
||||||
snprintf(ErrorMsg, MsgSize, "Failed to read from '%s' for hashing", Filename);
|
|
||||||
av_sha1_final(ctx, Digest);
|
av_sha1_final(ctx, Digest);
|
||||||
fclose(SFile);
|
fclose(SFile);
|
||||||
return 1;
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ,
|
||||||
|
boost::format("Failed to read '%1%' for hashing") % Filename);
|
||||||
}
|
}
|
||||||
av_sha1_update(ctx, &FileBuffer[0], BlockSize);
|
av_sha1_update(ctx, &FileBuffer[0], BlockSize);
|
||||||
|
|
||||||
|
@ -199,61 +207,67 @@ int FFIndex::CalculateFileSignature(const char *Filename, int64_t *Filesize, uin
|
||||||
memset(&FileBuffer[0], 0, BlockSize);
|
memset(&FileBuffer[0], 0, BlockSize);
|
||||||
fread(&FileBuffer[0], 1, BlockSize, SFile);
|
fread(&FileBuffer[0], 1, BlockSize, SFile);
|
||||||
if (ferror(SFile) && !feof(SFile)) {
|
if (ferror(SFile) && !feof(SFile)) {
|
||||||
snprintf(ErrorMsg, MsgSize, "Failed to seek with offset %d from file end in '%s' for hashing", BlockSize, Filename);
|
|
||||||
av_sha1_final(ctx, Digest);
|
av_sha1_final(ctx, Digest);
|
||||||
fclose(SFile);
|
fclose(SFile);
|
||||||
return 1;
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ,
|
||||||
|
boost::format("Failed to seek with offset %1% from file end in '%2%' for hashing") % BlockSize % Filename);
|
||||||
}
|
}
|
||||||
av_sha1_update(ctx, &FileBuffer[0], BlockSize);
|
av_sha1_update(ctx, &FileBuffer[0], BlockSize);
|
||||||
|
|
||||||
fseeko(SFile, 0, SEEK_END);
|
fseeko(SFile, 0, SEEK_END);
|
||||||
if (ferror(SFile)) {
|
if (ferror(SFile)) {
|
||||||
snprintf(ErrorMsg, MsgSize, "Failed to seek to end of '%s' for hashing", Filename);
|
|
||||||
av_sha1_final(ctx, Digest);
|
av_sha1_final(ctx, Digest);
|
||||||
fclose(SFile);
|
fclose(SFile);
|
||||||
return 1;
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ,
|
||||||
|
boost::format("Failed to seek to end of '%1%' for hashing") % Filename);
|
||||||
}
|
}
|
||||||
*Filesize = ftello(SFile);
|
*Filesize = ftello(SFile);
|
||||||
fclose(SFile);
|
fclose(SFile);
|
||||||
|
|
||||||
av_sha1_final(ctx, Digest);
|
av_sha1_final(ctx, Digest);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool DTSComparison(TFrameInfo FI1, TFrameInfo FI2) {
|
static bool DTSComparison(TFrameInfo FI1, TFrameInfo FI2) {
|
||||||
return FI1.DTS < FI2.DTS;
|
return FI1.DTS < FI2.DTS;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFIndex::Sort() {
|
void FFMS_Index::Sort() {
|
||||||
for (FFIndex::iterator Cur=begin(); Cur!=end(); Cur++)
|
for (FFMS_Index::iterator Cur=begin(); Cur!=end(); Cur++) {
|
||||||
|
|
||||||
|
for (size_t i = 0; i < Cur->size(); i++)
|
||||||
|
Cur->at(i).OriginalPos = i;
|
||||||
|
|
||||||
std::sort(Cur->begin(), Cur->end(), DTSComparison);
|
std::sort(Cur->begin(), Cur->end(), DTSComparison);
|
||||||
|
|
||||||
|
std::vector<size_t> ReorderTemp;
|
||||||
|
ReorderTemp.resize(Cur->size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < Cur->size(); i++)
|
||||||
|
ReorderTemp[i] = Cur->at(i).OriginalPos;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < Cur->size(); i++)
|
||||||
|
Cur->at(ReorderTemp[i]).OriginalPos = i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFIndex::CompareFileSignature(const char *Filename, char *ErrorMsg, unsigned MsgSize) {
|
bool FFMS_Index::CompareFileSignature(const char *Filename) {
|
||||||
int64_t CFilesize;
|
int64_t CFilesize;
|
||||||
uint8_t CDigest[20];
|
uint8_t CDigest[20];
|
||||||
CalculateFileSignature(Filename, &CFilesize, CDigest, ErrorMsg, MsgSize);
|
CalculateFileSignature(Filename, &CFilesize, CDigest);
|
||||||
|
return (CFilesize == Filesize && !memcmp(CDigest, Digest, sizeof(Digest)));
|
||||||
if (CFilesize != Filesize || memcmp(CDigest, Digest, sizeof(Digest))) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Index and source file signature mismatch");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFIndex::WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) {
|
void FFMS_Index::WriteIndex(const char *IndexFile) {
|
||||||
ffms_fstream IndexStream(IndexFile, std::ios::out | std::ios::binary | std::ios::trunc);
|
ffms_fstream IndexStream(IndexFile, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||||
|
|
||||||
if (!IndexStream.is_open()) {
|
if (!IndexStream.is_open())
|
||||||
snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for writing", IndexFile);
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_WRITE,
|
||||||
return 1;
|
boost::format("Failed to open '%1%' for writing") % IndexFile);
|
||||||
}
|
|
||||||
|
|
||||||
// Write the index file header
|
// Write the index file header
|
||||||
IndexHeader IH;
|
IndexHeader IH;
|
||||||
IH.Id = INDEXID;
|
IH.Id = INDEXID;
|
||||||
IH.Version = INDEXVERSION;
|
IH.Version = FFMS_VERSION;
|
||||||
IH.Tracks = size();
|
IH.Tracks = size();
|
||||||
IH.Decoder = Decoder;
|
IH.Decoder = Decoder;
|
||||||
IH.LAVUVersion = avutil_version();
|
IH.LAVUVersion = avutil_version();
|
||||||
|
@ -267,158 +281,152 @@ int FFIndex::WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize)
|
||||||
IndexStream.write(reinterpret_cast<char *>(&IH), sizeof(IH));
|
IndexStream.write(reinterpret_cast<char *>(&IH), sizeof(IH));
|
||||||
|
|
||||||
for (unsigned int i = 0; i < IH.Tracks; i++) {
|
for (unsigned int i = 0; i < IH.Tracks; i++) {
|
||||||
uint32_t TT = at(i).TT;
|
TrackHeader TH;
|
||||||
IndexStream.write(reinterpret_cast<char *>(&TT), sizeof(TT));
|
TH.TT = at(i).TT;
|
||||||
int64_t Num = at(i).TB.Num;
|
TH.Frames = at(i).size();
|
||||||
IndexStream.write(reinterpret_cast<char *>(&Num), sizeof(Num));
|
TH.Num = at(i).TB.Num;;
|
||||||
int64_t Den = at(i).TB.Den;
|
TH.Den = at(i).TB.Den;
|
||||||
IndexStream.write(reinterpret_cast<char *>(&Den), sizeof(Den));
|
|
||||||
int64_t Frames = at(i).size();
|
|
||||||
IndexStream.write(reinterpret_cast<char *>(&Frames), sizeof(Frames));
|
|
||||||
|
|
||||||
for (FFTrack::iterator Cur=at(i).begin(); Cur!=at(i).end(); Cur++)
|
IndexStream.write(reinterpret_cast<char *>(&TH), sizeof(TH));
|
||||||
IndexStream.write(reinterpret_cast<char *>(&*Cur), sizeof(TFrameInfo));
|
IndexStream.write(reinterpret_cast<char *>(FFMS_GET_VECTOR_PTR(at(i))), TH.Frames * sizeof(TFrameInfo));
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFIndex::ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) {
|
void FFMS_Index::ReadIndex(const char *IndexFile) {
|
||||||
ffms_fstream Index(IndexFile, std::ios::in | std::ios::binary);
|
ffms_fstream Index(IndexFile, std::ios::in | std::ios::binary);
|
||||||
|
|
||||||
if (!Index.is_open()) {
|
if (!Index.is_open())
|
||||||
snprintf(ErrorMsg, MsgSize, "Failed to open '%s' for reading", IndexFile);
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_READ,
|
||||||
return 1;
|
boost::format("Failed to open '%1%' for reading") % IndexFile);
|
||||||
}
|
|
||||||
|
|
||||||
// Read the index file header
|
// Read the index file header
|
||||||
IndexHeader IH;
|
IndexHeader IH;
|
||||||
Index.read(reinterpret_cast<char *>(&IH), sizeof(IH));
|
Index.read(reinterpret_cast<char *>(&IH), sizeof(IH));
|
||||||
if (IH.Id != INDEXID) {
|
if (IH.Id != INDEXID)
|
||||||
snprintf(ErrorMsg, MsgSize, "'%s' is not a valid index file", IndexFile);
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
return 2;
|
boost::format("'%1%' is not a valid index file") % IndexFile);
|
||||||
}
|
|
||||||
|
|
||||||
if (IH.Version != INDEXVERSION) {
|
if (IH.Version != FFMS_VERSION)
|
||||||
snprintf(ErrorMsg, MsgSize, "'%s' is not the expected index version", IndexFile);
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_VERSION,
|
||||||
return 3;
|
boost::format("'%1%' is not the expected index version") % IndexFile);
|
||||||
}
|
|
||||||
|
|
||||||
if (IH.LAVUVersion != avutil_version() || IH.LAVFVersion != avformat_version() ||
|
if (IH.LAVUVersion != avutil_version() || IH.LAVFVersion != avformat_version() ||
|
||||||
IH.LAVCVersion != avcodec_version() || IH.LSWSVersion != swscale_version() ||
|
IH.LAVCVersion != avcodec_version() || IH.LSWSVersion != swscale_version() ||
|
||||||
IH.LPPVersion != postproc_version()) {
|
IH.LPPVersion != postproc_version())
|
||||||
snprintf(ErrorMsg, MsgSize, "A different FFmpeg build was used to create '%s'", IndexFile);
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_VERSION,
|
||||||
return 4;
|
boost::format("A different FFmpeg build was used to create '%1%'") % IndexFile);
|
||||||
}
|
|
||||||
|
if (!(IH.Decoder & FFMS_GetEnabledSources()))
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_NOT_AVAILABLE,
|
||||||
|
"The source which this index was created with is not available");
|
||||||
|
|
||||||
Decoder = IH.Decoder;
|
Decoder = IH.Decoder;
|
||||||
Filesize = IH.FileSize;
|
Filesize = IH.FileSize;
|
||||||
memcpy(Digest, IH.FileSignature, sizeof(Digest));
|
memcpy(Digest, IH.FileSignature, sizeof(Digest));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
for (unsigned int i = 0; i < IH.Tracks; i++) {
|
for (unsigned int i = 0; i < IH.Tracks; i++) {
|
||||||
// Read how many records belong to the current stream
|
TrackHeader TH;
|
||||||
uint32_t TT;
|
Index.read(reinterpret_cast<char *>(&TH), sizeof(TH));
|
||||||
Index.read(reinterpret_cast<char *>(&TT), sizeof(TT));
|
push_back(FFMS_Track(TH.Num, TH.Den, static_cast<FFMS_TrackType>(TH.TT)));
|
||||||
int64_t Num;
|
|
||||||
Index.read(reinterpret_cast<char *>(&Num), sizeof(Num));
|
|
||||||
int64_t Den;
|
|
||||||
Index.read(reinterpret_cast<char *>(&Den), sizeof(Den));
|
|
||||||
int64_t Frames;
|
|
||||||
Index.read(reinterpret_cast<char *>(&Frames), sizeof(Frames));
|
|
||||||
push_back(FFTrack(Num, Den, static_cast<FFMS_TrackType>(TT)));
|
|
||||||
|
|
||||||
TFrameInfo FI = TFrameInfo::VideoFrameInfo(0, 0, false);
|
if (TH.Frames) {
|
||||||
for (size_t j = 0; j < Frames; j++) {
|
at(i).resize(TH.Frames);
|
||||||
Index.read(reinterpret_cast<char *>(&FI), sizeof(TFrameInfo));
|
Index.read(reinterpret_cast<char *>(FFMS_GET_VECTOR_PTR(at(i))), TH.Frames * sizeof(TFrameInfo));
|
||||||
at(i).push_back(FI);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
snprintf(ErrorMsg, MsgSize, "Unknown error while reading index information in '%s'", IndexFile);
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_UNKNOWN,
|
||||||
return 5;
|
boost::format("Unknown error while reading index information in '%1%'") % IndexFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FFIndex::FFIndex() {
|
FFMS_Index::FFMS_Index() {
|
||||||
// this comment documents nothing
|
// this comment documents nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
FFIndex::FFIndex(int64_t Filesize, uint8_t Digest[20]) {
|
FFMS_Index::FFMS_Index(int64_t Filesize, uint8_t Digest[20]) {
|
||||||
this->Filesize = Filesize;
|
this->Filesize = Filesize;
|
||||||
memcpy(this->Digest, Digest, sizeof(this->Digest));
|
memcpy(this->Digest, Digest, sizeof(this->Digest));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFIndexer::SetIndexMask(int IndexMask) {
|
void FFMS_Indexer::SetIndexMask(int IndexMask) {
|
||||||
this->IndexMask = IndexMask;
|
this->IndexMask = IndexMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFIndexer::SetDumpMask(int DumpMask) {
|
void FFMS_Indexer::SetDumpMask(int DumpMask) {
|
||||||
this->DumpMask = DumpMask;
|
this->DumpMask = DumpMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFIndexer::SetIgnoreDecodeErrors(bool IgnoreDecodeErrors) {
|
void FFMS_Indexer::SetErrorHandling(int ErrorHandling) {
|
||||||
this->IgnoreDecodeErrors = IgnoreDecodeErrors;
|
if (ErrorHandling != FFMS_IEH_ABORT && ErrorHandling != FFMS_IEH_CLEAR_TRACK &&
|
||||||
|
ErrorHandling != FFMS_IEH_STOP_TRACK && ErrorHandling != FFMS_IEH_IGNORE)
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Invalid error handling mode specified");
|
||||||
|
this->ErrorHandling = ErrorHandling;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFIndexer::SetProgressCallback(TIndexCallback IC, void *ICPrivate) {
|
void FFMS_Indexer::SetProgressCallback(TIndexCallback IC, void *ICPrivate) {
|
||||||
this->IC = IC;
|
this->IC = IC;
|
||||||
this->ICPrivate = ICPrivate;
|
this->ICPrivate = ICPrivate;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFIndexer::SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate) {
|
void FFMS_Indexer::SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate) {
|
||||||
this->ANC = ANC;
|
this->ANC = ANC;
|
||||||
this->ANCPrivate = ANCPrivate;
|
this->ANCPrivate = ANCPrivate;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFIndexer *FFIndexer::CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_Indexer *FFMS_Indexer::CreateIndexer(const char *Filename) {
|
||||||
AVFormatContext *FormatContext = NULL;
|
AVFormatContext *FormatContext = NULL;
|
||||||
|
|
||||||
if (av_open_input_file(&FormatContext, Filename, NULL, 0, NULL) != 0) {
|
if (av_open_input_file(&FormatContext, Filename, NULL, 0, NULL) != 0)
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't open '%s'", Filename);
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
return NULL;
|
boost::format("Can't open '%1%'") % Filename);
|
||||||
}
|
|
||||||
|
|
||||||
// Do matroska indexing instead?
|
// Do matroska indexing instead?
|
||||||
if (!strcmp(FormatContext->iformat->name, "matroska")) {
|
if (!strcmp(FormatContext->iformat->name, "matroska")) {
|
||||||
av_close_input_file(FormatContext);
|
av_close_input_file(FormatContext);
|
||||||
return new FFMatroskaIndexer(Filename, ErrorMsg, MsgSize);
|
return new FFMatroskaIndexer(Filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
// Do haali ts indexing instead?
|
// Do haali ts indexing instead?
|
||||||
if (!strcmp(FormatContext->iformat->name, "mpeg") || !strcmp(FormatContext->iformat->name, "mpegts")) {
|
if (HasHaaliMPEG && (!strcmp(FormatContext->iformat->name, "mpeg") || !strcmp(FormatContext->iformat->name, "mpegts"))) {
|
||||||
av_close_input_file(FormatContext);
|
av_close_input_file(FormatContext);
|
||||||
return new FFHaaliIndexer(Filename, 0, ErrorMsg, MsgSize);
|
return new FFHaaliIndexer(Filename, FFMS_SOURCE_HAALIMPEG);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(FormatContext->iformat->name, "ogg")) {
|
if (HasHaaliOGG && !strcmp(FormatContext->iformat->name, "ogg")) {
|
||||||
av_close_input_file(FormatContext);
|
av_close_input_file(FormatContext);
|
||||||
return new FFHaaliIndexer(Filename, 1, ErrorMsg, MsgSize);
|
return new FFHaaliIndexer(Filename, FFMS_SOURCE_HAALIOGG);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return new FFLAVFIndexer(Filename, FormatContext, ErrorMsg, MsgSize);
|
return new FFLAVFIndexer(Filename, FormatContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFIndexer::FFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 5) {
|
FFMS_Indexer::FFMS_Indexer(const char *Filename) : DecodingBuffer(AVCODEC_MAX_AUDIO_FRAME_SIZE * 5) {
|
||||||
if (FFIndex::CalculateFileSignature(Filename, &Filesize, Digest, ErrorMsg, MsgSize))
|
IndexMask = 0;
|
||||||
throw ErrorMsg;
|
DumpMask = 0;
|
||||||
|
ErrorHandling = FFMS_IEH_CLEAR_TRACK;
|
||||||
|
IC = NULL;
|
||||||
|
ICPrivate = NULL;
|
||||||
|
ANC = NULL;
|
||||||
|
ANCPrivate = NULL;
|
||||||
|
|
||||||
|
FFMS_Index::CalculateFileSignature(Filename, &Filesize, Digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFIndexer::~FFIndexer() {
|
FFMS_Indexer::~FFMS_Indexer() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FFIndexer::WriteAudio(SharedAudioContext &AudioContext, FFIndex *Index, int Track, int DBSize, char *ErrorMsg, unsigned MsgSize) {
|
void FFMS_Indexer::WriteAudio(SharedAudioContext &AudioContext, FFMS_Index *Index, int Track, int DBSize) {
|
||||||
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers.
|
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers.
|
||||||
if (DBSize > 0) {
|
if (DBSize > 0) {
|
||||||
if (!AudioContext.W64Writer) {
|
if (!AudioContext.W64Writer) {
|
||||||
FFAudioProperties AP;
|
FFMS_AudioProperties AP;
|
||||||
FillAP(AP, AudioContext.CodecContext, (*Index)[Track]);
|
FillAP(AP, AudioContext.CodecContext, (*Index)[Track]);
|
||||||
int FNSize = (*ANC)(SourceFile, Track, &AP, NULL, 0, ANCPrivate);
|
int FNSize = (*ANC)(SourceFile, Track, &AP, NULL, 0, ANCPrivate);
|
||||||
std::vector<char> WName(FNSize);
|
std::vector<char> WName(FNSize);
|
||||||
|
@ -428,13 +436,11 @@ bool FFIndexer::WriteAudio(SharedAudioContext &AudioContext, FFIndex *Index, int
|
||||||
AudioContext.W64Writer = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioContext.CodecContext->sample_fmt),
|
AudioContext.W64Writer = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioContext.CodecContext->sample_fmt),
|
||||||
AudioContext.CodecContext->channels, AudioContext.CodecContext->sample_rate, (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_FLT) || (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_DBL));
|
AudioContext.CodecContext->channels, AudioContext.CodecContext->sample_rate, (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_FLT) || (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_DBL));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
snprintf(ErrorMsg, MsgSize, "Failed to write wave data");
|
throw FFMS_Exception(FFMS_ERROR_WAVE_WRITER, FFMS_ERROR_FILE_WRITE,
|
||||||
return false;
|
"Failed to write wave data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioContext.W64Writer->WriteData(&DecodingBuffer[0], DBSize);
|
AudioContext.W64Writer->WriteData(&DecodingBuffer[0], DBSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,8 +37,7 @@
|
||||||
# include "guids.h"
|
# include "guids.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define INDEXVERSION 28
|
|
||||||
#define INDEXID 0x53920873
|
|
||||||
|
|
||||||
class SharedVideoContext {
|
class SharedVideoContext {
|
||||||
private:
|
private:
|
||||||
|
@ -69,52 +68,55 @@ struct TFrameInfo {
|
||||||
public:
|
public:
|
||||||
FFMS_FRAMEINFO_COMMON
|
FFMS_FRAMEINFO_COMMON
|
||||||
int64_t SampleStart;
|
int64_t SampleStart;
|
||||||
|
unsigned int SampleCount;
|
||||||
int64_t FilePos;
|
int64_t FilePos;
|
||||||
unsigned int FrameSize;
|
unsigned int FrameSize;
|
||||||
|
size_t OriginalPos;
|
||||||
|
|
||||||
|
TFrameInfo();
|
||||||
static TFrameInfo VideoFrameInfo(int64_t DTS, int RepeatPict, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0);
|
static TFrameInfo VideoFrameInfo(int64_t DTS, int RepeatPict, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0);
|
||||||
static TFrameInfo AudioFrameInfo(int64_t DTS, int64_t SampleStart, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0);
|
static TFrameInfo AudioFrameInfo(int64_t DTS, int64_t SampleStart, unsigned int SampleCount, bool KeyFrame, int64_t FilePos = 0, unsigned int FrameSize = 0);
|
||||||
private:
|
private:
|
||||||
TFrameInfo(int64_t DTS, int64_t SampleStart, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize);
|
TFrameInfo(int64_t DTS, int64_t SampleStart, unsigned int SampleCount, int RepeatPict, bool KeyFrame, int64_t FilePos, unsigned int FrameSize);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FFTrack : public std::vector<TFrameInfo> {
|
class FFMS_Track : public std::vector<TFrameInfo> {
|
||||||
public:
|
public:
|
||||||
FFMS_TrackType TT;
|
FFMS_TrackType TT;
|
||||||
FFTrackTimeBase TB;
|
FFMS_TrackTimeBase TB;
|
||||||
|
|
||||||
int FindClosestVideoKeyFrame(int Frame);
|
int FindClosestVideoKeyFrame(int Frame);
|
||||||
int FindClosestAudioKeyFrame(int64_t Sample);
|
int FindClosestAudioKeyFrame(int64_t Sample);
|
||||||
int FrameFromDTS(int64_t DTS);
|
int FrameFromDTS(int64_t DTS);
|
||||||
int ClosestFrameFromDTS(int64_t DTS);
|
int ClosestFrameFromDTS(int64_t DTS);
|
||||||
int WriteTimecodes(const char *TimecodeFile, char *ErrorMsg, unsigned MsgSize);
|
void WriteTimecodes(const char *TimecodeFile);
|
||||||
|
|
||||||
FFTrack();
|
FFMS_Track();
|
||||||
FFTrack(int64_t Num, int64_t Den, FFMS_TrackType TT);
|
FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FFIndex : public std::vector<FFTrack> {
|
class FFMS_Index : public std::vector<FFMS_Track> {
|
||||||
public:
|
public:
|
||||||
static int CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20], char *ErrorMsg, unsigned MsgSize);
|
static void CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20]);
|
||||||
|
|
||||||
int Decoder;
|
int Decoder;
|
||||||
int64_t Filesize;
|
int64_t Filesize;
|
||||||
uint8_t Digest[20];
|
uint8_t Digest[20];
|
||||||
|
|
||||||
void Sort();
|
void Sort();
|
||||||
int CompareFileSignature(const char *Filename, char *ErrorMsg, unsigned MsgSize);
|
bool CompareFileSignature(const char *Filename);
|
||||||
int WriteIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize);
|
void WriteIndex(const char *IndexFile);
|
||||||
int ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize);
|
void ReadIndex(const char *IndexFile);
|
||||||
|
|
||||||
FFIndex();
|
FFMS_Index();
|
||||||
FFIndex(int64_t Filesize, uint8_t Digest[20]);
|
FFMS_Index(int64_t Filesize, uint8_t Digest[20]);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FFIndexer {
|
class FFMS_Indexer {
|
||||||
protected:
|
protected:
|
||||||
int IndexMask;
|
int IndexMask;
|
||||||
int DumpMask;
|
int DumpMask;
|
||||||
bool IgnoreDecodeErrors;
|
int ErrorHandling;
|
||||||
TIndexCallback IC;
|
TIndexCallback IC;
|
||||||
void *ICPrivate;
|
void *ICPrivate;
|
||||||
TAudioNameCallback ANC;
|
TAudioNameCallback ANC;
|
||||||
|
@ -125,43 +127,43 @@ protected:
|
||||||
int64_t Filesize;
|
int64_t Filesize;
|
||||||
uint8_t Digest[20];
|
uint8_t Digest[20];
|
||||||
|
|
||||||
bool WriteAudio(SharedAudioContext &AudioContext, FFIndex *Index, int Track, int DBSize, char *ErrorMsg, unsigned MsgSize);
|
void WriteAudio(SharedAudioContext &AudioContext, FFMS_Index *Index, int Track, int DBSize);
|
||||||
public:
|
public:
|
||||||
static FFIndexer *CreateFFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize);
|
static FFMS_Indexer *CreateIndexer(const char *Filename);
|
||||||
FFIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize);
|
FFMS_Indexer(const char *Filename);
|
||||||
virtual ~FFIndexer();
|
virtual ~FFMS_Indexer();
|
||||||
void SetIndexMask(int IndexMask);
|
void SetIndexMask(int IndexMask);
|
||||||
void SetDumpMask(int DumpMask);
|
void SetDumpMask(int DumpMask);
|
||||||
void SetIgnoreDecodeErrors(bool IgnoreDecodeErrors);
|
void SetErrorHandling(int ErrorHandling);
|
||||||
void SetProgressCallback(TIndexCallback IC, void *ICPrivate);
|
void SetProgressCallback(TIndexCallback IC, void *ICPrivate);
|
||||||
void SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate);
|
void SetAudioNameCallback(TAudioNameCallback ANC, void *ANCPrivate);
|
||||||
virtual FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize) = 0;
|
virtual FFMS_Index *DoIndexing() = 0;
|
||||||
virtual int GetNumberOfTracks() = 0;
|
virtual int GetNumberOfTracks() = 0;
|
||||||
virtual FFMS_TrackType GetTrackType(int Track) = 0;
|
virtual FFMS_TrackType GetTrackType(int Track) = 0;
|
||||||
virtual const char *GetTrackCodec(int Track) = 0;
|
virtual const char *GetTrackCodec(int Track) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FFLAVFIndexer : public FFIndexer {
|
class FFLAVFIndexer : public FFMS_Indexer {
|
||||||
private:
|
private:
|
||||||
AVFormatContext *FormatContext;
|
AVFormatContext *FormatContext;
|
||||||
public:
|
public:
|
||||||
FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize);
|
FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext);
|
||||||
~FFLAVFIndexer();
|
~FFLAVFIndexer();
|
||||||
FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize);
|
FFMS_Index *DoIndexing();
|
||||||
int GetNumberOfTracks();
|
int GetNumberOfTracks();
|
||||||
FFMS_TrackType GetTrackType(int Track);
|
FFMS_TrackType GetTrackType(int Track);
|
||||||
const char *GetTrackCodec(int Track);
|
const char *GetTrackCodec(int Track);
|
||||||
};
|
};
|
||||||
|
|
||||||
class FFMatroskaIndexer : public FFIndexer {
|
class FFMatroskaIndexer : public FFMS_Indexer {
|
||||||
private:
|
private:
|
||||||
MatroskaFile *MF;
|
MatroskaFile *MF;
|
||||||
MatroskaReaderContext MC;
|
MatroskaReaderContext MC;
|
||||||
AVCodec *Codec[32];
|
AVCodec *Codec[32];
|
||||||
public:
|
public:
|
||||||
FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize);
|
FFMatroskaIndexer(const char *Filename);
|
||||||
~FFMatroskaIndexer();
|
~FFMatroskaIndexer();
|
||||||
FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize);
|
FFMS_Index *DoIndexing();
|
||||||
int GetNumberOfTracks();
|
int GetNumberOfTracks();
|
||||||
FFMS_TrackType GetTrackType(int Track);
|
FFMS_TrackType GetTrackType(int Track);
|
||||||
const char *GetTrackCodec(int Track);
|
const char *GetTrackCodec(int Track);
|
||||||
|
@ -169,7 +171,7 @@ public:
|
||||||
|
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
|
|
||||||
class FFHaaliIndexer : public FFIndexer {
|
class FFHaaliIndexer : public FFMS_Indexer {
|
||||||
private:
|
private:
|
||||||
int SourceMode;
|
int SourceMode;
|
||||||
CComPtr<IMMContainer> pMMC;
|
CComPtr<IMMContainer> pMMC;
|
||||||
|
@ -181,8 +183,8 @@ private:
|
||||||
CComQIPtr<IPropertyBag> PropertyBags[32];
|
CComQIPtr<IPropertyBag> PropertyBags[32];
|
||||||
int64_t Duration;
|
int64_t Duration;
|
||||||
public:
|
public:
|
||||||
FFHaaliIndexer(const char *Filename, int SourceMode, char *ErrorMsg, unsigned MsgSize);
|
FFHaaliIndexer(const char *Filename, enum FFMS_Sources SourceMode);
|
||||||
FFIndex *DoIndexing(char *ErrorMsg, unsigned MsgSize);
|
FFMS_Index *DoIndexing();
|
||||||
int GetNumberOfTracks();
|
int GetNumberOfTracks();
|
||||||
FFMS_TrackType GetTrackType(int Track);
|
FFMS_TrackType GetTrackType(int Track);
|
||||||
const char *GetTrackCodec(int Track);
|
const char *GetTrackCodec(int Track);
|
||||||
|
|
|
@ -18,7 +18,7 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#include "ffaudiosource.h"
|
#include "audiosource.h"
|
||||||
|
|
||||||
void FFLAVFAudio::Free(bool CloseCodec) {
|
void FFLAVFAudio::Free(bool CloseCodec) {
|
||||||
if (CloseCodec)
|
if (CloseCodec)
|
||||||
|
@ -27,75 +27,53 @@ void FFLAVFAudio::Free(bool CloseCodec) {
|
||||||
av_close_input_file(FormatContext);
|
av_close_input_file(FormatContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFIndex *Index,
|
FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFMS_Index *Index)
|
||||||
char *ErrorMsg, unsigned MsgSize)
|
: Res(FFSourceResources<FFMS_AudioSource>(this)), FFMS_AudioSource(SourceFile, Index, Track){
|
||||||
: FFAudio(SourceFile, Index, ErrorMsg, MsgSize){
|
|
||||||
FormatContext = NULL;
|
FormatContext = NULL;
|
||||||
AVCodec *Codec = NULL;
|
AVCodec *Codec = NULL;
|
||||||
AudioTrack = Track;
|
AudioTrack = Track;
|
||||||
Frames = (*Index)[AudioTrack];
|
Frames = (*Index)[AudioTrack];
|
||||||
|
|
||||||
if (Frames.size() == 0) {
|
LAVFOpenFile(SourceFile, FormatContext);
|
||||||
Free(false);
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Couldn't open '%s'", SourceFile);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (av_find_stream_info(FormatContext) < 0) {
|
|
||||||
Free(false);
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Couldn't find stream information");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
CodecContext = FormatContext->streams[AudioTrack]->codec;
|
CodecContext = FormatContext->streams[AudioTrack]->codec;
|
||||||
|
|
||||||
Codec = avcodec_find_decoder(CodecContext->codec_id);
|
Codec = avcodec_find_decoder(CodecContext->codec_id);
|
||||||
if (Codec == NULL) {
|
if (Codec == NULL)
|
||||||
Free(false);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
"Audio codec not found");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
if (avcodec_open(CodecContext, Codec) < 0)
|
||||||
Free(false);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
"Could not open audio codec");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
Res.CloseCodec(true);
|
||||||
|
|
||||||
// Always try to decode a frame to make sure all required parameters are known
|
// Always try to decode a frame to make sure all required parameters are known
|
||||||
int64_t Dummy;
|
int64_t Dummy;
|
||||||
if (DecodeNextAudioBlock(&Dummy, ErrorMsg, MsgSize) < 0) {
|
DecodeNextAudioBlock(&Dummy);
|
||||||
Free(true);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
if (av_seek_frame(FormatContext, AudioTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0)
|
if (av_seek_frame(FormatContext, AudioTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0)
|
||||||
av_seek_frame(FormatContext, AudioTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY);
|
av_seek_frame(FormatContext, AudioTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY);
|
||||||
avcodec_flush_buffers(CodecContext);
|
avcodec_flush_buffers(CodecContext);
|
||||||
|
|
||||||
FillAP(AP, CodecContext, Frames);
|
FillAP(AP, CodecContext, Frames);
|
||||||
|
|
||||||
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) {
|
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0)
|
||||||
Free(true);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio");
|
"Codec returned zero size audio");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
|
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFLAVFAudio::DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned MsgSize) {
|
void FFLAVFAudio::DecodeNextAudioBlock(int64_t *Count) {
|
||||||
const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||||
int Ret = -1;
|
int Ret = -1;
|
||||||
*Count = 0;
|
*Count = 0;
|
||||||
uint8_t *Buf = &DecodingBuffer[0];
|
uint8_t *Buf = &DecodingBuffer[0];
|
||||||
AVPacket Packet, TempPacket;
|
AVPacket Packet, TempPacket;
|
||||||
InitNullPacket(&Packet);
|
InitNullPacket(Packet);
|
||||||
InitNullPacket(&TempPacket);
|
InitNullPacket(TempPacket);
|
||||||
|
|
||||||
while (av_read_frame(FormatContext, &Packet) >= 0) {
|
while (av_read_frame(FormatContext, &Packet) >= 0) {
|
||||||
if (Packet.stream_index == AudioTrack) {
|
if (Packet.stream_index == AudioTrack) {
|
||||||
|
@ -127,11 +105,12 @@ int FFLAVFAudio::DecodeNextAudioBlock(int64_t *Count, char *ErrorMsg, unsigned M
|
||||||
av_free_packet(&Packet);
|
av_free_packet(&Packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
Done:
|
Done:;
|
||||||
return Ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) {
|
void FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) {
|
||||||
|
GetAudioCheck(Start, Count);
|
||||||
|
|
||||||
const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||||
memset(Buf, 0, static_cast<size_t>(SizeConst * Count));
|
memset(Buf, 0, static_cast<size_t>(SizeConst * Count));
|
||||||
|
|
||||||
|
@ -142,7 +121,7 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs
|
||||||
int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
|
int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
|
||||||
// Was everything in the cache?
|
// Was everything in the cache?
|
||||||
if (CacheEnd == Start + Count)
|
if (CacheEnd == Start + Count)
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
size_t CurrentAudioBlock;
|
size_t CurrentAudioBlock;
|
||||||
// Is seeking required to decode the requested samples?
|
// Is seeking required to decode the requested samples?
|
||||||
|
@ -158,7 +137,7 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs
|
||||||
avcodec_flush_buffers(CodecContext);
|
avcodec_flush_buffers(CodecContext);
|
||||||
|
|
||||||
AVPacket Packet;
|
AVPacket Packet;
|
||||||
InitNullPacket(&Packet);
|
InitNullPacket(Packet);
|
||||||
|
|
||||||
// Establish where we actually are
|
// Establish where we actually are
|
||||||
// Trigger on packet dts difference since groups can otherwise be indistinguishable
|
// Trigger on packet dts difference since groups can otherwise be indistinguishable
|
||||||
|
@ -189,11 +168,7 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs
|
||||||
int64_t DecodeCount;
|
int64_t DecodeCount;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int Ret = DecodeNextAudioBlock(&DecodeCount, ErrorMsg, MsgSize);
|
DecodeNextAudioBlock(&DecodeCount);
|
||||||
if (Ret < 0) {
|
|
||||||
// FIXME
|
|
||||||
//Env->ThrowError("Bleh, bad audio decoding");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache the block if enough blocks before it have been decoded to avoid garbage
|
// Cache the block if enough blocks before it have been decoded to avoid garbage
|
||||||
if (PreDecBlocks == 0) {
|
if (PreDecBlocks == 0) {
|
||||||
|
@ -207,10 +182,4 @@ int FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMs
|
||||||
if (CurrentAudioBlock < Frames.size())
|
if (CurrentAudioBlock < Frames.size())
|
||||||
CurrentSample = Frames[CurrentAudioBlock].SampleStart;
|
CurrentSample = Frames[CurrentAudioBlock].SampleStart;
|
||||||
} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < Frames.size());
|
} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < Frames.size());
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
FFLAVFAudio::~FFLAVFAudio() {
|
|
||||||
Free(true);
|
|
||||||
}
|
}
|
|
@ -22,14 +22,14 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FFLAVFIndexer::FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext, char *ErrorMsg, unsigned MsgSize) : FFIndexer(Filename, ErrorMsg, MsgSize) {
|
FFLAVFIndexer::FFLAVFIndexer(const char *Filename, AVFormatContext *FormatContext) : FFMS_Indexer(Filename) {
|
||||||
SourceFile = Filename;
|
SourceFile = Filename;
|
||||||
this->FormatContext = FormatContext;
|
this->FormatContext = FormatContext;
|
||||||
|
|
||||||
if (av_find_stream_info(FormatContext) < 0) {
|
if (av_find_stream_info(FormatContext) < 0) {
|
||||||
av_close_input_file(FormatContext);
|
av_close_input_file(FormatContext);
|
||||||
snprintf(ErrorMsg, MsgSize, "Couldn't find stream information");
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
throw ErrorMsg;
|
"Couldn't find stream information");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,15 +37,15 @@ FFLAVFIndexer::~FFLAVFIndexer() {
|
||||||
av_close_input_file(FormatContext);
|
av_close_input_file(FormatContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
FFMS_Index *FFLAVFIndexer::DoIndexing() {
|
||||||
std::vector<SharedAudioContext> AudioContexts(FormatContext->nb_streams, SharedAudioContext(false));
|
std::vector<SharedAudioContext> AudioContexts(FormatContext->nb_streams, SharedAudioContext(false));
|
||||||
std::vector<SharedVideoContext> VideoContexts(FormatContext->nb_streams, SharedVideoContext(false));
|
std::vector<SharedVideoContext> VideoContexts(FormatContext->nb_streams, SharedVideoContext(false));
|
||||||
|
|
||||||
std::auto_ptr<FFIndex> TrackIndices(new FFIndex(Filesize, Digest));
|
std::auto_ptr<FFMS_Index> TrackIndices(new FFMS_Index(Filesize, Digest));
|
||||||
TrackIndices->Decoder = 0;
|
TrackIndices->Decoder = FFMS_SOURCE_LAVF;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < FormatContext->nb_streams; i++) {
|
for (unsigned int i = 0; i < FormatContext->nb_streams; i++) {
|
||||||
TrackIndices->push_back(FFTrack((int64_t)FormatContext->streams[i]->time_base.num * 1000,
|
TrackIndices->push_back(FFMS_Track((int64_t)FormatContext->streams[i]->time_base.num * 1000,
|
||||||
FormatContext->streams[i]->time_base.den,
|
FormatContext->streams[i]->time_base.den,
|
||||||
static_cast<FFMS_TrackType>(FormatContext->streams[i]->codec->codec_type)));
|
static_cast<FFMS_TrackType>(FormatContext->streams[i]->codec->codec_type)));
|
||||||
|
|
||||||
|
@ -53,15 +53,13 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
(VideoContexts[i].Parser = av_parser_init(FormatContext->streams[i]->codec->codec_id))) {
|
(VideoContexts[i].Parser = av_parser_init(FormatContext->streams[i]->codec->codec_id))) {
|
||||||
|
|
||||||
AVCodec *VideoCodec = avcodec_find_decoder(FormatContext->streams[i]->codec->codec_id);
|
AVCodec *VideoCodec = avcodec_find_decoder(FormatContext->streams[i]->codec->codec_id);
|
||||||
if (VideoCodec == NULL) {
|
if (VideoCodec == NULL)
|
||||||
snprintf(ErrorMsg, MsgSize, "Vudio codec not found");
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||||
return NULL;
|
"Video codec not found");
|
||||||
}
|
|
||||||
|
|
||||||
if (avcodec_open(FormatContext->streams[i]->codec, VideoCodec) < 0) {
|
if (avcodec_open(FormatContext->streams[i]->codec, VideoCodec) < 0)
|
||||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||||
return NULL;
|
"Could not open video codec");
|
||||||
}
|
|
||||||
|
|
||||||
VideoContexts[i].CodecContext = FormatContext->streams[i]->codec;
|
VideoContexts[i].CodecContext = FormatContext->streams[i]->codec;
|
||||||
VideoContexts[i].Parser->flags = PARSER_FLAG_COMPLETE_FRAMES;
|
VideoContexts[i].Parser->flags = PARSER_FLAG_COMPLETE_FRAMES;
|
||||||
|
@ -71,15 +69,13 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
AVCodecContext *AudioCodecContext = FormatContext->streams[i]->codec;
|
AVCodecContext *AudioCodecContext = FormatContext->streams[i]->codec;
|
||||||
|
|
||||||
AVCodec *AudioCodec = avcodec_find_decoder(AudioCodecContext->codec_id);
|
AVCodec *AudioCodec = avcodec_find_decoder(AudioCodecContext->codec_id);
|
||||||
if (AudioCodec == NULL) {
|
if (AudioCodec == NULL)
|
||||||
snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||||
return NULL;
|
"Audio codec not found");
|
||||||
}
|
|
||||||
|
|
||||||
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
|
if (avcodec_open(AudioCodecContext, AudioCodec) < 0)
|
||||||
snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||||
return NULL;
|
"Could not open audio codec");
|
||||||
}
|
|
||||||
|
|
||||||
AudioContexts[i].CodecContext = AudioCodecContext;
|
AudioContexts[i].CodecContext = AudioCodecContext;
|
||||||
} else {
|
} else {
|
||||||
|
@ -90,15 +86,14 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
//
|
//
|
||||||
|
|
||||||
AVPacket Packet, TempPacket;
|
AVPacket Packet, TempPacket;
|
||||||
InitNullPacket(&Packet);
|
InitNullPacket(Packet);
|
||||||
InitNullPacket(&TempPacket);
|
InitNullPacket(TempPacket);
|
||||||
while (av_read_frame(FormatContext, &Packet) >= 0) {
|
while (av_read_frame(FormatContext, &Packet) >= 0) {
|
||||||
// Update progress
|
// Update progress
|
||||||
if (IC) {
|
if (IC) {
|
||||||
if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, ICPrivate)) {
|
if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, ICPrivate))
|
||||||
snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
|
||||||
return NULL;
|
"Cancelled by user");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only create index entries for video for now to save space
|
// Only create index entries for video for now to save space
|
||||||
|
@ -114,7 +109,7 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
|
|
||||||
(*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::VideoFrameInfo(Packet.dts, RepeatPict, (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0));
|
(*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::VideoFrameInfo(Packet.dts, RepeatPict, (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0));
|
||||||
} else if (FormatContext->streams[Packet.stream_index]->codec->codec_type == CODEC_TYPE_AUDIO && (IndexMask & (1 << Packet.stream_index))) {
|
} else if (FormatContext->streams[Packet.stream_index]->codec->codec_type == CODEC_TYPE_AUDIO && (IndexMask & (1 << Packet.stream_index))) {
|
||||||
(*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::AudioFrameInfo(Packet.dts, AudioContexts[Packet.stream_index].CurrentSample, (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0));
|
int64_t StartSample = AudioContexts[Packet.stream_index].CurrentSample;
|
||||||
AVCodecContext *AudioCodecContext = FormatContext->streams[Packet.stream_index]->codec;
|
AVCodecContext *AudioCodecContext = FormatContext->streams[Packet.stream_index]->codec;
|
||||||
TempPacket.data = Packet.data;
|
TempPacket.data = Packet.data;
|
||||||
TempPacket.size = Packet.size;
|
TempPacket.size = Packet.size;
|
||||||
|
@ -124,13 +119,18 @@ FFIndex *FFLAVFIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
|
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
|
||||||
int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
|
int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
|
||||||
if (Ret < 0) {
|
if (Ret < 0) {
|
||||||
if (IgnoreDecodeErrors) {
|
if (ErrorHandling == FFMS_IEH_ABORT) {
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||||
|
"Audio decoding error");
|
||||||
|
} else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) {
|
||||||
(*TrackIndices)[Packet.stream_index].clear();
|
(*TrackIndices)[Packet.stream_index].clear();
|
||||||
IndexMask &= ~(1 << Packet.stream_index);
|
IndexMask &= ~(1 << Packet.stream_index);
|
||||||
|
break;
|
||||||
|
} else if (ErrorHandling == FFMS_IEH_STOP_TRACK) {
|
||||||
|
IndexMask &= ~(1 << Packet.stream_index);
|
||||||
|
break;
|
||||||
|
} else if (ErrorHandling == FFMS_IEH_IGNORE) {
|
||||||
break;
|
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);
|
AudioContexts[Packet.stream_index].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
|
||||||
|
|
||||||
if (DumpMask & (1 << Packet.stream_index))
|
if (DumpMask & (1 << Packet.stream_index))
|
||||||
WriteAudio(AudioContexts[Packet.stream_index], TrackIndices.get(), Packet.stream_index, dbsize, ErrorMsg, MsgSize);
|
WriteAudio(AudioContexts[Packet.stream_index], TrackIndices.get(), Packet.stream_index, dbsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::AudioFrameInfo(Packet.dts, StartSample,
|
||||||
|
static_cast<unsigned int>(AudioContexts[Packet.stream_index].CurrentSample - StartSample), (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
av_free_packet(&Packet);
|
av_free_packet(&Packet);
|
||||||
|
@ -163,5 +166,5 @@ FFMS_TrackType FFLAVFIndexer::GetTrackType(int Track) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *FFLAVFIndexer::GetTrackCodec(int Track) {
|
const char *FFLAVFIndexer::GetTrackCodec(int Track) {
|
||||||
return (avcodec_find_decoder(FormatContext->streams[Track]->codec->codec_id))->long_name;
|
return (avcodec_find_decoder(FormatContext->streams[Track]->codec->codec_id))->name;
|
||||||
}
|
}
|
|
@ -18,7 +18,7 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#include "ffvideosource.h"
|
#include "videosource.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,9 +28,9 @@ void FFLAVFVideo::Free(bool CloseCodec) {
|
||||||
av_close_input_file(FormatContext);
|
av_close_input_file(FormatContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index,
|
FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index,
|
||||||
const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize)
|
int Threads, int SeekMode)
|
||||||
: FFVideo(SourceFile, Index, ErrorMsg, MsgSize) {
|
: Res(FFSourceResources<FFMS_VideoSource>(this)), FFMS_VideoSource(SourceFile, Index, Track) {
|
||||||
|
|
||||||
FormatContext = NULL;
|
FormatContext = NULL;
|
||||||
AVCodec *Codec = NULL;
|
AVCodec *Codec = NULL;
|
||||||
|
@ -38,68 +38,45 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index,
|
||||||
VideoTrack = Track;
|
VideoTrack = Track;
|
||||||
Frames = (*Index)[VideoTrack];
|
Frames = (*Index)[VideoTrack];
|
||||||
|
|
||||||
if (Frames.size() == 0) {
|
LAVFOpenFile(SourceFile, FormatContext);
|
||||||
snprintf(ErrorMsg, MsgSize, "Video track contains no frames");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0) {
|
if (SeekMode >= 0 && av_seek_frame(FormatContext, VideoTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0)
|
||||||
snprintf(ErrorMsg, MsgSize, "Couldn't open '%s'", SourceFile);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
throw ErrorMsg;
|
"Video track is unseekable");
|
||||||
}
|
|
||||||
|
|
||||||
if (av_find_stream_info(FormatContext) < 0) {
|
|
||||||
Free(false);
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Couldn't find stream information");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SeekMode >= 0 && av_seek_frame(FormatContext, VideoTrack, Frames[0].DTS, AVSEEK_FLAG_BACKWARD) < 0) {
|
|
||||||
Free(false);
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Video track is unseekable");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
CodecContext = FormatContext->streams[VideoTrack]->codec;
|
CodecContext = FormatContext->streams[VideoTrack]->codec;
|
||||||
CodecContext->thread_count = Threads;
|
CodecContext->thread_count = Threads;
|
||||||
|
|
||||||
Codec = avcodec_find_decoder(CodecContext->codec_id);
|
Codec = avcodec_find_decoder(CodecContext->codec_id);
|
||||||
if (Codec == NULL) {
|
if (Codec == NULL)
|
||||||
Free(false);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Video codec not found");
|
"Video codec not found");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
if (avcodec_open(CodecContext, Codec) < 0)
|
||||||
Free(false);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
"Could not open video codec");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
Res.CloseCodec(true);
|
||||||
|
|
||||||
// Always try to decode a frame to make sure all required parameters are known
|
// Always try to decode a frame to make sure all required parameters are known
|
||||||
int64_t Dummy;
|
int64_t Dummy;
|
||||||
if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
|
DecodeNextFrame(&Dummy);
|
||||||
Free(true);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
//VP.image_type = VideoInfo::IT_TFF;
|
//VP.image_type = VideoInfo::IT_TFF;
|
||||||
VP.Width = CodecContext->width;
|
|
||||||
VP.Height = CodecContext->height;
|
|
||||||
VP.FPSDenominator = FormatContext->streams[VideoTrack]->time_base.num;
|
VP.FPSDenominator = FormatContext->streams[VideoTrack]->time_base.num;
|
||||||
VP.FPSNumerator = FormatContext->streams[VideoTrack]->time_base.den;
|
VP.FPSNumerator = FormatContext->streams[VideoTrack]->time_base.den;
|
||||||
VP.RFFDenominator = CodecContext->time_base.num;
|
VP.RFFDenominator = CodecContext->time_base.num;
|
||||||
VP.RFFNumerator = CodecContext->time_base.den;
|
VP.RFFNumerator = CodecContext->time_base.den;
|
||||||
VP.NumFrames = Frames.size();
|
VP.NumFrames = Frames.size();
|
||||||
VP.VPixelFormat = CodecContext->pix_fmt;
|
VP.TopFieldFirst = DecodeFrame->top_field_first;
|
||||||
|
VP.ColorSpace = CodecContext->colorspace;
|
||||||
|
VP.ColorRange = CodecContext->color_range;
|
||||||
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||||
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||||
|
|
||||||
if (VP.Width <= 0 || VP.Height <= 0) {
|
if (CodecContext->width <= 0 || CodecContext->height <= 0)
|
||||||
Free(true);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Codec returned zero size video");
|
"Codec returned zero size video");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sanity check framerate
|
// sanity check framerate
|
||||||
if (VP.FPSDenominator > VP.FPSNumerator || VP.FPSDenominator <= 0 || VP.FPSNumerator <= 0) {
|
if (VP.FPSDenominator > VP.FPSNumerator || VP.FPSDenominator <= 0 || VP.FPSNumerator <= 0) {
|
||||||
|
@ -107,11 +84,6 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index,
|
||||||
VP.FPSNumerator = 30;
|
VP.FPSNumerator = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (InitPP(PP, ErrorMsg, MsgSize)) {
|
|
||||||
Free(true);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust framerate to match the duration of the first frame
|
// Adjust framerate to match the duration of the first frame
|
||||||
if (Frames.size() >= 2) {
|
if (Frames.size() >= 2) {
|
||||||
unsigned int DTSDiff = (unsigned int)FFMAX(Frames[1].DTS - Frames[0].DTS, 1);
|
unsigned int DTSDiff = (unsigned int)FFMAX(Frames[1].DTS - Frames[0].DTS, 1);
|
||||||
|
@ -120,23 +92,16 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index,
|
||||||
|
|
||||||
// Cannot "output" to PPFrame without doing all other initialization
|
// Cannot "output" to PPFrame without doing all other initialization
|
||||||
// This is the additional mess required for seekmode=-1 to work in a reasonable way
|
// This is the additional mess required for seekmode=-1 to work in a reasonable way
|
||||||
if (!OutputFrame(DecodeFrame, ErrorMsg, MsgSize)) {
|
OutputFrame(DecodeFrame);
|
||||||
Free(true);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set AR variables
|
// Set AR variables
|
||||||
VP.SARNum = CodecContext->sample_aspect_ratio.num;
|
VP.SARNum = CodecContext->sample_aspect_ratio.num;
|
||||||
VP.SARDen = CodecContext->sample_aspect_ratio.den;
|
VP.SARDen = CodecContext->sample_aspect_ratio.den;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFLAVFVideo::~FFLAVFVideo() {
|
void FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime) {
|
||||||
Free(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned MsgSize) {
|
|
||||||
AVPacket Packet;
|
AVPacket Packet;
|
||||||
InitNullPacket(&Packet);
|
InitNullPacket(Packet);
|
||||||
int FrameFinished = 0;
|
int FrameFinished = 0;
|
||||||
*AStartTime = -1;
|
*AStartTime = -1;
|
||||||
|
|
||||||
|
@ -145,6 +110,11 @@ int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned M
|
||||||
if (*AStartTime < 0)
|
if (*AStartTime < 0)
|
||||||
*AStartTime = Packet.dts;
|
*AStartTime = Packet.dts;
|
||||||
|
|
||||||
|
if (CodecContext->codec_id == CODEC_ID_MPEG4 && IsNVOP(Packet)) {
|
||||||
|
av_free_packet(&Packet);
|
||||||
|
goto Done;
|
||||||
|
}
|
||||||
|
|
||||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
|
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,23 +127,22 @@ int FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime, char *ErrorMsg, unsigned M
|
||||||
// Flush the last frames
|
// Flush the last frames
|
||||||
if (CodecContext->has_b_frames) {
|
if (CodecContext->has_b_frames) {
|
||||||
AVPacket NullPacket;
|
AVPacket NullPacket;
|
||||||
InitNullPacket(&NullPacket);
|
InitNullPacket(NullPacket);
|
||||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
|
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!FrameFinished)
|
if (!FrameFinished)
|
||||||
goto Error;
|
goto Error;
|
||||||
|
|
||||||
// Ignore errors for now
|
|
||||||
Error:
|
Error:
|
||||||
Done:
|
Done:;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FFAVFrame *FFLAVFVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_Frame *FFLAVFVideo::GetFrame(int n) {
|
||||||
// PPFrame always holds frame LastFrameNum even if no PP is applied
|
GetFrameCheck(n);
|
||||||
|
|
||||||
if (LastFrameNum == n)
|
if (LastFrameNum == n)
|
||||||
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize);
|
return &LocalFrame;
|
||||||
|
|
||||||
bool HasSeeked = false;
|
bool HasSeeked = false;
|
||||||
int SeekOffset = 0;
|
int SeekOffset = 0;
|
||||||
|
@ -200,14 +169,13 @@ ReSeek:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (n < CurrentFrame) {
|
} else if (n < CurrentFrame) {
|
||||||
snprintf(ErrorMsg, MsgSize, "Non-linear access attempted");
|
throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
return NULL;
|
"Non-linear access attempted");
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int64_t StartTime;
|
int64_t StartTime;
|
||||||
if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize))
|
DecodeNextFrame(&StartTime);
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (HasSeeked) {
|
if (HasSeeked) {
|
||||||
HasSeeked = false;
|
HasSeeked = false;
|
||||||
|
@ -217,10 +185,10 @@ ReSeek:
|
||||||
switch (SeekMode) {
|
switch (SeekMode) {
|
||||||
case 1:
|
case 1:
|
||||||
// No idea where we are so go back a bit further
|
// No idea where we are so go back a bit further
|
||||||
if (ClosestKF + SeekOffset == 0) {
|
if (ClosestKF + SeekOffset == 0)
|
||||||
snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file\n");
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
return NULL;
|
"Frame accurate seeking is not possible in this file");
|
||||||
}
|
|
||||||
|
|
||||||
SeekOffset -= FFMIN(10, ClosestKF + SeekOffset);
|
SeekOffset -= FFMIN(10, ClosestKF + SeekOffset);
|
||||||
goto ReSeek;
|
goto ReSeek;
|
||||||
|
@ -229,8 +197,8 @@ ReSeek:
|
||||||
CurrentFrame = Frames.ClosestFrameFromDTS(StartTime);
|
CurrentFrame = Frames.ClosestFrameFromDTS(StartTime);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
snprintf(ErrorMsg, MsgSize, "Failed assertion");
|
throw FFMS_Exception(FFMS_ERROR_SEEKING, FFMS_ERROR_UNKNOWN,
|
||||||
return NULL;
|
"Failed assertion");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -239,5 +207,5 @@ ReSeek:
|
||||||
} while (CurrentFrame <= n);
|
} while (CurrentFrame <= n);
|
||||||
|
|
||||||
LastFrameNum = n;
|
LastFrameNum = n;
|
||||||
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize);
|
return OutputFrame(DecodeFrame);
|
||||||
}
|
}
|
|
@ -18,7 +18,7 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#include "ffaudiosource.h"
|
#include "audiosource.h"
|
||||||
|
|
||||||
void FFMatroskaAudio::Free(bool CloseCodec) {
|
void FFMatroskaAudio::Free(bool CloseCodec) {
|
||||||
if (CS)
|
if (CS)
|
||||||
|
@ -32,89 +32,74 @@ void FFMatroskaAudio::Free(bool CloseCodec) {
|
||||||
av_freep(&CodecContext);
|
av_freep(&CodecContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track,
|
FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index *Index)
|
||||||
FFIndex *Index, char *ErrorMsg, unsigned MsgSize)
|
: Res(FFSourceResources<FFMS_AudioSource>(this)), FFMS_AudioSource(SourceFile, Index, Track) {
|
||||||
: FFAudio(SourceFile, Index, ErrorMsg, MsgSize) {
|
|
||||||
CodecContext = NULL;
|
CodecContext = NULL;
|
||||||
AVCodec *Codec = NULL;
|
AVCodec *Codec = NULL;
|
||||||
TrackInfo *TI = NULL;
|
TrackInfo *TI = NULL;
|
||||||
CS = NULL;
|
CS = NULL;
|
||||||
|
PacketNumber = 0;
|
||||||
Frames = (*Index)[Track];
|
Frames = (*Index)[Track];
|
||||||
|
|
||||||
if (Frames.size() == 0) {
|
|
||||||
Free(false);
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Audio track contains no frames, was it indexed properly?");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
MC.ST.fp = ffms_fopen(SourceFile, "rb");
|
MC.ST.fp = ffms_fopen(SourceFile, "rb");
|
||||||
if (MC.ST.fp == NULL) {
|
if (MC.ST.fp == NULL)
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
throw ErrorMsg;
|
boost::format("Can't open '%1%': %2%") % SourceFile % strerror(errno));
|
||||||
}
|
|
||||||
|
|
||||||
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
||||||
|
|
||||||
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
|
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
|
||||||
if (MF == NULL) {
|
if (MF == NULL) {
|
||||||
fclose(MC.ST.fp);
|
fclose(MC.ST.fp);
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage);
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
throw ErrorMsg;
|
boost::format("Can't parse Matroska file: %1%") % ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
mkv_SetTrackMask(MF, ~(1 << Track));
|
|
||||||
TI = mkv_GetTrackInfo(MF, Track);
|
TI = mkv_GetTrackInfo(MF, Track);
|
||||||
|
|
||||||
if (TI->CompEnabled) {
|
if (TI->CompEnabled) {
|
||||||
CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage));
|
CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage));
|
||||||
if (CS == NULL) {
|
if (CS == NULL) {
|
||||||
Free(false);
|
Free(false);
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||||
throw ErrorMsg;
|
boost::format("Can't create decompressor: %1%") % ErrorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CodecContext = avcodec_alloc_context();
|
CodecContext = avcodec_alloc_context();
|
||||||
|
|
||||||
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
|
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
|
||||||
if (Codec == NULL) {
|
if (Codec == NULL)
|
||||||
Free(false);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Video codec not found");
|
"Audio codec not found");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext);
|
InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext);
|
||||||
|
|
||||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
if (avcodec_open(CodecContext, Codec) < 0)
|
||||||
Free(false);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
"Could not open audio codec");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
Res.CloseCodec(true);
|
||||||
|
|
||||||
// Always try to decode a frame to make sure all required parameters are known
|
// Always try to decode a frame to make sure all required parameters are known
|
||||||
int64_t Dummy;
|
int64_t Dummy;
|
||||||
if (DecodeNextAudioBlock(&Dummy, 0, ErrorMsg, MsgSize) < 0) {
|
DecodeNextAudioBlock(&Dummy);
|
||||||
Free(true);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
avcodec_flush_buffers(CodecContext);
|
avcodec_flush_buffers(CodecContext);
|
||||||
|
|
||||||
FillAP(AP, CodecContext, Frames);
|
FillAP(AP, CodecContext, Frames);
|
||||||
|
|
||||||
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) {
|
if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0)
|
||||||
Free(true);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio");
|
"Codec returned zero size audio");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
|
AudioCache.Initialize((AP.Channels * AP.BitsPerSample) / 8, 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMatroskaAudio::~FFMatroskaAudio() {
|
void FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) {
|
||||||
Free(true);
|
GetAudioCheck(Start, Count);
|
||||||
}
|
|
||||||
|
|
||||||
int FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) {
|
|
||||||
const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||||
memset(Buf, 0, static_cast<size_t>(SizeConst * Count));
|
memset(Buf, 0, static_cast<size_t>(SizeConst * Count));
|
||||||
|
|
||||||
|
@ -125,58 +110,50 @@ int FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count, char *Err
|
||||||
int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
|
int64_t CacheEnd = AudioCache.FillRequest(Start, Count, DstBuf);
|
||||||
// Was everything in the cache?
|
// Was everything in the cache?
|
||||||
if (CacheEnd == Start + Count)
|
if (CacheEnd == Start + Count)
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
int CurrentAudioBlock;
|
|
||||||
// Is seeking required to decode the requested samples?
|
// Is seeking required to decode the requested samples?
|
||||||
// if (!(CurrentSample >= Start && CurrentSample <= CacheEnd)) {
|
// if (!(CurrentSample >= Start && CurrentSample <= CacheEnd)) {
|
||||||
if (CurrentSample != CacheEnd) {
|
if (CurrentSample != CacheEnd) {
|
||||||
PreDecBlocks = 15;
|
PreDecBlocks = 15;
|
||||||
CurrentAudioBlock = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks, (int64_t)0);
|
PacketNumber = FFMAX((int64_t)Frames.FindClosestAudioKeyFrame(CacheEnd) - PreDecBlocks, (int64_t)0);;
|
||||||
avcodec_flush_buffers(CodecContext);
|
avcodec_flush_buffers(CodecContext);
|
||||||
} else {
|
}
|
||||||
CurrentAudioBlock = Frames.FindClosestAudioKeyFrame(CurrentSample);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t DecodeCount;
|
int64_t DecodeCount;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int Ret = DecodeNextAudioBlock(&DecodeCount, CurrentAudioBlock, ErrorMsg, MsgSize);
|
const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos];
|
||||||
if (Ret < 0) {
|
DecodeNextAudioBlock(&DecodeCount);
|
||||||
// FIXME
|
|
||||||
//Env->ThrowError("Bleh, bad audio decoding");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache the block if enough blocks before it have been decoded to avoid garbage
|
// Cache the block if enough blocks before it have been decoded to avoid garbage
|
||||||
if (PreDecBlocks == 0) {
|
if (PreDecBlocks == 0) {
|
||||||
AudioCache.CacheBlock(Frames[CurrentAudioBlock].SampleStart, DecodeCount, &DecodingBuffer[0]);
|
AudioCache.CacheBlock(FI.SampleStart, DecodeCount, &DecodingBuffer[0]);
|
||||||
CacheEnd = AudioCache.FillRequest(CacheEnd, Start + Count - CacheEnd, DstBuf + (CacheEnd - Start) * SizeConst);
|
CacheEnd = AudioCache.FillRequest(CacheEnd, Start + Count - CacheEnd, DstBuf + (CacheEnd - Start) * SizeConst);
|
||||||
} else {
|
} else {
|
||||||
PreDecBlocks--;
|
PreDecBlocks--;
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentAudioBlock++;
|
} while (Start + Count - CacheEnd > 0 && PacketNumber < Frames.size());
|
||||||
if (CurrentAudioBlock < static_cast<int>(Frames.size()))
|
|
||||||
CurrentSample = Frames[CurrentAudioBlock].SampleStart;
|
|
||||||
} while (Start + Count - CacheEnd > 0 && CurrentAudioBlock < static_cast<int>(Frames.size()));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count, int AudioBlock, char *ErrorMsg, unsigned MsgSize) {
|
void FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count) {
|
||||||
const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8;
|
||||||
int Ret = -1;
|
int Ret = -1;
|
||||||
*Count = 0;
|
*Count = 0;
|
||||||
uint8_t *Buf = &DecodingBuffer[0];
|
uint8_t *Buf = &DecodingBuffer[0];
|
||||||
AVPacket TempPacket;
|
AVPacket TempPacket;
|
||||||
InitNullPacket(&TempPacket);
|
InitNullPacket(TempPacket);
|
||||||
|
|
||||||
unsigned int FrameSize = Frames[AudioBlock].FrameSize;
|
const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos];
|
||||||
if (ReadFrame(Frames[AudioBlock].FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize))
|
unsigned int FrameSize = FI.FrameSize;
|
||||||
return 1;
|
CurrentSample = FI.SampleStart + FI.SampleCount;
|
||||||
|
PacketNumber++;
|
||||||
|
|
||||||
|
ReadFrame(FI.FilePos, FrameSize, CS, MC);
|
||||||
TempPacket.data = MC.Buffer;
|
TempPacket.data = MC.Buffer;
|
||||||
TempPacket.size = FrameSize;
|
TempPacket.size = FrameSize;
|
||||||
if (Frames[AudioBlock].KeyFrame)
|
if (FI.KeyFrame)
|
||||||
TempPacket.flags = AV_PKT_FLAG_KEY;
|
TempPacket.flags = AV_PKT_FLAG_KEY;
|
||||||
else
|
else
|
||||||
TempPacket.flags = 0;
|
TempPacket.flags = 0;
|
||||||
|
@ -197,6 +174,5 @@ int FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count, int AudioBlock, char *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Done:
|
Done:;
|
||||||
return 0;
|
|
||||||
}
|
}
|
|
@ -23,25 +23,26 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
FFMatroskaIndexer::FFMatroskaIndexer(const char *Filename, char *ErrorMsg, unsigned MsgSize) : FFIndexer(Filename, ErrorMsg, MsgSize) {
|
FFMatroskaIndexer::FFMatroskaIndexer(const char *Filename) : FFMS_Indexer(Filename) {
|
||||||
memset(Codec, 0, sizeof(Codec));
|
|
||||||
SourceFile = Filename;
|
SourceFile = Filename;
|
||||||
char ErrorMessage[256];
|
char ErrorMessage[256];
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
Codec[i] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
InitStdIoStream(&MC.ST);
|
InitStdIoStream(&MC.ST);
|
||||||
MC.ST.fp = ffms_fopen(SourceFile, "rb");
|
MC.ST.fp = ffms_fopen(SourceFile, "rb");
|
||||||
if (MC.ST.fp == NULL) {
|
if (MC.ST.fp == NULL)
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
throw ErrorMsg;
|
boost::format("Can't open '%1%': %2%") % SourceFile % strerror(errno));
|
||||||
}
|
|
||||||
|
|
||||||
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
||||||
|
|
||||||
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
|
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
|
||||||
if (MF == NULL) {
|
if (MF == NULL) {
|
||||||
fclose(MC.ST.fp);
|
fclose(MC.ST.fp);
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage);
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
throw ErrorMsg;
|
boost::format("Can't parse Matroska file: %1%") % ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) {
|
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) {
|
||||||
|
@ -55,17 +56,17 @@ FFMatroskaIndexer::~FFMatroskaIndexer() {
|
||||||
fclose(MC.ST.fp);
|
fclose(MC.ST.fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
FFMS_Index *FFMatroskaIndexer::DoIndexing() {
|
||||||
char ErrorMessage[256];
|
char ErrorMessage[256];
|
||||||
std::vector<SharedAudioContext> AudioContexts(mkv_GetNumTracks(MF), SharedAudioContext(true));
|
std::vector<SharedAudioContext> AudioContexts(mkv_GetNumTracks(MF), SharedAudioContext(true));
|
||||||
std::vector<SharedVideoContext> VideoContexts(mkv_GetNumTracks(MF), SharedVideoContext(true));
|
std::vector<SharedVideoContext> VideoContexts(mkv_GetNumTracks(MF), SharedVideoContext(true));
|
||||||
|
|
||||||
std::auto_ptr<FFIndex> TrackIndices(new FFIndex(Filesize, Digest));
|
std::auto_ptr<FFMS_Index> TrackIndices(new FFMS_Index(Filesize, Digest));
|
||||||
TrackIndices->Decoder = 1;
|
TrackIndices->Decoder = FFMS_SOURCE_MATROSKA;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) {
|
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) {
|
||||||
TrackInfo *TI = mkv_GetTrackInfo(MF, i);
|
TrackInfo *TI = mkv_GetTrackInfo(MF, i);
|
||||||
TrackIndices->push_back(FFTrack(mkv_TruncFloat(mkv_GetTrackInfo(MF, i)->TimecodeScale), 1000000, HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, i)->Type)));
|
TrackIndices->push_back(FFMS_Track(mkv_TruncFloat(mkv_GetTrackInfo(MF, i)->TimecodeScale), 1000000, HaaliTrackTypeToFFTrackType(mkv_GetTrackInfo(MF, i)->Type)));
|
||||||
|
|
||||||
if (HaaliTrackTypeToFFTrackType(TI->Type) == FFMS_TYPE_VIDEO && Codec[i] && (VideoContexts[i].Parser = av_parser_init(Codec[i]->id))) {
|
if (HaaliTrackTypeToFFTrackType(TI->Type) == FFMS_TYPE_VIDEO && Codec[i] && (VideoContexts[i].Parser = av_parser_init(Codec[i]->id))) {
|
||||||
|
|
||||||
|
@ -75,16 +76,15 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
|
|
||||||
if (avcodec_open(CodecContext, Codec[i]) < 0) {
|
if (avcodec_open(CodecContext, Codec[i]) < 0) {
|
||||||
av_freep(&CodecContext);
|
av_freep(&CodecContext);
|
||||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||||
return NULL;
|
"Could not open video codec");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TI->CompEnabled) {
|
if (TI->CompEnabled) {
|
||||||
VideoContexts[i].CS = cs_Create(MF, i, ErrorMessage, sizeof(ErrorMessage));
|
VideoContexts[i].CS = cs_Create(MF, i, ErrorMessage, sizeof(ErrorMessage));
|
||||||
if (VideoContexts[i].CS == NULL) {
|
if (VideoContexts[i].CS == NULL)
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||||
return NULL;
|
boost::format("Can't create decompressor: %1%") % ErrorMessage);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoContexts[i].CodecContext = CodecContext;
|
VideoContexts[i].CodecContext = CodecContext;
|
||||||
|
@ -102,8 +102,8 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
if (AudioContexts[i].CS == NULL) {
|
if (AudioContexts[i].CS == NULL) {
|
||||||
av_freep(&AudioCodecContext);
|
av_freep(&AudioCodecContext);
|
||||||
AudioContexts[i].CodecContext = NULL;
|
AudioContexts[i].CodecContext = NULL;
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||||
return NULL;
|
boost::format("Can't create decompressor: %1%") % ErrorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,15 +111,15 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
if (AudioCodec == NULL) {
|
if (AudioCodec == NULL) {
|
||||||
av_freep(&AudioCodecContext);
|
av_freep(&AudioCodecContext);
|
||||||
AudioContexts[i].CodecContext = NULL;
|
AudioContexts[i].CodecContext = NULL;
|
||||||
snprintf(ErrorMsg, MsgSize, "Audio codec not found");
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||||
return NULL;
|
"Audio codec not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
|
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
|
||||||
av_freep(&AudioCodecContext);
|
av_freep(&AudioCodecContext);
|
||||||
AudioContexts[i].CodecContext = NULL;
|
AudioContexts[i].CodecContext = NULL;
|
||||||
snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||||
return NULL;
|
"Could not open audio codec");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
IndexMask &= ~(1 << i);
|
IndexMask &= ~(1 << i);
|
||||||
|
@ -131,15 +131,14 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
ulonglong StartTime, EndTime, FilePos;
|
ulonglong StartTime, EndTime, FilePos;
|
||||||
unsigned int Track, FrameFlags, FrameSize;
|
unsigned int Track, FrameFlags, FrameSize;
|
||||||
AVPacket TempPacket;
|
AVPacket TempPacket;
|
||||||
InitNullPacket(&TempPacket);
|
InitNullPacket(TempPacket);
|
||||||
|
|
||||||
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) {
|
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) {
|
||||||
// Update progress
|
// Update progress
|
||||||
if (IC) {
|
if (IC) {
|
||||||
if ((*IC)(ftello(MC.ST.fp), Filesize, ICPrivate)) {
|
if ((*IC)(ftello(MC.ST.fp), Filesize, ICPrivate))
|
||||||
snprintf(ErrorMsg, MsgSize, "Cancelled by user");
|
throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
|
||||||
return NULL;
|
"Cancelled by user");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only create index entries for video for now to save space
|
// Only create index entries for video for now to save space
|
||||||
|
@ -155,9 +154,10 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
|
|
||||||
(*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(StartTime, RepeatPict, (FrameFlags & FRAME_KF) != 0, FilePos, FrameSize));
|
(*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(StartTime, RepeatPict, (FrameFlags & FRAME_KF) != 0, FilePos, FrameSize));
|
||||||
} else if (mkv_GetTrackInfo(MF, Track)->Type == TT_AUDIO && (IndexMask & (1 << Track))) {
|
} else if (mkv_GetTrackInfo(MF, Track)->Type == TT_AUDIO && (IndexMask & (1 << Track))) {
|
||||||
(*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(StartTime, AudioContexts[Track].CurrentSample, (FrameFlags & FRAME_KF) != 0, FilePos, FrameSize));
|
int64_t StartSample = AudioContexts[Track].CurrentSample;
|
||||||
ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC, ErrorMsg, MsgSize);
|
unsigned int CompressedFrameSize = FrameSize;
|
||||||
AVCodecContext *AudioCodecContext = AudioContexts[Track].CodecContext;
|
AVCodecContext *AudioCodecContext = AudioContexts[Track].CodecContext;
|
||||||
|
ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC);
|
||||||
TempPacket.data = MC.Buffer;
|
TempPacket.data = MC.Buffer;
|
||||||
TempPacket.size = FrameSize;
|
TempPacket.size = FrameSize;
|
||||||
if ((FrameFlags & FRAME_KF) != 0)
|
if ((FrameFlags & FRAME_KF) != 0)
|
||||||
|
@ -169,13 +169,18 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
|
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
|
||||||
int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
|
int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
|
||||||
if (Ret < 0) {
|
if (Ret < 0) {
|
||||||
if (IgnoreDecodeErrors) {
|
if (ErrorHandling == FFMS_IEH_ABORT) {
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_DECODING,
|
||||||
|
"Audio decoding error");
|
||||||
|
} else if (ErrorHandling == FFMS_IEH_CLEAR_TRACK) {
|
||||||
(*TrackIndices)[Track].clear();
|
(*TrackIndices)[Track].clear();
|
||||||
IndexMask &= ~(1 << Track);
|
IndexMask &= ~(1 << Track);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else if (ErrorHandling == FFMS_IEH_STOP_TRACK) {
|
||||||
snprintf(ErrorMsg, MsgSize, "Audio decoding error");
|
IndexMask &= ~(1 << Track);
|
||||||
return NULL;
|
break;
|
||||||
|
} else if (ErrorHandling == FFMS_IEH_IGNORE) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,8 +193,11 @@ FFIndex *FFMatroskaIndexer::DoIndexing(char *ErrorMsg, unsigned MsgSize) {
|
||||||
AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
|
AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
|
||||||
|
|
||||||
if (DumpMask & (1 << Track))
|
if (DumpMask & (1 << Track))
|
||||||
WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize, ErrorMsg, MsgSize);
|
WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(*TrackIndices)[Track].push_back(TFrameInfo::AudioFrameInfo(StartTime, StartSample,
|
||||||
|
static_cast<unsigned int>(AudioContexts[Track].CurrentSample - StartSample), (FrameFlags & FRAME_KF) != 0, FilePos, CompressedFrameSize));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +215,7 @@ FFMS_TrackType FFMatroskaIndexer::GetTrackType(int Track) {
|
||||||
|
|
||||||
const char *FFMatroskaIndexer::GetTrackCodec(int Track) {
|
const char *FFMatroskaIndexer::GetTrackCodec(int Track) {
|
||||||
if (Codec[Track])
|
if (Codec[Track])
|
||||||
return Codec[Track]->long_name;
|
return Codec[Track]->name;
|
||||||
else
|
else
|
||||||
return "Unsupported codec/Unknown codec name";
|
return "Unsupported codec/Unknown codec name";
|
||||||
}
|
}
|
|
@ -18,7 +18,9 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#include "ffvideosource.h"
|
#include "videosource.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void FFMatroskaVideo::Free(bool CloseCodec) {
|
void FFMatroskaVideo::Free(bool CloseCodec) {
|
||||||
if (CS)
|
if (CS)
|
||||||
|
@ -33,46 +35,39 @@ void FFMatroskaVideo::Free(bool CloseCodec) {
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
|
FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
|
||||||
FFIndex *Index, const char *PP,
|
FFMS_Index *Index, int Threads)
|
||||||
int Threads, char *ErrorMsg, unsigned MsgSize)
|
: Res(FFSourceResources<FFMS_VideoSource>(this)), FFMS_VideoSource(SourceFile, Index, Track) {
|
||||||
: FFVideo(SourceFile, Index, ErrorMsg, MsgSize) {
|
|
||||||
|
|
||||||
AVCodec *Codec = NULL;
|
AVCodec *Codec = NULL;
|
||||||
CodecContext = NULL;
|
CodecContext = NULL;
|
||||||
TrackInfo *TI = NULL;
|
TrackInfo *TI = NULL;
|
||||||
CS = NULL;
|
CS = NULL;
|
||||||
|
PacketNumber = 0;
|
||||||
VideoTrack = Track;
|
VideoTrack = Track;
|
||||||
Frames = (*Index)[VideoTrack];
|
Frames = (*Index)[VideoTrack];
|
||||||
|
|
||||||
if (Frames.size() == 0) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Video track contains no frames");
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
MC.ST.fp = ffms_fopen(SourceFile, "rb");
|
MC.ST.fp = ffms_fopen(SourceFile, "rb");
|
||||||
if (MC.ST.fp == NULL) {
|
if (MC.ST.fp == NULL)
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno));
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
throw ErrorMsg;
|
boost::format("Can't open '%1%': %2%") % SourceFile % strerror(errno));
|
||||||
}
|
|
||||||
|
|
||||||
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE);
|
||||||
|
|
||||||
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
|
MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
|
||||||
if (MF == NULL) {
|
if (MF == NULL) {
|
||||||
fclose(MC.ST.fp);
|
fclose(MC.ST.fp);
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage);
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
throw ErrorMsg;
|
boost::format("Can't parse Matroska file: %1%") % ErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
mkv_SetTrackMask(MF, ~(1 << VideoTrack));
|
|
||||||
TI = mkv_GetTrackInfo(MF, VideoTrack);
|
TI = mkv_GetTrackInfo(MF, VideoTrack);
|
||||||
|
|
||||||
if (TI->CompEnabled) {
|
if (TI->CompEnabled) {
|
||||||
CS = cs_Create(MF, VideoTrack, ErrorMessage, sizeof(ErrorMessage));
|
CS = cs_Create(MF, VideoTrack, ErrorMessage, sizeof(ErrorMessage));
|
||||||
if (CS == NULL) {
|
if (CS == NULL) {
|
||||||
Free(false);
|
Free(false);
|
||||||
snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage);
|
throw FFMS_Exception(FFMS_ERROR_CODEC, FFMS_ERROR_UNSUPPORTED,
|
||||||
throw ErrorMsg;
|
boost::format("Can't create decompressor: %1%") % ErrorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,48 +75,35 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
|
||||||
CodecContext->thread_count = Threads;
|
CodecContext->thread_count = Threads;
|
||||||
|
|
||||||
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
|
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
|
||||||
if (Codec == NULL) {
|
if (Codec == NULL)
|
||||||
Free(false);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Video codec not found");
|
"Video codec not found");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext);
|
InitializeCodecContextFromMatroskaTrackInfo(TI, CodecContext);
|
||||||
|
|
||||||
if (avcodec_open(CodecContext, Codec) < 0) {
|
if (avcodec_open(CodecContext, Codec) < 0)
|
||||||
Free(false);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Could not open video codec");
|
"Could not open video codec");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
Res.CloseCodec(true);
|
||||||
|
|
||||||
// Always try to decode a frame to make sure all required parameters are known
|
// Always try to decode a frame to make sure all required parameters are known
|
||||||
int64_t Dummy;
|
DecodeNextFrame();
|
||||||
if (DecodeNextFrame(&Dummy, ErrorMsg, MsgSize)) {
|
|
||||||
Free(true);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
VP.Width = CodecContext->width;
|
|
||||||
VP.Height = CodecContext->height;;
|
|
||||||
VP.FPSDenominator = 1;
|
VP.FPSDenominator = 1;
|
||||||
VP.FPSNumerator = 30;
|
VP.FPSNumerator = 30;
|
||||||
VP.RFFDenominator = CodecContext->time_base.num;
|
VP.RFFDenominator = CodecContext->time_base.num;
|
||||||
VP.RFFNumerator = CodecContext->time_base.den;
|
VP.RFFNumerator = CodecContext->time_base.den;
|
||||||
VP.NumFrames = Frames.size();
|
VP.NumFrames = Frames.size();
|
||||||
VP.VPixelFormat = CodecContext->pix_fmt;
|
VP.TopFieldFirst = DecodeFrame->top_field_first;
|
||||||
|
VP.ColorSpace = CodecContext->colorspace;
|
||||||
|
VP.ColorRange = CodecContext->color_range;
|
||||||
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||||
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||||
|
|
||||||
if (VP.Width <= 0 || VP.Height <= 0) {
|
if (CodecContext->width <= 0 || CodecContext->height <= 0)
|
||||||
Free(true);
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_CODEC,
|
||||||
snprintf(ErrorMsg, MsgSize, "Codec returned zero size video");
|
"Codec returned zero size video");
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (InitPP(PP, ErrorMsg, MsgSize)) {
|
|
||||||
Free(true);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the average framerate
|
// Calculate the average framerate
|
||||||
if (Frames.size() >= 2) {
|
if (Frames.size() >= 2) {
|
||||||
|
@ -131,10 +113,7 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Output the already decoded frame so it isn't wasted
|
// Output the already decoded frame so it isn't wasted
|
||||||
if (!OutputFrame(DecodeFrame, ErrorMsg, MsgSize)) {
|
OutputFrame(DecodeFrame);
|
||||||
Free(true);
|
|
||||||
throw ErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set AR variables
|
// Set AR variables
|
||||||
VP.SARNum = TI->AV.Video.DisplayWidth * TI->AV.Video.PixelHeight;
|
VP.SARNum = TI->AV.Video.DisplayWidth * TI->AV.Video.PixelHeight;
|
||||||
|
@ -147,33 +126,33 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
|
||||||
VP.CropBottom = TI->AV.Video.CropB;
|
VP.CropBottom = TI->AV.Video.CropB;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFMatroskaVideo::~FFMatroskaVideo() {
|
void FFMatroskaVideo::DecodeNextFrame() {
|
||||||
Free(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) {
|
|
||||||
int FrameFinished = 0;
|
int FrameFinished = 0;
|
||||||
*AFirstStartTime = -1;
|
|
||||||
AVPacket Packet;
|
AVPacket Packet;
|
||||||
InitNullPacket(&Packet);
|
InitNullPacket(Packet);
|
||||||
|
|
||||||
ulonglong StartTime, EndTime, FilePos;
|
unsigned int FrameSize;
|
||||||
unsigned int Track, FrameFlags, FrameSize;
|
|
||||||
|
|
||||||
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) {
|
while (PacketNumber < Frames.size()) {
|
||||||
if (*AFirstStartTime < 0)
|
// The additional indirection is because the packets are stored in
|
||||||
*AFirstStartTime = StartTime;
|
// presentation order and not decoding order, this is unnoticable
|
||||||
|
// in the other sources where less is done manually
|
||||||
if (ReadFrame(FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize))
|
const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos];
|
||||||
return 1;
|
FrameSize = FI.FrameSize;
|
||||||
|
ReadFrame(FI.FilePos, FrameSize, CS, MC);
|
||||||
|
|
||||||
Packet.data = MC.Buffer;
|
Packet.data = MC.Buffer;
|
||||||
Packet.size = FrameSize;
|
Packet.size = FrameSize;
|
||||||
if (FrameFlags & FRAME_KF)
|
if (FI.KeyFrame)
|
||||||
Packet.flags = AV_PKT_FLAG_KEY;
|
Packet.flags = AV_PKT_FLAG_KEY;
|
||||||
else
|
else
|
||||||
Packet.flags = 0;
|
Packet.flags = 0;
|
||||||
|
|
||||||
|
PacketNumber++;
|
||||||
|
|
||||||
|
if (CodecContext->codec_id == CODEC_ID_MPEG4 && IsNVOP(Packet))
|
||||||
|
goto Done;
|
||||||
|
|
||||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
|
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
|
||||||
|
|
||||||
if (FrameFinished)
|
if (FrameFinished)
|
||||||
|
@ -183,7 +162,7 @@ int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, u
|
||||||
// Flush the last frames
|
// Flush the last frames
|
||||||
if (CodecContext->has_b_frames) {
|
if (CodecContext->has_b_frames) {
|
||||||
AVPacket NullPacket;
|
AVPacket NullPacket;
|
||||||
InitNullPacket(&NullPacket);
|
InitNullPacket(NullPacket);
|
||||||
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
|
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &NullPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,40 +170,27 @@ int FFMatroskaVideo::DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, u
|
||||||
goto Error;
|
goto Error;
|
||||||
|
|
||||||
Error:
|
Error:
|
||||||
Done:
|
Done:;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FFAVFrame *FFMatroskaVideo::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_Frame *FFMatroskaVideo::GetFrame(int n) {
|
||||||
// PPFrame always holds frame LastFrameNum even if no PP is applied
|
GetFrameCheck(n);
|
||||||
|
|
||||||
if (LastFrameNum == n)
|
if (LastFrameNum == n)
|
||||||
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize);
|
return &LocalFrame;
|
||||||
|
|
||||||
bool HasSeeked = false;
|
int ClosestKF = Frames.FindClosestVideoKeyFrame(n);
|
||||||
|
if (CurrentFrame > n || ClosestKF > CurrentFrame + 10) {
|
||||||
if (n < CurrentFrame || Frames.FindClosestVideoKeyFrame(n) > CurrentFrame) {
|
PacketNumber = ClosestKF;
|
||||||
mkv_Seek(MF, Frames[n].DTS, MKVF_SEEK_TO_PREV_KEYFRAME_STRICT);
|
CurrentFrame = ClosestKF;
|
||||||
avcodec_flush_buffers(CodecContext);
|
avcodec_flush_buffers(CodecContext);
|
||||||
HasSeeked = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
int64_t StartTime;
|
DecodeNextFrame();
|
||||||
if (DecodeNextFrame(&StartTime, ErrorMsg, MsgSize))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
if (HasSeeked) {
|
|
||||||
HasSeeked = false;
|
|
||||||
|
|
||||||
if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentFrame++;
|
CurrentFrame++;
|
||||||
} while (CurrentFrame <= n);
|
} while (CurrentFrame <= n);
|
||||||
|
|
||||||
LastFrameNum = n;
|
LastFrameNum = n;
|
||||||
return OutputFrame(DecodeFrame, ErrorMsg, MsgSize);
|
return OutputFrame(DecodeFrame);
|
||||||
}
|
}
|
|
@ -104,13 +104,13 @@ longlong StdIoGetFileSize(StdIoStream *st) {
|
||||||
|
|
||||||
void InitStdIoStream(StdIoStream *st) {
|
void InitStdIoStream(StdIoStream *st) {
|
||||||
memset(st,0,sizeof(StdIoStream));
|
memset(st,0,sizeof(StdIoStream));
|
||||||
st->base.read = StdIoRead;
|
st->base.read = (int (*)(InputStream *,ulonglong,void *,int))StdIoRead;
|
||||||
st->base.scan = StdIoScan;
|
st->base.scan = (longlong (*)(InputStream *,ulonglong,unsigned int))StdIoScan;
|
||||||
st->base.getcachesize = StdIoGetCacheSize;
|
st->base.getcachesize = (unsigned int (*)(InputStream *))StdIoGetCacheSize;
|
||||||
st->base.geterror = StdIoGetLastError;
|
st->base.geterror = (const char *(*)(InputStream *))StdIoGetLastError;
|
||||||
st->base.memalloc = StdIoMalloc;
|
st->base.memalloc = (void *(*)(InputStream *,size_t))StdIoMalloc;
|
||||||
st->base.memrealloc = StdIoRealloc;
|
st->base.memrealloc = (void *(*)(InputStream *,void *,size_t))StdIoRealloc;
|
||||||
st->base.memfree = StdIoFree;
|
st->base.memfree = (void (*)(InputStream *,void *))StdIoFree;
|
||||||
st->base.progress = StdIoProgress;
|
st->base.progress = (int (*)(InputStream *,ulonglong,ulonglong))StdIoProgress;
|
||||||
st->base.getfilesize = StdIoGetFileSize;
|
st->base.getfilesize = (longlong (*)(InputStream *))StdIoGetFileSize;
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,6 +46,38 @@ extern const AVCodecTag ff_codec_wav_tags[];
|
||||||
|
|
||||||
extern int CPUFeatures;
|
extern int CPUFeatures;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
FFMS_Exception::FFMS_Exception(int ErrorType, int SubType, const char *Message) : _ErrorType(ErrorType), _SubType(SubType), _Message(Message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
FFMS_Exception::FFMS_Exception(int ErrorType, int SubType, const std::string &Message) : _ErrorType(ErrorType), _SubType(SubType), _Message(Message) {
|
||||||
|
}
|
||||||
|
|
||||||
|
FFMS_Exception::FFMS_Exception(int ErrorType, int SubType, const boost::format &Message) : _ErrorType(ErrorType), _SubType(SubType), _Message(Message.str()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
FFMS_Exception::~FFMS_Exception() throw () {
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string &FFMS_Exception::GetErrorMessage() const {
|
||||||
|
return _Message;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FFMS_Exception::CopyOut(FFMS_ErrorInfo *ErrorInfo) const {
|
||||||
|
if (ErrorInfo) {
|
||||||
|
ErrorInfo->ErrorType = _ErrorType;
|
||||||
|
ErrorInfo->SubType = _SubType;
|
||||||
|
|
||||||
|
if (ErrorInfo->BufferSize > 0) {
|
||||||
|
memset(ErrorInfo->Buffer, 0, ErrorInfo->BufferSize);
|
||||||
|
_Message.copy(ErrorInfo->Buffer, ErrorInfo->BufferSize - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (_ErrorType << 16) | _SubType;
|
||||||
|
}
|
||||||
|
|
||||||
int GetSWSCPUFlags() {
|
int GetSWSCPUFlags() {
|
||||||
int Flags = 0;
|
int Flags = 0;
|
||||||
|
|
||||||
|
@ -78,6 +110,16 @@ int GetPPCPUFlags() {
|
||||||
return Flags;
|
return Flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ClearErrorInfo(FFMS_ErrorInfo *ErrorInfo) {
|
||||||
|
if (ErrorInfo) {
|
||||||
|
ErrorInfo->ErrorType = FFMS_ERROR_SUCCESS;
|
||||||
|
ErrorInfo->SubType = FFMS_ERROR_SUCCESS;
|
||||||
|
|
||||||
|
if (ErrorInfo->BufferSize > 0)
|
||||||
|
ErrorInfo->Buffer[0] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT) {
|
FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT) {
|
||||||
switch (TT) {
|
switch (TT) {
|
||||||
case TT_VIDEO: return FFMS_TYPE_VIDEO; break;
|
case TT_VIDEO: return FFMS_TYPE_VIDEO; break;
|
||||||
|
@ -87,90 +129,92 @@ FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize) {
|
void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context) {
|
||||||
if (CS) {
|
if (CS) {
|
||||||
char CSBuffer[4096];
|
|
||||||
|
|
||||||
unsigned int DecompressedFrameSize = 0;
|
unsigned int DecompressedFrameSize = 0;
|
||||||
|
|
||||||
cs_NextFrame(CS, FilePos, FrameSize);
|
cs_NextFrame(CS, FilePos, FrameSize);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int ReadBytes = cs_ReadData(CS, CSBuffer, sizeof(CSBuffer));
|
int ReadBytes = cs_ReadData(CS, Context.CSBuffer, sizeof(Context.CSBuffer));
|
||||||
if (ReadBytes < 0) {
|
if (ReadBytes < 0)
|
||||||
snprintf(ErrorMsg, MsgSize, "Error decompressing data: %s", cs_GetLastError(CS));
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
return 1;
|
boost::format("Error decompressing data: %1%") % cs_GetLastError(CS));
|
||||||
}
|
|
||||||
if (ReadBytes == 0) {
|
if (ReadBytes == 0) {
|
||||||
FrameSize = DecompressedFrameSize;
|
FrameSize = DecompressedFrameSize;
|
||||||
return 0;
|
memset(Context.Buffer + DecompressedFrameSize, 0,
|
||||||
|
Context.BufferSize + FF_INPUT_BUFFER_PADDING_SIZE - DecompressedFrameSize);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Context.BufferSize < DecompressedFrameSize + ReadBytes) {
|
if (Context.BufferSize < DecompressedFrameSize + ReadBytes) {
|
||||||
Context.BufferSize = FrameSize;
|
Context.BufferSize = DecompressedFrameSize + ReadBytes;
|
||||||
Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16);
|
Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + FF_INPUT_BUFFER_PADDING_SIZE);
|
||||||
if (Context.Buffer == NULL) {
|
if (Context.Buffer == NULL)
|
||||||
snprintf(ErrorMsg, MsgSize, "Out of memory");
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
|
||||||
return 2;
|
"Out of memory");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(Context.Buffer + DecompressedFrameSize, CSBuffer, ReadBytes);
|
memcpy(Context.Buffer + DecompressedFrameSize, Context.CSBuffer, ReadBytes);
|
||||||
DecompressedFrameSize += ReadBytes;
|
DecompressedFrameSize += ReadBytes;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (fseeko(Context.ST.fp, FilePos, SEEK_SET)) {
|
if (fseeko(Context.ST.fp, FilePos, SEEK_SET))
|
||||||
snprintf(ErrorMsg, MsgSize, "fseek(): %s", strerror(errno));
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_SEEKING,
|
||||||
return 3;
|
boost::format("fseek(): %1%") % strerror(errno));
|
||||||
}
|
|
||||||
|
|
||||||
if (Context.BufferSize < FrameSize) {
|
if (Context.BufferSize < FrameSize) {
|
||||||
Context.BufferSize = FrameSize;
|
Context.BufferSize = FrameSize;
|
||||||
Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16);
|
Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16);
|
||||||
if (Context.Buffer == NULL) {
|
if (Context.Buffer == NULL)
|
||||||
snprintf(ErrorMsg, MsgSize, "Out of memory");
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
|
||||||
return 4;
|
"Out of memory");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ReadBytes = fread(Context.Buffer, 1, FrameSize, Context.ST.fp);
|
size_t ReadBytes = fread(Context.Buffer, 1, FrameSize, Context.ST.fp);
|
||||||
if (ReadBytes != FrameSize) {
|
if (ReadBytes != FrameSize) {
|
||||||
if (ReadBytes == 0) {
|
if (ReadBytes == 0) {
|
||||||
if (feof(Context.ST.fp)) {
|
if (feof(Context.ST.fp)) {
|
||||||
snprintf(ErrorMsg, MsgSize, "Unexpected EOF while reading frame");
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
return 5;
|
"Unexpected EOF while reading frame");
|
||||||
} else {
|
} else {
|
||||||
snprintf(ErrorMsg, MsgSize, "Error reading frame: %s", strerror(errno));
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_SEEKING,
|
||||||
return 6;
|
boost::format("Error reading frame: %1%") % strerror(errno));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
snprintf(ErrorMsg, MsgSize, "Short read while reading frame");
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
return 7;
|
"Short read while reading frame");
|
||||||
}
|
}
|
||||||
snprintf(ErrorMsg, MsgSize, "Unknown read error");
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
return 8;
|
"Unknown read error");
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitNullPacket(AVPacket *pkt) {
|
void InitNullPacket(AVPacket &pkt) {
|
||||||
av_init_packet(pkt);
|
av_init_packet(&pkt);
|
||||||
pkt->data = NULL;
|
pkt.data = NULL;
|
||||||
pkt->size = 0;
|
pkt.size = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FillAP(FFAudioProperties &AP, AVCodecContext *CTX, FFTrack &Frames) {
|
bool IsNVOP(AVPacket &pkt) {
|
||||||
|
const uint8_t MPEG4NVOP[] = { 0x00, 0x00, 0x01, 0xB6 };
|
||||||
|
return pkt.size == 7 && !memcmp(pkt.data, MPEG4NVOP, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames) {
|
||||||
AP.SampleFormat = static_cast<FFMS_SampleFormat>(CTX->sample_fmt);
|
AP.SampleFormat = static_cast<FFMS_SampleFormat>(CTX->sample_fmt);
|
||||||
AP.BitsPerSample = av_get_bits_per_sample_format(CTX->sample_fmt);
|
AP.BitsPerSample = av_get_bits_per_sample_format(CTX->sample_fmt);
|
||||||
if (CTX->sample_fmt == SAMPLE_FMT_S32)
|
if (CTX->sample_fmt == SAMPLE_FMT_S32 && CTX->bits_per_raw_sample)
|
||||||
AP.BitsPerSample = CTX->bits_per_raw_sample;
|
AP.BitsPerSample = CTX->bits_per_raw_sample;
|
||||||
|
|
||||||
AP.Channels = CTX->channels;;
|
AP.Channels = CTX->channels;;
|
||||||
AP.ChannelLayout = CTX->channel_layout;
|
AP.ChannelLayout = CTX->channel_layout;
|
||||||
AP.SampleRate = CTX->sample_rate;
|
AP.SampleRate = CTX->sample_rate;
|
||||||
AP.NumSamples = (Frames.back()).SampleStart;
|
AP.NumSamples = (Frames.back()).SampleStart + (Frames.back()).SampleCount;
|
||||||
AP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
AP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||||
AP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
AP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
|
||||||
}
|
}
|
||||||
|
@ -198,35 +242,37 @@ void vtCopy(VARIANT& vt,void *dest) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// used for matroska<->ffmpeg codec ID mapping to avoid Win32 dependency
|
|
||||||
typedef struct BITMAPINFOHEADER {
|
|
||||||
uint32_t biSize;
|
|
||||||
int32_t biWidth;
|
|
||||||
int32_t biHeight;
|
|
||||||
uint16_t biPlanes;
|
|
||||||
uint16_t biBitCount;
|
|
||||||
uint32_t biCompression;
|
|
||||||
uint32_t biSizeImage;
|
|
||||||
int32_t biXPelsPerMeter;
|
|
||||||
int32_t biYPelsPerMeter;
|
|
||||||
uint32_t biClrUsed;
|
|
||||||
uint32_t biClrImportant;
|
|
||||||
} BITMAPINFOHEADER;
|
|
||||||
|
|
||||||
#define MAKEFOURCC(ch0, ch1, ch2, ch3)\
|
|
||||||
((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) |\
|
|
||||||
((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 ))
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC) {
|
CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC, unsigned int BitsPerSample) {
|
||||||
/* Look up native codecs */
|
/* Look up native codecs */
|
||||||
for(int i = 0; ff_mkv_codec_tags[i].id != CODEC_ID_NONE; i++){
|
for(int i = 0; ff_mkv_codec_tags[i].id != CODEC_ID_NONE; i++){
|
||||||
if(!strncmp(ff_mkv_codec_tags[i].str, Codec,
|
if(!strncmp(ff_mkv_codec_tags[i].str, Codec,
|
||||||
strlen(ff_mkv_codec_tags[i].str))){
|
strlen(ff_mkv_codec_tags[i].str))) {
|
||||||
return ff_mkv_codec_tags[i].id;
|
|
||||||
|
// Uncompressed and exotic format fixup
|
||||||
|
// This list is incomplete
|
||||||
|
CodecID CID = ff_mkv_codec_tags[i].id;
|
||||||
|
switch (CID) {
|
||||||
|
case CODEC_ID_PCM_S16LE:
|
||||||
|
switch (BitsPerSample) {
|
||||||
|
case 8: CID = CODEC_ID_PCM_S8; break;
|
||||||
|
case 16: CID = CODEC_ID_PCM_S16LE; break;
|
||||||
|
case 24: CID = CODEC_ID_PCM_S24LE; break;
|
||||||
|
case 32: CID = CODEC_ID_PCM_S32LE; break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case CODEC_ID_PCM_S16BE:
|
||||||
|
switch (BitsPerSample) {
|
||||||
|
case 8: CID = CODEC_ID_PCM_S8; break;
|
||||||
|
case 16: CID = CODEC_ID_PCM_S16BE; break;
|
||||||
|
case 24: CID = CODEC_ID_PCM_S24BE; break;
|
||||||
|
case 32: CID = CODEC_ID_PCM_S32BE; break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +280,7 @@ CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC
|
||||||
const AVCodecTag *const tags[] = { ff_codec_bmp_tags, 0 };
|
const AVCodecTag *const tags[] = { ff_codec_bmp_tags, 0 };
|
||||||
|
|
||||||
if (!strcmp(Codec, "V_MS/VFW/FOURCC")) {
|
if (!strcmp(Codec, "V_MS/VFW/FOURCC")) {
|
||||||
BITMAPINFOHEADER *b = reinterpret_cast<BITMAPINFOHEADER *>(CodecPrivate);
|
FFMS_BITMAPINFOHEADER *b = reinterpret_cast<FFMS_BITMAPINFOHEADER *>(CodecPrivate);
|
||||||
return av_codec_get_id(tags, b->biCompression);
|
return av_codec_get_id(tags, b->biCompression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,3 +408,60 @@ void ffms_fstream::open(const char *filename, std::ios_base::openmode mode) {
|
||||||
ffms_fstream::ffms_fstream(const char *filename, std::ios_base::openmode mode) {
|
ffms_fstream::ffms_fstream(const char *filename, std::ios_base::openmode mode) {
|
||||||
open(filename, mode);
|
open(filename, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAALISOURCE
|
||||||
|
|
||||||
|
CComPtr<IMMContainer> HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode) {
|
||||||
|
CComPtr<IMMContainer> pMMC;
|
||||||
|
|
||||||
|
CLSID clsid = HAALI_MPEG_PARSER;
|
||||||
|
if (SourceMode == FFMS_SOURCE_HAALIOGG)
|
||||||
|
clsid = HAALI_OGG_PARSER;
|
||||||
|
|
||||||
|
if (FAILED(pMMC.CoCreateInstance(clsid)))
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
|
||||||
|
"Can't create parser");
|
||||||
|
|
||||||
|
CComPtr<IMemAlloc> pMA;
|
||||||
|
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc)))
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
|
||||||
|
"Can't create memory allocator");
|
||||||
|
|
||||||
|
CComPtr<IMMStream> pMS;
|
||||||
|
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile)))
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED,
|
||||||
|
"Can't create disk file reader");
|
||||||
|
|
||||||
|
WCHAR WSourceFile[2048];
|
||||||
|
ffms_mbstowcs(WSourceFile, SourceFile, 2000);
|
||||||
|
CComQIPtr<IMMStreamOpen> pMSO(pMS);
|
||||||
|
if (FAILED(pMSO->Open(WSourceFile)))
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
|
"Can't open file");
|
||||||
|
|
||||||
|
if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
|
||||||
|
if (SourceMode == FFMS_SOURCE_HAALIMPEG)
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Can't parse file, most likely a transport stream not cut at packet boundaries");
|
||||||
|
else
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Can't parse file");
|
||||||
|
}
|
||||||
|
|
||||||
|
return pMMC;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext) {
|
||||||
|
if (av_open_input_file(&FormatContext, SourceFile, NULL, 0, NULL) != 0)
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
|
boost::format("Couldn't open '%1'") % SourceFile);
|
||||||
|
|
||||||
|
if (av_find_stream_info(FormatContext) < 0) {
|
||||||
|
av_close_input_file(FormatContext);
|
||||||
|
FormatContext = NULL;
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ,
|
||||||
|
"Couldn't find stream information");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,7 +23,8 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <stdio.h>
|
#include <cstdio>
|
||||||
|
#include <boost/format.hpp>
|
||||||
#include "ffms.h"
|
#include "ffms.h"
|
||||||
#include "matroskaparser.h"
|
#include "matroskaparser.h"
|
||||||
|
|
||||||
|
@ -50,17 +51,73 @@ extern "C" {
|
||||||
# include "guids.h"
|
# include "guids.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define FFMS_GET_VECTOR_PTR(v) (((v).size() ? &(v)[0] : NULL))
|
#define FFMS_GET_VECTOR_PTR(v) (((v).size() ? &(v)[0] : NULL))
|
||||||
|
|
||||||
const int64_t ffms_av_nopts_value = static_cast<int64_t>(1) << 63;
|
const int64_t ffms_av_nopts_value = static_cast<int64_t>(1) << 63;
|
||||||
|
|
||||||
|
// used for matroska<->ffmpeg codec ID mapping to avoid Win32 dependency
|
||||||
|
typedef struct FFMS_BITMAPINFOHEADER {
|
||||||
|
uint32_t biSize;
|
||||||
|
int32_t biWidth;
|
||||||
|
int32_t biHeight;
|
||||||
|
uint16_t biPlanes;
|
||||||
|
uint16_t biBitCount;
|
||||||
|
uint32_t biCompression;
|
||||||
|
uint32_t biSizeImage;
|
||||||
|
int32_t biXPelsPerMeter;
|
||||||
|
int32_t biYPelsPerMeter;
|
||||||
|
uint32_t biClrUsed;
|
||||||
|
uint32_t biClrImportant;
|
||||||
|
} FFMS_BITMAPINFOHEADER;
|
||||||
|
|
||||||
|
class FFMS_Exception : public std::exception {
|
||||||
|
private:
|
||||||
|
std::string _Message;
|
||||||
|
int _ErrorType;
|
||||||
|
int _SubType;
|
||||||
|
public:
|
||||||
|
FFMS_Exception(int ErrorType, int SubType, const char *Message = "");
|
||||||
|
FFMS_Exception(int ErrorType, int SubType, const std::string &Message);
|
||||||
|
FFMS_Exception(int ErrorType, int SubType, const boost::format &Message);
|
||||||
|
~FFMS_Exception() throw ();
|
||||||
|
const std::string &GetErrorMessage() const;
|
||||||
|
int CopyOut(FFMS_ErrorInfo *ErrorInfo) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
class FFSourceResources {
|
||||||
|
private:
|
||||||
|
T *_PrivClass;
|
||||||
|
bool _Enabled;
|
||||||
|
bool _Arg;
|
||||||
|
public:
|
||||||
|
FFSourceResources(T *Target) : _PrivClass(Target), _Enabled(true), _Arg(false) {
|
||||||
|
}
|
||||||
|
|
||||||
|
~FFSourceResources() {
|
||||||
|
if (_Enabled)
|
||||||
|
_PrivClass->Free(_Arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetEnabled(bool Enabled) {
|
||||||
|
_Enabled = Enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetArg(bool Arg) {
|
||||||
|
_Arg = Arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CloseCodec(bool Arg) {
|
||||||
|
_Arg = Arg;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct MatroskaReaderContext {
|
struct MatroskaReaderContext {
|
||||||
public:
|
public:
|
||||||
StdIoStream ST;
|
StdIoStream ST;
|
||||||
uint8_t *Buffer;
|
uint8_t *Buffer;
|
||||||
unsigned int BufferSize;
|
unsigned int BufferSize;
|
||||||
|
char CSBuffer[4096];
|
||||||
|
|
||||||
MatroskaReaderContext() {
|
MatroskaReaderContext() {
|
||||||
InitStdIoStream(&ST);
|
InitStdIoStream(&ST);
|
||||||
|
@ -81,19 +138,25 @@ public:
|
||||||
|
|
||||||
int GetSWSCPUFlags();
|
int GetSWSCPUFlags();
|
||||||
int GetPPCPUFlags();
|
int GetPPCPUFlags();
|
||||||
|
void ClearErrorInfo(FFMS_ErrorInfo *ErrorInfo);
|
||||||
FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT);
|
FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT);
|
||||||
int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize);
|
void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context);
|
||||||
bool AudioFMTIsFloat(SampleFormat FMT);
|
bool AudioFMTIsFloat(SampleFormat FMT);
|
||||||
void InitNullPacket(AVPacket *pkt);
|
void InitNullPacket(AVPacket &pkt);
|
||||||
void FillAP(FFAudioProperties &AP, AVCodecContext *CTX, FFTrack &Frames);
|
bool IsNVOP(AVPacket &pkt);
|
||||||
|
void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames);
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
unsigned vtSize(VARIANT &vt);
|
unsigned vtSize(VARIANT &vt);
|
||||||
void vtCopy(VARIANT& vt,void *dest);
|
void vtCopy(VARIANT& vt,void *dest);
|
||||||
void InitializeCodecContextFromHaaliInfo(CComQIPtr<IPropertyBag> pBag, AVCodecContext *CodecContext);
|
void InitializeCodecContextFromHaaliInfo(CComQIPtr<IPropertyBag> pBag, AVCodecContext *CodecContext);
|
||||||
#endif
|
#endif
|
||||||
void InitializeCodecContextFromMatroskaTrackInfo(TrackInfo *TI, AVCodecContext *CodecContext);
|
void InitializeCodecContextFromMatroskaTrackInfo(TrackInfo *TI, AVCodecContext *CodecContext);
|
||||||
CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC = 0);
|
CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC = 0, unsigned int BitsPerSample = 0);
|
||||||
FILE *ffms_fopen(const char *filename, const char *mode);
|
FILE *ffms_fopen(const char *filename, const char *mode);
|
||||||
size_t ffms_mbstowcs (wchar_t *wcstr, const char *mbstr, size_t max);
|
size_t ffms_mbstowcs (wchar_t *wcstr, const char *mbstr, size_t max);
|
||||||
|
#ifdef HAALISOURCE
|
||||||
|
CComPtr<IMMContainer> HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode);
|
||||||
|
#endif
|
||||||
|
void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -18,24 +18,52 @@
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
// THE SOFTWARE.
|
// THE SOFTWARE.
|
||||||
|
|
||||||
#include "ffvideosource.h"
|
#include "videosource.h"
|
||||||
|
|
||||||
int FFVideo::InitPP(const char *PP, char *ErrorMsg, unsigned MsgSize) {
|
void FFMS_VideoSource::GetFrameCheck(int n) {
|
||||||
if (PP == NULL || !strcmp(PP, ""))
|
if (n < 0 || n >= VP.NumFrames)
|
||||||
return 0;
|
throw FFMS_Exception(FFMS_ERROR_DECODING, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Out of bounds frame requested");
|
||||||
PPMode = pp_get_mode_by_name_and_quality(PP, PP_QUALITY_MAX);
|
|
||||||
if (!PPMode) {
|
|
||||||
snprintf(ErrorMsg, MsgSize, "Invalid postprocesing settings");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFVideo::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height, char *ErrorMsg, unsigned MsgSize) {
|
void FFMS_VideoSource::SetPP(const char *PP) {
|
||||||
|
if (PPMode)
|
||||||
|
pp_free_mode(PPMode);
|
||||||
|
PPMode = NULL;
|
||||||
|
|
||||||
|
if (PP != NULL && strcmp(PP, "")) {
|
||||||
|
PPMode = pp_get_mode_by_name_and_quality(PP, PP_QUALITY_MAX);
|
||||||
|
if (!PPMode) {
|
||||||
|
ResetPP();
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Invalid postprocesing settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height);
|
||||||
|
OutputFrame(DecodeFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFMS_VideoSource::ResetPP() {
|
||||||
|
if (PPContext)
|
||||||
|
pp_free_context(PPContext);
|
||||||
|
PPContext = NULL;
|
||||||
|
|
||||||
|
if (PPMode)
|
||||||
|
pp_free_mode(PPMode);
|
||||||
|
PPMode = NULL;
|
||||||
|
|
||||||
|
OutputFrame(DecodeFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FFMS_VideoSource::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height) {
|
||||||
|
if (PPContext)
|
||||||
|
pp_free_context(PPContext);
|
||||||
|
PPContext = NULL;
|
||||||
|
|
||||||
if (!PPMode)
|
if (!PPMode)
|
||||||
return 0;
|
return;
|
||||||
|
|
||||||
int Flags = GetPPCPUFlags();
|
int Flags = GetPPCPUFlags();
|
||||||
|
|
||||||
|
@ -45,34 +73,30 @@ int FFVideo::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height, char *E
|
||||||
case PIX_FMT_YUV411P: Flags |= PP_FORMAT_411; break;
|
case PIX_FMT_YUV411P: Flags |= PP_FORMAT_411; break;
|
||||||
case PIX_FMT_YUV444P: Flags |= PP_FORMAT_444; break;
|
case PIX_FMT_YUV444P: Flags |= PP_FORMAT_444; break;
|
||||||
default:
|
default:
|
||||||
snprintf(ErrorMsg, MsgSize, "Input format is not supported for postprocessing");
|
ResetPP();
|
||||||
return 1;
|
throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_UNSUPPORTED,
|
||||||
|
"The video does not have a colorspace suitable for postprocessing");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PPContext)
|
|
||||||
pp_free_context(PPContext);
|
|
||||||
PPContext = pp_get_context(Width, Height, Flags);
|
PPContext = pp_get_context(Width, Height, Flags);
|
||||||
|
|
||||||
avpicture_free(&PPFrame);
|
avpicture_free(&PPFrame);
|
||||||
avpicture_alloc(&PPFrame, VPixelFormat, Width, Height);
|
avpicture_alloc(&PPFrame, VPixelFormat, Width, Height);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CopyAVPictureFields(AVPicture &Picture, FFAVFrame &Dst) {
|
static void CopyAVPictureFields(AVPicture &Picture, FFMS_Frame &Dst) {
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
Dst.Data[i] = Picture.data[i];
|
Dst.Data[i] = Picture.data[i];
|
||||||
Dst.Linesize[i] = Picture.linesize[i];
|
Dst.Linesize[i] = Picture.linesize[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FFAVFrame *FFVideo::OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_Frame *FFMS_VideoSource::OutputFrame(AVFrame *Frame) {
|
||||||
if (LastFrameWidth != CodecContext->width || LastFrameHeight != CodecContext->height || LastFramePixelFormat != CodecContext->pix_fmt) {
|
if (LastFrameWidth != CodecContext->width || LastFrameHeight != CodecContext->height || LastFramePixelFormat != CodecContext->pix_fmt) {
|
||||||
if (ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height, ErrorMsg, MsgSize))
|
ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height);
|
||||||
return NULL;
|
|
||||||
if (TargetHeight > 0 && TargetWidth > 0 && TargetPixelFormats != 0)
|
if (TargetHeight > 0 && TargetWidth > 0 && TargetPixelFormats != 0)
|
||||||
if (ReAdjustOutputFormat(TargetPixelFormats, TargetWidth, TargetHeight, TargetResizer, ErrorMsg, MsgSize))
|
ReAdjustOutputFormat(TargetPixelFormats, TargetWidth, TargetHeight, TargetResizer);
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PPMode) {
|
if (PPMode) {
|
||||||
|
@ -99,9 +123,9 @@ FFAVFrame *FFVideo::OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize
|
||||||
LocalFrame.EncodedWidth = CodecContext->width;
|
LocalFrame.EncodedWidth = CodecContext->width;
|
||||||
LocalFrame.EncodedHeight = CodecContext->height;
|
LocalFrame.EncodedHeight = CodecContext->height;
|
||||||
LocalFrame.EncodedPixelFormat = CodecContext->pix_fmt;
|
LocalFrame.EncodedPixelFormat = CodecContext->pix_fmt;
|
||||||
LocalFrame.ScaledWidth = VP.Width;
|
LocalFrame.ScaledWidth = TargetWidth;
|
||||||
LocalFrame.ScaledHeight = VP.Height;
|
LocalFrame.ScaledHeight = TargetHeight;
|
||||||
LocalFrame.ConvertedPixelFormat = VP.VPixelFormat;
|
LocalFrame.ConvertedPixelFormat = OutputFormat;
|
||||||
LocalFrame.KeyFrame = Frame->key_frame;
|
LocalFrame.KeyFrame = Frame->key_frame;
|
||||||
LocalFrame.PictType = av_get_pict_type_char(Frame->pict_type);
|
LocalFrame.PictType = av_get_pict_type_char(Frame->pict_type);
|
||||||
LocalFrame.RepeatPict = Frame->repeat_pict;
|
LocalFrame.RepeatPict = Frame->repeat_pict;
|
||||||
|
@ -115,9 +139,22 @@ FFAVFrame *FFVideo::OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize
|
||||||
return &LocalFrame;
|
return &LocalFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
FFVideo::FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track) {
|
||||||
if (Index->CompareFileSignature(SourceFile, ErrorMsg, MsgSize))
|
if (Track < 0 || Track >= static_cast<int>(Index->size()))
|
||||||
throw ErrorMsg;
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Out of bounds track index selected");
|
||||||
|
|
||||||
|
if (Index->at(Track).TT != FFMS_TYPE_VIDEO)
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Not a video track");
|
||||||
|
|
||||||
|
if (Index[Track].size() == 0)
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
|
"Video track contains no frames");
|
||||||
|
|
||||||
|
if (!Index->CompareFileSignature(SourceFile))
|
||||||
|
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_FILE_MISMATCH,
|
||||||
|
"The index does not match the source file");
|
||||||
|
|
||||||
memset(&VP, 0, sizeof(VP));
|
memset(&VP, 0, sizeof(VP));
|
||||||
PPContext = NULL;
|
PPContext = NULL;
|
||||||
|
@ -133,6 +170,7 @@ FFVideo::FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigne
|
||||||
TargetWidth = -1;
|
TargetWidth = -1;
|
||||||
TargetPixelFormats = 0;
|
TargetPixelFormats = 0;
|
||||||
TargetResizer = 0;
|
TargetResizer = 0;
|
||||||
|
OutputFormat = PIX_FMT_NONE;
|
||||||
DecodeFrame = avcodec_alloc_frame();
|
DecodeFrame = avcodec_alloc_frame();
|
||||||
|
|
||||||
// Dummy allocations so the unallocated case doesn't have to be handled later
|
// Dummy allocations so the unallocated case doesn't have to be handled later
|
||||||
|
@ -140,7 +178,7 @@ FFVideo::FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigne
|
||||||
avpicture_alloc(&SWSFrame, PIX_FMT_GRAY8, 16, 16);
|
avpicture_alloc(&SWSFrame, PIX_FMT_GRAY8, 16, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFVideo::~FFVideo() {
|
FFMS_VideoSource::~FFMS_VideoSource() {
|
||||||
if (PPMode)
|
if (PPMode)
|
||||||
pp_free_mode(PPMode);
|
pp_free_mode(PPMode);
|
||||||
|
|
||||||
|
@ -155,32 +193,33 @@ FFVideo::~FFVideo() {
|
||||||
av_freep(&DecodeFrame);
|
av_freep(&DecodeFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
FFAVFrame *FFVideo::GetFrameByTime(double Time, char *ErrorMsg, unsigned MsgSize) {
|
FFMS_Frame *FFMS_VideoSource::GetFrameByTime(double Time) {
|
||||||
int Frame = Frames.ClosestFrameFromDTS(static_cast<int64_t>((Time * 1000 * Frames.TB.Den) / Frames.TB.Num));
|
int Frame = Frames.ClosestFrameFromDTS(static_cast<int64_t>((Time * 1000 * Frames.TB.Den) / Frames.TB.Num));
|
||||||
return GetFrame(Frame, ErrorMsg, MsgSize);
|
return GetFrame(Frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFVideo::SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize) {
|
void FFMS_VideoSource::SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer) {
|
||||||
this->TargetWidth = Width;
|
this->TargetWidth = Width;
|
||||||
this->TargetHeight = Height;
|
this->TargetHeight = Height;
|
||||||
this->TargetPixelFormats = TargetFormats;
|
this->TargetPixelFormats = TargetFormats;
|
||||||
this->TargetResizer = Resizer;
|
this->TargetResizer = Resizer;
|
||||||
return ReAdjustOutputFormat(TargetFormats, Width, Height, Resizer, ErrorMsg, MsgSize);
|
ReAdjustOutputFormat(TargetFormats, Width, Height, Resizer);
|
||||||
|
OutputFrame(DecodeFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
int FFVideo::ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize) {
|
void FFMS_VideoSource::ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer) {
|
||||||
if (SWS) {
|
if (SWS) {
|
||||||
sws_freeContext(SWS);
|
sws_freeContext(SWS);
|
||||||
SWS = NULL;
|
SWS = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Loss;
|
int Loss;
|
||||||
PixelFormat OutputFormat = avcodec_find_best_pix_fmt(TargetFormats,
|
OutputFormat = avcodec_find_best_pix_fmt(TargetFormats,
|
||||||
CodecContext->pix_fmt, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss);
|
CodecContext->pix_fmt, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss);
|
||||||
if (OutputFormat == PIX_FMT_NONE) {
|
if (OutputFormat == PIX_FMT_NONE) {
|
||||||
ResetOutputFormat();
|
ResetOutputFormat();
|
||||||
snprintf(ErrorMsg, MsgSize, "No suitable output format found");
|
throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
return 1;
|
"No suitable output format found");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (CodecContext->pix_fmt != OutputFormat || Width != CodecContext->width || Height != CodecContext->height) {
|
if (CodecContext->pix_fmt != OutputFormat || Width != CodecContext->width || Height != CodecContext->height) {
|
||||||
|
@ -188,22 +227,16 @@ int FFVideo::ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height,
|
||||||
OutputFormat, GetSWSCPUFlags() | Resizer, NULL, NULL, NULL);
|
OutputFormat, GetSWSCPUFlags() | Resizer, NULL, NULL, NULL);
|
||||||
if (SWS == NULL) {
|
if (SWS == NULL) {
|
||||||
ResetOutputFormat();
|
ResetOutputFormat();
|
||||||
snprintf(ErrorMsg, MsgSize, "Failed to allocate SWScale context");
|
throw FFMS_Exception(FFMS_ERROR_SCALING, FFMS_ERROR_INVALID_ARGUMENT,
|
||||||
return 1;
|
"Failed to allocate SWScale context");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VP.VPixelFormat = OutputFormat;
|
|
||||||
VP.Height = Height;
|
|
||||||
VP.Width = Width;
|
|
||||||
|
|
||||||
avpicture_free(&SWSFrame);
|
avpicture_free(&SWSFrame);
|
||||||
avpicture_alloc(&SWSFrame, OutputFormat, Width, Height);
|
avpicture_alloc(&SWSFrame, OutputFormat, Width, Height);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FFVideo::ResetOutputFormat() {
|
void FFMS_VideoSource::ResetOutputFormat() {
|
||||||
if (SWS) {
|
if (SWS) {
|
||||||
sws_freeContext(SWS);
|
sws_freeContext(SWS);
|
||||||
SWS = NULL;
|
SWS = NULL;
|
||||||
|
@ -212,8 +245,7 @@ void FFVideo::ResetOutputFormat() {
|
||||||
TargetWidth = -1;
|
TargetWidth = -1;
|
||||||
TargetHeight = -1;
|
TargetHeight = -1;
|
||||||
TargetPixelFormats = 0;
|
TargetPixelFormats = 0;
|
||||||
|
OutputFormat = PIX_FMT_NONE;
|
||||||
VP.Height = CodecContext->height;
|
|
||||||
VP.Width = CodecContext->width;
|
OutputFrame(DecodeFrame);
|
||||||
VP.VPixelFormat = CodecContext->pix_fmt;
|
|
||||||
}
|
}
|
|
@ -45,7 +45,8 @@ extern "C" {
|
||||||
# include "guids.h"
|
# include "guids.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class FFVideo {
|
class FFMS_VideoSource {
|
||||||
|
friend class FFSourceResources<FFMS_VideoSource>;
|
||||||
private:
|
private:
|
||||||
pp_context_t *PPContext;
|
pp_context_t *PPContext;
|
||||||
pp_mode_t *PPMode;
|
pp_mode_t *PPMode;
|
||||||
|
@ -57,75 +58,83 @@ private:
|
||||||
int TargetWidth;
|
int TargetWidth;
|
||||||
int64_t TargetPixelFormats;
|
int64_t TargetPixelFormats;
|
||||||
int TargetResizer;
|
int TargetResizer;
|
||||||
|
PixelFormat OutputFormat;
|
||||||
AVPicture PPFrame;
|
AVPicture PPFrame;
|
||||||
AVPicture SWSFrame;
|
AVPicture SWSFrame;
|
||||||
protected:
|
protected:
|
||||||
FFVideoProperties VP;
|
FFMS_VideoProperties VP;
|
||||||
FFAVFrame LocalFrame;
|
FFMS_Frame LocalFrame;
|
||||||
AVFrame *DecodeFrame;
|
AVFrame *DecodeFrame;
|
||||||
int LastFrameNum;
|
int LastFrameNum;
|
||||||
FFTrack Frames;
|
FFMS_Track Frames;
|
||||||
int VideoTrack;
|
int VideoTrack;
|
||||||
int CurrentFrame;
|
int CurrentFrame;
|
||||||
AVCodecContext *CodecContext;
|
AVCodecContext *CodecContext;
|
||||||
|
|
||||||
FFVideo(const char *SourceFile, FFIndex *Index, char *ErrorMsg, unsigned MsgSize);
|
FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track);
|
||||||
int InitPP(const char *PP, char *ErrorMsg, unsigned MsgSize);
|
void ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height);
|
||||||
int ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height, char *ErrorMsg, unsigned MsgSize);
|
void ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer);
|
||||||
FFAVFrame *OutputFrame(AVFrame *Frame, char *ErrorMsg, unsigned MsgSize);
|
FFMS_Frame *OutputFrame(AVFrame *Frame);
|
||||||
|
virtual void Free(bool CloseCodec) = 0;
|
||||||
public:
|
public:
|
||||||
virtual ~FFVideo();
|
virtual ~FFMS_VideoSource();
|
||||||
const FFVideoProperties& GetFFVideoProperties() { return VP; }
|
const FFMS_VideoProperties& GetVideoProperties() { return VP; }
|
||||||
FFTrack *GetFFTrack() { return &Frames; }
|
FFMS_Track *GetTrack() { return &Frames; }
|
||||||
virtual FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize) = 0;
|
virtual FFMS_Frame *GetFrame(int n) = 0;
|
||||||
FFAVFrame *GetFrameByTime(double Time, char *ErrorMsg, unsigned MsgSize);
|
void GetFrameCheck(int n);
|
||||||
int SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize);
|
FFMS_Frame *GetFrameByTime(double Time);
|
||||||
int ReAdjustOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer, char *ErrorMsg, unsigned MsgSize);
|
void SetPP(const char *PP);
|
||||||
|
void ResetPP();
|
||||||
|
void SetOutputFormat(int64_t TargetFormats, int Width, int Height, int Resizer);
|
||||||
void ResetOutputFormat();
|
void ResetOutputFormat();
|
||||||
};
|
};
|
||||||
|
|
||||||
class FFLAVFVideo : public FFVideo {
|
class FFLAVFVideo : public FFMS_VideoSource {
|
||||||
private:
|
private:
|
||||||
AVFormatContext *FormatContext;
|
AVFormatContext *FormatContext;
|
||||||
int SeekMode;
|
int SeekMode;
|
||||||
|
FFSourceResources<FFMS_VideoSource> Res;
|
||||||
|
|
||||||
|
void DecodeNextFrame(int64_t *DTS);
|
||||||
|
protected:
|
||||||
void Free(bool CloseCodec);
|
void Free(bool CloseCodec);
|
||||||
int DecodeNextFrame(int64_t *DTS, char *ErrorMsg, unsigned MsgSize);
|
|
||||||
public:
|
public:
|
||||||
FFLAVFVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize);
|
FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode);
|
||||||
~FFLAVFVideo();
|
FFMS_Frame *GetFrame(int n);
|
||||||
FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class FFMatroskaVideo : public FFVideo {
|
class FFMatroskaVideo : public FFMS_VideoSource {
|
||||||
private:
|
private:
|
||||||
MatroskaFile *MF;
|
MatroskaFile *MF;
|
||||||
MatroskaReaderContext MC;
|
MatroskaReaderContext MC;
|
||||||
CompressedStream *CS;
|
CompressedStream *CS;
|
||||||
char ErrorMessage[256];
|
char ErrorMessage[256];
|
||||||
|
FFSourceResources<FFMS_VideoSource> Res;
|
||||||
|
size_t PacketNumber;
|
||||||
|
|
||||||
|
void DecodeNextFrame();
|
||||||
|
protected:
|
||||||
void Free(bool CloseCodec);
|
void Free(bool CloseCodec);
|
||||||
int DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
|
|
||||||
public:
|
public:
|
||||||
FFMatroskaVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, char *ErrorMsg, unsigned MsgSize);
|
FFMatroskaVideo(const char *SourceFile, int Track, FFMS_Index *Index, int Threads);
|
||||||
~FFMatroskaVideo();
|
FFMS_Frame *GetFrame(int n);
|
||||||
FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef HAALISOURCE
|
#ifdef HAALISOURCE
|
||||||
|
|
||||||
class FFHaaliVideo : public FFVideo {
|
class FFHaaliVideo : public FFMS_VideoSource {
|
||||||
private:
|
private:
|
||||||
CComPtr<IMMContainer> pMMC;
|
CComPtr<IMMContainer> pMMC;
|
||||||
std::vector<uint8_t> CodecPrivate;
|
std::vector<uint8_t> CodecPrivate;
|
||||||
AVBitStreamFilterContext *BitStreamFilter;
|
AVBitStreamFilterContext *BitStreamFilter;
|
||||||
|
FFSourceResources<FFMS_VideoSource> Res;
|
||||||
|
|
||||||
|
void DecodeNextFrame(int64_t *AFirstStartTime);
|
||||||
|
protected:
|
||||||
void Free(bool CloseCodec);
|
void Free(bool CloseCodec);
|
||||||
int DecodeNextFrame(int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
|
|
||||||
public:
|
public:
|
||||||
FFHaaliVideo(const char *SourceFile, int Track, FFIndex *Index, const char *PP, int Threads, int SourceMode, char *ErrorMsg, unsigned MsgSize);
|
FFHaaliVideo(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, enum FFMS_Sources SourceMode);
|
||||||
~FFHaaliVideo();
|
FFMS_Frame *GetFrame(int n);
|
||||||
FFAVFrame *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HAALISOURCE
|
#endif // HAALISOURCE
|
Loading…
Reference in a new issue