FFmpegSource 1.10

Originally committed to SVN as r1548.
This commit is contained in:
Fredrik Mellbin 2007-08-31 20:26:51 +00:00
parent 89d076c760
commit 36aecac4c3
9 changed files with 1170 additions and 862 deletions

View File

@ -1,19 +1,5 @@
#include "ffmpegsource.h"
int GetSWSCPUFlags(IScriptEnvironment *Env) {
int Flags = 0;
long CPUFlags = Env->GetCPUFlags();
if (CPUFlags & CPUF_MMX)
CPUFlags |= SWS_CPU_CAPS_MMX;
if (CPUFlags & CPUF_INTEGER_SSE)
CPUFlags |= SWS_CPU_CAPS_MMX2;
if (CPUFlags & CPUF_3DNOW)
CPUFlags |= SWS_CPU_CAPS_3DNOW;
return Flags;
}
int FFBase::FrameFromDTS(int64_t ADTS) {
for (int i = 0; i < (int)FrameToDTS.size(); i++)
if (FrameToDTS[i].DTS == ADTS)
@ -101,37 +87,203 @@ bool FFBase::SaveTimecodesToFile(const char *ATimecodeFile, int64_t ScaleD, int6
return true;
}
bool FFBase::PrepareAudioCache(const char *AAudioCacheFile, const char *ASource, int AAudioTrack, IScriptEnvironment *Env) {
static FLAC__StreamDecoderReadStatus FLACStreamDecoderReadCallback(const FLAC__StreamDecoder *ADecoder, FLAC__byte ABuffer[], size_t *ABytes, FFBase *AOwner) {
if(*ABytes > 0) {
*ABytes = fread(ABuffer, sizeof(FLAC__byte), *ABytes, AOwner->FCFile);
if(ferror(AOwner->FCFile))
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
else if(*ABytes == 0)
return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
else
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
} else {
return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
}
}
static FLAC__StreamDecoderSeekStatus FLACStreamDecoderSeekCallback(const FLAC__StreamDecoder *ADecoder, FLAC__uint64 AAbsoluteByteOffset, FFBase *AOwner) {
if(_fseeki64(AOwner->FCFile, AAbsoluteByteOffset, SEEK_SET) < 0)
return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
else
return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
}
static FLAC__StreamDecoderTellStatus FLACStreamDecoderTellCallback(const FLAC__StreamDecoder *ADecoder, FLAC__uint64 *AAbsoluteByteOffset, FFBase *AOwner) {
__int64 Pos;
if ((Pos = _ftelli64(AOwner->FCFile)) < 0) {
return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
} else {
*AAbsoluteByteOffset = (FLAC__uint64)Pos;
return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}
}
static FLAC__StreamDecoderLengthStatus FLACStreamDecoderLengthCallback(const FLAC__StreamDecoder *ADecoder, FLAC__uint64 *AStreamLength, FFBase *AOwner) {
__int64 OriginalPos;
__int64 Length;
if ((OriginalPos = _ftelli64(AOwner->FCFile)) < 0)
return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
_fseeki64(AOwner->FCFile, 0, SEEK_END);
if ((Length = _ftelli64(AOwner->FCFile)) < 0)
return FLAC__STREAM_DECODER_LENGTH_STATUS_ERROR;
_fseeki64(AOwner->FCFile, OriginalPos, SEEK_SET);
*AStreamLength = Length;
return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
}
static FLAC__bool FLACStreamDecoderEofCallback(const FLAC__StreamDecoder *ADecoder, FFBase *AOwner) {
return feof(AOwner->FCFile) ? true : false;
}
static FLAC__StreamDecoderWriteStatus FLACStreamDecoderWriteCallback(const FLAC__StreamDecoder *ADecoder, const FLAC__Frame *AFrame, const FLAC__int32 *const ABuffer[], FFBase *AOwner) {
unsigned Blocksize = AFrame->header.blocksize;
const VideoInfo VI = AOwner->GetVideoInfo();
int16_t *Buffer = (int16_t *)AOwner->FCBuffer;
int j = 0;
while (AOwner->FCCount > 0 && Blocksize > 0) {
for (int i = 0; i < VI.nchannels; i++)
*Buffer++ = ABuffer[i][j];
j++;
AOwner->FCCount--;
Blocksize--;
}
AOwner->FCBuffer = Buffer;
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
static void FLACStreamDecoderMetadataCallback(const FLAC__StreamDecoder *ADecoder, const FLAC__StreamMetadata *AMetadata, FFBase *AOwner) {
AOwner->FCError = (AMetadata->data.stream_info.total_samples <= 0);
}
static void FLACStreamDecoderErrorCallback(const FLAC__StreamDecoder *ADecoder, FLAC__StreamDecoderErrorStatus AStatus, FFBase *AOwner) {
AOwner->FCError = true;
}
bool FFBase::OpenAudioCache(const char *AAudioCacheFile, const char *ASource, int AAudioTrack, IScriptEnvironment *Env) {
char DefaultCacheFilename[1024];
sprintf(DefaultCacheFilename, "%s.ffa%dcache", ASource, AAudioTrack);
if (!strcmp(AAudioCacheFile, ""))
AAudioCacheFile = DefaultCacheFilename;
bool IsWritable = false;
AudioCache = fopen(AAudioCacheFile, "rb");
if (!AudioCache) {
AudioCache = fopen(AAudioCacheFile, "wb+");
if (!AudioCache)
Env->ThrowError("FFmpegSource: Failed to open the audio cache file for writing");
IsWritable = true;
// Is an empty file?
FCFile = fopen(AAudioCacheFile, "rb");
int64_t CacheSize;
if (FCFile) {
_fseeki64(FCFile, 0, SEEK_END);
CacheSize = _ftelli64(FCFile);
_fseeki64(FCFile, 0, SEEK_SET);
if (CacheSize <= 0) {
fclose(FCFile);
FCFile = NULL;
return false;
}
} else {
return false;
}
_fseeki64(AudioCache, 0, SEEK_END);
int64_t CacheSize = _ftelli64(AudioCache);
if (CacheSize > 0) {
// If FLAC?
FLACAudioCache = FLAC__stream_decoder_new();
if (FLAC__stream_decoder_init_stream(FLACAudioCache,
&(FLAC__StreamDecoderReadCallback)FLACStreamDecoderReadCallback,
&(FLAC__StreamDecoderSeekCallback)FLACStreamDecoderSeekCallback,
&(FLAC__StreamDecoderTellCallback)FLACStreamDecoderTellCallback,
&(FLAC__StreamDecoderLengthCallback)FLACStreamDecoderLengthCallback,
&(FLAC__StreamDecoderEofCallback)FLACStreamDecoderEofCallback,
&(FLAC__StreamDecoderWriteCallback)FLACStreamDecoderWriteCallback,
&(FLAC__StreamDecoderMetadataCallback)FLACStreamDecoderMetadataCallback, &(FLAC__StreamDecoderErrorCallback)FLACStreamDecoderErrorCallback, this) == FLAC__STREAM_DECODER_INIT_STATUS_OK) {
FCError = true;
FLAC__stream_decoder_process_until_end_of_metadata(FLACAudioCache);
if (!FCError) {
VI.num_audio_samples = FLAC__stream_decoder_get_total_samples(FLACAudioCache);
AudioCacheType = acFLAC;
return true;
}
}
FLAC__stream_decoder_delete(FLACAudioCache);
FLACAudioCache = NULL;
// Raw audio
VI.num_audio_samples = VI.AudioSamplesFromBytes(CacheSize);
AudioCacheType = acRaw;
RawAudioCache = FCFile;
FCFile = NULL;
return true;
}
if (!IsWritable) {
AudioCache = freopen(AAudioCacheFile, "wb+", AudioCache);
if (!AudioCache)
Env->ThrowError("FFmpegSource: Failed to open the audio cache file for writing");
static FLAC__StreamEncoderWriteStatus FLACStreamEncoderWriteCallback(const FLAC__StreamEncoder *ASncoder, const FLAC__byte ABuffer[], size_t ABytes, unsigned ABamples, unsigned ACurrentFrame, FFBase *AOwner) {
fwrite(ABuffer, sizeof(FLAC__byte), ABytes, AOwner->FCFile);
if(ferror(AOwner->FCFile))
return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
else
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
}
return false;
static FLAC__StreamEncoderSeekStatus FLACStreamEncoderSeekCallback(const FLAC__StreamEncoder *AEncoder, FLAC__uint64 AAbsoluteByteOffset, FFBase *AOwner) {
if(_fseeki64(AOwner->FCFile, AAbsoluteByteOffset, SEEK_SET) < 0)
return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR;
else
return FLAC__STREAM_ENCODER_SEEK_STATUS_OK;
}
static FLAC__StreamEncoderTellStatus FLACStreamEncoderTellCallback(const FLAC__StreamEncoder *AEncoder, FLAC__uint64 *AAbsoluteByteOffset, FFBase *AOwner) {
__int64 Pos;
if((Pos = _ftelli64(AOwner->FCFile)) < 0) {
return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR;
} else {
*AAbsoluteByteOffset = (FLAC__uint64)Pos;
return FLAC__STREAM_ENCODER_TELL_STATUS_OK;
}
}
FLAC__StreamEncoder *FFBase::NewFLACCacheWriter(const char *AAudioCacheFile, const char *ASource, int AAudioTrack, int ACompression, IScriptEnvironment *Env) {
char DefaultCacheFilename[1024];
sprintf(DefaultCacheFilename, "%s.ffa%dcache", ASource, AAudioTrack);
if (!strcmp(AAudioCacheFile, ""))
AAudioCacheFile = DefaultCacheFilename;
FCFile = fopen(AAudioCacheFile, "wb");
if (!FCFile)
Env->ThrowError("FFmpegSource: Failed to open '%s' for writing", AAudioCacheFile);
FLAC__StreamEncoder *FSE;
FSE = FLAC__stream_encoder_new();
FLAC__stream_encoder_set_channels(FSE, VI.nchannels);
FLAC__stream_encoder_set_bits_per_sample(FSE, VI.BytesPerChannelSample() * 8);
FLAC__stream_encoder_set_sample_rate(FSE, VI.audio_samples_per_second);
FLAC__stream_encoder_set_compression_level(FSE, ACompression);
if (FLAC__stream_encoder_init_stream(FSE, &(FLAC__StreamEncoderWriteCallback)FLACStreamEncoderWriteCallback,
&(FLAC__StreamEncoderSeekCallback)FLACStreamEncoderSeekCallback,
&(FLAC__StreamEncoderTellCallback)FLACStreamEncoderTellCallback, NULL, this)
!= FLAC__STREAM_ENCODER_INIT_STATUS_OK)
Env->ThrowError("FFmpegSource: Failed to initialize the FLAC encoder for '%s'", AAudioCacheFile);
return FSE;
}
void FFBase::CloseFLACCacheWriter(FLAC__StreamEncoder *AFSE) {
FLAC__stream_encoder_finish(AFSE);
FLAC__stream_encoder_delete(AFSE);
fclose(FCFile);
FCFile = NULL;
}
FILE *FFBase::NewRawCacheWriter(const char *AAudioCacheFile, const char *ASource, int AAudioTrack, IScriptEnvironment *Env) {
char DefaultCacheFilename[1024];
sprintf(DefaultCacheFilename, "%s.ffa%dcache", ASource, AAudioTrack);
if (!strcmp(AAudioCacheFile, ""))
AAudioCacheFile = DefaultCacheFilename;
FILE *RCF = fopen(AAudioCacheFile, "wb");
if (RCF == NULL)
Env->ThrowError("FFmpegSource: Failed to open '%s' for writing", AAudioCacheFile);
return RCF;
}
void FFBase::CloseRawCacheWriter(FILE *ARawCache) {
fclose(ARawCache);
}
void FFBase::InitPP(int AWidth, int AHeight, const char *APPString, int AQuality, int APixelFormat, IScriptEnvironment *Env) {
@ -164,9 +316,10 @@ void FFBase::InitPP(int AWidth, int AHeight, const char *APPString, int AQuality
void FFBase::SetOutputFormat(int ACurrentFormat, IScriptEnvironment *Env) {
int Loss;
int BestFormat = avcodec_find_best_pix_fmt((1 << PIX_FMT_YUV420P) | (1 << PIX_FMT_YUYV422) | (1 << PIX_FMT_RGB32) | (1 << PIX_FMT_BGR24), ACurrentFormat, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss);
int BestFormat = avcodec_find_best_pix_fmt((1 << PIX_FMT_YUVJ420P) | (1 << PIX_FMT_YUV420P) | (1 << PIX_FMT_YUYV422) | (1 << PIX_FMT_RGB32) | (1 << PIX_FMT_BGR24), ACurrentFormat, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss);
switch (BestFormat) {
case PIX_FMT_YUVJ420P: // stupid yv12 distinctions, also inexplicably completely undeniably incompatible with all other supported output formats
case PIX_FMT_YUV420P: VI.pixel_type = VideoInfo::CS_I420; break;
case PIX_FMT_YUYV422: VI.pixel_type = VideoInfo::CS_YUY2; break;
case PIX_FMT_RGB32: VI.pixel_type = VideoInfo::CS_BGR32; break;
@ -220,22 +373,47 @@ PVideoFrame FFBase::OutputFrame(AVFrame *AFrame, IScriptEnvironment *Env) {
}
void __stdcall FFBase::GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment* Env) {
_fseeki64(AudioCache, VI.BytesFromAudioSamples(Start), SEEK_SET);
fread(Buf, 1, VI.BytesFromAudioSamples(Count), AudioCache);
if (AudioCacheType == acRaw) {
_fseeki64(RawAudioCache, VI.BytesFromAudioSamples(Start), SEEK_SET);
fread(Buf, 1, VI.BytesFromAudioSamples(Count), RawAudioCache);
} else if (AudioCacheType == acFLAC) {
FCCount = Count;
FCBuffer = Buf;
FLAC__stream_decoder_seek_absolute(FLACAudioCache, Start);
while (FCCount > 0)
FLAC__stream_decoder_process_single(FLACAudioCache);
} else {
Env->ThrowError("FFmpegSource: Audio requested but none available");
}
}
FFBase::FFBase() {
memset(&VI, 0, sizeof(VI));
AudioCache = NULL;
AudioCacheType = acNone;
FCError = false;
RawAudioCache = NULL;
FLACAudioCache = NULL;
PPContext = NULL;
PPMode = NULL;
SWS = NULL;
DecodingBuffer = new uint8_t[AVCODEC_MAX_AUDIO_FRAME_SIZE];
FLACBuffer = new FLAC__int32[AVCODEC_MAX_AUDIO_FRAME_SIZE];
ConvertToFormat = PIX_FMT_NONE;
memset(&PPPicture, 0, sizeof(PPPicture));
DecodeFrame = avcodec_alloc_frame();
}
FFBase::~FFBase() {
delete [] DecodingBuffer;
delete [] FLACBuffer;
if (RawAudioCache)
fclose(RawAudioCache);
if (FLACAudioCache) {
FLAC__stream_decoder_finish(FLACAudioCache);
FLAC__stream_decoder_delete(FLACAudioCache);
}
if (FCFile)
fclose(FCFile);
if (SWS)
sws_freeContext(SWS);
if (PPMode)

View File

@ -0,0 +1,458 @@
#include "ffmpegsource.h"
#include "stdiostream.c"
class FFMatroskaSource : public FFBase {
private:
StdIoStream ST;
unsigned int BufferSize;
CompressedStream *VideoCS;
CompressedStream *AudioCS;
AVCodecContext *VideoCodecContext;
MatroskaFile *MF;
char ErrorMessage[256];
uint8_t *Buffer;
int CurrentFrame;
int ReadFrame(uint64_t AFilePos, unsigned int AFrameSize, CompressedStream *ACS, IScriptEnvironment *Env);
int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, IScriptEnvironment* Env);
int GetTrackIndex(int Index, unsigned char ATrackType, IScriptEnvironment *Env) {
if (Index == -1)
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++)
if (mkv_GetTrackInfo(MF, i)->Type == ATrackType) {
Index = i;
break;
}
if (Index == -1)
Env->ThrowError("FFmpegSource: No %s track found", (ATrackType & TT_VIDEO) ? "video" : "audio");
if (Index <= -2)
return -2;
if (Index >= (int)mkv_GetNumTracks(MF))
Env->ThrowError("FFmpegSource: Invalid %s track number", (ATrackType & TT_VIDEO) ? "video" : "audio");
TrackInfo *TI = mkv_GetTrackInfo(MF, Index);
if (TI->Type != ATrackType)
Env->ThrowError("FFmpegSource: Selected track is not %s", (ATrackType & TT_VIDEO) ? "video" : "audio");
return Index;
}
public:
FFMatroskaSource(const char *ASource, int AVideoTrack, int AAudioTrack, const char *ATimecodes, bool AVCache, const char *AVideoCache, const char *AAudioCache, int AACCompression, const char *APPString, int AQuality, IScriptEnvironment* Env) {
CurrentFrame = 0;
int VideoTrack;
int AudioTrack;
unsigned int TrackMask = ~0;
AVCodecContext *AudioCodecContext = NULL;
AVCodec *AudioCodec = NULL;
VideoCodecContext = NULL;
AVCodec *VideoCodec = NULL;
TrackInfo *VideoTI = NULL;
BufferSize = 0;
Buffer = NULL;
VideoCS = NULL;
AudioCS = NULL;
memset(&ST,0,sizeof(ST));
ST.base.read = (int (__cdecl *)(InputStream *,ulonglong,void *,int))StdIoRead;
ST.base.scan = (longlong (__cdecl *)(InputStream *,ulonglong,unsigned int))StdIoScan;
ST.base.getcachesize = (unsigned int (__cdecl *)(InputStream *))StdIoGetCacheSize;
ST.base.geterror = (const char *(__cdecl *)(InputStream *))StdIoGetLastError;
ST.base.memalloc = (void *(__cdecl *)(InputStream *,size_t))StdIoMalloc;
ST.base.memrealloc = (void *(__cdecl *)(InputStream *,void *,size_t))StdIoRealloc;
ST.base.memfree = (void (__cdecl *)(InputStream *,void *)) StdIoFree;
ST.base.progress = (int (__cdecl *)(InputStream *,ulonglong,ulonglong))StdIoProgress;
ST.fp = fopen(ASource, "rb");
if (ST.fp == NULL)
Env->ThrowError("FFmpegSource: Can't open '%s': %s", ASource, strerror(errno));
setvbuf(ST.fp, NULL, _IOFBF, CACHESIZE);
MF = mkv_OpenEx(&ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
if (MF == NULL) {
fclose(ST.fp);
Env->ThrowError("FFmpegSource: Can't parse Matroska file: %s", ErrorMessage);
}
VideoTrack = GetTrackIndex(AVideoTrack, TT_VIDEO, Env);
AudioTrack = GetTrackIndex(AAudioTrack, TT_AUDIO, Env);
bool VCacheIsValid = true;
bool ACacheIsValid = true;
if (VideoTrack >= 0) {
VCacheIsValid = LoadFrameInfoFromFile(AVideoCache, ASource, VideoTrack);
VideoTI = mkv_GetTrackInfo(MF, VideoTrack);
if (VideoTI->CompEnabled) {
VideoCS = cs_Create(MF, VideoTrack, ErrorMessage, sizeof(ErrorMessage));
if (VideoCS == NULL)
Env->ThrowError("FFmpegSource: Can't create decompressor: %s", ErrorMessage);
}
VideoCodecContext = avcodec_alloc_context();
VideoCodecContext->extradata = (uint8_t *)VideoTI->CodecPrivate;
VideoCodecContext->extradata_size = VideoTI->CodecPrivateSize;
VideoCodec = avcodec_find_decoder(MatroskaToFFCodecID(VideoTI));
if (VideoCodec == NULL)
Env->ThrowError("FFmpegSource: Video codec not found");
if (avcodec_open(VideoCodecContext, VideoCodec) < 0)
Env->ThrowError("FFmpegSource: Could not open video codec");
// Fix for mpeg2 and other formats where decoding a frame is necessary to get information about the stream
if (VideoCodecContext->pix_fmt == PIX_FMT_NONE) {
mkv_SetTrackMask(MF, ~(1 << VideoTrack));
int64_t Dummy;
DecodeNextFrame(DecodeFrame, &Dummy, Env);
mkv_Seek(MF, 0, MKVF_SEEK_TO_PREV_KEYFRAME);
}
VI.image_type = VideoInfo::IT_TFF;
VI.width = VideoTI->AV.Video.PixelWidth;
VI.height = VideoTI->AV.Video.PixelHeight;
VI.fps_denominator = 1;
VI.fps_numerator = 30;
SetOutputFormat(VideoCodecContext->pix_fmt, Env);
InitPP(VI.width, VI.height, APPString, AQuality, VideoCodecContext->pix_fmt, Env);
if (!VCacheIsValid)
TrackMask &= ~(1 << VideoTrack);
}
if (AudioTrack >= 0) {
TrackInfo *AudioTI = mkv_GetTrackInfo(MF, AudioTrack);
if (AudioTI->CompEnabled) {
AudioCS = cs_Create(MF, AudioTrack, ErrorMessage, sizeof(ErrorMessage));
if (AudioCS == NULL)
Env->ThrowError("FFmpegSource: Can't create decompressor: %s", ErrorMessage);
}
AudioCodecContext = avcodec_alloc_context();
AudioCodecContext->extradata = (uint8_t *)AudioTI->CodecPrivate;
AudioCodecContext->extradata_size = AudioTI->CodecPrivateSize;
AudioCodec = avcodec_find_decoder(MatroskaToFFCodecID(AudioTI));
if (AudioCodec == NULL)
Env->ThrowError("FFmpegSource: Audio codec not found");
if (avcodec_open(AudioCodecContext, AudioCodec) < 0)
Env->ThrowError("FFmpegSource: Could not open audio codec");
// Fix for ac3 and other codecs where decoding a block of audio is required to get information about it
if (AudioCodecContext->channels == 0 || AudioCodecContext->sample_rate == 0) {
mkv_SetTrackMask(MF, ~(1 << AudioTrack));
uint64_t StartTime, EndTime, FilePos;
unsigned int Track, FrameFlags, FrameSize;
mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags);
uint8_t DecodingBuffer[AVCODEC_MAX_AUDIO_FRAME_SIZE];
int Size = ReadFrame(FilePos, FrameSize, AudioCS, Env);
uint8_t *Data = Buffer;
while (Size > 0) {
int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
int Ret = avcodec_decode_audio2(AudioCodecContext, (int16_t *)DecodingBuffer, &TempOutputBufSize, Data, Size);
if (Ret < 0)
Env->ThrowError("FFmpegSource: Audio decoding error");
Size -= Ret;
Data += Ret;
}
mkv_Seek(MF, 0, MKVF_SEEK_TO_PREV_KEYFRAME);
}
VI.nchannels = AudioCodecContext->channels;
VI.audio_samples_per_second = AudioCodecContext->sample_rate;
switch (AudioCodecContext->sample_fmt) {
case SAMPLE_FMT_U8: VI.sample_type = SAMPLE_INT8; AACCompression = -1; break;
case SAMPLE_FMT_S16: VI.sample_type = SAMPLE_INT16; break;
case SAMPLE_FMT_S24: VI.sample_type = SAMPLE_INT24; AACCompression = -1; break;
case SAMPLE_FMT_S32: VI.sample_type = SAMPLE_INT32; AACCompression = -1; break;
case SAMPLE_FMT_FLT: VI.sample_type = SAMPLE_FLOAT; AACCompression = -1; break;
default:
Env->ThrowError("FFmpegSource: Unsupported/unknown sample format");
}
ACacheIsValid = OpenAudioCache(AAudioCache, ASource, AudioTrack, Env);
if (!ACacheIsValid)
TrackMask &= ~(1 << AudioTrack);
}
mkv_SetTrackMask(MF, TrackMask);
// Needs to be indexed?
if (!ACacheIsValid || !VCacheIsValid) {
FLAC__StreamEncoder *FSE = NULL;
FILE *RawCache = NULL;
if (!ACacheIsValid)
if (AACCompression >= 0)
AudioCacheType = acFLAC;
else
AudioCacheType = acRaw;
switch (AudioCacheType) {
case acFLAC: FSE = NewFLACCacheWriter(AAudioCache, ASource, AudioTrack, AACCompression, Env); break;
case acRaw: RawCache = NewRawCacheWriter(AAudioCache, ASource, AudioTrack, Env); break;
}
uint64_t StartTime, EndTime, FilePos;
unsigned int Track, FrameFlags, FrameSize;
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0)
if (Track == VideoTrack && !VCacheIsValid) {
FrameToDTS.push_back(FrameInfo(StartTime, (FrameFlags & FRAME_KF) != 0));
VI.num_frames++;
} else if (Track == AudioTrack && !ACacheIsValid) {
int Size = ReadFrame(FilePos, FrameSize, AudioCS, Env);
uint8_t *Data = Buffer;
while (Size > 0) {
int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
int Ret = avcodec_decode_audio2(AudioCodecContext, (int16_t *)DecodingBuffer, &TempOutputBufSize, Data, Size);
if (Ret < 0)
Env->ThrowError("FFmpegSource: Audio decoding error");
int DecodedSamples = VI.AudioSamplesFromBytes(TempOutputBufSize);
Size -= Ret;
Data += Ret;
VI.num_audio_samples += DecodedSamples;
if (AudioCacheType == acFLAC) {
for (int i = 0; i < DecodedSamples * VI.nchannels; i++)
FLACBuffer[i] = ((int16_t *)DecodingBuffer)[i];
FLAC__stream_encoder_process_interleaved(FSE, FLACBuffer, DecodedSamples);
} else if (AudioCacheType == acRaw) {
fwrite(DecodingBuffer, 1, TempOutputBufSize, RawCache);
}
}
}
if (!ACacheIsValid) {
switch (AudioCacheType) {
case acFLAC: CloseFLACCacheWriter(FSE); break;
case acRaw: CloseRawCacheWriter(RawCache); break;
}
ACacheIsValid = OpenAudioCache(AAudioCache, ASource, AudioTrack, Env);
if (!ACacheIsValid)
Env->ThrowError("FFmpegSource: Failed to open newly created audio cache for reading");
}
if (VideoTrack >= 0 && VI.num_frames == 0)
Env->ThrowError("FFmpegSource: Video track contains no frames");
if (AudioTrack >= 0 && VI.num_audio_samples == 0)
Env->ThrowError("FFmpegSource: Audio track contains no samples");
if (VideoTrack >= 0)
mkv_Seek(MF, FrameToDTS.front().DTS, MKVF_SEEK_TO_PREV_KEYFRAME);
if (AVCache && !VCacheIsValid)
if (!SaveFrameInfoToFile(AVideoCache, ASource, VideoTrack))
Env->ThrowError("FFmpegSource: Failed to write video cache info");
}
if (AudioTrack >= 0) {
avcodec_close(AudioCodecContext);
av_free(AudioCodecContext);
}
if (VideoTrack >= 0) {
mkv_SetTrackMask(MF, ~(1 << VideoTrack));
// Calculate the average framerate
if (FrameToDTS.size() >= 2) {
double DTSDiff = (double)(FrameToDTS.back().DTS - FrameToDTS.front().DTS);
VI.fps_denominator = (unsigned int)(DTSDiff * mkv_TruncFloat(VideoTI->TimecodeScale) / (double)1000 / (double)(VI.num_frames - 1) + 0.5);
VI.fps_numerator = 1000000;
}
if (!SaveTimecodesToFile(ATimecodes, mkv_TruncFloat(VideoTI->TimecodeScale), 1000000))
Env->ThrowError("FFmpegSource: Failed to write timecodes");
}
}
~FFMatroskaSource() {
free(Buffer);
mkv_Close(MF);
fclose(ST.fp);
if (VideoCodecContext)
avcodec_close(VideoCodecContext);
av_free(VideoCodecContext);
}
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* Env);
};
int FFMatroskaSource::ReadFrame(uint64_t AFilePos, unsigned int AFrameSize, CompressedStream *ACS, IScriptEnvironment *Env) {
if (ACS) {
char CSBuffer[4096];
unsigned int DecompressedFrameSize = 0;
cs_NextFrame(ACS, AFilePos, AFrameSize);
for (;;) {
int ReadBytes = cs_ReadData(ACS, CSBuffer, sizeof(CSBuffer));
if (ReadBytes < 0)
Env->ThrowError("FFmpegSource: Error decompressing data: %s", cs_GetLastError(ACS));
if (ReadBytes == 0) {
return DecompressedFrameSize;
}
if (BufferSize < DecompressedFrameSize + ReadBytes) {
BufferSize = AFrameSize;
Buffer = (uint8_t *)realloc(Buffer, BufferSize);
if (Buffer == NULL)
Env->ThrowError("FFmpegSource: Out of memory");
}
memcpy(Buffer + DecompressedFrameSize, CSBuffer, ReadBytes);
DecompressedFrameSize += ReadBytes;
}
} else {
if (_fseeki64(ST.fp, AFilePos, SEEK_SET))
Env->ThrowError("FFmpegSource: fseek(): %s", strerror(errno));
if (BufferSize < AFrameSize) {
BufferSize = AFrameSize;
Buffer = (uint8_t *)realloc(Buffer, BufferSize);
if (Buffer == NULL)
Env->ThrowError("FFmpegSource: Out of memory");
}
size_t ReadBytes = fread(Buffer, 1, AFrameSize, ST.fp);
if (ReadBytes != AFrameSize) {
if (ReadBytes == 0) {
if (feof(ST.fp))
Env->ThrowError("FFmpegSource: Unexpected EOF while reading frame");
else
Env->ThrowError("FFmpegSource: Error reading frame: %s", strerror(errno));
} else
Env->ThrowError("FFmpegSource: Short read while reading frame");
Env->ThrowError("FFmpegSource: Unknown read error");
}
return AFrameSize;
}
return 0;
}
int FFMatroskaSource::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, IScriptEnvironment* Env) {
int FrameFinished = 0;
int Ret = -1;
*AFirstStartTime = -1;
uint64_t StartTime, EndTime, FilePos;
unsigned int Track, FrameFlags, FrameSize;
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) {
FrameSize = ReadFrame(FilePos, FrameSize, VideoCS, Env);
if (*AFirstStartTime < 0)
*AFirstStartTime = StartTime;
Ret = avcodec_decode_video(VideoCodecContext, AFrame, &FrameFinished, Buffer, FrameSize);
if (FrameFinished)
goto Done;
}
// Flush the last frame
if (CurrentFrame == VI.num_frames - 1 && VideoCodecContext->has_b_frames)
Ret = avcodec_decode_video(VideoCodecContext, AFrame, &FrameFinished, NULL, 0);
Done:
return Ret;
}
PVideoFrame __stdcall FFMatroskaSource::GetFrame(int n, IScriptEnvironment* Env) {
bool HasSeeked = false;
if (n < CurrentFrame || FindClosestKeyFrame(n) > CurrentFrame) {
mkv_Seek(MF, FrameToDTS[n].DTS, MKVF_SEEK_TO_PREV_KEYFRAME);
avcodec_flush_buffers(VideoCodecContext);
HasSeeked = true;
}
do {
int64_t StartTime;
int Ret = DecodeNextFrame(DecodeFrame, &StartTime, Env);
if (HasSeeked) {
HasSeeked = false;
if (StartTime < 0 || (CurrentFrame = FrameFromDTS(StartTime)) < 0)
Env->ThrowError("FFmpegSource: Frame accurate seeking is not possible in this file");
}
CurrentFrame++;
} while (CurrentFrame <= n);
return OutputFrame(DecodeFrame, Env);
}
AVSValue __cdecl CreateFFmpegSource(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
if (!UserData) {
av_register_all();
UserData = (void *)-1;
}
if (!Args[0].Defined())
Env->ThrowError("FFmpegSource: No source specified");
const char *Source = Args[0].AsString();
int VTrack = Args[1].AsInt(-1);
int ATrack = Args[2].AsInt(-2);
const char *Timecodes = Args[3].AsString("");
bool VCache = Args[4].AsBool(true);
const char *VCacheFile = Args[5].AsString("");
const char *ACacheFile = Args[6].AsString("");
int ACCompression = Args[7].AsInt(-1);
const char *PPString = Args[8].AsString("");
int PPQuality = Args[9].AsInt(PP_QUALITY_MAX);
int SeekMode = Args[10].AsInt(1);
if (VTrack <= -2 && ATrack <= -2)
Env->ThrowError("FFmpegSource: No tracks selected");
if (ACCompression < -1 || ACCompression > 8)
Env->ThrowError("FFmpegSource: Invalid audio cache compression selected");
AVFormatContext *FormatContext;
if (av_open_input_file(&FormatContext, Source, NULL, 0, NULL) != 0)
Env->ThrowError("FFmpegSource: Couldn't open %s", Args[0].AsString());
bool IsMatroska = !strcmp(FormatContext->iformat->name, "matroska");
av_close_input_file(FormatContext);
if (IsMatroska)
return new FFMatroskaSource(Source, VTrack, ATrack, Timecodes, VCache, VCacheFile, ACacheFile, ACCompression, PPString, PPQuality, Env);
else
return new FFmpegSource(Source, VTrack, ATrack, Timecodes, VCache, VCacheFile, ACacheFile, ACCompression, PPString, PPQuality, SeekMode, Env);
}
AVSValue __cdecl CreateFFPP(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
return new FFPP(Args[0].AsClip(), Args[1].AsString(""), Args[2].AsInt(PP_QUALITY_MAX), Env);
}
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* Env) {
Env->AddFunction("FFmpegSource", "[source]s[vtrack]i[atrack]i[timecodes]s[vcache]b[vcachefile]s[acachefile]s[accompression]i[pp]s[ppquality]i[seekmode]i", CreateFFmpegSource, 0);
Env->AddFunction("FFPP", "c[pp]s[ppquality]i", CreateFFPP, 0);
return "FFmpegSource";
};

View File

@ -1,397 +1,10 @@
#include "ffmpegsource.h"
#include "stdiostream.c"
#include "matroskacodecs.c"
class FFMatroskaSource : public FFBase {
private:
StdIoStream ST;
unsigned int BufferSize;
CompressedStream *VideoCS;
CompressedStream *AudioCS;
AVCodecContext *VideoCodecContext;
MatroskaFile *MF;
char ErrorMessage[256];
uint8_t *Buffer;
int CurrentFrame;
int ReadFrame(uint64_t AFilePos, unsigned int AFrameSize, CompressedStream *ACS, IScriptEnvironment *Env);
int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, IScriptEnvironment* Env);
int GetTrackIndex(int Index, unsigned char ATrackType, IScriptEnvironment *Env) {
if (Index == -1)
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++)
if (mkv_GetTrackInfo(MF, i)->Type == ATrackType) {
Index = i;
break;
static DWORD WINAPI AVFindStreamInfoExecute(AVFormatContext *FormatContext) {
return av_find_stream_info(FormatContext);
}
if (Index == -1)
Env->ThrowError("FFmpegSource: No %s track found", (ATrackType & TT_VIDEO) ? "video" : "audio");
if (Index <= -2)
return -2;
if (Index >= (int)mkv_GetNumTracks(MF))
Env->ThrowError("FFmpegSource: Invalid %s track number", (ATrackType & TT_VIDEO) ? "video" : "audio");
TrackInfo *TI = mkv_GetTrackInfo(MF, Index);
if (TI->Type != ATrackType)
Env->ThrowError("FFmpegSource: Selected track is not %s", (ATrackType & TT_VIDEO) ? "video" : "audio");
return Index;
}
public:
FFMatroskaSource(const char *ASource, int AVideoTrack, int AAudioTrack, const char *ATimecodes, bool AVCache, const char *AVideoCache, const char *AAudioCache, const char *APPString, int AQuality, IScriptEnvironment* Env) {
CurrentFrame = 0;
int VideoTrack;
int AudioTrack;
unsigned int TrackMask = ~0;
AVCodecContext *AudioCodecContext = NULL;
AVCodec *AudioCodec = NULL;
VideoCodecContext = NULL;
AVCodec *VideoCodec = NULL;
TrackInfo *VideoTI = NULL;
BufferSize = 0;
Buffer = NULL;
VideoCS = NULL;
AudioCS = NULL;
memset(&ST,0,sizeof(ST));
ST.base.read = (int (__cdecl *)(InputStream *,ulonglong,void *,int))StdIoRead;
ST.base.scan = (longlong (__cdecl *)(InputStream *,ulonglong,unsigned int))StdIoScan;
ST.base.getcachesize = (unsigned int (__cdecl *)(InputStream *))StdIoGetCacheSize;
ST.base.geterror = (const char *(__cdecl *)(InputStream *))StdIoGetLastError;
ST.base.memalloc = (void *(__cdecl *)(InputStream *,size_t))StdIoMalloc;
ST.base.memrealloc = (void *(__cdecl *)(InputStream *,void *,size_t))StdIoRealloc;
ST.base.memfree = (void (__cdecl *)(InputStream *,void *)) StdIoFree;
ST.base.progress = (int (__cdecl *)(InputStream *,ulonglong,ulonglong))StdIoProgress;
ST.fp = fopen(ASource, "rb");
if (ST.fp == NULL)
Env->ThrowError("FFmpegSource: Can't open '%s': %s", ASource, strerror(errno));
setvbuf(ST.fp, NULL, _IOFBF, CACHESIZE);
MF = mkv_OpenEx(&ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage));
if (MF == NULL) {
fclose(ST.fp);
Env->ThrowError("FFmpegSource: Can't parse Matroska file: %s", ErrorMessage);
}
VideoTrack = GetTrackIndex(AVideoTrack, TT_VIDEO, Env);
AudioTrack = GetTrackIndex(AAudioTrack, TT_AUDIO, Env);
bool VCacheIsValid = true;
bool ACacheIsValid = true;
if (VideoTrack >= 0) {
VCacheIsValid = LoadFrameInfoFromFile(AVideoCache, ASource, VideoTrack);
VideoTI = mkv_GetTrackInfo(MF, VideoTrack);
if (VideoTI->CompEnabled) {
VideoCS = cs_Create(MF, VideoTrack, ErrorMessage, sizeof(ErrorMessage));
if (VideoCS == NULL)
Env->ThrowError("FFmpegSource: Can't create decompressor: %s", ErrorMessage);
}
VideoCodecContext = avcodec_alloc_context();
VideoCodecContext->extradata = (uint8_t *)VideoTI->CodecPrivate;
VideoCodecContext->extradata_size = VideoTI->CodecPrivateSize;
VideoCodec = avcodec_find_decoder(MatroskaToFFCodecID(VideoTI));
if (VideoCodec == NULL)
Env->ThrowError("FFmpegSource: Video codec not found");
if (avcodec_open(VideoCodecContext, VideoCodec) < 0)
Env->ThrowError("FFmpegSource: Could not open video codec");
// Fix for mpeg2 and other formats where decoding a frame is necessary to get information about the stream
if (VideoCodecContext->pix_fmt == PIX_FMT_NONE) {
mkv_SetTrackMask(MF, ~(1 << VideoTrack));
int64_t Dummy;
DecodeNextFrame(DecodeFrame, &Dummy, Env);
mkv_Seek(MF, 0, MKVF_SEEK_TO_PREV_KEYFRAME);
}
VI.image_type = VideoInfo::IT_TFF;
VI.width = VideoTI->AV.Video.PixelWidth;
VI.height = VideoTI->AV.Video.PixelHeight;
VI.fps_denominator = 1;
VI.fps_numerator = 30;
SetOutputFormat(VideoCodecContext->pix_fmt, Env);
InitPP(VI.width, VI.height, APPString, AQuality, VideoCodecContext->pix_fmt, Env);
if (!VCacheIsValid)
TrackMask &= ~(1 << VideoTrack);
}
if (AudioTrack >= 0) {
TrackInfo *AudioTI = mkv_GetTrackInfo(MF, AudioTrack);
if (AudioTI->CompEnabled) {
AudioCS = cs_Create(MF, AudioTrack, ErrorMessage, sizeof(ErrorMessage));
if (AudioCS == NULL)
Env->ThrowError("FFmpegSource: Can't create decompressor: %s", ErrorMessage);
}
AudioCodecContext = avcodec_alloc_context();
AudioCodecContext->extradata = (uint8_t *)AudioTI->CodecPrivate;
AudioCodecContext->extradata_size = AudioTI->CodecPrivateSize;
AudioCodec = avcodec_find_decoder(MatroskaToFFCodecID(AudioTI));
if (AudioCodec == NULL)
Env->ThrowError("FFmpegSource: Audio codec not found");
if (avcodec_open(AudioCodecContext, AudioCodec) < 0)
Env->ThrowError("FFmpegSource: Could not open audio codec");
// Fix for ac3 and other codecs where decoding a block of audio is required to get information about it
if (AudioCodecContext->channels == 0 || AudioCodecContext->sample_rate == 0) {
mkv_SetTrackMask(MF, ~(1 << AudioTrack));
uint64_t StartTime, EndTime, FilePos;
unsigned int Track, FrameFlags, FrameSize;
mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags);
uint8_t DecodingBuffer[AVCODEC_MAX_AUDIO_FRAME_SIZE];
int Size = ReadFrame(FilePos, FrameSize, AudioCS, Env);
uint8_t *Data = Buffer;
while (Size > 0) {
int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
int Ret = avcodec_decode_audio2(AudioCodecContext, (int16_t *)DecodingBuffer, &TempOutputBufSize, Data, Size);
if (Ret < 0)
Env->ThrowError("FFmpegSource: Audio decoding error");
Size -= Ret;
Data += Ret;
}
mkv_Seek(MF, 0, MKVF_SEEK_TO_PREV_KEYFRAME);
}
VI.nchannels = AudioCodecContext->channels;
VI.audio_samples_per_second = AudioCodecContext->sample_rate;
switch (AudioCodecContext->sample_fmt) {
case SAMPLE_FMT_U8: VI.sample_type = SAMPLE_INT8; break;
case SAMPLE_FMT_S16: VI.sample_type = SAMPLE_INT16; break;
case SAMPLE_FMT_S24: VI.sample_type = SAMPLE_INT24; break;
case SAMPLE_FMT_S32: VI.sample_type = SAMPLE_INT32; break;
case SAMPLE_FMT_FLT: VI.sample_type = SAMPLE_FLOAT; break;
default:
Env->ThrowError("FFmpegSource: Unsupported/unknown sample format");
}
ACacheIsValid = PrepareAudioCache(AAudioCache, ASource, AudioTrack, Env);
if (!ACacheIsValid)
TrackMask &= ~(1 << AudioTrack);
}
mkv_SetTrackMask(MF, TrackMask);
uint8_t DecodingBuffer[AVCODEC_MAX_AUDIO_FRAME_SIZE];
// Needs to be indexed?
if (!ACacheIsValid || !VCacheIsValid) {
uint64_t StartTime, EndTime, FilePos;
unsigned int Track, FrameFlags, FrameSize;
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0)
if (Track == VideoTrack && !VCacheIsValid) {
FrameToDTS.push_back(FrameInfo(StartTime, (FrameFlags & FRAME_KF) != 0));
VI.num_frames++;
} else if (Track == AudioTrack && !ACacheIsValid) {
int Size = ReadFrame(FilePos, FrameSize, AudioCS, Env);
uint8_t *Data = Buffer;
while (Size > 0) {
int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE;
int Ret = avcodec_decode_audio2(AudioCodecContext, (int16_t *)DecodingBuffer, &TempOutputBufSize, Data, Size);
if (Ret < 0)
Env->ThrowError("FFmpegSource: Audio decoding error");
Size -= Ret;
Data += Ret;
VI.num_audio_samples += VI.AudioSamplesFromBytes(TempOutputBufSize);
fwrite(DecodingBuffer, 1, TempOutputBufSize, AudioCache);
}
}
if (VideoTrack >= 0 && VI.num_frames == 0)
Env->ThrowError("FFmpegSource: Video track contains no frames");
if (AudioTrack >= 0 && VI.num_audio_samples == 0)
Env->ThrowError("FFmpegSource: Audio track contains no samples");
if (VideoTrack >= 0)
mkv_Seek(MF, FrameToDTS.front().DTS, MKVF_SEEK_TO_PREV_KEYFRAME);
if (AVCache && !VCacheIsValid)
if (!SaveFrameInfoToFile(AVideoCache, ASource, VideoTrack))
Env->ThrowError("FFmpegSource: Failed to write video cache info");
}
if (AudioTrack >= 0) {
avcodec_close(AudioCodecContext);
av_free(AudioCodecContext);
}
if (VideoTrack >= 0) {
mkv_SetTrackMask(MF, ~(1 << VideoTrack));
// Calculate the average framerate
if (FrameToDTS.size() >= 2) {
double DTSDiff = (double)(FrameToDTS.back().DTS - FrameToDTS.front().DTS);
VI.fps_denominator = (unsigned int)(DTSDiff * mkv_TruncFloat(VideoTI->TimecodeScale) / (double)1000 / (double)(VI.num_frames - 1) + 0.5);
VI.fps_numerator = 1000000;
}
if (!SaveTimecodesToFile(ATimecodes, mkv_TruncFloat(VideoTI->TimecodeScale), 1000000))
Env->ThrowError("FFmpegSource: Failed to write timecodes");
}
}
~FFMatroskaSource() {
free(Buffer);
mkv_Close(MF);
fclose(ST.fp);
if (AudioCache)
fclose(AudioCache);
if (VideoCodecContext)
avcodec_close(VideoCodecContext);
av_free(VideoCodecContext);
}
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* Env);
};
int FFMatroskaSource::ReadFrame(uint64_t AFilePos, unsigned int AFrameSize, CompressedStream *ACS, IScriptEnvironment *Env) {
if (ACS) {
char CSBuffer[4096];
unsigned int DecompressedFrameSize = 0;
cs_NextFrame(ACS, AFilePos, AFrameSize);
for (;;) {
int ReadBytes = cs_ReadData(ACS, CSBuffer, sizeof(CSBuffer));
if (ReadBytes < 0)
Env->ThrowError("FFmpegSource: Error decompressing data: %s", cs_GetLastError(ACS));
if (ReadBytes == 0) {
return DecompressedFrameSize;
}
if (BufferSize < DecompressedFrameSize + ReadBytes) {
BufferSize = AFrameSize;
Buffer = (uint8_t *)realloc(Buffer, BufferSize);
if (Buffer == NULL)
Env->ThrowError("FFmpegSource: Out of memory");
}
memcpy(Buffer + DecompressedFrameSize, CSBuffer, ReadBytes);
DecompressedFrameSize += ReadBytes;
}
} else {
if (_fseeki64(ST.fp, AFilePos, SEEK_SET))
Env->ThrowError("FFmpegSource: fseek(): %s", strerror(errno));
if (BufferSize < AFrameSize) {
BufferSize = AFrameSize;
Buffer = (uint8_t *)realloc(Buffer, BufferSize);
if (Buffer == NULL)
Env->ThrowError("FFmpegSource: Out of memory");
}
size_t ReadBytes = fread(Buffer, 1, AFrameSize, ST.fp);
if (ReadBytes != AFrameSize) {
if (ReadBytes == 0) {
if (feof(ST.fp))
Env->ThrowError("FFmpegSource: Unexpected EOF while reading frame");
else
Env->ThrowError("FFmpegSource: Error reading frame: %s", strerror(errno));
} else
Env->ThrowError("FFmpegSource: Short read while reading frame");
Env->ThrowError("FFmpegSource: Unknown read error");
}
return AFrameSize;
}
return 0;
}
int FFMatroskaSource::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, IScriptEnvironment* Env) {
int FrameFinished = 0;
int Ret = -1;
*AFirstStartTime = -1;
uint64_t StartTime, EndTime, FilePos;
unsigned int Track, FrameFlags, FrameSize;
while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) {
FrameSize = ReadFrame(FilePos, FrameSize, VideoCS, Env);
if (*AFirstStartTime < 0)
*AFirstStartTime = StartTime;
Ret = avcodec_decode_video(VideoCodecContext, AFrame, &FrameFinished, Buffer, FrameSize);
if (FrameFinished)
goto Done;
}
// Flush the last frame
if (CurrentFrame == VI.num_frames - 1 && VideoCodecContext->has_b_frames)
Ret = avcodec_decode_video(VideoCodecContext, AFrame, &FrameFinished, NULL, 0);
Done:
return Ret;
}
PVideoFrame __stdcall FFMatroskaSource::GetFrame(int n, IScriptEnvironment* Env) {
bool HasSeeked = false;
if (n < CurrentFrame || FindClosestKeyFrame(n) > CurrentFrame) {
mkv_Seek(MF, FrameToDTS[n].DTS, MKVF_SEEK_TO_PREV_KEYFRAME);
avcodec_flush_buffers(VideoCodecContext);
HasSeeked = true;
}
do {
int64_t StartTime;
int Ret = DecodeNextFrame(DecodeFrame, &StartTime, Env);
if (HasSeeked) {
HasSeeked = false;
if (StartTime < 0 || (CurrentFrame = FrameFromDTS(StartTime)) < 0)
Env->ThrowError("FFmpegSource: Frame accurate seeking is not possible in this file");
}
CurrentFrame++;
} while (CurrentFrame <= n);
return OutputFrame(DecodeFrame, Env);
}
class FFmpegSource : public FFBase {
private:
AVFormatContext *FormatContext;
AVCodecContext *VideoCodecContext;
int VideoTrack;
int CurrentFrame;
int SeekMode;
int DecodeNextFrame(AVFrame *Frame, int64_t *DTS);
int GetTrackIndex(int Index, CodecType ATrackType, IScriptEnvironment *Env) {
int FFmpegSource::GetTrackIndex(int Index, CodecType ATrackType, IScriptEnvironment *Env) {
if (Index == -1)
for (unsigned int i = 0; i < FormatContext->nb_streams; i++)
if (FormatContext->streams[i]->codec->codec_type == ATrackType) {
@ -412,8 +25,8 @@ private:
return Index;
}
public:
FFmpegSource(const char *ASource, int AVideoTrack, int AAudioTrack, const char *ATimecodes, bool AVCache, const char *AVideoCache, const char *AAudioCache, const char *APPString, int AQuality, int ASeekMode, IScriptEnvironment* Env) {
FFmpegSource::FFmpegSource(const char *ASource, int AVideoTrack, int AAudioTrack, const char *ATimecodes, bool AVCache, const char *AVideoCache, const char *AAudioCache, int AACCompression, const char *APPString, int AQuality, int ASeekMode, IScriptEnvironment* Env) {
CurrentFrame = 0;
SeekMode = ASeekMode;
@ -476,11 +89,11 @@ public:
Env->ThrowError("FFmpegSource: Could not open audio codec");
switch (AudioCodecContext->sample_fmt) {
case SAMPLE_FMT_U8: VI.sample_type = SAMPLE_INT8; break;
case SAMPLE_FMT_U8: VI.sample_type = SAMPLE_INT8; AACCompression = -1; break;
case SAMPLE_FMT_S16: VI.sample_type = SAMPLE_INT16; break;
case SAMPLE_FMT_S24: VI.sample_type = SAMPLE_INT24; break;
case SAMPLE_FMT_S32: VI.sample_type = SAMPLE_INT32; break;
case SAMPLE_FMT_FLT: VI.sample_type = SAMPLE_FLOAT; break;
case SAMPLE_FMT_S24: VI.sample_type = SAMPLE_INT24; AACCompression = -1; break;
case SAMPLE_FMT_S32: VI.sample_type = SAMPLE_INT32; AACCompression = -1; break;
case SAMPLE_FMT_FLT: VI.sample_type = SAMPLE_FLOAT; AACCompression = -1; break;
default:
Env->ThrowError("FFmpegSource: Unsupported/unknown sample format");
}
@ -488,13 +101,25 @@ public:
VI.nchannels = AudioCodecContext->channels;
VI.audio_samples_per_second = AudioCodecContext->sample_rate;
ACacheIsValid = PrepareAudioCache(AAudioCache, ASource, AudioTrack, Env);
ACacheIsValid = OpenAudioCache(AAudioCache, ASource, AudioTrack, Env);
}
uint8_t DecodingBuffer[AVCODEC_MAX_AUDIO_FRAME_SIZE];
// Needs to be indexed?
if (!ACacheIsValid || !VCacheIsValid) {
FLAC__StreamEncoder *FSE = NULL;
FILE *RawCache = NULL;
if (!ACacheIsValid)
if (AACCompression >= 0)
AudioCacheType = acFLAC;
else
AudioCacheType = acRaw;
switch (AudioCacheType) {
case acFLAC: FSE = NewFLACCacheWriter(AAudioCache, ASource, AudioTrack, AACCompression, Env); break;
case acRaw: RawCache = NewRawCacheWriter(AAudioCache, ASource, AudioTrack, Env); break;
}
AVPacket Packet;
while (av_read_frame(FormatContext, &Packet) >= 0) {
if (Packet.stream_index == VideoTrack && !VCacheIsValid) {
@ -510,17 +135,36 @@ public:
if (Ret < 0)
Env->ThrowError("FFmpegSource: Audio decoding error");
int DecodedSamples = VI.AudioSamplesFromBytes(TempOutputBufSize);
Size -= Ret;
Data += Ret;
VI.num_audio_samples += VI.AudioSamplesFromBytes(TempOutputBufSize);
VI.num_audio_samples += DecodedSamples;
fwrite(DecodingBuffer, 1, TempOutputBufSize, AudioCache);
if (AudioCacheType == acFLAC) {
for (int i = 0; i < DecodedSamples * VI.nchannels; i++)
FLACBuffer[i] = ((int16_t *)DecodingBuffer)[i];
FLAC__stream_encoder_process_interleaved(FSE, FLACBuffer, DecodedSamples);
} else if (AudioCacheType == acRaw) {
fwrite(DecodingBuffer, 1, TempOutputBufSize, RawCache);
}
}
}
av_free_packet(&Packet);
}
if (!ACacheIsValid) {
switch (AudioCacheType) {
case acFLAC: CloseFLACCacheWriter(FSE); break;
case acRaw: CloseRawCacheWriter(RawCache); break;
}
ACacheIsValid = OpenAudioCache(AAudioCache, ASource, AudioTrack, Env);
if (!ACacheIsValid)
Env->ThrowError("FFmpegSource: Failed to open newly created audio cache for reading");
}
if (VideoTrack >= 0 && VI.num_frames == 0)
Env->ThrowError("FFmpegSource: Video track contains no frames");
@ -530,7 +174,7 @@ public:
if (VideoTrack >= 0)
av_seek_frame(FormatContext, VideoTrack, FrameToDTS.front().DTS, AVSEEK_FLAG_BACKWARD);
if (AVCache)
if (AVCache && !VCacheIsValid)
if (!SaveFrameInfoToFile(AVideoCache, ASource, VideoTrack))
Env->ThrowError("FFmpegSource: Failed to write video cache info");
}
@ -550,14 +194,12 @@ public:
}
}
~FFmpegSource() {
FFmpegSource::~FFmpegSource() {
if (VideoTrack >= 0)
avcodec_close(VideoCodecContext);
av_close_input_file(FormatContext);
}
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* Env);
};
int FFmpegSource::DecodeNextFrame(AVFrame *AFrame, int64_t *AStartTime) {
AVPacket Packet;
@ -633,50 +275,3 @@ PVideoFrame __stdcall FFmpegSource::GetFrame(int n, IScriptEnvironment* Env) {
return OutputFrame(DecodeFrame, Env);
}
AVSValue __cdecl CreateFFmpegSource(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
if (!UserData) {
av_register_all();
UserData = (void *)-1;
}
if (!Args[0].Defined())
Env->ThrowError("FFmpegSource: No source specified");
const char *Source = Args[0].AsString();
int VTrack = Args[1].AsInt(-1);
int ATrack = Args[2].AsInt(-2);
const char *Timecodes = Args[3].AsString("");
bool VCache = Args[4].AsBool(true);
const char *VCacheFile = Args[5].AsString("");
const char *ACacheFile = Args[6].AsString("");
const char *PPString = Args[7].AsString("");
int PPQuality = Args[8].AsInt(PP_QUALITY_MAX);
int SeekMode = Args[9].AsInt(1);
if (VTrack <= -2 && ATrack <= -2)
Env->ThrowError("FFmpegSource: No tracks selected");
AVFormatContext *FormatContext;
if (av_open_input_file(&FormatContext, Source, NULL, 0, NULL) != 0)
Env->ThrowError("FFmpegSource: Couldn't open %s", Args[0].AsString());
bool IsMatroska = !strcmp(FormatContext->iformat->name, "matroska");
av_close_input_file(FormatContext);
if (IsMatroska)
return new FFMatroskaSource(Source, VTrack, ATrack, Timecodes, VCache, VCacheFile, ACacheFile, PPString, PPQuality, Env);
else
return new FFmpegSource(Source, VTrack, ATrack, Timecodes, VCache, VCacheFile, ACacheFile, PPString, PPQuality, SeekMode, Env);
}
AVSValue __cdecl CreateFFPP(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
return new FFPP(Args[0].AsClip(), Args[1].AsString(""), Args[2].AsInt(PP_QUALITY_MAX), Env);
}
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* Env) {
Env->AddFunction("FFmpegSource", "[source]s[vtrack]i[atrack]i[timecodes]s[vcache]b[vcachefile]s[acachefile]s[pp]s[ppquality]i[seekmode]i", CreateFFmpegSource, 0);
Env->AddFunction("FFPP", "c[pp]s[ppquality]i", CreateFFPP, 0);
return "FFmpegSource";
};

