new video provider: ffmpegsource

Originally committed to SVN as r2310.
This commit is contained in:
Karl Blomster 2008-09-03 17:03:20 +00:00
parent 6e6d85df7c
commit d8c8d47e4c
10 changed files with 398 additions and 13 deletions

View file

@ -41,8 +41,8 @@
#endif #endif
#include <wx/filename.h> #include <wx/filename.h>
extern "C" { extern "C" {
#include <ffmpeg/avcodec.h> #include <libavcodec/avcodec.h>
#include <ffmpeg/avformat.h> #include <libavformat/avformat.h>
} }
#include "include/aegisub/aegisub.h" #include "include/aegisub/aegisub.h"

View file

@ -100,7 +100,7 @@ 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)); framesData.push_back(LAVCFrameInfo(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)
@ -134,7 +134,7 @@ int LAVCKeyFrames::GetNumFrames() {
} }
// returns the frame metadata vector // returns the frame metadata vector
FrameInfoVector LAVCKeyFrames::GetFrameData() { LAVCFrameInfoVector LAVCKeyFrames::GetFrameData() {
return framesData; return framesData;
} }

View file

@ -39,13 +39,13 @@
#include "lavc_file.h" #include "lavc_file.h"
#include <vector> #include <vector>
struct FrameInfo { struct LAVCFrameInfo {
int64_t DTS; int64_t DTS;
bool isKeyFrame; bool isKeyFrame;
FrameInfo(int64_t ADTS, bool isAKeyFrame) : DTS(ADTS), isKeyFrame(isAKeyFrame) {}; LAVCFrameInfo(int64_t ADTS, bool isAKeyFrame) : DTS(ADTS), isKeyFrame(isAKeyFrame) {};
}; };
typedef std::vector<FrameInfo> FrameInfoVector; typedef std::vector<LAVCFrameInfo> LAVCFrameInfoVector;
class LAVCKeyFrames { class LAVCKeyFrames {
private: private:
@ -54,11 +54,11 @@ class LAVCKeyFrames {
int streamN; // Stream index int streamN; // Stream index
int numFrames; // number of frames in the video int numFrames; // number of frames in the video
protected: protected:
FrameInfoVector framesData; LAVCFrameInfoVector framesData;
public: public:
LAVCKeyFrames(const Aegisub::String filename); LAVCKeyFrames(const Aegisub::String filename);
~LAVCKeyFrames(); ~LAVCKeyFrames();
wxArrayInt GetKeyFrames(); wxArrayInt GetKeyFrames();
int GetNumFrames(); int GetNumFrames();
FrameInfoVector GetFrameData(); LAVCFrameInfoVector GetFrameData();
}; };

View file

@ -0,0 +1,259 @@
// Copyright (c) 2008, Karl Blomster
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// -----------------------------------------------------------------------------
//
// AEGISUB
//
// Website: http://aegisub.cellosoft.com
// Contact: mailto:zeratul@cellosoft.com
//
#ifdef WITH_FFMPEGSOURCE
#ifdef WIN32
#define EMULATE_INTTYPES
#endif /* WIN32 */
///////////
// Headers
#include <wx/wxprec.h>
#include "video_provider_ffmpegsource.h"
#include <ffms.h>
#include "vfr.h"
#include "video_context.h"
// #include "options.h" // for later use
///////////////
// Constructor
FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(Aegisub::String filename, double fps) {
// initialize ffmpegsource
FFMS_Init();
// clean up variables
VideoSource = NULL;
SWSContext = NULL;
BufferRGB = NULL;
KeyFramesLoaded = false;
FrameAllocated = false;
FrameNumber = -1;
MessageSize = sizeof(FFMSErrorMessage);
// and here we go
LoadVideo(filename, fps);
}
///////////////
// Destructor
FFmpegSourceVideoProvider::~FFmpegSourceVideoProvider() {
Close();
}
///////////////
// Open video
void FFmpegSourceVideoProvider::LoadVideo(Aegisub::String filename, double fps) {
// make sure we don't have anything messy lying around
Close();
wxString FileNameWX(filename.c_str(), wxConvUTF8);
// generate a name for the cache file
wxString CacheName(filename.c_str());
CacheName.append(_T(".ffindex"));
// initialize index object
FrameIndex *Index = FFMS_CreateFrameIndex();
// try to read index (these index functions all return 0 on success)
if (FFMS_ReadIndex(CacheName.char_str(), Index, FFMSErrorMessage, MessageSize)) {
// reading failed, create index
if (FFMS_MakeIndex(FileNameWX.char_str(), Index, 0, NULL, NULL, FFMSErrorMessage, MessageSize)) {
ErrorMsg.Printf(_T("FFmpegSource video provider: %s"), FFMSErrorMessage);
throw ErrorMsg;
}
// write it to disk
if (FFMS_WriteIndex(CacheName.char_str(), Index, FFMSErrorMessage, MessageSize)) {
ErrorMsg.Printf(_T("FFmpegSource video provider: %s"), FFMSErrorMessage);
throw ErrorMsg;
}
}
// TODO: make this user-configurable
int Threads = 1;
if (Threads < 1)
throw _T("FFmpegSource video provider: invalid decoding thread count");
// TODO: tie this to the option "ffmpeg allow unsafe seeking"
int SeekMode = 1;
// finally create the actual video source
VideoSource = FFMS_CreateVideoSource(FileNameWX.char_str(), -1, Index, "", Threads, SeekMode, FFMSErrorMessage, MessageSize);
if (VideoSource == NULL) {
ErrorMsg.Printf(_T("FFmpegSource video provider: %s"), FFMSErrorMessage);
throw ErrorMsg;
}
// load video properties
VideoInfo = FFMS_GetVideoProperties(VideoSource);
// get frame info data
// disabled until Myrsloik answers some questions
#if 0
FrameInfoVector FrameData = FFMS_GetTITrackIndex(Index, -1, FFMSErrorMessage, MessageSize);
if (FrameData == NULL) {
ErrorMsg.Printf(_T("FFmpegSource video provider: %s"), FFMSErrorMessage);
throw ErrorMsg;
}
FrameInfo CurFrameData;
// build list of keyframes
for (int CurFrameNum = 0; CurFrameNum < VideoInfo->NumFrames; CurFrameNum++) {
CurFrameData = FFMS_GetFrameInfo(FrameData, CurFrameNum, FFMSErrorMessage, MessageSize);
if (CurFrameData == NULL) {
ErrorMsg.Printf(_T("FFmpegSource video provider: %s"), FFMSErrorMessage);
throw ErrorMsg;
}
if (CurFrameData.KeyFrame)
KeyFramesList.Add(CurFrameNum);
}
KeyFramesLoaded = true;
#endif
// TODO: VFR handling
// we don't need this anymore
FFMS_DestroyFrameIndex(Index);
FrameNumber = 0;
}
///////////////
// Close video
void FFmpegSourceVideoProvider::Close() {
if (SWSContext)
sws_freeContext(SWSContext);
SWSContext = NULL;
if (VideoSource)
FFMS_DestroyVideoSource(VideoSource);
VideoSource = NULL;
if (FrameAllocated)
avpicture_free(&FrameRGB);
FrameAllocated = false;
if (BufferRGB)
delete BufferRGB;
KeyFramesLoaded = false;
KeyFramesList.clear();
FrameNumber = -1;
}
///////////////
// Get frame
const AegiVideoFrame FFmpegSourceVideoProvider::GetFrame(int n, int FormatType) {
const AVFrameLite *SrcFrame = FFMS_GetFrame(VideoSource, n, FFMSErrorMessage, MessageSize);
if (SrcFrame == NULL) {
ErrorMsg.Printf(_T("FFmpegSource video provider: %s"), FFMSErrorMessage);
throw ErrorMsg;
}
// set position
FrameNumber = n;
AVPicture *SrcPicture = reinterpret_cast<AVPicture *>(const_cast<AVFrameLite *>(SrcFrame));
// prepare stuff for conversion to RGB32
int w = VideoInfo->Width;
int h = VideoInfo->Height;
PixelFormat DstFormat;
switch (FormatType) {
case FORMAT_RGB32: DstFormat = PIX_FMT_RGB32; break;
case FORMAT_RGB24: DstFormat = PIX_FMT_RGB24; break;
// TODO: add YV12?
default: throw _T("FFmpegSource video provider: upstream provider requested unknown or unsupported pixel format");
}
if (!SWSContext) {
if (avpicture_alloc(&FrameRGB, DstFormat, w, h) != 0)
throw _T("FFmpegSource video provider: could not allocate output picture buffer");
FrameAllocated = true;
unsigned int DstSize = avpicture_get_size(DstFormat,w,h);
BufferRGB = new uint8_t[DstSize];
// initialize swscaler context
SWSContext = sws_getContext(w, h, VideoInfo->PixelFormat, w, h, DstFormat, SWS_BICUBIC, NULL, NULL, NULL);
if (SWSContext == NULL)
throw _T("FFmpegSource video provider: failed to initialize SWScale colorspace conversion");
}
avpicture_fill(&FrameRGB, BufferRGB, DstFormat, w, h);
// this is what we'll return eventually
AegiVideoFrame &DstFrame = CurFrame;
// set some properties
DstFrame.w = w;
DstFrame.h = h;
// only rgb supported for now
DstFrame.flipped = false;
DstFrame.invertChannels = true;
DstFrame.format = (VideoFrameFormat)FormatType;
// allocate destination frame
for (int i=0;i<4;i++) DstFrame.pitch[i] = FrameRGB.linesize[i];
DstFrame.Allocate();
// let swscale do the conversion to RGB and write directly to the output frame
sws_scale(SWSContext, SrcPicture->data, SrcPicture->linesize, 0, h, DstFrame.data, FrameRGB.linesize);
return DstFrame;
}
///////////////
// Utility functions
int FFmpegSourceVideoProvider::GetWidth() {
return VideoInfo->Width;
}
int FFmpegSourceVideoProvider::GetHeight() {
return VideoInfo->Height;
}
int FFmpegSourceVideoProvider::GetFrameCount() {
return VideoInfo->NumFrames;
}
int FFmpegSourceVideoProvider::GetPosition() {
return FrameNumber;
}
double FFmpegSourceVideoProvider::GetFPS() {
return double(VideoInfo->FPSNumerator) / double(VideoInfo->FPSDenominator);
}
#endif /* WITH_FFMPEGSOURCE */

View file

@ -0,0 +1,109 @@
// Copyright (c) 2008, Karl Blomster
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// -----------------------------------------------------------------------------
//
// AEGISUB
//
// Website: http://aegisub.cellosoft.com
// Contact: mailto:zeratul@cellosoft.com
//
///////////
// Headers
#include <wx/wxprec.h>
#ifdef WITH_FFMPEGSOURCE
#ifdef WIN32
#define EMULATE_INTTYPES
#endif /* WIN32 */
#include "include/aegisub/video_provider.h"
#include "include/aegisub/aegisub.h"
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
}
#include <vector>
#include <ffms.h>
///////////////////////
// FFmpegSource provider
class FFmpegSourceVideoProvider : public VideoProvider {
private:
VideoBase *VideoSource;
const VideoProperties *VideoInfo;
SwsContext *SWSContext;
int FrameNumber;
wxArrayInt KeyFramesList;
bool KeyFramesLoaded;
AVPicture FrameRGB;
bool FrameAllocated;
uint8_t *BufferRGB;
AegiVideoFrame CurFrame;
char FFMSErrorMessage[1024];
unsigned MessageSize;
wxString ErrorMsg;
void LoadVideo(Aegisub::String filename, double fps);
void Close();
protected:
public:
FFmpegSourceVideoProvider(Aegisub::String filename, double fps);
~FFmpegSourceVideoProvider();
const AegiVideoFrame GetFrame(int n, int formatType);
int GetPosition();
int GetFrameCount();
int GetWidth();
int GetHeight();
double GetFPS();
bool AreKeyFramesLoaded() { return KeyFramesLoaded; };
wxArrayInt GetKeyFrames() { return KeyFramesList; };
bool IsVFR() { return false; }; // FIXME: might want to talk to Myrsloik about this
FrameRate GetTrueFrameRate() { return FrameRate(); }; // FIXME: try to remember what this actually means
Aegisub::String GetDecoderName() { return L"FFmpegSource"; }
bool IsNativelyByFrames() { return true; }
int GetDesiredCacheSize() { return 8; }
};
///////////
// Factory
class FFmpegSourceVideoProviderFactory : public VideoProviderFactory {
public:
VideoProvider *CreateProvider(Aegisub::String video,double fps=0.0) { return new FFmpegSourceVideoProvider(video,fps); }
};
#endif /* WITH_FFMPEGSOURCE */

