diff --git a/FFmpegSource/ffbase.cpp b/FFmpegSource/ffbase.cpp index 24445a2fc..2e18a66b2 100644 --- a/FFmpegSource/ffbase.cpp +++ b/FFmpegSource/ffbase.cpp @@ -318,6 +318,7 @@ void FFBase::InitPP(int AWidth, int AHeight, const char *APPString, int AQuality if (AQuality < 0 || AQuality > PP_QUALITY_MAX) Env->ThrowError("FFmpegSource: Quality is out of range"); + // Unsafe? PPMode = pp_get_mode_by_name_and_quality((char *)APPString, AQuality); if (!PPMode) Env->ThrowError("FFmpegSource: Invalid postprocesing settings"); @@ -341,14 +342,14 @@ void FFBase::InitPP(int AWidth, int AHeight, const char *APPString, int AQuality void FFBase::SetOutputFormat(int ACurrentFormat, IScriptEnvironment *Env) { int Loss; - int BestFormat = avcodec_find_best_pix_fmt((1 << PIX_FMT_YUVJ420P) | (1 << PIX_FMT_YUV420P) | (1 << PIX_FMT_YUYV422) | (1 << PIX_FMT_RGB32) | (1 << PIX_FMT_RGB24), ACurrentFormat, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss); + int BestFormat = avcodec_find_best_pix_fmt((1 << PIX_FMT_YUVJ420P) | (1 << PIX_FMT_YUV420P) | (1 << PIX_FMT_YUYV422) | (1 << PIX_FMT_RGB32) | (1 << PIX_FMT_BGR24), ACurrentFormat, 1 /* Required to prevent pointless RGB32 => RGB24 conversion */, &Loss); switch (BestFormat) { case PIX_FMT_YUVJ420P: // stupid yv12 distinctions, also inexplicably completely undeniably incompatible with all other supported output formats case PIX_FMT_YUV420P: VI.pixel_type = VideoInfo::CS_I420; break; case PIX_FMT_YUYV422: VI.pixel_type = VideoInfo::CS_YUY2; break; case PIX_FMT_RGB32: VI.pixel_type = VideoInfo::CS_BGR32; break; - case PIX_FMT_RGB24: VI.pixel_type = VideoInfo::CS_BGR24; break; + case PIX_FMT_BGR24: VI.pixel_type = VideoInfo::CS_BGR24; break; default: Env->ThrowError("FFmpegSource: No suitable output format found"); } @@ -422,6 +423,7 @@ FFBase::FFBase() { PPContext = NULL; PPMode = NULL; SWS = NULL; + LastFrameNum = -1; DecodingBuffer = new uint8_t[AVCODEC_MAX_AUDIO_FRAME_SIZE]; #ifdef FLAC_CACHE FLACAudioCache = NULL; diff --git a/FFmpegSource/ffmatroskasource.cpp b/FFmpegSource/ffmatroskasource.cpp index 83a9a6d36..a1d5d40ca 100644 --- a/FFmpegSource/ffmatroskasource.cpp +++ b/FFmpegSource/ffmatroskasource.cpp @@ -44,7 +44,10 @@ int FFMatroskaSource::GetTrackIndex(int Index, unsigned char ATrackType, IScript return Index; } -FFMatroskaSource::FFMatroskaSource(const char *ASource, int AVideoTrack, int AAudioTrack, const char *ATimecodes, bool AVCache, const char *AVideoCache, const char *AAudioCache, int AACCompression, const char *APPString, int AQuality, IScriptEnvironment* Env) { +FFMatroskaSource::FFMatroskaSource(const char *ASource, int AVideoTrack, int AAudioTrack, const char *ATimecodes, + bool AVCache, const char *AVideoCache, const char *AAudioCache, int AACCompression, const char *APPString, + int AQuality, IScriptEnvironment* Env) { + CurrentFrame = 0; int VideoTrack; int AudioTrack; @@ -386,6 +389,9 @@ Done: } PVideoFrame FFMatroskaSource::GetFrame(int n, IScriptEnvironment* Env) { + if (LastFrameNum == n) + return LastFrame; + bool HasSeeked = false; if (n < CurrentFrame || FindClosestKeyFrame(n) > CurrentFrame) { @@ -408,5 +414,7 @@ PVideoFrame FFMatroskaSource::GetFrame(int n, IScriptEnvironment* Env) { CurrentFrame++; } while (CurrentFrame <= n); - return OutputFrame(DecodeFrame, Env); + LastFrame = OutputFrame(DecodeFrame, Env); + LastFrameNum = n; + return LastFrame; } diff --git a/FFmpegSource/ffmpegsource.cpp b/FFmpegSource/ffmpegsource.cpp index a545bb2c1..76e06ecbb 100644 --- a/FFmpegSource/ffmpegsource.cpp +++ b/FFmpegSource/ffmpegsource.cpp @@ -20,10 +20,6 @@ #include "ffmpegsource.h" -static DWORD WINAPI AVFindStreamInfoExecute(AVFormatContext *FormatContext) { - return av_find_stream_info(FormatContext); -} - int FFmpegSource::GetTrackIndex(int Index, CodecType ATrackType, IScriptEnvironment *Env) { if (Index == -1) for (unsigned int i = 0; i < FormatContext->nb_streams; i++) @@ -46,7 +42,10 @@ int FFmpegSource::GetTrackIndex(int Index, CodecType ATrackType, IScriptEnvironm return Index; } -FFmpegSource::FFmpegSource(const char *ASource, int AVideoTrack, int AAudioTrack, const char *ATimecodes, bool AVCache, const char *AVideoCache, const char *AAudioCache, int AACCompression, const char *APPString, int AQuality, int ASeekMode, IScriptEnvironment* Env) { +FFmpegSource::FFmpegSource(const char *ASource, int AVideoTrack, int AAudioTrack, const char *ATimecodes, + bool AVCache, const char *AVideoCache, const char *AAudioCache, int AACCompression, const char *APPString, + int AQuality, int ASeekMode, IScriptEnvironment* Env) { + CurrentFrame = 0; SeekMode = ASeekMode; @@ -71,7 +70,10 @@ FFmpegSource::FFmpegSource(const char *ASource, int AVideoTrack, int AAudioTrack bool ACacheIsValid = true; if (VideoTrack >= 0) { - VCacheIsValid = LoadFrameInfoFromFile(AVideoCache, ASource, VideoTrack); + if (SeekMode >= 0 && av_seek_frame(FormatContext, VideoTrack, 0, AVSEEK_FLAG_BACKWARD) < 0) + Env->ThrowError("FFmpegSource: Video track is unseekable"); + + VCacheIsValid = LoadFrameInfoFromFile(AVideoCache, ASource, VideoTrack); VideoCodecContext = FormatContext->streams[VideoTrack]->codec; @@ -124,6 +126,9 @@ FFmpegSource::FFmpegSource(const char *ASource, int AVideoTrack, int AAudioTrack ACacheIsValid = OpenAudioCache(AAudioCache, ASource, AudioTrack, Env); } + if ((!ACacheIsValid || !VCacheIsValid) && SeekMode == -1) + Env->ThrowError("FFmpegSource: Unusual indexing error, report on doom9"); + // Needs to be indexed? if (!ACacheIsValid || !VCacheIsValid) { #ifdef FLAC_CACHE @@ -257,22 +262,30 @@ Done: } PVideoFrame FFmpegSource::GetFrame(int n, IScriptEnvironment* Env) { - bool HasSeeked = false; - int ClosestKF = FindClosestKeyFrame(n); + if (LastFrameNum == n) + return LastFrame; - if (SeekMode == 0) { - if (n < CurrentFrame) { - av_seek_frame(FormatContext, VideoTrack, FrameToDTS.front().DTS, AVSEEK_FLAG_BACKWARD); - avcodec_flush_buffers(VideoCodecContext); - CurrentFrame = 0; - } - } 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) ? FrameToDTS[n].DTS : FrameToDTS[ClosestKF].DTS, AVSEEK_FLAG_BACKWARD); - avcodec_flush_buffers(VideoCodecContext); - HasSeeked = true; + bool HasSeeked = false; + + if (SeekMode >= 0) { + int ClosestKF = FindClosestKeyFrame(n); + + if (SeekMode == 0) { + if (n < CurrentFrame) { + av_seek_frame(FormatContext, VideoTrack, 0, AVSEEK_FLAG_BACKWARD); + avcodec_flush_buffers(VideoCodecContext); + CurrentFrame = 0; + } + } 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) ? FrameToDTS[n].DTS : FrameToDTS[ClosestKF].DTS, AVSEEK_FLAG_BACKWARD); + avcodec_flush_buffers(VideoCodecContext); + HasSeeked = true; + } } + } else if (n < CurrentFrame) { + Env->ThrowError("FFmpegSource: Non-linear access attempted"); } do { @@ -300,5 +313,7 @@ PVideoFrame FFmpegSource::GetFrame(int n, IScriptEnvironment* Env) { CurrentFrame++; } while (CurrentFrame <= n); - return OutputFrame(DecodeFrame, Env); + LastFrame = OutputFrame(DecodeFrame, Env); + LastFrameNum = n; + return LastFrame; } diff --git a/FFmpegSource/ffmpegsource.h b/FFmpegSource/ffmpegsource.h index 561ac0114..c1b0be41d 100644 --- a/FFmpegSource/ffmpegsource.h +++ b/FFmpegSource/ffmpegsource.h @@ -81,13 +81,16 @@ protected: AVFrame *DecodeFrame; AudioCacheFormat AudioCacheType; FILE *RawAudioCache; + PVideoFrame LastFrame; + int LastFrameNum; + uint8_t *DecodingBuffer; #ifdef FLAC_CACHE FLAC__StreamDecoder *FLACAudioCache; FLAC__int32 *FLACBuffer; #endif // FLAC_CACHE - uint8_t *DecodingBuffer; + struct FrameInfo { int64_t DTS; diff --git a/FFmpegSource/ffmpegsource.html b/FFmpegSource/ffmpegsource.html index c7d7a2ce9..20971c0a0 100644 --- a/FFmpegSource/ffmpegsource.html +++ b/FFmpegSource/ffmpegsource.html @@ -9,6 +9,14 @@ FFmpegSource Documentation
-Note that the audio cache will always be created when opening files with audio and that it will be huge since it stores all audio as raw signed 16 bit pcm. +Note that the audio cache will always be created when opening files with audio and that it will be huge since it stores all audio as raw signed 16/24/32 bit pcm, unsigned 8 bit pcm or as float, using flac to compress it is also possible to reduce the size by half.
@@ -152,7 +161,7 @@ Separate postprocessing which also seems to include a few simple deinterlacers
-accompression +accompression: Audio cache compression, -1 means raw audio and 0-8 uses FLAC with that compression level.
@@ -169,6 +178,7 @@ Separate postprocessing which also seems to include a few simple deinterlacers
seekmode:
Force how seeking is handled, has no effect on matroska files which always use the equivalent of seekmode=1
+ -1: linear access without rewind, will throw an error if each successive requested frame number isn't bigger than the last one, only intended for opening images but might work on well with some obscure video format
0: linear access, the definition of slow but should make some formats "usable"
1: safe normal, bases seeking decisions on the reported keyframe positions
2: unsafe normal, same as 1 but no error will be thrown if the exact destination has to be guessed
@@ -234,4 +244,4 @@ tn:64:128:256
--disable-encoders --disable-muxers --enable-small