View File

@ -7,8 +7,8 @@
#include <fcntl.h>
#include <io.h>
#include "MatroskaParser.h"
#include "avisynth.h"
#include <stream_decoder.h>
#include <stream_encoder.h>
extern "C" {
#include <ffmpeg\avformat.h>
@ -17,6 +17,11 @@ extern "C" {
#include <postproc\postprocess.h>
}
#include "MatroskaParser.h"
#include "avisynth.h"
enum AudioCacheFormat {acNone, acRaw, acFLAC};
int GetPPCPUFlags(IScriptEnvironment *Env);
int GetSWSCPUFlags(IScriptEnvironment *Env);
CodecID MatroskaToFFCodecID(TrackInfo *TI);
@ -45,7 +50,12 @@ private:
protected:
VideoInfo VI;
AVFrame *DecodeFrame;
FILE *AudioCache;
FILE *RawAudioCache;
FLAC__StreamDecoder *FLACAudioCache;
AudioCacheFormat AudioCacheType;
uint8_t *DecodingBuffer;
FLAC__int32 *FLACBuffer;
struct FrameInfo {
int64_t DTS;
@ -61,12 +71,23 @@ protected:
bool LoadFrameInfoFromFile(const char *AVideoCacheFile, const char *ASource, int AVideoTrack);
bool SaveFrameInfoToFile(const char *AVideoCacheFile, const char *ASource, int AVideoTrack);
bool SaveTimecodesToFile(const char *ATimecodeFile, int64_t ScaleD, int64_t ScaleN);
bool PrepareAudioCache(const char *AAudioCacheFile, const char *ASource, int AAudioTrack, IScriptEnvironment *Env);
bool OpenAudioCache(const char *AAudioCacheFile, const char *ASource, int AAudioTrack, IScriptEnvironment *Env);
FLAC__StreamEncoder *FFBase::NewFLACCacheWriter(const char *AAudioCacheFile, const char *ASource, int AAudioTrack, int ACompression, IScriptEnvironment *Env);
void FFBase::CloseFLACCacheWriter(FLAC__StreamEncoder *AFSE);
FILE *FFBase::NewRawCacheWriter(const char *AAudioCacheFile, const char *ASource, int AAudioTrack, IScriptEnvironment *Env);
void FFBase::CloseRawCacheWriter(FILE *ARawCache);
void InitPP(int AWidth, int AHeight, const char *APPString, int AQuality, int APixelFormat, IScriptEnvironment *Env);
void SetOutputFormat(int ACurrentFormat, IScriptEnvironment *Env);
PVideoFrame OutputFrame(AVFrame *AFrame, IScriptEnvironment *Env);
public:
// FLAC decoder variables, have to be public
FILE *FCFile;
__int64 FCCount;
void *FCBuffer;
bool FCError;
FFBase();
~FFBase();
@ -75,3 +96,20 @@ public:
const VideoInfo& __stdcall GetVideoInfo() { return VI; }
void __stdcall GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment* Env);
};
class FFmpegSource : public FFBase {
private:
AVFormatContext *FormatContext;
AVCodecContext *VideoCodecContext;
int VideoTrack;
int CurrentFrame;
int SeekMode;
int GetTrackIndex(int Index, CodecType ATrackType, IScriptEnvironment *Env);
int DecodeNextFrame(AVFrame *Frame, int64_t *DTS);
public:
FFmpegSource(const char *ASource, int AVideoTrack, int AAudioTrack, const char *ATimecodes, bool AVCache, const char *AVideoCache, const char *AAudioCache, int AACCompression, const char *APPString, int AQuality, int ASeekMode, IScriptEnvironment* Env);
~FFmpegSource();
PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* Env);
};

