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:
parent
8760c9a547
commit
a1d44cafc1
27 changed files with 207 additions and 485 deletions
|
@ -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)
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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) { }
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(); }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
||||
const AegiVideoFrame GetFrame(int n);
|
||||
std::shared_ptr<VideoFrame> GetFrame(int n);
|
||||
|
||||
int GetFrameCount() const { return VideoInfo->NumFrames; }
|
||||
int GetWidth() const { return Width; }
|
||||
int GetHeight() const { return Height; }
|
||||
double GetDAR() const { return DAR; }
|
||||
agi::vfr::Framerate GetFPS() const { return Timecodes; }
|
||||
|
||||
std::string GetColorSpace() const { return ColorSpace; }
|
||||
|
||||
/// @brief Gets a list of keyframes
|
||||
/// @return Returns a wxArrayInt of keyframes.
|
||||
std::vector<int> GetKeyFrames() const { return KeyFramesList; };
|
||||
std::string GetDecoderName() const { return "FFmpegSource"; }
|
||||
/// @brief Gets the desired cache behavior.
|
||||
/// @return Returns true.
|
||||
bool WantsCaching() const { return true; }
|
||||
};
|
||||
#endif /* WITH_FFMS2 */
|
||||
|
|
|
@ -64,7 +64,6 @@ YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(agi::fs::path const& filename)
|
|||
, w (0)
|
||||
, h (0)
|
||||
, num_frames(-1)
|
||||
, cur_fn(-1)
|
||||
, pixfmt(Y4M_PIXFMT_NONE)
|
||||
, imode(Y4M_ILACE_NOTSET)
|
||||
{
|
||||
|
@ -113,7 +112,6 @@ YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(agi::fs::path const& filename)
|
|||
num_frames = IndexFile();
|
||||
if (num_frames <= 0 || seek_table.empty())
|
||||
throw VideoOpenError("Unable to determine file length");
|
||||
cur_fn = 0;
|
||||
|
||||
fseeko(sf, 0, SEEK_SET);
|
||||
}
|
||||
|
@ -123,7 +121,6 @@ YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(agi::fs::path const& filename)
|
|||
}
|
||||
}
|
||||
|
||||
/// @brief Destructor
|
||||
YUV4MPEGVideoProvider::~YUV4MPEGVideoProvider() {
|
||||
fclose(sf);
|
||||
}
|
||||
|
@ -347,11 +344,8 @@ static FORCEINLINE int clamp(int x) {
|
|||
return x;
|
||||
}
|
||||
|
||||
/// @brief Gets a given frame
|
||||
/// @param n The frame number to return
|
||||
/// @return The video frame
|
||||
const AegiVideoFrame YUV4MPEGVideoProvider::GetFrame(int n) {
|
||||
cur_fn = mid(0, n, num_frames - 1);
|
||||
std::shared_ptr<VideoFrame> YUV4MPEGVideoProvider::GetFrame(int n) {
|
||||
n = mid(0, n, num_frames - 1);
|
||||
|
||||
int uv_width = w / 2;
|
||||
switch (pixfmt) {
|
||||
|
@ -380,17 +374,12 @@ const AegiVideoFrame YUV4MPEGVideoProvider::GetFrame(int n) {
|
|||
throw "YUV4MPEG video provider: GetFrame: failed to read chroma planes";
|
||||
}
|
||||
|
||||
AegiVideoFrame dst_frame;
|
||||
dst_frame.invertChannels = true;
|
||||
dst_frame.w = w;
|
||||
dst_frame.h = h;
|
||||
dst_frame.pitch = w * 4;
|
||||
dst_frame.Allocate();
|
||||
|
||||
const unsigned char *src_y = &planes[0][0];
|
||||
const unsigned char *src_u = &planes[1][0];
|
||||
const unsigned char *src_v = &planes[2][0];
|
||||
unsigned char *dst = dst_frame.data;
|
||||
std::vector<unsigned char> data;
|
||||
data.resize(w * h * 4);
|
||||
unsigned char *dst = &data[0];
|
||||
|
||||
for (int py = 0; py < h; ++py) {
|
||||
for (int px = 0; px < w / 2; ++px) {
|
||||
|
@ -413,5 +402,5 @@ const AegiVideoFrame YUV4MPEGVideoProvider::GetFrame(int n) {
|
|||
}
|
||||
}
|
||||
|
||||
return dst_frame;
|
||||
return std::make_shared<VideoFrame>(data.data(), w, h, w * 4, false);
|
||||
}
|
||||
|
|
|
@ -108,7 +108,6 @@ class YUV4MPEGVideoProvider : public VideoProvider {
|
|||
int frame_sz; /// size of each frame in bytes
|
||||
int luma_sz; /// size of the luma plane of each frame, in bytes
|
||||
int chroma_sz; /// size of one of the two chroma planes of each frame, in bytes
|
||||
int cur_fn; /// current frame number
|
||||
|
||||
Y4M_PixelFormat pixfmt; /// colorspace/pixel format
|
||||
Y4M_InterlacingMode imode; /// interlacing mode (for the entire stream)
|
||||
|
@ -133,7 +132,7 @@ public:
|
|||
YUV4MPEGVideoProvider(agi::fs::path const& filename);
|
||||
~YUV4MPEGVideoProvider();
|
||||
|
||||
const AegiVideoFrame GetFrame(int n);
|
||||
std::shared_ptr<VideoFrame> GetFrame(int n);
|
||||
|
||||
int GetFrameCount() const { return num_frames; }
|
||||
int GetWidth() const { return w; }
|
||||
|
|
Loading…
Reference in a new issue