View file

@ -425,6 +425,7 @@ const AegiVideoFrame LAVCVideoProvider::GetFrame(int n,int formatType) {
bufferRGB = new uint8_t[dstSize]; bufferRGB = new uint8_t[dstSize];
sws_context = sws_getContext(w, h, srcFormat, w, h, dstFormat, SWS_PRINT_INFO | SWS_BICUBIC, NULL, NULL, NULL); sws_context = sws_getContext(w, h, srcFormat, w, h, dstFormat, SWS_PRINT_INFO | SWS_BICUBIC, NULL, NULL, NULL);
// sws_getContext() always returns NULL if context creation failed
if (sws_context == NULL) if (sws_context == NULL)
throw _T("ffmpeg video provider: failed to initialize SwScaler colorspace conversion"); throw _T("ffmpeg video provider: failed to initialize SwScaler colorspace conversion");
} }

View file

@ -44,9 +44,9 @@
#endif #endif
#include <vector> #include <vector>
extern "C" { extern "C" {
#include <ffmpeg/avcodec.h> #include <libavcodec/avcodec.h>
#include <ffmpeg/avformat.h> #include <libavformat/avformat.h>
#include <ffmpeg/swscale.h> #include <libswscale/swscale.h>
} }
#include "include/aegisub/video_provider.h" #include "include/aegisub/video_provider.h"
#include "include/aegisub/aegisub.h" #include "include/aegisub/aegisub.h"
@ -90,7 +90,7 @@ private:
int length; int length;
AegiVideoFrame curFrame; AegiVideoFrame curFrame;
bool validFrame; bool validFrame;
FrameInfoVector framesData; LAVCFrameInfoVector framesData;
uint8_t *buffer1; uint8_t *buffer1;
uint8_t *buffer2; uint8_t *buffer2;

