Redesign VideoFrame

Eliminate the manual memory management and shuffle around where the
copies are made to eliminate the need for non-owning video frames.
This commit is contained in:
Thomas Goyne 2013-06-30 20:15:43 -07:00
parent 8760c9a547
commit a1d44cafc1
27 changed files with 207 additions and 485 deletions

View file

@ -294,7 +294,7 @@ struct video_frame_copy : public validator_video_loaded {
STR_HELP("Copy the currently displayed frame to the clipboard")
void operator()(agi::Context *c) {
SetClipboard(wxBitmap(c->videoController->GetFrame(c->videoController->GetFrameN())->GetImage(), 24));
SetClipboard(wxBitmap(GetImage(*c->videoController->GetFrame(c->videoController->GetFrameN())), 24));
}
};
@ -306,7 +306,7 @@ struct video_frame_copy_raw : public validator_video_loaded {
STR_HELP("Copy the currently displayed frame to the clipboard, without the subtitles")
void operator()(agi::Context *c) {
SetClipboard(wxBitmap(c->videoController->GetFrame(c->videoController->GetFrameN(), true)->GetImage(), 24));
SetClipboard(wxBitmap(GetImage(*c->videoController->GetFrame(c->videoController->GetFrameN(), true)), 24));
}
};
@ -493,7 +493,7 @@ static void save_snapshot(agi::Context *c, bool raw) {
path = str(boost::format("%s_%03d_%d.png") % basepath.string() % session_shot_count++ % c->videoController->GetFrameN());
} while (agi::fs::FileExists(path));
c->videoController->GetFrame(c->videoController->GetFrameN(), raw)->GetImage().SaveFile(to_wx(path), wxBITMAP_TYPE_PNG);
GetImage(*c->videoController->GetFrame(c->videoController->GetFrameN(), raw)).SaveFile(to_wx(path), wxBITMAP_TYPE_PNG);
}
/// Save the current video frame, with subtitles (if any)

View file

