FFMS2 beta 4

This commit breaks the shit out of linux

Originally committed to SVN as r2571.
This commit is contained in:
Fredrik Mellbin 2008-12-30 23:57:22 +00:00
parent 8a4e4efff3
commit ff8d019d58
13 changed files with 805 additions and 27 deletions

121
FFmpegSource2/coparser.h Normal file
View file

@ -0,0 +1,121 @@
/*
* Copyright (c) 2004-2008 Mike Matsnev. All Rights Reserved.
*
* $Id: CoParser.h,v 1.21 2008/03/29 15:41:28 mike Exp $
*
*/
#ifndef COPARSER_H
#define COPARSER_H
// Generic multimedia container parsers support
// random access stream, this will be provided by
// IMMContainer's client
[uuid("8E192E9F-E536-4027-8D46-664CC7A102C5")]
interface IMMStream : public IUnknown {
// read count bytes starting at position
STDMETHOD(Read)( unsigned long long position,
void *buffer,
unsigned int *count) = 0;
// scan the file starting at position for the signature
// signature can't have zero bytes
STDMETHOD(Scan)( unsigned long long *position,
unsigned int signature) = 0;
};
[uuid("A237C873-C6AD-422E-90DB-7CB4627DCFD9")]
interface IMMStreamOpen : public IUnknown {
STDMETHOD(Open)(LPCWSTR name) = 0;
};
[uuid("D8FF7213-6E09-4256-A2E5-5872C798B128")]
interface IMMFrame : public IMediaSample2 {
// track number must be the same as returned by
// IMMContainer->EnumTracks() iterator
STDMETHOD_(unsigned, GetTrack)() = 0;
STDMETHOD(SetTrack)(unsigned) = 0;
STDMETHOD_(unsigned, GetPre)() = 0;
STDMETHOD(SetPre)(unsigned) = 0;
};
[uuid("B8324E2A-21A9-46A1-8922-70C55D06311A")]
interface IMMErrorInfo : public IUnknown {
STDMETHOD(LogError)(BSTR message) = 0; // message is owned by the caller
STDMETHOD(LogWarning)(BSTR message) = 0;
};
[uuid("C7120EDB-528C-4ebe-BB53-DA8E70E618EE")]
interface IMemAlloc : public IUnknown {
STDMETHOD(GetBuffer)(HANDLE hAbortEvt, DWORD size, IMMFrame **pS) = 0;
};
// container itself should support IPropertyBag and return
// at least Duration[UI8] (in ns) property
// if a containter supports multiple segments in the same
// physical file it should return SegmentTop[UI8] property
// that return the offset of the first byte after this
// segment's end
[uuid("A369001B-F292-45f7-A942-84F9C8C0718A")]
interface IMMContainer : public IUnknown {
STDMETHOD(Open)( IMMStream *stream,
unsigned long long position,
IMMErrorInfo *einfo,
IMemAlloc *alloc) = 0;
STDMETHOD(GetProgress)(unsigned long long *cur,unsigned long long *max) = 0;
STDMETHOD(AbortOpen)() = 0;
// pu->Next() returns objects supporting IPropertyBag interface
STDMETHOD(EnumTracks)(IEnumUnknown **pu) = 0;
// pu->Next() returns objects supporting IProperyBag and IEnumUnknown interfaces
STDMETHOD(EnumEditions)(IEnumUnknown **pu) = 0;
// pu->Next() returns objects supporting IProperyBag and IMMStream interfaces
STDMETHOD(EnumAttachments)(IEnumUnknown **pu) = 0;
// S_FALSE is end of stream, S_OK next valid frame returned, E_ABORT wait aborted
STDMETHOD(ReadFrame)(HANDLE hAbortEvt, IMMFrame **frame) = 0;
// seeking
STDMETHOD(Seek)(unsigned long long timecode,unsigned flags) = 0;
};
/* FIXME: duplicated in matroska parser
enum { // Track type
TT_VIDEO = 1,
TT_AUDIO = 2,
TT_SUBS = 17,
TT_INTERLEAVED = 0x10001,
};
*/
enum { // Seek flags
MMSF_PREV_KF = 1,
MMSF_NEXT_KF = 2
};
/* Track properties [IPropertyBag from EnumTracks->Next()]
Name Type Optional
DefaultDuration UI8 Yes in ns
Video.Interlaced BOOL Yes
Video.DisplayWidth UI4 Yes
Video.DisplayHeight UI4 Yes
Video.PixelWidth UI4 No
Video.PixelHeight UI4 No
CodecID BSTR No
Type UI4 No TT_* enumeration
CodecPrivate ARRAY|UI1 Yes
Audio.Channels UI4 No
Audio.BitDepth UI4 Yes
Audio.SamplingFreq UI4 No
Audio.OutputSamplingFreq UI4 Yes
Language BSTR Yes
Name BSTR Yes
FOURCC UI4 Yes
*/
#endif

View file

