Major rewrite of the video providing system. Hilights:

- It is now the responsibility of each video provider to provide a list of keyframe positions and (if it can) timecodes.
- The ffmpeg video provider now indexes files before opening them and does no longer rely on stream->duration to determine the number of frames. Fixes opening of MKV files, but it does not (currently) open timecodes automatically and reported keyframe positions seem way off. Status of frame-accuracy with MKV files unknown but it may very well work.
- Modified the way the ffmpeg video provider seeks (inspired by code from Myrsloik's ffmpegsource). Should no longer lose the first frame and should also no longer be frame-inaccurate, at least not with AVI.
- DirectShow video provider may or may not be completely broken, not tested.

Originally committed to SVN as r2252.
This commit is contained in:
Karl Blomster 2008-07-15 00:08:05 +00:00
parent 7e2b6afdf1
commit 893ff2f78a
14 changed files with 351 additions and 110 deletions

View file

@ -213,8 +213,10 @@ void LAVCAudioProvider::GetAudio(void *buf, int64_t start, int64_t count)
if (retval <= 0)
throw _T("ffmpeg audio provider: failed to decode audio");
/* decoding succeeded but the output buffer is empty, go to next packet */
if (temp_output_buffer_size == 0)
if (temp_output_buffer_size == 0) {
av_free_packet(&packet);
continue;
}
decoded_bytes = temp_output_buffer_size;
decoded_samples = decoded_bytes / 2; /* 2 bytes per sample */

View file

@ -39,8 +39,10 @@
//////////
// Headers
#include <wx/wxprec.h>
#include "video_frame.h"
#include "aegisub.h"
#include "vfr.h"
//////////////
@ -64,6 +66,10 @@ public:
virtual int GetWidth()=0; // Returns the video width in pixels
virtual int GetHeight()=0; // Returns the video height in pixels
virtual double GetFPS()=0; // Get framerate in frames per second
virtual bool AreKeyFramesLoaded()=0; // Returns true if keyframe info is loaded, false otherwise
virtual bool IsVFR()=0; // Returns true if video is VFR
virtual wxArrayInt GetKeyFrames()=0; // Returns list of keyframes
virtual FrameRate GetTrueFrameRate()=0; // Returns magic VFR stuff
// Use this to set any post-loading warnings, such as "being loaded with unreliable seeking"
virtual Aegisub::String GetWarning() { return L""; }

View file

@ -47,10 +47,11 @@
#include "dialog_progress.h"
#include "lavc_keyframes.h"
///////////////
// Constructor
LAVCKeyFrames::LAVCKeyFrames(const Aegisub::String filename)
: file(0), stream(0), streamN(-1) {
: file(0), stream(0), streamN(-1), numFrames(0) {
// Open LAVCFile
file = LAVCFile::Create(filename);
@ -63,6 +64,7 @@ LAVCKeyFrames::LAVCKeyFrames(const Aegisub::String filename)
}
}
if (streamN == -1) throw _T("ffmpeg keyframes reader: Could not find a video stream");
}
//////////////
@ -77,11 +79,18 @@ wxArrayInt LAVCKeyFrames::GetKeyFrames() {
wxArrayInt keyframes;
AVPacket packet;
// sanity check stream duration
if (stream->duration == AV_NOPTS_VALUE)
throw _T("ffmpeg keyframes reader: demuxer returned invalid stream length");
int total_frames = stream->duration; // FIXME: this will most likely NOT WORK for VFR files!
int total_frames;
// check if the stream duration is bogus (will happen for MKV files)
if (stream->duration == AV_NOPTS_VALUE) {
// throw _T("ffmpeg keyframes reader: demuxer returned invalid stream length");
// FIXME: find some less dumb way to do this
total_frames = 123456; // random suitably big number
}
else {
total_frames = stream->duration;
}
register unsigned int frameN = 0; // Number of parsed frames
numFrames = 0;
volatile bool canceled = false;
DialogProgress *progress = new DialogProgress(NULL,_("Load keyframes"),&canceled,_("Reading keyframes from video"),0,total_frames);
@ -91,6 +100,8 @@ wxArrayInt LAVCKeyFrames::GetKeyFrames() {
while (av_read_frame(file->fctx, &packet) == 0 && !canceled) {
// Check if packet is part of video stream
if (packet.stream_index == streamN) {
framesData.push_back(FrameInfo(packet.dts, (packet.flags & PKT_FLAG_KEY) ? 1 : 0));
// Check if the packet contains a keyframe
if (packet.flags & PKT_FLAG_KEY)
// note: frame numbers start from 0, watch out for the fencepost error
@ -108,6 +119,8 @@ wxArrayInt LAVCKeyFrames::GetKeyFrames() {
av_free_packet(&packet);
}
numFrames = frameN;
// Clean up progress
if (!canceled) progress->Destroy();
else throw wxString(_T("Keyframe loading cancelled by user"));
@ -115,4 +128,14 @@ wxArrayInt LAVCKeyFrames::GetKeyFrames() {
return keyframes;
}
// returns the video duration in frames
int LAVCKeyFrames::GetNumFrames() {
return numFrames;
}
// returns the frame metadata vector
FrameInfoVector LAVCKeyFrames::GetFrameData() {
return framesData;
}
#endif // WITH_FFMPEG

View file

@ -37,15 +37,28 @@
#include <wx/wxprec.h>
#include "lavc_file.h"
#include <vector>
struct FrameInfo {
int64_t DTS;
bool isKeyFrame;
FrameInfo(int64_t ADTS, bool isAKeyFrame) : DTS(ADTS), isKeyFrame(isAKeyFrame) {};
};
typedef std::vector<FrameInfo> FrameInfoVector;
class LAVCKeyFrames {
private:
LAVCFile* file; // Video file
AVStream* stream; // Used stream
int streamN; // Stream index
int numFrames; // number of frames in the video
protected:
FrameInfoVector framesData;
public:
LAVCKeyFrames(const Aegisub::String filename);
~LAVCKeyFrames();
wxArrayInt GetKeyFrames();
int GetNumFrames();
FrameInfoVector GetFrameData();
};

View file

@ -61,17 +61,6 @@
#include "ass_dialogue.h"
#include "ass_style.h"
#include "subs_grid.h"
#include "vfw_wrap.h"
#ifdef WITH_FFMPEG
#ifdef WIN32
#define __STDC_CONSTANT_MACROS 1
#include <stdint.h>
#endif /* WIN32 */
#include "lavc_keyframes.h"
#endif
#include "mkv_wrap.h"
#include "options.h"
#include "subs_edit_box.h"
#include "audio_display.h"
@ -254,8 +243,7 @@ void VideoContext::SetVideo(const wxString &filename) {
if (!filename.IsEmpty()) {
try {
grid->CommitChanges(true);
bool isVfr = false;
double overFps = 0;
// double overFps = 0;
FrameRate temp;
// Unload timecodes
@ -263,73 +251,6 @@ void VideoContext::SetVideo(const wxString &filename) {
//if (VFR_Output.IsLoaded()) unload = wxMessageBox(_("Do you want to unload timecodes, too?"),_("Unload timecodes?"),wxYES_NO | wxICON_QUESTION);
//if (unload == wxYES) VFR_Output.Unload();
// Read extra data from file
bool mkvOpen = MatroskaWrapper::wrapper.IsOpen();
wxString ext = filename.Right(4).Lower();
KeyFrames.Clear();
if (ext == _T(".mkv") || mkvOpen) {
// Parse mkv
if (!mkvOpen) MatroskaWrapper::wrapper.Open(filename);
// Get keyframes
KeyFrames = MatroskaWrapper::wrapper.GetKeyFrames();
keyFramesLoaded = true;
// Ask to override timecodes
int override = wxYES;
if (VFR_Output.IsLoaded()) override = wxMessageBox(_("You already have timecodes loaded. Replace them with the timecodes from the Matroska file?"),_("Replace timecodes?"),wxYES_NO | wxICON_QUESTION);
if (override == wxYES) {
MatroskaWrapper::wrapper.SetToTimecodes(temp);
isVfr = temp.GetFrameRateType() == VFR;
if (isVfr) {
overFps = temp.GetCommonFPS();
MatroskaWrapper::wrapper.SetToTimecodes(VFR_Input);
MatroskaWrapper::wrapper.SetToTimecodes(VFR_Output);
}
}
// Close mkv
MatroskaWrapper::wrapper.Close();
}
// do we have ffmpeg? if so try to load keyframes with it
#ifdef WITH_FFMPEG
else {
keyFramesLoaded = false;
KeyFrames.Clear();
LAVCKeyFrames k(filename.c_str());
KeyFrames = k.GetKeyFrames();
keyFramesLoaded = true;
}
#else
// no ffmpeg, check if we have windows, if so we can load keyframes
// from AVI files using VFW
#ifdef __WINDOWS__
else if (ext == _T(".avi")) {
keyFramesLoaded = false;
KeyFrames.Clear();
KeyFrames = VFWWrapper::GetKeyFrames(filename);
keyFramesLoaded = true;
}
#endif
#endif
// Check if the file is all keyframes
bool isAllKeyFrames = true;
for (unsigned int i=1; i<KeyFrames.GetCount(); i++) {
// Is the last keyframe not this keyframe -1?
if (KeyFrames[i-1] != (int)(i-1)) {
// It's not all keyframes, go ahead
isAllKeyFrames = false;
break;
}
}
// If it is all keyframes, discard the keyframe info as it is useless
if (isAllKeyFrames) {
KeyFrames.Clear();
keyFramesLoaded = false;
}
// Set GL context
#ifdef __WXMAC__
GetGLContext(displayList.front())->SetCurrent();
@ -338,7 +259,7 @@ void VideoContext::SetVideo(const wxString &filename) {
#endif
// Choose a provider
provider = VideoProviderFactoryManager::GetProvider(filename,overFps);
provider = VideoProviderFactoryManager::GetProvider(filename, 0);
loaded = provider != NULL;
// Get subtitles provider
@ -349,13 +270,28 @@ void VideoContext::SetVideo(const wxString &filename) {
catch (wxString err) { wxMessageBox(_T("Error while loading subtitles provider: ") + err,_T("Subtitles provider")); }
catch (const wchar_t *err) { wxMessageBox(_T("Error while loading subtitles provider: ") + wxString(err),_T("Subtitles provider")); }
KeyFrames.Clear();
// load keyframes if available
if (provider->AreKeyFramesLoaded()) {
KeyFrames = provider->GetKeyFrames();
keyFramesLoaded = true;
}
else {
keyFramesLoaded = false;
}
bool isVfr = provider->IsVFR();
// Set frame rate
fps = provider->GetFPS();
if (!isVfr || provider->IsNativelyByFrames()) {
VFR_Input.SetCFR(fps);
if (VFR_Output.GetFrameRateType() != VFR) VFR_Output.SetCFR(fps);
}
else provider->OverrideFrameTimeList(temp.GetFrameTimeList());
else {
FrameRate temp = provider->GetTrueFrameRate();
provider->OverrideFrameTimeList(temp.GetFrameTimeList());
}
// Gather video parameters
length = provider->GetFrameCount();

View file

@ -48,6 +48,8 @@
#include "vfr.h"
#include "ass_file.h"
#include "gl_wrap.h"
#include "mkv_wrap.h"
#include "vfw_wrap.h"
///////////////
@ -61,6 +63,9 @@ AvisynthVideoProvider::AvisynthVideoProvider(Aegisub::String _filename, double _
num_frames = 0;
last_fnum = -1;
byFrame = false;
KeyFrames.Clear();
keyFramesLoaded = false;
isVfr = false;
AVSTRACE(_T("AvisynthVideoProvider: Loading Subtitles Renderer"));
LoadRenderer();
@ -285,6 +290,64 @@ PClip AvisynthVideoProvider::OpenVideo(Aegisub::String _filename, bool mpeg2dec3
throw _T("Avisynth: No usable video found in ") + _filename;
}
// Read keyframes and timecodes from MKV file
isVfr = false;
FrameRate temp;
double overFps = 0;
bool mkvOpen = MatroskaWrapper::wrapper.IsOpen();
KeyFrames.Clear();
if (extension == _T(".mkv") || mkvOpen) {
// Parse mkv
if (!mkvOpen) MatroskaWrapper::wrapper.Open(_filename);
// Get keyframes
KeyFrames = MatroskaWrapper::wrapper.GetKeyFrames();
keyFramesLoaded = true;
// Ask to override timecodes
int override = wxYES;
if (VFR_Output.IsLoaded()) override = wxMessageBox(_("You already have timecodes loaded. Replace them with the timecodes from the Matroska file?"),_("Replace timecodes?"),wxYES_NO | wxICON_QUESTION);
if (override == wxYES) {
MatroskaWrapper::wrapper.SetToTimecodes(temp);
isVfr = temp.GetFrameRateType() == VFR;
if (isVfr) {
overFps = temp.GetCommonFPS();
MatroskaWrapper::wrapper.SetToTimecodes(VFR_Input);
MatroskaWrapper::wrapper.SetToTimecodes(VFR_Output);
trueFrameRate = temp;
}
}
// Close mkv
MatroskaWrapper::wrapper.Close();
}
// check if we have windows, if so we can load keyframes from AVI files using VFW
#ifdef __WINDOWS__
else if (extension == _T(".avi")) {
keyFramesLoaded = false;
KeyFrames.Clear();
KeyFrames = VFWWrapper::GetKeyFrames(_filename);
keyFramesLoaded = true;
}
#endif /* __WINDOWS__ */
// Check if the file is all keyframes
bool isAllKeyFrames = true;
for (unsigned int i=1; i<KeyFrames.GetCount(); i++) {
// Is the last keyframe not this keyframe -1?
if (KeyFrames[i-1] != (int)(i-1)) {
// It's not all keyframes, go ahead
isAllKeyFrames = false;
break;
}
}
// If it is all keyframes, discard the keyframe info as it is useless
if (isAllKeyFrames) {
KeyFrames.Clear();
keyFramesLoaded = false;
}
// Convert to RGB32
// If "Avisynth renders its own subs" is enabled, this should always be done,
// regardless of shaders being enabled or not. (Since VSFilter will convert

View file

@ -61,6 +61,11 @@ private:
wxArrayInt frameTime;
bool byFrame;
wxArrayInt KeyFrames;
bool keyFramesLoaded;
bool isVfr;
FrameRate trueFrameRate;
PClip RGB32Video;
PClip SubtitledVideo;
@ -88,6 +93,10 @@ public:
double GetFPS() { return (double)vi.fps_numerator/(double)vi.fps_denominator; };
int GetWidth() { return vi.width; };
int GetHeight() { return vi.height; };
bool AreKeyFramesLoaded() { return keyFramesLoaded; };
wxArrayInt GetKeyFrames() { return KeyFrames; };
bool IsVFR() { return isVfr; };
FrameRate GetTrueFrameRate() { return isVfr? trueFrameRate: FrameRate(); };
void OverrideFrameTimeList(wxArrayInt list);
bool IsNativelyByFrames() { return byFrame; }

View file

@ -162,6 +162,18 @@ int VideoProviderCache::GetHeight() {
double VideoProviderCache::GetFPS() {
return master->GetFPS();
}
bool VideoProviderCache::IsVFR() {
return master->IsVFR();
}
bool VideoProviderCache::AreKeyFramesLoaded() {
return master->AreKeyFramesLoaded();
}
wxArrayInt VideoProviderCache::GetKeyFrames() {
return master->GetKeyFrames();
}
FrameRate VideoProviderCache::GetTrueFrameRate() {
return master->GetTrueFrameRate();
}
void VideoProviderCache::OverrideFrameTimeList(Aegisub::IntArray list) {
master->OverrideFrameTimeList(list);
}

View file

@ -41,6 +41,7 @@
// Headers
#include <list>
#include "include/aegisub/video_provider.h"
#include "vfr.h"
////////////////
@ -86,6 +87,10 @@ public:
virtual int GetWidth(); // Returns the video width in pixels
virtual int GetHeight(); // Returns the video height in pixels
virtual double GetFPS(); // Get framerate in frames per second
virtual bool AreKeyFramesLoaded();
virtual bool IsVFR();
virtual wxArrayInt GetKeyFrames();
virtual FrameRate GetTrueFrameRate();
virtual void OverrideFrameTimeList(Aegisub::IntArray list); // Override the list with the provided one, for VFR handling
virtual bool IsNativelyByFrames();
virtual Aegisub::String GetWarning();

View file

@ -298,6 +298,62 @@ HRESULT DirectShowVideoProvider::OpenVideo(wxString _filename) {
// Register graph with Running Objects Table for remote graphedit connection
RegROT();
// Read keyframes and timecodes from MKV file
isVfr = false;
FrameRate temp;
double overFps = 0;
bool mkvOpen = MatroskaWrapper::wrapper.IsOpen();
KeyFrames.Clear();
wxString extension = _filename.Right(4).Lower();
if (extension == _T(".mkv") || mkvOpen) {
// Parse mkv
if (!mkvOpen) MatroskaWrapper::wrapper.Open(_filename);
// Get keyframes
KeyFrames = MatroskaWrapper::wrapper.GetKeyFrames();
keyFramesLoaded = true;
// Ask to override timecodes
int override = wxYES;
if (VFR_Output.IsLoaded()) override = wxMessageBox(_("You already have timecodes loaded. Replace them with the timecodes from the Matroska file?"),_("Replace timecodes?"),wxYES_NO | wxICON_QUESTION);
if (override == wxYES) {
MatroskaWrapper::wrapper.SetToTimecodes(temp);
isVfr = temp.GetFrameRateType() == VFR;
if (isVfr) {
overFps = temp.GetCommonFPS();
MatroskaWrapper::wrapper.SetToTimecodes(VFR_Input);
MatroskaWrapper::wrapper.SetToTimecodes(VFR_Output);
trueFrameRate = temp;
}
}
// Close mkv
MatroskaWrapper::wrapper.Close();
}
else if (extension == _T(".avi")) {
keyFramesLoaded = false;
KeyFrames.Clear();
KeyFrames = VFWWrapper::GetKeyFrames(_filename);
keyFramesLoaded = true;
}
// Check if the file is all keyframes
bool isAllKeyFrames = true;
for (unsigned int i=1; i<KeyFrames.GetCount(); i++) {
// Is the last keyframe not this keyframe -1?
if (KeyFrames[i-1] != (int)(i-1)) {
// It's not all keyframes, go ahead
isAllKeyFrames = false;
break;
}
}
// If it is all keyframes, discard the keyframe info as it is useless
if (isAllKeyFrames) {
KeyFrames.Clear();
keyFramesLoaded = false;
}
//NextFrame();
// Set frame count

View file

@ -52,6 +52,7 @@
#include <initguid.h>
#include "include/aegisub/video_provider.h"
#include "videosink.h"
#include "vfr.h"
///////////////////////////////////
@ -78,6 +79,10 @@ private:
double fps;
__int64 defd;
wxArrayInt KeyFrames;
bool keyFramesLoaded;
bool isVfr;
HRESULT OpenVideo(wxString _filename);
void CloseVideo();
@ -110,6 +115,11 @@ public:
double GetFPS() { return fps; };
int GetWidth() { return width; };
int GetHeight() { return height; };
bool AreKeyFramesLoaded() { return keyFramesLoaded; };
wxArrayInt GetKeyFrames() { return KeyFrames; };
bool IsVFR() { return isVfr; };
FrameRate GetTrueFrameRate() { return isVfr? trueFrameRate: FrameRate() };
Aegisub::String GetDecoderName() { return L"DirectShow"; }
bool IsNativelyByFrames() { return false; }

View file

@ -43,6 +43,7 @@
// Headers
#include "include/aegisub/video_provider.h"
#include <wx/colour.h>
#include "vfr.h"
////////////////////////
@ -72,6 +73,12 @@ public:
int GetWidth();
int GetHeight();
double GetFPS();
bool AreKeyFramesLoaded() { return false; };
wxArrayInt GetKeyFrames() { return wxArrayInt(); };
bool IsVFR() { return false; };
FrameRate GetTrueFrameRate() { return FrameRate(); };
Aegisub::String GetDecoderName();
};

View file

@ -41,16 +41,22 @@
#ifdef WIN32
#define EMULATE_INTTYPES
#endif
#define __STDC_CONSTANT_MACROS 1
#include <stdint.h>
#endif /* WIN32 */
#include <wx/wxprec.h>
#include <wx/image.h>
#include <algorithm>
#include <vector>
#include "video_provider_lavc.h"
#include "mkv_wrap.h"
#include "lavc_file.h"
#include "utils.h"
#include "vfr.h"
#include "ass_file.h"
#include "lavc_keyframes.h"
#include "video_context.h"
///////////////
@ -71,6 +77,7 @@ LAVCVideoProvider::LAVCVideoProvider(Aegisub::String filename,double fps) {
buffer2Size = 0;
vidStream = -1;
validFrame = false;
framesData.clear();
// Load
LoadVideo(filename,fps);
@ -119,8 +126,13 @@ void LAVCVideoProvider::LoadVideo(Aegisub::String filename, double fps) {
result = avcodec_open(codecContext,codec);
if (result < 0) throw _T("Failed to open video decoder");
// Check length
length = stream->duration;
// Parse file for keyframes and other useful stuff
LAVCKeyFrames LAVCFrameData(filename);
KeyFramesList = LAVCFrameData.GetKeyFrames();
keyFramesLoaded = true;
// set length etc.
length = LAVCFrameData.GetNumFrames();
framesData = LAVCFrameData.GetFrameData();
#if 0
isMkv = false;
length = stream->duration;
@ -141,6 +153,7 @@ void LAVCVideoProvider::LoadVideo(Aegisub::String filename, double fps) {
// Set frame
frameNumber = -1;
lastFrameNumber = -1;
}
// Catch errors
@ -196,14 +209,19 @@ void LAVCVideoProvider::Close() {
//////////////////
// Get next frame
bool LAVCVideoProvider::GetNextFrame() {
// Read packet
bool LAVCVideoProvider::GetNextFrame(int64_t *startDTS) {
AVPacket packet;
*startDTS = -1; // magic
// Read packet
while (av_read_frame(lavcfile->fctx, &packet)>=0) {
// Check if packet is part of video stream
if(packet.stream_index == vidStream) {
// Decode frame
int frameFinished;
if (*startDTS < 0)
*startDTS = packet.dts;
avcodec_decode_video(codecContext, frame, &frameFinished, packet.data, packet.size);
// Success?
@ -211,11 +229,13 @@ bool LAVCVideoProvider::GetNextFrame() {
// Set time
lastDecodeTime = packet.dts;
// Free packet
// Free packet and return
av_free_packet(&packet);
return true;
}
}
// free packet
av_free_packet(&packet);
}
// No more packets
@ -299,22 +319,27 @@ wxBitmap LAVCVideoProvider::AVFrameToWX(AVFrame *source, int n) {
// Get frame
const AegiVideoFrame LAVCVideoProvider::GetFrame(int n,int formatType) {
// Return stored frame
n = MID(0,n,GetFrameCount()-1);
if (n == frameNumber) {
// n = MID(0,n,GetFrameCount()-1);
if (n == lastFrameNumber) {
if (!validFrame) validFrame = true;
return curFrame;
}
if (frameNumber < 0)
frameNumber = 0;
// Following frame, just get it
if (n == frameNumber+1) {
GetNextFrame();
}
/* if (n == frameNumber+1) {
int64_t temp = -1;
GetNextFrame(&temp);
} */
// Needs to seek
else {
// else {
// Prepare seek
int64_t seekTo;
int result = 0;
// int64_t seekTo;
// int result = 0;
int closestKeyFrame = FindClosestKeyframe(n);
#if 0
// Get time to seek to
@ -352,11 +377,37 @@ const AegiVideoFrame LAVCVideoProvider::GetFrame(int n,int formatType) {
// Constant frame rate
else {
#endif
seekTo = n;
result = av_seek_frame(lavcfile->fctx,vidStream,seekTo,AVSEEK_FLAG_BACKWARD);
// seekTo = closestKeyFrame;
bool hasSeeked = false;
// do we really need to seek?
// 10 frames is used as a margin to prevent excessive seeking since the predicted best keyframe isn't always selected by avformat
if (n < frameNumber || closestKeyFrame > frameNumber+10) {
// do it
av_seek_frame(lavcfile->fctx, vidStream, framesData[closestKeyFrame].DTS, AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers(codecContext);
hasSeeked = true;
}
// decode frames until we have the one we want
do {
int64_t startTime;
GetNextFrame(&startTime);
if (hasSeeked) {
hasSeeked = false;
// is the seek destination known? does it belong to a frame?
if (startTime < 0 || (frameNumber = FrameFromDTS(startTime)) < 0)
throw _T("ffmpeg video provider: frame accurate seeking failed");
//frameNumber = ClosestFrameFromDTS(startTime);
}
frameNumber++;
} while (frameNumber <= n);
// Seek to keyframe
if (result == 0) {
/* if (result == 0) {
avcodec_flush_buffers(codecContext);
// Seek until final frame
@ -369,11 +420,11 @@ const AegiVideoFrame LAVCVideoProvider::GetFrame(int n,int formatType) {
// Failed seeking
else {
GetNextFrame();
}
}*/
#if 0
}
#endif
}
//}
// Get aegisub frame
@ -418,7 +469,7 @@ const AegiVideoFrame LAVCVideoProvider::GetFrame(int n,int formatType) {
// Set current frame
validFrame = true;
frameNumber = n;
lastFrameNumber = n;
// Return
return final;
@ -459,4 +510,37 @@ int LAVCVideoProvider::GetHeight() {
return codecContext->height;
}
//////////////////////
// Find the keyframe we should seek to if we want to seek to a given frame N
int LAVCVideoProvider::FindClosestKeyframe(int frameN) {
for (int i = frameN; i > 0; i--)
if (framesData[i].isKeyFrame)
return i;
return 0;
}
//////////////////////
// Convert a DTS into a frame number
int LAVCVideoProvider::FrameFromDTS(int64_t ADTS) {
for (int i = 0; i < (int)framesData.size(); i++)
if (framesData[i].DTS == ADTS)
return i;
return -1;
}
//////////////////////
// Find closest frame to the given DTS
int LAVCVideoProvider::ClosestFrameFromDTS(int64_t ADTS) {
int n = 0;
int64_t bestDiff = 0xFFFFFFFFFFFFFFLL; // big number
for (int i = 0; i < (int)framesData.size(); i++) {
int64_t currentDiff = FFABS(framesData[i].DTS - ADTS);
if (currentDiff < bestDiff) {
bestDiff = currentDiff;
n = i;
}
}
return n;
}
#endif // WITH_FFMPEG

View file

@ -42,6 +42,7 @@
#ifdef WIN32
#define EMULATE_INTTYPES
#endif
#include <vector>
extern "C" {
#include <ffmpeg/avcodec.h>
#include <ffmpeg/avformat.h>
@ -51,6 +52,7 @@ extern "C" {
#include "include/aegisub/aegisub.h"
#include "mkv_wrap.h"
#include "lavc_file.h"
#include "lavc_keyframes.h"
///////////////////////
@ -70,6 +72,10 @@ private:
AVFrame *frameRGB;
uint8_t *bufferRGB;
SwsContext *sws_context;
wxArrayInt KeyFramesList;
bool keyFramesLoaded;
// bool isVfr; // currently unused
int display_w;
int display_h;
@ -79,16 +85,21 @@ private:
bool isMkv;
int64_t lastDecodeTime;
int frameNumber;
int lastFrameNumber;
int length;
AegiVideoFrame curFrame;
bool validFrame;
FrameInfoVector framesData;
uint8_t *buffer1;
uint8_t *buffer2;
int buffer1Size;
int buffer2Size;
bool GetNextFrame();
int FindClosestKeyframe(int frameN);
int FrameFromDTS(int64_t ADTS);
int ClosestFrameFromDTS(int64_t ADTS);
bool GetNextFrame(int64_t *DTS);
void LoadVideo(Aegisub::String filename, double fps);
void Close();
@ -105,6 +116,10 @@ public:
int GetWidth();
int GetHeight();
double GetFPS();
bool AreKeyFramesLoaded() { return keyFramesLoaded; };
wxArrayInt GetKeyFrames() { return KeyFramesList; };
bool IsVFR() { return false; }; // FIXME: bork?
FrameRate GetTrueFrameRate() { return FrameRate(); }; // nothing useful here
Aegisub::String GetDecoderName() { return L"FFMpeg/libavcodec"; }
bool IsNativelyByFrames() { return true; }
int GetDesiredCacheSize() { return 8; }