From bf9f2b50b7d260283927666f51a1b660e173fac5 Mon Sep 17 00:00:00 2001 From: Fredrik Mellbin Date: Fri, 19 Sep 2008 21:35:46 +0000 Subject: [PATCH] FFmpegSource2: matroska as audio source, first try Originally committed to SVN as r2366. --- FFmpegSource2/ffaudiosource.cpp | 222 +++++++++++++++++++++++++++++++ FFmpegSource2/ffaudiosource.h | 89 +++++++++++++ FFmpegSource2/ffavisynth.cpp | 47 +++++++ FFmpegSource2/ffavisynth.h | 15 +++ FFmpegSource2/ffavsfilters.cpp | 51 +++++++ FFmpegSource2/ffmpegsource2.html | 197 +++++++++++++++++++++++++++ FFmpegSource2/ffms.cpp | 27 ++++ FFmpegSource2/ffms.h | 23 +++- FFmpegSource2/ffvideosource.cpp | 14 +- FFmpegSource2/indexing.cpp | 17 +++ FFmpegSource2/indexing.h | 2 +- 11 files changed, 695 insertions(+), 9 deletions(-) create mode 100644 FFmpegSource2/ffaudiosource.cpp create mode 100644 FFmpegSource2/ffaudiosource.h create mode 100644 FFmpegSource2/ffmpegsource2.html diff --git a/FFmpegSource2/ffaudiosource.cpp b/FFmpegSource2/ffaudiosource.cpp new file mode 100644 index 000000000..9df353c23 --- /dev/null +++ b/FFmpegSource2/ffaudiosource.cpp @@ -0,0 +1,222 @@ +// Copyright (c) 2007-2008 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 "ffaudiosource.h" + + +AudioBase::AudioBase() { + DecodingBuffer = new uint8_t[AVCODEC_MAX_AUDIO_FRAME_SIZE * 10]; +}; + +AudioBase::~AudioBase() { + delete[] DecodingBuffer; +}; + +size_t AudioBase::FindClosestAudioKeyFrame(int64_t Sample) { + for (size_t i = 0; i < Frames.size(); i++) { + if (Frames[i].SampleStart == Sample && Frames[i].KeyFrame) + return i; + else if (Frames[i].SampleStart > Sample && Frames[i].KeyFrame) + return i - 1; + } + return Frames.size() - 1; +} + +int MatroskaAudioSource::GetTrackIndex(int &Index, char *ErrorMsg, unsigned MsgSize) { + if (Index < 0) { + Index = -1; + for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) + if (mkv_GetTrackInfo(MF, i)->Type == TT_AUDIO) { + Index = i; + break; + } + } + + if (Index < 0) { + _snprintf(ErrorMsg, MsgSize, "No audio track found"); + return 1; + } + + if (Index >= (int)mkv_GetNumTracks(MF)) { + _snprintf(ErrorMsg, MsgSize, "Invalid audio track number"); + return 2; + } + + if (mkv_GetTrackInfo(MF, Index)->Type != TT_AUDIO) { + _snprintf(ErrorMsg, MsgSize, "Selected track is not audio"); + return 3; + } + + return 0; +} + +void MatroskaAudioSource::Free(bool CloseAudio) { + +} + +MatroskaAudioSource::MatroskaAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize) { + CodecContext = NULL; + AVCodec *Codec = NULL; + TrackInfo *TI = NULL; + CS = NULL; + + MC.ST.fp = fopen(SourceFile, "rb"); + if (MC.ST.fp == NULL) { + _snprintf(ErrorMsg, MsgSize, "Can't open '%s': %s", SourceFile, strerror(errno)); + throw ErrorMsg; + } + + setvbuf(MC.ST.fp, NULL, _IOFBF, CACHESIZE); + + MF = mkv_OpenEx(&MC.ST.base, 0, 0, ErrorMessage, sizeof(ErrorMessage)); + if (MF == NULL) { + fclose(MC.ST.fp); + _snprintf(ErrorMsg, MsgSize, "Can't parse Matroska file: %s", ErrorMessage); + throw ErrorMsg; + } + + if (GetTrackIndex(Track, ErrorMsg, MsgSize)) { + Free(false); + throw ErrorMsg; + } + + Frames = (*TrackIndices)[Track]; + + if (Frames.size() == 0) { + Free(false); + _snprintf(ErrorMsg, MsgSize, "Audio track contains no frames"); + throw ErrorMsg; + } + + mkv_SetTrackMask(MF, ~(1 << Track)); + TI = mkv_GetTrackInfo(MF, Track); + + if (TI->CompEnabled) { + CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage)); + if (CS == NULL) { + Free(false); + _snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage); + throw ErrorMsg; + } + } + + CodecContext = avcodec_alloc_context(); + CodecContext->extradata = (uint8_t *)TI->CodecPrivate; + CodecContext->extradata_size = TI->CodecPrivateSize; + + Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI)); + if (Codec == NULL) { + Free(false); + _snprintf(ErrorMsg, MsgSize, "Video codec not found"); + throw ErrorMsg; + } + + if (avcodec_open(CodecContext, Codec) < 0) { + Free(false); + _snprintf(ErrorMsg, MsgSize, "Could not open video codec"); + throw ErrorMsg; + } + + // Always try to decode a frame to make sure all required parameters are known + uint8_t DummyBuf[512]; + if (GetAudio(DummyBuf, 0, 1, ErrorMsg, MsgSize)) { + Free(true); + throw ErrorMsg; + } + + AP.BitsPerSample = av_get_bits_per_sample_format(CodecContext->sample_fmt); + AP.Channels = CodecContext->channels;; + AP.Float = AudioFMTIsFloat(CodecContext->sample_fmt); + AP.SampleRate = CodecContext->sample_rate; + AP.NumSamples = (Frames.back()).SampleStart; + + if (AP.SampleRate <= 0 || AP.BitsPerSample <= 0) { + Free(true); + _snprintf(ErrorMsg, MsgSize, "Codec returned zero size audio"); + throw ErrorMsg; + } +} + +MatroskaAudioSource::~MatroskaAudioSource() { + +} + +int MatroskaAudioSource::GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) { + const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; + size_t CurrentAudioBlock = FFMAX((int64_t)FindClosestAudioKeyFrame(Start) - 10, (int64_t)0); + avcodec_flush_buffers(CodecContext); + + memset(Buf, 0, SizeConst * Count); + + uint8_t *DstBuf = (uint8_t *)Buf; + int64_t RemainingSamples = Count; + int64_t DecodeCount; + + do { + int64_t DecodeStart = Frames[CurrentAudioBlock].SampleStart; + int Ret = DecodeNextAudioBlock(DecodingBuffer, &DecodeCount, Frames[CurrentAudioBlock].FilePos, Frames[CurrentAudioBlock].FrameSize, ErrorMsg, MsgSize); + if (Ret < 0) { + //Env->ThrowError("Bleh, bad audio decoding"); + } + CurrentAudioBlock++; + + int64_t OffsetBytes = SizeConst * FFMAX(0, Start - DecodeStart); + int64_t CopyBytes = FFMAX(0, SizeConst * FFMIN(RemainingSamples, DecodeCount - FFMAX(0, Start - DecodeStart))); + + memcpy(DstBuf, DecodingBuffer + OffsetBytes, CopyBytes); + DstBuf += CopyBytes; + + if (SizeConst) + RemainingSamples -= CopyBytes / SizeConst; + + } while (RemainingSamples > 0 && CurrentAudioBlock < Frames.size()); + + return 0; +} + +int MatroskaAudioSource::DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, uint64_t FilePos, unsigned int FrameSize, char *ErrorMsg, unsigned MsgSize) { + const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; + int Ret = -1; + *Count = 0; + + // FIXME check return + ReadFrame(FilePos, FrameSize, CS, MC, ErrorMsg, MsgSize); + int Size = FrameSize; + uint8_t *Data = MC.Buffer; + + while (Size > 0) { + int TempOutputBufSize = AVCODEC_MAX_AUDIO_FRAME_SIZE; + Ret = avcodec_decode_audio2(CodecContext, (int16_t *)Buf, &TempOutputBufSize, Data, Size); + + if (Ret < 0) // throw error or something? + goto Done; + + if (Ret > 0) { + Size -= Ret; + Data += Ret; + Buf += TempOutputBufSize; + if (SizeConst) + *Count += TempOutputBufSize / SizeConst; + } + } + +Done: + return Ret; +} diff --git a/FFmpegSource2/ffaudiosource.h b/FFmpegSource2/ffaudiosource.h new file mode 100644 index 000000000..a262b46d6 --- /dev/null +++ b/FFmpegSource2/ffaudiosource.h @@ -0,0 +1,89 @@ +// Copyright (c) 2007-2008 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. + +#ifndef FFAUDIOSOURCE_H +#define FFAUDIOSOURCE_H + +extern "C" { +#include +#include +} + +#include +#include "indexing.h" +#include "utils.h" +#include "ffms.h" + +class AudioBase { +protected: + uint8_t *DecodingBuffer; + FrameInfoVector Frames; + AVCodecContext *CodecContext; + AudioProperties AP; + + size_t FindClosestAudioKeyFrame(int64_t Sample); +public: + AudioBase(); + ~AudioBase(); + + const AudioProperties& GetAudioProperties() { return AP; } + virtual int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) = 0; +}; + +/* +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 *AAudioCacheFile2, 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 *AAudioCacheFile2, IScriptEnvironment *Env); + ~FFmpegAudioSource(); + + void __stdcall GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment *Env); +}; +*/ + +class MatroskaAudioSource : public AudioBase { +private: + MatroskaFile *MF; + MatroskaReaderContext MC; + CompressedStream *CS; + char ErrorMessage[256]; + + int DecodeNextAudioBlock(uint8_t *Buf, int64_t *Count, uint64_t FilePos, unsigned int FrameSize, char *ErrorMsg, unsigned MsgSize); + int GetTrackIndex(int &Index, char *ErrorMsg, unsigned MsgSize); + void Free(bool CloseAudio); +public: + MatroskaAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize); + ~MatroskaAudioSource(); + + int GetAudio(void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize); +}; + +#endif diff --git a/FFmpegSource2/ffavisynth.cpp b/FFmpegSource2/ffavisynth.cpp index cdd5ca0fb..47bdbecc0 100644 --- a/FFmpegSource2/ffavisynth.cpp +++ b/FFmpegSource2/ffavisynth.cpp @@ -22,11 +22,14 @@ #include "utils.h" AvisynthVideoSource::AvisynthVideoSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, const char *PP, int Threads, int SeekMode, IScriptEnvironment* Env, char *ErrorMsg, unsigned MsgSize) { + memset(&VI, 0, sizeof(VI)); SWS = NULL; ConvertToFormat = PIX_FMT_NONE; try { VS = FFMS_CreateVideoSource(SourceFile, Track, TrackIndices, PP, Threads, SeekMode, ErrorMsg, MsgSize); + if (!VS) + throw ErrorMsg; } catch (...) { Env->ThrowError(ErrorMsg); } @@ -137,3 +140,47 @@ PVideoFrame AvisynthVideoSource::GetFrame(int n, IScriptEnvironment *Env) { Env->SetVar("FFPICT_TYPE", Frame->PictType); return OutputFrame(Frame, Env); } + + +AvisynthAudioSource::AvisynthAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, IScriptEnvironment* Env, char *ErrorMsg, unsigned MsgSize) { + memset(&VI, 0, sizeof(VI)); + + try { + AS = FFMS_CreateAudioSource(SourceFile, Track, TrackIndices, ErrorMsg, MsgSize); + if (!AS) + throw ErrorMsg; + } catch (...) { + Env->ThrowError(ErrorMsg); + } + + const AudioProperties AP = *FFMS_GetAudioProperties(AS); + + + VI.nchannels = AP.Channels; + VI.num_audio_samples = AP.NumSamples; + VI.audio_samples_per_second = AP.SampleRate; + + if (AP.Float && AP.BitsPerSample == 32) { + VI.sample_type = SAMPLE_FLOAT; + } else { + switch (AP.BitsPerSample) { + case 8: VI.sample_type = SAMPLE_INT8; break; + case 16: VI.sample_type = SAMPLE_INT16; break; + case 24: VI.sample_type = SAMPLE_INT24; break; + case 32: VI.sample_type = SAMPLE_INT32; break; + default:; + // FIXME error here + } + } +} + +AvisynthAudioSource::~AvisynthAudioSource() { + FFMS_DestroyAudioSource(AS); +} + +void AvisynthAudioSource::GetAudio(void* Buf, __int64 Start, __int64 Count, IScriptEnvironment *Env) { + char ErrorMsg[1024]; + unsigned MsgSize = sizeof(ErrorMsg); + if (FFMS_GetAudio(AS, Buf, Start, Count, ErrorMsg, MsgSize)) + Env->ThrowError(ErrorMsg); +} \ No newline at end of file diff --git a/FFmpegSource2/ffavisynth.h b/FFmpegSource2/ffavisynth.h index 91660cc8e..6e592431d 100644 --- a/FFmpegSource2/ffavisynth.h +++ b/FFmpegSource2/ffavisynth.h @@ -52,4 +52,19 @@ public: PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *Env); }; +class AvisynthAudioSource : public IClip { +private: + VideoInfo VI; + AudioBase *AS; +public: + AvisynthAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, IScriptEnvironment* Env, char *ErrorMsg, unsigned MsgSize); + ~AvisynthAudioSource(); + int GetTrack() { return FFMS_GetASTrack(AS); } + 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); + PVideoFrame __stdcall GetFrame(int n, IScriptEnvironment *Env) { return NULL; }; +}; + #endif diff --git a/FFmpegSource2/ffavsfilters.cpp b/FFmpegSource2/ffavsfilters.cpp index 741f29331..63da69b17 100644 --- a/FFmpegSource2/ffavsfilters.cpp +++ b/FFmpegSource2/ffavsfilters.cpp @@ -146,6 +146,56 @@ AVSValue __cdecl CreateFFVideoSource(AVSValue Args, void* UserData, IScriptEnvir } } + FFMS_DestroyFrameIndex(Index); + return Filter; +} + +AVSValue __cdecl CreateFFAudioSource(AVSValue Args, void* UserData, IScriptEnvironment* Env) { + FFMS_Init(); + + char ErrorMsg[1024]; + unsigned MsgSize = sizeof(ErrorMsg); + + if (!Args[0].Defined()) + Env->ThrowError("FFAudioSource: No source specified"); + + const char *Source = Args[0].AsString(); + int Track = Args[1].AsInt(-1); + bool Cache = Args[2].AsBool(true); + const char *CacheFile = Args[3].AsString(""); + + if (Track <= -2) + Env->ThrowError("FFAudioSource: No video track selected"); + + std::string DefaultCache(Source); + DefaultCache.append(".ffindex"); + if (!strcmp(CacheFile, "")) + CacheFile = DefaultCache.c_str(); + + FrameIndex *Index; + if (Cache) { + if (!(Index = FFMS_ReadIndex(CacheFile, ErrorMsg, MsgSize))) { + if (!(Index = FFMS_MakeIndex(Source, -1, NULL, NULL, NULL, ErrorMsg, MsgSize))) + Env->ThrowError("FFAudioSource: %s", ErrorMsg); + + if (Cache) + if (FFMS_WriteIndex(CacheFile, Index, ErrorMsg, MsgSize)) { + FFMS_DestroyFrameIndex(Index); + Env->ThrowError("FFAudioSource: %s", ErrorMsg); + } + } + } + + AvisynthAudioSource *Filter; + + try { + Filter = new AvisynthAudioSource(Source, Track, Index, Env, ErrorMsg, MsgSize); + } catch (...) { + FFMS_DestroyFrameIndex(Index); + throw; + } + + FFMS_DestroyFrameIndex(Index); return Filter; } @@ -160,6 +210,7 @@ AVSValue __cdecl CreateSWScale(AVSValue Args, void* UserData, IScriptEnvironment extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* Env) { Env->AddFunction("FFIndex", "[source]s[cachefile]s[trackmask]i[audiofile]s[overwrite]b", CreateFFIndex, 0); Env->AddFunction("FFVideoSource", "[source]s[track]i[cache]b[cachefile]s[pp]s[threads]i[timecodes]s[seekmode]i", CreateFFVideoSource, 0); + Env->AddFunction("FFAudioSource", "[source]s[track]i[cache]b[cachefile]s", CreateFFAudioSource, 0); Env->AddFunction("FFPP", "c[pp]s", CreateFFPP, 0); Env->AddFunction("SWScale", "c[width]i[height]i[resizer]s[colorspace]s", CreateSWScale, 0); diff --git a/FFmpegSource2/ffmpegsource2.html b/FFmpegSource2/ffmpegsource2.html new file mode 100644 index 000000000..7d06152fe --- /dev/null +++ b/FFmpegSource2/ffmpegsource2.html @@ -0,0 +1,197 @@ + + + +FFmpegSource2 Documentation + + + +