View File

@ -9,6 +9,24 @@ FFmpegSource Documentation
<h2>Changes</h2>
<ul>
<li>1.10<ul>
<li>The audio cache compression level is now ignored if the source isn't 16bit and the raw format is used instead</li>
<li>FLAC is now actually initialized properly so the cache actually works for files that aren't stereo (16bit limit still applies)</li>
<li>Now uses proper callbacks for FLAC so it works with larger than 2GB files</li>
<li>Doesn't (over)write the video cache with an empty one in certain cases when avformat is used for the source</li>
</ul></li>
<li>1.9<ul>
<li>Added the possibility to compress the audio cache with FLAC (currently only works with 16bit audio)</li>
<li>Added another planar YUV 4:2:0 format to the supported output formats (fixes certain mov files)</li>
<li>Less memory is now allocated on the stack which makes av_find_stream_info() work for all files (fixes certain mov files)</li>
<li>Updated FFmpeg to rev 10186</li>
</ul></li>
<li>1.8<ul>
<li>Updated FFmpeg to rev 10141</li>
</ul></li>
<li>1.7<ul>
<li>Updated FFmpeg</li>
<li>Fixed error with mkv for codecs without codec private data and the first packet doesn't belong to them</li>
@ -85,7 +103,7 @@ FFmpegSource Documentation
<h2>Usage</h2>
<p>
<b>FFmpegSource(string source, int vtrack = -1, int atrack = -2, string timecodes, bool vcache = true, string vcachefile, string acachefile, string pp, int ppquality = 6, int seekmode = 1)</b><br />
<b>FFmpegSource(string source, int vtrack = -1, int atrack = -2, string timecodes, bool vcache = true, string vcachefile, string acachefile, int accompression = -1, string pp, int ppquality = 6, int seekmode = 1)</b><br />
</p>
<p>
@ -97,7 +115,6 @@ Note that the audio cache will always be created when opening files with audio a
Separate postprocessing which also seems to include a few simple deinterlacers
</p>
<p>
<b>source:</b>
Source file.
@ -116,7 +133,7 @@ Separate postprocessing which also seems to include a few simple deinterlacers
<p>
<b>vcache:</b>
Write video indexing information to a file for later use.
Write video indexing information to a file for later use. This setting does not control if The video index is loaded which it always is if it exists.
</p>
<p>
@ -124,6 +141,11 @@ Separate postprocessing which also seems to include a few simple deinterlacers
Specifies the file to store the index information or raw audio in, if nothing is specified (source).ffv(tracknumber)cache is used for video and (source).ffa(tracknumber)cache for audio.
</p>
<p>
<b>accompression</b>
Audio cache compression, -1 means raw audio and 0-8 uses FLAC with that compression level.
</p>
<p>
<b>pp:</b>
See the table below for a full description, an empty string means no processing. It is recommended to avoid the autoq option since it's currently unknown what effect it will have on the processing.

