diff --git a/FFmpegSource/ffaudiobase.cpp b/FFmpegSource/ffaudiobase.cpp new file mode 100644 index 000000000..57f6bbea8 --- /dev/null +++ b/FFmpegSource/ffaudiobase.cpp @@ -0,0 +1,92 @@ +// Copyright (c) 2007 Fredrik Mellbin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "ffmpegsource.h" + +FFAudioBase::FFAudioBase() { + memset(&VI, 0, sizeof(VI)); + DecodingBuffer = new uint8_t[AVCODEC_MAX_AUDIO_FRAME_SIZE]; +}; + +FFAudioBase::~FFAudioBase() { + delete[] DecodingBuffer; +}; + +size_t FFAudioBase::FindClosestAudioKeyFrame(int64_t Sample) { + for (size_t i = 0; i < SI.size(); i++) { + if (SI[i].SampleStart == Sample && SI[i].KeyFrame) + return i; + else if (SI[i].SampleStart > Sample && SI[i].KeyFrame) + return i - 1; + } + return SI.size() - 1; +} + +bool FFAudioBase::LoadSampleInfoFromFile(const char *AAudioCacheFile, const char *ASource, int AAudioTrack) { + char DefaultCacheFilename[1024]; + sprintf(DefaultCacheFilename, "%s.ffas%dcache", ASource, AAudioTrack); + if (!strcmp(AAudioCacheFile, "")) + AAudioCacheFile = DefaultCacheFilename; + + FILE *CacheFile = fopen(AAudioCacheFile, "r"); + if (!CacheFile) + return false; + + size_t AudioBlocks = 0; + + if (fscanf(CacheFile, "%lld %u\r\n", &VI.num_audio_samples, &AudioBlocks) <= 0 || VI.num_audio_samples <= 0 || AudioBlocks <= 0) { + VI.num_audio_samples = 0; + fclose(CacheFile); + return false; + } + + for (size_t i = 0; i < AudioBlocks; i++) { + int64_t SampleStart; + int64_t FilePos; + unsigned int FrameSize; + int Flags; + + fscanf(CacheFile, "%lld %lld %u %d\r\n", &SampleStart, &FilePos, &FrameSize, &Flags); + SI.push_back(SampleInfo(SampleStart, FilePos, FrameSize, (Flags & 1) != 0)); + } + + fclose(CacheFile); + return true; +} + +bool FFAudioBase::SaveSampleInfoToFile(const char *AAudioCacheFile, const char *ASource, int AAudioTrack) { + char DefaultCacheFilename[1024]; + sprintf(DefaultCacheFilename, "%s.ffas%dcache", ASource, AAudioTrack); + if (!strcmp(AAudioCacheFile, "")) + AAudioCacheFile = DefaultCacheFilename; + + FILE *CacheFile = fopen(AAudioCacheFile, "wb"); + if (!CacheFile) + return false; + + fprintf(CacheFile, "%lld %u\r\n", VI.num_audio_samples, SI.size()); + for (size_t i = 0; i < SI.size(); i++) { + int Flags = SI[i].KeyFrame ? 1 : 0; + fprintf(CacheFile, "%lld %lld %u %d\r\n", SI[i].SampleStart, SI[i].FilePos, SI[i].FrameSize, Flags); + } + + fclose(CacheFile); + return true; +} \ No newline at end of file diff --git a/FFmpegSource/ffbase.cpp b/FFmpegSource/ffbase.cpp index 07e117ebe..67c296caf 100644 --- a/FFmpegSource/ffbase.cpp +++ b/FFmpegSource/ffbase.cpp @@ -21,8 +21,8 @@ #include "ffmpegsource.h" int FFBase::FrameFromDTS(int64_t ADTS) { - for (int i = 0; i < (int)FrameToDTS.size(); i++) - if (FrameToDTS[i].DTS == ADTS) + for (int i = 0; i < (int)Frames.size(); i++) + if (Frames[i].DTS == ADTS) return i; return -1; } @@ -30,8 +30,8 @@ int FFBase::FrameFromDTS(int64_t ADTS) { int FFBase::ClosestFrameFromDTS(int64_t ADTS) { int Frame = 0; int64_t BestDiff = 0xFFFFFFFFFFFFFFLL; - for (int i = 0; i < (int)FrameToDTS.size(); i++) { - int64_t CurrentDiff = FFABS(FrameToDTS[i].DTS - ADTS); + for (int i = 0; i < (int)Frames.size(); i++) { + int64_t CurrentDiff = FFABS(Frames[i].DTS - ADTS); if (CurrentDiff < BestDiff) { BestDiff = CurrentDiff; Frame = i; @@ -42,7 +42,7 @@ int FFBase::ClosestFrameFromDTS(int64_t ADTS) { int FFBase::FindClosestKeyFrame(int AFrame) { for (int i = AFrame; i > 0; i--) - if (FrameToDTS[i].KeyFrame) + if (Frames[i].KeyFrame) return i; return 0; } @@ -67,7 +67,7 @@ bool FFBase::LoadFrameInfoFromFile(const char *AVideoCacheFile, const char *ASou int64_t DTSTemp; int KFTemp; fscanf(CacheFile, "%lld %d\r\n", &DTSTemp, &KFTemp); - FrameToDTS.push_back(FrameInfo(DTSTemp, KFTemp != 0)); + Frames.push_back(FrameInfo(DTSTemp, KFTemp != 0)); } fclose(CacheFile); @@ -86,7 +86,7 @@ bool FFBase::SaveFrameInfoToFile(const char *AVideoCacheFile, const char *ASourc fprintf(CacheFile, "%d\r\n", VI.num_frames); for (int i = 0; i < VI.num_frames; i++) - fprintf(CacheFile, "%lld %d\r\n", FrameToDTS[i].DTS, (int)(FrameToDTS[i].KeyFrame ? 1 : 0)); + fprintf(CacheFile, "%lld %d\r\n", Frames[i].DTS, (int)(Frames[i].KeyFrame ? 1 : 0)); fclose(CacheFile); return true; @@ -102,7 +102,7 @@ bool FFBase::SaveTimecodesToFile(const char *ATimecodeFile, int64_t ScaleD, int6 std::set Timecodes; for (int i = 0; i < VI.num_frames; i++) - Timecodes.insert(FrameToDTS[i].DTS); + Timecodes.insert(Frames[i].DTS); fprintf(TimecodeFile, "# timecode format v2\r\n"); @@ -364,6 +364,15 @@ void FFBase::SetOutputFormat(int ACurrentFormat, IScriptEnvironment *Env) { ConvertToFormat = BestFormat; SWS = sws_getContext(VI.width, VI.height, ACurrentFormat, VI.width, VI.height, ConvertToFormat, GetSWSCPUFlags(Env) | SWS_BICUBIC, NULL, NULL, NULL); } + + if (BestFormat == PIX_FMT_YUVJ420P || BestFormat == PIX_FMT_YUV420P) { + VI.height -= VI.height & 1; + VI.width -= VI.width & 1; + } + + if (BestFormat == PIX_FMT_YUYV422) { + VI.width -= VI.width & 1; + } } PVideoFrame FFBase::OutputFrame(AVFrame *AFrame, IScriptEnvironment *Env) { @@ -407,7 +416,7 @@ PVideoFrame FFBase::OutputFrame(AVFrame *AFrame, IScriptEnvironment *Env) { void FFBase::GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment* Env) { if (AudioCacheType == acRaw) { _fseeki64(RawAudioCache, VI.BytesFromAudioSamples(Start), SEEK_SET); - fread(Buf, 1, VI.BytesFromAudioSamples(Count), RawAudioCache); + fread(Buf, 1, (size_t)VI.BytesFromAudioSamples(Count), RawAudioCache); #ifdef FLAC_CACHE } else if (AudioCacheType == acFLAC) { FCCount = Count; diff --git a/FFmpegSource/ffmatroskaaudiosource.cpp b/FFmpegSource/ffmatroskaaudiosource.cpp new file mode 100644 index 000000000..5b08ed5ca --- /dev/null +++ b/FFmpegSource/ffmatroskaaudiosource.cpp @@ -0,0 +1,297 @@ +// Copyright (c) 2007 Fredrik Mellbin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "ffmpegsource.h" + +int FFMatroskaAudioSource::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; +} + +FFMatroskaAudioSource::FFMatroskaAudioSource(const char *ASource, int AAudioTrack, const char *AAudioCache, IScriptEnvironment *Env) { + int AudioTrack; + AudioCodecContext = NULL; + AVCodec *AudioCodec = NULL; + TrackInfo *VideoTI = NULL; + BufferSize = 0; + Buffer = 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); + } + + AudioTrack = GetTrackIndex(AAudioTrack, TT_AUDIO, Env); + mkv_SetTrackMask(MF, ~(1 << AudioTrack)); + + 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) { + uint64_t StartTime, EndTime, FilePos; + unsigned int Track, FrameFlags, FrameSize; + mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags); + + 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); + avcodec_flush_buffers(AudioCodecContext); + } + + 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"); + } + + //load audio cache + bool ACacheIsValid = LoadSampleInfoFromFile(AAudioCache, ASource, AudioTrack); + + // Needs to be indexed? + if (!ACacheIsValid) { + uint64_t StartTime, EndTime, FilePos; + unsigned int Track, FrameFlags, FrameSize; + + while (mkv_ReadFrame(MF, 0, &Track, &StartTime, &EndTime, &FilePos, &FrameSize, &FrameFlags) == 0) { + SI.push_back(SampleInfo(VI.num_audio_samples, FilePos, FrameSize, (FrameFlags & FRAME_KF) != 0)); + + if (AudioCodecContext->frame_size > 0) { + VI.num_audio_samples += AudioCodecContext->frame_size; + } else { + 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"); + + if (Ret > 0) { + int DecodedSamples = (int)VI.AudioSamplesFromBytes(TempOutputBufSize); + Size -= Ret; + Data += Ret; + VI.num_audio_samples += DecodedSamples; + } + } + } + } + + mkv_Seek(MF, 0, MKVF_SEEK_TO_PREV_KEYFRAME); + avcodec_flush_buffers(AudioCodecContext); + + if (!SaveSampleInfoToFile(AAudioCache, ASource, AudioTrack)) + Env->ThrowError("FFmpegSource: Failed to save audio cache index"); + } + + if (VI.num_audio_samples == 0) + Env->ThrowError("FFmpegSource: Audio track contains no samples"); +} + +FFMatroskaAudioSource::~FFMatroskaAudioSource() { + free(Buffer); + mkv_Close(MF); + fclose(ST.fp); + if (AudioCodecContext) + avcodec_close(AudioCodecContext); + av_free(AudioCodecContext); +} + +int FFMatroskaAudioSource::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; +} + +void __stdcall FFMatroskaAudioSource::GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment *Env) { + size_t CurrentAudioBlock = FFMAX((int64_t)FindClosestAudioKeyFrame(Start) - 10, (int64_t)0); + avcodec_flush_buffers(AudioCodecContext); + + memset(Buf, 0, VI.BytesFromAudioSamples(Count)); + + uint8_t *DstBuf = (uint8_t *)Buf; + int64_t RemainingSamples = Count; + int64_t DecodeCount; + + do { + int64_t DecodeStart = SI[CurrentAudioBlock].SampleStart; + int Ret = DecodeNextAudioBlock(DecodingBuffer, &DecodeCount, SI[CurrentAudioBlock].FilePos, SI[CurrentAudioBlock].FrameSize, Env); + if (Ret < 0) + Env->ThrowError("Bleh, bad audio decoding"); + CurrentAudioBlock++; + + int64_t OffsetBytes = VI.BytesFromAudioSamples(FFMAX(0, Start - DecodeStart)); + int64_t CopyBytes = FFMAX(0, VI.BytesFromAudioSamples(FFMIN(RemainingSamples, DecodeCount - FFMAX(0, Start - DecodeStart)))); + + memcpy(DstBuf, DecodingBuffer + OffsetBytes, CopyBytes); + DstBuf += CopyBytes; + + RemainingSamples -= VI.AudioSamplesFromBytes(CopyBytes); + + } while (RemainingSamples > 0 && CurrentAudioBlock < SI.size()); +} + +int FFMatroskaAudioSource::DecodeNextAudioBlock(uint8_t *ABuf, int64_t *ACount, uint64_t AFilePos, unsigned int AFrameSize, IScriptEnvironment *Env) { + int Ret = -1; + *ACount = 0; + + int FrameSize = ReadFrame(AFilePos, AFrameSize, AudioCS, Env); + uint8_t *Data = Buffer; + int Size = FrameSize; + + while (Size > 0) { + int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE; + Ret = avcodec_decode_audio2(AudioCodecContext, (int16_t *)ABuf, &TempOutputBufSize, Data, Size); + + if (Ret < 0) // throw error or something? + goto Done; + + if (Ret > 0) { + Size -= Ret; + Data += Ret; + ABuf += TempOutputBufSize; + *ACount += VI.AudioSamplesFromBytes(TempOutputBufSize); + } + } + +Done: + return Ret; +} diff --git a/FFmpegSource/ffmatroskasource.cpp b/FFmpegSource/ffmatroskasource.cpp index 536851474..5cfa626f2 100644 --- a/FFmpegSource/ffmatroskasource.cpp +++ b/FFmpegSource/ffmatroskasource.cpp @@ -46,8 +46,9 @@ int FFMatroskaSource::GetTrackIndex(int Index, unsigned char ATrackType, IScript FFMatroskaSource::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) { + int AQuality, IScriptEnvironment* Env, FrameInfoVector *AFrames) { + AFrames = &Frames; CurrentFrame = 0; int VideoTrack; int AudioTrack; @@ -121,8 +122,8 @@ FFMatroskaSource::FFMatroskaSource(const char *ASource, int AVideoTrack, int AAu } VI.image_type = VideoInfo::IT_TFF; - VI.width = VideoTI->AV.Video.PixelWidth; - VI.height = VideoTI->AV.Video.PixelHeight; + VI.width = VideoCodecContext->width; + VI.height = VideoCodecContext->height;; VI.fps_denominator = 1; VI.fps_numerator = 30; @@ -163,7 +164,6 @@ FFMatroskaSource::FFMatroskaSource(const char *ASource, int AVideoTrack, int AAu 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; @@ -224,7 +224,7 @@ FFMatroskaSource::FFMatroskaSource(const char *ASource, int AVideoTrack, int AAu 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)); + Frames.push_back(FrameInfo(StartTime, (FrameFlags & FRAME_KF) != 0)); VI.num_frames++; } else if (Track == AudioTrack && !ACacheIsValid) { int Size = ReadFrame(FilePos, FrameSize, AudioCS, Env); @@ -236,7 +236,7 @@ FFMatroskaSource::FFMatroskaSource(const char *ASource, int AVideoTrack, int AAu if (Ret < 0) Env->ThrowError("FFmpegSource: Audio decoding error"); - int DecodedSamples = VI.AudioSamplesFromBytes(TempOutputBufSize); + int DecodedSamples = (int)VI.AudioSamplesFromBytes(TempOutputBufSize); Size -= Ret; Data += Ret; @@ -274,7 +274,7 @@ FFMatroskaSource::FFMatroskaSource(const char *ASource, int AVideoTrack, int AAu Env->ThrowError("FFmpegSource: Audio track contains no samples"); if (VideoTrack >= 0) - mkv_Seek(MF, FrameToDTS.front().DTS, MKVF_SEEK_TO_PREV_KEYFRAME); + mkv_Seek(MF, Frames.front().DTS, MKVF_SEEK_TO_PREV_KEYFRAME); if (AVCache && !VCacheIsValid) if (!SaveFrameInfoToFile(AVideoCache, ASource, VideoTrack)) @@ -290,8 +290,8 @@ FFMatroskaSource::FFMatroskaSource(const char *ASource, int AVideoTrack, int AAu mkv_SetTrackMask(MF, ~(1 << VideoTrack)); // Calculate the average framerate - if (FrameToDTS.size() >= 2) { - double DTSDiff = (double)(FrameToDTS.back().DTS - FrameToDTS.front().DTS); + if (Frames.size() >= 2) { + double DTSDiff = (double)(Frames.back().DTS - Frames.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; } @@ -398,7 +398,7 @@ PVideoFrame 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); + mkv_Seek(MF, Frames[n].DTS, MKVF_SEEK_TO_PREV_KEYFRAME); avcodec_flush_buffers(VideoCodecContext); HasSeeked = true; } diff --git a/FFmpegSource/ffmpegaudiosource.cpp b/FFmpegSource/ffmpegaudiosource.cpp new file mode 100644 index 000000000..b9816118f --- /dev/null +++ b/FFmpegSource/ffmpegaudiosource.cpp @@ -0,0 +1,222 @@ +// Copyright (c) 2007 Fredrik Mellbin +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#include "ffmpegsource.h" + +int FFmpegAudioSource::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) { + Index = i; + break; + } + + if (Index == -1) + Env->ThrowError("FFmpegSource: No %s track found", (ATrackType == CODEC_TYPE_VIDEO) ? "video" : "audio"); + if (Index <= -2) + return -2; + + if (Index >= (int)FormatContext->nb_streams) + Env->ThrowError("FFmpegSource: Invalid %s track number", (ATrackType == CODEC_TYPE_VIDEO) ? "video" : "audio"); + + if (FormatContext->streams[Index]->codec->codec_type != ATrackType) + Env->ThrowError("FFmpegSource: Selected track is not %s", (ATrackType == CODEC_TYPE_VIDEO) ? "video" : "audio"); + + return Index; +} + +bool FFmpegAudioSource::LoadSampleInfoFromFile(const char *AAudioCacheFile, const char *ADemuxedAudioFile, const char *ASource, int AAudioTrack) { + if (!FFAudioBase::LoadSampleInfoFromFile(AAudioCacheFile, ASource, AAudioTrack)) + return false; + + char DefaultCacheFilename[1024]; + sprintf(DefaultCacheFilename, "%s.ffasd%dcache", ASource, AAudioTrack); + if (!strcmp(ADemuxedAudioFile, "")) + ADemuxedAudioFile = DefaultCacheFilename; + + RawCache = fopen(ADemuxedAudioFile, "rb"); + if (!RawCache) + return false; + + return true; +} + +FFmpegAudioSource::FFmpegAudioSource(const char *ASource, int AAudioTrack, const char *AAudioCache, const char *ADemuxedAudioFile, IScriptEnvironment *Env) { + BufferSize = 0; + Buffer = NULL; + RawCache = NULL; + FormatContext = NULL; + AudioCodecContext = NULL; + AVCodec *AudioCodec = NULL; + + if (av_open_input_file(&FormatContext, ASource, NULL, 0, NULL) != 0) + Env->ThrowError("FFmpegSource: Couldn't open '%s'", ASource); + + if (av_find_stream_info(FormatContext) < 0) + Env->ThrowError("FFmpegSource: Couldn't find stream information"); + + AudioTrack = GetTrackIndex(AAudioTrack, CODEC_TYPE_AUDIO, Env); + + AudioCodecContext = FormatContext->streams[AudioTrack]->codec; + + AudioCodec = avcodec_find_decoder(AudioCodecContext->codec_id); + if (AudioCodec == NULL) + Env->ThrowError("FFmpegSource: Audio codec not found"); + + if (avcodec_open(AudioCodecContext, AudioCodec) < 0) + Env->ThrowError("FFmpegSource: Could not open audio codec"); + + 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"); + } + + //load cache + bool ACacheIsValid = LoadSampleInfoFromFile(AAudioCache, ADemuxedAudioFile, ASource, AudioTrack); + + char DefaultCacheFilename[1024]; + sprintf(DefaultCacheFilename, "%s.ffasd%dcache", ASource, AudioTrack); + if (!strcmp(ADemuxedAudioFile, "")) + ADemuxedAudioFile = DefaultCacheFilename; + if (!RawCache) + RawCache = fopen(ADemuxedAudioFile, "wb+"); + + // Needs to be indexed? + if (!ACacheIsValid) { + AVPacket Packet; + + while (av_read_frame(FormatContext, &Packet) >= 0) { + if (Packet.stream_index == AudioTrack) { + SI.push_back(SampleInfo(VI.num_audio_samples, _ftelli64(RawCache), Packet.size, (Packet.flags & PKT_FLAG_KEY) ? 1 : 0)); + fwrite(Packet.data, 1, Packet.size, RawCache); + + if (AudioCodecContext->frame_size > 0) { + VI.num_audio_samples += AudioCodecContext->frame_size; + } else { + int Size = Packet.size; + uint8_t *Data = Packet.data; + + 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"); + + if (Ret > 0) { + int DecodedSamples = (int)VI.AudioSamplesFromBytes(TempOutputBufSize); + Size -= Ret; + Data += Ret; + VI.num_audio_samples += DecodedSamples; + } + } + } + } + + av_free_packet(&Packet); + } + + av_seek_frame(FormatContext, AudioTrack, 0, AVSEEK_FLAG_BACKWARD); + avcodec_flush_buffers(AudioCodecContext); + + if (!SaveSampleInfoToFile(AAudioCache, ASource, AudioTrack)) + Env->ThrowError("FFmpegSource: Failed to save audio cache index"); + } + + if (VI.num_audio_samples == 0) + Env->ThrowError("FFmpegSource: Audio track contains no samples"); +} + +int FFmpegAudioSource::DecodeNextAudioBlock(uint8_t *ABuf, int64_t *ACount, uint64_t AFilePos, unsigned int AFrameSize, IScriptEnvironment *Env) { + int Ret = -1; + *ACount = 0; + + _fseeki64(RawCache, AFilePos, SEEK_SET); + + if (AFrameSize > BufferSize) { + Buffer = (uint8_t *)realloc(Buffer, AFrameSize); + BufferSize = AFrameSize; + } + + fread(Buffer, 1, AFrameSize, RawCache); + + uint8_t *Data = Buffer; + int Size = AFrameSize; + + while (Size > 0) { + int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE; + Ret = avcodec_decode_audio2(AudioCodecContext, (int16_t *)ABuf, &TempOutputBufSize, Data, Size); + + if (Ret < 0) // throw error or something? + goto Done; + + if (Ret > 0) { + Size -= Ret; + Data += Ret; + ABuf += TempOutputBufSize; + *ACount += VI.AudioSamplesFromBytes(TempOutputBufSize); + } + } + +Done: + return Ret; +} + +void FFmpegAudioSource::GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment *Env) { + size_t CurrentAudioBlock = FFMAX((int64_t)FindClosestAudioKeyFrame(Start) - 10, (int64_t)0); + avcodec_flush_buffers(AudioCodecContext); + + memset(Buf, 0, VI.BytesFromAudioSamples(Count)); + + uint8_t *DstBuf = (uint8_t *)Buf; + int64_t RemainingSamples = Count; + int64_t DecodeCount; + + do { + int64_t DecodeStart = SI[CurrentAudioBlock].SampleStart; + int Ret = DecodeNextAudioBlock(DecodingBuffer, &DecodeCount, SI[CurrentAudioBlock].FilePos, SI[CurrentAudioBlock].FrameSize, Env); + if (Ret < 0) + Env->ThrowError("Bleh, bad audio decoding"); + CurrentAudioBlock++; + + int64_t OffsetBytes = VI.BytesFromAudioSamples(FFMAX(0, Start - DecodeStart)); + int64_t CopyBytes = FFMAX(0, VI.BytesFromAudioSamples(FFMIN(RemainingSamples, DecodeCount - FFMAX(0, Start - DecodeStart)))); + + memcpy(DstBuf, DecodingBuffer + OffsetBytes, CopyBytes); + DstBuf += CopyBytes; + + RemainingSamples -= VI.AudioSamplesFromBytes(CopyBytes); + } while (RemainingSamples > 0 && DecodeCount > 0); +} + +FFmpegAudioSource::~FFmpegAudioSource() { + if (RawCache) + fclose(RawCache); + if (AudioCodecContext) + avcodec_close(AudioCodecContext); + av_close_input_file(FormatContext); +} diff --git a/FFmpegSource/ffmpegsource.avsi b/FFmpegSource/ffmpegsource.avsi deleted file mode 100644 index 59e5ea846..000000000 --- a/FFmpegSource/ffmpegsource.avsi +++ /dev/null @@ -1 +0,0 @@ -loadplugin("ffmpegsource.dll") diff --git a/FFmpegSource/ffmpegsource.cpp b/FFmpegSource/ffmpegsource.cpp index 0d7d581ad..30d7316ef 100644 --- a/FFmpegSource/ffmpegsource.cpp +++ b/FFmpegSource/ffmpegsource.cpp @@ -42,10 +42,13 @@ int FFmpegSource::GetTrackIndex(int Index, CodecType ATrackType, IScriptEnvironm return Index; } + + 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) { + int AQuality, int ASeekMode, IScriptEnvironment *Env, FrameInfoVector *AFrames) { + AFrames = &Frames; CurrentFrame = 0; SeekMode = ASeekMode; @@ -161,7 +164,7 @@ FFmpegSource::FFmpegSource(const char *ASource, int AVideoTrack, int AAudioTrack AVPacket Packet; while (av_read_frame(FormatContext, &Packet) >= 0) { if (Packet.stream_index == VideoTrack && !VCacheIsValid) { - FrameToDTS.push_back(FrameInfo(Packet.dts, (Packet.flags & PKT_FLAG_KEY) ? 1 : 0)); + Frames.push_back(FrameInfo(Packet.dts, (Packet.flags & PKT_FLAG_KEY) ? 1 : 0)); VI.num_frames++; } else if (Packet.stream_index == AudioTrack && !ACacheIsValid) { int Size = Packet.size; @@ -173,7 +176,7 @@ FFmpegSource::FFmpegSource(const char *ASource, int AVideoTrack, int AAudioTrack if (Ret < 0) Env->ThrowError("FFmpegSource: Audio decoding error"); - int DecodedSamples = VI.AudioSamplesFromBytes(TempOutputBufSize); + int DecodedSamples = (int)VI.AudioSamplesFromBytes(TempOutputBufSize); Size -= Ret; Data += Ret; @@ -214,7 +217,7 @@ FFmpegSource::FFmpegSource(const char *ASource, int AVideoTrack, int AAudioTrack Env->ThrowError("FFmpegSource: Audio track contains no samples"); if (VideoTrack >= 0) - av_seek_frame(FormatContext, VideoTrack, FrameToDTS.front().DTS, AVSEEK_FLAG_BACKWARD); + av_seek_frame(FormatContext, VideoTrack, Frames.front().DTS, AVSEEK_FLAG_BACKWARD); if (AVCache && !VCacheIsValid) if (!SaveFrameInfoToFile(AVideoCache, ASource, VideoTrack)) @@ -229,8 +232,8 @@ FFmpegSource::FFmpegSource(const char *ASource, int AVideoTrack, int AAudioTrack Env->ThrowError("FFmpegSource: Failed to write timecodes"); // Adjust framerate to match the duration of the first frame - if (FrameToDTS.size() >= 2) { - int64_t DTSDiff = FFMAX(FrameToDTS[1].DTS - FrameToDTS[0].DTS, 1); + if (Frames.size() >= 2) { + unsigned int DTSDiff = (unsigned int)FFMAX(Frames[1].DTS - Frames[0].DTS, 1); VI.fps_denominator *= DTSDiff; } } @@ -289,7 +292,7 @@ PVideoFrame FFmpegSource::GetFrame(int n, IScriptEnvironment* Env) { } else { // 10 frames is used as a margin to prevent excessive seeking since the predicted best keyframe isn't always selected by avformat if (n < CurrentFrame || ClosestKF > CurrentFrame + 10 || (SeekMode == 3 && n > CurrentFrame + 10)) { - av_seek_frame(FormatContext, VideoTrack, (SeekMode == 3) ? FrameToDTS[n].DTS : FrameToDTS[ClosestKF].DTS, AVSEEK_FLAG_BACKWARD); + av_seek_frame(FormatContext, VideoTrack, (SeekMode == 3) ? Frames[n].DTS : Frames[ClosestKF].DTS, AVSEEK_FLAG_BACKWARD); avcodec_flush_buffers(VideoCodecContext); HasSeeked = true; } diff --git a/FFmpegSource/ffmpegsource.h b/FFmpegSource/ffmpegsource.h index 10a9f3653..a3de98b6e 100644 --- a/FFmpegSource/ffmpegsource.h +++ b/FFmpegSource/ffmpegsource.h @@ -22,7 +22,7 @@ #define FFMPEGSOURCE_H #ifndef NO_FLAC_CACHE -#define FLAC_CACHE +//#define FLAC_CACHE #endif #include @@ -54,6 +54,29 @@ extern "C" { enum AudioCacheFormat {acNone, acRaw, acFLAC}; +struct FrameInfo { + int64_t DTS; + bool KeyFrame; + FrameInfo(int64_t ADTS, bool AKeyFrame) : DTS(ADTS), KeyFrame(AKeyFrame) {}; +}; + +typedef std::vector FrameInfoVector; + +struct SampleInfo { + int64_t SampleStart; + int64_t FilePos; + unsigned int FrameSize; + bool KeyFrame; + SampleInfo(int64_t ASampleStart, int64_t AFilePos, unsigned int AFrameSize, bool AKeyFrame) { + SampleStart = ASampleStart; + FilePos = AFilePos; + FrameSize = AFrameSize; + KeyFrame = AKeyFrame; + } +}; + +typedef std::vector SampleInfoVector; + int GetPPCPUFlags(IScriptEnvironment *Env); int GetSWSCPUFlags(IScriptEnvironment *Env); CodecID MatroskaToFFCodecID(TrackInfo *TI); @@ -93,15 +116,7 @@ protected: FLAC__int32 *FLACBuffer; #endif // FLAC_CACHE - - - struct FrameInfo { - int64_t DTS; - bool KeyFrame; - FrameInfo(int64_t ADTS, bool AKeyFrame) : DTS(ADTS), KeyFrame(AKeyFrame) {}; - }; - - std::vector FrameToDTS; + FrameInfoVector Frames; int FindClosestKeyFrame(int AFrame); int FrameFromDTS(int64_t ADTS); @@ -134,7 +149,7 @@ public: bool __stdcall GetParity(int n) { return false; } void __stdcall SetCacheHints(int cachehints, int frame_range) { } const VideoInfo& __stdcall GetVideoInfo() { return VI; } - void __stdcall GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment* Env); + void __stdcall GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment *Env); }; class FFmpegSource : public FFBase { @@ -149,9 +164,9 @@ private: 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(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, FrameInfoVector *AFrames); ~FFmpegSource(); - PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* Env); + PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *Env); }; class FFMatroskaSource : public FFBase { @@ -170,9 +185,68 @@ private: int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, IScriptEnvironment* Env); int GetTrackIndex(int Index, unsigned char ATrackType, IScriptEnvironment *Env); 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); + 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, FrameInfoVector *AFrames); ~FFMatroskaSource(); - PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment* Env); + PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *Env); }; -#endif \ No newline at end of file +class FFAudioBase : public IClip{ +protected: + VideoInfo VI; + uint8_t *DecodingBuffer; + SampleInfoVector SI; + + size_t FindClosestAudioKeyFrame(int64_t Sample); + bool LoadSampleInfoFromFile(const char *AAudioCacheFile, const char *ASource, int AAudioTrack); + bool SaveSampleInfoToFile(const char *AAudioCacheFile, const char *ASource, int AAudioTrack); +public: + FFAudioBase(); + ~FFAudioBase(); + + bool __stdcall GetParity(int n) { return false; } + void __stdcall SetCacheHints(int cachehints, int frame_range) { } + const VideoInfo& __stdcall GetVideoInfo() { return VI; } + PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *Env) { return NULL; } +}; + +class FFmpegAudioSource : public FFAudioBase { +private: + AVFormatContext *FormatContext; + AVCodecContext *AudioCodecContext; + + int AudioTrack; + FILE *RawCache; + unsigned int BufferSize; + uint8_t *Buffer; + + bool LoadSampleInfoFromFile(const char *AAudioCacheFile, const char *ADemuxedAudioFile, const char *ASource, int AAudioTrack); + int DecodeNextAudioBlock(uint8_t *ABuf, int64_t *ACount, uint64_t AFilePos, unsigned int AFrameSize, IScriptEnvironment *Env); + int GetTrackIndex(int Index, CodecType ATrackType, IScriptEnvironment *Env); +public: + FFmpegAudioSource(const char *ASource, int AAudioTrack, const char *AAudioCache, const char *ADemuxedAudioFile, IScriptEnvironment *Env); + ~FFmpegAudioSource(); + + void __stdcall GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment *Env); +}; + +class FFMatroskaAudioSource : public FFAudioBase { +private: + StdIoStream ST; + CompressedStream *AudioCS; + AVCodecContext *AudioCodecContext; + MatroskaFile *MF; + char ErrorMessage[256]; + unsigned int BufferSize; + uint8_t *Buffer; + + int ReadFrame(uint64_t AFilePos, unsigned int AFrameSize, CompressedStream *ACS, IScriptEnvironment *Env); + int DecodeNextAudioBlock(uint8_t *ABuf, int64_t *ACount, uint64_t AFilePos, unsigned int AFrameSize, IScriptEnvironment *Env); + int GetTrackIndex(int Index, unsigned char ATrackType, IScriptEnvironment *Env); +public: + FFMatroskaAudioSource(const char *ASource, int AAudioTrack, const char *AAudioCache, IScriptEnvironment *Env); + ~FFMatroskaAudioSource(); + + void __stdcall GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment *Env); +}; + +#endif diff --git a/FFmpegSource/ffmpegsource.html b/FFmpegSource/ffmpegsource.html index 71681c4ac..eff59ee22 100644 --- a/FFmpegSource/ffmpegsource.html +++ b/FFmpegSource/ffmpegsource.html @@ -23,15 +23,9 @@ Loads video files without sucking

