forked from mia/Aegisub
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")
|
STR_HELP("Copy the currently displayed frame to the clipboard")
|
||||||
|
|
||||||
void operator()(agi::Context *c) {
|
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")
|
STR_HELP("Copy the currently displayed frame to the clipboard, without the subtitles")
|
||||||
|
|
||||||
void operator()(agi::Context *c) {
|
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());
|
path = str(boost::format("%s_%03d_%d.png") % basepath.string() % session_shot_count++ % c->videoController->GetFrameN());
|
||||||
} while (agi::fs::FileExists(path));
|
} 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)
|
/// Save the current video frame, with subtitles (if any)
|
||||||
|
|
|
@ -37,14 +37,14 @@
|
||||||
#include "factory_manager.h"
|
#include "factory_manager.h"
|
||||||
|
|
||||||
class AssFile;
|
class AssFile;
|
||||||
class AegiVideoFrame;
|
struct VideoFrame;
|
||||||
|
|
||||||
class SubtitlesProvider {
|
class SubtitlesProvider {
|
||||||
public:
|
public:
|
||||||
virtual ~SubtitlesProvider() { };
|
virtual ~SubtitlesProvider() { };
|
||||||
|
|
||||||
virtual void LoadSubtitles(AssFile *subs)=0;
|
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> {
|
class SubtitlesProviderFactory : public Factory1<SubtitlesProvider, std::string> {
|
||||||
|
|
|
@ -37,16 +37,17 @@
|
||||||
#include <libaegisub/exception.h>
|
#include <libaegisub/exception.h>
|
||||||
#include <libaegisub/vfr.h>
|
#include <libaegisub/vfr.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class AegiVideoFrame;
|
struct VideoFrame;
|
||||||
|
|
||||||
class VideoProvider {
|
class VideoProvider {
|
||||||
public:
|
public:
|
||||||
virtual ~VideoProvider() {}
|
virtual ~VideoProvider() {}
|
||||||
|
|
||||||
/// Override this method to actually get frames
|
/// 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:
|
// Override the following methods to get video information:
|
||||||
virtual int GetFrameCount() const=0; ///< Get total number of frames
|
virtual int GetFrameCount() const=0; ///< Get total number of frames
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
#include "ass_style.h"
|
#include "ass_style.h"
|
||||||
#include "subs_preview.h"
|
#include "subs_preview.h"
|
||||||
#include "include/aegisub/subtitles_provider.h"
|
#include "include/aegisub/subtitles_provider.h"
|
||||||
|
#include "video_frame.h"
|
||||||
#include "video_provider_dummy.h"
|
#include "video_provider_dummy.h"
|
||||||
|
|
||||||
#include <libaegisub/util.h>
|
#include <libaegisub/util.h>
|
||||||
|
@ -102,20 +103,18 @@ void SubtitlesPreview::SetColour(agi::Color col) {
|
||||||
void SubtitlesPreview::UpdateBitmap() {
|
void SubtitlesPreview::UpdateBitmap() {
|
||||||
if (!vid) return;
|
if (!vid) return;
|
||||||
|
|
||||||
AegiVideoFrame frame;
|
auto frame = vid->GetFrame(0);
|
||||||
frame.CopyFrom(vid->GetFrame(0));
|
|
||||||
|
|
||||||
if (provider) {
|
if (provider) {
|
||||||
try {
|
try {
|
||||||
provider->LoadSubtitles(sub_file.get());
|
provider->LoadSubtitles(sub_file.get());
|
||||||
provider->DrawSubtitles(frame, 0.1);
|
provider->DrawSubtitles(*frame, 0.1);
|
||||||
}
|
}
|
||||||
catch (...) { }
|
catch (...) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert frame to bitmap
|
// Convert frame to bitmap
|
||||||
*bmp = static_cast<wxBitmap>(frame.GetImage());
|
*bmp = static_cast<wxBitmap>(GetImage(*frame));
|
||||||
frame.Clear();
|
|
||||||
Refresh();
|
Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -87,21 +87,21 @@ void CSRISubtitlesProvider::LoadSubtitles(AssFile *subs) {
|
||||||
instance = csri_open_file(renderer, tempfile.string().c_str(), nullptr);
|
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;
|
if (!instance) return;
|
||||||
|
|
||||||
csri_frame frame;
|
csri_frame frame;
|
||||||
if (dst.flipped) {
|
if (dst.flipped) {
|
||||||
frame.planes[0] = dst.data + (dst.h-1) * dst.pitch;
|
frame.planes[0] = dst.data.data() + (dst.height-1) * dst.width * 4;
|
||||||
frame.strides[0] = -(signed)dst.pitch;
|
frame.strides[0] = -(signed)dst.width * 4;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
frame.planes[0] = dst.data;
|
frame.planes[0] = dst.data.data();
|
||||||
frame.strides[0] = dst.pitch;
|
frame.strides[0] = dst.width * 4;
|
||||||
}
|
}
|
||||||
frame.pixfmt = CSRI_F_BGR_;
|
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);
|
std::lock_guard<std::mutex> lock(csri_mutex);
|
||||||
if (!csri_request_fmt(instance, &format))
|
if (!csri_request_fmt(instance, &format))
|
||||||
|
|
|
@ -56,7 +56,7 @@ public:
|
||||||
~CSRISubtitlesProvider();
|
~CSRISubtitlesProvider();
|
||||||
|
|
||||||
void LoadSubtitles(AssFile *subs);
|
void LoadSubtitles(AssFile *subs);
|
||||||
void DrawSubtitles(AegiVideoFrame &dst, double time);
|
void DrawSubtitles(VideoFrame &dst, double time);
|
||||||
|
|
||||||
static std::vector<std::string> GetSubTypes();
|
static std::vector<std::string> GetSubTypes();
|
||||||
};
|
};
|
||||||
|
|
|
@ -144,8 +144,8 @@ void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) {
|
||||||
#define _b(c) (((c)>>8)&0xFF)
|
#define _b(c) (((c)>>8)&0xFF)
|
||||||
#define _a(c) ((c)&0xFF)
|
#define _a(c) ((c)&0xFF)
|
||||||
|
|
||||||
void LibassSubtitlesProvider::DrawSubtitles(AegiVideoFrame &frame,double time) {
|
void LibassSubtitlesProvider::DrawSubtitles(VideoFrame &frame,double time) {
|
||||||
ass_set_frame_size(ass_renderer, frame.w, frame.h);
|
ass_set_frame_size(ass_renderer, frame.width, frame.height);
|
||||||
|
|
||||||
ASS_Image* img = ass_render_frame(ass_renderer, ass_track, int(time * 1000), nullptr);
|
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.
|
// This is repeated for all of them.
|
||||||
|
|
||||||
using namespace boost::gil;
|
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)
|
if (frame.flipped)
|
||||||
dst = flipped_up_down_view(dst);
|
dst = flipped_up_down_view(dst);
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ public:
|
||||||
~LibassSubtitlesProvider();
|
~LibassSubtitlesProvider();
|
||||||
|
|
||||||
void LoadSubtitles(AssFile *subs);
|
void LoadSubtitles(AssFile *subs);
|
||||||
void DrawSubtitles(AegiVideoFrame &dst, double time);
|
void DrawSubtitles(VideoFrame &dst, double time);
|
||||||
|
|
||||||
static void CacheFonts();
|
static void CacheFonts();
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,14 +41,11 @@ enum {
|
||||||
SUBS_FILE_ALREADY_LOADED = -2
|
SUBS_FILE_ALREADY_LOADED = -2
|
||||||
};
|
};
|
||||||
|
|
||||||
std::shared_ptr<AegiVideoFrame> ThreadedFrameSource::ProcFrame(int frame_number, double time, bool raw) {
|
std::shared_ptr<VideoFrame> ThreadedFrameSource::ProcFrame(int frame_number, double time, bool raw) {
|
||||||
std::shared_ptr<AegiVideoFrame> frame(new AegiVideoFrame, [](AegiVideoFrame *frame) {
|
std::shared_ptr<VideoFrame> frame;
|
||||||
frame->Clear();
|
|
||||||
delete frame;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
frame->CopyFrom(video_provider->GetFrame(frame_number));
|
frame = video_provider->GetFrame(frame_number);
|
||||||
}
|
}
|
||||||
catch (VideoProviderError const& err) { throw VideoProviderErrorEvent(err); }
|
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<VideoFrame> ThreadedFrameSource::GetFrame(int frame, double time, bool raw) {
|
||||||
std::shared_ptr<AegiVideoFrame> ret;
|
std::shared_ptr<VideoFrame> ret;
|
||||||
worker->Sync([&]{
|
worker->Sync([&]{
|
||||||
ret = ProcFrame(frame, time, raw);
|
ret = ProcFrame(frame, time, raw);
|
||||||
});
|
});
|
||||||
|
|
|
@ -29,12 +29,12 @@
|
||||||
|
|
||||||
#include <wx/event.h>
|
#include <wx/event.h>
|
||||||
|
|
||||||
class AegiVideoFrame;
|
|
||||||
class AssEntry;
|
class AssEntry;
|
||||||
class AssFile;
|
class AssFile;
|
||||||
class SubtitlesProvider;
|
class SubtitlesProvider;
|
||||||
class VideoProvider;
|
class VideoProvider;
|
||||||
class VideoProviderError;
|
class VideoProviderError;
|
||||||
|
struct VideoFrame;
|
||||||
namespace agi { namespace dispatch { class Queue; } }
|
namespace agi { namespace dispatch { class Queue; } }
|
||||||
|
|
||||||
/// @class ThreadedFrameSource
|
/// @class ThreadedFrameSource
|
||||||
|
@ -61,7 +61,7 @@ class ThreadedFrameSource {
|
||||||
/// currently loaded file is out of date.
|
/// currently loaded file is out of date.
|
||||||
int single_frame;
|
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
|
/// Produce a frame if req_version is still the current version
|
||||||
void ProcAsync(uint_fast32_t req_version);
|
void ProcAsync(uint_fast32_t req_version);
|
||||||
|
@ -98,7 +98,7 @@ public:
|
||||||
/// @brief frame Frame number
|
/// @brief frame Frame number
|
||||||
/// @brief time Exact start time of the frame in seconds
|
/// @brief time Exact start time of the frame in seconds
|
||||||
/// @brief raw Get raw frame without subtitles
|
/// @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
|
/// Get a reference to the video provider this is using
|
||||||
VideoProvider *GetVideoProvider() const { return video_provider.get(); }
|
VideoProvider *GetVideoProvider() const { return video_provider.get(); }
|
||||||
|
@ -113,11 +113,11 @@ public:
|
||||||
/// Event which signals that a requested frame is ready
|
/// Event which signals that a requested frame is ready
|
||||||
struct FrameReadyEvent : public wxEvent {
|
struct FrameReadyEvent : public wxEvent {
|
||||||
/// Frame which is ready
|
/// Frame which is ready
|
||||||
std::shared_ptr<AegiVideoFrame> frame;
|
std::shared_ptr<VideoFrame> frame;
|
||||||
/// Time which was used for subtitle rendering
|
/// Time which was used for subtitle rendering
|
||||||
double time;
|
double time;
|
||||||
wxEvent *Clone() const { return new FrameReadyEvent(*this); };
|
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) { }
|
: frame(frame), time(time) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -289,7 +289,7 @@ void VideoContext::GetFrameAsync(int n) {
|
||||||
provider->RequestFrame(n, TimeAtFrame(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);
|
return provider->GetFrame(n, TimeAtFrame(n), raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,10 +45,10 @@
|
||||||
|
|
||||||
#include <wx/timer.h>
|
#include <wx/timer.h>
|
||||||
|
|
||||||
class AegiVideoFrame;
|
|
||||||
class AssEntry;
|
class AssEntry;
|
||||||
struct SubtitlesProviderErrorEvent;
|
struct SubtitlesProviderErrorEvent;
|
||||||
class ThreadedFrameSource;
|
class ThreadedFrameSource;
|
||||||
|
struct VideoFrame;
|
||||||
class VideoProvider;
|
class VideoProvider;
|
||||||
struct VideoProviderErrorEvent;
|
struct VideoProviderErrorEvent;
|
||||||
|
|
||||||
|
@ -176,7 +176,7 @@ public:
|
||||||
/// @param n Frame number to get
|
/// @param n Frame number to get
|
||||||
/// @param raw If true, subtitles are not rendered on the frame
|
/// @param raw If true, subtitles are not rendered on the frame
|
||||||
/// @return The requested 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
|
/// Asynchronously get a video frame, triggering a EVT_FRAME_READY event when it's ready
|
||||||
/// @param n Frame number to get
|
/// @param n Frame number to get
|
||||||
|
|
|
@ -42,7 +42,6 @@
|
||||||
#include <wx/glcanvas.h>
|
#include <wx/glcanvas.h>
|
||||||
|
|
||||||
// Prototypes
|
// Prototypes
|
||||||
class AegiVideoFrame;
|
|
||||||
struct FrameReadyEvent;
|
struct FrameReadyEvent;
|
||||||
class VideoContext;
|
class VideoContext;
|
||||||
class VideoOutGL;
|
class VideoOutGL;
|
||||||
|
@ -50,6 +49,7 @@ class VisualToolBase;
|
||||||
class wxComboBox;
|
class wxComboBox;
|
||||||
class wxTextCtrl;
|
class wxTextCtrl;
|
||||||
class wxToolBar;
|
class wxToolBar;
|
||||||
|
struct VideoFrame;
|
||||||
|
|
||||||
namespace agi {
|
namespace agi {
|
||||||
struct Context;
|
struct Context;
|
||||||
|
@ -104,7 +104,7 @@ class VideoDisplay : public wxGLCanvas {
|
||||||
bool freeSize;
|
bool freeSize;
|
||||||
|
|
||||||
/// Frame which will replace the currently visible frame on the next render
|
/// 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
|
/// @brief Draw an overscan mask
|
||||||
/// @param horizontal_percent The percent of the video reserved horizontally
|
/// @param horizontal_percent The percent of the video reserved horizontally
|
||||||
|
|
|
@ -1,146 +1,43 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file video_frame.cpp
|
|
||||||
/// @brief Wrapper around a frame of video data
|
|
||||||
/// @ingroup video
|
|
||||||
///
|
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include "utils.h"
|
|
||||||
#include "video_frame.h"
|
#include "video_frame.h"
|
||||||
|
|
||||||
void AegiVideoFrame::Reset() {
|
#include <boost/gil/gil_all.hpp>
|
||||||
// Zero variables
|
#include <wx/image.h>
|
||||||
data = 0;
|
|
||||||
pitch = 0;
|
|
||||||
memSize = 0;
|
|
||||||
w = 0;
|
|
||||||
h = 0;
|
|
||||||
|
|
||||||
// Set properties
|
VideoFrame::VideoFrame(const unsigned char *data, size_t width, size_t height, size_t pitch, bool flipped)
|
||||||
flipped = false;
|
: data(data, data + width * height * 4)
|
||||||
invertChannels = true;
|
, width(width)
|
||||||
ownMem = true;
|
, height(height)
|
||||||
|
, pitch(pitch)
|
||||||
|
, flipped(flipped)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
AegiVideoFrame::AegiVideoFrame() {
|
wxImage GetImage(VideoFrame const& frame) {
|
||||||
Reset();
|
using namespace boost::gil;
|
||||||
}
|
|
||||||
|
|
||||||
/// @brief Create a solid black frame of the request size and format
|
wxImage img(frame.width, frame.height);
|
||||||
/// @param width
|
auto src = interleaved_view(frame.width, frame.height, (bgra8_pixel_t*)frame.data.data(), frame.pitch);
|
||||||
/// @param height
|
auto dst = interleaved_view(frame.width, frame.height, (rgb8_pixel_t*)img.GetData(), 3 * frame.width);
|
||||||
AegiVideoFrame::AegiVideoFrame(unsigned int width, unsigned int height) {
|
if (frame.flipped)
|
||||||
assert(width > 0 && width < 10000);
|
src = flipped_up_down_view(src);
|
||||||
assert(height > 0 && height < 10000);
|
copy_and_convert_pixels(src, dst);
|
||||||
|
|
||||||
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);
|
|
||||||
return img;
|
return img;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +1,31 @@
|
||||||
// Copyright (c) 2007, Rodrigo Braz Monteiro
|
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
/// @file video_frame.h
|
#include <vector>
|
||||||
/// @see video_frame.cpp
|
|
||||||
/// @ingroup video
|
|
||||||
///
|
|
||||||
|
|
||||||
#pragma once
|
class wxImage;
|
||||||
|
|
||||||
#include <wx/image.h>
|
struct VideoFrame {
|
||||||
|
std::vector<unsigned char> data;
|
||||||
class AegiVideoFrame {
|
size_t width;
|
||||||
/// Whether the object owns its buffer. If this is false, **data should never be modified
|
size_t height;
|
||||||
bool ownMem;
|
size_t pitch;
|
||||||
/// @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
|
|
||||||
bool flipped;
|
bool flipped;
|
||||||
|
|
||||||
/// Swap Red and Blue channels (controls RGB versus BGR ordering etc)
|
VideoFrame(const unsigned char *data, size_t width, size_t height, size_t pitch, bool fipped);
|
||||||
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; };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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) {
|
void VideoOutGL::UploadFrameData(VideoFrame const& frame) {
|
||||||
if (frame.h == 0 || frame.w == 0) return;
|
if (frame.height == 0 || frame.width == 0) return;
|
||||||
|
|
||||||
GLuint format = frame.invertChannels ? GL_BGRA_EXT : GL_RGBA;
|
InitTextures(frame.width, frame.height, GL_BGRA_EXT, 4, frame.flipped);
|
||||||
InitTextures(frame.w, frame.h, format, frame.GetBpp(), frame.flipped);
|
|
||||||
|
|
||||||
// Set the row length, needed to be able to upload partial rows
|
// 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) {
|
for (auto& ti : textureList) {
|
||||||
CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
|
CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, ti.textureID));
|
||||||
CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ti.sourceW,
|
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));
|
CHECK_ERROR(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0));
|
||||||
|
|
|
@ -1,29 +1,16 @@
|
||||||
// Copyright (c) 2009, Thomas Goyne
|
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// Aegisub Project http://www.aegisub.org/
|
||||||
|
|
||||||
|
@ -36,12 +23,11 @@
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class AegiVideoFrame;
|
struct VideoFrame;
|
||||||
|
|
||||||
/// @class VideoOutGL
|
/// @class VideoOutGL
|
||||||
/// @brief OpenGL based video renderer
|
/// @brief OpenGL based video renderer
|
||||||
class VideoOutGL {
|
class VideoOutGL {
|
||||||
private:
|
|
||||||
struct TextureInfo;
|
struct TextureInfo;
|
||||||
|
|
||||||
/// The maximum texture size supported by the user's graphics card
|
/// The maximum texture size supported by the user's graphics card
|
||||||
|
@ -80,7 +66,7 @@ private:
|
||||||
public:
|
public:
|
||||||
/// @brief Set the frame to be displayed when Render() is called
|
/// @brief Set the frame to be displayed when Render() is called
|
||||||
/// @param frame The frame to be displayed
|
/// @param frame The frame to be displayed
|
||||||
void UploadFrameData(const AegiVideoFrame& frame);
|
void UploadFrameData(VideoFrame const& frame);
|
||||||
|
|
||||||
/// @brief Render a frame
|
/// @brief Render a frame
|
||||||
/// @param x Bottom left x coordinate
|
/// @param x Bottom left x coordinate
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include "video_provider_avs.h"
|
#include "video_provider_avs.h"
|
||||||
|
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#include "video_frame.h"
|
||||||
|
|
||||||
#include <libaegisub/access.h>
|
#include <libaegisub/access.h>
|
||||||
#include <libaegisub/charset_conv.h>
|
#include <libaegisub/charset_conv.h>
|
||||||
|
@ -53,13 +54,9 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename)
|
AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename)
|
||||||
: last_fnum(-1)
|
|
||||||
{
|
{
|
||||||
agi::acs::CheckFileRead(filename);
|
agi::acs::CheckFileRead(filename);
|
||||||
|
|
||||||
iframe.flipped = true;
|
|
||||||
iframe.invertChannels = true;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(avs.GetMutex());
|
std::lock_guard<std::mutex> lock(avs.GetMutex());
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
@ -121,12 +118,12 @@ AvisynthVideoProvider::AvisynthVideoProvider(agi::fs::path const& filename)
|
||||||
|
|
||||||
for (size_t i = 0; i < avis.dwLength; i++) {
|
for (size_t i = 0; i < avis.dwLength; i++) {
|
||||||
if (AVIStreamIsKeyFrame(ppavi, 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 every frame is a keyframe then just discard the keyframe data as it's useless
|
||||||
if (KeyFrames.size() == (size_t)avis.dwLength)
|
if (keyframes.size() == (size_t)avis.dwLength)
|
||||||
KeyFrames.clear();
|
keyframes.clear();
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
stream_release:
|
stream_release:
|
||||||
|
@ -170,10 +167,6 @@ file_exit:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AvisynthVideoProvider::~AvisynthVideoProvider() {
|
|
||||||
iframe.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
|
AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
|
||||||
IScriptEnvironment *env = avs.GetEnv();
|
IScriptEnvironment *env = avs.GetEnv();
|
||||||
char *videoFilename = env->SaveString(agi::fs::ShortName(filename).c_str());
|
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
|
// Avisynth file, just import it
|
||||||
if (agi::fs::HasExtension(filename, "avs")) {
|
if (agi::fs::HasExtension(filename, "avs")) {
|
||||||
LOG_I("avisynth/video") << "Opening .avs file with Import";
|
LOG_I("avisynth/video") << "Opening .avs file with Import";
|
||||||
decoderName = "Avisynth/Import";
|
decoder_name = "Avisynth/Import";
|
||||||
return env->Invoke("Import", videoFilename);
|
return env->Invoke("Import", videoFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +184,7 @@ AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
|
||||||
try {
|
try {
|
||||||
const char *argnames[2] = { 0, "audio" };
|
const char *argnames[2] = { 0, "audio" };
|
||||||
AVSValue args[2] = { videoFilename, false };
|
AVSValue args[2] = { videoFilename, false };
|
||||||
decoderName = "Avisynth/AviSource";
|
decoder_name = "Avisynth/AviSource";
|
||||||
return env->Invoke("AviSource", AVSValue(args,2), argnames);
|
return env->Invoke("AviSource", AVSValue(args,2), argnames);
|
||||||
}
|
}
|
||||||
// On Failure, fallback to DSS
|
// 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")) {
|
if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("Mpeg2Dec3_Mpeg2Source")) {
|
||||||
LOG_I("avisynth/video") << "Opening .d2v file with Mpeg2Dec3_Mpeg2Source";
|
LOG_I("avisynth/video") << "Opening .d2v file with Mpeg2Dec3_Mpeg2Source";
|
||||||
auto script = env->Invoke("Mpeg2Dec3_Mpeg2Source", videoFilename);
|
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 avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this
|
||||||
if (env->FunctionExists("SetPlanarLegacyAlignment")) {
|
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 that fails, try opening it with DGDecode
|
||||||
if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("DGDecode_Mpeg2Source")) {
|
if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("DGDecode_Mpeg2Source")) {
|
||||||
LOG_I("avisynth/video") << "Opening .d2v file with 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);
|
return env->Invoke("Avisynth/Mpeg2Source", videoFilename);
|
||||||
|
|
||||||
//note that DGDecode will also have issues like if the version is too
|
//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")) {
|
if (agi::fs::HasExtension(filename, "d2v") && env->FunctionExists("Mpeg2Source")) {
|
||||||
LOG_I("avisynth/video") << "Opening .d2v file with other Mpeg2Source";
|
LOG_I("avisynth/video") << "Opening .d2v file with other Mpeg2Source";
|
||||||
AVSValue script = env->Invoke("Mpeg2Source", videoFilename);
|
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 avisynth is 2.5.7 beta 2 or newer old mpeg2decs will crash without this
|
||||||
if (env->FunctionExists("SetPlanarLegacyAlignment"))
|
if (env->FunctionExists("SetPlanarLegacyAlignment"))
|
||||||
|
@ -247,7 +240,7 @@ AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
|
||||||
// If DSS2 loaded properly, try using it
|
// If DSS2 loaded properly, try using it
|
||||||
if (env->FunctionExists("dss2")) {
|
if (env->FunctionExists("dss2")) {
|
||||||
LOG_I("avisynth/video") << "Opening file with DSS2";
|
LOG_I("avisynth/video") << "Opening file with DSS2";
|
||||||
decoderName = "Avisynth/DSS2";
|
decoder_name = "Avisynth/DSS2";
|
||||||
return env->Invoke("DSS2", videoFilename);
|
return env->Invoke("DSS2", videoFilename);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,7 +254,7 @@ AVSValue AvisynthVideoProvider::Open(agi::fs::path const& filename) {
|
||||||
if (env->FunctionExists("DirectShowSource")) {
|
if (env->FunctionExists("DirectShowSource")) {
|
||||||
const char *argnames[3] = { 0, "video", "audio" };
|
const char *argnames[3] = { 0, "video", "audio" };
|
||||||
AVSValue args[3] = { videoFilename, true, false };
|
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!";
|
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";
|
LOG_I("avisynth/video") << "Opening file with DirectShowSource";
|
||||||
return env->Invoke("DirectShowSource", AVSValue(args,3), argnames);
|
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");
|
throw VideoNotSupported("No function suitable for opening the video found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const AegiVideoFrame AvisynthVideoProvider::GetFrame(int n) {
|
std::shared_ptr<VideoFrame> AvisynthVideoProvider::GetFrame(int n) {
|
||||||
if (n == last_fnum) return iframe;
|
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(avs.GetMutex());
|
std::lock_guard<std::mutex> lock(avs.GetMutex());
|
||||||
|
|
||||||
auto frame = RGB32Video->GetFrame(n, avs.GetEnv());
|
auto frame = RGB32Video->GetFrame(n, avs.GetEnv());
|
||||||
iframe.pitch = frame->GetPitch();
|
return std::make_shared<VideoFrame>(frame->GetReadPtr(), frame->GetRowSize() / 4, frame->GetHeight(), frame->GetPitch(), true);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
#endif // HAVE_AVISYNTH
|
#endif // HAVE_AVISYNTH
|
||||||
|
|
|
@ -35,40 +35,37 @@
|
||||||
#ifdef WITH_AVISYNTH
|
#ifdef WITH_AVISYNTH
|
||||||
#include "include/aegisub/video_provider.h"
|
#include "include/aegisub/video_provider.h"
|
||||||
|
|
||||||
|
#define VideoFrame AVSVideoFrame
|
||||||
#include "avisynth.h"
|
#include "avisynth.h"
|
||||||
|
#undef VideoFrame
|
||||||
#include "avisynth_wrap.h"
|
#include "avisynth_wrap.h"
|
||||||
#include "video_frame.h"
|
|
||||||
|
|
||||||
class AvisynthVideoProvider: public VideoProvider {
|
class AvisynthVideoProvider: public VideoProvider {
|
||||||
AviSynthWrapper avs;
|
AviSynthWrapper avs;
|
||||||
AegiVideoFrame iframe;
|
std::string decoder_name;
|
||||||
std::string decoderName;
|
|
||||||
agi::vfr::Framerate fps;
|
agi::vfr::Framerate fps;
|
||||||
std::vector<int> KeyFrames;
|
std::vector<int> keyframes;
|
||||||
std::string warning;
|
std::string warning;
|
||||||
std::string colorspace;
|
std::string colorspace;
|
||||||
|
|
||||||
PClip RGB32Video;
|
PClip RGB32Video;
|
||||||
VideoInfo vi;
|
VideoInfo vi;
|
||||||
|
|
||||||
int last_fnum;
|
|
||||||
|
|
||||||
AVSValue Open(agi::fs::path const& filename);
|
AVSValue Open(agi::fs::path const& filename);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AvisynthVideoProvider(agi::fs::path const& filename);
|
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; };
|
int GetFrameCount() const { return vi.num_frames; }
|
||||||
agi::vfr::Framerate GetFPS() const { return fps; };
|
agi::vfr::Framerate GetFPS() const { return fps; }
|
||||||
int GetWidth() const { return vi.width; };
|
int GetWidth() const { return vi.width; }
|
||||||
int GetHeight() const { return vi.height; };
|
int GetHeight() const { return vi.height; }
|
||||||
double GetDAR() const { return 0; }
|
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 GetWarning() const { return warning; }
|
||||||
std::string GetDecoderName() const { return decoderName; }
|
std::string GetDecoderName() const { return decoder_name; }
|
||||||
std::string GetColorSpace() const { return colorspace; }
|
std::string GetColorSpace() const { return colorspace; }
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,37 +1,19 @@
|
||||||
// Copyright (c) 2008, Rodrigo Braz Monteiro
|
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// 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 "config.h"
|
||||||
|
|
||||||
#include "video_provider_cache.h"
|
#include "video_provider_cache.h"
|
||||||
|
@ -40,51 +22,44 @@
|
||||||
#include "video_frame.h"
|
#include "video_frame.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
/// A video frame and its frame number
|
/// A video frame and its frame number
|
||||||
struct CachedFrame : public AegiVideoFrame {
|
struct CachedFrame : public VideoFrame {
|
||||||
int frame_number;
|
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))
|
: master(std::move(parent))
|
||||||
, max_cache_size(OPT_GET("Provider/Video/Cache/Size")->GetInt() << 20) // convert MB to bytes
|
, max_cache_size(OPT_GET("Provider/Video/Cache/Size")->GetInt() << 20) // convert MB to bytes
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoProviderCache::~VideoProviderCache() {
|
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;
|
size_t total_size = 0;
|
||||||
|
|
||||||
// See if frame is cached
|
|
||||||
for (auto cur = cache.begin(); cur != cache.end(); ++cur) {
|
for (auto cur = cache.begin(); cur != cache.end(); ++cur) {
|
||||||
if (cur->frame_number == n) {
|
if (cur->frame_number == n) {
|
||||||
cache.push_front(*cur);
|
cache.splice(cache.begin(), cache, cur); // Move to front
|
||||||
cache.erase(cur);
|
return std::make_shared<VideoFrame>(cache.front());
|
||||||
return cache.front();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
total_size += cur->memSize;
|
total_size += cur->data.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not cached, retrieve it
|
auto frame = master->GetFrame(n);
|
||||||
const AegiVideoFrame frame = master->GetFrame(n);
|
|
||||||
|
|
||||||
// Cache full, use oldest frame
|
if (total_size >= max_cache_size)
|
||||||
if (total_size >= max_cache_size) {
|
|
||||||
cache.push_front(cache.back());
|
|
||||||
cache.pop_back();
|
cache.pop_back();
|
||||||
}
|
cache.emplace_front(n, *frame);
|
||||||
// Cache not full, insert new one
|
|
||||||
else
|
|
||||||
cache.push_front(CachedFrame());
|
|
||||||
|
|
||||||
// Cache
|
return frame;
|
||||||
cache.front().frame_number = n;
|
|
||||||
cache.front().CopyFrom(frame);
|
|
||||||
return cache.front();
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,19 @@
|
||||||
// Copyright (c) 2008, Rodrigo Braz Monteiro, Fredrik Mellbin
|
// Copyright (c) 2013, Thomas Goyne <plorkyeran@aegisub.org>
|
||||||
// All rights reserved.
|
|
||||||
//
|
//
|
||||||
// Redistribution and use in source and binary forms, with or without
|
// Permission to use, copy, modify, and distribute this software for any
|
||||||
// modification, are permitted provided that the following conditions are met:
|
// 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,
|
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
// this list of conditions and the following disclaimer.
|
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
// this list of conditions and the following disclaimer in the documentation
|
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
// and/or other materials provided with the distribution.
|
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
// * Neither the name of the Aegisub Group nor the names of its contributors
|
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
// may be used to endorse or promote products derived from this software
|
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
// without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
||||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
||||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
||||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
||||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
||||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
||||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
||||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
//
|
//
|
||||||
// Aegisub Project http://www.aegisub.org/
|
// 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 <boost/container/list.hpp>
|
||||||
|
|
||||||
#include "include/aegisub/video_provider.h"
|
#include "include/aegisub/video_provider.h"
|
||||||
|
@ -54,10 +36,10 @@ class VideoProviderCache : public VideoProvider {
|
||||||
boost::container::list<CachedFrame> cache;
|
boost::container::list<CachedFrame> cache;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VideoProviderCache(std::unique_ptr<VideoProvider>&& master);
|
VideoProviderCache(std::unique_ptr<VideoProvider> master);
|
||||||
~VideoProviderCache();
|
~VideoProviderCache();
|
||||||
|
|
||||||
const AegiVideoFrame GetFrame(int n);
|
std::shared_ptr<VideoFrame> GetFrame(int n);
|
||||||
|
|
||||||
int GetFrameCount() const { return master->GetFrameCount(); }
|
int GetFrameCount() const { return master->GetFrameCount(); }
|
||||||
int GetWidth() const { return master->GetWidth(); }
|
int GetWidth() const { return master->GetWidth(); }
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
#include "video_provider_dummy.h"
|
#include "video_provider_dummy.h"
|
||||||
|
|
||||||
#include "colorspace.h"
|
#include "colorspace.h"
|
||||||
|
#include "video_frame.h"
|
||||||
|
|
||||||
#include <libaegisub/color.h>
|
#include <libaegisub/color.h>
|
||||||
#include <libaegisub/fs.h>
|
#include <libaegisub/fs.h>
|
||||||
|
@ -46,18 +47,21 @@
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
#include <boost/algorithm/string/split.hpp>
|
#include <boost/algorithm/string/split.hpp>
|
||||||
#include <boost/format.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) {
|
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->framecount = frames;
|
||||||
this->fps = fps;
|
this->fps = fps;
|
||||||
this->width = width;
|
this->width = width;
|
||||||
this->height = height;
|
this->height = height;
|
||||||
this->frame = AegiVideoFrame(width, height);
|
data.resize(width * height * 4);
|
||||||
|
|
||||||
unsigned char *dst = frame.data;
|
using namespace boost::gil;
|
||||||
unsigned char colors[2][4] = {
|
auto dst = interleaved_view(width, height, (bgra8_pixel_t*)data.data(), 4 * width);
|
||||||
{ blue, green, red, 0 },
|
|
||||||
{ 0, 0, 0, 0 }
|
bgra8_pixel_t colors[2] = {
|
||||||
|
bgra8_pixel_t(blue, green, red, 0),
|
||||||
|
bgra8_pixel_t(blue, green, red, 0)
|
||||||
};
|
};
|
||||||
|
|
||||||
if (pattern) {
|
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);
|
rgb_to_hsl(red, blue, green, &h, &s, &l);
|
||||||
l += 24;
|
l += 24;
|
||||||
if (l < 24) l -= 48;
|
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
|
// Divide into a 8x8 grid and use light colours when row % 2 != col % 2
|
||||||
int ppitch = frame.pitch / frame.GetBpp();
|
auto out = dst.begin();
|
||||||
for (unsigned int y = 0; y < frame.h; ++y) {
|
for (int y = 0; y < height; ++y)
|
||||||
for (int x = 0; x < ppitch; ++x) {
|
for (int x = 0; x < width; ++x)
|
||||||
memcpy(dst, colors[((y / 8) & 1) != ((x / 8) & 1)], 4);
|
*out++ = colors[((y / 8) & 1) != ((x / 8) & 1)];
|
||||||
dst += 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (int i = frame.pitch * frame.h / frame.GetBpp() - 1; i >= 0; --i)
|
fill_pixels(dst, colors[0]);
|
||||||
memcpy(dst + i * 4, colors[0], 4);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
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) {
|
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" : ""));
|
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 "include/aegisub/video_provider.h"
|
||||||
#include "video_frame.h"
|
|
||||||
|
|
||||||
namespace agi { struct Color; }
|
namespace agi { struct Color; }
|
||||||
|
|
||||||
|
@ -48,8 +47,8 @@ class DummyVideoProvider : public VideoProvider {
|
||||||
int width; ///< Width in pixels
|
int width; ///< Width in pixels
|
||||||
int height; ///< Height in pixels
|
int height; ///< Height in pixels
|
||||||
|
|
||||||
/// The single image which is returned for all frames
|
/// The data for the image returned for all frames
|
||||||
AegiVideoFrame frame;
|
std::vector<unsigned char> data;
|
||||||
|
|
||||||
/// Create the dummy frame from the given parameters
|
/// Create the dummy frame from the given parameters
|
||||||
/// @param fps Frame rate of the dummy video
|
/// @param fps Frame rate of the dummy video
|
||||||
|
@ -75,14 +74,12 @@ public:
|
||||||
/// @param pattern Use a checkerboard pattern rather than a solid colour
|
/// @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);
|
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
|
/// Make a fake filename which when passed to the constructor taking a
|
||||||
/// string will result in a video with the given parameters
|
/// 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);
|
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 GetFrameCount() const { return framecount; }
|
||||||
int GetWidth() const { return width; }
|
int GetWidth() const { return width; }
|
||||||
int GetHeight() const { return height; }
|
int GetHeight() const { return height; }
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "video_context.h"
|
#include "video_context.h"
|
||||||
|
#include "video_frame.h"
|
||||||
|
|
||||||
#include <libaegisub/fs.h>
|
#include <libaegisub/fs.h>
|
||||||
|
|
||||||
|
@ -52,7 +53,6 @@ FFmpegSourceVideoProvider::FFmpegSourceVideoProvider(agi::fs::path const& filena
|
||||||
, VideoInfo(nullptr)
|
, VideoInfo(nullptr)
|
||||||
, Width(-1)
|
, Width(-1)
|
||||||
, Height(-1)
|
, Height(-1)
|
||||||
, FrameNumber(-1)
|
|
||||||
{
|
{
|
||||||
ErrInfo.Buffer = FFMSErrMsg;
|
ErrInfo.Buffer = FFMSErrMsg;
|
||||||
ErrInfo.BufferSize = sizeof(FFMSErrMsg);
|
ErrInfo.BufferSize = sizeof(FFMSErrMsg);
|
||||||
|
@ -238,20 +238,16 @@ void FFmpegSourceVideoProvider::LoadVideo(agi::fs::path const& filename) {
|
||||||
Timecodes = 25.0;
|
Timecodes = 25.0;
|
||||||
else
|
else
|
||||||
Timecodes = agi::vfr::Framerate(TimecodesVector);
|
Timecodes = agi::vfr::Framerate(TimecodesVector);
|
||||||
|
|
||||||
FrameNumber = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AegiVideoFrame FFmpegSourceVideoProvider::GetFrame(int n) {
|
std::shared_ptr<VideoFrame> FFmpegSourceVideoProvider::GetFrame(int n) {
|
||||||
FrameNumber = mid(0, n, GetFrameCount() - 1);
|
n = mid(0, n, GetFrameCount() - 1);
|
||||||
|
|
||||||
// decode frame
|
auto frame = FFMS_GetFrame(VideoSource, n, &ErrInfo);
|
||||||
const FFMS_Frame *SrcFrame = FFMS_GetFrame(VideoSource, FrameNumber, &ErrInfo);
|
if (!frame)
|
||||||
if (!SrcFrame)
|
|
||||||
throw VideoDecodeError(std::string("Failed to retrieve frame: ") + ErrInfo.Buffer);
|
throw VideoDecodeError(std::string("Failed to retrieve frame: ") + ErrInfo.Buffer);
|
||||||
|
|
||||||
CurFrame.SetTo(SrcFrame->Data[0], Width, Height, SrcFrame->Linesize[0]);
|
return std::make_shared<VideoFrame>(frame->Data[0], Width, Height, frame->Linesize[0], false);
|
||||||
return CurFrame;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* WITH_FFMS2 */
|
#endif /* WITH_FFMS2 */
|
||||||
|
|
|
@ -33,11 +33,8 @@
|
||||||
///
|
///
|
||||||
|
|
||||||
#ifdef WITH_FFMS2
|
#ifdef WITH_FFMS2
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "ffmpegsource_common.h"
|
#include "ffmpegsource_common.h"
|
||||||
#include "include/aegisub/video_provider.h"
|
#include "include/aegisub/video_provider.h"
|
||||||
#include "video_frame.h"
|
|
||||||
|
|
||||||
/// @class FFmpegSourceVideoProvider
|
/// @class FFmpegSourceVideoProvider
|
||||||
/// @brief Implements video loading through the FFMS library.
|
/// @brief Implements video loading through the FFMS library.
|
||||||
|
@ -49,13 +46,10 @@ class FFmpegSourceVideoProvider : public VideoProvider, FFmpegSourceProvider {
|
||||||
int Width; ///< width in pixels
|
int Width; ///< width in pixels
|
||||||
int Height; ///< height in pixels
|
int Height; ///< height in pixels
|
||||||
double DAR; ///< display aspect ratio
|
double DAR; ///< display aspect ratio
|
||||||
int FrameNumber; ///< current framenumber
|
|
||||||
std::vector<int> KeyFramesList; ///< list of keyframes
|
std::vector<int> KeyFramesList; ///< list of keyframes
|
||||||
agi::vfr::Framerate Timecodes; ///< vfr object
|
agi::vfr::Framerate Timecodes; ///< vfr object
|
||||||
std::string ColorSpace; ///< Colorspace name
|
std::string ColorSpace; ///< Colorspace name
|
||||||
|
|
||||||
AegiVideoFrame CurFrame; ///< current video frame
|
|
||||||
|
|
||||||
char FFMSErrMsg[1024]; ///< FFMS error message
|
char FFMSErrMsg[1024]; ///< FFMS error message
|
||||||
FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
|
FFMS_ErrorInfo ErrInfo; ///< FFMS error codes/messages
|
||||||
|
|
||||||
|
@ -64,22 +58,16 @@ class FFmpegSourceVideoProvider : public VideoProvider, FFmpegSourceProvider {
|
||||||
public:
|
public:
|
||||||
FFmpegSourceVideoProvider(agi::fs::path const& filename);
|
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 GetFrameCount() const { return VideoInfo->NumFrames; }
|
||||||
int GetWidth() const { return Width; }
|
int GetWidth() const { return Width; }
|
||||||
int GetHeight() const { return Height; }
|
int GetHeight() const { return Height; }
|
||||||
double GetDAR() const { return DAR; }
|
double GetDAR() const { return DAR; }
|
||||||
agi::vfr::Framerate GetFPS() const { return Timecodes; }
|
agi::vfr::Framerate GetFPS() const { return Timecodes; }
|
||||||
|
|
||||||
std::string GetColorSpace() const { return ColorSpace; }
|
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::vector<int> GetKeyFrames() const { return KeyFramesList; };
|
||||||
std::string GetDecoderName() const { return "FFmpegSource"; }
|
std::string GetDecoderName() const { return "FFmpegSource"; }
|
||||||
/// @brief Gets the desired cache behavior.
|
|
||||||
/// @return Returns true.
|
|
||||||
bool WantsCaching() const { return true; }
|
bool WantsCaching() const { return true; }
|
||||||
};
|
};
|
||||||
#endif /* WITH_FFMS2 */
|
#endif /* WITH_FFMS2 */
|
||||||
|
|
|
@ -64,7 +64,6 @@ YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(agi::fs::path const& filename)
|
||||||
, w (0)
|
, w (0)
|
||||||
, h (0)
|
, h (0)
|
||||||
, num_frames(-1)
|
, num_frames(-1)
|
||||||
, cur_fn(-1)
|
|
||||||
, pixfmt(Y4M_PIXFMT_NONE)
|
, pixfmt(Y4M_PIXFMT_NONE)
|
||||||
, imode(Y4M_ILACE_NOTSET)
|
, imode(Y4M_ILACE_NOTSET)
|
||||||
{
|
{
|
||||||
|
@ -113,7 +112,6 @@ YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(agi::fs::path const& filename)
|
||||||
num_frames = IndexFile();
|
num_frames = IndexFile();
|
||||||
if (num_frames <= 0 || seek_table.empty())
|
if (num_frames <= 0 || seek_table.empty())
|
||||||
throw VideoOpenError("Unable to determine file length");
|
throw VideoOpenError("Unable to determine file length");
|
||||||
cur_fn = 0;
|
|
||||||
|
|
||||||
fseeko(sf, 0, SEEK_SET);
|
fseeko(sf, 0, SEEK_SET);
|
||||||
}
|
}
|
||||||
|
@ -123,7 +121,6 @@ YUV4MPEGVideoProvider::YUV4MPEGVideoProvider(agi::fs::path const& filename)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Destructor
|
|
||||||
YUV4MPEGVideoProvider::~YUV4MPEGVideoProvider() {
|
YUV4MPEGVideoProvider::~YUV4MPEGVideoProvider() {
|
||||||
fclose(sf);
|
fclose(sf);
|
||||||
}
|
}
|
||||||
|
@ -347,11 +344,8 @@ static FORCEINLINE int clamp(int x) {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Gets a given frame
|
std::shared_ptr<VideoFrame> YUV4MPEGVideoProvider::GetFrame(int n) {
|
||||||
/// @param n The frame number to return
|
n = mid(0, n, num_frames - 1);
|
||||||
/// @return The video frame
|
|
||||||
const AegiVideoFrame YUV4MPEGVideoProvider::GetFrame(int n) {
|
|
||||||
cur_fn = mid(0, n, num_frames - 1);
|
|
||||||
|
|
||||||
int uv_width = w / 2;
|
int uv_width = w / 2;
|
||||||
switch (pixfmt) {
|
switch (pixfmt) {
|
||||||
|
@ -380,17 +374,12 @@ const AegiVideoFrame YUV4MPEGVideoProvider::GetFrame(int n) {
|
||||||
throw "YUV4MPEG video provider: GetFrame: failed to read chroma planes";
|
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_y = &planes[0][0];
|
||||||
const unsigned char *src_u = &planes[1][0];
|
const unsigned char *src_u = &planes[1][0];
|
||||||
const unsigned char *src_v = &planes[2][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 py = 0; py < h; ++py) {
|
||||||
for (int px = 0; px < w / 2; ++px) {
|
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 frame_sz; /// size of each frame in bytes
|
||||||
int luma_sz; /// size of the luma plane 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 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_PixelFormat pixfmt; /// colorspace/pixel format
|
||||||
Y4M_InterlacingMode imode; /// interlacing mode (for the entire stream)
|
Y4M_InterlacingMode imode; /// interlacing mode (for the entire stream)
|
||||||
|
@ -133,7 +132,7 @@ public:
|
||||||
YUV4MPEGVideoProvider(agi::fs::path const& filename);
|
YUV4MPEGVideoProvider(agi::fs::path const& filename);
|
||||||
~YUV4MPEGVideoProvider();
|
~YUV4MPEGVideoProvider();
|
||||||
|
|
||||||
const AegiVideoFrame GetFrame(int n);
|
std::shared_ptr<VideoFrame> GetFrame(int n);
|
||||||
|
|
||||||
int GetFrameCount() const { return num_frames; }
|
int GetFrameCount() const { return num_frames; }
|
||||||
int GetWidth() const { return w; }
|
int GetWidth() const { return w; }
|
||||||
|
|
Loading…
Reference in a new issue