Update ffms2 to r312. Fixes a bunch of rather critical bugs.

Originally committed to SVN as r4487.
This commit is contained in:
Karl Blomster 2010-06-12 14:51:33 +00:00
parent 60b9e5dee6
commit ea6dac551e
17 changed files with 445 additions and 382 deletions

View file

@ -22,7 +22,7 @@
#define FFMS_H
// Version format: major - minor - micro - bump
#define FFMS_VERSION ((2 << 24) | (13 << 16)| (0 << 8) | 1)
#define FFMS_VERSION ((2 << 24) | (13 << 16) | (1 << 8) | 0)
#include <stdint.h>
@ -36,10 +36,14 @@
#ifdef _WIN32
# define FFMS_CC __stdcall
# ifdef FFMS_EXPORTS
# define FFMS_API(ret) EXTERN_C __declspec(dllexport) ret FFMS_CC
# ifdef _MSC_VER
# ifdef FFMS_EXPORTS
# define FFMS_API(ret) EXTERN_C __declspec(dllexport) ret FFMS_CC
# else
# define FFMS_API(ret) EXTERN_C __declspec(dllimport) ret FFMS_CC
# endif
# else
# define FFMS_API(ret) EXTERN_C __declspec(dllimport) ret FFMS_CC
# define FFMS_API(ret) EXTERN_C ret FFMS_CC
# endif
#else
# define FFMS_CC
@ -253,13 +257,14 @@ FFMS_API(void) FFMS_ResetOutputFormatV(FFMS_VideoSource *V);
FFMS_API(int) FFMS_SetPP(FFMS_VideoSource *V, const char *PP, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(void) FFMS_ResetPP(FFMS_VideoSource *V);
FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index);
FFMS_API(int) FFMS_GetSourceType(FFMS_Index *Index);
FFMS_API(int) FFMS_GetFirstTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(int) FFMS_GetFirstIndexedTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo);
FFMS_API(int) FFMS_GetNumTracks(FFMS_Index *Index);
FFMS_API(int) FFMS_GetNumTracksI(FFMS_Indexer *Indexer);
FFMS_API(int) FFMS_GetTrackType(FFMS_Track *T);
FFMS_API(int) FFMS_GetTrackTypeI(FFMS_Indexer *Indexer, int Track);
FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track);
FFMS_API(const char *) FFMS_GetCodecNameI(FFMS_Indexer *Indexer, int Track);
FFMS_API(int) FFMS_GetNumFrames(FFMS_Track *T);
FFMS_API(const FFMS_FrameInfo *) FFMS_GetFrameInfo(FFMS_Track *T, int Frame);
FFMS_API(FFMS_Track *) FFMS_GetTrackFromIndex(FFMS_Index *Index, int Track);

View file

@ -51,10 +51,17 @@
# warning "Your FFmpeg is too old to support reporting colorspace and luma range information. The corresponding fields of FFMS_VideoProperties will be set to 0. Please update FFmpeg to get rid of this warning."
# endif
# endif
# if (LIBAVCODEC_VERSION_INT) < (AV_VERSION_INT(52,30,2))
# define AV_PKT_FLAG_KEY PKT_FLAG_KEY
# endif
#endif
#ifndef AV_PKT_FLAG_KEY
# define AV_PKT_FLAG_KEY PKT_FLAG_KEY
#ifdef LIBSWSCALE_VERSION_INT
# if (LIBSWSCALE_VERSION_INT) < (AV_VERSION_INT(0,8,0))
# define FFMS_SWS_CONST_PARAM
# else
# define FFMS_SWS_CONST_PARAM const
# endif
#endif

View file

@ -104,7 +104,7 @@ FFMS_AudioSource::FFMS_AudioSource(const char *SourceFile, FFMS_Index *Index, in
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
"Not an audio track");
if (Index[Track].size() == 0)
if (Index->at(Track).size() == 0)
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
"Audio track contains no audio frames");

View file