View File

@ -1,19 +1,5 @@
#include "ffmpegsource.h"
int GetPPCPUFlags(IScriptEnvironment *Env) {
int Flags = 0;
long CPUFlags = Env->GetCPUFlags();
if (CPUFlags & CPUF_MMX)
CPUFlags |= PP_CPU_CAPS_MMX;
if (CPUFlags & CPUF_INTEGER_SSE)
CPUFlags |= PP_CPU_CAPS_MMX2;
if (CPUFlags & CPUF_3DNOW)
CPUFlags |= PP_CPU_CAPS_3DNOW;
return Flags;
}
FFPP::FFPP(PClip AChild, const char *APPString, int AQuality, IScriptEnvironment *Env) : GenericVideoFilter(AChild) {
if (!strcmp(APPString, ""))
Env->ThrowError("FFPP: PP argument is empty");

29
FFmpegSource/ffshared.cpp Normal file
View File

@ -0,0 +1,29 @@
#include "ffmpegsource.h"
int GetPPCPUFlags(IScriptEnvironment *Env) {
int Flags = 0;
long CPUFlags = Env->GetCPUFlags();
if (CPUFlags & CPUF_MMX)
CPUFlags |= PP_CPU_CAPS_MMX;
if (CPUFlags & CPUF_INTEGER_SSE)
CPUFlags |= PP_CPU_CAPS_MMX2;
if (CPUFlags & CPUF_3DNOW)
CPUFlags |= PP_CPU_CAPS_3DNOW;
return Flags;
}
int GetSWSCPUFlags(IScriptEnvironment *Env) {
int Flags = 0;
long CPUFlags = Env->GetCPUFlags();
if (CPUFlags & CPUF_MMX)
CPUFlags |= SWS_CPU_CAPS_MMX;
if (CPUFlags & CPUF_INTEGER_SSE)
CPUFlags |= SWS_CPU_CAPS_MMX2;
if (CPUFlags & CPUF_3DNOW)
CPUFlags |= SWS_CPU_CAPS_3DNOW;
return Flags;
}

View File

@ -1,238 +0,0 @@
CodecID MatroskaToFFCodecID(TrackInfo *TI) {
char *Codec = TI->CodecID;
/* Video Codecs */
if (!strcmp(Codec, "V_MS/VFW/FOURCC")) {
// fourcc list from ffdshow
switch (((BITMAPINFOHEADER *)TI->CodecPrivate)->biCompression) {
case MAKEFOURCC('F', 'F', 'D', 'S'):
case MAKEFOURCC('F', 'V', 'F', 'W'):
case MAKEFOURCC('X', 'V', 'I', 'D'):
case MAKEFOURCC('D', 'I', 'V', 'X'):
case MAKEFOURCC('D', 'X', '5', '0'):
case MAKEFOURCC('M', 'P', '4', 'V'):
case MAKEFOURCC('3', 'I', 'V', 'X'):
case MAKEFOURCC('W', 'V', '1', 'F'):
case MAKEFOURCC('F', 'M', 'P', '4'):
case MAKEFOURCC('S', 'M', 'P', '4'):
return CODEC_ID_MPEG4;
case MAKEFOURCC('D', 'I', 'V', '3'):
case MAKEFOURCC('D', 'V', 'X', '3'):
case MAKEFOURCC('M', 'P', '4', '3'):
return CODEC_ID_MSMPEG4V3;
case MAKEFOURCC('M', 'P', '4', '2'):
return CODEC_ID_MSMPEG4V2;
case MAKEFOURCC('M', 'P', '4', '1'):
return CODEC_ID_MSMPEG4V1;
case MAKEFOURCC('W', 'M', 'V', '1'):
return CODEC_ID_WMV1;
case MAKEFOURCC('W', 'M', 'V', '2'):
return CODEC_ID_WMV2;
case MAKEFOURCC('W', 'M', 'V', '3'):
return CODEC_ID_WMV3;
/*
case MAKEFOURCC('M', 'S', 'S', '1'):
case MAKEFOURCC('M', 'S', 'S', '2'):
case MAKEFOURCC('W', 'V', 'P', '2'):
case MAKEFOURCC('W', 'M', 'V', 'P'):
return CODEC_ID_WMV9_LIB;
*/
case MAKEFOURCC('W', 'V', 'C', '1'):
return CODEC_ID_VC1;
case MAKEFOURCC('V', 'P', '5', '0'):
return CODEC_ID_VP5;
case MAKEFOURCC('V', 'P', '6', '0'):
case MAKEFOURCC('V', 'P', '6', '1'):
case MAKEFOURCC('V', 'P', '6', '2'):
return CODEC_ID_VP6;
case MAKEFOURCC('V', 'P', '6', 'F'):
case MAKEFOURCC('F', 'L', 'V', '4'):
return CODEC_ID_VP6F;
case MAKEFOURCC('C', 'A', 'V', 'S'):
return CODEC_ID_CAVS;
case MAKEFOURCC('M', 'P', 'G', '1'):
case MAKEFOURCC('M', 'P', 'E', 'G'):
return CODEC_ID_MPEG2VIDEO; // not a typo
case MAKEFOURCC('M', 'P', 'G', '2'):
case MAKEFOURCC('E', 'M', '2', 'V'):
case MAKEFOURCC('M', 'M', 'E', 'S'):
return CODEC_ID_MPEG2VIDEO;
case MAKEFOURCC('H', '2', '6', '3'):
case MAKEFOURCC('S', '2', '6', '3'):
case MAKEFOURCC('L', '2', '6', '3'):
case MAKEFOURCC('M', '2', '6', '3'):
case MAKEFOURCC('U', '2', '6', '3'):
case MAKEFOURCC('X', '2', '6', '3'):
return CODEC_ID_H263;
case MAKEFOURCC('H', '2', '6', '4'):
case MAKEFOURCC('X', '2', '6', '4'):
case MAKEFOURCC('V', 'S', 'S', 'H'):
case MAKEFOURCC('D', 'A', 'V', 'C'):
case MAKEFOURCC('P', 'A', 'V', 'C'):
case MAKEFOURCC('A', 'V', 'C', '1'):
return CODEC_ID_H264;
case MAKEFOURCC('M', 'J', 'P', 'G'):
case MAKEFOURCC('L', 'J', 'P', 'G'):
case MAKEFOURCC('M', 'J', 'L', 'S'):
case MAKEFOURCC('J', 'P', 'E', 'G'): // questionable fourcc?
case MAKEFOURCC('A', 'V', 'R', 'N'):
case MAKEFOURCC('M', 'J', 'P', 'A'):
return CODEC_ID_MJPEG;
case MAKEFOURCC('D', 'V', 'S', 'D'):
case MAKEFOURCC('D', 'V', '2', '5'):
case MAKEFOURCC('D', 'V', '5', '0'):
case MAKEFOURCC('C', 'D', 'V', 'C'):
case MAKEFOURCC('C', 'D', 'V', '5'):
case MAKEFOURCC('D', 'V', 'I', 'S'):
case MAKEFOURCC('P', 'D', 'V', 'C'):
return CODEC_ID_DVVIDEO;
case MAKEFOURCC('H', 'F', 'Y', 'U'):
case MAKEFOURCC('F', 'F', 'V', 'H'):
return CODEC_ID_HUFFYUV;
case MAKEFOURCC('C', 'Y', 'U', 'V'):
return CODEC_ID_CYUV;
case MAKEFOURCC('A', 'S', 'V', '1'):
return CODEC_ID_ASV1;
case MAKEFOURCC('A', 'S', 'V', '2'):
return CODEC_ID_ASV2;
case MAKEFOURCC('V', 'C', 'R', '1'):
return CODEC_ID_VCR1;
case MAKEFOURCC('T', 'H', 'E', 'O'):
return CODEC_ID_THEORA;
case MAKEFOURCC('S', 'V', 'Q', '1'):
return CODEC_ID_SVQ1;
case MAKEFOURCC('S', 'V', 'Q', '3'):
return CODEC_ID_SVQ3;
case MAKEFOURCC('R', 'P', 'Z', 'A'):
return CODEC_ID_RPZA;
case MAKEFOURCC('F', 'F', 'V', '1'):
return CODEC_ID_FFV1;
case MAKEFOURCC('V', 'P', '3', '1'):
return CODEC_ID_VP3;
case MAKEFOURCC('R', 'L', 'E', '8'):
return CODEC_ID_MSRLE;
case MAKEFOURCC('M', 'S', 'Z', 'H'):
return CODEC_ID_MSZH;
case MAKEFOURCC('Z', 'L', 'I', 'B'):
return CODEC_ID_FLV1;
case MAKEFOURCC('F', 'L', 'V', '1'):
return CODEC_ID_ZLIB;
/*
case MAKEFOURCC('P', 'N', 'G', '1'):
return CODEC_ID_COREPNG;
*/
case MAKEFOURCC('M', 'P', 'N', 'G'):
return CODEC_ID_PNG;
/*
case MAKEFOURCC('A', 'V', 'I', 'S'):
return CODEC_ID_AVISYNTH;
*/
case MAKEFOURCC('C', 'R', 'A', 'M'):
return CODEC_ID_MSVIDEO1;
case MAKEFOURCC('R', 'T', '2', '1'):
return CODEC_ID_INDEO2;
case MAKEFOURCC('I', 'V', '3', '2'):
case MAKEFOURCC('I', 'V', '3', '1'):
return CODEC_ID_INDEO3;
case MAKEFOURCC('C', 'V', 'I', 'D'):
return CODEC_ID_CINEPAK;
case MAKEFOURCC('R', 'V', '1', '0'):
return CODEC_ID_RV10;
case MAKEFOURCC('R', 'V', '2', '0'):
return CODEC_ID_RV20;
case MAKEFOURCC('8', 'B', 'P', 'S'):
return CODEC_ID_8BPS;
case MAKEFOURCC('Q', 'R', 'L', 'E'):
return CODEC_ID_QTRLE;
case MAKEFOURCC('D', 'U', 'C', 'K'):
return CODEC_ID_TRUEMOTION1;
case MAKEFOURCC('T', 'M', '2', '0'):
return CODEC_ID_TRUEMOTION2;
case MAKEFOURCC('T', 'S', 'C', 'C'):
return CODEC_ID_TSCC;
case MAKEFOURCC('S', 'N', 'O', 'W'):
return CODEC_ID_SNOW;
case MAKEFOURCC('Q', 'P', 'E', 'G'):
case MAKEFOURCC('Q', '1', '_', '0'):
case MAKEFOURCC('Q', '1', '_', '1'):
return CODEC_ID_QPEG;
case MAKEFOURCC('H', '2', '6', '1'):
case MAKEFOURCC('M', '2', '6', '1'):
return CODEC_ID_H261;
case MAKEFOURCC('L', 'O', 'C', 'O'):
return CODEC_ID_LOCO;
case MAKEFOURCC('W', 'N', 'V', '1'):
return CODEC_ID_WNV1;
case MAKEFOURCC('C', 'S', 'C', 'D'):
return CODEC_ID_CSCD;
case MAKEFOURCC('Z', 'M', 'B', 'V'):
return CODEC_ID_ZMBV;
case MAKEFOURCC('U', 'L', 'T', 'I'):
return CODEC_ID_ULTI;
case MAKEFOURCC('V', 'I', 'X', 'L'):
return CODEC_ID_VIXL;
case MAKEFOURCC('A', 'A', 'S', 'C'):
return CODEC_ID_AASC;
case MAKEFOURCC('F', 'P', 'S', '1'):
return CODEC_ID_FRAPS;
default:
return CODEC_ID_NONE;
}
} else if (!strcmp(Codec, "V_MPEG4/ISO/AVC"))
return CODEC_ID_H264;
else if (!strcmp(Codec, "V_MPEG4/ISO/ASP"))
return CODEC_ID_MPEG4;
else if (!strcmp(Codec, "V_MPEG2"))
return CODEC_ID_MPEG2VIDEO;
else if (!strcmp(Codec, "V_MPEG1"))
return CODEC_ID_MPEG2VIDEO; // still not a typo
else if (!strcmp(Codec, "V_SNOW"))
return CODEC_ID_SNOW;
else if (!strcmp(Codec, "V_THEORA"))
return CODEC_ID_THEORA;
else if (!strncmp(Codec, "V_REAL/RV", 9)) {
switch (Codec[9]) {
case '1':
return CODEC_ID_RV10;
case '2':
return CODEC_ID_RV20;
case '3':
return CODEC_ID_RV30;
case '4':
return CODEC_ID_RV40;
default:
return CODEC_ID_NONE;
}
/* Audio Codecs */
} else if (!strcmp(Codec, "A_AC3"))
return CODEC_ID_AC3;
else if (!strcmp(Codec, "A_MPEG/L3"))
return CODEC_ID_MP3;
else if (!strcmp(Codec, "A_MPEG/L2"))
return CODEC_ID_MP2;
else if (!strcmp(Codec, "A_MPEG/L1"))
return CODEC_ID_MP2; // correct?
else if (!strcmp(Codec, "A_DTS"))
return CODEC_ID_DTS;
/*
else if (!strcmp(Codec, "A_PCM/INT/LIT"))
return CODEC_ID_PCM_S16LE;
else if (!strcmp(Codec, "A_PCM/FLOAT/IEEE"))
return CODEC_ID_PCM_S16LE;
*/
else if (!strcmp(Codec, "A_TTA1"))
return CODEC_ID_TTA;
else if (!strcmp(Codec, "A_WAVPACK4"))
return CODEC_ID_WAVPACK;
else if (!strcmp(Codec, "A_VORBIS"))
return CODEC_ID_VORBIS;
else if (!strncmp(Codec, "A_REAL/", 7)) {
// not supported
return CODEC_ID_NONE;
} else if (!strncmp(Codec, "A_AAC", 5))
return CODEC_ID_AAC;
else if (!strcmp(Codec, "A_MS/ACM")) {
// not supported
return CODEC_ID_NONE;
} else
return CODEC_ID_NONE;
}

View File

@ -0,0 +1,240 @@
#include "ffmpegsource.h"
CodecID MatroskaToFFCodecID(TrackInfo *TI) {
char *Codec = TI->CodecID;
/* Video Codecs */
if (!strcmp(Codec, "V_MS/VFW/FOURCC")) {
// fourcc list from ffdshow
switch (((BITMAPINFOHEADER *)TI->CodecPrivate)->biCompression) {
case MAKEFOURCC('F', 'F', 'D', 'S'):
case MAKEFOURCC('F', 'V', 'F', 'W'):
case MAKEFOURCC('X', 'V', 'I', 'D'):
case MAKEFOURCC('D', 'I', 'V', 'X'):
case MAKEFOURCC('D', 'X', '5', '0'):
case MAKEFOURCC('M', 'P', '4', 'V'):
case MAKEFOURCC('3', 'I', 'V', 'X'):
case MAKEFOURCC('W', 'V', '1', 'F'):
case MAKEFOURCC('F', 'M', 'P', '4'):
case MAKEFOURCC('S', 'M', 'P', '4'):
return CODEC_ID_MPEG4;
case MAKEFOURCC('D', 'I', 'V', '3'):
case MAKEFOURCC('D', 'V', 'X', '3'):
case MAKEFOURCC('M', 'P', '4', '3'):
return CODEC_ID_MSMPEG4V3;
case MAKEFOURCC('M', 'P', '4', '2'):
return CODEC_ID_MSMPEG4V2;
case MAKEFOURCC('M', 'P', '4', '1'):
return CODEC_ID_MSMPEG4V1;
case MAKEFOURCC('W', 'M', 'V', '1'):
return CODEC_ID_WMV1;
case MAKEFOURCC('W', 'M', 'V', '2'):
return CODEC_ID_WMV2;
case MAKEFOURCC('W', 'M', 'V', '3'):
return CODEC_ID_WMV3;
/*
case MAKEFOURCC('M', 'S', 'S', '1'):
case MAKEFOURCC('M', 'S', 'S', '2'):
case MAKEFOURCC('W', 'V', 'P', '2'):
case MAKEFOURCC('W', 'M', 'V', 'P'):
return CODEC_ID_WMV9_LIB;
*/
case MAKEFOURCC('W', 'V', 'C', '1'):
return CODEC_ID_VC1;
case MAKEFOURCC('V', 'P', '5', '0'):
return CODEC_ID_VP5;
case MAKEFOURCC('V', 'P', '6', '0'):
case MAKEFOURCC('V', 'P', '6', '1'):
case MAKEFOURCC('V', 'P', '6', '2'):
return CODEC_ID_VP6;
case MAKEFOURCC('V', 'P', '6', 'F'):
case MAKEFOURCC('F', 'L', 'V', '4'):
return CODEC_ID_VP6F;
case MAKEFOURCC('C', 'A', 'V', 'S'):
return CODEC_ID_CAVS;
case MAKEFOURCC('M', 'P', 'G', '1'):
case MAKEFOURCC('M', 'P', 'E', 'G'):
return CODEC_ID_MPEG2VIDEO; // not a typo
case MAKEFOURCC('M', 'P', 'G', '2'):
case MAKEFOURCC('E', 'M', '2', 'V'):
case MAKEFOURCC('M', 'M', 'E', 'S'):
return CODEC_ID_MPEG2VIDEO;
case MAKEFOURCC('H', '2', '6', '3'):
case MAKEFOURCC('S', '2', '6', '3'):
case MAKEFOURCC('L', '2', '6', '3'):
case MAKEFOURCC('M', '2', '6', '3'):
case MAKEFOURCC('U', '2', '6', '3'):
case MAKEFOURCC('X', '2', '6', '3'):
return CODEC_ID_H263;
case MAKEFOURCC('H', '2', '6', '4'):
case MAKEFOURCC('X', '2', '6', '4'):
case MAKEFOURCC('V', 'S', 'S', 'H'):
case MAKEFOURCC('D', 'A', 'V', 'C'):
case MAKEFOURCC('P', 'A', 'V', 'C'):
case MAKEFOURCC('A', 'V', 'C', '1'):
return CODEC_ID_H264;
case MAKEFOURCC('M', 'J', 'P', 'G'):
case MAKEFOURCC('L', 'J', 'P', 'G'):
case MAKEFOURCC('M', 'J', 'L', 'S'):
case MAKEFOURCC('J', 'P', 'E', 'G'): // questionable fourcc?
case MAKEFOURCC('A', 'V', 'R', 'N'):
case MAKEFOURCC('M', 'J', 'P', 'A'):
return CODEC_ID_MJPEG;
case MAKEFOURCC('D', 'V', 'S', 'D'):
case MAKEFOURCC('D', 'V', '2', '5'):
case MAKEFOURCC('D', 'V', '5', '0'):
case MAKEFOURCC('C', 'D', 'V', 'C'):
case MAKEFOURCC('C', 'D', 'V', '5'):
case MAKEFOURCC('D', 'V', 'I', 'S'):
case MAKEFOURCC('P', 'D', 'V', 'C'):
return CODEC_ID_DVVIDEO;
case MAKEFOURCC('H', 'F', 'Y', 'U'):
case MAKEFOURCC('F', 'F', 'V', 'H'):
return CODEC_ID_HUFFYUV;
case MAKEFOURCC('C', 'Y', 'U', 'V'):
return CODEC_ID_CYUV;
case MAKEFOURCC('A', 'S', 'V', '1'):
return CODEC_ID_ASV1;
case MAKEFOURCC('A', 'S', 'V', '2'):
return CODEC_ID_ASV2;
case MAKEFOURCC('V', 'C', 'R', '1'):
return CODEC_ID_VCR1;
case MAKEFOURCC('T', 'H', 'E', 'O'):
return CODEC_ID_THEORA;
case MAKEFOURCC('S', 'V', 'Q', '1'):
return CODEC_ID_SVQ1;
case MAKEFOURCC('S', 'V', 'Q', '3'):
return CODEC_ID_SVQ3;
case MAKEFOURCC('R', 'P', 'Z', 'A'):
return CODEC_ID_RPZA;
case MAKEFOURCC('F', 'F', 'V', '1'):
return CODEC_ID_FFV1;
case MAKEFOURCC('V', 'P', '3', '1'):
return CODEC_ID_VP3;
case MAKEFOURCC('R', 'L', 'E', '8'):
return CODEC_ID_MSRLE;
case MAKEFOURCC('M', 'S', 'Z', 'H'):
return CODEC_ID_MSZH;
case MAKEFOURCC('Z', 'L', 'I', 'B'):
return CODEC_ID_FLV1;
case MAKEFOURCC('F', 'L', 'V', '1'):
return CODEC_ID_ZLIB;
/*
case MAKEFOURCC('P', 'N', 'G', '1'):
return CODEC_ID_COREPNG;
*/
case MAKEFOURCC('M', 'P', 'N', 'G'):
return CODEC_ID_PNG;
/*
case MAKEFOURCC('A', 'V', 'I', 'S'):
return CODEC_ID_AVISYNTH;
*/
case MAKEFOURCC('C', 'R', 'A', 'M'):
return CODEC_ID_MSVIDEO1;
case MAKEFOURCC('R', 'T', '2', '1'):
return CODEC_ID_INDEO2;
case MAKEFOURCC('I', 'V', '3', '2'):
case MAKEFOURCC('I', 'V', '3', '1'):
return CODEC_ID_INDEO3;
case MAKEFOURCC('C', 'V', 'I', 'D'):
return CODEC_ID_CINEPAK;
case MAKEFOURCC('R', 'V', '1', '0'):
return CODEC_ID_RV10;
case MAKEFOURCC('R', 'V', '2', '0'):
return CODEC_ID_RV20;
case MAKEFOURCC('8', 'B', 'P', 'S'):
return CODEC_ID_8BPS;
case MAKEFOURCC('Q', 'R', 'L', 'E'):
return CODEC_ID_QTRLE;
case MAKEFOURCC('D', 'U', 'C', 'K'):
return CODEC_ID_TRUEMOTION1;
case MAKEFOURCC('T', 'M', '2', '0'):
return CODEC_ID_TRUEMOTION2;
case MAKEFOURCC('T', 'S', 'C', 'C'):
return CODEC_ID_TSCC;
case MAKEFOURCC('S', 'N', 'O', 'W'):
return CODEC_ID_SNOW;
case MAKEFOURCC('Q', 'P', 'E', 'G'):
case MAKEFOURCC('Q', '1', '_', '0'):
case MAKEFOURCC('Q', '1', '_', '1'):
return CODEC_ID_QPEG;
case MAKEFOURCC('H', '2', '6', '1'):
case MAKEFOURCC('M', '2', '6', '1'):
return CODEC_ID_H261;
case MAKEFOURCC('L', 'O', 'C', 'O'):
return CODEC_ID_LOCO;
case MAKEFOURCC('W', 'N', 'V', '1'):
return CODEC_ID_WNV1;
case MAKEFOURCC('C', 'S', 'C', 'D'):
return CODEC_ID_CSCD;
case MAKEFOURCC('Z', 'M', 'B', 'V'):
return CODEC_ID_ZMBV;
case MAKEFOURCC('U', 'L', 'T', 'I'):
return CODEC_ID_ULTI;
case MAKEFOURCC('V', 'I', 'X', 'L'):
return CODEC_ID_VIXL;
case MAKEFOURCC('A', 'A', 'S', 'C'):
return CODEC_ID_AASC;
case MAKEFOURCC('F', 'P', 'S', '1'):
return CODEC_ID_FRAPS;
default:
return CODEC_ID_NONE;
}
} else if (!strcmp(Codec, "V_MPEG4/ISO/AVC"))
return CODEC_ID_H264;
else if (!strcmp(Codec, "V_MPEG4/ISO/ASP"))
return CODEC_ID_MPEG4;
else if (!strcmp(Codec, "V_MPEG2"))
return CODEC_ID_MPEG2VIDEO;
else if (!strcmp(Codec, "V_MPEG1"))
return CODEC_ID_MPEG2VIDEO; // still not a typo
else if (!strcmp(Codec, "V_SNOW"))
return CODEC_ID_SNOW;
else if (!strcmp(Codec, "V_THEORA"))
return CODEC_ID_THEORA;
else if (!strncmp(Codec, "V_REAL/RV", 9)) {
switch (Codec[9]) {
case '1':
return CODEC_ID_RV10;
case '2':
return CODEC_ID_RV20;
case '3':
return CODEC_ID_RV30;
case '4':
return CODEC_ID_RV40;
default:
return CODEC_ID_NONE;
}
/* Audio Codecs */
} else if (!strcmp(Codec, "A_AC3"))
return CODEC_ID_AC3;
else if (!strcmp(Codec, "A_MPEG/L3"))
return CODEC_ID_MP3;
else if (!strcmp(Codec, "A_MPEG/L2"))
return CODEC_ID_MP2;
else if (!strcmp(Codec, "A_MPEG/L1"))
return CODEC_ID_MP2; // correct?
else if (!strcmp(Codec, "A_DTS"))
return CODEC_ID_DTS;
/*
else if (!strcmp(Codec, "A_PCM/INT/LIT"))
return CODEC_ID_PCM_S16LE;
else if (!strcmp(Codec, "A_PCM/FLOAT/IEEE"))
return CODEC_ID_PCM_S16LE;
*/
else if (!strcmp(Codec, "A_TTA1"))
return CODEC_ID_TTA;
else if (!strcmp(Codec, "A_WAVPACK4"))
return CODEC_ID_WAVPACK;
else if (!strcmp(Codec, "A_VORBIS"))
return CODEC_ID_VORBIS;
else if (!strncmp(Codec, "A_REAL/", 7)) {
// not supported
return CODEC_ID_NONE;
} else if (!strncmp(Codec, "A_AAC", 5))
return CODEC_ID_AAC;
else if (!strcmp(Codec, "A_MS/ACM")) {
// not supported
return CODEC_ID_NONE;
} else
return CODEC_ID_NONE;
}