forked from mia/Aegisub
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)
|
||||
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 */
|
||||
|
|
|
@ -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""; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
Loading…
Reference in a new issue