diff --git a/aegisub/src/command/video.cpp b/aegisub/src/command/video.cpp index 05aabe39f..eb81fb2ae 100644 --- a/aegisub/src/command/video.cpp +++ b/aegisub/src/command/video.cpp @@ -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) diff --git a/aegisub/src/include/aegisub/subtitles_provider.h b/aegisub/src/include/aegisub/subtitles_provider.h index cc10378ee..be2bc9459 100644 --- a/aegisub/src/include/aegisub/subtitles_provider.h +++ b/aegisub/src/include/aegisub/subtitles_provider.h @@ -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 { diff --git a/aegisub/src/include/aegisub/video_provider.h b/aegisub/src/include/aegisub/video_provider.h index e6f2a2cdb..c42305da6 100644 --- a/aegisub/src/include/aegisub/video_provider.h +++ b/aegisub/src/include/aegisub/video_provider.h @@ -37,16 +37,17 @@ #include #include +#include #include -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 GetFrame(int n)=0; // Override the following methods to get video information: virtual int GetFrameCount() const=0; ///< Get total number of frames diff --git a/aegisub/src/subs_preview.cpp b/aegisub/src/subs_preview.cpp index 5e544e9c1..9f9c7e8e0 100644 --- a/aegisub/src/subs_preview.cpp +++ b/aegisub/src/subs_preview.cpp @@ -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 @@ -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(frame.GetImage()); - frame.Clear(); + *bmp = static_cast(GetImage(*frame)); Refresh(); } diff --git a/aegisub/src/subtitles_provider_csri.cpp b/aegisub/src/subtitles_provider_csri.cpp index 1da871bad..3bf710f37 100644 --- a/aegisub/src/subtitles_provider_csri.cpp +++ b/aegisub/src/subtitles_provider_csri.cpp @@ -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 lock(csri_mutex); if (!csri_request_fmt(instance, &format)) diff --git a/aegisub/src/subtitles_provider_csri.h b/aegisub/src/subtitles_provider_csri.h index ab31e062d..e620f9385 100644 --- a/aegisub/src/subtitles_provider_csri.h +++ b/aegisub/src/subtitles_provider_csri.h @@ -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 GetSubTypes(); }; diff --git a/aegisub/src/subtitles_provider_libass.cpp b/aegisub/src/subtitles_provider_libass.cpp index a89dc1a31..8f9922f58 100644 --- a/aegisub/src/subtitles_provider_libass.cpp +++ b/aegisub/src/subtitles_provider_libass.cpp @@ -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); diff --git a/aegisub/src/subtitles_provider_libass.h b/aegisub/src/subtitles_provider_libass.h index 4f398eb07..9dd88f090 100644 --- a/aegisub/src/subtitles_provider_libass.h +++ b/aegisub/src/subtitles_provider_libass.h @@ -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(); }; diff --git a/aegisub/src/threaded_frame_source.cpp b/aegisub/src/threaded_frame_source.cpp index 13fda7a2c..10d3740e3 100644 --- a/aegisub/src/threaded_frame_source.cpp +++ b/aegisub/src/threaded_frame_source.cpp @@ -41,14 +41,11 @@ enum { SUBS_FILE_ALREADY_LOADED = -2 }; -std::shared_ptr ThreadedFrameSource::ProcFrame(int frame_number, double time, bool raw) { - std::shared_ptr frame(new AegiVideoFrame, [](AegiVideoFrame *frame) { - frame->Clear(); - delete frame; - }); +std::shared_ptr ThreadedFrameSource::ProcFrame(int frame_number, double time, bool raw) { + std::shared_ptr 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 ThreadedFrameSource::GetFrame(int frame, double time, bool raw) { - std::shared_ptr ret; +std::shared_ptr ThreadedFrameSource::GetFrame(int frame, double time, bool raw) { + std::shared_ptr ret; worker->Sync([&]{ ret = ProcFrame(frame, time, raw); }); diff --git a/aegisub/src/threaded_frame_source.h b/aegisub/src/threaded_frame_source.h index dc2e26c84..5f1fc2d57 100644 --- a/aegisub/src/threaded_frame_source.h +++ b/aegisub/src/threaded_frame_source.h @@ -29,12 +29,12 @@ #include -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 ProcFrame(int frame, double time, bool raw = false); + std::shared_ptr 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 GetFrame(int frame, double time, bool raw = false); + std::shared_ptr 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 frame; + std::shared_ptr frame; /// Time which was used for subtitle rendering double time; wxEvent *Clone() const { return new FrameReadyEvent(*this); }; - FrameReadyEvent(std::shared_ptr frame, double time) + FrameReadyEvent(std::shared_ptr frame, double time) : frame(frame), time(time) { } }; diff --git a/aegisub/src/video_context.cpp b/aegisub/src/video_context.cpp index 505428662..9935a80fe 100644 --- a/aegisub/src/video_context.cpp +++ b/aegisub/src/video_context.cpp @@ -289,7 +289,7 @@ void VideoContext::GetFrameAsync(int n) { provider->RequestFrame(n, TimeAtFrame(n)); } -std::shared_ptr VideoContext::GetFrame(int n, bool raw) { +std::shared_ptr VideoContext::GetFrame(int n, bool raw) { return provider->GetFrame(n, TimeAtFrame(n), raw); } diff --git a/aegisub/src/video_context.h b/aegisub/src/video_context.h index 702a4fcf8..f8a808e42 100644 --- a/aegisub/src/video_context.h +++ b/aegisub/src/video_context.h @@ -45,10 +45,10 @@ #include -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 GetFrame(int n, bool raw = false); + std::shared_ptr 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 diff --git a/aegisub/src/video_display.h b/aegisub/src/video_display.h index 36dc79b5c..a7f9ae03c 100644 --- a/aegisub/src/video_display.h +++ b/aegisub/src/video_display.h @@ -42,7 +42,6 @@ #include // 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 pending_frame; + std::shared_ptr pending_frame; /// @brief Draw an overscan mask /// @param horizontal_percent The percent of the video reserved horizontally diff --git a/aegisub/src/video_frame.cpp b/aegisub/src/video_frame.cpp index 4055e3245..6497f1a2a 100644 --- a/aegisub/src/video_frame.cpp +++ b/aegisub/src/video_frame.cpp @@ -1,146 +1,43 @@ -// Copyright (c) 2007, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2013, Thomas Goyne // -// 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 +#include - // 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(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 // -// 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 -#pragma once +class wxImage; -#include - -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 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); diff --git a/aegisub/src/video_out_gl.cpp b/aegisub/src/video_out_gl.cpp index 51c24256f..f68e36b08 100644 --- a/aegisub/src/video_out_gl.cpp +++ b/aegisub/src/video_out_gl.cpp @@ -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)); diff --git a/aegisub/src/video_out_gl.h b/aegisub/src/video_out_gl.h index a362bda6d..18f649129 100644 --- a/aegisub/src/video_out_gl.h +++ b/aegisub/src/video_out_gl.h @@ -1,29 +1,16 @@ -// Copyright (c) 2009, Thomas Goyne -// All rights reserved. +// Copyright (c) 2013, Thomas Goyne // -// 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 -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 diff --git a/aegisub/src/video_provider_avs.cpp b/aegisub/src/video_provider_avs.cpp index 1d0c5448e..58e32f215 100644 --- a/aegisub/src/video_provider_avs.cpp +++ b/aegisub/src/video_provider_avs.cpp @@ -38,6 +38,7 @@ #include "video_provider_avs.h" #include "options.h" +#include "video_frame.h" #include #include @@ -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 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 AvisynthVideoProvider::GetFrame(int n) { std::lock_guard 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(frame->GetReadPtr(), frame->GetRowSize() / 4, frame->GetHeight(), frame->GetPitch(), true); } #endif // HAVE_AVISYNTH diff --git a/aegisub/src/video_provider_avs.h b/aegisub/src/video_provider_avs.h index abbc4929a..27b64c451 100644 --- a/aegisub/src/video_provider_avs.h +++ b/aegisub/src/video_provider_avs.h @@ -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 KeyFrames; + std::vector 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 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 GetKeyFrames() const { return KeyFrames; }; + std::vector 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 diff --git a/aegisub/src/video_provider_cache.cpp b/aegisub/src/video_provider_cache.cpp index 2deadc7fa..add2f441f 100644 --- a/aegisub/src/video_provider_cache.cpp +++ b/aegisub/src/video_provider_cache.cpp @@ -1,37 +1,19 @@ -// Copyright (c) 2008, Rodrigo Braz Monteiro -// All rights reserved. +// Copyright (c) 2013, Thomas Goyne // -// 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 -#include /// 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&& parent) +VideoProviderCache::VideoProviderCache(std::unique_ptr 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 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(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; } diff --git a/aegisub/src/video_provider_cache.h b/aegisub/src/video_provider_cache.h index 27cd5aa71..3791560e5 100644 --- a/aegisub/src/video_provider_cache.h +++ b/aegisub/src/video_provider_cache.h @@ -1,37 +1,19 @@ -// Copyright (c) 2008, Rodrigo Braz Monteiro, Fredrik Mellbin -// All rights reserved. +// Copyright (c) 2013, Thomas Goyne // -// 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 #include "include/aegisub/video_provider.h" @@ -54,10 +36,10 @@ class VideoProviderCache : public VideoProvider { boost::container::list cache; public: - VideoProviderCache(std::unique_ptr&& master); + VideoProviderCache(std::unique_ptr master); ~VideoProviderCache(); - const AegiVideoFrame GetFrame(int n); + std::shared_ptr GetFrame(int n); int GetFrameCount() const { return master->GetFrameCount(); } int GetWidth() const { return master->GetWidth(); } diff --git a/aegisub/src/video_provider_dummy.cpp b/aegisub/src/video_provider_dummy.cpp index 8d6c28c09..bdbeaba6b 100644 --- a/aegisub/src/video_provider_dummy.cpp +++ b/aegisub/src/video_provider_dummy.cpp @@ -37,6 +37,7 @@ #include "video_provider_dummy.h" #include "colorspace.h" +#include "video_frame.h" #include #include @@ -46,18 +47,21 @@ #include #include #include +#include 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 DummyVideoProvider::GetFrame(int) { + return std::make_shared(data.data(), width, height, width * 4, false); +} diff --git a/aegisub/src/video_provider_dummy.h b/aegisub/src/video_provider_dummy.h index bf6161ba0..c2ae5defe 100644 --- a/aegisub/src/video_provider_dummy.h +++ b/aegisub/src/video_provider_dummy.h @@ -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 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 GetFrame(int n); + int GetFrameCount() const { return framecount; } int GetWidth() const { return width; } int GetHeight() const { return height; } diff --git a/aegisub/src/video_provider_ffmpegsource.cpp b/aegisub/src/video_provider_ffmpegsource.cpp index 5fa2254ad..9e54ebf6f 100644 --- a/aegisub/src/video_provider_ffmpegsource.cpp +++ b/aegisub/src/video_provider_ffmpegsource.cpp @@ -41,6 +41,7 @@ #include "options.h" #include "utils.h" #include "video_context.h" +#include "video_frame.h" #include @@ -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 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(frame->Data[0], Width, Height, frame->Linesize[0], false); } #endif /* WITH_FFMS2 */ diff --git a/aegisub/src/video_provider_ffmpegsource.h b/aegisub/src/video_provider_ffmpegsource.h index d99d716ba..96988d784 100644 --- a/aegisub/src/video_provider_ffmpegsource.h +++ b/aegisub/src/video_provider_ffmpegsource.h @@ -33,11 +33,8 @@ /// #ifdef WITH_FFMS2 -#include - #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 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 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 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 */ diff --git a/aegisub/src/video_provider_yuv4mpeg.cpp b/aegisub/src/video_provider_yuv4mpeg.cpp index 9b633a1fc..3ba023585 100644 --- a/aegisub/src/video_provider_yuv4mpeg.cpp +++ b/aegisub/src/video_provider_yuv4mpeg.cpp @@ -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 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 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(data.data(), w, h, w * 4, false); } diff --git a/aegisub/src/video_provider_yuv4mpeg.h b/aegisub/src/video_provider_yuv4mpeg.h index 4f9263467..5d8432686 100644 --- a/aegisub/src/video_provider_yuv4mpeg.h +++ b/aegisub/src/video_provider_yuv4mpeg.h @@ -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 GetFrame(int n); int GetFrameCount() const { return num_frames; } int GetWidth() const { return w; }