@ -264,7 +264,7 @@ MatroskaAudioSource::MatroskaAudioSource(const char *SourceFile, int Track, Fram
CodecContext->extradata = (uint8_t *)TI->CodecPrivate;
CodecContext->extradata_size = TI->CodecPrivateSize;
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI));
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
if (Codec == NULL) {
Free(false);
_snprintf(ErrorMsg, MsgSize, "Video codec not found");

View file

@ -219,12 +219,18 @@ AVSValue __cdecl CreateSWScale(AVSValue Args, void* UserData, IScriptEnvironment
return new SWScale(Args[0].AsClip(), Args[1].AsInt(0), Args[2].AsInt(0), Args[3].AsString("BICUBIC"), Args[4].AsString(""), Env);
}
AVSValue __cdecl FFNoLog(AVSValue Args, void* UserData, IScriptEnvironment* Env) {
av_log_set_level(AV_LOG_QUIET);
return 0;
}
extern "C" __declspec(dllexport) const char* __stdcall AvisynthPluginInit2(IScriptEnvironment* Env) {
Env->AddFunction("FFIndex", "[source]s[cachefile]s[indexmask]i[dumpmask]i[audiofile]s[overwrite]b", CreateFFIndex, 0);
Env->AddFunction("FFVideoSource", "[source]s[track]i[cache]b[cachefile]s[fpsnum]i[fpsden]i[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);
Env->AddFunction("FFNoLog", "", FFNoLog, 0);
return "FFmpegSource - The Second Coming";
}

View file

@ -59,6 +59,7 @@ FFMS_API(VideoBase *) FFMS_CreateVideoSource(const char *SourceFile, int Track,
switch (TrackIndices->Decoder) {
case 0: return new FFVideoSource(SourceFile, Track, TrackIndices, PP, Threads, SeekMode, ErrorMsg, MsgSize);
case 1: return new MatroskaVideoSource(SourceFile, Track, TrackIndices, PP, Threads, ErrorMsg, MsgSize);
case 2: return new HaaliTSVideoSource(SourceFile, Track, TrackIndices, PP, Threads, ErrorMsg, MsgSize);
default:
_snprintf(ErrorMsg, MsgSize, "Unsupported format");
return NULL;

View file

@ -55,7 +55,7 @@ enum TrackType {
FFMS_TYPE_AUDIO = 1,
};
// PixelFormat declarations from avutil.h so external libraries don't necessarily have to include and ffmpeg headers
// PixelFormat declarations from avutil.h so external libraries don't necessarily have to include ffmpeg headers
enum FFMS_PixelFormat {
FFMS_PIX_FMT_NONE= -1,
FFMS_PIX_FMT_YUV420P, ///< Planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)

View file

@ -29,7 +29,7 @@ Opens files using ffmpeg and nothing else. May be frame accurate on good days. T
<h2>Usage</h2>
<p>
<b>FFIndex("string source, string cachefile = source + ".ffindex", int indexmask = 0, int dumpmask = 0, string audiofile = source, bool overwrite = false)</b><br />
<b>FFIndex(string source, string cachefile = source + ".ffindex", int indexmask = 0, int dumpmask = 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>
@ -38,6 +38,11 @@ Opens files using ffmpeg and nothing else. May be frame accurate on good days. T
Opens video, will invoke indexing with the defaults if no preexisting index is found
</p>
<p>
<b>FFAudioSource(string source, int track, bool cache = true, string cachefile = source + ".ffindex")</b><br />
Opens audio, <b>if an index already exists it needs to contain a suitable audio index or empty audio will be returned</b>, 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
@ -48,6 +53,11 @@ Opens files using ffmpeg and nothing else. May be frame accurate on good days. T
Separate postprocessing which also seems to include a few simple deinterlacers
</p>
<b>FFNoLog()</b><br />
Disable all logging output from FFmpeg
</p>
<p>
<b>source:</b>
Source file.
@ -191,13 +201,19 @@ Note that --enable-w32threads is required for multithreaded decoding to work.
<h2>Changes</h2>
<ul>
<li>2.00 beta 4<ul>
<li>Added the function FFNoLog which suppresses all messages from ffmpeg</li>
<li>Experimental new TS parsing using Haali's splitter (with bugs)</li>
<li>Everything is now compiled with VS2008 and GCC 4.3.2</li>
<li>Updated FFmpeg to rev 16383 (no libfaad2 this time)</li>
</ul></li>
<li>2.00 beta 3<ul>
<li>Compiled with libfaad2 again (has anyone seen a single aac file lavc can open right now?)</li>
<li>More API changes (and even more are likely to come)</li>
<li>Several access violations and memory leaks on opening and indexing files fixed</li>
<li>Added a VFR to CFR mode</li>
<li>Readded FFAudioSource support for other containers (glitches still present now and then but no separate raw cache is required and possibly less buggy</li>
<li>Readded FFAudioSource support for other containers (glitches still present now and then but no separate raw cache is required and possibly less buggy)</li>
<li>Renamed the dll to FFMS2.dll, FFMS2 is now the official short name of the project</li>
<li>Updated FFmpeg to rev 15522</li>
</ul></li>
@ -220,4 +236,3 @@ Note that --enable-w32threads is required for multithreaded decoding to work.
</body>
</html>

View file

@ -327,7 +327,7 @@ AVFrameLite *FFVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) {
} 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) ? Frames[n].DTS : Frames[ClosestKF].DTS, AVSEEK_FLAG_BACKWARD);
av_seek_frame(FormatContext, VideoTrack, (SeekMode == 3) ? Frames[n].DTS : Frames[FFMAX(ClosestKF - 20, 0)].DTS, AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers(CodecContext);
HasSeeked = true;
}
@ -429,7 +429,7 @@ MatroskaVideoSource::MatroskaVideoSource(const char *SourceFile, int Track,
CodecContext->extradata_size = TI->CodecPrivateSize;
CodecContext->thread_count = Threads;
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI));
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
if (Codec == NULL) {
Free(false);
_snprintf(ErrorMsg, MsgSize, "Video codec not found");
@ -560,3 +560,232 @@ AVFrameLite *MatroskaVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSi
LastFrameNum = n;
return OutputFrame(DecodeFrame);
}
void HaaliTSVideoSource::Free(bool CloseCodec) {
if (CloseCodec)
avcodec_close(CodecContext);
av_free(CodecContext);
}
HaaliTSVideoSource::HaaliTSVideoSource(const char *SourceFile, int Track,
FrameIndex *TrackIndices, const char *PP,
int Threads, char *ErrorMsg, unsigned MsgSize) {
AVCodec *Codec = NULL;
CodecContext = NULL;
VideoTrack = Track;
Frames = (*TrackIndices)[VideoTrack];
if (Frames.size() == 0) {
_snprintf(ErrorMsg, MsgSize, "Video track contains no frames");
throw ErrorMsg;
}
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
CLSID clsid = Haali_TS_Parser;
if (FAILED(pMMC.CoCreateInstance(clsid))) {
_snprintf(ErrorMsg, MsgSize, "Can't create parser");
throw ErrorMsg;
}
CComPtr<IMemAlloc> pMA;
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
_snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
throw ErrorMsg;
}
CComPtr<IMMStream> pMS;
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) {
_snprintf(ErrorMsg, MsgSize, "Can't create disk file reader");
throw ErrorMsg;
}
WCHAR WSourceFile[2048];
mbstowcs(WSourceFile, SourceFile, 2000);
CComQIPtr<IMMStreamOpen> pMSO(pMS);
if (FAILED(pMSO->Open(WSourceFile))) {
_snprintf(ErrorMsg, MsgSize, "Can't open file");
throw ErrorMsg;
}
if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
_snprintf(ErrorMsg, MsgSize, "Can't parse file");
throw ErrorMsg;
}
BSTR CodecID = NULL;
uint8_t * CodecPrivate = NULL;
int CodecPrivateSize = 0;
int CurrentTrack = 0;
CComPtr<IEnumUnknown> pEU;
if (SUCCEEDED(pMMC->EnumTracks(&pEU))) {
CComPtr<IUnknown> pU;
while (pEU->Next(1, &pU, NULL) == S_OK) {
if (CurrentTrack++ == Track) {
CComQIPtr<IPropertyBag> pBag = pU;
if (pBag) {
VARIANT pV;
pV.vt = VT_EMPTY;
if (pBag->Read(L"CodecID", &pV, NULL) == S_OK)
CodecID = pV.bstrVal;
pV.vt = VT_EMPTY;
if (pBag->Read(L"CodecPrivate", &pV, NULL) == S_OK) {
CodecPrivate = (uint8_t *)pV.parray->pvData;
CodecPrivateSize = pV.parray->cbElements;
}
}
}
pU = NULL;
}
}
CodecContext = avcodec_alloc_context();
CodecContext->extradata = CodecPrivate;
CodecContext->extradata_size = CodecPrivateSize;
CodecContext->thread_count = Threads;
char ACodecID[2048];
wcstombs(ACodecID, CodecID, 2000);
Codec = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, CodecPrivate));
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
int64_t Dummy;
if (DecodeNextFrame(DecodeFrame, &Dummy, ErrorMsg, MsgSize)) {
Free(true);
throw ErrorMsg;
}
VP.Width = CodecContext->width;
VP.Height = CodecContext->height;;
VP.FPSDenominator = 1;
VP.FPSNumerator = 30;
VP.NumFrames = Frames.size();
VP.PixelFormat = CodecContext->pix_fmt;
VP.FirstTime = ((Frames.front().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
VP.LastTime = ((Frames.back().DTS * Frames.TB.Num) / (double)Frames.TB.Den) / 1000;
if (VP.Width <= 0 || VP.Height <= 0) {
Free(true);
_snprintf(ErrorMsg, MsgSize, "Codec returned zero size video");
throw ErrorMsg;
}
if (InitPP(PP, CodecContext->pix_fmt, ErrorMsg, MsgSize)) {
Free(true);
throw ErrorMsg;
}
// Calculate the average framerate
if (Frames.size() >= 2) {
double DTSDiff = (double)(Frames.back().DTS - Frames.front().DTS);
// FIXME
VP.FPSDenominator = (unsigned int)((DTSDiff * 1000000000) / (double)1000 / (double)(VP.NumFrames - 1) + 0.5);
VP.FPSNumerator = 1000000;
}
// Output the already decoded frame so it isn't wasted
OutputFrame(DecodeFrame);
LastFrameNum = 0;
// Set AR variables
// VP.SARNum = TI->AV.Video.DisplayWidth * TI->AV.Video.PixelHeight;
// VP.SARDen = TI->AV.Video.DisplayHeight * TI->AV.Video.PixelWidth;
// Set crop variables
// VP.CropLeft = TI->AV.Video.CropL;
// VP.CropRight = TI->AV.Video.CropR;
// VP.CropTop = TI->AV.Video.CropT;
// VP.CropBottom = TI->AV.Video.CropB;
}
HaaliTSVideoSource::~HaaliTSVideoSource() {
Free(true);
}
int HaaliTSVideoSource::DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize) {
int FrameFinished = 0;
*AFirstStartTime = -1;
for (;;) {
CComPtr<IMMFrame> pMMF;
if (pMMC->ReadFrame(NULL, &pMMF) != S_OK)
break;
REFERENCE_TIME Ts, Te;
if (*AFirstStartTime < 0 && SUCCEEDED(pMMF->GetTime(&Ts, &Te)))
*AFirstStartTime = Ts;
if (pMMF->GetTrack() == VideoTrack) {
BYTE *Data = NULL;
if (FAILED(pMMF->GetPointer(&Data)))
goto Error;
avcodec_decode_video(CodecContext, AFrame, &FrameFinished, Data, pMMF->GetActualDataLength());
if (FrameFinished)
goto Done;
}
}
// Flush the last frames
if (CodecContext->has_b_frames)
avcodec_decode_video(CodecContext, AFrame, &FrameFinished, NULL, 0);
if (!FrameFinished)
goto Error;
Error:
Done:
return 0;
}
AVFrameLite *HaaliTSVideoSource::GetFrame(int n, char *ErrorMsg, unsigned MsgSize) {
// PPFrame always holds frame LastFrameNum even if no PP is applied
if (LastFrameNum == n)
return OutputFrame(DecodeFrame);
bool HasSeeked = false;
if (n < CurrentFrame || Frames.FindClosestKeyFrame(n) > CurrentFrame) {
int64_t dtsp = Frames[n].DTS;
pMMC->Seek(Frames[n].DTS, MMSF_PREV_KF);
// FIXME for some reason required to make it seek properly
//avcodec_flush_buffers(CodecContext);
HasSeeked = true;
}
do {
int64_t StartTime;
if (DecodeNextFrame(DecodeFrame, &StartTime, ErrorMsg, MsgSize))
return NULL;
if (HasSeeked) {
HasSeeked = false;
if (StartTime < 0 || (CurrentFrame = Frames.FrameFromDTS(StartTime)) < 0) {
_snprintf(ErrorMsg, MsgSize, "Frame accurate seeking is not possible in this file\n");
return NULL;
}
}
CurrentFrame++;
} while (CurrentFrame <= n);
LastFrameNum = n;
return OutputFrame(DecodeFrame);
}