Compatibility - Audio

    -
  • Sample accurate in all containers
  • +
  • Should be sample accurate in all containers with audio cache
-

Loading the Plugin - Dll Hell

-

-In order to load FFmpegSource all included dlls except ffmpegsource.dll must be located in the path or in the current working directory. -It is also possible to autoload it if you put all dlls and the avsi file in avisynth's autoloading directory (usually C:\Program Files\AviSynth 2.5\plugins). -

-

Usage

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)
@@ -41,6 +35,14 @@ It is also possible to autoload it if you put all dlls and the avsi file in avis Note that the audio cache will always be created when opening files with audio and that it will be huge since it stores all audio as raw signed 16/24/32 bit pcm, unsigned 8 bit pcm or as float, using flac to compress it is also possible to reduce the size by half.

+

+FFAudioSource(string source, int atrack = -1, string acachefile, string ademuxedfile)
+

+ +

+Experimental, may or may not be accurate enough for real usage. +

+

FFPP(clip, string pp, int ppquality = 6)
Separate postprocessing which also seems to include a few simple deinterlacers @@ -69,7 +71,12 @@ Separate postprocessing which also seems to include a few simple deinterlacers

vcachefile & acachefile: - 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. + 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(d if FFAudioSource is used)(tracknumber)cache for audio. +