@ -37,14 +37,14 @@
#include "factory_manager.h"
class AssFile;
class AegiVideoFrame;
struct VideoFrame;
class SubtitlesProvider {
public:
virtual ~SubtitlesProvider() { };
virtual void LoadSubtitles(AssFile *subs)=0;
virtual void DrawSubtitles(AegiVideoFrame &dst,double time)=0;
virtual void DrawSubtitles(VideoFrame &dst,double time)=0;
};
class SubtitlesProviderFactory : public Factory1<SubtitlesProvider, std::string> {

View file

@ -37,16 +37,17 @@
#include <libaegisub/exception.h>
#include <libaegisub/vfr.h>
#include <memory>
#include <string>
class AegiVideoFrame;
struct VideoFrame;
class VideoProvider {
public:
virtual ~VideoProvider() {}
/// Override this method to actually get frames
virtual const AegiVideoFrame GetFrame(int n)=0;
virtual std::shared_ptr<VideoFrame> GetFrame(int n)=0;
// Override the following methods to get video information:
virtual int GetFrameCount() const=0; ///< Get total number of frames

View file

@ -40,6 +40,7 @@
#include "ass_style.h"
#include "subs_preview.h"
#include "include/aegisub/subtitles_provider.h"
#include "video_frame.h"
#include "video_provider_dummy.h"
#include <libaegisub/util.h>
@ -102,20 +103,18 @@ void SubtitlesPreview::SetColour(agi::Color col) {
void SubtitlesPreview::UpdateBitmap() {
if (!vid) return;
AegiVideoFrame frame;
frame.CopyFrom(vid->GetFrame(0));
auto frame = vid->GetFrame(0);
if (provider) {
try {
provider->LoadSubtitles(sub_file.get());
provider->DrawSubtitles(frame, 0.1);
provider->DrawSubtitles(*frame, 0.1);
}
catch (...) { }
}
// Convert frame to bitmap
*bmp = static_cast<wxBitmap>(frame.GetImage());
frame.Clear();
*bmp = static_cast<wxBitmap>(GetImage(*frame));
Refresh();
}

View file

@ -87,21 +87,21 @@ void CSRISubtitlesProvider::LoadSubtitles(AssFile *subs) {
instance = csri_open_file(renderer, tempfile.string().c_str(), nullptr);
}
void CSRISubtitlesProvider::DrawSubtitles(AegiVideoFrame &dst,double time) {
void CSRISubtitlesProvider::DrawSubtitles(VideoFrame &dst,double time) {
if (!instance) return;
csri_frame frame;
if (dst.flipped) {
frame.planes[0] = dst.data + (dst.h-1) * dst.pitch;
frame.strides[0] = -(signed)dst.pitch;
frame.planes[0] = dst.data.data() + (dst.height-1) * dst.width * 4;
frame.strides[0] = -(signed)dst.width * 4;
}
else {
frame.planes[0] = dst.data;
frame.strides[0] = dst.pitch;
frame.planes[0] = dst.data.data();
frame.strides[0] = dst.width * 4;
}
frame.pixfmt = CSRI_F_BGR_;
csri_fmt format = { frame.pixfmt, dst.w, dst.h };
csri_fmt format = { frame.pixfmt, dst.width, dst.height };
std::lock_guard<std::mutex> lock(csri_mutex);
if (!csri_request_fmt(instance, &format))

View file

@ -56,7 +56,7 @@ public:
~CSRISubtitlesProvider();
void LoadSubtitles(AssFile *subs);
void DrawSubtitles(AegiVideoFrame &dst, double time);
void DrawSubtitles(VideoFrame &dst, double time);
static std::vector<std::string> GetSubTypes();
};

View file

@ -144,8 +144,8 @@ void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) {
#define _b(c) (((c)>>8)&0xFF)
#define _a(c) ((c)&0xFF)
void LibassSubtitlesProvider::DrawSubtitles(AegiVideoFrame &frame,double time) {
ass_set_frame_size(ass_renderer, frame.w, frame.h);
void LibassSubtitlesProvider::DrawSubtitles(VideoFrame &frame,double time) {
ass_set_frame_size(ass_renderer, frame.width, frame.height);
ASS_Image* img = ass_render_frame(ass_renderer, ass_track, int(time * 1000), nullptr);
@ -154,7 +154,7 @@ void LibassSubtitlesProvider::DrawSubtitles(AegiVideoFrame &frame,double time) {
// This is repeated for all of them.
using namespace boost::gil;
auto dst = interleaved_view(frame.w, frame.h, (bgra8_pixel_t*)frame.data, frame.pitch);
auto dst = interleaved_view(frame.width, frame.height, (bgra8_pixel_t*)frame.data.data(), frame.width * 4);
if (frame.flipped)
dst = flipped_up_down_view(dst);

View file

@ -47,7 +47,7 @@ public:
~LibassSubtitlesProvider();
void LoadSubtitles(AssFile *subs);
void DrawSubtitles(AegiVideoFrame &dst, double time);
void DrawSubtitles(VideoFrame &dst, double time);
static void CacheFonts();
};

View file

@ -41,14 +41,11 @@ enum {
SUBS_FILE_ALREADY_LOADED = -2
};
std::shared_ptr<AegiVideoFrame> ThreadedFrameSource::ProcFrame(int frame_number, double time, bool raw) {
std::shared_ptr<AegiVideoFrame> frame(new AegiVideoFrame, [](AegiVideoFrame *frame) {
frame->Clear();
delete frame;
});
std::shared_ptr<VideoFrame> ThreadedFrameSource::ProcFrame(int frame_number, double time, bool raw) {
std::shared_ptr<VideoFrame> frame;
try {
frame->CopyFrom(video_provider->GetFrame(frame_number));
frame = video_provider->GetFrame(frame_number);
}
catch (VideoProviderError const& err) { throw VideoProviderErrorEvent(err); }
@ -193,8 +190,8 @@ void ThreadedFrameSource::ProcAsync(uint_fast32_t req_version) {
}
}
std::shared_ptr<AegiVideoFrame> ThreadedFrameSource::GetFrame(int frame, double time, bool raw) {
std::shared_ptr<AegiVideoFrame> ret;
std::shared_ptr<VideoFrame> ThreadedFrameSource::GetFrame(int frame, double time, bool raw) {
std::shared_ptr<VideoFrame> ret;
worker->Sync([&]{
ret = ProcFrame(frame, time, raw);
});

View file

@ -29,12 +29,12 @@
#include <wx/event.h>
class AegiVideoFrame;
class AssEntry;
class AssFile;
class SubtitlesProvider;
class VideoProvider;
class VideoProviderError;
struct VideoFrame;
namespace agi { namespace dispatch { class Queue; } }
/// @class ThreadedFrameSource
@ -61,7 +61,7 @@ class ThreadedFrameSource {
/// currently loaded file is out of date.
int single_frame;
std::shared_ptr<AegiVideoFrame> ProcFrame(int frame, double time, bool raw = false);
std::shared_ptr<VideoFrame> ProcFrame(int frame, double time, bool raw = false);
/// Produce a frame if req_version is still the current version
void ProcAsync(uint_fast32_t req_version);
@ -98,7 +98,7 @@ public:
/// @brief frame Frame number
/// @brief time Exact start time of the frame in seconds
/// @brief raw Get raw frame without subtitles
std::shared_ptr<AegiVideoFrame> GetFrame(int frame, double time, bool raw = false);
std::shared_ptr<VideoFrame> GetFrame(int frame, double time, bool raw = false);
/// Get a reference to the video provider this is using
VideoProvider *GetVideoProvider() const { return video_provider.get(); }
@ -113,11 +113,11 @@ public:
/// Event which signals that a requested frame is ready
struct FrameReadyEvent : public wxEvent {
/// Frame which is ready
std::shared_ptr<AegiVideoFrame> frame;
std::shared_ptr<VideoFrame> frame;
/// Time which was used for subtitle rendering
double time;
wxEvent *Clone() const { return new FrameReadyEvent(*this); };
FrameReadyEvent(std::shared_ptr<AegiVideoFrame> frame, double time)
FrameReadyEvent(std::shared_ptr<VideoFrame> frame, double time)
: frame(frame), time(time) { }
};

View file

@ -289,7 +289,7 @@ void VideoContext::GetFrameAsync(int n) {
provider->RequestFrame(n, TimeAtFrame(n));
}
std::shared_ptr<AegiVideoFrame> VideoContext::GetFrame(int n, bool raw) {
std::shared_ptr<VideoFrame> VideoContext::GetFrame(int n, bool raw) {
return provider->GetFrame(n, TimeAtFrame(n), raw);
}

View file

@ -45,10 +45,10 @@
#include <wx/timer.h>
class AegiVideoFrame;
class AssEntry;
struct SubtitlesProviderErrorEvent;
class ThreadedFrameSource;
struct VideoFrame;
class VideoProvider;
struct VideoProviderErrorEvent;
@ -176,7 +176,7 @@ public:
/// @param n Frame number to get
/// @param raw If true, subtitles are not rendered on the frame
/// @return The requested frame
std::shared_ptr<AegiVideoFrame> GetFrame(int n, bool raw = false);
std::shared_ptr<VideoFrame> GetFrame(int n, bool raw = false);
/// Asynchronously get a video frame, triggering a EVT_FRAME_READY event when it's ready
/// @param n Frame number to get

View file

@ -42,7 +42,6 @@
#include <wx/glcanvas.h>
// Prototypes
class AegiVideoFrame;
struct FrameReadyEvent;
class VideoContext;
class VideoOutGL;
@ -50,6 +49,7 @@ class VisualToolBase;
class wxComboBox;
class wxTextCtrl;
class wxToolBar;
struct VideoFrame;
namespace agi {
struct Context;
@ -104,7 +104,7 @@ class VideoDisplay : public wxGLCanvas {
bool freeSize;
/// Frame which will replace the currently visible frame on the next render
std::shared_ptr<AegiVideoFrame> pending_frame;
std::shared_ptr<VideoFrame> pending_frame;
/// @brief Draw an overscan mask
/// @param horizontal_percent The percent of the video reserved horizontally

View file

@ -1,146 +1,43 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro
// All rights reserved.
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// * 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.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
/// @file video_frame.cpp
/// @brief Wrapper around a frame of video data
/// @ingroup video
///
#include "config.h"
#include "utils.h"
#include "video_frame.h"
void AegiVideoFrame::Reset() {
// Zero variables
data = 0;
pitch = 0;
memSize = 0;
w = 0;
h = 0;
#include <boost/gil/gil_all.hpp>
#include <wx/image.h>
// Set properties
flipped = false;
invertChannels = true;
ownMem = true;
VideoFrame::VideoFrame(const unsigned char *data, size_t width, size_t height, size_t pitch, bool flipped)
: data(data, data + width * height * 4)
, width(width)
, height(height)
, pitch(pitch)
, flipped(flipped)
{
}
AegiVideoFrame::AegiVideoFrame() {
Reset();
}
wxImage GetImage(VideoFrame const& frame) {
using namespace boost::gil;
/// @brief Create a solid black frame of the request size and format
/// @param width
/// @param height
AegiVideoFrame::AegiVideoFrame(unsigned int width, unsigned int height) {
assert(width > 0 && width < 10000);
assert(height > 0 && height < 10000);
Reset();
// Set format
w = width;
h = height;
pitch = w * GetBpp();
Allocate();
memset(data, 0, pitch * height);
}
void AegiVideoFrame::Allocate() {
assert(pitch > 0 && pitch < 10000);
assert(w > 0 && w < 10000);
assert(h > 0 && h < 10000);
unsigned int size = pitch * h;
// Reallocate, if necessary
if (memSize != size || !ownMem) {
if (ownMem) {
delete[] data;
}
data = new unsigned char[size];
memSize = size;
}
ownMem = true;
}
void AegiVideoFrame::Clear() {
if (ownMem) delete[] data;
Reset();
}
void AegiVideoFrame::CopyFrom(const AegiVideoFrame &source) {
w = source.w;
h = source.h;
pitch = source.pitch;
Allocate();
memcpy(data, source.data, memSize);
flipped = source.flipped;
invertChannels = source.invertChannels;
}
void AegiVideoFrame::SetTo(const unsigned char *source, unsigned int width, unsigned int height, unsigned int pitch) {
assert(pitch > 0 && pitch < 10000);
assert(width > 0 && width < 10000);
assert(height > 0 && height < 10000);
ownMem = false;
w = width;
h = height;
// Note that despite this cast, the contents of data should still never be modified
data = const_cast<unsigned char*>(source);
this->pitch = pitch;
}
wxImage AegiVideoFrame::GetImage() const {
unsigned char *buf = (unsigned char*)malloc(w*h*3);
if (!buf) throw std::bad_alloc();
int Bpp = GetBpp();
// Convert
for (unsigned int y=0;y<h;y++) {
unsigned char *dst = buf + y*w*3;
const unsigned char *src;
if (flipped) src = data + (h-y-1)*pitch;
else src = data + y*pitch;
for (unsigned int x=0;x<w;x++) {
*dst++ = *(src+2);
*dst++ = *(src+1);
*dst++ = *(src);
src += Bpp;
}
}
wxImage img(w,h);
img.SetData(buf);
wxImage img(frame.width, frame.height);
auto src = interleaved_view(frame.width, frame.height, (bgra8_pixel_t*)frame.data.data(), frame.pitch);
auto dst = interleaved_view(frame.width, frame.height, (rgb8_pixel_t*)img.GetData(), 3 * frame.width);
if (frame.flipped)
src = flipped_up_down_view(src);
copy_and_convert_pixels(src, dst);
return img;
}

View file

@ -1,94 +1,31 @@
// Copyright (c) 2007, Rodrigo Braz Monteiro
// All rights reserved.
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// * 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.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
/// @file video_frame.h
/// @see video_frame.cpp
/// @ingroup video
///
#include <vector>
#pragma once
class wxImage;
#include <wx/image.h>
class AegiVideoFrame {
/// Whether the object owns its buffer. If this is false, **data should never be modified
bool ownMem;
/// @brief Reset values to the defaults
///
/// Note that this function DOES NOT deallocate memory.
/// Use Clear() for that
void Reset();
public:
/// @brief Allocate memory if needed
void Allocate();
/// The size in bytes of the frame buffer
unsigned int memSize;
/// Pointer to the data planes
unsigned char *data;
/// Width in pixels
unsigned int w;
/// Height in pixels
unsigned int h;
// Pitch, that is, the number of bytes used by each row.
unsigned int pitch;
/// First row is actually the bottom one
struct VideoFrame {
std::vector<unsigned char> data;
size_t width;
size_t height;
size_t pitch;
bool flipped;
/// Swap Red and Blue channels (controls RGB versus BGR ordering etc)
bool invertChannels;
AegiVideoFrame();
AegiVideoFrame(unsigned int width, unsigned int height);
// @brief Clear this frame, freeing its memory if nessesary
void Clear();
/// @brief Copy from an AegiVideoFrame
/// @param source The frame to copy from
void CopyFrom(const AegiVideoFrame &source);
/// @brief Set the frame to an externally allocated block of memory
/// @param source Target frame data
/// @param width The frame width in pixels
/// @param height The frame height in pixels
/// @param pitch The frame's pitch
/// @param format The frame's format
void SetTo(const unsigned char *source, unsigned int width, unsigned int height, unsigned int pitch);
/// @brief Get this frame as a wxImage
wxImage GetImage() const;
int GetBpp() const { return 4; };
VideoFrame(const unsigned char *data, size_t width, size_t height, size_t pitch, bool fipped);
};
wxImage GetImage(VideoFrame const& frame);

View file

@ -265,19 +265,18 @@ void VideoOutGL::InitTextures(int width, int height, GLenum format, int bpp, boo
}
}
void VideoOutGL::UploadFrameData(const AegiVideoFrame& frame) {
if (frame.h == 0 || frame.w == 0) return;
void VideoOutGL::UploadFrameData(VideoFrame const& frame) {
if (frame.height == 0 || frame.width == 0) return;
GLuint format = frame.invertChannels ? GL_BGRA_EXT : GL_RGBA;
InitTextures(frame.w, frame.h, format, frame.GetBpp(), frame.flipped);
InitTextures(frame.width, frame.height, GL_BGRA_EXT, 4, frame.flipped);
// Set the row length, needed to be able to upload partial rows
CHECK_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, frame.pitch / frame.GetBpp()));
CHECK_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, frame.pitch / 4));
for (auto& ti : textureList) {
CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ti.sourceW,
ti.sourceH, format, GL_UNSIGNED_BYTE, frame.data + ti.dataOffset));
ti.sourceH, GL_BGRA_EXT, GL_UNSIGNED_BYTE, &frame.data[ti.dataOffset]));
}
CHECK_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));

View file

@ -1,29 +1,16 @@
// Copyright (c) 2009, Thomas Goyne
// All rights reserved.
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// * 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.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
@ -36,12 +23,11 @@
#include <vector>
class AegiVideoFrame;
struct VideoFrame;
/// @class VideoOutGL
/// @brief OpenGL based video renderer
class VideoOutGL {
private:
struct TextureInfo;
/// The maximum texture size supported by the user's graphics card
@ -80,7 +66,7 @@ private:
public:
/// @brief Set the frame to be displayed when Render() is called
/// @param frame The frame to be displayed
void UploadFrameData(const AegiVideoFrame& frame);
void UploadFrameData(VideoFrame const& frame);
/// @brief Render a frame
/// @param x Bottom left x coordinate

View file

@ -38,6 +38,7 @@
#include "video_provider_avs.h"
#include "options.h"
#include "video_frame.h"
#include <libaegisub/access.h>
#include <libaegisub/charset_conv.h>
@ -53,13 +54,9 @@
#endif
AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename)
: last_fnum(-1)
{
agi::acs::CheckFileRead(filename);
iframe.flipped = true;
iframe.invertChannels = true;
std::lock_guard<std::mutex> lock(avs.GetMutex());
#ifdef _WIN32
@ -121,12 +118,12 @@ AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename)
for (size_t i = 0; i < avis.dwLength; i++) {
if (AVIStreamIsKeyFrame(ppavi, i))
KeyFrames.push_back(i);
keyframes.push_back(i);
}
// If every frame is a keyframe then just discard the keyframe data as it's useless
if (KeyFrames.size() == (size_t)avis.dwLength)
KeyFrames.clear();
if (keyframes.size() == (size_t)avis.dwLength)
keyframes.clear();
// Clean up
stream_release:
@ -170,10 +167,6 @@ file_exit:
}
}
AvisynthVideoProvider::~AvisynthVideoProvider() {
iframe.Clear();
}
AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
IScriptEnvironment *env = avs.GetEnv();
char *videoFilename = env->SaveString(agi::fs::ShortName(filename).c_str());
@ -181,7 +174,7 @@ AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
// Avisynth file, just import it
if (agi::fs::HasExtension(filename, "avs")) {
LOG_I("avisynth/video") << "Opening .avs file with Import";
decoderName = "Avisynth/Import";
decoder_name = "Avisynth/Import";
return env->Invoke("Import", videoFilename);
}
@ -191,7 +184,7 @@ AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
try {
const char *argnames[2] = { 0, "audio" };
AVSValue args[2] = { videoFilename, false };
decoderName = "Avisynth/AviSource";
decoder_name = "Avisynth/AviSource";
return env->Invoke("AviSource", AVSValue(args,2), argnames);
}
// On Failure, fallback to DSS
@ -205,7 +198,7 @@ AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("Mpeg2Dec3_Mpeg2Source")) {
LOG_I("avisynth/video") << "Opening .d2v file with Mpeg2Dec3_Mpeg2Source";
auto script = env->Invoke("Mpeg2Dec3_Mpeg2Source", videoFilename);
decoderName = "Avisynth/Mpeg2Dec3_Mpeg2Source";
decoder_name = "Avisynth/Mpeg2Dec3_Mpeg2Source";
//if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this
if (env->FunctionExists("SetPlanarLegacyAlignment")) {
@ -218,7 +211,7 @@ AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
// If that fails, try opening it with DGDecode
if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("DGDecode_Mpeg2Source")) {
LOG_I("avisynth/video") << "Opening .d2v file with DGDecode_Mpeg2Source";
decoderName = "DGDecode_Mpeg2Source";
decoder_name = "DGDecode_Mpeg2Source";
return env->Invoke("Avisynth/Mpeg2Source", videoFilename);
//note that DGDecode will also have issues like if the version is too
@ -228,7 +221,7 @@ AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("Mpeg2Source")) {
LOG_I("avisynth/video") << "Opening .d2v file with other Mpeg2Source";
AVSValue script = env->Invoke("Mpeg2Source", videoFilename);
decoderName = "Avisynth/Mpeg2Source";
decoder_name = "Avisynth/Mpeg2Source";
//if avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this
if (env->FunctionExists("SetPlanarLegacyAlignment"))
@ -247,7 +240,7 @@ AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
// If DSS2 loaded properly, try using it
if (env->FunctionExists("dss2")) {
LOG_I("avisynth/video") << "Opening file with DSS2";
decoderName = "Avisynth/DSS2";
decoder_name = "Avisynth/DSS2";
return env->Invoke("DSS2", videoFilename);
}
@ -261,7 +254,7 @@ AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
if (env->FunctionExists("DirectShowSource")) {
const char *argnames[3] = { 0, "video", "audio" };
AVSValue args[3] = { videoFilename, true, false };
decoderName = "Avisynth/DirectShowSource";
decoder_name = "Avisynth/DirectShowSource";
warning = "Warning! The file is being opened using Avisynth's DirectShowSource, which has unreliable seeking. Frame numbers might not match the real number. PROCEED AT YOUR OWN RISK!";
LOG_I("avisynth/video") << "Opening file with DirectShowSource";
return env->Invoke("DirectShowSource", AVSValue(args,3), argnames);
@ -272,21 +265,10 @@ AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
throw VideoNotSupported("No function suitable for opening the video found");
}
const AegiVideoFrame AvisynthVideoProvider::GetFrame(int n) {
if (n == last_fnum) return iframe;
std::shared_ptr<VideoFrame> AvisynthVideoProvider::GetFrame(int n) {
std::lock_guard<std::mutex> lock(avs.GetMutex());
auto frame = RGB32Video->GetFrame(n, avs.GetEnv());
iframe.pitch = frame->GetPitch();
iframe.w = frame->GetRowSize() / (vi.BitsPerPixel() / 8);
iframe.h = frame->GetHeight();
iframe.Allocate();
memcpy(iframe.data, frame->GetReadPtr(), iframe.pitch * iframe.h);
last_fnum = n;
return iframe;
return std::make_shared<VideoFrame>(frame->GetReadPtr(), frame->GetRowSize() / 4, frame->GetHeight(), frame->GetPitch(), true);
}
#endif // HAVE_AVISYNTH

View file

@ -35,40 +35,37 @@
#ifdef WITH_AVISYNTH
#include "include/aegisub/video_provider.h"
#define VideoFrame AVSVideoFrame
#include "avisynth.h"
#undef VideoFrame
#include "avisynth_wrap.h"
#include "video_frame.h"
class AvisynthVideoProvider: public VideoProvider {
AviSynthWrapper avs;
AegiVideoFrame iframe;
std::string decoderName;
std::string decoder_name;
agi::vfr::Framerate fps;
std::vector<int> KeyFrames;
std::vector<int> keyframes;
std::string warning;
std::string colorspace;
PClip RGB32Video;
VideoInfo vi;
int last_fnum;
AVSValue Open(agi::fs::path const& filename);
public:
AvisynthVideoProvider(agi::fs::path const& filename);
~AvisynthVideoProvider();
const AegiVideoFrame GetFrame(int n);
std::shared_ptr<VideoFrame> GetFrame(int n);
int GetFrameCount() const { return vi.num_frames; };
agi::vfr::Framerate GetFPS() const { return fps; };
int GetWidth() const { return vi.width; };
int GetHeight() const { return vi.height; };
int GetFrameCount() const { return vi.num_frames; }
agi::vfr::Framerate GetFPS() const { return fps; }
int GetWidth() const { return vi.width; }
int GetHeight() const { return vi.height; }
double GetDAR() const { return 0; }
std::vector<int> GetKeyFrames() const { return KeyFrames; };
std::vector<int> GetKeyFrames() const { return keyframes; }
std::string GetWarning() const { return warning; }
std::string GetDecoderName() const { return decoderName; }
std::string GetDecoderName() const { return decoder_name; }
std::string GetColorSpace() const { return colorspace; }
};
#endif

View file

@ -1,37 +1,19 @@
// Copyright (c) 2008, Rodrigo Braz Monteiro
// All rights reserved.
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// * 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.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
/// @file video_provider_cache.cpp
/// @brief Aggregate video provider caching previously requested frames
/// @ingroup video_input
///
#include "config.h"
#include "video_provider_cache.h"
@ -40,51 +22,44 @@
#include "video_frame.h"
#include <algorithm>
#include <functional>
/// A video frame and its frame number
struct CachedFrame : public AegiVideoFrame {
struct CachedFrame : public VideoFrame {
int frame_number;
CachedFrame(int frame_number, VideoFrame const& frame)
: VideoFrame(frame.data.data(), frame.width, frame.height, frame.pitch, frame.flipped)
, frame_number(frame_number)
{
}
};
VideoProviderCache::VideoProviderCache(std::unique_ptr<VideoProvider>&& parent)
VideoProviderCache::VideoProviderCache(std::unique_ptr<VideoProvider> parent)
: master(std::move(parent))
, max_cache_size(OPT_GET("Provider/Video/Cache/Size")->GetInt() << 20) // convert MB to bytes
{
}
VideoProviderCache::~VideoProviderCache() {
for_each(cache.begin(), cache.end(), std::mem_fn(&AegiVideoFrame::Clear));
}
const AegiVideoFrame VideoProviderCache::GetFrame(int n) {
std::shared_ptr<VideoFrame> VideoProviderCache::GetFrame(int n) {
size_t total_size = 0;
// See if frame is cached
for (auto cur = cache.begin(); cur != cache.end(); ++cur) {
if (cur->frame_number == n) {
cache.push_front(*cur);
cache.erase(cur);
return cache.front();
cache.splice(cache.begin(), cache, cur); // Move to front
return std::make_shared<VideoFrame>(cache.front());
}
total_size += cur->memSize;
total_size += cur->data.size();
}
// Not cached, retrieve it
const AegiVideoFrame frame = master->GetFrame(n);
auto frame = master->GetFrame(n);
// Cache full, use oldest frame
if (total_size >= max_cache_size) {
cache.push_front(cache.back());
if (total_size >= max_cache_size)
cache.pop_back();
}
// Cache not full, insert new one
else
cache.push_front(CachedFrame());
cache.emplace_front(n, *frame);
// Cache
cache.front().frame_number = n;
cache.front().CopyFrom(frame);
return cache.front();
return frame;
}

View file

@ -1,37 +1,19 @@
// Copyright (c) 2008, Rodrigo Braz Monteiro, Fredrik Mellbin
// All rights reserved.
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// * 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.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
//
// Aegisub Project http://www.aegisub.org/
/// @file video_provider_cache.h
/// @see video_provider_cache.cpp
/// @ingroup video_input
///
#include <boost/container/list.hpp>
#include "include/aegisub/video_provider.h"
@ -54,10 +36,10 @@ class VideoProviderCache : public VideoProvider {
boost::container::list<CachedFrame> cache;
public:
VideoProviderCache(std::unique_ptr<VideoProvider>&& master);
VideoProviderCache(std::unique_ptr<VideoProvider> master);
~VideoProviderCache();
const AegiVideoFrame GetFrame(int n);
std::shared_ptr<VideoFrame> GetFrame(int n);
int GetFrameCount() const { return master->GetFrameCount(); }
int GetWidth() const { return master->GetWidth(); }

View file

@ -37,6 +37,7 @@
#include "video_provider_dummy.h"
#include "colorspace.h"
#include "video_frame.h"
#include <libaegisub/color.h>
#include <libaegisub/fs.h>
@ -46,18 +47,21 @@
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/format.hpp>
#include <boost/gil/gil_all.hpp>
void DummyVideoProvider::Create(double fps, int frames, int width, int height, unsigned char red, unsigned char green, unsigned char blue, bool pattern) {
this->framecount = frames;
this->fps = fps;
this->width = width;
this->height = height;
this->frame = AegiVideoFrame(width, height);
data.resize(width * height * 4);
unsigned char *dst = frame.data;
unsigned char colors[2][4] = {
{ blue, green, red, 0 },
{ 0, 0, 0, 0 }
using namespace boost::gil;
auto dst = interleaved_view(width, height, (bgra8_pixel_t*)data.data(), 4 * width);
bgra8_pixel_t colors[2] = {
bgra8_pixel_t(blue, green, red, 0),
bgra8_pixel_t(blue, green, red, 0)
};
if (pattern) {
@ -66,20 +70,17 @@ void DummyVideoProvider::Create(double fps, int frames, int width, int height, u
rgb_to_hsl(red, blue, green, &h, &s, &l);
l += 24;
if (l < 24) l -= 48;
hsl_to_rgb(h, s, l, &colors[1][2], &colors[1][1], &colors[1][0]);
hsl_to_rgb(h, s, l, &red, &blue, &green);
colors[1] = bgra8_pixel_t(blue, green, red, 0);
// Divide into a 8x8 grid and use light colours when row % 2 != col % 2
int ppitch = frame.pitch / frame.GetBpp();
for (unsigned int y = 0; y < frame.h; ++y) {
for (int x = 0; x < ppitch; ++x) {
memcpy(dst, colors[((y / 8) & 1) != ((x / 8) & 1)], 4);
dst += 4;
}
}
auto out = dst.begin();
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x)
*out++ = colors[((y / 8) & 1) != ((x / 8) & 1)];
}
else {
for (int i = frame.pitch * frame.h / frame.GetBpp() - 1; i >= 0; --i)
memcpy(dst + i * 4, colors[0], 4);
fill_pixels(dst, colors[0]);
}
}
@ -115,10 +116,10 @@ DummyVideoProvider::DummyVideoProvider(double fps, int frames, int width, int he
Create(fps, frames, width, height, colour.r, colour.g, colour.b, pattern);
}
DummyVideoProvider::~DummyVideoProvider() {
frame.Clear();
}
std::string DummyVideoProvider::MakeFilename(double fps, int frames, int width, int height, agi::Color colour, bool pattern) {
return str(boost::format("?dummy:%f:%d:%d:%d:%d:%d:%d:%s") % fps % frames % width % height % (int)colour.r % (int)colour.g % (int)colour.b % (pattern ? "c" : ""));
}
std::shared_ptr<VideoFrame> DummyVideoProvider::GetFrame(int) {
return std::make_shared<VideoFrame>(data.data(), width, height, width * 4, false);
}

View file

@ -33,7 +33,6 @@
///
#include "include/aegisub/video_provider.h"
#include "video_frame.h"
namespace agi { struct Color; }
@ -48,8 +47,8 @@ class DummyVideoProvider : public VideoProvider {
int width; ///< Width in pixels
int height; ///< Height in pixels
/// The single image which is returned for all frames
AegiVideoFrame frame;
/// The data for the image returned for all frames
std::vector<unsigned char> data;
/// Create the dummy frame from the given parameters
/// @param fps Frame rate of the dummy video
@ -75,14 +74,12 @@ public:
/// @param pattern Use a checkerboard pattern rather than a solid colour
DummyVideoProvider(double fps, int frames, int width, int height, agi::Color colour, bool pattern);
/// Destructor
~DummyVideoProvider();
/// Make a fake filename which when passed to the constructor taking a
/// string will result in a video with the given parameters
static std::string MakeFilename(double fps, int frames, int width, int height, agi::Color colour, bool pattern);
const AegiVideoFrame GetFrame(int n) { return frame; }
std::shared_ptr<VideoFrame> GetFrame(int n);
int GetFrameCount() const { return framecount; }
int GetWidth() const { return width; }
int GetHeight() const { return height; }

View file

@ -41,6 +41,7 @@
#include "options.h"
#include "utils.h"
#include "video_context.h"
#include "video_frame.h"
#include <libaegisub/fs.h>
@ -52,7 +53,6 @@ FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(agi::fs::path const& filena
, VideoInfo(nullptr)
, Width(-1)
, Height(-1)
, FrameNumber(-1)
{
ErrInfo.Buffer = FFMSErrMsg;
ErrInfo.BufferSize = sizeof(FFMSErrMsg);
@ -238,20 +238,16 @@ void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename) {
Timecodes = 25.0;
else
Timecodes = agi::vfr::Framerate(TimecodesVector);
FrameNumber = 0;
}
const AegiVideoFrame FFmpegSourceVideoProvider::GetFrame(int n) {
FrameNumber = mid(0, n, GetFrameCount() - 1);
std::shared_ptr<VideoFrame> FFmpegSourceVideoProvider::GetFrame(int n) {
n = mid(0, n, GetFrameCount() - 1);
// decode frame
const FFMS_Frame *SrcFrame = FFMS_GetFrame(VideoSource, FrameNumber, &ErrInfo);
if (!SrcFrame)
auto frame = FFMS_GetFrame(VideoSource, n, &ErrInfo);
if (!frame)
throw VideoDecodeError(std::string("Failed to retrieve frame: ") + ErrInfo.Buffer);
CurFrame.SetTo(SrcFrame->Data[0], Width, Height, SrcFrame->Linesize[0]);
return CurFrame;
return std::make_shared<VideoFrame>(frame->Data[0], Width, Height, frame->Linesize[0], false);
}
#endif /* WITH_FFMS2 */

View file

@ -33,11 +33,8 @@
///
#ifdef WITH_FFMS2
#include <vector>
#include "ffmpegsource_common.h"
#include "include/aegisub/video_provider.h"
#include "video_frame.h"
/// @class FFmpegSourceVideoProvider
/// @brief Implements video loading through the FFMS library.
@ -49,13 +46,10 @@ class FFmpegSourceVideoProvider : public VideoProvider, FFmpegSourceProvider {
int Width; ///< width in pixels
int Height; ///< height in pixels
double DAR; ///< display aspect ratio
int FrameNumber; ///< current framenumber
std::vector<int> KeyFramesList; ///< list of keyframes
agi::vfr::Framerate Timecodes; ///< vfr object
std::string ColorSpace; ///< Colorspace name
AegiVideoFrame CurFrame; ///< current video frame
char FFMSErrMsg[1024]; ///< FFMS error message
FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
@ -64,22 +58,16 @@ class FFmpegSourceVideoProvider : public VideoProvider, FFmpegSourceProvider {
public:
FFmpegSourceVideoProvider(agi::fs::path const& filename);