FFmpegSource2 Documentation

+

+Opens files using ffmpeg and nothing else. May be frame accurate on good days. The source is MIT licensed and can be obtained from https://spaceboyz.net/svn/aegisub/trunk/FFmpegSource2. The precompiled binary is GPL licensed. If you are religious you may consider this the second coming. +

+ +

Compatibility - Video

+
    +
  • AVI, MKV, MP4, FLV: Frame accurate
  • +
  • WMV: Frame accurate(?) but avformat seems to pick keyframes relatively far away
  • +
  • OGM: Messed up first frame and seeking produces smearing with seekmode=3, incredibly slow seeking without, remux to mkv or avi
  • +
  • VOB: No rff flags applied, frame accurate?
  • +
  • MPG: Seeking seems to be off by one or two frames now and then
  • +
  • M2TS, TS: Linear access only (seekmode=-1)
  • +
  • Image files: Most formats can be opened if seekmode=-1 is set
  • +
+ +

Compatibility - Audio

+
    +
  • All formats are written to separate wave64 files
  • +
  • No wave64 reader is provided
  • +
+ +

Usage

+

+FFIndex("string source, string cachefile = source + ".ffindex", int trackmask = 0, string audiofile = source, bool overwrite = false)
+ Used to invoke indexing separately and to write audio tracks to disk as wave64 files +

+ +

+FFVideoSource(string source, int track, bool cache = true, string cachefile = source + ".ffindex", string pp, int threads = -1, string timecodes, int seekmode = 1)
+ Opens video, will invoke indexing with the defaults if no preexisting index is found +

+ +

+FFPP(clip, string pp)
+ Separate postprocessing which also seems to include a few simple deinterlacers +

+ +

+SWScale(clip, width = -1, height = -1, resizer = "BICUBIC", colorspace = "")
+ Separate postprocessing which also seems to include a few simple deinterlacers +

+ +

+source: + Source file. +

+ +

+trackmask: + Which audio tracks to write to disk. It is a binary mask meaning that 7 corresponds to writing tracks 1-3. Non-audio tracks are ignored. -1 writes all tracks. +

+ +

+audiofile: + The base name to use for the decoded audio. Track number and delay is appended. +

+ +

+overwrite: + Forces reindexing even if a valid index already exists. May be useful for trackmask changes or testing. +

+ +

+track: + Track number as seen by the relevant demuxer, starts from 0, -1 means it will pick the first suitable track. +

+ +

+timecodes: + File to output timecodes to, if the file exists it will be overwritten. +

+ +

+cache: + Write 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. +

+ +

+cachefile + Where to write the cache information. +

+ +

+pp: + 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. +

+ +

+threads: + Sets the number of decoder threads used. Defaults to the number of cpus reported by windows. Ignored by lavc if the used decoder doesn't implement it. +

+ +

+seekmode: + Force how seeking is handled, has no effect on matroska files which always use the equivalent of seekmode=1
+ -1: linear access without rewind, will throw an error if each successive requested frame number isn't bigger than the last one, only intended for opening images but might work on well with some obscure video format
+ 0: linear access, the definition of slow but should make some formats "usable"
+ 1: safe normal, bases seeking decisions on the reported keyframe positions
+ 2: unsafe normal, same as 1 but no error will be thrown if the exact destination has to be guessed
+ 3: aggressive, seek in the forward direction even if no closer keyframe is known to exist, only useful for testing and containers where avformat doesn't report keyframes properly +

+ +

+width & height: + Width and height to resize to. Value below or equal to 0 is the same as specifying the input dimensions. +

+ +

+resizer: + Selects the resizer used for resampling the chroma planes and normal resizing. The available methods are: FAST_BILINEAR, BILINEAR, BICUBIC, X, POINT, AREA, BICUBLIN, GAUSS, SINC, LANCZOS and SPLINE. +

+ +

+colorspace: + The colorspace to convert to. The names are YV12, YUY2, RGB24, RGB32 and the empty string for same as input. +

+ + +

PP string format

+
+Available postprocessing filters:
+Filters                        Options
+short  long name       short   long option     Description
+*      *               a       autoq           CPU power dependent enabler
+                       c       chrom           chrominance filtering enabled
+                       y       nochrom         chrominance filtering disabled
+                       n       noluma          luma filtering disabled
+hb     hdeblock        (2 threshold)           horizontal deblocking filter
+       1. difference factor: default=32, higher -> more deblocking
+       2. flatness threshold: default=39, lower -> more deblocking
+                       the h & v deblocking filters share these
+                       so you can't set different thresholds for h / v
+vb     vdeblock        (2 threshold)           vertical deblocking filter
+ha     hadeblock       (2 threshold)           horizontal deblocking filter
+va     vadeblock       (2 threshold)           vertical deblocking filter
+h1     x1hdeblock                              experimental h deblock filter 1
+v1     x1vdeblock                              experimental v deblock filter 1
+dr     dering                                  deringing filter
+al     autolevels                              automatic brightness / contrast
+                       f        fullyrange     stretch luminance to (0..255)
+lb     linblenddeint                           linear blend deinterlacer
+li     linipoldeint                            linear interpolating deinterlace
+ci     cubicipoldeint                          cubic interpolating deinterlacer
+md     mediandeint                             median deinterlacer
+fd     ffmpegdeint                             ffmpeg deinterlacer
+l5     lowpass5                                FIR lowpass deinterlacer
+de     default                                 hb:a,vb:a,dr:a
+fa     fast                                    h1:a,v1:a,dr:a
+ac                                             ha:a:128:7,va:a,dr:a
+tn     tmpnoise        (3 threshold)           temporal noise reducer
+                     1. <= 2. <= 3.            larger -> stronger filtering
+fq     forceQuant                   force quantizer
+Usage:
+[:
+ +

Compiling

+ +

zlib from http://www.zlib.net/

+ +

FFmpeg svn from http://ffmpeg.mplayerhq.hu/

+ +

Required FFmpeg Configuration: +./configure --enable-memalign-hack --enable-gpl --enable-swscale --enable-postproc + +

Suggested Additional Options: +--enable-w32threads --disable-encoders --disable-muxers --disable-debug

+ +

+Note that --enable-w32threads is required for multithreaded decoding to work. +

+ +

Changes

+
    +
  • 2.00 beta 1
      +
    • Can now be used as a stand alone library for making indices and retrieving frames
    • +
    • Rewrote most things
    • +
    • Updated FFmpeg to rev 15301
    • +
  • +
+ + + diff --git a/FFmpegSource2/ffms.cpp b/FFmpegSource2/ffms.cpp index 56aa01d6a..ee5423842 100644 --- a/FFmpegSource2/ffms.cpp +++ b/FFmpegSource2/ffms.cpp @@ -20,6 +20,7 @@ #include "ffms.h" #include "ffvideosource.h" +#include "ffaudiosource.h" #include "indexing.h" FFMS_API(void) FFMS_Init() { @@ -37,22 +38,48 @@ FFMS_API(VideoBase *) FFMS_CreateVideoSource(const char *SourceFile, int Track, } } +FFMS_API(AudioBase *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize) { + switch (TrackIndices->Decoder) { + //case 0: return new FFVideoSource(SourceFile, Track, TrackIndices, ErrorMsg, MsgSize); + case 1: return new MatroskaAudioSource(SourceFile, Track, TrackIndices, ErrorMsg, MsgSize); + default: return NULL; + } +} + FFMS_API(void) FFMS_DestroyVideoSource(VideoBase *VB) { delete VB; } +FFMS_API(void) FFMS_DestroyAudioSource(AudioBase *AB) { + delete AB; +} + FFMS_API(int) FFMS_GetVSTrack(VideoBase *VB) { return VB->GetTrack(); } +FFMS_API(int) FFMS_GetASTrack(AudioBase *AB) { + // FIXME + // return AB->GetTrack(); + return 0; +} + FFMS_API(const VideoProperties *) FFMS_GetVideoProperties(VideoBase *VB) { return &VB->GetVideoProperties(); } +FFMS_API(const AudioProperties *) FFMS_GetAudioProperties(AudioBase *AB) { + return &AB->GetAudioProperties(); +} + FFMS_API(const AVFrameLite *) FFMS_GetFrame(VideoBase *VB, int n, char *ErrorMsg, unsigned MsgSize) { return (AVFrameLite *)VB->GetFrame(n, ErrorMsg, MsgSize); } +FFMS_API(int) FFMS_GetAudio(AudioBase *AB, void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize) { + return AB->GetAudio(Buf, Start, Count, ErrorMsg, MsgSize); +} + FFMS_API(int) FFMS_SetOutputFormat(VideoBase *VB, int TargetFormat, int Width, int Height) { return VB->SetOutputFormat(TargetFormat, Width, Height); } diff --git a/FFmpegSource2/ffms.h b/FFmpegSource2/ffms.h index c2d340dc3..c56e544c3 100644 --- a/FFmpegSource2/ffms.h +++ b/FFmpegSource2/ffms.h @@ -48,6 +48,7 @@ #endif class VideoBase; +class AudioBase; class FrameIndex; class FrameInfoVector; @@ -120,9 +121,15 @@ struct TrackTimeBase { class FrameInfo { public: - int64_t DTS; + union { + int64_t DTS; + int64_t SampleStart; + }; + int64_t FilePos; + unsigned int FrameSize; bool KeyFrame; FrameInfo(int64_t DTS, bool KeyFrame); + FrameInfo(int64_t SampleStart, int64_t FilePos, unsigned int FrameSize, bool KeyFrame); }; struct VideoProperties { @@ -140,12 +147,25 @@ struct VideoProperties { int CropRight; }; +struct AudioProperties { + int SampleRate; + int Channels; + int BitsPerSample; + bool Float; + int64_t NumSamples; +}; + FFMS_API(void) FFMS_Init(); FFMS_API(VideoBase *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, const char *PP, int Threads, int SeekMode, char *ErrorMsg, unsigned MsgSize); +FFMS_API(AudioBase *) FFMS_CreateAudioSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, char *ErrorMsg, unsigned MsgSize); FFMS_API(void) FFMS_DestroyVideoSource(VideoBase *VB); +FFMS_API(void) FFMS_DestroyAudioSource(AudioBase *AB); FFMS_API(int) FFMS_GetVSTrack(VideoBase *VB); +FFMS_API(int) FFMS_GetASTrack(AudioBase *AB); FFMS_API(const VideoProperties *) FFMS_GetVideoProperties(VideoBase *VB); +FFMS_API(const AudioProperties *) FFMS_GetAudioProperties(AudioBase *AB); FFMS_API(const AVFrameLite *) FFMS_GetFrame(VideoBase *VB, int n, char *ErrorMsg, unsigned MsgSize); +FFMS_API(int) FFMS_GetAudio(AudioBase *AB, void *Buf, int64_t Start, int64_t Count, char *ErrorMsg, unsigned MsgSize); FFMS_API(int) FFMS_SetOutputFormat(VideoBase *VB, int TargetFormat, int Width, int Height); FFMS_API(void) FFMS_ResetOutputFormat(VideoBase *VB); FFMS_API(void) FFMS_DestroyFrameIndex(FrameIndex *FI); @@ -154,6 +174,7 @@ FFMS_API(int) FFMS_GetNumFrames(FrameInfoVector *FIV, char *ErrorMsg, unsigned M FFMS_API(const FrameInfo *) FFMS_GetFrameInfo(FrameInfoVector *FIV, int Frame, char *ErrorMsg, unsigned MsgSize); FFMS_API(FrameInfoVector *) FFMS_GetTITrackIndex(FrameIndex *TrackIndices, int Track, char *ErrorMsg, unsigned MsgSize); FFMS_API(FrameInfoVector *) FFMS_GetVSTrackIndex(VideoBase *VB, char *ErrorMsg, unsigned MsgSize); +FFMS_API(FrameInfoVector *) FFMS_GetASTrackIndex(AudioBase *AB, char *ErrorMsg, unsigned MsgSize); FFMS_API(int) FFMS_FindClosestKeyFrame(FrameInfoVector *FIV, int Frame, char *ErrorMsg, unsigned MsgSize); FFMS_API(int) FFMS_FrameFromDTS(FrameInfoVector *FIV, int64_t DTS, char *ErrorMsg, unsigned MsgSize); FFMS_API(int) FFMS_ClosestFrameFromDTS(FrameInfoVector *FIV, int64_t DTS, char *ErrorMsg, unsigned MsgSize); diff --git a/FFmpegSource2/ffvideosource.cpp b/FFmpegSource2/ffvideosource.cpp index 438c60cb4..23848827d 100644 --- a/FFmpegSource2/ffvideosource.cpp +++ b/FFmpegSource2/ffvideosource.cpp @@ -485,14 +485,14 @@ MatroskaVideoSource::MatroskaVideoSource(const char *SourceFile, int Track, mkv_SetTrackMask(MF, ~(1 << VideoTrack)); TI = mkv_GetTrackInfo(MF, VideoTrack); - if (TI->CompEnabled) { - CS = cs_Create(MF, VideoTrack, ErrorMessage, sizeof(ErrorMessage)); - if (CS == NULL) { - Free(false); - _snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage); - throw ErrorMsg; - } + if (TI->CompEnabled) { + CS = cs_Create(MF, VideoTrack, ErrorMessage, sizeof(ErrorMessage)); + if (CS == NULL) { + Free(false); + _snprintf(ErrorMsg, MsgSize, "Can't create decompressor: %s", ErrorMessage); + throw ErrorMsg; } + } CodecContext = avcodec_alloc_context(); CodecContext->extradata = (uint8_t *)TI->CodecPrivate; diff --git a/FFmpegSource2/indexing.cpp b/FFmpegSource2/indexing.cpp index 14af03a14..a2660f31a 100644 --- a/FFmpegSource2/indexing.cpp +++ b/FFmpegSource2/indexing.cpp @@ -32,11 +32,13 @@ public: Wave64Writer *W64W; AVCodecContext *CTX; CompressedStream *CS; + int64_t CurrentSample; AudioContext() { W64W = NULL; CTX = NULL; CS = NULL; + CurrentSample = 0; } ~AudioContext() { @@ -208,6 +210,7 @@ static FrameIndex *MakeMatroskaIndex(const char *SourceFile, int AudioTrackMask, if (IP) { if ((*IP)(0, _ftelli64(MC.ST.fp), SourceSize, Private)) { _snprintf(ErrorMsg, MsgSize, "Cancelled by user"); + delete TrackIndices; return NULL; } } @@ -218,6 +221,7 @@ static FrameIndex *MakeMatroskaIndex(const char *SourceFile, int AudioTrackMask, if (AudioTrackMask & (1 << Track)) { ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC, ErrorMsg, MsgSize); + (*TrackIndices)[Track].push_back(FrameInfo(AudioContexts[Track].CurrentSample, FilePos, FrameSize, (FrameFlags & FRAME_KF) != 0)); int Size = FrameSize; uint8_t *Data = MC.Buffer; @@ -228,6 +232,7 @@ static FrameIndex *MakeMatroskaIndex(const char *SourceFile, int AudioTrackMask, int Ret = avcodec_decode_audio2(AudioCodecContext, db, &dbsize, Data, Size); if (Ret < 0) { _snprintf(ErrorMsg, MsgSize, "Audio decoding error"); + delete TrackIndices; return NULL; } @@ -248,6 +253,7 @@ static FrameIndex *MakeMatroskaIndex(const char *SourceFile, int AudioTrackMask, AudioCodecContext->channels, AudioCodecContext->sample_rate, AudioFMTIsFloat(AudioCodecContext->sample_fmt)); } + AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels); AudioContexts[Track].W64W->WriteData(db, dbsize); } } @@ -318,6 +324,7 @@ FrameIndex *MakeIndex(const char *SourceFile, int AudioTrackMask, const char *Au if (IP) { if ((*IP)(0, FormatContext->pb->pos, FormatContext->file_size, Private)) { _snprintf(ErrorMsg, MsgSize, "Cancelled by user"); + delete TrackIndices; return NULL; } } @@ -336,6 +343,7 @@ FrameIndex *MakeIndex(const char *SourceFile, int AudioTrackMask, const char *Au int Ret = avcodec_decode_audio2(AudioCodecContext, db, &dbsize, Data, Size); if (Ret < 0) { _snprintf(ErrorMsg, MsgSize, "Audio decoding error"); + delete TrackIndices; return NULL; } @@ -427,6 +435,15 @@ FrameIndex *ReadIndex(const char *IndexFile, char *ErrorMsg, unsigned MsgSize) { FrameInfo::FrameInfo(int64_t DTS, bool KeyFrame) { this->DTS = DTS; + this->FilePos = 0; + this->FrameSize = 0; + this->KeyFrame = KeyFrame; +} + +FrameInfo::FrameInfo(int64_t SampleStart, int64_t FilePos, unsigned int FrameSize, bool KeyFrame) { + this->SampleStart = SampleStart; + this->FilePos = FilePos; + this->FrameSize = FrameSize; this->KeyFrame = KeyFrame; } diff --git a/FFmpegSource2/indexing.h b/FFmpegSource2/indexing.h index 623e9ff82..5c2b5d544 100644 --- a/FFmpegSource2/indexing.h +++ b/FFmpegSource2/indexing.h @@ -33,7 +33,7 @@ extern "C" { #include "utils.h" #include "ffms.h" -#define INDEXVERSION 4 +#define INDEXVERSION 5 #define INDEXID 0x53920873 struct IndexHeader {