FFmpegSource2: matroska as audio source, first try

Originally committed to SVN as r2366.
This commit is contained in:
Fredrik Mellbin 2008-09-19 21:35:46 +00:00
parent 5b8ce78d40
commit bf9f2b50b7
11 changed files with 695 additions and 9 deletions

View File

@ -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;
}

View File

@ -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 <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
}
#include <vector>
#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

View File

@ -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);
}

View File

@ -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

View File

@ -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);

View File

@ -0,0 +1,197 @@
<html>
<head>
<title>
FFmpegSource2 Documentation
</title>
</head>
<body>
<h1>FFmpegSource2 Documentation</h1>
<p>
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.
</p>
<h2>Compatibility - Video</h2>
<ul>
<li>AVI, MKV, MP4, FLV: Frame accurate</li>
<li>WMV: Frame accurate(?) but avformat seems to pick keyframes relatively far away</li>
<li>OGM: Messed up first frame and seeking produces smearing with seekmode=3, incredibly slow seeking without, remux to mkv or avi</li>
<li>VOB: No rff flags applied, frame accurate?</li>
<li>MPG: Seeking seems to be off by one or two frames now and then</li>
<li>M2TS, TS: Linear access only (seekmode=-1)</li>
<li>Image files: Most formats can be opened if seekmode=-1 is set</li>
</ul>
<h2>Compatibility - Audio</h2>
<ul>
<li>All formats are written to separate wave64 files</li>
<li>No wave64 reader is provided</li>
</ul>
<h2>Usage</h2>
<p>
<b>FFIndex("string source, string cachefile = source + ".ffindex", int trackmask = 0, string audiofile = source, bool overwrite = false)</b><br />
Used to invoke indexing separately and to write audio tracks to disk as wave64 files
</p>
<p>
<b>FFVideoSource(string source, int track, bool cache = true, string cachefile = source + ".ffindex", string pp, int threads = -1, string timecodes, int seekmode = 1)</b><br />
Opens video, will invoke indexing with the defaults if no preexisting index is found
</p>
<p>
<b>FFPP(clip, string pp)</b><br />
Separate postprocessing which also seems to include a few simple deinterlacers
</p>
<p>
<b>SWScale(clip, width = -1, height = -1, resizer = "BICUBIC", colorspace = "")</b><br />
Separate postprocessing which also seems to include a few simple deinterlacers
</p>
<p>
<b>source:</b>
Source file.
</p>
<p>
<b>trackmask:</b>
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.
</p>
<p>
<b>audiofile:</b>
The base name to use for the decoded audio. Track number and delay is appended.
</p>
<p>
<b>overwrite:</b>
Forces reindexing even if a valid index already exists. May be useful for trackmask changes or testing.
</p>
<p>
<b>track:</b>
Track number as seen by the relevant demuxer, starts from 0, -1 means it will pick the first suitable track.
</p>
<p>
<b>timecodes:</b>
File to output timecodes to, if the file exists it will be overwritten.
</p>
<p>
<b>cache:</b>
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.
</p>
<p>
<b>cachefile</b>
Where to write the cache information.
</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.
</p>
<p>
<b>threads:</b>
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.
</p>
<p>
<b>seekmode:</b>
Force how seeking is handled, has no effect on matroska files which always use the equivalent of seekmode=1<br />
<b>-1:</b> 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<br />
<b>0:</b> linear access, the definition of slow but should make some formats "usable"<br />
<b>1:</b> safe normal, bases seeking decisions on the reported keyframe positions<br />
<b>2:</b> unsafe normal, same as 1 but no error will be thrown if the exact destination has to be guessed<br />
<b>3:</b> 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
</p>
<p>
<b>width &amp; height:</b>
Width and height to resize to. Value below or equal to 0 is the same as specifying the input dimensions.
</p>
<p>
<b>resizer:</b>
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.
</p>
<p>
<b>colorspace:</b>
The colorspace to convert to. The names are YV12, YUY2, RGB24, RGB32 and the empty string for same as input.
</p>
<h2>PP string format</h2>
<pre>
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 <quantizer> force quantizer
Usage:
<filterName>[:<option>[:<option>...]][[,|/][-]<filterName>[:<option>...]]...
long form example:
vdeblock:autoq/hdeblock:autoq/linblenddeint default,-vdeblock
short form example:
vb:a/hb:a/lb de,-vb
more examples:
tn:64:128:256
</pre>
<h2>Compiling</h2>
<p><b>zlib</b> from http://www.zlib.net/</p>
<p><b>FFmpeg svn</b> from http://ffmpeg.mplayerhq.hu/</p>
<p><b>Required FFmpeg Configuration:</b>
./configure --enable-memalign-hack --enable-gpl --enable-swscale --enable-postproc
<p><b>Suggested Additional Options:</b>
--enable-w32threads --disable-encoders --disable-muxers --disable-debug</p>
<p>
Note that --enable-w32threads is required for multithreaded decoding to work.
</p>
<h2>Changes</h2>
<ul>
<li>2.00 beta 1<ul>
<li>Can now be used as a stand alone library for making indices and retrieving frames</li>
<li>Rewrote most things</li>
<li>Updated FFmpeg to rev 15301</li>
</ul></li>
</ul>
</body>
</html>

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View File

@ -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;
}

View File

@ -33,7 +33,7 @@ extern "C" {
#include "utils.h"
#include "ffms.h"
#define INDEXVERSION 4
#define INDEXVERSION 5
#define INDEXID 0x53920873
struct IndexHeader {