+ +

+ademuxedfile: + Specifies the file to store the demuxed audio stream in. Only used by FFAudioSource if the stream isn't matroska. If nothing is specified (source).ffasd(tracknumber)cache is used.

@@ -155,6 +162,16 @@ tn:64:128:256

Suggested Additional Options: --disable-encoders --disable-muxers --enable-small

+

Changes

+
    +
  • 1.14
      +
    • If the output colorspace is YV12 or YUY2 the width and height may be automatically cropped by one pixel to make it an even number
    • +
    • FLAC cache is disabled because the static FLAC lib doesn't want to link
    • +
    • Added the experimental FFAudioSource which doesn't need a huge uncompressed cache
    • +
    • The plugin is now statically compiled
    • +
    • Updated FFmpeg to rev 11413
    • +
  • +

    Changes

    • 1.13
        diff --git a/FFmpegSource/ffpp.cpp b/FFmpegSource/ffpp.cpp index e8701241b..f77479d59 100644 --- a/FFmpegSource/ffpp.cpp +++ b/FFmpegSource/ffpp.cpp @@ -94,4 +94,4 @@ PVideoFrame FFPP::GetFrame(int n, IScriptEnvironment* Env) { } return Dst; -} \ No newline at end of file +} diff --git a/FFmpegSource/ffshared.cpp b/FFmpegSource/ffshared.cpp index 9cb0fed14..ad3e272c8 100644 --- a/FFmpegSource/ffshared.cpp +++ b/FFmpegSource/ffshared.cpp @@ -90,13 +90,46 @@ AVSValue __cdecl CreateFFmpegSource(AVSValue Args, void* UserData, IScriptEnviro bool IsMatroska = !strcmp(FormatContext->iformat->name, "matroska"); av_close_input_file(FormatContext); + FrameInfoVector Frames; + if (IsMatroska) { - return new FFMatroskaSource(Source, VTrack, ATrack, Timecodes, VCache, VCacheFile, ACacheFile, ACCompression, PPString, PPQuality, Env); + return new FFMatroskaSource(Source, VTrack, ATrack, Timecodes, VCache, VCacheFile, ACacheFile, ACCompression, PPString, PPQuality, Env, &Frames); } else { // Do a separate indexing pass, enjoy the constructor sideeffects if (SeekMode == -1) - delete new FFmpegSource(Source, VTrack, ATrack, Timecodes, VCache, VCacheFile, ACacheFile, ACCompression, PPString, PPQuality, -2, Env); - return new FFmpegSource(Source, VTrack, ATrack, Timecodes, VCache, VCacheFile, ACacheFile, ACCompression, PPString, PPQuality, SeekMode, Env); + delete new FFmpegSource(Source, VTrack, ATrack, Timecodes, VCache, VCacheFile, ACacheFile, ACCompression, PPString, PPQuality, -2, Env, &Frames); + return new FFmpegSource(Source, VTrack, ATrack, Timecodes, VCache, VCacheFile, ACacheFile, ACCompression, PPString, PPQuality, SeekMode, Env, &Frames); + } +} + +AVSValue __cdecl CreateFFAudioSource(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 ATrack = Args[1].AsInt(-1); + const char *ACacheFile = Args[2].AsString(""); + const char *ADemuxedFile = Args[3].AsString(""); + + if (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 FFMatroskaAudioSource(Source, ATrack, ACacheFile, Env); + } else { + return new FFmpegAudioSource(Source, ATrack, ACacheFile, ADemuxedFile, Env); } } @@ -106,6 +139,7 @@ AVSValue __cdecl CreateFFPP(AVSValue Args, void* UserData, IScriptEnvironment* E 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); + Env->AddFunction("FFAudioSource", "[source]s[atrack]i[acachefile]s[ademuxedfile]s", CreateFFAudioSource, 0); + Env->AddFunction("FFPP", "c[pp]s[ppquality]i", CreateFFPP, 0); return "FFmpegSource"; }; diff --git a/FFmpegSource/stdiostream.c b/FFmpegSource/stdiostream.c index 97988a90f..9f97430ac 100644 --- a/FFmpegSource/stdiostream.c +++ b/FFmpegSource/stdiostream.c @@ -25,7 +25,7 @@ /* read count bytes into buffer starting at file position pos * return the number of bytes read, -1 on error or 0 on EOF */ -int StdIoRead(StdIoStream *st, ulonglong pos, void *buffer, int count) { +int StdIoRead(StdIoStream *st, ulonglong pos, void *buffer, int count) { size_t rd; if (_fseeki64(st->fp, pos, SEEK_SET)) { st->error = errno; @@ -72,21 +72,21 @@ const char *StdIoGetLastError(StdIoStream *st) { } /* memory allocation, this is done via stdlib */ -void *StdIoMalloc(StdIoStream *st, size_t size) { +void *StdIoMalloc(StdIoStream *st, size_t size) { return malloc(size); } -void *StdIoRealloc(StdIoStream *st, void *mem, size_t size) { +void *StdIoRealloc(StdIoStream *st, void *mem, size_t size) { return realloc(mem,size); } -void StdIoFree(StdIoStream *st, void *mem) { +void StdIoFree(StdIoStream *st, void *mem) { free(mem); } /* progress report handler for lengthy operations * returns 0 to abort operation, nonzero to continue */ -int StdIoProgress(StdIoStream *st, ulonglong cur, ulonglong max) { +int StdIoProgress(StdIoStream *st, ulonglong cur, ulonglong max) { return 1; }