View file

@ -49,6 +49,9 @@
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
#include "video_provider_lavc.h" #include "video_provider_lavc.h"
#endif #endif
#ifdef WITH_FFMPEGSOURCE
#include "video_provider_ffmpegsource.h"
#endif
#include "video_provider_dummy.h" #include "video_provider_dummy.h"
#include "video_provider_cache.h" #include "video_provider_cache.h"
@ -103,6 +106,9 @@ void VideoProviderFactoryManager::RegisterProviders() {
#ifdef WITH_FFMPEG #ifdef WITH_FFMPEG
RegisterFactory(new LAVCVideoProviderFactory(),_T("FFMPEG")); RegisterFactory(new LAVCVideoProviderFactory(),_T("FFMPEG"));
#endif #endif
#ifdef WITH_FFMPEGSOURCE
RegisterFactory(new FFmpegSourceVideoProviderFactory(),_T("FFmpegSource"));
#endif
} }

View file

@ -142,6 +142,11 @@
// #define WITH_STATIC_FFMPEG // #define WITH_STATIC_FFMPEG
// Enable ffmpegsource video and audio providers
// Requires: ffmpegsource version 2
#define WITH_FFMPEGSOURCE
// Enable Ruby support for Automation // Enable Ruby support for Automation
// Requires: Ruby 1.9 // Requires: Ruby 1.9
//#define WITH_RUBY //#define WITH_RUBY

View file

@ -138,6 +138,11 @@
// #define WITH_STATIC_FFMPEG // #define WITH_STATIC_FFMPEG
// Enable ffmpegsource video and audio providers
// Requires: ffmpegsource version 2
// #define WITH_FFMPEGSOURCE
// Enable Ruby support for Automation // Enable Ruby support for Automation
// Requires: Ruby 1.9 // Requires: Ruby 1.9
//#define WITH_RUBY //#define WITH_RUBY