From faa7f798ff85e8b6e0fceb949b7475df4a56eb61 Mon Sep 17 00:00:00 2001 From: itsmattkc Date: Thu, 5 Sep 2019 01:15:50 +1000 Subject: [PATCH] more or less completed OIIO decoder --- app/config/config.h | 2 + app/decoder/decoder.cpp | 3 ++ app/decoder/ffmpeg/ffmpegdecoder.cpp | 2 +- app/decoder/oiio/oiiodecoder.cpp | 65 +++++++++++++++--------- app/decoder/oiio/oiiodecoder.h | 4 ++ app/project/item/footage/footage.cpp | 15 +++--- app/render/colorservice.cpp | 2 +- app/render/pixelformat.h | 4 +- app/render/pixelservice.cpp | 66 +++++++++++++++++++++---- app/render/pixelservice.h | 5 ++ app/widget/timelineview/tool/import.cpp | 13 ++++- 11 files changed, 134 insertions(+), 47 deletions(-) diff --git a/app/config/config.h b/app/config/config.h index dacf33969..d04c7a534 100644 --- a/app/config/config.h +++ b/app/config/config.h @@ -29,4 +29,6 @@ const olive::TimecodeDisplay kTimecodeDisplay = olive::kTimecodeFrames; +const rational kDefaultImageLength = 2; + #endif // CONFIG_H diff --git a/app/decoder/decoder.cpp b/app/decoder/decoder.cpp index 583a057c2..2df89cb01 100644 --- a/app/decoder/decoder.cpp +++ b/app/decoder/decoder.cpp @@ -62,6 +62,9 @@ void Decoder::set_stream(StreamPtr fs) QVector ReceiveListOfAllDecoders() { QVector decoders; + // The order in which these decoders are added is their priority when probing. Hence FFmpeg should usually be last, + // since it supports so many formats and we presumably want to override those formats with a more specific decoder. + decoders.append(std::make_shared()); decoders.append(std::make_shared()); diff --git a/app/decoder/ffmpeg/ffmpegdecoder.cpp b/app/decoder/ffmpeg/ffmpegdecoder.cpp index fc62a44df..795431783 100644 --- a/app/decoder/ffmpeg/ffmpegdecoder.cpp +++ b/app/decoder/ffmpeg/ffmpegdecoder.cpp @@ -142,7 +142,7 @@ bool FFmpegDecoder::Open() output_fmt_ = olive::PIX_FMT_RGBA8; break; case AV_PIX_FMT_RGBA64: - output_fmt_ = olive::PIX_FMT_RGBA16; + output_fmt_ = olive::PIX_FMT_RGBA16U; break; default: // We should never get here, but if we do there's nothing we can do with this format diff --git a/app/decoder/oiio/oiiodecoder.cpp b/app/decoder/oiio/oiiodecoder.cpp index df674f755..0b87bed77 100644 --- a/app/decoder/oiio/oiiodecoder.cpp +++ b/app/decoder/oiio/oiiodecoder.cpp @@ -22,7 +22,11 @@ #include -OIIODecoder::OIIODecoder() +#include "common/define.h" + +OIIODecoder::OIIODecoder() : + image_(nullptr), + frame_(nullptr) { } @@ -67,25 +71,24 @@ bool OIIODecoder::Open() width_ = spec.width; height_ = spec.height; - // Weirdly, compiler complains this is a boolean value without casting to int - switch (static_cast(spec.format)) { - case OIIO::TypeDesc::UINT8: + // Weirdly, switch statement doesn't work correctly here + if (spec.format == OIIO::TypeDesc::UINT8) { pix_fmt_ = olive::PIX_FMT_RGBA8; - break; - case OIIO::TypeDesc::UINT16: - pix_fmt_ = olive::PIX_FMT_RGBA16; - break; - case OIIO::TypeDesc::HALF: + } else if (spec.format == OIIO::TypeDesc::UINT16) { + pix_fmt_ = olive::PIX_FMT_RGBA16U; + } else if (spec.format == OIIO::TypeDesc::HALF) { pix_fmt_ = olive::PIX_FMT_RGBA16F; - break; - case OIIO::TypeDesc::FLOAT: + } else if (spec.format == OIIO::TypeDesc::FLOAT) { pix_fmt_ = olive::PIX_FMT_RGBA32F; - break; - default: + } else { qWarning() << "Failed to convert OIIO::ImageDesc to native pixel format"; return false; } + // FIXME: Many OIIO pixel formats are not handled here + + is_rgba_ = (spec.nchannels == kRGBAChannels); + pix_fmt_info_ = PixelService::GetPixelFormatInfo(static_cast(pix_fmt_)); return true; @@ -93,27 +96,41 @@ bool OIIODecoder::Open() FramePtr OIIODecoder::Retrieve(const rational &timecode, const rational &length) { + if (!open_ && !Open()) { + return nullptr; + } + Q_UNUSED(timecode) Q_UNUSED(length) - FramePtr f = Frame::Create(); + if (frame_ == nullptr) { + frame_ = Frame::Create(); - f->set_width(width_); - f->set_height(height_); - f->set_format(pix_fmt_); - f->allocate(); + frame_->set_width(width_); + frame_->set_height(height_); + frame_->set_format(pix_fmt_); + frame_->allocate(); - // Use the native format to determine what format OIIO should return - // FIXME: Behavior of RGB images as opposed to RGBA? - image_->read_image(pix_fmt_info_.oiio_desc, f->data()); + // Use the native format to determine what format OIIO should return + // FIXME: Behavior of RGB images as opposed to RGBA? + image_->read_image(pix_fmt_info_.oiio_desc, frame_->data()); - return f; + if (!is_rgba_) { + PixelService::ConvertRGBtoRGBA(frame_); + } + } + + return frame_; } void OIIODecoder::Close() { - image_->close(); - image_ = nullptr; + if (image_ != nullptr) { + image_->close(); + image_ = nullptr; + } + + frame_ = nullptr; } int64_t OIIODecoder::GetTimestampFromTime(const rational &time) diff --git a/app/decoder/oiio/oiiodecoder.h b/app/decoder/oiio/oiiodecoder.h index d062e7563..b4ffa8d99 100644 --- a/app/decoder/oiio/oiiodecoder.h +++ b/app/decoder/oiio/oiiodecoder.h @@ -54,6 +54,10 @@ private: PixelFormatInfo pix_fmt_info_; + bool is_rgba_; + + FramePtr frame_; + }; #endif // OIIODECODER_H diff --git a/app/project/item/footage/footage.cpp b/app/project/item/footage/footage.cpp index 706f79ede..495ad23cc 100644 --- a/app/project/item/footage/footage.cpp +++ b/app/project/item/footage/footage.cpp @@ -146,23 +146,22 @@ void Footage::UpdateIcon() // Prioritize the video icon set_icon(olive::icon::Video); - - // FIXME: When image sources can be reliably picked up, use image icon instead - // Perhaps all image sources can be left to OpenImageIO meaning only video sources need to be here + break; } else if (HasStreamsOfType(Stream::kAudio)) { // Otherwise assume it's audio only set_icon(olive::icon::Audio); + break; - } else { + } else if (HasStreamsOfType(Stream::kImage)) { - // FIXME Icon/indicator for a media file with no video or audio streams? - // The footage should probably be deemed kInvalid in this state + // Otherwise assume it's an image + set_icon(olive::icon::Image); + break; } - break; - case kInvalid: + [[clang::fallthrough]]; case kInvalid: set_icon(olive::icon::Error); break; } diff --git a/app/render/colorservice.cpp b/app/render/colorservice.cpp index 17d02db2b..63abbaa2d 100644 --- a/app/render/colorservice.cpp +++ b/app/render/colorservice.cpp @@ -66,7 +66,7 @@ void ColorService::AssociateAlphaPixFmtFilter(ColorService::AlphaAction action, qWarning() << "Alpha association functions received an invalid pixel format"; break; case olive::PIX_FMT_RGBA8: - case olive::PIX_FMT_RGBA16: + case olive::PIX_FMT_RGBA16U: qWarning() << "Alpha association functions only works on float-based pixel formats at this time"; break; case olive::PIX_FMT_RGBA16F: diff --git a/app/render/pixelformat.h b/app/render/pixelformat.h index 90fbee0b3..b8594da58 100644 --- a/app/render/pixelformat.h +++ b/app/render/pixelformat.h @@ -28,10 +28,12 @@ namespace olive { */ enum PixelFormat { PIX_FMT_INVALID = -1, + PIX_FMT_RGBA8, - PIX_FMT_RGBA16, + PIX_FMT_RGBA16U, PIX_FMT_RGBA16F, PIX_FMT_RGBA32F, + PIX_FMT_COUNT }; diff --git a/app/render/pixelservice.cpp b/app/render/pixelservice.cpp index b648cb9b9..50280863a 100644 --- a/app/render/pixelservice.cpp +++ b/app/render/pixelservice.cpp @@ -41,7 +41,7 @@ PixelFormatInfo PixelService::GetPixelFormatInfo(const olive::PixelFormat &forma info.pixel_type = GL_UNSIGNED_BYTE; info.oiio_desc = OIIO::TypeDesc::UINT8; break; - case olive::PIX_FMT_RGBA16: + case olive::PIX_FMT_RGBA16U: info.name = tr("16-bit Integer"); info.internal_format = GL_RGBA16; info.pixel_type = GL_UNSIGNED_SHORT; @@ -59,7 +59,8 @@ PixelFormatInfo PixelService::GetPixelFormatInfo(const olive::PixelFormat &forma info.pixel_type = GL_FLOAT; info.oiio_desc = OIIO::TypeDesc::FLOAT; break; - default: + case olive::PIX_FMT_INVALID: + case olive::PIX_FMT_COUNT: qFatal("Invalid pixel format requested"); } @@ -84,14 +85,17 @@ 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_RGBA16U: case olive::PIX_FMT_RGBA16F: return 2; case olive::PIX_FMT_RGBA32F: return 4; - default: - qFatal("Invalid pixel format requested"); + case olive::PIX_FMT_INVALID: + case olive::PIX_FMT_COUNT: + break; } + + qFatal("Invalid pixel format requested"); } FramePtr PixelService::ConvertPixelFormat(FramePtr frame, const olive::PixelFormat &dest_format) @@ -121,7 +125,7 @@ FramePtr PixelService::ConvertPixelFormat(FramePtr frame, const olive::PixelForm uint8_t* source = frame->data(); switch (dest_format) { - case olive::PIX_FMT_RGBA16: // 8-bit Integer -> 16-bit Integer + case olive::PIX_FMT_RGBA16U: // 8-bit Integer -> 16-bit Integer { uint16_t* destination = reinterpret_cast(converted->data()); for (int i=0;i(frame->data()); @@ -182,7 +186,7 @@ FramePtr PixelService::ConvertPixelFormat(FramePtr frame, const olive::PixelForm break; } case olive::PIX_FMT_INVALID: - case olive::PIX_FMT_RGBA16: + case olive::PIX_FMT_RGBA16U: case olive::PIX_FMT_COUNT: valid = false; } @@ -201,7 +205,7 @@ FramePtr PixelService::ConvertPixelFormat(FramePtr frame, const olive::PixelForm } break; } - case olive::PIX_FMT_RGBA16: // 16-bit Float -> 16-bit Integer + case olive::PIX_FMT_RGBA16U: // 16-bit Float -> 16-bit Integer { uint16_t* destination = reinterpret_cast(converted->data()); for (int i=0;i 16-bit Integer + case olive::PIX_FMT_RGBA16U: // 32-bit Float -> 16-bit Integer { uint16_t* destination = reinterpret_cast(converted->data()); for (int i=0;i(frame->format()); + + int rgb_pixel_size = BytesPerChannel(dest_format) * kRGBChannels; + int rgb_frame_size = frame->width() * frame->height() * rgb_pixel_size; + int rgb_iter = rgb_frame_size - rgb_pixel_size; + + int rgba_pixel_size = BytesPerChannel(dest_format) * kRGBAChannels; + int rgba_frame_size = frame->width() * frame->height() * rgba_pixel_size; + int rgba_iter = rgba_frame_size - rgba_pixel_size; + + // Work backwards to save time + while (rgb_iter >= 0) { + memcpy(&frame->data()[rgba_iter], &frame->data()[rgb_iter], static_cast(rgb_pixel_size)); + + uint8_t* alpha_ptr = &frame->data()[rgba_iter + rgb_pixel_size]; + + // Write a full alpha value according to the format + switch (dest_format) { + case olive::PIX_FMT_RGBA8: + *alpha_ptr = UINT8_MAX; + break; + case olive::PIX_FMT_RGBA16U: + *reinterpret_cast(alpha_ptr) = UINT16_MAX; + break; + case olive::PIX_FMT_RGBA16F: + *reinterpret_cast(alpha_ptr) = 1.0f; + break; + case olive::PIX_FMT_RGBA32F: + *reinterpret_cast(alpha_ptr) = 1.0f; + break; + case olive::PIX_FMT_INVALID: + case olive::PIX_FMT_COUNT: + qFatal("Invalid pixel format requested"); + } + + rgb_iter -= rgb_pixel_size; + rgba_iter -= rgba_pixel_size; + } +} diff --git a/app/render/pixelservice.h b/app/render/pixelservice.h index 6cb0f5102..b2bcd4773 100644 --- a/app/render/pixelservice.h +++ b/app/render/pixelservice.h @@ -97,6 +97,11 @@ public: */ static FramePtr ConvertPixelFormat(FramePtr frame, const olive::PixelFormat &dest_format); + /** + * @brief Convert an RGB image to an RGBA image + */ + static void ConvertRGBtoRGBA(FramePtr frame); + }; #endif // PIXELSERVICE_H diff --git a/app/widget/timelineview/tool/import.cpp b/app/widget/timelineview/tool/import.cpp index 6a924b8a2..ecc6f956d 100644 --- a/app/widget/timelineview/tool/import.cpp +++ b/app/widget/timelineview/tool/import.cpp @@ -22,6 +22,7 @@ #include +#include "config/config.h" #include "common/qtversionabstraction.h" #include "node/input/media/media.h" @@ -72,8 +73,16 @@ void TimelineView::ImportTool::DragEnter(QDragEnterEvent *event) TimelineViewGhostItem* ghost = new TimelineViewGhostItem(); - rational footage_duration(stream->timebase().numerator() * stream->duration(), - stream->timebase().denominator()); + rational footage_duration; + + if (stream->type() == Stream::kImage) { + // Stream is essentially length-less - use config's default image length + footage_duration = kDefaultImageLength; + } else { + // Use duration from file + footage_duration = rational(stream->timebase().numerator() * stream->duration(), + stream->timebase().denominator()); + } ghost->SetIn(ghost_start); ghost->SetOut(ghost_start + footage_duration);