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:
parent
7e2b6afdf1
commit
893ff2f78a
14 changed files with 351 additions and 110 deletions
|
@ -213,8 +213,10 @@ void LAVCAudioProvider::GetAudio(void *buf, int64_t start, int64_t count)
|
||||||
if (retval <= 0)
|
if (retval <= 0)
|
||||||
throw _T("ffmpeg audio provider: failed to decode audio");
|
throw _T("ffmpeg audio provider: failed to decode audio");
|
||||||
/* decoding succeeded but the output buffer is empty, go to next packet */
|
/* 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;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
decoded_bytes = temp_output_buffer_size;
|
decoded_bytes = temp_output_buffer_size;
|
||||||
decoded_samples = decoded_bytes / 2; /* 2 bytes per sample */
|
decoded_samples = decoded_bytes / 2; /* 2 bytes per sample */
|
||||||
|
|
|
@ -39,8 +39,10 @@
|
||||||
|
|
||||||
//////////
|
//////////
|
||||||
// Headers
|
// Headers
|
||||||
|
#include <wx/wxprec.h>
|
||||||
#include "video_frame.h"
|
#include "video_frame.h"
|
||||||
#include "aegisub.h"
|
#include "aegisub.h"
|
||||||
|
#include "vfr.h"
|
||||||
|
|
||||||
|
|
||||||
//////////////
|
//////////////
|
||||||
|
@ -64,6 +66,10 @@ public:
|
||||||
virtual int GetWidth()=0; // Returns the video width in pixels
|
virtual int GetWidth()=0; // Returns the video width in pixels
|
||||||
virtual int GetHeight()=0; // Returns the video height in pixels
|
virtual int GetHeight()=0; // Returns the video height in pixels
|
||||||
virtual double GetFPS()=0; // Get framerate in frames per second
|
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"
|
// Use this to set any post-loading warnings, such as "being loaded with unreliable seeking"
|
||||||
virtual Aegisub::String GetWarning() { return L""; }
|
virtual Aegisub::String GetWarning() { return L""; }
|
||||||
|
|
|
@ -47,10 +47,11 @@
|
||||||
#include "dialog_progress.h"
|
#include "dialog_progress.h"
|
||||||
#include "lavc_keyframes.h"
|
#include "lavc_keyframes.h"
|
||||||
|
|
||||||
|
|
||||||
///////////////
|
///////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
LAVCKeyFrames::LAVCKeyFrames(const Aegisub::String filename)
|
LAVCKeyFrames::LAVCKeyFrames(const Aegisub::String filename)
|
||||||
: file(0), stream(0), streamN(-1) {
|
: file(0), stream(0), streamN(-1), numFrames(0) {
|
||||||
// Open LAVCFile
|
// Open LAVCFile
|
||||||
file = LAVCFile::Create(filename);
|
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");
|
if (streamN == -1) throw _T("ffmpeg keyframes reader: Could not find a video stream");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////
|
//////////////
|
||||||
|
@ -77,11 +79,18 @@ wxArrayInt LAVCKeyFrames::GetKeyFrames() {
|
||||||
wxArrayInt keyframes;
|
wxArrayInt keyframes;
|
||||||
|
|
||||||
AVPacket packet;
|
AVPacket packet;
|
||||||
// sanity check stream duration
|
int total_frames;
|
||||||
if (stream->duration == AV_NOPTS_VALUE)
|
// check if the stream duration is bogus (will happen for MKV files)
|
||||||
throw _T("ffmpeg keyframes reader: demuxer returned invalid stream length");
|
if (stream->duration == AV_NOPTS_VALUE) {
|
||||||
int total_frames = stream->duration; // FIXME: this will most likely NOT WORK for VFR files!
|
// 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
|
register unsigned int frameN = 0; // Number of parsed frames
|
||||||
|
numFrames = 0;
|
||||||
|
|
||||||
volatile bool canceled = false;
|
volatile bool canceled = false;
|
||||||
DialogProgress *progress = new DialogProgress(NULL,_("Load keyframes"),&canceled,_("Reading keyframes from video"),0,total_frames);
|
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) {
|
while (av_read_frame(file->fctx, &packet) == 0 && !canceled) {
|
||||||
// Check if packet is part of video stream
|
// Check if packet is part of video stream
|
||||||
if (packet.stream_index == streamN) {
|
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
|
// Check if the packet contains a keyframe
|
||||||
if (packet.flags & PKT_FLAG_KEY)
|
if (packet.flags & PKT_FLAG_KEY)
|
||||||
// note: frame numbers start from 0, watch out for the fencepost error
|
// note: frame numbers start from 0, watch out for the fencepost error
|
||||||
|
@ -108,6 +119,8 @@ wxArrayInt LAVCKeyFrames::GetKeyFrames() {
|
||||||
av_free_packet(&packet);
|
av_free_packet(&packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
numFrames = frameN;
|
||||||
|
|
||||||
// Clean up progress
|
// Clean up progress
|
||||||
if (!canceled) progress->Destroy();
|
if (!canceled) progress->Destroy();
|
||||||
else throw wxString(_T("Keyframe loading cancelled by user"));
|
else throw wxString(_T("Keyframe loading cancelled by user"));
|
||||||
|
@ -115,4 +128,14 @@ wxArrayInt LAVCKeyFrames::GetKeyFrames() {
|
||||||
return keyframes;
|
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
|
#endif // WITH_FFMPEG
|
||||||
|
|
|
@ -37,15 +37,28 @@
|
||||||
|
|
||||||
#include <wx/wxprec.h>
|
#include <wx/wxprec.h>
|
||||||
#include "lavc_file.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 {
|
class LAVCKeyFrames {
|
||||||
private:
|
private:
|
||||||
LAVCFile* file; // Video file
|
LAVCFile* file; // Video file
|
||||||
AVStream* stream; // Used stream
|
AVStream* stream; // Used stream
|
||||||
|
|
||||||
int streamN; // Stream index
|
int streamN; // Stream index
|
||||||
|
int numFrames; // number of frames in the video
|
||||||
|
protected:
|
||||||
|
FrameInfoVector framesData;
|
||||||
public:
|
public:
|
||||||
LAVCKeyFrames(const Aegisub::String filename);
|
LAVCKeyFrames(const Aegisub::String filename);
|
||||||
~LAVCKeyFrames();
|
~LAVCKeyFrames();
|
||||||
wxArrayInt GetKeyFrames();
|
wxArrayInt GetKeyFrames();
|
||||||
|
int GetNumFrames();
|
||||||
|
FrameInfoVector GetFrameData();
|
||||||
};
|
};
|
||||||
|
|
|
@ -61,17 +61,6 @@
|
||||||
#include "ass_dialogue.h"
|
#include "ass_dialogue.h"
|
||||||
#include "ass_style.h"
|
#include "ass_style.h"
|
||||||
#include "subs_grid.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 "options.h"
|
||||||
#include "subs_edit_box.h"
|
#include "subs_edit_box.h"
|
||||||
#include "audio_display.h"
|
#include "audio_display.h"
|
||||||
|
@ -254,8 +243,7 @@ void VideoContext::SetVideo(const wxString &filename) {
|
||||||
if (!filename.IsEmpty()) {
|
if (!filename.IsEmpty()) {
|
||||||
try {
|
try {
|
||||||
grid->CommitChanges(true);
|
grid->CommitChanges(true);
|
||||||
bool isVfr = false;
|
// double overFps = 0;
|
||||||
double overFps = 0;
|
|
||||||
FrameRate temp;
|
FrameRate temp;
|
||||||
|
|
||||||
// Unload timecodes
|
// 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 (VFR_Output.IsLoaded()) unload = wxMessageBox(_("Do you want to unload timecodes, too?"),_("Unload timecodes?"),wxYES_NO | wxICON_QUESTION);
|
||||||
//if (unload == wxYES) VFR_Output.Unload();
|
//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
|
// Set GL context
|
||||||
#ifdef __WXMAC__
|
#ifdef __WXMAC__
|
||||||
GetGLContext(displayList.front())->SetCurrent();
|
GetGLContext(displayList.front())->SetCurrent();
|
||||||
|
@ -338,7 +259,7 @@ void VideoContext::SetVideo(const wxString &filename) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Choose a provider
|
// Choose a provider
|
||||||
provider = VideoProviderFactoryManager::GetProvider(filename,overFps);
|
provider = VideoProviderFactoryManager::GetProvider(filename, 0);
|
||||||
loaded = provider != NULL;
|
loaded = provider != NULL;
|
||||||
|
|
||||||
// Get subtitles provider
|
// 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 (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")); }
|
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
|
// Set frame rate
|
||||||
fps = provider->GetFPS();
|
fps = provider->GetFPS();
|
||||||
if (!isVfr || provider->IsNativelyByFrames()) {
|
if (!isVfr || provider->IsNativelyByFrames()) {
|
||||||
VFR_Input.SetCFR(fps);
|
VFR_Input.SetCFR(fps);
|
||||||
if (VFR_Output.GetFrameRateType() != VFR) VFR_Output.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
|
// Gather video parameters
|
||||||
length = provider->GetFrameCount();
|
length = provider->GetFrameCount();
|
||||||
|
|
|
@ -48,6 +48,8 @@
|
||||||
#include "vfr.h"
|
#include "vfr.h"
|
||||||
#include "ass_file.h"
|
#include "ass_file.h"
|
||||||
#include "gl_wrap.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;
|
num_frames = 0;
|
||||||
last_fnum = -1;
|
last_fnum = -1;
|
||||||
byFrame = false;
|
byFrame = false;
|
||||||
|
KeyFrames.Clear();
|
||||||
|
keyFramesLoaded = false;
|
||||||
|
isVfr = false;
|
||||||
|
|
||||||
AVSTRACE(_T("AvisynthVideoProvider: Loading Subtitles Renderer"));
|
AVSTRACE(_T("AvisynthVideoProvider: Loading Subtitles Renderer"));
|
||||||
LoadRenderer();
|
LoadRenderer();
|
||||||
|
@ -285,6 +290,64 @@ PClip AvisynthVideoProvider::OpenVideo(Aegisub::String _filename, bool mpeg2dec3
|
||||||
throw _T("Avisynth: No usable video found in ") + _filename;
|
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
|
// Convert to RGB32
|
||||||
// If "Avisynth renders its own subs" is enabled, this should always be done,
|
// If "Avisynth renders its own subs" is enabled, this should always be done,
|
||||||
// regardless of shaders being enabled or not. (Since VSFilter will convert
|
// regardless of shaders being enabled or not. (Since VSFilter will convert
|
||||||
|
|
|
@ -61,6 +61,11 @@ private:
|
||||||
wxArrayInt frameTime;
|
wxArrayInt frameTime;
|
||||||
bool byFrame;
|
bool byFrame;
|
||||||
|
|
||||||
|
wxArrayInt KeyFrames;
|
||||||
|
bool keyFramesLoaded;
|
||||||
|
bool isVfr;
|
||||||
|
FrameRate trueFrameRate;
|
||||||
|
|
||||||
PClip RGB32Video;
|
PClip RGB32Video;
|
||||||
PClip SubtitledVideo;
|
PClip SubtitledVideo;
|
||||||
|
|
||||||
|
@ -88,6 +93,10 @@ public:
|
||||||
double GetFPS() { return (double)vi.fps_numerator/(double)vi.fps_denominator; };
|
double GetFPS() { return (double)vi.fps_numerator/(double)vi.fps_denominator; };
|
||||||
int GetWidth() { return vi.width; };
|
int GetWidth() { return vi.width; };
|
||||||
int GetHeight() { return vi.height; };
|
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);
|
void OverrideFrameTimeList(wxArrayInt list);
|
||||||
bool IsNativelyByFrames() { return byFrame; }
|
bool IsNativelyByFrames() { return byFrame; }
|
||||||
|
|
|
@ -162,6 +162,18 @@ int VideoProviderCache::GetHeight() {
|
||||||
double VideoProviderCache::GetFPS() {
|
double VideoProviderCache::GetFPS() {
|
||||||
return master->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) {
|
void VideoProviderCache::OverrideFrameTimeList(Aegisub::IntArray list) {
|
||||||
master->OverrideFrameTimeList(list);
|
master->OverrideFrameTimeList(list);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
// Headers
|
// Headers
|
||||||
#include <list>
|
#include <list>
|
||||||
#include "include/aegisub/video_provider.h"
|
#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 GetWidth(); // Returns the video width in pixels
|
||||||
virtual int GetHeight(); // Returns the video height in pixels
|
virtual int GetHeight(); // Returns the video height in pixels
|
||||||
virtual double GetFPS(); // Get framerate in frames per second
|
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 void OverrideFrameTimeList(Aegisub::IntArray list); // Override the list with the provided one, for VFR handling
|
||||||
virtual bool IsNativelyByFrames();
|
virtual bool IsNativelyByFrames();
|
||||||
virtual Aegisub::String GetWarning();
|
virtual Aegisub::String GetWarning();
|
||||||
|
|
|
@ -298,6 +298,62 @@ HRESULT DirectShowVideoProvider::OpenVideo(wxString _filename) {
|
||||||
// Register graph with Running Objects Table for remote graphedit connection
|
// Register graph with Running Objects Table for remote graphedit connection
|
||||||
RegROT();
|
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();
|
//NextFrame();
|
||||||
|
|
||||||
// Set frame count
|
// Set frame count
|
||||||
|
|
|
@ -52,6 +52,7 @@
|
||||||
#include <initguid.h>
|
#include <initguid.h>
|
||||||
#include "include/aegisub/video_provider.h"
|
#include "include/aegisub/video_provider.h"
|
||||||
#include "videosink.h"
|
#include "videosink.h"
|
||||||
|
#include "vfr.h"
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////
|
///////////////////////////////////
|
||||||
|
@ -78,6 +79,10 @@ private:
|
||||||
double fps;
|
double fps;
|
||||||
__int64 defd;
|
__int64 defd;
|
||||||
|
|
||||||
|
wxArrayInt KeyFrames;
|
||||||
|
bool keyFramesLoaded;
|
||||||
|
bool isVfr;
|
||||||
|
|
||||||
HRESULT OpenVideo(wxString _filename);
|
HRESULT OpenVideo(wxString _filename);
|
||||||
void CloseVideo();
|
void CloseVideo();
|
||||||
|
|
||||||
|
@ -110,6 +115,11 @@ public:
|
||||||
double GetFPS() { return fps; };
|
double GetFPS() { return fps; };
|
||||||
int GetWidth() { return width; };
|
int GetWidth() { return width; };
|
||||||
int GetHeight() { return height; };
|
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"; }
|
Aegisub::String GetDecoderName() { return L"DirectShow"; }
|
||||||
bool IsNativelyByFrames() { return false; }
|
bool IsNativelyByFrames() { return false; }
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
// Headers
|
// Headers
|
||||||
#include "include/aegisub/video_provider.h"
|
#include "include/aegisub/video_provider.h"
|
||||||
#include <wx/colour.h>
|
#include <wx/colour.h>
|
||||||
|
#include "vfr.h"
|
||||||
|
|
||||||
|
|
||||||
////////////////////////
|
////////////////////////
|
||||||
|
@ -72,6 +73,12 @@ public:
|
||||||
int GetWidth();
|
int GetWidth();
|
||||||
int GetHeight();
|
int GetHeight();
|
||||||
double GetFPS();
|
double GetFPS();
|
||||||
|
|
||||||
|
bool AreKeyFramesLoaded() { return false; };
|
||||||
|
wxArrayInt GetKeyFrames() { return wxArrayInt(); };
|
||||||
|
bool IsVFR() { return false; };
|
||||||
|
FrameRate GetTrueFrameRate() { return FrameRate(); };
|
||||||
|
|
||||||
Aegisub::String GetDecoderName();
|
Aegisub::String GetDecoderName();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -41,16 +41,22 @@
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#define EMULATE_INTTYPES
|
#define EMULATE_INTTYPES
|
||||||
#endif
|
#define __STDC_CONSTANT_MACROS 1
|
||||||
|
#include <stdint.h>
|
||||||
|
#endif /* WIN32 */
|
||||||
|
|
||||||
#include <wx/wxprec.h>
|
#include <wx/wxprec.h>
|
||||||
#include <wx/image.h>
|
#include <wx/image.h>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
#include "video_provider_lavc.h"
|
#include "video_provider_lavc.h"
|
||||||
#include "mkv_wrap.h"
|
#include "mkv_wrap.h"
|
||||||
#include "lavc_file.h"
|
#include "lavc_file.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "vfr.h"
|
#include "vfr.h"
|
||||||
#include "ass_file.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;
|
buffer2Size = 0;
|
||||||
vidStream = -1;
|
vidStream = -1;
|
||||||
validFrame = false;
|
validFrame = false;
|
||||||
|
framesData.clear();
|
||||||
|
|
||||||
// Load
|
// Load
|
||||||
LoadVideo(filename,fps);
|
LoadVideo(filename,fps);
|
||||||
|
@ -119,8 +126,13 @@ void LAVCVideoProvider::LoadVideo(Aegisub::String filename, double fps) {
|
||||||
result = avcodec_open(codecContext,codec);
|
result = avcodec_open(codecContext,codec);
|
||||||
if (result < 0) throw _T("Failed to open video decoder");
|
if (result < 0) throw _T("Failed to open video decoder");
|
||||||
|
|
||||||
// Check length
|
// Parse file for keyframes and other useful stuff
|
||||||
length = stream->duration;
|
LAVCKeyFrames LAVCFrameData(filename);
|
||||||
|
KeyFramesList = LAVCFrameData.GetKeyFrames();
|
||||||
|
keyFramesLoaded = true;
|
||||||
|
// set length etc.
|
||||||
|
length = LAVCFrameData.GetNumFrames();
|
||||||
|
framesData = LAVCFrameData.GetFrameData();
|
||||||
#if 0
|
#if 0
|
||||||
isMkv = false;
|
isMkv = false;
|
||||||
length = stream->duration;
|
length = stream->duration;
|
||||||
|
@ -141,6 +153,7 @@ void LAVCVideoProvider::LoadVideo(Aegisub::String filename, double fps) {
|
||||||
|
|
||||||
// Set frame
|
// Set frame
|
||||||
frameNumber = -1;
|
frameNumber = -1;
|
||||||
|
lastFrameNumber = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Catch errors
|
// Catch errors
|
||||||
|
@ -196,14 +209,19 @@ void LAVCVideoProvider::Close() {
|
||||||
|
|
||||||
//////////////////
|
//////////////////
|
||||||
// Get next frame
|
// Get next frame
|
||||||
bool LAVCVideoProvider::GetNextFrame() {
|
bool LAVCVideoProvider::GetNextFrame(int64_t *startDTS) {
|
||||||
// Read packet
|
|
||||||
AVPacket packet;
|
AVPacket packet;
|
||||||
|
*startDTS = -1; // magic
|
||||||
|
|
||||||
|
// Read packet
|
||||||
while (av_read_frame(lavcfile->fctx, &packet)>=0) {
|
while (av_read_frame(lavcfile->fctx, &packet)>=0) {
|
||||||
// Check if packet is part of video stream
|
// Check if packet is part of video stream
|
||||||
if(packet.stream_index == vidStream) {
|
if(packet.stream_index == vidStream) {
|
||||||
// Decode frame
|
// Decode frame
|
||||||
int frameFinished;
|
int frameFinished;
|
||||||
|
if (*startDTS < 0)
|
||||||
|
*startDTS = packet.dts;
|
||||||
|
|
||||||
avcodec_decode_video(codecContext, frame, &frameFinished, packet.data, packet.size);
|
avcodec_decode_video(codecContext, frame, &frameFinished, packet.data, packet.size);
|
||||||
|
|
||||||
// Success?
|
// Success?
|
||||||
|
@ -211,11 +229,13 @@ bool LAVCVideoProvider::GetNextFrame() {
|
||||||
// Set time
|
// Set time
|
||||||
lastDecodeTime = packet.dts;
|
lastDecodeTime = packet.dts;
|
||||||
|
|
||||||
// Free packet
|
// Free packet and return
|
||||||
av_free_packet(&packet);
|
av_free_packet(&packet);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// free packet
|
||||||
|
av_free_packet(&packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
// No more packets
|
// No more packets
|
||||||
|
@ -299,22 +319,27 @@ wxBitmap LAVCVideoProvider::AVFrameToWX(AVFrame *source, int n) {
|
||||||
// Get frame
|
// Get frame
|
||||||
const AegiVideoFrame LAVCVideoProvider::GetFrame(int n,int formatType) {
|
const AegiVideoFrame LAVCVideoProvider::GetFrame(int n,int formatType) {
|
||||||
// Return stored frame
|
// Return stored frame
|
||||||
n = MID(0,n,GetFrameCount()-1);
|
// n = MID(0,n,GetFrameCount()-1);
|
||||||
if (n == frameNumber) {
|
if (n == lastFrameNumber) {
|
||||||
if (!validFrame) validFrame = true;
|
if (!validFrame) validFrame = true;
|
||||||
return curFrame;
|
return curFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (frameNumber < 0)
|
||||||
|
frameNumber = 0;
|
||||||
|
|
||||||
// Following frame, just get it
|
// Following frame, just get it
|
||||||
if (n == frameNumber+1) {
|
/* if (n == frameNumber+1) {
|
||||||
GetNextFrame();
|
int64_t temp = -1;
|
||||||
}
|
GetNextFrame(&temp);
|
||||||
|
} */
|
||||||
|
|
||||||
// Needs to seek
|
// Needs to seek
|
||||||
else {
|
// else {
|
||||||
// Prepare seek
|
// Prepare seek
|
||||||
int64_t seekTo;
|
// int64_t seekTo;
|
||||||
int result = 0;
|
// int result = 0;
|
||||||
|
int closestKeyFrame = FindClosestKeyframe(n);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// Get time to seek to
|
// Get time to seek to
|
||||||
|
@ -352,11 +377,37 @@ const AegiVideoFrame LAVCVideoProvider::GetFrame(int n,int formatType) {
|
||||||
// Constant frame rate
|
// Constant frame rate
|
||||||
else {
|
else {
|
||||||
#endif
|
#endif
|
||||||
seekTo = n;
|
// seekTo = closestKeyFrame;
|
||||||
result = av_seek_frame(lavcfile->fctx,vidStream,seekTo,AVSEEK_FLAG_BACKWARD);
|
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
|
// Seek to keyframe
|
||||||
if (result == 0) {
|
/* if (result == 0) {
|
||||||
avcodec_flush_buffers(codecContext);
|
avcodec_flush_buffers(codecContext);
|
||||||
|
|
||||||
// Seek until final frame
|
// Seek until final frame
|
||||||
|
@ -369,11 +420,11 @@ const AegiVideoFrame LAVCVideoProvider::GetFrame(int n,int formatType) {
|
||||||
// Failed seeking
|
// Failed seeking
|
||||||
else {
|
else {
|
||||||
GetNextFrame();
|
GetNextFrame();
|
||||||
}
|
}*/
|
||||||
#if 0
|
#if 0
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|
||||||
// Get aegisub frame
|
// Get aegisub frame
|
||||||
|
@ -418,7 +469,7 @@ const AegiVideoFrame LAVCVideoProvider::GetFrame(int n,int formatType) {
|
||||||
|
|
||||||
// Set current frame
|
// Set current frame
|
||||||
validFrame = true;
|
validFrame = true;
|
||||||
frameNumber = n;
|
lastFrameNumber = n;
|
||||||
|
|
||||||
// Return
|
// Return
|
||||||
return final;
|
return final;
|
||||||
|
@ -459,4 +510,37 @@ int LAVCVideoProvider::GetHeight() {
|
||||||
return codecContext->height;
|
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
|
#endif // WITH_FFMPEG
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#define EMULATE_INTTYPES
|
#define EMULATE_INTTYPES
|
||||||
#endif
|
#endif
|
||||||
|
#include <vector>
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <ffmpeg/avcodec.h>
|
#include <ffmpeg/avcodec.h>
|
||||||
#include <ffmpeg/avformat.h>
|
#include <ffmpeg/avformat.h>
|
||||||
|
@ -51,6 +52,7 @@ extern "C" {
|
||||||
#include "include/aegisub/aegisub.h"
|
#include "include/aegisub/aegisub.h"
|
||||||
#include "mkv_wrap.h"
|
#include "mkv_wrap.h"
|
||||||
#include "lavc_file.h"
|
#include "lavc_file.h"
|
||||||
|
#include "lavc_keyframes.h"
|
||||||
|
|
||||||
|
|
||||||
///////////////////////
|
///////////////////////
|
||||||
|
@ -70,6 +72,10 @@ private:
|
||||||
AVFrame *frameRGB;
|
AVFrame *frameRGB;
|
||||||
uint8_t *bufferRGB;
|
uint8_t *bufferRGB;
|
||||||
SwsContext *sws_context;
|
SwsContext *sws_context;
|
||||||
|
|
||||||
|
wxArrayInt KeyFramesList;
|
||||||
|
bool keyFramesLoaded;
|
||||||
|
// bool isVfr; // currently unused
|
||||||
|
|
||||||
int display_w;
|
int display_w;
|
||||||
int display_h;
|
int display_h;
|
||||||
|
@ -79,16 +85,21 @@ private:
|
||||||
bool isMkv;
|
bool isMkv;
|
||||||
int64_t lastDecodeTime;
|
int64_t lastDecodeTime;
|
||||||
int frameNumber;
|
int frameNumber;
|
||||||
|
int lastFrameNumber;
|
||||||
int length;
|
int length;
|
||||||
AegiVideoFrame curFrame;
|
AegiVideoFrame curFrame;
|
||||||
bool validFrame;
|
bool validFrame;
|
||||||
|
FrameInfoVector framesData;
|
||||||
|
|
||||||
uint8_t *buffer1;
|
uint8_t *buffer1;
|
||||||
uint8_t *buffer2;
|
uint8_t *buffer2;
|
||||||
int buffer1Size;
|
int buffer1Size;
|
||||||
int buffer2Size;
|
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 LoadVideo(Aegisub::String filename, double fps);
|
||||||
void Close();
|
void Close();
|
||||||
|
|
||||||
|
@ -105,6 +116,10 @@ public:
|
||||||
int GetWidth();
|
int GetWidth();
|
||||||
int GetHeight();
|
int GetHeight();
|
||||||
double GetFPS();
|
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"; }
|
Aegisub::String GetDecoderName() { return L"FFMpeg/libavcodec"; }
|
||||||
bool IsNativelyByFrames() { return true; }
|
bool IsNativelyByFrames() { return true; }
|
||||||
int GetDesiredCacheSize() { return 8; }
|
int GetDesiredCacheSize() { return 8; }
|
||||||
|
|
Loading…
Reference in a new issue