Add StereoCodecAdapter classes
This CL is the step 1 for adding alpha channel support over the wire in webrtc.
- Add the footprint for adapter classes that wraps actual codecs.
- This CL does not add a webrtc::VideoFrame container that can carry alpha to
make the CL shorter for an easier review. Therefore, it exercises a code path
for when we receive no alpha input, just regular I420 frames.
- Unittest sends a video frame for encode/decode through these adapters and
checks the output PSNR.
- 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: I9d3be13647a0a958feceb8d7a9aa93852fc6a1fa
Reviewed-on: https://webrtc-review.googlesource.com/11841
Commit-Queue: Emircan Uysaler <emircan@webrtc.org>
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Niklas Enbom <niklas.enbom@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20490}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index 581f1c2..72078b4 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -304,6 +304,23 @@
]
}
+ rtc_source_set("mock_video_codec_factory") {
+ testonly = true
+ sources = [
+ "test/mock_video_decoder_factory.h",
+ "test/mock_video_encoder_factory.h",
+ ]
+
+ public_deps = [
+ "../api/video_codecs:video_codecs_api",
+ ]
+
+ deps = [
+ "../test:test_support",
+ "//testing/gmock",
+ ]
+ }
+
rtc_source_set("fakemetricsobserver") {
testonly = true
sources = [
diff --git a/api/test/mock_video_decoder_factory.h b/api/test/mock_video_decoder_factory.h
new file mode 100644
index 0000000..915e391
--- /dev/null
+++ b/api/test/mock_video_decoder_factory.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_TEST_MOCK_VIDEO_DECODER_FACTORY_H_
+#define API_TEST_MOCK_VIDEO_DECODER_FACTORY_H_
+
+#include <memory>
+#include <vector>
+
+#include "api/video_codecs/sdp_video_format.h"
+#include "api/video_codecs/video_decoder_factory.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+class MockVideoDecoderFactory : public webrtc::VideoDecoderFactory {
+ public:
+ MOCK_CONST_METHOD0(GetSupportedFormats,
+ std::vector<webrtc::SdpVideoFormat>());
+
+ // We need to proxy to a return type that is copyable.
+ std::unique_ptr<webrtc::VideoDecoder> CreateVideoDecoder(
+ const webrtc::SdpVideoFormat& format) {
+ return std::unique_ptr<webrtc::VideoDecoder>(
+ CreateVideoDecoderProxy(format));
+ }
+ MOCK_METHOD1(CreateVideoDecoderProxy,
+ webrtc::VideoDecoder*(const webrtc::SdpVideoFormat&));
+
+ MOCK_METHOD0(Die, void());
+ ~MockVideoDecoderFactory() { Die(); }
+};
+} // namespace webrtc
+
+#endif // API_TEST_MOCK_VIDEO_DECODER_FACTORY_H_
diff --git a/api/test/mock_video_encoder_factory.h b/api/test/mock_video_encoder_factory.h
new file mode 100644
index 0000000..a694b63
--- /dev/null
+++ b/api/test/mock_video_encoder_factory.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_TEST_MOCK_VIDEO_ENCODER_FACTORY_H_
+#define API_TEST_MOCK_VIDEO_ENCODER_FACTORY_H_
+
+#include <memory>
+#include <vector>
+
+#include "api/video_codecs/sdp_video_format.h"
+#include "api/video_codecs/video_encoder_factory.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+class MockVideoEncoderFactory : public webrtc::VideoEncoderFactory {
+ public:
+ MOCK_CONST_METHOD0(GetSupportedFormats,
+ std::vector<webrtc::SdpVideoFormat>());
+ MOCK_CONST_METHOD1(QueryVideoEncoder,
+ CodecInfo(const webrtc::SdpVideoFormat&));
+
+ // We need to proxy to a return type that is copyable.
+ std::unique_ptr<webrtc::VideoEncoder> CreateVideoEncoder(
+ const webrtc::SdpVideoFormat& format) {
+ return std::unique_ptr<webrtc::VideoEncoder>(
+ CreateVideoEncoderProxy(format));
+ }
+ MOCK_METHOD1(CreateVideoEncoderProxy,
+ webrtc::VideoEncoder*(const webrtc::SdpVideoFormat&));
+
+ MOCK_METHOD0(Die, void());
+ ~MockVideoEncoderFactory() { Die(); }
+};
+
+} // namespace webrtc
+
+#endif // API_TEST_MOCK_VIDEO_ENCODER_FACTORY_H_
diff --git a/media/BUILD.gn b/media/BUILD.gn
index e98001a..2e46ee5 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -523,6 +523,7 @@
":rtc_media",
":rtc_media_base",
":rtc_media_tests_utils",
+ "../api:mock_video_codec_factory",
"../api:video_frame_api",
"../api/audio_codecs:builtin_audio_decoder_factory",
"../api/audio_codecs:builtin_audio_encoder_factory",
diff --git a/media/engine/webrtcvideoengine_unittest.cc b/media/engine/webrtcvideoengine_unittest.cc
index 7fc5924..c3d3446 100644
--- a/media/engine/webrtcvideoengine_unittest.cc
+++ b/media/engine/webrtcvideoengine_unittest.cc
@@ -13,6 +13,8 @@
#include <memory>
#include <vector>
+#include "api/test/mock_video_decoder_factory.h"
+#include "api/test/mock_video_encoder_factory.h"
#include "api/video_codecs/sdp_video_format.h"
#include "api/video_codecs/video_decoder_factory.h"
#include "api/video_codecs/video_encoder.h"
@@ -863,44 +865,6 @@
ASSERT_EQ(1u, decoder_factory_->decoders().size());
}
-class MockVideoEncoderFactory : public webrtc::VideoEncoderFactory {
- public:
- MOCK_CONST_METHOD0(GetSupportedFormats,
- std::vector<webrtc::SdpVideoFormat>());
- MOCK_CONST_METHOD1(QueryVideoEncoder,
- CodecInfo(const webrtc::SdpVideoFormat&));
-
- // We need to proxy to a return type that is copyable.
- std::unique_ptr<webrtc::VideoEncoder> CreateVideoEncoder(
- const webrtc::SdpVideoFormat& format) {
- return std::unique_ptr<webrtc::VideoEncoder>(
- CreateVideoEncoderProxy(format));
- }
- MOCK_METHOD1(CreateVideoEncoderProxy,
- webrtc::VideoEncoder*(const webrtc::SdpVideoFormat&));
-
- MOCK_METHOD0(Die, void());
- ~MockVideoEncoderFactory() { Die(); }
-};
-
-class MockVideoDecoderFactory : public webrtc::VideoDecoderFactory {
- public:
- MOCK_CONST_METHOD0(GetSupportedFormats,
- std::vector<webrtc::SdpVideoFormat>());
-
- // We need to proxy to a return type that is copyable.
- std::unique_ptr<webrtc::VideoDecoder> CreateVideoDecoder(
- const webrtc::SdpVideoFormat& format) {
- return std::unique_ptr<webrtc::VideoDecoder>(
- CreateVideoDecoderProxy(format));
- }
- MOCK_METHOD1(CreateVideoDecoderProxy,
- webrtc::VideoDecoder*(const webrtc::SdpVideoFormat&));
-
- MOCK_METHOD0(Die, void());
- ~MockVideoDecoderFactory() { Die(); }
-};
-
TEST(WebRtcVideoEngineNewVideoCodecFactoryTest, NullFactories) {
std::unique_ptr<webrtc::VideoEncoderFactory> encoder_factory;
std::unique_ptr<webrtc::VideoDecoderFactory> decoder_factory;
@@ -911,8 +875,10 @@
TEST(WebRtcVideoEngineNewVideoCodecFactoryTest, EmptyFactories) {
// |engine| take ownership of the factories.
- MockVideoEncoderFactory* encoder_factory = new MockVideoEncoderFactory();
- MockVideoDecoderFactory* decoder_factory = new MockVideoDecoderFactory();
+ webrtc::MockVideoEncoderFactory* encoder_factory =
+ new webrtc::MockVideoEncoderFactory();
+ webrtc::MockVideoDecoderFactory* decoder_factory =
+ new webrtc::MockVideoDecoderFactory();
WebRtcVideoEngine engine(
(std::unique_ptr<webrtc::VideoEncoderFactory>(encoder_factory)),
(std::unique_ptr<webrtc::VideoDecoderFactory>(decoder_factory)));
@@ -928,8 +894,10 @@
// new factories.
TEST(WebRtcVideoEngineNewVideoCodecFactoryTest, Vp8) {
// |engine| take ownership of the factories.
- MockVideoEncoderFactory* encoder_factory = new MockVideoEncoderFactory();
- MockVideoDecoderFactory* decoder_factory = new MockVideoDecoderFactory();
+ webrtc::MockVideoEncoderFactory* encoder_factory =
+ new webrtc::MockVideoEncoderFactory();
+ webrtc::MockVideoDecoderFactory* decoder_factory =
+ new webrtc::MockVideoDecoderFactory();
WebRtcVideoEngine engine(
(std::unique_ptr<webrtc::VideoEncoderFactory>(encoder_factory)),
(std::unique_ptr<webrtc::VideoDecoderFactory>(decoder_factory)));
diff --git a/modules/video_coding/BUILD.gn b/modules/video_coding/BUILD.gn
index eb0a419..d1e3311 100644
--- a/modules/video_coding/BUILD.gn
+++ b/modules/video_coding/BUILD.gn
@@ -92,6 +92,7 @@
":video_coding_utility",
":webrtc_h264",
":webrtc_i420",
+ ":webrtc_stereo",
":webrtc_vp8",
":webrtc_vp9",
"..:module_api",
@@ -233,6 +234,30 @@
]
}
+rtc_static_library("webrtc_stereo") {
+ sources = [
+ "codecs/stereo/include/stereo_decoder_adapter.h",
+ "codecs/stereo/include/stereo_encoder_adapter.h",
+ "codecs/stereo/stereo_decoder_adapter.cc",
+ "codecs/stereo/stereo_encoder_adapter.cc",
+ ]
+
+ if (!build_with_chromium && is_clang) {
+ # Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
+ suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
+ }
+
+ deps = [
+ ":video_coding_utility",
+ "..:module_api",
+ "../..:webrtc_common",
+ "../../api/video_codecs:video_codecs_api",
+ "../../common_video:common_video",
+ "../../rtc_base:rtc_base_approved",
+ "../../system_wrappers",
+ ]
+}
+
rtc_static_library("webrtc_vp8") {
sources = [
"codecs/vp8/default_temporal_layers.cc",
@@ -423,6 +448,7 @@
}
sources = [
"codecs/h264/test/h264_impl_unittest.cc",
+ "codecs/stereo/test/stereo_adapter_unittest.cc",
"codecs/test/videoprocessor_integrationtest.cc",
"codecs/test/videoprocessor_integrationtest.h",
"codecs/test/videoprocessor_integrationtest_libvpx.cc",
@@ -437,9 +463,11 @@
":video_coding",
":video_coding_utility",
":webrtc_h264",
+ ":webrtc_stereo",
":webrtc_vp8",
":webrtc_vp9",
"../..:webrtc_common",
+ "../../api:mock_video_codec_factory",
"../../api:optional",
"../../api:video_frame_api",
"../../common_video",
diff --git a/modules/video_coding/codecs/stereo/OWNERS b/modules/video_coding/codecs/stereo/OWNERS
new file mode 100644
index 0000000..6b72be3
--- /dev/null
+++ b/modules/video_coding/codecs/stereo/OWNERS
@@ -0,0 +1 @@
+emircan@webrtc.org
diff --git a/modules/video_coding/codecs/stereo/include/stereo_decoder_adapter.h b/modules/video_coding/codecs/stereo/include/stereo_decoder_adapter.h
new file mode 100644
index 0000000..729517f
--- /dev/null
+++ b/modules/video_coding/codecs/stereo/include/stereo_decoder_adapter.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_VIDEO_CODING_CODECS_STEREO_INCLUDE_STEREO_DECODER_ADAPTER_H_
+#define MODULES_VIDEO_CODING_CODECS_STEREO_INCLUDE_STEREO_DECODER_ADAPTER_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "api/video_codecs/video_decoder.h"
+#include "api/video_codecs/video_decoder_factory.h"
+#include "modules/video_coding/codecs/stereo/include/stereo_encoder_adapter.h"
+
+namespace webrtc {
+
+class StereoDecoderAdapter : public VideoDecoder {
+ public:
+ // |factory| is not owned and expected to outlive this class' lifetime.
+ explicit StereoDecoderAdapter(VideoDecoderFactory* factory);
+ virtual ~StereoDecoderAdapter();
+
+ // Implements VideoDecoder
+ int32_t InitDecode(const VideoCodec* codec_settings,
+ int32_t number_of_cores) override;
+ int32_t Decode(const EncodedImage& input_image,
+ bool missing_frames,
+ const RTPFragmentationHeader* fragmentation,
+ const CodecSpecificInfo* codec_specific_info,
+ int64_t render_time_ms) override;
+ int32_t RegisterDecodeCompleteCallback(
+ DecodedImageCallback* callback) override;
+ int32_t Release() override;
+
+ void Decoded(AlphaCodecStream stream_idx,
+ VideoFrame* decoded_image,
+ rtc::Optional<int32_t> decode_time_ms,
+ rtc::Optional<uint8_t> qp);
+
+ private:
+ // Wrapper class that redirects Decoded() calls.
+ class AdapterDecodedImageCallback;
+
+ // Holds the decoded image output of a frame.
+ struct DecodedImageData;
+
+ void MergeAlphaImages(VideoFrame* decoded_image,
+ const rtc::Optional<int32_t>& decode_time_ms,
+ const rtc::Optional<uint8_t>& qp,
+ VideoFrame* stereo_decoded_image,
+ const rtc::Optional<int32_t>& stereo_decode_time_ms,
+ const rtc::Optional<uint8_t>& stereo_qp);
+
+ VideoDecoderFactory* const factory_;
+ std::vector<std::unique_ptr<VideoDecoder>> decoders_;
+ std::vector<std::unique_ptr<AdapterDecodedImageCallback>> adapter_callbacks_;
+ DecodedImageCallback* decoded_complete_callback_;
+
+ // Holds YUV or AXX decode output of a frame that is identified by timestamp.
+ std::map<uint32_t /* timestamp */, DecodedImageData> decoded_data_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_VIDEO_CODING_CODECS_STEREO_INCLUDE_STEREO_DECODER_ADAPTER_H_
diff --git a/modules/video_coding/codecs/stereo/include/stereo_encoder_adapter.h b/modules/video_coding/codecs/stereo/include/stereo_encoder_adapter.h
new file mode 100644
index 0000000..ef1e9e1
--- /dev/null
+++ b/modules/video_coding/codecs/stereo/include/stereo_encoder_adapter.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_VIDEO_CODING_CODECS_STEREO_INCLUDE_STEREO_ENCODER_ADAPTER_H_
+#define MODULES_VIDEO_CODING_CODECS_STEREO_INCLUDE_STEREO_ENCODER_ADAPTER_H_
+
+#include <memory>
+#include <vector>
+
+#include "api/video_codecs/video_encoder.h"
+#include "api/video_codecs/video_encoder_factory.h"
+#include "modules/video_coding/include/video_codec_interface.h"
+
+namespace webrtc {
+
+enum AlphaCodecStream {
+ kYUVStream = 0,
+ kAXXStream = 1,
+ kAlphaCodecStreams = 2,
+};
+
+class StereoEncoderAdapter : public VideoEncoder {
+ public:
+ // |factory| is not owned and expected to outlive this class' lifetime.
+ explicit StereoEncoderAdapter(VideoEncoderFactory* factory);
+ virtual ~StereoEncoderAdapter();
+
+ // Implements VideoEncoder
+ int InitEncode(const VideoCodec* inst,
+ int number_of_cores,
+ size_t max_payload_size) override;
+ int Encode(const VideoFrame& input_image,
+ const CodecSpecificInfo* codec_specific_info,
+ const std::vector<FrameType>* frame_types) override;
+ int RegisterEncodeCompleteCallback(EncodedImageCallback* callback) override;
+ int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override;
+ int SetRateAllocation(const BitrateAllocation& bitrate,
+ uint32_t new_framerate) override;
+ int Release() override;
+ const char* ImplementationName() const override;
+
+ EncodedImageCallback::Result OnEncodedImage(
+ AlphaCodecStream stream_idx,
+ const EncodedImage& encodedImage,
+ const CodecSpecificInfo* codecSpecificInfo,
+ const RTPFragmentationHeader* fragmentation);
+
+ private:
+ // Wrapper class that redirects OnEncodedImage() calls.
+ class AdapterEncodedImageCallback;
+
+ // Holds the encoded image output of a frame.
+ struct EncodedImageData;
+
+ VideoEncoderFactory* const factory_;
+ std::vector<std::unique_ptr<VideoEncoder>> encoders_;
+ std::vector<std::unique_ptr<AdapterEncodedImageCallback>> adapter_callbacks_;
+ EncodedImageCallback* encoded_complete_callback_;
+
+ uint64_t picture_index_ = 0;
+ std::vector<uint8_t> stereo_dummy_planes_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_VIDEO_CODING_CODECS_STEREO_INCLUDE_STEREO_ENCODER_ADAPTER_H_
diff --git a/modules/video_coding/codecs/stereo/stereo_decoder_adapter.cc b/modules/video_coding/codecs/stereo/stereo_decoder_adapter.cc
new file mode 100644
index 0000000..5f8b4c59
--- /dev/null
+++ b/modules/video_coding/codecs/stereo/stereo_decoder_adapter.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/video_coding/codecs/stereo/include/stereo_decoder_adapter.h"
+
+#include "api/video/i420_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"
+#include "common_video/libyuv/include/webrtc_libyuv.h"
+#include "rtc_base/keep_ref_until_done.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+class StereoDecoderAdapter::AdapterDecodedImageCallback
+ : public webrtc::DecodedImageCallback {
+ public:
+ AdapterDecodedImageCallback(webrtc::StereoDecoderAdapter* adapter,
+ AlphaCodecStream stream_idx)
+ : adapter_(adapter), stream_idx_(stream_idx) {}
+
+ void Decoded(VideoFrame& decodedImage,
+ rtc::Optional<int32_t> decode_time_ms,
+ rtc::Optional<uint8_t> qp) override {
+ if (!adapter_)
+ return;
+ adapter_->Decoded(stream_idx_, &decodedImage, decode_time_ms, qp);
+ }
+ int32_t Decoded(VideoFrame& decodedImage) override {
+ RTC_NOTREACHED();
+ return WEBRTC_VIDEO_CODEC_OK;
+ }
+ int32_t Decoded(VideoFrame& decodedImage, int64_t decode_time_ms) override {
+ RTC_NOTREACHED();
+ return WEBRTC_VIDEO_CODEC_OK;
+ }
+
+ private:
+ StereoDecoderAdapter* adapter_;
+ const AlphaCodecStream stream_idx_;
+};
+
+struct StereoDecoderAdapter::DecodedImageData {
+ explicit DecodedImageData(AlphaCodecStream stream_idx)
+ : stream_idx_(stream_idx),
+ decodedImage_(I420Buffer::Create(1 /* width */, 1 /* height */),
+ 0,
+ 0,
+ kVideoRotation_0) {
+ RTC_DCHECK_EQ(kAXXStream, stream_idx);
+ }
+ DecodedImageData(AlphaCodecStream stream_idx,
+ const VideoFrame& decodedImage,
+ const rtc::Optional<int32_t>& decode_time_ms,
+ const rtc::Optional<uint8_t>& qp)
+ : stream_idx_(stream_idx),
+ decodedImage_(decodedImage),
+ decode_time_ms_(decode_time_ms),
+ qp_(qp) {}
+ const AlphaCodecStream stream_idx_;
+ VideoFrame decodedImage_;
+ const rtc::Optional<int32_t> decode_time_ms_;
+ const rtc::Optional<uint8_t> qp_;
+
+ private:
+ RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(DecodedImageData);
+};
+
+StereoDecoderAdapter::StereoDecoderAdapter(VideoDecoderFactory* factory)
+ : factory_(factory) {}
+
+StereoDecoderAdapter::~StereoDecoderAdapter() {
+ Release();
+}
+
+int32_t StereoDecoderAdapter::InitDecode(const VideoCodec* codec_settings,
+ int32_t number_of_cores) {
+ VideoCodec settings = *codec_settings;
+ settings.codecType = kVideoCodecVP9;
+ for (size_t i = 0; i < kAlphaCodecStreams; ++i) {
+ const SdpVideoFormat format("VP9");
+ std::unique_ptr<VideoDecoder> decoder =
+ factory_->CreateVideoDecoder(format);
+ const int32_t rv = decoder->InitDecode(&settings, number_of_cores);
+ if (rv)
+ return rv;
+ adapter_callbacks_.emplace_back(
+ new StereoDecoderAdapter::AdapterDecodedImageCallback(
+ this, static_cast<AlphaCodecStream>(i)));
+ decoder->RegisterDecodeCompleteCallback(adapter_callbacks_.back().get());
+ decoders_.emplace_back(std::move(decoder));
+ }
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t StereoDecoderAdapter::Decode(
+ const EncodedImage& input_image,
+ bool missing_frames,
+ const RTPFragmentationHeader* /*fragmentation*/,
+ const CodecSpecificInfo* codec_specific_info,
+ int64_t render_time_ms) {
+ // TODO(emircan): Read |codec_specific_info->stereoInfo| to split frames.
+ int32_t rv =
+ decoders_[kYUVStream]->Decode(input_image, missing_frames, nullptr,
+ codec_specific_info, render_time_ms);
+ if (rv)
+ return rv;
+ rv = decoders_[kAXXStream]->Decode(input_image, missing_frames, nullptr,
+ codec_specific_info, render_time_ms);
+ return rv;
+}
+
+int32_t StereoDecoderAdapter::RegisterDecodeCompleteCallback(
+ DecodedImageCallback* callback) {
+ decoded_complete_callback_ = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int32_t StereoDecoderAdapter::Release() {
+ for (auto& decoder : decoders_) {
+ const int32_t rv = decoder->Release();
+ if (rv)
+ return rv;
+ }
+ decoders_.clear();
+ adapter_callbacks_.clear();
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+void StereoDecoderAdapter::Decoded(AlphaCodecStream stream_idx,
+ VideoFrame* decoded_image,
+ rtc::Optional<int32_t> decode_time_ms,
+ rtc::Optional<uint8_t> qp) {
+ const auto& other_decoded_data_it =
+ decoded_data_.find(decoded_image->timestamp());
+ if (other_decoded_data_it != decoded_data_.end()) {
+ auto& other_image_data = other_decoded_data_it->second;
+ if (stream_idx == kYUVStream) {
+ RTC_DCHECK_EQ(kAXXStream, other_image_data.stream_idx_);
+ MergeAlphaImages(decoded_image, decode_time_ms, qp,
+ &other_image_data.decodedImage_,
+ other_image_data.decode_time_ms_, other_image_data.qp_);
+ } else {
+ RTC_DCHECK_EQ(kYUVStream, other_image_data.stream_idx_);
+ RTC_DCHECK_EQ(kAXXStream, stream_idx);
+ MergeAlphaImages(&other_image_data.decodedImage_,
+ other_image_data.decode_time_ms_, other_image_data.qp_,
+ decoded_image, decode_time_ms, qp);
+ }
+ decoded_data_.erase(decoded_data_.begin(), other_decoded_data_it);
+ return;
+ }
+ RTC_DCHECK(decoded_data_.find(decoded_image->timestamp()) ==
+ decoded_data_.end());
+ decoded_data_.emplace(
+ std::piecewise_construct,
+ std::forward_as_tuple(decoded_image->timestamp()),
+ std::forward_as_tuple(stream_idx, *decoded_image, decode_time_ms, qp));
+}
+
+void StereoDecoderAdapter::MergeAlphaImages(
+ VideoFrame* decodedImage,
+ const rtc::Optional<int32_t>& decode_time_ms,
+ const rtc::Optional<uint8_t>& qp,
+ 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);
+}
+
+} // namespace webrtc
diff --git a/modules/video_coding/codecs/stereo/stereo_encoder_adapter.cc b/modules/video_coding/codecs/stereo/stereo_encoder_adapter.cc
new file mode 100644
index 0000000..f6a04d0
--- /dev/null
+++ b/modules/video_coding/codecs/stereo/stereo_encoder_adapter.cc
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/video_coding/codecs/stereo/include/stereo_encoder_adapter.h"
+
+#include "api/video_codecs/sdp_video_format.h"
+#include "common_video/include/video_frame.h"
+#include "common_video/include/video_frame_buffer.h"
+#include "common_video/libyuv/include/webrtc_libyuv.h"
+#include "modules/include/module_common_types.h"
+#include "rtc_base/keep_ref_until_done.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+// Callback wrapper that helps distinguish returned results from |encoders_|
+// instances.
+class StereoEncoderAdapter::AdapterEncodedImageCallback
+ : public webrtc::EncodedImageCallback {
+ public:
+ AdapterEncodedImageCallback(webrtc::StereoEncoderAdapter* adapter,
+ AlphaCodecStream stream_idx)
+ : adapter_(adapter), stream_idx_(stream_idx) {}
+
+ EncodedImageCallback::Result OnEncodedImage(
+ const EncodedImage& encoded_image,
+ const CodecSpecificInfo* codec_specific_info,
+ const RTPFragmentationHeader* fragmentation) override {
+ if (!adapter_)
+ return Result(Result::OK);
+ return adapter_->OnEncodedImage(stream_idx_, encoded_image,
+ codec_specific_info, fragmentation);
+ }
+
+ private:
+ StereoEncoderAdapter* adapter_;
+ const AlphaCodecStream stream_idx_;
+};
+
+StereoEncoderAdapter::StereoEncoderAdapter(VideoEncoderFactory* factory)
+ : factory_(factory), encoded_complete_callback_(nullptr) {}
+
+StereoEncoderAdapter::~StereoEncoderAdapter() {
+ Release();
+}
+
+int StereoEncoderAdapter::InitEncode(const VideoCodec* inst,
+ int number_of_cores,
+ size_t max_payload_size) {
+ const size_t buffer_size =
+ CalcBufferSize(VideoType::kI420, inst->width, inst->height);
+ stereo_dummy_planes_.resize(buffer_size);
+ // It is more expensive to encode 0x00, so use 0x80 instead.
+ std::fill(stereo_dummy_planes_.begin(), stereo_dummy_planes_.end(), 0x80);
+
+ for (size_t i = 0; i < kAlphaCodecStreams; ++i) {
+ const SdpVideoFormat format("VP9");
+ std::unique_ptr<VideoEncoder> encoder =
+ factory_->CreateVideoEncoder(format);
+ const int rv = encoder->InitEncode(inst, number_of_cores, max_payload_size);
+ if (rv) {
+ LOG(LS_ERROR) << "Failed to create stere codec index " << i;
+ return rv;
+ }
+ adapter_callbacks_.emplace_back(new AdapterEncodedImageCallback(
+ this, static_cast<AlphaCodecStream>(i)));
+ encoder->RegisterEncodeCompleteCallback(adapter_callbacks_.back().get());
+ encoders_.emplace_back(std::move(encoder));
+ }
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int StereoEncoderAdapter::Encode(const VideoFrame& input_image,
+ const CodecSpecificInfo* codec_specific_info,
+ const std::vector<FrameType>* frame_types) {
+ 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);
+ return rv;
+}
+
+int StereoEncoderAdapter::RegisterEncodeCompleteCallback(
+ EncodedImageCallback* callback) {
+ encoded_complete_callback_ = callback;
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int StereoEncoderAdapter::SetChannelParameters(uint32_t packet_loss,
+ int64_t rtt) {
+ for (auto& encoder : encoders_) {
+ const int rv = encoder->SetChannelParameters(packet_loss, rtt);
+ if (rv)
+ return rv;
+ }
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int StereoEncoderAdapter::SetRateAllocation(const BitrateAllocation& bitrate,
+ uint32_t framerate) {
+ for (auto& encoder : encoders_) {
+ // TODO(emircan): |new_framerate| is used to calculate duration for encoder
+ // instances. We report the total frame rate to keep real time for now.
+ // Remove this after refactoring duration logic.
+ const int rv =
+ encoder->SetRateAllocation(bitrate, encoders_.size() * framerate);
+ if (rv)
+ return rv;
+ }
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+int StereoEncoderAdapter::Release() {
+ for (auto& encoder : encoders_) {
+ const int rv = encoder->Release();
+ if (rv)
+ return rv;
+ }
+ encoders_.clear();
+ adapter_callbacks_.clear();
+ return WEBRTC_VIDEO_CODEC_OK;
+}
+
+const char* StereoEncoderAdapter::ImplementationName() const {
+ return "StereoEncoderAdapter";
+}
+
+EncodedImageCallback::Result StereoEncoderAdapter::OnEncodedImage(
+ AlphaCodecStream stream_idx,
+ const EncodedImage& encodedImage,
+ const CodecSpecificInfo* codecSpecificInfo,
+ const RTPFragmentationHeader* fragmentation) {
+ // TODO(emircan): Fill |codec_specific_info| with stereo parameters.
+ encoded_complete_callback_->OnEncodedImage(encodedImage, codecSpecificInfo,
+ fragmentation);
+ return EncodedImageCallback::Result(EncodedImageCallback::Result::OK);
+}
+
+} // namespace webrtc
diff --git a/modules/video_coding/codecs/stereo/test/stereo_adapter_unittest.cc b/modules/video_coding/codecs/stereo/test/stereo_adapter_unittest.cc
new file mode 100644
index 0000000..a8913e7
--- /dev/null
+++ b/modules/video_coding/codecs/stereo/test/stereo_adapter_unittest.cc
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "api/test/mock_video_decoder_factory.h"
+#include "api/test/mock_video_encoder_factory.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"
+
+using testing::_;
+using testing::Return;
+
+namespace webrtc {
+
+class TestStereoAdapter : public VideoCodecTest {
+ public:
+ TestStereoAdapter()
+ : decoder_factory_(new webrtc::MockVideoDecoderFactory),
+ encoder_factory_(new webrtc::MockVideoEncoderFactory) {}
+
+ protected:
+ VideoDecoder* CreateDecoder() override {
+ return new StereoDecoderAdapter(decoder_factory_.get());
+ }
+
+ VideoEncoder* CreateEncoder() override {
+ return new StereoEncoderAdapter(encoder_factory_.get());
+ }
+
+ VideoCodec codec_settings() override {
+ VideoCodec codec_settings;
+ codec_settings.codecType = webrtc::kVideoCodecVP9;
+ codec_settings.VP9()->numberOfTemporalLayers = 1;
+ codec_settings.VP9()->numberOfSpatialLayers = 1;
+ return codec_settings;
+ }
+
+ private:
+ void SetUp() override {
+ EXPECT_CALL(*decoder_factory_, Die());
+ VideoDecoder* decoder1 = VP9Decoder::Create();
+ VideoDecoder* decoder2 = VP9Decoder::Create();
+ EXPECT_CALL(*decoder_factory_, CreateVideoDecoderProxy(_))
+ .WillOnce(Return(decoder1))
+ .WillOnce(Return(decoder2));
+
+ EXPECT_CALL(*encoder_factory_, Die());
+ VideoEncoder* encoder1 = VP9Encoder::Create();
+ VideoEncoder* encoder2 = VP9Encoder::Create();
+ EXPECT_CALL(*encoder_factory_, CreateVideoEncoderProxy(_))
+ .WillOnce(Return(encoder1))
+ .WillOnce(Return(encoder2));
+
+ VideoCodecTest::SetUp();
+ }
+
+ const std::unique_ptr<webrtc::MockVideoDecoderFactory> decoder_factory_;
+ const std::unique_ptr<webrtc::MockVideoEncoderFactory> encoder_factory_;
+};
+
+// TODO(emircan): Currently VideoCodecTest tests do a complete setup
+// step that goes beyond constructing |decoder_|. Simplify these tests to do
+// less.
+TEST_F(TestStereoAdapter, ConstructAndDestructDecoder) {
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, decoder_->Release());
+}
+
+TEST_F(TestStereoAdapter, ConstructAndDestructEncoder) {
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, encoder_->Release());
+}
+
+TEST_F(TestStereoAdapter, EncodeDecodeI420Frame) {
+ EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK,
+ encoder_->Encode(*input_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(input_frame_.get(), decoded_frame.get()), 36);
+}
+
+} // namespace webrtc