From f55094c5dfd77aec44ba9162cd5f8ae7be335a8f Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Sun, 4 Aug 2019 19:45:06 +1000 Subject: [PATCH] media now plays in node graph --- app/decoder/decoder.h | 3 +- app/decoder/ffmpeg/ffmpegdecoder.cpp | 122 +++++++++++++++++++++++++-- app/decoder/ffmpeg/ffmpegdecoder.h | 16 +++- app/decoder/frame.cpp | 87 ++++++++++++------- app/decoder/frame.h | 49 +++++------ app/node/input/media/media.cpp | 32 ++----- app/node/input/media/media.h | 9 +- app/render/CMakeLists.txt | 3 + app/render/pixelformat.cpp | 63 -------------- app/render/pixelformat.h | 64 +------------- app/render/pixelservice.cpp | 89 +++++++++++++++++++ app/render/pixelservice.h | 93 ++++++++++++++++++++ app/render/sampleformat.h | 41 +++++++++ app/render/texturebuffer.cpp | 34 +++++++- app/render/texturebuffer.h | 8 +- 15 files changed, 484 insertions(+), 229 deletions(-) create mode 100644 app/render/pixelservice.cpp create mode 100644 app/render/pixelservice.h create mode 100644 app/render/sampleformat.h diff --git a/app/decoder/decoder.h b/app/decoder/decoder.h index 70378e29a..4fd823f78 100644 --- a/app/decoder/decoder.h +++ b/app/decoder/decoder.h @@ -48,6 +48,7 @@ class Decoder : public QObject { Q_OBJECT public: + Decoder(); Decoder(Stream* fs); @@ -142,8 +143,6 @@ public: */ //virtual void Analyze();// = 0; - - protected: bool open_; diff --git a/app/decoder/ffmpeg/ffmpegdecoder.cpp b/app/decoder/ffmpeg/ffmpegdecoder.cpp index 160259361..6fe0ee740 100644 --- a/app/decoder/ffmpeg/ffmpegdecoder.cpp +++ b/app/decoder/ffmpeg/ffmpegdecoder.cpp @@ -30,10 +30,14 @@ extern "C" { #include #include +#include "render/pixelservice.h" + FFmpegDecoder::FFmpegDecoder() : fmt_ctx_(nullptr), codec_ctx_(nullptr), - opts_(nullptr) + opts_(nullptr), + scale_ctx_(nullptr), + resample_ctx_(nullptr) { } @@ -115,6 +119,43 @@ bool FFmpegDecoder::Open() return false; } + // Set up + if (codec_ctx_->codec_type == AVMEDIA_TYPE_VIDEO) { + // Set up pixel format conversion for video + AVPixelFormat pix_fmt = static_cast(avstream_->codecpar->format); + + // Get an Olive compatible AVPixelFormat + AVPixelFormat ideal_pix_fmt = GetCompatiblePixelFormat(pix_fmt); + + // Determine which Olive native pixel format we retrieved + // Note that FFmpeg doesn't support float formats + switch (ideal_pix_fmt) { + case AV_PIX_FMT_RGBA: + output_fmt_ = olive::PIX_FMT_RGBA8; + break; + case AV_PIX_FMT_RGBA64: + output_fmt_ = olive::PIX_FMT_RGBA16; + break; + default: + // We should never get here, but if we do there's nothing we can do with this format + return false; + } + + scale_ctx_ = sws_getContext(avstream_->codecpar->width, + avstream_->codecpar->height, + pix_fmt, + avstream_->codecpar->width, + avstream_->codecpar->height, + ideal_pix_fmt, + 0, + nullptr, + nullptr, + nullptr); + + } else if (codec_ctx_->codec_type == AVMEDIA_TYPE_AUDIO) { + // FIXME: Fill this in + } + open_ = true; return true; @@ -129,15 +170,31 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt // avcodec_flush_buffers(codec_ctx_); // av_seek_frame(fmt_ctx_, avstream_->index, 0, AVSEEK_FLAG_BACKWARD); - AVFrame* frame = av_frame_alloc(); + // Allocate and init a packet for reading encoded data AVPacket pkt; av_init_packet(&pkt); - int ret = 69; + // Allocate a new frame to place decoded data + AVFrame* dec_frame = av_frame_alloc(); + + // Cache FFmpeg error code returns + int ret; + + // Variable set if FFmpeg signals file has finished bool eof = false; - while ((ret = avcodec_receive_frame(codec_ctx_, frame)) == AVERROR(EAGAIN) && !eof) { - ret = av_read_frame(fmt_ctx_, &pkt); + // FFmpeg frame retrieve loop + while ((ret = avcodec_receive_frame(codec_ctx_, dec_frame)) == AVERROR(EAGAIN) && !eof) { + + // Find next packet in the correct stream index + do { + // Free buffer in packet if there is one + if (pkt.buf != nullptr) { + av_packet_unref(&pkt); + } + + ret = av_read_frame(fmt_ctx_, &pkt); + } while (pkt.stream_index != avstream_->index && ret >= 0); if (ret == AVERROR_EOF) { // Don't break so that receive gets called again, but don't try to read again @@ -151,6 +208,9 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt } else { // Successful read, send the packet ret = avcodec_send_packet(codec_ctx_, &pkt); + + // We don't need the packet anymore, so free it + // FIXME: Is this true??? av_packet_unref(&pkt); if (ret < 0) { @@ -159,25 +219,57 @@ FramePtr FFmpegDecoder::Retrieve(const rational &timecode, const rational &lengt } } + // Handle any errors received during the frame retrieve process if (ret < 0) { qWarning() << tr("Failed to retrieve frame from FFmpeg decoder: %1").arg(ret); - av_frame_free(&frame); + av_frame_free(&dec_frame); return nullptr; } + // Frame was valid, now we create an Olive frame to place the data into FramePtr frame_container = std::make_shared(); - frame_container->SetAVFrame(frame, avstream_->time_base); + frame_container->set_width(dec_frame->width); + frame_container->set_height(dec_frame->height); + frame_container->set_format(output_fmt_); // FIXME: Hardcoded value + frame_container->set_timestamp(rational(dec_frame->pts * avstream_->time_base.num, avstream_->time_base.den)); + frame_container->allocate(); + + // Convert pixel format/linesize if necessary + uint8_t* dst_data = frame_container->data(); + int dst_linesize = frame_container->width() * 4; + + // Perform pixel conversion + sws_scale(scale_ctx_, + dec_frame->data, + dec_frame->linesize, + 0, + dec_frame->height, + &dst_data, + &dst_linesize); + + // Don't need AVFrame anymore + av_frame_free(&dec_frame); Q_UNUSED(timecode) Q_UNUSED(length) - // Close(); +// Close(); return frame_container; } void FFmpegDecoder::Close() { + if (scale_ctx_ != nullptr) { + sws_freeContext(scale_ctx_); + scale_ctx_ = nullptr; + } + + if (resample_ctx_ != nullptr) { + swr_free(&resample_ctx_); + resample_ctx_ = nullptr; + } + if (opts_ != nullptr) { av_dict_free(&opts_); opts_ = nullptr; @@ -309,3 +401,17 @@ void FFmpegDecoder::Error(const QString &s) Close(); } + +AVPixelFormat FFmpegDecoder::GetCompatiblePixelFormat(const AVPixelFormat &pix_fmt) +{ + AVPixelFormat possible_pix_fmts[] = { + AV_PIX_FMT_RGBA, + AV_PIX_FMT_RGBA64, + AV_PIX_FMT_NONE + }; + + return avcodec_find_best_pix_fmt_of_list(possible_pix_fmts, + pix_fmt, + 1, + nullptr); +} diff --git a/app/decoder/ffmpeg/ffmpegdecoder.h b/app/decoder/ffmpeg/ffmpegdecoder.h index 46b3fc9cc..3c84e1041 100644 --- a/app/decoder/ffmpeg/ffmpegdecoder.h +++ b/app/decoder/ffmpeg/ffmpegdecoder.h @@ -21,6 +21,12 @@ #ifndef FFMPEGDECODER_H #define FFMPEGDECODER_H +extern "C" { +#include +#include +#include +} + #include #include "decoder/decoder.h" @@ -39,17 +45,21 @@ public: virtual FramePtr Retrieve(const rational &timecode, const rational &length = 0) override; virtual void Close() override; -protected: +private: void FFmpegErr(int error_code); void Error(const QString& s); + AVPixelFormat GetCompatiblePixelFormat(const AVPixelFormat& pix_fmt); + AVFormatContext* fmt_ctx_; AVCodecContext* codec_ctx_; AVStream* avstream_; - AVPacket* pkt_; - AVFrame* frame_; AVDictionary* opts_; + SwsContext* scale_ctx_; + SwrContext* resample_ctx_; + int output_fmt_; + QVector frame_index_; }; diff --git a/app/decoder/frame.cpp b/app/decoder/frame.cpp index 031c287b5..464566491 100644 --- a/app/decoder/frame.cpp +++ b/app/decoder/frame.cpp @@ -23,41 +23,40 @@ #include #include -Frame::Frame() : - frame_(nullptr) -{ -} +#include "render/pixelservice.h" -Frame::Frame(AVFrame *f) : - frame_(f) +Frame::Frame() : + width_(0), + height_(0), + data_(nullptr) { } Frame::Frame(const Frame &f) : - frame_(nullptr) + data_(nullptr) { Q_UNUSED(f) } Frame::Frame(Frame &&f) : - frame_(f.frame_) + data_(f.data_) { - f.frame_ = nullptr; + f.data_ = nullptr; } Frame &Frame::operator=(const Frame &f) { Q_UNUSED(f) - frame_ = nullptr; + data_ = nullptr; return *this; } Frame &Frame::operator=(Frame &&f) { if (&f != this) { - frame_ = f.frame_; - f.frame_ = nullptr; + data_ = f.data_; + f.data_ = nullptr; } return *this; @@ -65,25 +64,27 @@ Frame &Frame::operator=(Frame &&f) Frame::~Frame() { - FreeChild(); -} - -void Frame::SetAVFrame(AVFrame *f, AVRational timebase) -{ - FreeChild(); - - frame_ = f; - timestamp_ = rational(timebase.num*f->pts, timebase.den); + destroy(); } const int &Frame::width() { - return frame_->width; + return width_; +} + +void Frame::set_width(const int &width) +{ + width_ = width; } const int &Frame::height() { - return frame_->height; + return height_; +} + +void Frame::set_height(const int &height) +{ + height_ = height; } const rational &Frame::timestamp() @@ -91,25 +92,47 @@ const rational &Frame::timestamp() return timestamp_; } +void Frame::set_timestamp(const rational ×tamp) +{ + timestamp_ = timestamp; +} + const int &Frame::format() { - return frame_->format; + return format_; } -uint8_t **Frame::data() +void Frame::set_format(const int &format) { - return frame_->data; + format_ = format; } -int *Frame::linesize() +uint8_t *Frame::data() { - return frame_->linesize; + return data_; } -void Frame::FreeChild() +const uint8_t *Frame::const_data() { - if (frame_ != nullptr) { - av_frame_free(&frame_); - frame_ = nullptr; + return data_; +} + +void Frame::allocate() +{ + if (data_ != nullptr) { + destroy(); } + + // Assume this frame is intended to be a video frame + if (width_ > 0 && height_ > 0) { + data_ = new uint8_t[PixelService::GetBufferSize(static_cast(format_), width_, height_)]; + } + + // FIXME: Audio sample allocation +} + +void Frame::destroy() +{ + delete [] data_; + data_ = nullptr; } diff --git a/app/decoder/frame.h b/app/decoder/frame.h index 6992ebbac..44149e5c0 100644 --- a/app/decoder/frame.h +++ b/app/decoder/frame.h @@ -21,13 +21,10 @@ #ifndef FRAME_H #define FRAME_H -extern "C" { -#include -} - #include #include "common/rational.h" +#include "render/pixelformat.h" /** * @brief Video frame data or audio sample data from a Decoder @@ -39,17 +36,9 @@ extern "C" { class Frame { public: - enum Type { - kNative, - kAVFrame - }; - /// Normal constructor Frame(); - /// AVFrame constructor - Frame(AVFrame* f); - /// Copy constructor Frame(const Frame& f); @@ -65,26 +54,17 @@ public: /// Destructor ~Frame(); - /** - * @brief Set frame child - * - * This class currently primarily functions as a wrapper for AVFrame for use outside of the Decoder classes. - * The internal AVFrame is set here. This class will also take ownership of the AVFrame and automatically - * clear it when deconstructed. - * - * @param f - */ - void SetAVFrame(AVFrame* f, AVRational timebase); - /** * @brief Get frame's width in pixels */ const int& width(); + void set_width(const int& width); /** * @brief Get frame's height in pixels */ const int& height(); + void set_height(const int& height); /** * @brief Get frame's timestamp. @@ -92,32 +72,43 @@ public: * This timestamp is always a rational that will equate to the time in seconds. */ const rational& timestamp(); + void set_timestamp(const rational& timestamp); /** * @brief Get frame's format * * @return * - * Currently this will either be an AVPixelFormat (video) or an AVSampleFormat (audio). + * Currently this will either be an olive::PixelFormat (video) or an olive::SampleFormat (audio). */ const int& format(); + void set_format(const int& format); /** * @brief Get the data buffer of this frame */ - uint8_t** data(); + uint8_t* data(); /** - * @brief Get the linesize information for this frame + * @brief Get the const data buffer of this frame */ - int* linesize(); + const uint8_t* const_data(); + + void allocate(); + + void destroy(); private: + int width_; - void FreeChild(); + int height_; + + int format_; + + uint8_t* data_; - AVFrame* frame_; rational timestamp_; + }; using FramePtr = std::shared_ptr; diff --git a/app/node/input/media/media.cpp b/app/node/input/media/media.cpp index 5eabc699b..e8a8c982c 100644 --- a/app/node/input/media/media.cpp +++ b/app/node/input/media/media.cpp @@ -27,10 +27,10 @@ // FIXME: Test code only #include "decoder/ffmpeg/ffmpegdecoder.h" +#include "render/pixelservice.h" // End test code -MediaInput::MediaInput() : - texture_(0) +MediaInput::MediaInput() { footage_input_ = new NodeInput(); footage_input_->add_data_input(NodeInput::kFootage); @@ -103,30 +103,12 @@ void MediaInput::Process(const rational &time) } // FIXME: Test code - if (texture_ == 0) { - glGenTextures(1, &texture_); - - glBindTexture(GL_TEXTURE_2D, texture_); - - // Set texture filtering to bilinear - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - // Set texture wrapping to clamp - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - glTexImage2D(GL_TEXTURE_2D, - 0, - GL_RGBA8, - frame->width(), - frame->height(), - 0, - GL_RGBA, - GL_UNSIGNED_BYTE, - frame->data()[0]); + if (tex_buf_.IsCreated()) { + tex_buf_.Upload(frame->data()); + } else { + tex_buf_.Create(QOpenGLContext::currentContext(), static_cast(frame->format()), frame->width(), frame->height(), frame->data()); } - texture_output_->set_value(texture_); + texture_output_->set_value(tex_buf_.texture()); // End test code } diff --git a/app/node/input/media/media.h b/app/node/input/media/media.h index f312bfce4..dd5065671 100644 --- a/app/node/input/media/media.h +++ b/app/node/input/media/media.h @@ -26,6 +26,10 @@ #include "decoder/decoder.h" #include "node/node.h" +// FIXME: Test code only +#include "render/texturebuffer.h" +// End test code + /** * @brief A node that imports an image * @@ -53,9 +57,12 @@ private: NodeOutput* texture_output_; - GLuint texture_; + // FIXME: TEST CODE ONLY + TextureBuffer tex_buf_; + // END TEST CODE Decoder* decoder_; + }; #endif // IMAGE_H diff --git a/app/render/CMakeLists.txt b/app/render/CMakeLists.txt index 3161ab4ff..e0b5f9616 100644 --- a/app/render/CMakeLists.txt +++ b/app/render/CMakeLists.txt @@ -24,6 +24,9 @@ set(OLIVE_SOURCES #render/memorybuffer.cpp render/pixelformat.h render/pixelformat.cpp + render/pixelservice.h + render/pixelservice.cpp + render/sampleformat.h render/texturebuffer.h render/texturebuffer.cpp PARENT_SCOPE diff --git a/app/render/pixelformat.cpp b/app/render/pixelformat.cpp index 8299ba374..4076ae21f 100644 --- a/app/render/pixelformat.cpp +++ b/app/render/pixelformat.cpp @@ -19,66 +19,3 @@ ***/ #include "pixelformat.h" - -#include - -const int kRGBAChannels = 4; - -PixelService::PixelService() -{ -} - -PixelFormatInfo PixelService::GetPixelFormatInfo(const olive::PixelFormat &format) -{ - PixelFormatInfo info; - - switch (format) { - case olive::PIX_FMT_RGBA8: - info.name = tr("8-bit"); - info.internal_format = GL_RGBA8; - info.pixel_type = GL_UNSIGNED_BYTE; - break; - case olive::PIX_FMT_RGBA16: - info.name = tr("16-bit Integer"); - info.internal_format = GL_RGBA16; - info.pixel_type = GL_UNSIGNED_SHORT; - break; - case olive::PIX_FMT_RGBA16F: - info.name = tr("Half-Float (16-bit)"); - info.internal_format = GL_RGBA16F; - info.pixel_type = GL_HALF_FLOAT; - break; - case olive::PIX_FMT_RGBA32F: - info.name = tr("Full-Float (32-bit)"); - info.internal_format = GL_RGBA32F; - info.pixel_type = GL_FLOAT; - break; - default: - qFatal("Invalid pixel format requested"); - } - - info.pixel_format = GL_RGBA; - info.bytes_per_pixel = BytesPerPixel(format); - - return info; -} - -int PixelService::GetBufferSize(const olive::PixelFormat &format, const int &width, const int &height) -{ - return BytesPerPixel(format) * width * height; -} - -int PixelService::BytesPerPixel(const olive::PixelFormat &format) -{ - switch (format) { - case olive::PIX_FMT_RGBA8: - return 1 * kRGBAChannels; - case olive::PIX_FMT_RGBA16: - case olive::PIX_FMT_RGBA16F: - return 2 * kRGBAChannels; - case olive::PIX_FMT_RGBA32F: - return 4 * kRGBAChannels; - default: - qFatal("Invalid pixel format requested"); - } -} diff --git a/app/render/pixelformat.h b/app/render/pixelformat.h index c1607fa94..90fbee0b3 100644 --- a/app/render/pixelformat.h +++ b/app/render/pixelformat.h @@ -21,34 +21,13 @@ #ifndef BITDEPTHS_H #define BITDEPTHS_H -#include -#include -#include - -/** - * @brief A struct of information pertaining to each enum PixelFormat. - * - * Primarily this is a means of retrieving OpenGL texture information for different pixel formats/bit depths. Both - * RAM and VRAM buffers will need a PixelFormat. To keep consistency between the OpenGL code and CPU code when using - * a given PixelFormat, the PixelFormatInfo struct contains all necessary variables that you'll need to plug into - * OpenGL. - * - * Use the static function PixelService::GetPixelFormatInfo to generate a PixelFormatInfo object. - */ -struct PixelFormatInfo { - QString name; - GLint internal_format; - GLenum pixel_format; - GLenum pixel_type; - int bytes_per_pixel; -}; - namespace olive { /** * @brief Olive's internal supported pixel formats. */ enum PixelFormat { + PIX_FMT_INVALID = -1, PIX_FMT_RGBA8, PIX_FMT_RGBA16, PIX_FMT_RGBA16F, @@ -58,45 +37,4 @@ enum PixelFormat { } -class PixelService : public QObject { -public: - - PixelService(); - - /** - * @brief Return a PixelFormatInfo containing information for a certain format - * - * \see PixelFormatInfo - */ - static PixelFormatInfo GetPixelFormatInfo(const olive::PixelFormat& format); - - /** - * @brief Returns the minimum buffer size (in bytes) necessary for a given format, width, and height. - * - * @param format - * - * The format of the data the buffer should contain. Must be a member of the olive::PixelFormat enum. - * - * @param width - * - * The width (in pixels) of the buffer. - * - * @param height - * - * The height (in pixels) of the buffer. - */ - static int GetBufferSize(const olive::PixelFormat &format, const int& width, const int& height); - - /** - * @brief Returns the number of bytes per pixel for a certain format - * - * Different formats use different sizes of data for pixels. Use this function to determine how many bytes a pixel - * requires for a certain format. The number of bytes will always be a multiple of 4 since all formats use RGBA and - * are at least 1 bpc. - */ - static int BytesPerPixel(const olive::PixelFormat& format); - -private: -}; - #endif // BITDEPTHS_H diff --git a/app/render/pixelservice.cpp b/app/render/pixelservice.cpp new file mode 100644 index 000000000..b73116e11 --- /dev/null +++ b/app/render/pixelservice.cpp @@ -0,0 +1,89 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#include "pixelservice.h" + +#include + +const int kRGBAChannels = 4; + +PixelService::PixelService() +{ +} + +PixelFormatInfo PixelService::GetPixelFormatInfo(const olive::PixelFormat &format) +{ + PixelFormatInfo info; + + switch (format) { + case olive::PIX_FMT_RGBA8: + info.name = tr("8-bit"); + info.internal_format = GL_RGBA8; + info.pixel_type = GL_UNSIGNED_BYTE; + break; + case olive::PIX_FMT_RGBA16: + info.name = tr("16-bit Integer"); + info.internal_format = GL_RGBA16; + info.pixel_type = GL_UNSIGNED_SHORT; + break; + case olive::PIX_FMT_RGBA16F: + info.name = tr("Half-Float (16-bit)"); + info.internal_format = GL_RGBA16F; + info.pixel_type = GL_HALF_FLOAT; + break; + case olive::PIX_FMT_RGBA32F: + info.name = tr("Full-Float (32-bit)"); + info.internal_format = GL_RGBA32F; + info.pixel_type = GL_FLOAT; + break; + default: + qFatal("Invalid pixel format requested"); + } + + info.pixel_format = GL_RGBA; + info.bytes_per_pixel = BytesPerPixel(format); + + return info; +} + +int PixelService::GetBufferSize(const olive::PixelFormat &format, const int &width, const int &height) +{ + return BytesPerPixel(format) * width * height; +} + +int PixelService::BytesPerPixel(const olive::PixelFormat &format) +{ + return BytesPerChannel(format) * kRGBAChannels; +} + +int PixelService::BytesPerChannel(const olive::PixelFormat &format) +{ + switch (format) { + case olive::PIX_FMT_RGBA8: + return 1; + case olive::PIX_FMT_RGBA16: + case olive::PIX_FMT_RGBA16F: + return 2; + case olive::PIX_FMT_RGBA32F: + return 4; + default: + qFatal("Invalid pixel format requested"); + } +} diff --git a/app/render/pixelservice.h b/app/render/pixelservice.h new file mode 100644 index 000000000..c5145d9e1 --- /dev/null +++ b/app/render/pixelservice.h @@ -0,0 +1,93 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef PIXELSERVICE_H +#define PIXELSERVICE_H + +#include +#include + +#include "pixelformat.h" + +/** + * @brief A struct of information pertaining to each enum PixelFormat. + * + * Primarily this is a means of retrieving OpenGL texture information for different pixel formats/bit depths. Both + * RAM and VRAM buffers will need a PixelFormat. To keep consistency between the OpenGL code and CPU code when using + * a given PixelFormat, the PixelFormatInfo struct contains all necessary variables that you'll need to plug into + * OpenGL. + * + * Use the static function PixelService::GetPixelFormatInfo to generate a PixelFormatInfo object. + */ +struct PixelFormatInfo { + QString name; + GLint internal_format; + GLenum pixel_format; + GLenum pixel_type; + int bytes_per_pixel; +}; + +class PixelService : public QObject { +public: + + PixelService(); + + /** + * @brief Return a PixelFormatInfo containing information for a certain format + * + * \see PixelFormatInfo + */ + static PixelFormatInfo GetPixelFormatInfo(const olive::PixelFormat& format); + + /** + * @brief Returns the minimum buffer size (in bytes) necessary for a given format, width, and height. + * + * @param format + * + * The format of the data the buffer should contain. Must be a member of the olive::PixelFormat enum. + * + * @param width + * + * The width (in pixels) of the buffer. + * + * @param height + * + * The height (in pixels) of the buffer. + */ + static int GetBufferSize(const olive::PixelFormat &format, const int& width, const int& height); + + /** + * @brief Returns the number of bytes per pixel for a certain format + * + * Different formats use different sizes of data for pixels. Use this function to determine how many bytes a pixel + * requires for a certain format. The number of bytes will always be a multiple of 4 since all formats use RGBA and + * are at least 1 bpc. + */ + static int BytesPerPixel(const olive::PixelFormat& format); + + /** + * @brief Returns the number of bytes per channel for a certain format + */ + static int BytesPerChannel(const olive::PixelFormat& format); + +private: +}; + +#endif // PIXELSERVICE_H diff --git a/app/render/sampleformat.h b/app/render/sampleformat.h new file mode 100644 index 000000000..6d5951866 --- /dev/null +++ b/app/render/sampleformat.h @@ -0,0 +1,41 @@ +/*** + + Olive - Non-Linear Video Editor + Copyright (C) 2019 Olive Team + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +***/ + +#ifndef SAMPLEFORMAT_H +#define SAMPLEFORMAT_H + +namespace olive { + +/** + * @brief Olive's internal supported sample formats + */ +enum SampleFormat { + SAMPLE_FMT_INVALID = -1, + SAMPLE_FMT_U8, + SAMPLE_FMT_S16, + SAMPLE_FMT_S32, + SAMPLE_FMT_FLT, + SAMPLE_FMT_DBL, + SAMPLE_FMT_COUNT +}; + +} + +#endif // SAMPLEFORMAT_H diff --git a/app/render/texturebuffer.cpp b/app/render/texturebuffer.cpp index 44bf75b1b..dc5418ddc 100644 --- a/app/render/texturebuffer.cpp +++ b/app/render/texturebuffer.cpp @@ -23,6 +23,8 @@ #include #include +#include "render/pixelservice.h" + TextureBuffer::TextureBuffer() : ctx_(nullptr), buffer_(0), @@ -39,7 +41,7 @@ bool TextureBuffer::IsCreated() return (ctx_ != nullptr); } -void TextureBuffer::Create(QOpenGLContext *ctx, const olive::PixelFormat &format, int width, int height) +void TextureBuffer::Create(QOpenGLContext *ctx, const olive::PixelFormat &format, int width, int height, void* data) { // free any previous textures Destroy(); @@ -47,6 +49,11 @@ void TextureBuffer::Create(QOpenGLContext *ctx, const olive::PixelFormat &format // set context to new context provided ctx_ = ctx; + // Store frame metadata + width_ = width; + height_ = height; + format_ = format; + QOpenGLFunctions* f = ctx->functions(); // create framebuffer object @@ -73,7 +80,7 @@ void TextureBuffer::Create(QOpenGLContext *ctx, const olive::PixelFormat &format 0, bit_depth.pixel_format, bit_depth.pixel_type, - nullptr + data ); // set texture filtering to bilinear @@ -107,6 +114,29 @@ void TextureBuffer::Destroy() } } +void TextureBuffer::Upload(void *data) +{ + if (!IsCreated()) { + return; + } + + BindTexture(); + + PixelFormatInfo info = PixelService::GetPixelFormatInfo(format_); + + glTexSubImage2D(GL_TEXTURE_2D, + 0, + 0, + 0, + width_, + height_, + info.pixel_format, + info.pixel_type, + data); + + ReleaseTexture(); +} + void TextureBuffer::BindBuffer() const { if (ctx_ == nullptr) { diff --git a/app/render/texturebuffer.h b/app/render/texturebuffer.h index 550ffe122..36fd458da 100644 --- a/app/render/texturebuffer.h +++ b/app/render/texturebuffer.h @@ -40,9 +40,11 @@ public: ~TextureBuffer(); bool IsCreated(); - void Create(QOpenGLContext* ctx, const olive::PixelFormat& format, int width, int height); + void Create(QOpenGLContext* ctx, const olive::PixelFormat& format, int width, int height, void *data = nullptr); void Destroy(); + void Upload(void *data); + const GLuint& buffer() const; const GLuint& texture() const; @@ -55,6 +57,10 @@ private: QOpenGLContext* ctx_; GLuint buffer_; GLuint texture_; + + int width_; + int height_; + olive::PixelFormat format_; }; #endif // FRAMEBUFFEROBJECT_H