View file

@ -33,6 +33,17 @@ extern "C" {
#include "utils.h"
#include "ffms.h"
#ifdef _WIN32
# define _WIN32_DCOM
# include <windows.h>
# include <tchar.h>
# include <atlbase.h>
# include <dshow.h>
# include "CoParser.h"
# include <initguid.h>
# include "guids.h"
#endif
class VideoBase {
private:
pp_context_t *PPContext;
@ -90,4 +101,16 @@ public:
AVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
};
class HaaliTSVideoSource : public VideoBase {
private:
CComPtr<IMMContainer> pMMC;
void Free(bool CloseCodec);
int DecodeNextFrame(AVFrame *AFrame, int64_t *AFirstStartTime, char *ErrorMsg, unsigned MsgSize);
public:
HaaliTSVideoSource(const char *SourceFile, int Track, FrameIndex *TrackIndices, const char *PP, int Threads, char *ErrorMsg, unsigned MsgSize);
~HaaliTSVideoSource();
AVFrameLite *GetFrame(int n, char *ErrorMsg, unsigned MsgSize);
};
#endif

138
FFmpegSource2/guids.h Normal file
View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2004-2008 Mike Matsnev. All Rights Reserved.
*
* $Id: guids.h,v 1.16 2008/03/29 15:41:30 mike Exp $
*
*/
#ifndef GUIDS_H
#define GUIDS_H
// {941A4793-A705-4312-8DFC-C11CA05F397E}
DEFINE_GUID(CLSID_RealAudioDecoder,
0x941A4793, 0xA705, 0x4312, 0x8D, 0xFC, 0xC1, 0x1C, 0xA0, 0x5F, 0x39, 0x7E);
// {B8CBBAD8-AA1A-4cea-B95E-730041A55EF0}
DEFINE_GUID(MEDIASUBTYPE_WavpackHybrid,
0xb8cbbad8, 0xaa1a, 0x4cea, 0xb9, 0x5e, 0x73, 0x0, 0x41, 0xa5, 0x5e, 0xf0);
// {1541C5C0-CDDF-477d-BC0A-86F8AE7F8354}
DEFINE_GUID(MEDIASUBTYPE_FLAC_FRAMED,
0x1541c5c0, 0xcddf, 0x477d, 0xbc, 0xa, 0x86, 0xf8, 0xae, 0x7f, 0x83, 0x54);
// {8D2FD10B-5841-4a6b-8905-588FEC1ADED9}
DEFINE_GUID(MEDIASUBTYPE_Vorbis2,
0x8d2fd10b, 0x5841, 0x4a6b, 0x89, 0x5, 0x58, 0x8f, 0xec, 0x1a, 0xde, 0xd9);
// {B36E107F-A938-4387-93C7-55E966757473}
DEFINE_GUID(FORMAT_VorbisFormat2,
0xb36e107f, 0xa938, 0x4387, 0x93, 0xc7, 0x55, 0xe9, 0x66, 0x75, 0x74, 0x73);
// {E487EB08-6B26-4be9-9DD3-993434D313FD}
DEFINE_GUID(MEDIATYPE_Subtitle,
0xe487eb08, 0x6b26, 0x4be9, 0x9d, 0xd3, 0x99, 0x34, 0x34, 0xd3, 0x13, 0xfd);
// {87C0B230-03A8-4fdf-8010-B27A5848200D}
DEFINE_GUID(MEDIASUBTYPE_UTF8,
0x87c0b230, 0x3a8, 0x4fdf, 0x80, 0x10, 0xb2, 0x7a, 0x58, 0x48, 0x20, 0xd);
// {3020560F-255A-4ddc-806E-6C5CC6DCD70A}
DEFINE_GUID(MEDIASUBTYPE_SSA,
0x3020560f, 0x255a, 0x4ddc, 0x80, 0x6e, 0x6c, 0x5c, 0xc6, 0xdc, 0xd7, 0xa);
// {326444F7-686F-47ff-A4B2-C8C96307B4C2}
DEFINE_GUID(MEDIASUBTYPE_ASS,
0x326444f7, 0x686f, 0x47ff, 0xa4, 0xb2, 0xc8, 0xc9, 0x63, 0x7, 0xb4, 0xc2);
// {370689E7-B226-4f67-978D-F10BC1A9C6AE}
DEFINE_GUID(MEDIASUBTYPE_ASS2,
0x370689e7, 0xb226, 0x4f67, 0x97, 0x8d, 0xf1, 0xb, 0xc1, 0xa9, 0xc6, 0xae);
// {B753B29A-0A96-45be-985F-68351D9CAB90}
DEFINE_GUID(MEDIASUBTYPE_USF,
0xb753b29a, 0xa96, 0x45be, 0x98, 0x5f, 0x68, 0x35, 0x1d, 0x9c, 0xab, 0x90);
// {F7239E31-9599-4e43-8DD5-FBAF75CF37F1}
DEFINE_GUID(MEDIASUBTYPE_VOBSUB,
0xf7239e31, 0x9599, 0x4e43, 0x8d, 0xd5, 0xfb, 0xaf, 0x75, 0xcf, 0x37, 0xf1);
// {0B10E53F-ABF9-4581-BE9C-2C9A5EC6F2E0}
DEFINE_GUID(MEDIASUBTYPE_VOBSUB2,
0xb10e53f, 0xabf9, 0x4581, 0xbe, 0x9c, 0x2c, 0x9a, 0x5e, 0xc6, 0xf2, 0xe0);
// {5965E924-63F9-4a64-B71E-F75188FD6384}
DEFINE_GUID(MEDIASUBTYPE_DXRSub,
0x5965e924, 0x63f9, 0x4a64, 0xb7, 0x1e, 0xf7, 0x51, 0x88, 0xfd, 0x63, 0x84);
// {A33D2F7D-96BC-4337-B23B-A8B9FBC295E9}
DEFINE_GUID(FORMAT_SubtitleInfo,
0xa33d2f7d, 0x96bc, 0x4337, 0xb2, 0x3b, 0xa8, 0xb9, 0xfb, 0xc2, 0x95, 0xe9);
DEFINE_GUID(MEDIASUBTYPE_AC3,
0x00002000, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
DEFINE_GUID(MEDIASUBTYPE_EAC3,
0x0000EAC3, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
DEFINE_GUID(MEDIASUBTYPE_DTS,
0x00002001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
// {B855F837-194F-4d9a-9358-E31ED6B49D03}
DEFINE_GUID(MEDIASUBTYPE_VC1,
0xb855f837, 0x194f, 0x4d9a, 0x93, 0x58, 0xe3, 0x1e, 0xd6, 0xb4, 0x9d, 0x3);
// {93A22E7A-5091-45ef-BA61-6DA26156A5D0}
DEFINE_GUID(CLSID_DirectVobSubFilter,
0x93a22e7a, 0x5091, 0x45ef, 0xba, 0x61, 0x6d, 0xa2, 0x61, 0x56, 0xa5, 0xd0);
// {9852A670-F845-491b-9BE6-EBD841B8A613}
DEFINE_GUID(CLSID_DirectVobSubFilter2,
0x9852a670, 0xf845, 0x491b, 0x9b, 0xe6, 0xeb, 0xd8, 0x41, 0xb8, 0xa6, 0x13);
// {F6D90F11-9C73-11D3-B32E-00C04F990BB4}
DEFINE_GUID(CLSID_MSXML2,
0xF6D90F11, 0x9C73, 0x11D3, 0xB3, 0x2E, 0x00, 0xC0, 0x4F, 0x99, 0x0B, 0xB4);
// {9F062738-CD84-4F54-A3C4-BD5EB44F416B}
DEFINE_GUID(CLSID_SonicAudioDec,
0x9F062738, 0xCD84, 0x4F54, 0xA3, 0xC4, 0xBD, 0x5E, 0xB4, 0x4F, 0x41, 0x6B);
// {53D9DE0B-FC61-4650-9773-74D13CC7E582}
DEFINE_GUID(CLSID_DiskFile,
0x53d9de0b, 0xfc61, 0x4650, 0x97, 0x73, 0x74, 0xd1, 0x3c, 0xc7, 0xe5, 0x82);
// {BD4FB4BE-809D-487b-ADD6-F7D164247E52}
DEFINE_GUID(CLSID_HTTPStream,
0xbd4fb4be, 0x809d, 0x487b, 0xad, 0xd6, 0xf7, 0xd1, 0x64, 0x24, 0x7e, 0x52);
// {90C7D10E-CE9A-479b-A238-1A0F2396DE43}
DEFINE_GUID(CLSID_MemAlloc,
0x90c7d10e, 0xce9a, 0x479b, 0xa2, 0x38, 0x1a, 0xf, 0x23, 0x96, 0xde, 0x43);
DEFINE_GUID(MEDIASUBTYPE_WVC1,
0x31435657, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
DEFINE_GUID(MEDIASUBTYPE_WMV3,
0x33564d57, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);
// FIXME: move somewhere else?
DEFINE_GUID(Haali_TS_Parser,
0xB841F346, 0x4835, 0x4de8, 0xAA, 0x5E, 0x2E, 0x7C, 0xD2, 0xD4, 0xC4, 0x35);
typedef struct tagVORBISFORMAT2
{
DWORD Channels;
DWORD SamplesPerSec;
DWORD BitsPerSample;
DWORD HeaderSize[3]; // 0: Identification, 1: Comment, 2: Setup
} VORBISFORMAT2, *PVORBISFORMAT2, FAR *LPVORBISFORMAT2;
#pragma pack(push, 1)
typedef struct {
DWORD dwOffset;
CHAR IsoLang[4]; // three letter lang code + terminating zero
WCHAR TrackName[256]; // 256 bytes ought to be enough for everyone :)
} SUBTITLEINFO;
#pragma pack(pop)
#endif

View file

@ -27,6 +27,24 @@
#include "indexing.h"
#include "wave64writer.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include "MatroskaParser.h"
#include "stdiostream.h"
}
#ifdef _WIN32
# define _WIN32_DCOM
# include <windows.h>
# include <tchar.h>
# include <atlbase.h>
# include <dshow.h>
# include "CoParser.h"
# include <initguid.h>
# include "guids.h"
#endif
class MatroskaAudioContext {
public:
Wave64Writer *W64W;
@ -71,6 +89,26 @@ public:
}
};
#ifdef HAALITS
class HaaliTSIndexMemory {
private:
int16_t *DecodingBuffer;
MatroskaAudioContext *AudioContexts;
public:
HaaliTSIndexMemory(int Tracks, int16_t *&DecodingBuffer, MatroskaAudioContext *&AudioContexts) {
DecodingBuffer = new int16_t[AVCODEC_MAX_AUDIO_FRAME_SIZE*10];
AudioContexts = new MatroskaAudioContext[Tracks];
this->DecodingBuffer = DecodingBuffer;
this->AudioContexts = AudioContexts;
}
~HaaliTSIndexMemory() {
delete [] DecodingBuffer;
delete [] AudioContexts;
}
};
#endif
class MatroskaIndexMemory {
private:
int16_t *DecodingBuffer;
@ -158,6 +196,203 @@ int WriteIndex(const char *IndexFile, FrameIndex *TrackIndices, char *ErrorMsg,
return 0;
}
#ifdef HAALITS
static FrameIndex *MakeHaaliTSIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, IndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize) {
::CoInitializeEx(NULL, COINIT_MULTITHREADED);
CLSID clsid = Haali_TS_Parser;
CComPtr<IMMContainer> pMMC;
if (FAILED(pMMC.CoCreateInstance(clsid))) {
_snprintf(ErrorMsg, MsgSize, "Can't create parser");
return NULL;
}
CComPtr<IMemAlloc> pMA;
if (FAILED(pMA.CoCreateInstance(CLSID_MemAlloc))) {
_snprintf(ErrorMsg, MsgSize, "Can't create memory allocator");
return NULL;
}
CComPtr<IMMStream> pMS;
if (FAILED(pMS.CoCreateInstance(CLSID_DiskFile))) {
_snprintf(ErrorMsg, MsgSize, "Can't create disk file reader");
return NULL;
}
WCHAR WSourceFile[2048];
mbstowcs(WSourceFile, SourceFile, 2000);
CComQIPtr<IMMStreamOpen> pMSO(pMS);
if (FAILED(pMSO->Open(WSourceFile))) {
_snprintf(ErrorMsg, MsgSize, "Can't open file");
return NULL;
}
if (FAILED(pMMC->Open(pMS, 0, NULL, pMA))) {
_snprintf(ErrorMsg, MsgSize, "Can't parse file");
return NULL;
}
int NumTracks = 0;
CComPtr<IEnumUnknown> pEU;
if (SUCCEEDED(pMMC->EnumTracks(&pEU))) {
CComPtr<IUnknown> pU;
while (pEU->Next(1, &pU, NULL) == S_OK) {
NumTracks++;
pU = NULL;
}
}
// Audio stuff
int16_t *db;
MatroskaAudioContext *AudioContexts;
HaaliTSIndexMemory IM = HaaliTSIndexMemory(NumTracks, db, AudioContexts);
FrameIndex *TrackIndices = new FrameIndex();
TrackIndices->Decoder = 2;
int TrackTypes[32];
int CurrentTrack = 0;
pEU = NULL;
if (SUCCEEDED(pMMC->EnumTracks(&pEU))) {
CComPtr<IUnknown> pU;
while (pEU->Next(1, &pU, NULL) == S_OK) {
CComQIPtr<IPropertyBag> pBag = pU;
BSTR CodecID = NULL;
TrackTypes[CurrentTrack] = -200;
uint8_t * CodecPrivate = NULL;
int CodecPrivateSize = 0;
if (pBag) {
VARIANT pV;
pV.vt = VT_EMPTY;
if (pBag->Read(L"CodecID", &pV, NULL) == S_OK)
CodecID = pV.bstrVal;
pV.vt = VT_EMPTY;
if (pBag->Read(L"Type", &pV, NULL) == S_OK)
TrackTypes[CurrentTrack] = pV.uintVal;
pV.vt = VT_EMPTY;
if (pBag->Read(L"CodecPrivate", &pV, NULL) == S_OK) {
CodecPrivate = (uint8_t *)pV.parray->pvData;
CodecPrivateSize = pV.parray->cbElements;
}
}
TrackIndices->push_back(FrameInfoVector(1, 1000000000, TrackTypes[CurrentTrack] - 1));
if (IndexMask & (1 << CurrentTrack) && TrackTypes[CurrentTrack] == TT_AUDIO) {
AVCodecContext *AudioCodecContext = avcodec_alloc_context();
AudioCodecContext->extradata = CodecPrivate;
AudioCodecContext->extradata_size = CodecPrivateSize;
AudioContexts[CurrentTrack].CTX = AudioCodecContext;
char ACodecID[2048];
wcstombs(ACodecID, CodecID, 2000);
AVCodec *AudioCodec = avcodec_find_decoder(MatroskaToFFCodecID(ACodecID, NULL));
if (AudioCodec == NULL) {
av_free(AudioCodecContext);
AudioContexts[CurrentTrack].CTX = NULL;
_snprintf(ErrorMsg, MsgSize, "Audio codec not found");
return NULL;
}
if (avcodec_open(AudioCodecContext, AudioCodec) < 0) {
av_free(AudioCodecContext);
AudioContexts[CurrentTrack].CTX = NULL;
_snprintf(ErrorMsg, MsgSize, "Could not open audio codec");
return NULL;
}
} else {
IndexMask &= ~(1 << CurrentTrack);
}
pU = NULL;
CurrentTrack++;
}
}
//
for (;;) {
if (IP) {
if ((*IP)(0, 0, 1, Private)) {
_snprintf(ErrorMsg, MsgSize, "Cancelled by user");
delete TrackIndices;
return NULL;
}
}
CComPtr<IMMFrame> pMMF;
if (pMMC->ReadFrame(NULL, &pMMF) != S_OK)
break;
REFERENCE_TIME Ts, Te;
HRESULT hr = pMMF->GetTime(&Ts, &Te);
unsigned int CurrentTrack = pMMF->GetTrack();
// Only create index entries for video for now to save space
if (TrackTypes[CurrentTrack] == TT_VIDEO) {
(*TrackIndices)[CurrentTrack].push_back(FrameInfo(Ts, pMMF->IsSyncPoint() == S_OK));
} else if (TrackTypes[CurrentTrack] == TT_AUDIO && (IndexMask & (1 << CurrentTrack))) {
/* (*TrackIndices)[Track].push_back(FrameInfo(AudioContexts[Track].CurrentSample, FilePos, FrameSize, (FrameFlags & FRAME_KF) != 0));
ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC, ErrorMsg, MsgSize);
int Size = FrameSize;
uint8_t *Data = MC.Buffer;
AVCodecContext *AudioCodecContext = AudioContexts[Track].CTX;
while (Size > 0) {
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
int Ret = avcodec_decode_audio2(AudioCodecContext, db, &dbsize, Data, Size);
if (Ret < 0) {
if (IgnoreDecodeErrors) {
(*TrackIndices)[Track].clear();
IndexMask &= ~(1 << Track);
break;
} else {
_snprintf(ErrorMsg, MsgSize, "Audio decoding error");
delete TrackIndices;
return NULL;
}
}
if (Ret > 0) {
Size -= Ret;
Data += Ret;
}
if (dbsize > 0)
AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels);
if (dbsize > 0 && (DumpMask & (1 << Track))) {
// Delay writer creation until after an audio frame has been decoded. This ensures that all parameters are known when writing the headers.
if (!AudioContexts[Track].W64W) {
char ABuf[50];
std::string WN(AudioFile);
int Offset = StartTime * mkv_TruncFloat(mkv_GetTrackInfo(MF, Track)->TimecodeScale) / (double)1000000;
_snprintf(ABuf, sizeof(ABuf), ".%02d.delay.%d.w64", Track, Offset);
WN += ABuf;
AudioContexts[Track].W64W = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioCodecContext->sample_fmt),
AudioCodecContext->channels, AudioCodecContext->sample_rate, AudioFMTIsFloat(AudioCodecContext->sample_fmt));
}
AudioContexts[Track].W64W->WriteData(db, dbsize);
}
}
*/
}
}
SortTrackIndices(TrackIndices);
return TrackIndices;
}
#endif
static FrameIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int DumpMask, const char *AudioFile, bool IgnoreDecodeErrors, IndexCallback IP, void *Private, char *ErrorMsg, unsigned MsgSize) {
MatroskaFile *MF;
char ErrorMessage[256];
@ -188,13 +423,14 @@ static FrameIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int
MatroskaIndexMemory IM = MatroskaIndexMemory(mkv_GetNumTracks(MF), db, AudioContexts, MF, &MC);
for (unsigned int i = 0; i < mkv_GetNumTracks(MF); i++) {
if (IndexMask & (1 << i) && mkv_GetTrackInfo(MF, i)->Type == TT_AUDIO) {
TrackInfo *TI = mkv_GetTrackInfo(MF, i);
if (IndexMask & (1 << i) && TI->Type == TT_AUDIO) {
AVCodecContext *AudioCodecContext = avcodec_alloc_context();
AudioCodecContext->extradata = (uint8_t *)mkv_GetTrackInfo(MF, i)->CodecPrivate;
AudioCodecContext->extradata_size = mkv_GetTrackInfo(MF, i)->CodecPrivateSize;
AudioCodecContext->extradata = (uint8_t *)TI->CodecPrivate;
AudioCodecContext->extradata_size = TI->CodecPrivateSize;
AudioContexts[i].CTX = AudioCodecContext;
if (mkv_GetTrackInfo(MF, i)->CompEnabled) {
if (TI->CompEnabled) {
AudioContexts[i].CS = cs_Create(MF, i, ErrorMessage, sizeof(ErrorMessage));
if (AudioContexts[i].CS == NULL) {
av_free(AudioCodecContext);
@ -204,7 +440,7 @@ static FrameIndex *MakeMatroskaIndex(const char *SourceFile, int IndexMask, int
}
}
AVCodec *AudioCodec = avcodec_find_decoder(MatroskaToFFCodecID(mkv_GetTrackInfo(MF, i)));
AVCodec *AudioCodec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
if (AudioCodec == NULL) {
av_free(AudioCodecContext);
AudioContexts[i].CTX = NULL;
@ -320,6 +556,14 @@ FrameIndex *MakeIndex(const char *SourceFile, int IndexMask, int DumpMask, const
av_close_input_file(FormatContext);
return MakeMatroskaIndex(SourceFile, IndexMask, DumpMask, AudioFile, IgnoreDecodeErrors, IP, Private, ErrorMsg, MsgSize);
}
#ifdef HAALITS
// Do haali ts indexing instead?
if (!strcmp(FormatContext->iformat->name, "mpegts")) {
av_close_input_file(FormatContext);
return MakeHaaliTSIndex(SourceFile, IndexMask, DumpMask, AudioFile, IgnoreDecodeErrors, IP, Private, ErrorMsg, MsgSize);
}
#endif
if (av_find_stream_info(FormatContext) < 0) {
av_close_input_file(FormatContext);
@ -521,6 +765,9 @@ int FrameInfoVector::ClosestFrameFromDTS(int64_t DTS) {
Frame = i;
}
}
//int64_t tmp = at(2).DTS - at(1).DTS;
//ATLASSERT(BestDiff == 0);
return Frame;
}

View file

@ -21,19 +21,11 @@
#ifndef INDEXING_H
#define INDEXING_H
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include "MatroskaParser.h"
#include "stdiostream.h"
}
#include <vector>
#include "utils.h"
#include "ffms.h"
#define INDEXVERSION 8
#define INDEXVERSION 11
#define INDEXID 0x53920873
struct IndexHeader {

View file

@ -144,12 +144,11 @@ typedef struct BITMAPINFOHEADER {
((uint32_t)(uint8_t)(ch0) | ((uint32_t)(uint8_t)(ch1) << 8) |\
((uint32_t)(uint8_t)(ch2) << 16) | ((uint32_t)(uint8_t)(ch3) << 24 ))
CodecID MatroskaToFFCodecID(TrackInfo *TI) {
char *Codec = TI->CodecID;
CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate) {
/* Video Codecs */
if (!strcmp(Codec, "V_MS/VFW/FOURCC")) {
// fourcc list from ffdshow
switch (((BITMAPINFOHEADER *)TI->CodecPrivate)->biCompression) {
switch (((BITMAPINFOHEADER *)CodecPrivate)->biCompression) {
case MAKEFOURCC('F', 'F', 'D', 'S'):
case MAKEFOURCC('F', 'V', 'F', 'W'):
case MAKEFOURCC('X', 'V', 'I', 'D'):
@ -379,6 +378,7 @@ CodecID MatroskaToFFCodecID(TrackInfo *TI) {
else if (!strcmp(Codec, "A_DTS"))
return CODEC_ID_DTS;
else if (!strcmp(Codec, "A_PCM/INT/LIT")) {
/* FIXME
switch (TI->AV.Audio.BitDepth) {
case 8: return CODEC_ID_PCM_S8;
case 16: return CODEC_ID_PCM_S16LE;
@ -386,7 +386,10 @@ CodecID MatroskaToFFCodecID(TrackInfo *TI) {
case 32: return CODEC_ID_PCM_S32LE;
default: return CODEC_ID_NONE;
}
*/
return CODEC_ID_NONE;
} else if (!strcmp(Codec, "A_PCM/INT/BIG")) {
/* FIXME
switch (TI->AV.Audio.BitDepth) {
case 8: return CODEC_ID_PCM_S8;
case 16: return CODEC_ID_PCM_S16BE;
@ -394,6 +397,8 @@ CodecID MatroskaToFFCodecID(TrackInfo *TI) {
case 32: return CODEC_ID_PCM_S32BE;
default: return CODEC_ID_NONE;
}
*/
return CODEC_ID_NONE;
} else if (!strcmp(Codec, "A_PCM/FLOAT/IEEE"))
return CODEC_ID_PCM_F32LE; // only a most likely guess, may do bad things
else if (!strcmp(Codec, "A_FLAC"))

View file

@ -22,13 +22,14 @@
#define UTILS_H
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libpostproc/postprocess.h>
#include <libavutil/sha1.h>
#include "MatroskaParser.h"
}
#include "stdiostream.h"
}
struct MatroskaReaderContext {
public:
@ -50,6 +51,6 @@ public:
int GetCPUFlags();
int ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context, char *ErrorMsg, unsigned MsgSize);
bool AudioFMTIsFloat(SampleFormat FMT);
CodecID MatroskaToFFCodecID(TrackInfo *TI);
CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate);
#endif