@ -1,121 +0,0 @@
/*
* 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
interface __declspec(uuid("8E192E9F-E536-4027-8D46-664CC7A102C5")) IMMStream;
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;
};
interface __declspec(uuid("A237C873-C6AD-422E-90DB-7CB4627DCFD9")) IMMStreamOpen;
interface IMMStreamOpen : public IUnknown {
STDMETHOD(Open)(LPCWSTR name) = 0;
};
interface __declspec(uuid("D8FF7213-6E09-4256-A2E5-5872C798B128")) IMMFrame;
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;
};
interface __declspec(uuid("B8324E2A-21A9-46A1-8922-70C55D06311A")) IMMErrorInfo;
interface IMMErrorInfo : public IUnknown {
STDMETHOD(LogError)(BSTR message) = 0; // message is owned by the caller
STDMETHOD(LogWarning)(BSTR message) = 0;
};
interface __declspec(uuid("C7120EDB-528C-4ebe-BB53-DA8E70E618EE")) IMemAlloc;
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
interface __declspec(uuid("A369001B-F292-45f7-A942-84F9C8C0718A")) IMMContainer;
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

@ -25,7 +25,9 @@
#include "audiosource.h"
#include "indexing.h"
#ifdef FFMS_WIN_DEBUG
# include <windows.h>
#endif
static bool FFmpegInited = false;
bool HasHaaliMPEG = false;
@ -71,6 +73,9 @@ void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl)
FFMS_API(void) FFMS_Init(int CPUFeatures) {
if (!FFmpegInited) {
av_register_all();
#if defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS)
ffms_patch_lavf_file_open();
#endif
#ifdef FFMS_WIN_DEBUG
av_log_set_callback(av_log_windebug_callback);
av_log_set_level(AV_LOG_INFO);
@ -98,6 +103,8 @@ FFMS_API(void) FFMS_SetLogLevel(int Level) {
}
FFMS_API(FFMS_VideoSource *) FFMS_CreateVideoSource(const char *SourceFile, int Track, FFMS_Index *Index, int Threads, int SeekMode, FFMS_ErrorInfo *ErrorInfo) {
if (Threads < 1)
Threads = 1;
try {
switch (Index->Decoder) {
case FFMS_SOURCE_LAVF:
@ -227,6 +234,10 @@ FFMS_API(void) FFMS_DestroyIndex(FFMS_Index *Index) {
delete Index;
}
FFMS_API(int) FFMS_GetSourceType(FFMS_Index *Index) {
return Index->Decoder;
}
FFMS_API(int) FFMS_GetFirstTrackOfType(FFMS_Index *Index, int TrackType, FFMS_ErrorInfo *ErrorInfo) {
ClearErrorInfo(ErrorInfo);
for (int i = 0; i < static_cast<int>(Index->size()); i++)

View file

@ -1,143 +0,0 @@
/*
* 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_MPEG_PARSER,
0xB841F346, 0x4835, 0x4de8, 0xAA, 0x5E, 0x2E, 0x7C, 0xD2, 0xD4, 0xC4, 0x35);
DEFINE_GUID(HAALI_OGG_PARSER,
0xDB43B405, 0x43AA, 0x4F01, 0x82, 0xD8, 0xD8, 0x4D, 0x47, 0xE6, 0x01, 0x9C);
//DB43B405-43AA-4F01-82D8-D84D47E6019C
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

@ -25,6 +25,7 @@
extern "C" {
#include <libavutil/sha1.h>
#include <zlib.h>
}
#define INDEXID 0x53920873
@ -51,6 +52,7 @@ struct TrackHeader {
uint32_t Frames;
int64_t Num;
int64_t Den;
uint32_t UseDTS;
};
SharedVideoContext::SharedVideoContext(bool FreeCodecContext) {
@ -155,8 +157,11 @@ int FFMS_Track::ClosestFrameFromPTS(int64_t PTS) {
int FFMS_Track::FindClosestVideoKeyFrame(int Frame) {
Frame = FFMIN(FFMAX(Frame, 0), static_cast<int>(size()) - 1);
for (int i = Frame; i > 0; i--)
if (at(i).KeyFrame)
return i;
if (at(i).KeyFrame) {
for (int j = i; j >= 0; j--)
if (at(at(j).OriginalPos).KeyFrame)
return j;
}
return 0;
}
@ -174,12 +179,14 @@ FFMS_Track::FFMS_Track() {
this->TT = FFMS_TYPE_UNKNOWN;
this->TB.Num = 0;
this->TB.Den = 0;
this->UseDTS = false;
}
FFMS_Track::FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT) {
FFMS_Track::FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT, bool UseDTS) {
this->TT = TT;
this->TB.Num = Num;
this->TB.Den = Den;
this->UseDTS = UseDTS;
}
void FFMS_Index::CalculateFileSignature(const char *Filename, int64_t *Filesize, uint8_t Digest[20]) {
@ -264,6 +271,31 @@ bool FFMS_Index::CompareFileSignature(const char *Filename) {
return (CFilesize == Filesize && !memcmp(CDigest, Digest, sizeof(Digest)));
}
#define CHUNK 65536
static unsigned int z_def(ffms_fstream *IndexStream, z_stream *stream, void *in, size_t in_sz, int finish) {
unsigned int total = 0, have;
int ret;
char out[CHUNK];
if (!finish && (in_sz == 0 || in == NULL)) return 0;
stream->next_in = (Bytef*) in;
stream->avail_in = in_sz;
do {
do {
stream->avail_out = CHUNK;
stream->next_out = (Bytef*) out;
ret = deflate(stream, finish ? Z_FINISH : Z_NO_FLUSH);
have = CHUNK - stream->avail_out;
if (have) IndexStream->write(out, have);
total += have;
} while (stream->avail_out == 0);
} while (finish && ret != Z_STREAM_END);
if (finish) deflateEnd(stream);
return total;
}
void FFMS_Index::WriteIndex(const char *IndexFile) {
ffms_fstream IndexStream(IndexFile, std::ios::out | std::ios::binary | std::ios::trunc);
@ -273,6 +305,12 @@ void FFMS_Index::WriteIndex(const char *IndexFile) {
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str());
}
z_stream stream;
memset(&stream, 0, sizeof(z_stream));
if (deflateInit(&stream, 9) != Z_OK) {
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to initialize zlib");
}
// Write the index file header
IndexHeader IH;
IH.Id = INDEXID;
@ -287,18 +325,72 @@ void FFMS_Index::WriteIndex(const char *IndexFile) {
IH.FileSize = Filesize;
memcpy(IH.FileSignature, Digest, sizeof(Digest));
IndexStream.write(reinterpret_cast<char *>(&IH), sizeof(IH));
z_def(&IndexStream, &stream, &IH, sizeof(IndexHeader), 0);
for (unsigned int i = 0; i < IH.Tracks; i++) {
FFMS_Track &ctrack = at(i);
TrackHeader TH;
TH.TT = at(i).TT;
TH.Frames = at(i).size();
TH.Num = at(i).TB.Num;;
TH.Den = at(i).TB.Den;
TH.TT = ctrack.TT;
TH.Frames = ctrack.size();
TH.Num = ctrack.TB.Num;;
TH.Den = ctrack.TB.Den;
TH.UseDTS = ctrack.UseDTS;
IndexStream.write(reinterpret_cast<char *>(&TH), sizeof(TH));
IndexStream.write(reinterpret_cast<char *>(FFMS_GET_VECTOR_PTR(at(i))), TH.Frames * sizeof(TFrameInfo));
FFMS_Track temptrack;
temptrack.resize(TH.Frames);
if (TH.Frames)
temptrack[0] = ctrack[0];
for (size_t j = 1; j < ctrack.size(); j++) {
temptrack[j] = ctrack[j];
temptrack[j].FilePos = ctrack[j].FilePos - ctrack[j - 1].FilePos;
temptrack[j].OriginalPos = ctrack[j].OriginalPos - ctrack[j - 1].OriginalPos;
temptrack[j].PTS = ctrack[j].PTS - ctrack[j - 1].PTS;
temptrack[j].SampleStart = ctrack[j].SampleStart - ctrack[j - 1].SampleStart;
}
z_def(&IndexStream, &stream, &TH, sizeof(TrackHeader), 0);
if (TH.Frames)
z_def(&IndexStream, &stream, FFMS_GET_VECTOR_PTR(temptrack), TH.Frames * sizeof(TFrameInfo), 0);
}
z_def(&IndexStream, &stream, NULL, 0, 1);
}
static unsigned int z_inf(ffms_fstream *Index, z_stream *stream, void *in, size_t in_sz, void *out, size_t out_sz) {
int ret;
if (out_sz == 0 || out == 0) return 0;
stream->next_out = (Bytef*) out;
stream->avail_out = out_sz;
do {
if (stream->avail_in) memmove(in, stream->next_in, stream->avail_in);
Index->read(((char*)in) + stream->avail_in, in_sz - stream->avail_in);
stream->next_in = (Bytef*) in;
stream->avail_in += Index->gcount();
ret = inflate(stream, Z_SYNC_FLUSH);
switch (ret) {
case Z_NEED_DICT:
inflateEnd(stream);
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Dictionary error.");
break;
case Z_DATA_ERROR:
inflateEnd(stream);
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Data error.");
break;
case Z_MEM_ERROR:
inflateEnd(stream);
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to read data: Memory error.");
break;
case Z_STREAM_END:
inflateEnd(stream);
return out_sz - stream->avail_out;
}
} while (stream->avail_out);
return out_sz;
}
void FFMS_Index::ReadIndex(const char *IndexFile) {
@ -309,10 +401,17 @@ void FFMS_Index::ReadIndex(const char *IndexFile) {
buf << "Failed to open '" << IndexFile << "' for reading";
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str());
}
z_stream stream;
memset(&stream, 0, sizeof(z_stream));
unsigned char in[CHUNK];
if (inflateInit(&stream) != Z_OK) {
throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, "Failed to initialize zlib");
}
// Read the index file header
IndexHeader IH;
Index.read(reinterpret_cast<char *>(&IH), sizeof(IH));
z_inf(&Index, &stream, &in, CHUNK, &IH, sizeof(IndexHeader));
if (IH.Id != INDEXID) {
std::ostringstream buf;
buf << "'" << IndexFile << "' is not a valid index file";
@ -340,16 +439,24 @@ void FFMS_Index::ReadIndex(const char *IndexFile) {
Decoder = IH.Decoder;
Filesize = IH.FileSize;
memcpy(Digest, IH.FileSignature, sizeof(Digest));
try {
for (unsigned int i = 0; i < IH.Tracks; i++) {
TrackHeader TH;
Index.read(reinterpret_cast<char *>(&TH), sizeof(TH));
push_back(FFMS_Track(TH.Num, TH.Den, static_cast<FFMS_TrackType>(TH.TT)));
z_inf(&Index, &stream, &in, CHUNK, &TH, sizeof(TrackHeader));
push_back(FFMS_Track(TH.Num, TH.Den, static_cast<FFMS_TrackType>(TH.TT), TH.UseDTS != 0));
FFMS_Track &ctrack = at(i);
if (TH.Frames) {
at(i).resize(TH.Frames);
Index.read(reinterpret_cast<char *>(FFMS_GET_VECTOR_PTR(at(i))), TH.Frames * sizeof(TFrameInfo));
ctrack.resize(TH.Frames);
z_inf(&Index, &stream, &in, CHUNK, FFMS_GET_VECTOR_PTR(ctrack), TH.Frames * sizeof(TFrameInfo));
}
for (size_t j = 1; j < ctrack.size(); j++) {
ctrack[j].FilePos = ctrack[j].FilePos + ctrack[j - 1].FilePos;
ctrack[j].OriginalPos = ctrack[j].OriginalPos + ctrack[j - 1].OriginalPos;
ctrack[j].PTS = ctrack[j].PTS + ctrack[j - 1].PTS;
ctrack[j].SampleStart = ctrack[j].SampleStart + ctrack[j - 1].SampleStart;
}
}
@ -361,7 +468,7 @@ void FFMS_Index::ReadIndex(const char *IndexFile) {
}
FFMS_Index::FFMS_Index() {
// this comment documents nothing
}
FFMS_Index::FFMS_Index(int64_t Filesize, uint8_t Digest[20]) {

View file

@ -84,6 +84,7 @@ class FFMS_Track : public std::vector<TFrameInfo> {
public:
FFMS_TrackType TT;
FFMS_TrackTimeBase TB;
bool UseDTS;
int FindClosestVideoKeyFrame(int Frame);
int FindClosestAudioKeyFrame(int64_t Sample);
@ -92,7 +93,7 @@ public:
void WriteTimecodes(const char *TimecodeFile);
FFMS_Track();
FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT);
FFMS_Track(int64_t Num, int64_t Den, FFMS_TrackType TT, bool UseDTS = false);
};
class FFMS_Index : public std::vector<FFMS_Track> {

View file

@ -152,10 +152,10 @@ void FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) {
while (av_read_frame(FormatContext, &Packet) >= 0) {
if (Packet.stream_index == AudioTrack) {
if (LastPTS < 0) {
LastPTS = Packet.dts;
} else if (LastPTS != Packet.dts) {
LastPTS = Packet.pts;
} else if (LastPTS != Packet.pts) {
for (size_t i = 0; i < Frames.size(); i++)
if (Frames[i].PTS == Packet.dts) {
if (Frames[i].PTS == Packet.pts) {
// The current match was consumed
CurrentAudioBlock = i + 1;
break;

View file

@ -88,9 +88,13 @@ FFMS_Index *FFLAVFIndexer::DoIndexing() {
AVPacket Packet, TempPacket;
InitNullPacket(Packet);
InitNullPacket(TempPacket);
std::vector<int64_t> LastValidTS;
LastValidTS.resize(FormatContext->nb_streams, ffms_av_nopts_value);
while (av_read_frame(FormatContext, &Packet) >= 0) {
// Update progress
if (IC) {
// FormatContext->pb can apparently be NULL when opening images.
if (IC && FormatContext->pb) {
if ((*IC)(FormatContext->pb->pos, FormatContext->file_size, ICPrivate))
throw FFMS_Exception(FFMS_ERROR_CANCELLED, FFMS_ERROR_USER,
"Cancelled by user");
@ -102,12 +106,24 @@ FFMS_Index *FFLAVFIndexer::DoIndexing() {
int OBSize;
int RepeatPict = -1;
// Duplicated code
if (!(*TrackIndices)[Packet.stream_index].UseDTS && Packet.pts != ffms_av_nopts_value)
LastValidTS[Packet.stream_index] = Packet.pts;
if (LastValidTS[Packet.stream_index] == ffms_av_nopts_value)
(*TrackIndices)[Packet.stream_index].UseDTS = true;
if ((*TrackIndices)[Packet.stream_index].UseDTS && Packet.dts != ffms_av_nopts_value)
LastValidTS[Packet.stream_index] = Packet.dts;
if (LastValidTS[Packet.stream_index] == ffms_av_nopts_value)
throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_PARSER,
"Invalid initial pts and dts");
//
if (VideoContexts[Packet.stream_index].Parser) {
av_parser_parse2(VideoContexts[Packet.stream_index].Parser, VideoContexts[Packet.stream_index].CodecContext, &OB, &OBSize, Packet.data, Packet.size, Packet.pts, Packet.dts, Packet.pos);
RepeatPict = VideoContexts[Packet.stream_index].Parser->repeat_pict;
}
(*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::VideoFrameInfo(Packet.dts, RepeatPict, (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0));
(*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::VideoFrameInfo(LastValidTS[Packet.stream_index], RepeatPict, (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0));
} else if (FormatContext->streams[Packet.stream_index]->codec->codec_type == CODEC_TYPE_AUDIO && (IndexMask & (1 << Packet.stream_index))) {
int64_t StartSample = AudioContexts[Packet.stream_index].CurrentSample;
AVCodecContext *AudioCodecContext = FormatContext->streams[Packet.stream_index]->codec;
@ -115,6 +131,18 @@ FFMS_Index *FFLAVFIndexer::DoIndexing() {
TempPacket.size = Packet.size;
TempPacket.flags = Packet.flags;
// Duplicated code
if (!(*TrackIndices)[Packet.stream_index].UseDTS && Packet.pts != ffms_av_nopts_value)
LastValidTS[Packet.stream_index] = Packet.pts;
if (LastValidTS[Packet.stream_index] == ffms_av_nopts_value)
(*TrackIndices)[Packet.stream_index].UseDTS = true;
if ((*TrackIndices)[Packet.stream_index].UseDTS && Packet.dts != ffms_av_nopts_value)
LastValidTS[Packet.stream_index] = Packet.dts;
if (LastValidTS[Packet.stream_index] == ffms_av_nopts_value)
throw FFMS_Exception(FFMS_ERROR_INDEXING, FFMS_ERROR_PARSER,
"Invalid initial pts and dts");
//
while (TempPacket.size > 0) {
int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10;
int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket);
@ -146,7 +174,7 @@ FFMS_Index *FFLAVFIndexer::DoIndexing() {
WriteAudio(AudioContexts[Packet.stream_index], TrackIndices.get(), Packet.stream_index, dbsize);
}
(*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::AudioFrameInfo(Packet.dts, StartSample,
(*TrackIndices)[Packet.stream_index].push_back(TFrameInfo::AudioFrameInfo(LastValidTS[Packet.stream_index], StartSample,
static_cast<unsigned int>(AudioContexts[Packet.stream_index].CurrentSample - StartSample), (Packet.flags & AV_PKT_FLAG_KEY) ? 1 : 0));
}

View file

@ -45,7 +45,8 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index,
"Video track is unseekable");
CodecContext = FormatContext->streams[VideoTrack]->codec;
CodecContext->thread_count = Threads;
if (avcodec_thread_init(CodecContext, Threads))
CodecContext->thread_count = 1;
Codec = avcodec_find_decoder(CodecContext->codec_id);
if (Codec == NULL)
@ -96,11 +97,20 @@ FFLAVFVideo::FFLAVFVideo(const char *SourceFile, int Track, FFMS_Index *Index,
}
// Adjust framerate to match the duration of the first frame
if (Frames.size() >= 2) {
unsigned int PTSDiff = (unsigned int)FFMAX(Frames[1].PTS - Frames[0].PTS, 1);
VP.FPSDenominator *= PTSDiff;
for (size_t i = 1; i < Frames.size(); i++) {
if (Frames[i].PTS != Frames[0].PTS) {
unsigned int PTSDiff = (unsigned int)(Frames[i].PTS - Frames[0].PTS);
VP.FPSDenominator *= PTSDiff;
VP.FPSDenominator /= i;
break;
}
}
// attempt to correct framerate to the proper NTSC fraction, if applicable
CorrectNTSCRationalFramerate(&VP.FPSNumerator, &VP.FPSDenominator);
// correct the timebase, if necessary
CorrectTimebase(&VP, &Frames.TB);
// Cannot "output" to PPFrame without doing all other initialization
// This is the additional mess required for seekmode=-1 to work in a reasonable way
OutputFrame(DecodeFrame);
@ -116,22 +126,31 @@ void FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime) {
int FrameFinished = 0;
*AStartTime = -1;
if (InitialDecode == -1) {
if (DelayCounter > CodecContext->has_b_frames) {
DelayCounter--;
goto Done;
} else {
InitialDecode = 0;
}
}
while (av_read_frame(FormatContext, &Packet) >= 0) {
if (Packet.stream_index == VideoTrack) {
if (*AStartTime < 0)
*AStartTime = Packet.dts;
if (*AStartTime < 0) {
if (Frames.UseDTS)
*AStartTime = Packet.dts;
else
*AStartTime = Packet.pts;
}
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
if (CodecContext->codec_id == CODEC_ID_MPEG4) {
if (IsPackedFrame(Packet)) {
MPEG4Counter++;
} else if (IsNVOP(Packet) && MPEG4Counter && FrameFinished) {
MPEG4Counter--;
} else if (IsNVOP(Packet) && !MPEG4Counter && !FrameFinished) {
av_free_packet(&Packet);
goto Done;
}
if (!FrameFinished)
DelayCounter++;
if (DelayCounter > CodecContext->has_b_frames && !InitialDecode) {
av_free_packet(&Packet);
goto Done;
}
}
@ -152,7 +171,8 @@ void FFLAVFVideo::DecodeNextFrame(int64_t *AStartTime) {
goto Error;
Error:
Done:;
Done:
if (InitialDecode == 1) InitialDecode = -1;
}
FFMS_Frame *FFLAVFVideo::GetFrame(int n) {
@ -173,6 +193,8 @@ FFMS_Frame *FFLAVFVideo::GetFrame(int n) {
av_seek_frame(FormatContext, VideoTrack, Frames[0].PTS, AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers(CodecContext);
CurrentFrame = 0;
DelayCounter = 0;
InitialDecode = 1;
}
} else {
// 10 frames is used as a margin to prevent excessive seeking since the predicted best keyframe isn't always selected by avformat
@ -183,6 +205,8 @@ ReSeek:
AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers(CodecContext);
HasSeeked = true;
DelayCounter = 0;
InitialDecode = 1;
}
}
} else if (n < CurrentFrame) {
@ -192,11 +216,14 @@ ReSeek:
do {
int64_t StartTime;
if (CurrentFrame+CodecContext->has_b_frames >= n)
CodecContext->skip_frame = AVDISCARD_DEFAULT;
else
CodecContext->skip_frame = AVDISCARD_NONREF;
DecodeNextFrame(&StartTime);
if (HasSeeked) {
HasSeeked = false;
MPEG4Counter = 0;
// Is the seek destination time known? Does it belong to a frame?
if (StartTime < 0 || (CurrentFrame = Frames.FrameFromPTS(StartTime)) < 0) {

View file

@ -27,6 +27,10 @@
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <stdio.h>
@ -34,11 +38,19 @@
#include <setjmp.h>
#ifdef _WIN32
#ifdef _MSC_VER
// MS names some functions differently
#define alloca _alloca
#define inline __inline
#else /* _MSC_VER */
// support for building with MinGW on Windows
#include <malloc.h>
#endif /* _MSC_VER */
#include <tchar.h>
#endif /* _WIN32 */
#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif
#ifndef EVCBUG
@ -286,7 +298,8 @@ static void myvsnprintf_int(char **pdest,char *de,int width,int zero,
static void myvsnprintf(char *dest,unsigned dsize,const char *fmt,va_list ap) {
// s,d,x,u,ll
char *de = dest + dsize - 1;
int state = 0, width, zero, neg, ll;
int state, width, zero, neg, ll;
state = width = zero = neg = ll = 0;
if (dsize <= 1) {
if (dsize > 0)
@ -904,16 +917,6 @@ static void readLangCC(MatroskaFile *mf, ulonglong len, char lcc[4]) {
#define STRGETA(f,v,len) STRGETF(f,v,len,myalloca)
#define STRGETM(f,v,len) STRGETF(f,v,len,f->cache->memalloc)
static int IsWritingApp(MatroskaFile *mf,const char *str) {
const char *cp = mf->Seg.WritingApp;
if (!cp)
return 0;
while (*str && *str++==*cp++) ;
return !*str;
}
static void parseEBML(MatroskaFile *mf,ulonglong toplen) {
ulonglong v;
char buf[32];
@ -1212,10 +1215,6 @@ static void parseAudioInfo(MatroskaFile *mf,ulonglong toplen,struct TrackInfo *t
break;
case 0x6264: // BitDepth
v = readUInt(mf,(unsigned)len);
#if 0
if ((v<1 || v>255) && !IsWritingApp(mf,"AVI-Mux GUI"))
errorjmp(mf,"Invalid BitDepth: %d",(int)v);
#endif
ti->AV.Audio.BitDepth = (unsigned char)v;
break;
ENDFOR(mf);
@ -1245,7 +1244,7 @@ static void parseTrackEntry(MatroskaFile *mf,ulonglong toplen) {
ulonglong v;
char *cp = NULL, *cs = NULL;
size_t cplen = 0, cslen = 0, cpadd = 0;
unsigned CompScope, num_comp = 0;
unsigned CompScope = 0, num_comp = 0;
if (mf->nTracks >= MAX_TRACKS)
errorjmp(mf,"Too many tracks.");

View file

@ -76,7 +76,8 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
}
CodecContext = avcodec_alloc_context();
CodecContext->thread_count = Threads;
if (avcodec_thread_init(CodecContext, Threads))
CodecContext->thread_count = 1;
Codec = avcodec_find_decoder(MatroskaToFFCodecID(TI->CodecID, TI->CodecPrivate));
if (Codec == NULL)
@ -127,6 +128,11 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track,
VP.FPSNumerator = 1000000;
}
// attempt to correct framerate to the proper NTSC fraction, if applicable
CorrectNTSCRationalFramerate(&VP.FPSNumerator, &VP.FPSDenominator);
// correct the timebase, if necessary
CorrectTimebase(&VP, &Frames.TB);
// Output the already decoded frame so it isn't wasted
OutputFrame(DecodeFrame);
@ -147,6 +153,15 @@ void FFMatroskaVideo::DecodeNextFrame() {
InitNullPacket(Packet);
unsigned int FrameSize;
if (InitialDecode == -1) {
if (DelayCounter > CodecContext->has_b_frames) {
DelayCounter--;
goto Done;
} else {
InitialDecode = 0;
}
}
while (PacketNumber < Frames.size()) {
// The additional indirection is because the packets are stored in
@ -167,15 +182,10 @@ void FFMatroskaVideo::DecodeNextFrame() {
avcodec_decode_video2(CodecContext, DecodeFrame, &FrameFinished, &Packet);
if (CodecContext->codec_id == CODEC_ID_MPEG4) {
if (IsPackedFrame(Packet)) {
MPEG4Counter++;
} else if (IsNVOP(Packet) && MPEG4Counter && FrameFinished) {
MPEG4Counter--;
} else if (IsNVOP(Packet) && !MPEG4Counter && !FrameFinished) {
goto Done;
}
}
if (!FrameFinished)
DelayCounter++;
if (DelayCounter > CodecContext->has_b_frames && !InitialDecode)
goto Done;
if (FrameFinished)
goto Done;
@ -192,7 +202,8 @@ void FFMatroskaVideo::DecodeNextFrame() {
goto Error;
Error:
Done:;
Done:
if (InitialDecode == 1) InitialDecode = -1;
}
FFMS_Frame *FFMatroskaVideo::GetFrame(int n) {
@ -203,13 +214,18 @@ FFMS_Frame *FFMatroskaVideo::GetFrame(int n) {
int ClosestKF = Frames.FindClosestVideoKeyFrame(n);
if (CurrentFrame > n || ClosestKF > CurrentFrame + 10) {
MPEG4Counter = 0;
DelayCounter = 0;
InitialDecode = 1;
PacketNumber = ClosestKF;
CurrentFrame = ClosestKF;
avcodec_flush_buffers(CodecContext);
}
do {
if (CurrentFrame+CodecContext->has_b_frames >= n)
CodecContext->skip_frame = AVDISCARD_DEFAULT;
else
CodecContext->skip_frame = AVDISCARD_NONREF;
DecodeNextFrame();
CurrentFrame++;
} while (CurrentFrame <= n);

View file

@ -20,13 +20,19 @@
#include <string.h>
#include <errno.h>
#include "utils.h"
#include "indexing.h"
#ifdef FFMS_USE_UTF8_PATHS
#ifdef _WIN32
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#ifdef FFMS_USE_UTF8_PATHS
# include <io.h>
# include <fcntl.h>
extern "C" {
# include "libavutil/avstring.h"
}
#endif
#endif
@ -202,20 +208,6 @@ void InitNullPacket(AVPacket &pkt) {
pkt.size = 0;
}
bool IsPackedFrame(AVPacket &pkt) {
for (int i = 0; i < pkt.size - 5; i++)
if (pkt.data[i] == 0x00 && pkt.data[i + 1] == 0x00 && pkt.data[i + 2] == 0x01 && pkt.data[i + 3] == 0xB6 && (pkt.data[i + 4] & 0x40))
for (i = i + 5; i < pkt.size - 5; i++)
if (pkt.data[i] == 0x00 && pkt.data[i + 1] == 0x00 && pkt.data[i + 2] == 0x01 && pkt.data[i + 3] == 0xB6 && (pkt.data[i + 4] & 0xC0) == 0x80)
return true;
return false;
}
bool IsNVOP(AVPacket &pkt) {
static const uint8_t MPEG4NVOP[] = { 0x00, 0x00, 0x01, 0xB6 };
return (pkt.size >= 4 && pkt.size <= 8) && !memcmp(pkt.data, MPEG4NVOP, 4);
}
void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames) {
AP.SampleFormat = static_cast<FFMS_SampleFormat>(CTX->sample_fmt);
AP.BitsPerSample = av_get_bits_per_sample_format(CTX->sample_fmt);
@ -281,6 +273,8 @@ CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC
case 32: CID = CODEC_ID_PCM_S32BE; break;
}
break;
default:
break;
}
return CID;
@ -367,24 +361,52 @@ void InitializeCodecContextFromHaaliInfo(CComQIPtr<IPropertyBag> pBag, AVCodecCo
#endif
// All this filename chikanery that follows is supposed to make sure both local
// codepage (used by avisynth etc) and UTF8 (potentially used by API users) strings
// work correctly on Win32.
// It's a really ugly hack, and I blame Microsoft for it.
#ifdef _WIN32
static wchar_t *dup_char_to_wchar(const char *s, unsigned int cp) {
wchar_t *w;
int l;
if (!(l = MultiByteToWideChar(cp, MB_ERR_INVALID_CHARS, s, -1, NULL, 0)))
return NULL;
if (!(w = (wchar_t *)malloc(l * sizeof(wchar_t))))
return NULL;
if (MultiByteToWideChar(cp, MB_ERR_INVALID_CHARS, s, -1 , w, l) <= 0) {
free(w);
w = NULL;
}
return w;
}
#endif
FILE *ffms_fopen(const char *filename, const char *mode) {
#ifdef _WIN32
int codepage = CP_ACP;
#ifdef FFMS_USE_UTF8_PATHS
// Hack: support utf8-in-char* filenames on windows
wchar_t filename_wide[MAX_PATH*2];
// 64 characters of mode string ought to be more than enough for everyone
wchar_t mode_wide[64];
if ((MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, filename_wide, MAX_PATH) > 0)
&& (MultiByteToWideChar(CP_ACP, NULL, mode, -1, mode_wide, 60) > 0))
return _wfopen(filename_wide, mode_wide);
codepage = CP_UTF8;
#endif
FILE *ret;
wchar_t *filename_wide = dup_char_to_wchar(filename, codepage);
wchar_t *mode_wide = dup_char_to_wchar(mode, codepage);
if (filename_wide && mode_wide)
ret = _wfopen(filename_wide, mode_wide);
else
return fopen(filename, mode);
ret = fopen(filename, mode);
free(filename_wide);
free(mode_wide);
return ret;
#else
return fopen(filename, mode);
#endif
#endif /* _WIN32 */
}
size_t ffms_mbstowcs (wchar_t *wcstr, const char *mbstr, size_t max) {
#ifdef FFMS_USE_UTF8_PATHS
#if defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS)
// try utf8 first
int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mbstr, -1, NULL, 0);
if (len > 0) {
@ -404,22 +426,85 @@ size_t ffms_mbstowcs (wchar_t *wcstr, const char *mbstr, size_t max) {
// ffms_fstream stuff
void ffms_fstream::open(const char *filename, std::ios_base::openmode mode) {
#ifdef FFMS_USE_UTF8_PATHS
// Hack: support utf8-in-char* filenames on windows
wchar_t filename_wide[MAX_PATH*2];
if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, filename, -1, filename_wide, MAX_PATH) > 0)
// Unlike MSVC, mingw's iostream library doesn't have an fstream overload
// that takes a wchar_t* filename, which means you can't open unicode
// filenames with it on Windows. gg.
#if defined(_WIN32) && !defined(__MINGW32__)
unsigned int codepage = CP_ACP;
#if defined(FFMS_USE_UTF8_PATHS)
codepage = CP_UTF8;
#endif /* defined(FFMS_USE_UTF8_PATHS) */
wchar_t *filename_wide = dup_char_to_wchar(filename, codepage);
if (filename_wide)
std::fstream::open(filename_wide, mode);
else
std::fstream::open(filename, mode);
#else
free(filename_wide);
#else /* defined(_WIN32) && !defined(__MINGW32__) */
std::fstream::open(filename, mode);
#endif
#endif /* defined(_WIN32) && !defined(__MINGW32__) */
}
ffms_fstream::ffms_fstream(const char *filename, std::ios_base::openmode mode) {
open(filename, mode);
}
#if defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS)
int ffms_wchar_open(const char *fname, int oflags, int pmode) {
wchar_t *wfname = dup_char_to_wchar(fname, CP_UTF8);
if (wfname) {
int ret = _wopen(wfname, oflags, pmode);
av_free(wfname);
return ret;
}
return -1;
}
static int ffms_lavf_file_open(URLContext *h, const char *filename, int flags) {
int access;
int fd;
av_strstart(filename, "file:", &filename);
if (flags & URL_RDWR) {
access = _O_CREAT | _O_TRUNC | _O_RDWR;
} else if (flags & URL_WRONLY) {
access = _O_CREAT | _O_TRUNC | _O_WRONLY;
} else {
access = _O_RDONLY;
}
#ifdef _O_BINARY
access |= _O_BINARY;
#endif
fd = ffms_wchar_open(filename, access, 0666);
if (fd == -1)
return AVERROR(ENOENT);
h->priv_data = (void *) (intptr_t) fd;
return 0;
}
// Hijack lavf's file protocol handler's open function and use our own instead.
// Hack by nielsm.
void ffms_patch_lavf_file_open() {
extern URLProtocol *first_protocol;
URLProtocol *proto = first_protocol;
while (proto != NULL) {
if (strcmp("file", proto->name) == 0) {
break;
}
proto = proto->next;
}
if (proto != NULL) {
proto->url_open = &ffms_lavf_file_open;
}
}
#endif /* defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS) */
// End of filename hackery.
#ifdef HAALISOURCE
CComPtr<IMMContainer> HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode) {
@ -478,3 +563,39 @@ void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext) {
"Couldn't find stream information");
}
}
// attempt to correct framerate to the proper NTSC fraction, if applicable
// code stolen from Perian
void CorrectNTSCRationalFramerate(int *Num, int *Den) {
AVRational TempFPS;
TempFPS.den = *Num; // not a typo
TempFPS.num = *Den; // still not a typo
av_reduce(&TempFPS.num, &TempFPS.den, TempFPS.num, TempFPS.den, INT_MAX);
if (TempFPS.num == 1) {
*Num = TempFPS.den;
*Den = TempFPS.num;
}
else {
double FTimebase = av_q2d(TempFPS);
double NearestNTSC = floor(FTimebase * 1001.0 + 0.5) / 1001.0;
const double SmallInterval = 1.0/120.0;
if (fabs(FTimebase - NearestNTSC) < SmallInterval) {
*Num = int((1001.0 / FTimebase) + 0.5);
*Den = 1001;
}
}
}
// correct the timebase if it is invalid
void CorrectTimebase(FFMS_VideoProperties *VP, FFMS_TrackTimeBase *TTimebase) {
double Timebase = (double)TTimebase->Num / TTimebase->Den;
double FPS = (double)VP->FPSNumerator / VP->FPSDenominator;
if ((1000/Timebase) / FPS < 1) {
TTimebase->Den = VP->FPSNumerator;
TTimebase->Num = (int64_t)VP->FPSDenominator * 1000;
}
}

View file

@ -142,8 +142,6 @@ FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT);
void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context);
bool AudioFMTIsFloat(SampleFormat FMT);
void InitNullPacket(AVPacket &pkt);
bool IsPackedFrame(AVPacket &pkt);
bool IsNVOP(AVPacket &pkt);
void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames);
#ifdef HAALISOURCE
unsigned vtSize(VARIANT &vt);
@ -154,9 +152,14 @@ void InitializeCodecContextFromMatroskaTrackInfo(TrackInfo *TI, AVCodecContext *
CodecID MatroskaToFFCodecID(char *Codec, void *CodecPrivate, unsigned int FourCC = 0, unsigned int BitsPerSample = 0);
FILE *ffms_fopen(const char *filename, const char *mode);
size_t ffms_mbstowcs (wchar_t *wcstr, const char *mbstr, size_t max);
#if defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS)
void ffms_patch_lavf_file_open();
#endif
#ifdef HAALISOURCE
CComPtr<IMMContainer> HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode);
#endif
void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext);
void CorrectNTSCRationalFramerate(int *Num, int *Den);
void CorrectTimebase(FFMS_VideoProperties *VP, FFMS_TrackTimeBase *TTimebase);
#endif

View file

@ -102,14 +102,14 @@ FFMS_Frame *FFMS_VideoSource::OutputFrame(AVFrame *Frame) {
if (PPMode) {
pp_postprocess(const_cast<const uint8_t **>(Frame->data), Frame->linesize, PPFrame.data, PPFrame.linesize, CodecContext->width, CodecContext->height, Frame->qscale_table, Frame->qstride, PPMode, PPContext, Frame->pict_type | (Frame->qscale_type ? PP_PICT_TYPE_QP2 : 0));
if (SWS) {
sws_scale(SWS, PPFrame.data, PPFrame.linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize);
sws_scale(SWS, const_cast<FFMS_SWS_CONST_PARAM uint8_t **>(PPFrame.data), PPFrame.linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize);
CopyAVPictureFields(SWSFrame, LocalFrame);
} else {
CopyAVPictureFields(PPFrame, LocalFrame);
}
} else {
if (SWS) {
sws_scale(SWS, Frame->data, Frame->linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize);
sws_scale(SWS, const_cast<FFMS_SWS_CONST_PARAM uint8_t **>(Frame->data), Frame->linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize);
CopyAVPictureFields(SWSFrame, LocalFrame);
} else {
// Special case to avoid ugly casts
@ -148,7 +148,7 @@ FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, in
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
"Not a video track");
if (Index[Track].size() == 0)
if (Index->at(Track).size() == 0)
throw FFMS_Exception(FFMS_ERROR_INDEX, FFMS_ERROR_INVALID_ARGUMENT,
"Video track contains no frames");
@ -162,7 +162,8 @@ FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, in
SWS = NULL;
LastFrameNum = 0;
CurrentFrame = 1;
MPEG4Counter = 0;
DelayCounter = 0;
InitialDecode = 1;
CodecContext = NULL;
LastFrameHeight = -1;
LastFrameWidth = -1;

View file

@ -73,7 +73,8 @@ protected:
FFMS_Track Frames;
int VideoTrack;
int CurrentFrame;
int MPEG4Counter;
int DelayCounter;
int InitialDecode;
AVCodecContext *CodecContext;
FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, int Track);