From d6dde4ec73b606fa2b5d7f52f26567b03ca7b864 Mon Sep 17 00:00:00 2001 From: Karl Blomster Date: Tue, 9 Nov 2010 19:55:23 +0000 Subject: [PATCH] update ffms2 to r354 Originally committed to SVN as r4835. --- aegisub/libffms/include/ffms.h | 4 +- aegisub/libffms/include/ffmscompat.h | 7 +- aegisub/libffms/src/core/audiosource.h | 4 +- aegisub/libffms/src/core/ffms.cpp | 16 ++- aegisub/libffms/src/core/indexing.cpp | 24 ++-- aegisub/libffms/src/core/indexing.h | 7 +- aegisub/libffms/src/core/lavfaudio.cpp | 4 +- aegisub/libffms/src/core/lavfindexer.cpp | 30 ++++- aegisub/libffms/src/core/matroskaaudio.cpp | 25 ++-- aegisub/libffms/src/core/matroskaindexer.cpp | 51 +++++--- aegisub/libffms/src/core/matroskaparser.c | 2 +- aegisub/libffms/src/core/matroskavideo.cpp | 21 ++-- aegisub/libffms/src/core/utils.cpp | 123 +++++++++++++------ aegisub/libffms/src/core/utils.h | 46 ++++++- aegisub/libffms/src/core/videosource.cpp | 36 +++++- aegisub/libffms/src/core/videosource.h | 7 +- aegisub/src/audio_provider_ffmpegsource.cpp | 6 + aegisub/src/video_provider_ffmpegsource.cpp | 4 + 18 files changed, 300 insertions(+), 117 deletions(-) diff --git a/aegisub/libffms/include/ffms.h b/aegisub/libffms/include/ffms.h index deed40bfb..84320c2c7 100644 --- a/aegisub/libffms/include/ffms.h +++ b/aegisub/libffms/include/ffms.h @@ -22,7 +22,7 @@ #define FFMS_H // Version format: major - minor - micro - bump -#define FFMS_VERSION ((2 << 24) | (13 << 16) | (1 << 8) | 0) +#define FFMS_VERSION ((2 << 24) | (14 << 16) | (0 << 8) | 0) #include @@ -240,7 +240,7 @@ typedef int (FFMS_CC *TAudioNameCallback)(const char *SourceFile, int Track, con // Most functions return 0 on success // Functions without error message output can be assumed to never fail in a graceful way -FFMS_API(void) FFMS_Init(int CPUFeatures); +FFMS_API(void) FFMS_Init(int CPUFeatures, int UseUTF8Paths); FFMS_API(int) FFMS_GetLogLevel(); 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); diff --git a/aegisub/libffms/include/ffmscompat.h b/aegisub/libffms/include/ffmscompat.h index 0d78da0a2..b0d16c31f 100644 --- a/aegisub/libffms/include/ffmscompat.h +++ b/aegisub/libffms/include/ffmscompat.h @@ -54,6 +54,12 @@ # if (LIBAVCODEC_VERSION_INT) < (AV_VERSION_INT(52,30,2)) # define AV_PKT_FLAG_KEY PKT_FLAG_KEY # endif +# if (LIBAVCODEC_VERSION_INT) >= (AV_VERSION_INT(52,94,3)) // there are ~3 revisions where this will break but fixing that is :effort: +# undef SampleFormat +# else +# define AVSampleFormat SampleFormat +# define av_get_bits_per_sample_fmt av_get_bits_per_sample_format +# endif #endif #ifdef LIBSWSCALE_VERSION_INT @@ -64,5 +70,4 @@ # endif #endif - #endif // FFMSCOMPAT_H diff --git a/aegisub/libffms/src/core/audiosource.h b/aegisub/libffms/src/core/audiosource.h index 3c9ed16d7..12fb9557c 100644 --- a/aegisub/libffms/src/core/audiosource.h +++ b/aegisub/libffms/src/core/audiosource.h @@ -74,7 +74,7 @@ friend class FFSourceResources; protected: TAudioCache AudioCache; int64_t CurrentSample; - std::vector DecodingBuffer; + AlignedBuffer DecodingBuffer; FFMS_Track Frames; AVCodecContext *CodecContext; int AudioTrack; @@ -106,7 +106,7 @@ class FFMatroskaAudio : public FFMS_AudioSource { private: MatroskaFile *MF; MatroskaReaderContext MC; - CompressedStream *CS; + TrackCompressionContext *TCC; char ErrorMessage[256]; FFSourceResources Res; size_t PacketNumber; diff --git a/aegisub/libffms/src/core/ffms.cpp b/aegisub/libffms/src/core/ffms.cpp index c3c640721..329d9e742 100644 --- a/aegisub/libffms/src/core/ffms.cpp +++ b/aegisub/libffms/src/core/ffms.cpp @@ -33,6 +33,8 @@ static bool FFmpegInited = false; bool HasHaaliMPEG = false; bool HasHaaliOGG = false; int CPUFeatures = 0; +bool GlobalUseUTF8Paths = false; + #ifdef FFMS_WIN_DEBUG @@ -70,11 +72,19 @@ void av_log_windebug_callback(void* ptr, int level, const char* fmt, va_list vl) #endif -FFMS_API(void) FFMS_Init(int CPUFeatures) { +FFMS_API(void) FFMS_Init(int CPUFeatures, int UseUTF8Paths) { if (!FFmpegInited) { av_register_all(); -#if defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS) - ffms_patch_lavf_file_open(); +#ifdef _WIN32 + if (UseUTF8Paths) { + ffms_patch_lavf_file_open(); + GlobalUseUTF8Paths = true; + } + else { + GlobalUseUTF8Paths = false; + } +#else + GlobalUseUTF8Paths = false; #endif #ifdef FFMS_WIN_DEBUG av_log_set_callback(av_log_windebug_callback); diff --git a/aegisub/libffms/src/core/indexing.cpp b/aegisub/libffms/src/core/indexing.cpp index 373f17f3a..8e7fe21b5 100644 --- a/aegisub/libffms/src/core/indexing.cpp +++ b/aegisub/libffms/src/core/indexing.cpp @@ -33,6 +33,10 @@ extern "C" { extern bool HasHaaliMPEG; extern bool HasHaaliOGG; +#ifndef WITH_LIBPOSTPROC +unsigned postproc_version(void) { return 0; } // ugly workaround to avoid lots of ifdeffing +#endif // WITH_LIBPOSTPROC + struct IndexHeader { uint32_t Id; uint32_t Version; @@ -55,11 +59,12 @@ struct TrackHeader { uint32_t UseDTS; }; + SharedVideoContext::SharedVideoContext(bool FreeCodecContext) { CodecContext = NULL; Parser = NULL; - CS = NULL; this->FreeCodecContext = FreeCodecContext; + TCC = NULL; } SharedVideoContext::~SharedVideoContext() { @@ -72,15 +77,15 @@ SharedVideoContext::~SharedVideoContext() { if (Parser) av_parser_close(Parser); - if (CS) - cs_Destroy(CS); + if (TCC) + delete TCC; } SharedAudioContext::SharedAudioContext(bool FreeCodecContext) { W64Writer = NULL; CodecContext = NULL; CurrentSample = 0; - CS = NULL; + TCC = NULL; this->FreeCodecContext = FreeCodecContext; } @@ -92,8 +97,8 @@ SharedAudioContext::~SharedAudioContext() { av_freep(&CodecContext); } - if (CS) - cs_Destroy(CS); + if (TCC) + delete TCC; } TFrameInfo::TFrameInfo() { @@ -322,6 +327,7 @@ void FFMS_Index::WriteIndex(const char *IndexFile) { IH.LAVCVersion = avcodec_version(); IH.LSWSVersion = swscale_version(); IH.LPPVersion = postproc_version(); + IH.LPPVersion = 0; IH.FileSize = Filesize; memcpy(IH.FileSignature, Digest, sizeof(Digest)); @@ -512,7 +518,7 @@ FFMS_Indexer *FFMS_Indexer::CreateIndexer(const char *Filename) { } // Do matroska indexing instead? - if (!strcmp(FormatContext->iformat->name, "matroska")) { + if (!strncmp(FormatContext->iformat->name, "matroska", 8)) { av_close_input_file(FormatContext); return new FFMatroskaIndexer(Filename); } @@ -560,8 +566,8 @@ void FFMS_Indexer::WriteAudio(SharedAudioContext &AudioContext, FFMS_Index *Inde (*ANC)(SourceFile, Track, &AP, &WName[0], FNSize, ANCPrivate); std::string WN(&WName[0]); try { - AudioContext.W64Writer = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_format(AudioContext.CodecContext->sample_fmt), - AudioContext.CodecContext->channels, AudioContext.CodecContext->sample_rate, (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_FLT) || (AudioContext.CodecContext->sample_fmt == SAMPLE_FMT_DBL)); + AudioContext.W64Writer = new Wave64Writer(WN.c_str(), av_get_bits_per_sample_fmt(AudioContext.CodecContext->sample_fmt), + AudioContext.CodecContext->channels, AudioContext.CodecContext->sample_rate, (AudioContext.CodecContext->sample_fmt == AV_SAMPLE_FMT_FLT) || (AudioContext.CodecContext->sample_fmt == AV_SAMPLE_FMT_DBL)); } catch (...) { throw FFMS_Exception(FFMS_ERROR_WAVE_WRITER, FFMS_ERROR_FILE_WRITE, "Failed to write wave data"); diff --git a/aegisub/libffms/src/core/indexing.h b/aegisub/libffms/src/core/indexing.h index 1fbbeabbd..655dc9044 100644 --- a/aegisub/libffms/src/core/indexing.h +++ b/aegisub/libffms/src/core/indexing.h @@ -38,14 +38,13 @@ #endif - class SharedVideoContext { private: bool FreeCodecContext; public: AVCodecContext *CodecContext; AVCodecParserContext *Parser; - CompressedStream *CS; + TrackCompressionContext *TCC; SharedVideoContext(bool FreeCodecContext); ~SharedVideoContext(); @@ -58,7 +57,7 @@ public: AVCodecContext *CodecContext; Wave64Writer *W64Writer; int64_t CurrentSample; - CompressedStream *CS; + TrackCompressionContext *TCC; SharedAudioContext(bool FreeCodecContext); ~SharedAudioContext(); @@ -123,7 +122,7 @@ protected: TAudioNameCallback ANC; void *ANCPrivate; const char *SourceFile; - std::vector DecodingBuffer; + AlignedBuffer DecodingBuffer; int64_t Filesize; uint8_t Digest[20]; diff --git a/aegisub/libffms/src/core/lavfaudio.cpp b/aegisub/libffms/src/core/lavfaudio.cpp index 0df72cd41..ec001cf78 100644 --- a/aegisub/libffms/src/core/lavfaudio.cpp +++ b/aegisub/libffms/src/core/lavfaudio.cpp @@ -69,7 +69,7 @@ FFLAVFAudio::FFLAVFAudio(const char *SourceFile, int Track, FFMS_Index *Index) } void FFLAVFAudio::DecodeNextAudioBlock(int64_t *Count) { - const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; + const size_t SizeConst = (av_get_bits_per_sample_fmt(CodecContext->sample_fmt) * CodecContext->channels) / 8; int Ret = -1; *Count = 0; uint8_t *Buf = &DecodingBuffer[0]; @@ -113,7 +113,7 @@ Done:; void FFLAVFAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) { GetAudioCheck(Start, Count); - const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; + const int64_t SizeConst = (av_get_bits_per_sample_fmt(CodecContext->sample_fmt) * CodecContext->channels) / 8; memset(Buf, 0, static_cast(SizeConst * Count)); unsigned int PreDecBlocks = 0; diff --git a/aegisub/libffms/src/core/lavfindexer.cpp b/aegisub/libffms/src/core/lavfindexer.cpp index b758ae082..8c94a985b 100644 --- a/aegisub/libffms/src/core/lavfindexer.cpp +++ b/aegisub/libffms/src/core/lavfindexer.cpp @@ -143,6 +143,10 @@ FFMS_Index *FFLAVFIndexer::DoIndexing() { "Invalid initial pts and dts"); // + bool first = true; + int LastNumChannels; + int LastSampleRate; + AVSampleFormat LastSampleFormat; while (TempPacket.size > 0) { int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket); @@ -161,6 +165,25 @@ FFMS_Index *FFLAVFIndexer::DoIndexing() { break; } } + + if (first) { + LastNumChannels = AudioCodecContext->channels; + LastSampleRate = AudioCodecContext->sample_rate; + LastSampleFormat = AudioCodecContext->sample_fmt; + first = false; + } + + if (LastNumChannels != AudioCodecContext->channels || LastSampleRate != AudioCodecContext->sample_rate + || LastSampleFormat != AudioCodecContext->sample_fmt) { + std::ostringstream buf; + buf << + "Audio format change detected. This is currently unsupported." + << " Channels: " << LastNumChannels << " -> " << AudioCodecContext->channels << ";" + << " Sample rate: " << LastSampleRate << " -> " << AudioCodecContext->sample_rate << ";" + << " Sample format: " << GetLAVCSampleFormatName(LastSampleFormat) << " -> " + << GetLAVCSampleFormatName(AudioCodecContext->sample_fmt); + throw FFMS_Exception(FFMS_ERROR_UNSUPPORTED, FFMS_ERROR_DECODING, buf.str()); + } if (Ret > 0) { TempPacket.size -= Ret; @@ -168,7 +191,7 @@ FFMS_Index *FFLAVFIndexer::DoIndexing() { } if (dbsize > 0) - AudioContexts[Packet.stream_index].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels); + AudioContexts[Packet.stream_index].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_fmt(AudioCodecContext->sample_fmt) * AudioCodecContext->channels); if (DumpMask & (1 << Packet.stream_index)) WriteAudio(AudioContexts[Packet.stream_index], TrackIndices.get(), Packet.stream_index, dbsize); @@ -193,6 +216,7 @@ FFMS_TrackType FFLAVFIndexer::GetTrackType(int Track) { return static_cast(FormatContext->streams[Track]->codec->codec_type); } -const char *FFLAVFIndexer::GetTrackCodec(int Track) { - return (avcodec_find_decoder(FormatContext->streams[Track]->codec->codec_id))->name; +const char *FFLAVFIndexer::GetTrackCodec(int Track) { + AVCodec *codec = avcodec_find_decoder(FormatContext->streams[Track]->codec->codec_id); + return codec ? codec->name : NULL; } diff --git a/aegisub/libffms/src/core/matroskaaudio.cpp b/aegisub/libffms/src/core/matroskaaudio.cpp index 7ce9419b4..738f60ec8 100644 --- a/aegisub/libffms/src/core/matroskaaudio.cpp +++ b/aegisub/libffms/src/core/matroskaaudio.cpp @@ -21,8 +21,8 @@ #include "audiosource.h" void FFMatroskaAudio::Free(bool CloseCodec) { - if (CS) - cs_Destroy(CS); + if (TCC) + delete TCC; if (MC.ST.fp) { mkv_Close(MF); fclose(MC.ST.fp); @@ -37,7 +37,7 @@ FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index * CodecContext = NULL; AVCodec *Codec = NULL; TrackInfo *TI = NULL; - CS = NULL; + TCC = NULL; PacketNumber = 0; Frames = (*Index)[Track]; @@ -61,15 +61,8 @@ FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index * TI = mkv_GetTrackInfo(MF, Track); - if (TI->CompEnabled) { - CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage)); - if (CS == NULL) { - Free(false); - std::ostringstream buf; - buf << "Can't create decompressor: " << ErrorMessage; - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); - } - } + if (TI->CompEnabled) + TCC = new TrackCompressionContext(MF, TI, Track); CodecContext = avcodec_alloc_context(); @@ -104,7 +97,7 @@ FFMatroskaAudio::FFMatroskaAudio(const char *SourceFile, int Track, FFMS_Index * void FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) { GetAudioCheck(Start, Count); - const int64_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; + const int64_t SizeConst = (av_get_bits_per_sample_fmt(CodecContext->sample_fmt) * CodecContext->channels) / 8; memset(Buf, 0, static_cast(SizeConst * Count)); unsigned int PreDecBlocks = 0; @@ -146,7 +139,7 @@ void FFMatroskaAudio::GetAudio(void *Buf, int64_t Start, int64_t Count) { } void FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count) { - const size_t SizeConst = (av_get_bits_per_sample_format(CodecContext->sample_fmt) * CodecContext->channels) / 8; + const size_t SizeConst = (av_get_bits_per_sample_fmt(CodecContext->sample_fmt) * CodecContext->channels) / 8; int Ret = -1; *Count = 0; uint8_t *Buf = &DecodingBuffer[0]; @@ -158,9 +151,9 @@ void FFMatroskaAudio::DecodeNextAudioBlock(int64_t *Count) { CurrentSample = FI.SampleStart + FI.SampleCount; PacketNumber++; - ReadFrame(FI.FilePos, FrameSize, CS, MC); + ReadFrame(FI.FilePos, FrameSize, TCC, MC); TempPacket.data = MC.Buffer; - TempPacket.size = FrameSize; + TempPacket.size = (TCC && TCC->CompressionMethod == COMP_PREPEND) ? FrameSize + TCC->CompressedPrivateDataSize : FrameSize; if (FI.KeyFrame) TempPacket.flags = AV_PKT_FLAG_KEY; else diff --git a/aegisub/libffms/src/core/matroskaindexer.cpp b/aegisub/libffms/src/core/matroskaindexer.cpp index 75729ec7f..827783f54 100644 --- a/aegisub/libffms/src/core/matroskaindexer.cpp +++ b/aegisub/libffms/src/core/matroskaindexer.cpp @@ -60,7 +60,7 @@ FFMatroskaIndexer::~FFMatroskaIndexer() { } FFMS_Index *FFMatroskaIndexer::DoIndexing() { - char ErrorMessage[256]; + // char ErrorMessage[256]; std::vector AudioContexts(mkv_GetNumTracks(MF), SharedAudioContext(true)); std::vector VideoContexts(mkv_GetNumTracks(MF), SharedVideoContext(true)); @@ -83,14 +83,8 @@ FFMS_Index *FFMatroskaIndexer::DoIndexing() { "Could not open video codec"); } - if (TI->CompEnabled) { - VideoContexts[i].CS = cs_Create(MF, i, ErrorMessage, sizeof(ErrorMessage)); - if (VideoContexts[i].CS == NULL) { - std::ostringstream buf; - buf << "Can't create decompressor: " << ErrorMessage; - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); - } - } + if (TI->CompEnabled) + VideoContexts[i].TCC = new TrackCompressionContext(MF, TI, i); VideoContexts[i].CodecContext = CodecContext; VideoContexts[i].Parser->flags = PARSER_FLAG_COMPLETE_FRAMES; @@ -102,13 +96,12 @@ FFMS_Index *FFMatroskaIndexer::DoIndexing() { AudioContexts[i].CodecContext = AudioCodecContext; if (TI->CompEnabled) { - AudioContexts[i].CS = cs_Create(MF, i, ErrorMessage, sizeof(ErrorMessage)); - if (AudioContexts[i].CS == NULL) { + try { + AudioContexts[i].TCC = new TrackCompressionContext(MF, TI, i); + } catch (FFMS_Exception &) { av_freep(&AudioCodecContext); AudioContexts[i].CodecContext = NULL; - std::ostringstream buf; - buf << "Can't create decompressor: " << ErrorMessage; - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); + throw; } } @@ -159,17 +152,22 @@ FFMS_Index *FFMatroskaIndexer::DoIndexing() { (*TrackIndices)[Track].push_back(TFrameInfo::VideoFrameInfo(StartTime, RepeatPict, (FrameFlags & FRAME_KF) != 0, FilePos, FrameSize)); } else if (mkv_GetTrackInfo(MF, Track)->Type == TT_AUDIO && (IndexMask & (1 << Track))) { + TrackCompressionContext *TCC = AudioContexts[Track].TCC; int64_t StartSample = AudioContexts[Track].CurrentSample; unsigned int CompressedFrameSize = FrameSize; AVCodecContext *AudioCodecContext = AudioContexts[Track].CodecContext; - ReadFrame(FilePos, FrameSize, AudioContexts[Track].CS, MC); + ReadFrame(FilePos, FrameSize, TCC, MC); TempPacket.data = MC.Buffer; - TempPacket.size = FrameSize; + TempPacket.size = (TCC && TCC->CompressionMethod == COMP_PREPEND) ? FrameSize + TCC->CompressedPrivateDataSize : FrameSize; if ((FrameFlags & FRAME_KF) != 0) TempPacket.flags = AV_PKT_FLAG_KEY; else TempPacket.flags = 0; + bool first = true; + int LastNumChannels; + int LastSampleRate; + AVSampleFormat LastSampleFormat; while (TempPacket.size > 0) { int dbsize = AVCODEC_MAX_AUDIO_FRAME_SIZE*10; int Ret = avcodec_decode_audio3(AudioCodecContext, &DecodingBuffer[0], &dbsize, &TempPacket); @@ -188,6 +186,25 @@ FFMS_Index *FFMatroskaIndexer::DoIndexing() { break; } } + + if (first) { + LastNumChannels = AudioCodecContext->channels; + LastSampleRate = AudioCodecContext->sample_rate; + LastSampleFormat = AudioCodecContext->sample_fmt; + first = false; + } + + if (LastNumChannels != AudioCodecContext->channels || LastSampleRate != AudioCodecContext->sample_rate + || LastSampleFormat != AudioCodecContext->sample_fmt) { + std::ostringstream buf; + buf << + "Audio format change detected. This is currently unsupported." + << " Channels: " << LastNumChannels << " -> " << AudioCodecContext->channels << ";" + << " Sample rate: " << LastSampleRate << " -> " << AudioCodecContext->sample_rate << ";" + << " Sample format: " << GetLAVCSampleFormatName(LastSampleFormat) << " -> " + << GetLAVCSampleFormatName(AudioCodecContext->sample_fmt); + throw FFMS_Exception(FFMS_ERROR_UNSUPPORTED, FFMS_ERROR_DECODING, buf.str()); + } if (Ret > 0) { TempPacket.size -= Ret; @@ -195,7 +212,7 @@ FFMS_Index *FFMatroskaIndexer::DoIndexing() { } if (dbsize > 0) - AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_format(AudioCodecContext->sample_fmt) * AudioCodecContext->channels); + AudioContexts[Track].CurrentSample += (dbsize * 8) / (av_get_bits_per_sample_fmt(AudioCodecContext->sample_fmt) * AudioCodecContext->channels); if (DumpMask & (1 << Track)) WriteAudio(AudioContexts[Track], TrackIndices.get(), Track, dbsize); diff --git a/aegisub/libffms/src/core/matroskaparser.c b/aegisub/libffms/src/core/matroskaparser.c index 507f1c9f0..d556907f7 100644 --- a/aegisub/libffms/src/core/matroskaparser.c +++ b/aegisub/libffms/src/core/matroskaparser.c @@ -28,7 +28,7 @@ */ #ifdef HAVE_CONFIG_H -//#include "config.h" +#include "config.h" #endif #include diff --git a/aegisub/libffms/src/core/matroskavideo.cpp b/aegisub/libffms/src/core/matroskavideo.cpp index 32b9d181f..a311f5ac8 100644 --- a/aegisub/libffms/src/core/matroskavideo.cpp +++ b/aegisub/libffms/src/core/matroskavideo.cpp @@ -23,8 +23,8 @@ void FFMatroskaVideo::Free(bool CloseCodec) { - if (CS) - cs_Destroy(CS); + if (TCC) + delete TCC; if (MC.ST.fp) { mkv_Close(MF); fclose(MC.ST.fp); @@ -41,7 +41,7 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track, AVCodec *Codec = NULL; CodecContext = NULL; TrackInfo *TI = NULL; - CS = NULL; + TCC = NULL; PacketNumber = 0; VideoTrack = Track; Frames = (*Index)[VideoTrack]; @@ -65,15 +65,8 @@ FFMatroskaVideo::FFMatroskaVideo(const char *SourceFile, int Track, TI = mkv_GetTrackInfo(MF, VideoTrack); - if (TI->CompEnabled) { - CS = cs_Create(MF, VideoTrack, ErrorMessage, sizeof(ErrorMessage)); - if (CS == NULL) { - Free(false); - std::ostringstream buf; - buf << "Can't create decompressor: " << ErrorMessage; - throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); - } - } + if (TI->CompEnabled) + TCC = new TrackCompressionContext(MF, TI, VideoTrack); CodecContext = avcodec_alloc_context(); if (avcodec_thread_init(CodecContext, Threads)) @@ -169,10 +162,10 @@ void FFMatroskaVideo::DecodeNextFrame() { // in the other sources where less is done manually const TFrameInfo &FI = Frames[Frames[PacketNumber].OriginalPos]; FrameSize = FI.FrameSize; - ReadFrame(FI.FilePos, FrameSize, CS, MC); + ReadFrame(FI.FilePos, FrameSize, TCC, MC); Packet.data = MC.Buffer; - Packet.size = FrameSize; + Packet.size = (TCC && TCC->CompressionMethod == COMP_PREPEND) ? FrameSize + TCC->CompressedPrivateDataSize : FrameSize; if (FI.KeyFrame) Packet.flags = AV_PKT_FLAG_KEY; else diff --git a/aegisub/libffms/src/core/utils.cpp b/aegisub/libffms/src/core/utils.cpp index 20ecbbf8b..345f7f01a 100644 --- a/aegisub/libffms/src/core/utils.cpp +++ b/aegisub/libffms/src/core/utils.cpp @@ -26,15 +26,12 @@ #ifdef _WIN32 # define WIN32_LEAN_AND_MEAN # include -#ifdef FFMS_USE_UTF8_PATHS # include # include extern "C" { # include "libavutil/avstring.h" } -#endif -#endif - +#endif // _WIN32 // Export the array but not its data type... fun... @@ -51,6 +48,7 @@ extern const AVCodecTag ff_codec_wav_tags[]; } extern int CPUFeatures; +extern bool GlobalUseUTF8Paths; @@ -81,6 +79,34 @@ int FFMS_Exception::CopyOut(FFMS_ErrorInfo *ErrorInfo) const { return (_ErrorType << 16) | _SubType; } +TrackCompressionContext::TrackCompressionContext(MatroskaFile *MF, TrackInfo *TI, unsigned int Track) { + CS = NULL; + CompressedPrivateData = NULL; + CompressedPrivateDataSize = 0; + CompressionMethod = TI->CompMethod; + + if (CompressionMethod == COMP_ZLIB) { + char ErrorMessage[512]; + CS = cs_Create(MF, Track, ErrorMessage, sizeof(ErrorMessage)); + if (CS == NULL) { + std::ostringstream buf; + buf << "Can't create MKV track decompressor: " << ErrorMessage; + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, buf.str()); + } + } else if (CompressionMethod == COMP_PREPEND) { + CompressedPrivateData = TI->CompMethodPrivate; + CompressedPrivateDataSize = TI->CompMethodPrivateSize; + } else { + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, + "Can't create MKV track decompressor: unknown or unsupported compression method"); + } +} + +TrackCompressionContext::~TrackCompressionContext() { + if (CS) + cs_Destroy(CS); +} + int GetSWSCPUFlags() { int Flags = 0; @@ -101,6 +127,8 @@ int GetSWSCPUFlags() { int GetPPCPUFlags() { int Flags = 0; +#ifdef WITH_LIBPOSTPROC +// not exactly a pretty solution but it'll never get called anyway if (CPUFeatures & FFMS_CPU_CAPS_MMX) Flags |= PP_CPU_CAPS_MMX; if (CPUFeatures & FFMS_CPU_CAPS_MMX2) @@ -109,6 +137,7 @@ int GetPPCPUFlags() { Flags |= PP_CPU_CAPS_3DNOW; if (CPUFeatures & FFMS_CPU_CAPS_ALTIVEC) Flags |= PP_CPU_CAPS_ALTIVEC; +#endif // WITH_LIBPOSTPROC return Flags; } @@ -132,8 +161,20 @@ FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT) { } } -void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context) { - if (CS) { +const char *GetLAVCSampleFormatName(AVSampleFormat s) { + switch (s) { + case AV_SAMPLE_FMT_U8: return "8-bit unsigned integer"; + case AV_SAMPLE_FMT_S16: return "16-bit signed integer"; + case AV_SAMPLE_FMT_S32: return "32-bit signed integer"; + case AV_SAMPLE_FMT_FLT: return "Single-precision floating point"; + case AV_SAMPLE_FMT_DBL: return "Double-precision floating point"; + default: return "Unknown"; + } +} + +void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, TrackCompressionContext *TCC, MatroskaReaderContext &Context) { + if (TCC && TCC->CS) { + CompressedStream *CS = TCC->CS; unsigned int DecompressedFrameSize = 0; cs_NextFrame(CS, FilePos, FrameSize); @@ -171,7 +212,23 @@ void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_SEEKING, buf.str()); } - if (Context.BufferSize < FrameSize) { + if (TCC && TCC->CompressionMethod == COMP_PREPEND) { + unsigned ReqBufsize = FrameSize + TCC->CompressedPrivateDataSize + 16; + if (Context.BufferSize < ReqBufsize) { + Context.BufferSize = FrameSize + TCC->CompressedPrivateDataSize; + Context.Buffer = (uint8_t *)realloc(Context.Buffer, ReqBufsize); + if (Context.Buffer == NULL) + throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_ALLOCATION_FAILED, "Out of memory"); + } + + /* // maybe faster? maybe not? + for (int i=0; i < TCC->CompressedPrivateDataSize; i++) + *(Context.Buffer)++ = ((uint8_t *)TCC->CompressedPrivateData)[i]; + */ + // screw it, memcpy and fuck the losers who use header compression + memcpy(Context.Buffer, TCC->CompressedPrivateData, TCC->CompressedPrivateDataSize); + } + else if (Context.BufferSize < FrameSize) { Context.BufferSize = FrameSize; Context.Buffer = (uint8_t *)realloc(Context.Buffer, Context.BufferSize + 16); if (Context.Buffer == NULL) @@ -179,8 +236,13 @@ void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, "Out of memory"); } - size_t ReadBytes = fread(Context.Buffer, 1, FrameSize, Context.ST.fp); + uint8_t *TargetPtr = Context.Buffer; + if (TCC && TCC->CompressionMethod == COMP_PREPEND) + TargetPtr += TCC->CompressedPrivateDataSize; + + size_t ReadBytes = fread(TargetPtr, 1, FrameSize, Context.ST.fp); if (ReadBytes != FrameSize) { + return; if (ReadBytes == 0) { if (feof(Context.ST.fp)) { throw FFMS_Exception(FFMS_ERROR_PARSER, FFMS_ERROR_FILE_READ, @@ -210,8 +272,8 @@ void InitNullPacket(AVPacket &pkt) { void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames) { AP.SampleFormat = static_cast(CTX->sample_fmt); - AP.BitsPerSample = av_get_bits_per_sample_format(CTX->sample_fmt); - if (CTX->sample_fmt == SAMPLE_FMT_S32 && CTX->bits_per_raw_sample) + AP.BitsPerSample = av_get_bits_per_sample_fmt(CTX->sample_fmt); + if (CTX->sample_fmt == AV_SAMPLE_FMT_S32 && CTX->bits_per_raw_sample) AP.BitsPerSample = CTX->bits_per_raw_sample; AP.Channels = CTX->channels;; @@ -384,10 +446,12 @@ static wchar_t *dup_char_to_wchar(const char *s, unsigned int cp) { FILE *ffms_fopen(const char *filename, const char *mode) { #ifdef _WIN32 - int codepage = CP_ACP; -#ifdef FFMS_USE_UTF8_PATHS - codepage = CP_UTF8; -#endif + unsigned int codepage; + if (GlobalUseUTF8Paths) + codepage = CP_UTF8; + else + codepage = CP_ACP; + FILE *ret; wchar_t *filename_wide = dup_char_to_wchar(filename, codepage); wchar_t *mode_wide = dup_char_to_wchar(mode, codepage); @@ -406,18 +470,9 @@ 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) - // try utf8 first - int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mbstr, -1, NULL, 0); - if (len > 0) { - MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, mbstr, -1, wcstr, max); - return static_cast(len); - } - // failed, use local ANSI codepage - else { - len = MultiByteToWideChar(CP_ACP, NULL, mbstr, -1, wcstr, max); - return static_cast(len); - } +#ifdef _WIN32 + // this is only called by HaaliOpenFile anyway, so I think this is safe + return static_cast(MultiByteToWideChar((GlobalUseUTF8Paths ? CP_UTF8 : CP_ACP), MB_ERR_INVALID_CHARS, mbstr, -1, wcstr, max)); #else return mbstowcs(wcstr, mbstr, max); #endif @@ -430,10 +485,8 @@ void ffms_fstream::open(const char *filename, std::ios_base::openmode mode) { // 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) */ + unsigned int codepage = GlobalUseUTF8Paths ? CP_UTF8 : CP_ACP; + wchar_t *filename_wide = dup_char_to_wchar(filename, codepage); if (filename_wide) std::fstream::open(filename_wide, mode); @@ -441,9 +494,9 @@ void ffms_fstream::open(const char *filename, std::ios_base::openmode mode) { std::fstream::open(filename, mode); free(filename_wide); -#else /* defined(_WIN32) && !defined(__MINGW32__) */ +#else // defined(_WIN32) && !defined(__MINGW32__) std::fstream::open(filename, mode); -#endif /* defined(_WIN32) && !defined(__MINGW32__) */ +#endif // defined(_WIN32) && !defined(__MINGW32__) } ffms_fstream::ffms_fstream(const char *filename, std::ios_base::openmode mode) { @@ -451,12 +504,12 @@ ffms_fstream::ffms_fstream(const char *filename, std::ios_base::openmode mode) { } -#if defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS) +#ifdef _WIN32 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); + free(wfname); return ret; } return -1; @@ -500,7 +553,7 @@ void ffms_patch_lavf_file_open() { proto->url_open = &ffms_lavf_file_open; } } -#endif /* defined(_WIN32) && defined(FFMS_USE_UTF8_PATHS) */ +#endif // _WIN32 // End of filename hackery. diff --git a/aegisub/libffms/src/core/utils.h b/aegisub/libffms/src/core/utils.h index 672c2b941..e895299cc 100644 --- a/aegisub/libffms/src/core/utils.h +++ b/aegisub/libffms/src/core/utils.h @@ -30,10 +30,13 @@ extern "C" { #include "stdiostream.h" +#include #include #include #include +#ifdef WITH_LIBPOSTPROC #include +#endif // WITH_LIBPOSTPROC } // must be included after ffmpeg headers @@ -135,12 +138,44 @@ public: ffms_fstream(const char *filename, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out); }; +template +class AlignedBuffer { + T *buf; + +public: + AlignedBuffer(size_t n = 1) { + buf = (T*) av_malloc(sizeof(*buf) * n); + if (!buf) throw std::bad_alloc(); + } + + ~AlignedBuffer() { + av_free(buf); + buf = 0; + } + + const T &operator[] (size_t i) const { return buf[i]; } + T &operator[] (size_t i) { return buf[i]; } +}; + + +class TrackCompressionContext { +public: + CompressedStream *CS; + unsigned CompressionMethod; + void *CompressedPrivateData; + unsigned CompressedPrivateDataSize; + + TrackCompressionContext(MatroskaFile *MF, TrackInfo *TI, unsigned int Track); + ~TrackCompressionContext(); +}; + + int GetSWSCPUFlags(); int GetPPCPUFlags(); void ClearErrorInfo(FFMS_ErrorInfo *ErrorInfo); FFMS_TrackType HaaliTrackTypeToFFTrackType(int TT); -void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, CompressedStream *CS, MatroskaReaderContext &Context); -bool AudioFMTIsFloat(SampleFormat FMT); +void ReadFrame(uint64_t FilePos, unsigned int &FrameSize, TrackCompressionContext *TCC, MatroskaReaderContext &Context); +bool AudioFMTIsFloat(AVSampleFormat FMT); void InitNullPacket(AVPacket &pkt); void FillAP(FFMS_AudioProperties &AP, AVCodecContext *CTX, FFMS_Track &Frames); #ifdef HAALISOURCE @@ -152,14 +187,15 @@ 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) +#ifdef _WIN32 void ffms_patch_lavf_file_open(); -#endif +#endif // _WIN32 #ifdef HAALISOURCE CComPtr HaaliOpenFile(const char *SourceFile, enum FFMS_Sources SourceMode); -#endif +#endif // HAALISOURCE void LAVFOpenFile(const char *SourceFile, AVFormatContext *&FormatContext); void CorrectNTSCRationalFramerate(int *Num, int *Den); void CorrectTimebase(FFMS_VideoProperties *VP, FFMS_TrackTimeBase *TTimebase); +const char *GetLAVCSampleFormatName(AVSampleFormat s); #endif diff --git a/aegisub/libffms/src/core/videosource.cpp b/aegisub/libffms/src/core/videosource.cpp index a65a169d9..abfb1f7d3 100644 --- a/aegisub/libffms/src/core/videosource.cpp +++ b/aegisub/libffms/src/core/videosource.cpp @@ -27,6 +27,8 @@ void FFMS_VideoSource::GetFrameCheck(int n) { } void FFMS_VideoSource::SetPP(const char *PP) { + +#ifdef WITH_LIBPOSTPROC if (PPMode) pp_free_mode(PPMode); PPMode = NULL; @@ -43,9 +45,14 @@ void FFMS_VideoSource::SetPP(const char *PP) { ReAdjustPP(CodecContext->pix_fmt, CodecContext->width, CodecContext->height); OutputFrame(DecodeFrame); +#else + throw FFMS_Exception(FFMS_ERROR_POSTPROCESSING, FFMS_ERROR_UNSUPPORTED, + "FFMS2 was not compiled with postprocessing support"); +#endif /* WITH_LIBPOSTPROC */ } void FFMS_VideoSource::ResetPP() { +#ifdef WITH_LIBPOSTPROC if (PPContext) pp_free_context(PPContext); PPContext = NULL; @@ -54,10 +61,12 @@ void FFMS_VideoSource::ResetPP() { pp_free_mode(PPMode); PPMode = NULL; +#endif /* WITH_LIBPOSTPROC */ OutputFrame(DecodeFrame); } void FFMS_VideoSource::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Height) { +#ifdef WITH_LIBPOSTPROC if (PPContext) pp_free_context(PPContext); PPContext = NULL; @@ -82,8 +91,13 @@ void FFMS_VideoSource::ReAdjustPP(PixelFormat VPixelFormat, int Width, int Heigh avpicture_free(&PPFrame); avpicture_alloc(&PPFrame, VPixelFormat, Width, Height); +#else + return; +#endif /* WITH_LIBPOSTPROC */ + } + static void CopyAVPictureFields(AVPicture &Picture, FFMS_Frame &Dst) { for (int i = 0; i < 4; i++) { Dst.Data[i] = Picture.data[i]; @@ -99,6 +113,7 @@ FFMS_Frame *FFMS_VideoSource::OutputFrame(AVFrame *Frame) { ReAdjustOutputFormat(TargetPixelFormats, TargetWidth, TargetHeight, TargetResizer); } +#ifdef WITH_LIBPOSTPROC if (PPMode) { pp_postprocess(const_cast(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) { @@ -119,6 +134,18 @@ FFMS_Frame *FFMS_VideoSource::OutputFrame(AVFrame *Frame) { } } } +#else // WITH_LIBPOSTPROC + if (SWS) { + sws_scale(SWS, const_cast(Frame->data), Frame->linesize, 0, CodecContext->height, SWSFrame.data, SWSFrame.linesize); + CopyAVPictureFields(SWSFrame, LocalFrame); + } else { + // Special case to avoid ugly casts + for (int i = 0; i < 4; i++) { + LocalFrame.Data[i] = Frame->data[i]; + LocalFrame.Linesize[i] = Frame->linesize[i]; + } + } +#endif // WITH_LIBPOSTPROC LocalFrame.EncodedWidth = CodecContext->width; LocalFrame.EncodedHeight = CodecContext->height; @@ -157,8 +184,10 @@ FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, in "The index does not match the source file"); memset(&VP, 0, sizeof(VP)); +#ifdef WITH_LIBPOSTPROC PPContext = NULL; PPMode = NULL; +#endif // WITH_LIBPOSTPROC SWS = NULL; LastFrameNum = 0; CurrentFrame = 1; @@ -176,21 +205,26 @@ FFMS_VideoSource::FFMS_VideoSource(const char *SourceFile, FFMS_Index *Index, in DecodeFrame = avcodec_alloc_frame(); // Dummy allocations so the unallocated case doesn't have to be handled later +#ifdef WITH_LIBPOSTPROC avpicture_alloc(&PPFrame, PIX_FMT_GRAY8, 16, 16); +#endif // WITH_LIBPOSTPROC avpicture_alloc(&SWSFrame, PIX_FMT_GRAY8, 16, 16); } FFMS_VideoSource::~FFMS_VideoSource() { +#ifdef WITH_LIBPOSTPROC if (PPMode) pp_free_mode(PPMode); if (PPContext) pp_free_context(PPContext); + avpicture_free(&PPFrame); +#endif // WITH_LIBPOSTPROC + if (SWS) sws_freeContext(SWS); - avpicture_free(&PPFrame); avpicture_free(&SWSFrame); av_freep(&DecodeFrame); } diff --git a/aegisub/libffms/src/core/videosource.h b/aegisub/libffms/src/core/videosource.h index 7836810f4..5b70fa26d 100644 --- a/aegisub/libffms/src/core/videosource.h +++ b/aegisub/libffms/src/core/videosource.h @@ -25,7 +25,9 @@ extern "C" { #include #include #include +#ifdef WITH_LIBPOSTPROC #include +#endif // WITH_LIBPOSTPROC } // must be included after ffmpeg headers @@ -52,8 +54,10 @@ extern "C" { class FFMS_VideoSource { friend class FFSourceResources; private: +#ifdef WITH_LIBPOSTPROC pp_context_t *PPContext; pp_mode_t *PPMode; +#endif // WITH_LIBPOSTPROC SwsContext *SWS; int LastFrameHeight; int LastFrameWidth; @@ -113,7 +117,7 @@ class FFMatroskaVideo : public FFMS_VideoSource { private: MatroskaFile *MF; MatroskaReaderContext MC; - CompressedStream *CS; + TrackCompressionContext *TCC; char ErrorMessage[256]; FFSourceResources Res; size_t PacketNumber; @@ -131,7 +135,6 @@ public: class FFHaaliVideo : public FFMS_VideoSource { private: CComPtr pMMC; - std::vector CodecPrivate; AVBitStreamFilterContext *BitStreamFilter; FFSourceResources Res; diff --git a/aegisub/src/audio_provider_ffmpegsource.cpp b/aegisub/src/audio_provider_ffmpegsource.cpp index 0fbb109d6..051009789 100644 --- a/aegisub/src/audio_provider_ffmpegsource.cpp +++ b/aegisub/src/audio_provider_ffmpegsource.cpp @@ -66,7 +66,13 @@ FFmpegSourceAudioProvider::FFmpegSourceAudioProvider(wxString filename) else if (res != RPC_E_CHANGED_MODE) throw AudioOpenError("COM initialization failure"); #endif + // initialize ffmpegsource + // FIXME: CPU detection? +#if FFMS_VERSION >= ((2 << 24) | (14 << 16) | (0 << 8) | 0) + FFMS_Init(0, 1); +#else FFMS_Init(0); +#endif ErrInfo.Buffer = FFMSErrMsg; ErrInfo.BufferSize = sizeof(FFMSErrMsg); diff --git a/aegisub/src/video_provider_ffmpegsource.cpp b/aegisub/src/video_provider_ffmpegsource.cpp index dd6fbb625..598225d5b 100644 --- a/aegisub/src/video_provider_ffmpegsource.cpp +++ b/aegisub/src/video_provider_ffmpegsource.cpp @@ -77,7 +77,11 @@ FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(wxString filename) #endif // initialize ffmpegsource // FIXME: CPU detection? +#if FFMS_VERSION >= ((2 << 24) | (14 << 16) | (0 << 8) | 0) + FFMS_Init(0, 1); +#else FFMS_Init(0); +#endif ErrInfo.Buffer = FFMSErrMsg; ErrInfo.BufferSize = sizeof(FFMSErrMsg);