Add alpha channel to VideoFrameBuffer containers
- Add alpha accessors to PlanarYuvBuffer interface, null by defualt.
- Add WrapI420ABuffer() that creates a container which implements these
accessors.
- Show the use via StereoDecoderAdapter.
This CL is the step 2 for adding alpha channel support over the wire in webrtc.
See https://webrtc-review.googlesource.com/c/src/+/7800 for the experimental
CL that gives an idea about how it will come together.
Design Doc: https://goo.gl/sFeSUT
Bug: webrtc:7671
Change-Id: Id5691cde00088ec811b63d89080d33ad2d6e3939
Reviewed-on: https://webrtc-review.googlesource.com/21130
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Commit-Queue: Emircan Uysaler <emircan@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20635}
diff --git a/api/video/video_frame_buffer.cc b/api/video/video_frame_buffer.cc
index dd869f7..b231745 100644
--- a/api/video/video_frame_buffer.cc
+++ b/api/video/video_frame_buffer.cc
@@ -27,6 +27,16 @@
return static_cast<const I420BufferInterface*>(this);
}
+I420ABufferInterface* VideoFrameBuffer::GetI420A() {
+ RTC_CHECK(type() == Type::kI420A);
+ return static_cast<I420ABufferInterface*>(this);
+}
+
+const I420ABufferInterface* VideoFrameBuffer::GetI420A() const {
+ RTC_CHECK(type() == Type::kI420A);
+ return static_cast<const I420ABufferInterface*>(this);
+}
+
I444BufferInterface* VideoFrameBuffer::GetI444() {
RTC_CHECK(type() == Type::kI444);
return static_cast<I444BufferInterface*>(this);
@@ -53,6 +63,10 @@
return this;
}
+VideoFrameBuffer::Type I420ABufferInterface::type() const {
+ return Type::kI420A;
+}
+
VideoFrameBuffer::Type I444BufferInterface::type() const {
return Type::kI444;
}
diff --git a/api/video/video_frame_buffer.h b/api/video/video_frame_buffer.h
index c8d02a1..e656edd 100644
--- a/api/video/video_frame_buffer.h
+++ b/api/video/video_frame_buffer.h
@@ -19,6 +19,7 @@
namespace webrtc {
class I420BufferInterface;
+class I420ABufferInterface;
class I444BufferInterface;
// Base class for frame buffers of different types of pixel format and storage.
@@ -44,6 +45,7 @@
enum class Type {
kNative,
kI420,
+ kI420A,
kI444,
};
@@ -67,6 +69,8 @@
// removed.
rtc::scoped_refptr<I420BufferInterface> GetI420();
rtc::scoped_refptr<const I420BufferInterface> GetI420() const;
+ I420ABufferInterface* GetI420A();
+ const I420ABufferInterface* GetI420A() const;
I444BufferInterface* GetI444();
const I444BufferInterface* GetI444() const;
@@ -97,7 +101,7 @@
class I420BufferInterface : public PlanarYuvBuffer {
public:
- Type type() const final;
+ Type type() const override;
int ChromaWidth() const final;
int ChromaHeight() const final;
@@ -108,6 +112,16 @@
~I420BufferInterface() override {}
};
+class I420ABufferInterface : public I420BufferInterface {
+ public:
+ Type type() const final;
+ virtual const uint8_t* DataA() const = 0;
+ virtual int StrideA() const = 0;
+
+ protected:
+ ~I420ABufferInterface() override {}
+};
+
class I444BufferInterface : public PlanarYuvBuffer {
public:
Type type() const final;
diff --git a/common_video/include/video_frame_buffer.h b/common_video/include/video_frame_buffer.h
index bc9d4f9..312979b 100644
--- a/common_video/include/video_frame_buffer.h
+++ b/common_video/include/video_frame_buffer.h
@@ -78,6 +78,19 @@
int v_stride,
const rtc::Callback0<void>& no_longer_used);
+rtc::scoped_refptr<I420ABufferInterface> WrapI420ABuffer(
+ int width,
+ int height,
+ const uint8_t* y_plane,
+ int y_stride,
+ const uint8_t* u_plane,
+ int u_stride,
+ const uint8_t* v_plane,
+ int v_stride,
+ const uint8_t* a_plane,
+ int a_stride,
+ const rtc::Callback0<void>& no_longer_used);
+
rtc::scoped_refptr<PlanarYuvBuffer> WrapYuvBuffer(
VideoFrameBuffer::Type type,
int width,
diff --git a/common_video/video_frame_buffer.cc b/common_video/video_frame_buffer.cc
index 19649cf..9c8955c 100644
--- a/common_video/video_frame_buffer.cc
+++ b/common_video/video_frame_buffer.cc
@@ -96,6 +96,8 @@
v_stride_(v_stride),
no_longer_used_cb_(no_longer_used) {}
+ ~WrappedYuvBuffer() override { no_longer_used_cb_(); }
+
int width() const override { return width_; }
int height() const override { return height_; }
@@ -115,8 +117,6 @@
private:
friend class rtc::RefCountedObject<WrappedYuvBuffer>;
- ~WrappedYuvBuffer() override { no_longer_used_cb_(); }
-
const int width_;
const int height_;
const uint8_t* const y_plane_;
@@ -128,6 +128,41 @@
rtc::Callback0<void> no_longer_used_cb_;
};
+// Template to implement a wrapped buffer for a I4??BufferInterface.
+template <typename BaseWithA>
+class WrappedYuvaBuffer : public WrappedYuvBuffer<BaseWithA> {
+ public:
+ WrappedYuvaBuffer(int width,
+ int height,
+ const uint8_t* y_plane,
+ int y_stride,
+ const uint8_t* u_plane,
+ int u_stride,
+ const uint8_t* v_plane,
+ int v_stride,
+ const uint8_t* a_plane,
+ int a_stride,
+ const rtc::Callback0<void>& no_longer_used)
+ : WrappedYuvBuffer<BaseWithA>(width,
+ height,
+ y_plane,
+ y_stride,
+ u_plane,
+ u_stride,
+ v_plane,
+ v_stride,
+ no_longer_used),
+ a_plane_(a_plane),
+ a_stride_(a_stride) {}
+
+ const uint8_t* DataA() const override { return a_plane_; }
+ int StrideA() const override { return a_stride_; }
+
+ private:
+ const uint8_t* const a_plane_;
+ const int a_stride_;
+};
+
rtc::scoped_refptr<I420BufferInterface> WrapI420Buffer(
int width,
int height,
@@ -144,6 +179,24 @@
v_stride, no_longer_used));
}
+rtc::scoped_refptr<I420ABufferInterface> WrapI420ABuffer(
+ int width,
+ int height,
+ const uint8_t* y_plane,
+ int y_stride,
+ const uint8_t* u_plane,
+ int u_stride,
+ const uint8_t* v_plane,
+ int v_stride,
+ const uint8_t* a_plane,
+ int a_stride,
+ const rtc::Callback0<void>& no_longer_used) {
+ return rtc::scoped_refptr<I420ABufferInterface>(
+ new rtc::RefCountedObject<WrappedYuvaBuffer<I420ABufferInterface>>(
+ width, height, y_plane, y_stride, u_plane, u_stride, v_plane,
+ v_stride, a_plane, a_stride, no_longer_used));
+}
+
rtc::scoped_refptr<I444BufferInterface> WrapI444Buffer(
int width,
int height,
diff --git a/modules/video_coding/codecs/stereo/stereo_decoder_adapter.cc b/modules/video_coding/codecs/stereo/stereo_decoder_adapter.cc
index 5f8b4c59..82e87d4 100644
--- a/modules/video_coding/codecs/stereo/stereo_decoder_adapter.cc
+++ b/modules/video_coding/codecs/stereo/stereo_decoder_adapter.cc
@@ -11,6 +11,7 @@
#include "modules/video_coding/codecs/stereo/include/stereo_decoder_adapter.h"
#include "api/video/i420_buffer.h"
+#include "api/video/video_frame_buffer.h"
#include "api/video_codecs/sdp_video_format.h"
#include "common_video/include/video_frame.h"
#include "common_video/include/video_frame_buffer.h"
@@ -18,6 +19,11 @@
#include "rtc_base/keep_ref_until_done.h"
#include "rtc_base/logging.h"
+namespace {
+void KeepBufferRefs(rtc::scoped_refptr<webrtc::VideoFrameBuffer>,
+ rtc::scoped_refptr<webrtc::VideoFrameBuffer>) {}
+} // anonymous namespace
+
namespace webrtc {
class StereoDecoderAdapter::AdapterDecodedImageCallback
@@ -173,11 +179,22 @@
VideoFrame* alpha_decodedImage,
const rtc::Optional<int32_t>& alpha_decode_time_ms,
const rtc::Optional<uint8_t>& alpha_qp) {
- // TODO(emircan): Merge the output and put in a VideoFrame container that can
- // transport I420A.
- decoded_complete_callback_->Decoded(*decodedImage, decode_time_ms, qp);
- decoded_complete_callback_->Decoded(*alpha_decodedImage, alpha_decode_time_ms,
- alpha_qp);
+ rtc::scoped_refptr<webrtc::I420BufferInterface> yuv_buffer =
+ decodedImage->video_frame_buffer()->ToI420();
+ rtc::scoped_refptr<webrtc::I420BufferInterface> alpha_buffer =
+ alpha_decodedImage->video_frame_buffer()->ToI420();
+ RTC_DCHECK_EQ(yuv_buffer->width(), alpha_buffer->width());
+ RTC_DCHECK_EQ(yuv_buffer->height(), alpha_buffer->height());
+ rtc::scoped_refptr<I420ABufferInterface> merged_buffer = WrapI420ABuffer(
+ yuv_buffer->width(), yuv_buffer->height(), yuv_buffer->DataY(),
+ yuv_buffer->StrideY(), yuv_buffer->DataU(), yuv_buffer->StrideU(),
+ yuv_buffer->DataV(), yuv_buffer->StrideV(), alpha_buffer->DataY(),
+ alpha_buffer->StrideY(),
+ rtc::Bind(&KeepBufferRefs, yuv_buffer, alpha_buffer));
+
+ VideoFrame merged_image(merged_buffer, decodedImage->timestamp(),
+ 0 /* render_time_ms */, decodedImage->rotation());
+ decoded_complete_callback_->Decoded(merged_image, decode_time_ms, qp);
}
} // namespace webrtc
diff --git a/modules/video_coding/codecs/stereo/stereo_encoder_adapter.cc b/modules/video_coding/codecs/stereo/stereo_encoder_adapter.cc
index 283273d..b449c68 100644
--- a/modules/video_coding/codecs/stereo/stereo_encoder_adapter.cc
+++ b/modules/video_coding/codecs/stereo/stereo_encoder_adapter.cc
@@ -83,28 +83,30 @@
if (!encoded_complete_callback_) {
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
}
-
- // TODO(emircan): Extract alpha and create an alpha frame with dummy planes.
- // Since we don't have a way of transporting alpha yet, put a dummy output for
- // alpha consisting of YXX.
-
- // Encode AXX
- rtc::scoped_refptr<I420BufferInterface> yuva_buffer =
- input_image.video_frame_buffer()->ToI420();
- rtc::scoped_refptr<WrappedI420Buffer> alpha_buffer(
- new rtc::RefCountedObject<webrtc::WrappedI420Buffer>(
- input_image.width(), input_image.height(), yuva_buffer->DataY(),
- yuva_buffer->StrideY(), stereo_dummy_planes_.data(),
- yuva_buffer->StrideU(), stereo_dummy_planes_.data(),
- yuva_buffer->StrideV(),
- rtc::KeepRefUntilDone(input_image.video_frame_buffer())));
- VideoFrame alpha_image(alpha_buffer, input_image.timestamp(),
- input_image.render_time_ms(), input_image.rotation());
- encoders_[kAXXStream]->Encode(alpha_image, codec_specific_info, frame_types);
-
// Encode YUV
int rv = encoders_[kYUVStream]->Encode(input_image, codec_specific_info,
frame_types);
+ if (rv)
+ return rv;
+
+ const bool has_alpha = input_image.video_frame_buffer()->type() ==
+ VideoFrameBuffer::Type::kI420A;
+ if (!has_alpha)
+ return rv;
+
+ // Encode AXX
+ const I420ABufferInterface* yuva_buffer =
+ input_image.video_frame_buffer()->GetI420A();
+ rtc::scoped_refptr<I420BufferInterface> alpha_buffer =
+ WrapI420Buffer(input_image.width(), input_image.height(),
+ yuva_buffer->DataA(), yuva_buffer->StrideA(),
+ stereo_dummy_planes_.data(), yuva_buffer->StrideU(),
+ stereo_dummy_planes_.data(), yuva_buffer->StrideV(),
+ rtc::KeepRefUntilDone(input_image.video_frame_buffer()));
+ VideoFrame alpha_image(alpha_buffer, input_image.timestamp(),
+ input_image.render_time_ms(), input_image.rotation());
+ rv = encoders_[kAXXStream]->Encode(alpha_image, codec_specific_info,
+ frame_types);
return rv;
}
@@ -158,6 +160,9 @@
const EncodedImage& encodedImage,
const CodecSpecificInfo* codecSpecificInfo,
const RTPFragmentationHeader* fragmentation) {
+ if (stream_idx == kAXXStream)
+ return EncodedImageCallback::Result(EncodedImageCallback::Result::OK);
+
// TODO(emircan): Fill |codec_specific_info| with stereo parameters.
encoded_complete_callback_->OnEncodedImage(encodedImage, codecSpecificInfo,
fragmentation);
diff --git a/modules/video_coding/codecs/stereo/test/stereo_adapter_unittest.cc b/modules/video_coding/codecs/stereo/test/stereo_adapter_unittest.cc
index a8913e7..7c16620 100644
--- a/modules/video_coding/codecs/stereo/test/stereo_adapter_unittest.cc
+++ b/modules/video_coding/codecs/stereo/test/stereo_adapter_unittest.cc
@@ -10,11 +10,14 @@
#include "api/test/mock_video_decoder_factory.h"
#include "api/test/mock_video_encoder_factory.h"
+#include "common_video/include/video_frame_buffer.h"
#include "common_video/libyuv/include/webrtc_libyuv.h"
#include "modules/video_coding/codecs/stereo/include/stereo_decoder_adapter.h"
#include "modules/video_coding/codecs/stereo/include/stereo_encoder_adapter.h"
#include "modules/video_coding/codecs/test/video_codec_test.h"
#include "modules/video_coding/codecs/vp9/include/vp9.h"
+#include "rtc_base/keep_ref_until_done.h"
+#include "rtc_base/ptr_util.h"
using testing::_;
using testing::Return;
@@ -44,6 +47,18 @@
return codec_settings;
}
+ std::unique_ptr<VideoFrame> CreateI420AInputFrame() {
+ rtc::scoped_refptr<webrtc::I420BufferInterface> yuv_buffer =
+ input_frame_->video_frame_buffer()->ToI420();
+ rtc::scoped_refptr<I420ABufferInterface> yuva_buffer = WrapI420ABuffer(
+ yuv_buffer->width(), yuv_buffer->height(), yuv_buffer->DataY(),
+ yuv_buffer->StrideY(), yuv_buffer->DataU(), yuv_buffer->StrideU(),
+ yuv_buffer->DataV(), yuv_buffer->StrideV(), yuv_buffer->DataY(),
+ yuv_buffer->StrideY(), rtc::KeepRefUntilDone(yuv_buffer));
+ return rtc::WrapUnique<VideoFrame>(
+ new VideoFrame(yuva_buffer, kVideoRotation_0, 0));
+ }
+
private:
void SetUp() override {
EXPECT_CALL(*decoder_factory_, Die());
@@ -93,4 +108,20 @@
EXPECT_GT(I420PSNR(input_frame_.get(), decoded_frame.get()), 36);
}
+TEST_F(TestStereoAdapter, EncodeDecodeI420AFrame) {
+ std::unique_ptr<VideoFrame> yuva_frame = CreateI420AInputFrame();
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ encoder_->Encode(*yuva_frame, nullptr, nullptr));
+ EncodedImage encoded_frame;
+ CodecSpecificInfo codec_specific_info;
+ ASSERT_TRUE(WaitForEncodedFrame(&encoded_frame, &codec_specific_info));
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ decoder_->Decode(encoded_frame, false, nullptr));
+ std::unique_ptr<VideoFrame> decoded_frame;
+ rtc::Optional<uint8_t> decoded_qp;
+ ASSERT_TRUE(WaitForDecodedFrame(&decoded_frame, &decoded_qp));
+ ASSERT_TRUE(decoded_frame);
+ EXPECT_GT(I420PSNR(yuva_frame.get(), decoded_frame.get()), 36);
+}
+
} // namespace webrtc