--- /dev/null
+// Copyright 2024 Samsung Electronics Inc. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/filters/tizen/h26x_bitstream_converter.h"
+
+#include <memory>
+#include <variant>
+
+#include "base/notreached.h"
+#include "media/base/tizen/logger/media_logger.h"
+#include "media/filters/h264_to_annex_b_bitstream_converter.h"
+#include "media/filters/h265_to_annex_b_bitstream_converter.h"
+#include "media/filters/tizen/media_video_codec.h"
+#include "media/formats/mp4/box_definitions.h"
+#include "media/formats/mp4/hevc.h"
+
+namespace media {
+
+class H26XBitstreamConverter::Impl {
+ public:
+ Impl() = default;
+
+ template <typename ConfigurationType, typename ConverterType>
+ void ParseConfiguration(const std::vector<uint8_t>& extradata);
+
+ template <typename ConfigurationType, typename ConverterType>
+ scoped_refptr<DecoderBuffer> ConvertBufferInternal(
+ scoped_refptr<DecoderBuffer> input_buffer);
+
+ private:
+ std::variant<std::monostate,
+ mp4::AVCDecoderConfigurationRecord,
+ mp4::HEVCDecoderConfigurationRecord>
+ h26x_configuration_;
+
+ std::variant<std::monostate,
+ H264ToAnnexBBitstreamConverter,
+ H265ToAnnexBBitstreamConverter>
+ h26x_bitstream_converter_;
+
+ bool h26x_configuration_parsed_ = false;
+};
+
+template <typename ConfigurationType, typename ConverterType>
+void H26XBitstreamConverter::Impl::ParseConfiguration(
+ const std::vector<uint8_t>& extradata) {
+ if (extradata.empty()) {
+ return;
+ }
+
+ auto& configuration = h26x_configuration_.emplace<ConfigurationType>();
+ auto& converter = h26x_bitstream_converter_.emplace<ConverterType>();
+
+ if (!converter.ParseConfiguration(extradata.data(), extradata.size(),
+ &configuration)) {
+ LOG(WARNING)
+ << "Failed to parse H264 extradata. Could not obtain configuration.";
+ return;
+ }
+
+ h26x_configuration_parsed_ = true;
+}
+
+template <typename ConfigurationType, typename ConverterType>
+scoped_refptr<DecoderBuffer>
+H26XBitstreamConverter::Impl::ConvertBufferInternal(
+ scoped_refptr<DecoderBuffer> input_buffer) {
+ if (!h26x_configuration_parsed_) {
+ // No extradata was provided for codec (so it is already in AnnexB format)
+ // or extradata parsing failed.
+ return input_buffer;
+ }
+
+ if (input_buffer->end_of_stream() || !input_buffer->data() ||
+ !input_buffer->data_size()) {
+ // Buffer contains no data. Cannot convert to AnnexB format.
+ return input_buffer;
+ }
+
+ LOG_ASSERT(std::holds_alternative<ConfigurationType>(h26x_configuration_));
+ LOG_ASSERT(std::holds_alternative<ConverterType>(h26x_bitstream_converter_));
+
+ const auto& configuration = std::get<ConfigurationType>(h26x_configuration_);
+ auto& converter = std::get<ConverterType>(h26x_bitstream_converter_);
+
+ const uint32_t output_packet_size = converter.CalculateNeededOutputBufferSize(
+ input_buffer->data(), input_buffer->data_size(), &configuration);
+
+ if (output_packet_size == 0) {
+ LOG(WARNING)
+ << "Could not convert packet. Wrong calculated output buffer size";
+ return input_buffer;
+ }
+
+ auto converted_data = std::make_unique<uint8_t[]>(output_packet_size);
+
+ auto bytes_written = output_packet_size;
+ if (!converter.ConvertNalUnitStreamToByteStream(
+ input_buffer->data(), input_buffer->data_size(), &configuration,
+ converted_data.get(), &bytes_written)) {
+ LOG(WARNING) << "Failed to convert AVCC buffer to AnnexB format";
+ return input_buffer;
+ }
+
+ auto converted_buffer =
+ DecoderBuffer::FromArray(std::move(converted_data), output_packet_size);
+
+ converted_buffer->set_is_key_frame(input_buffer->is_key_frame());
+ converted_buffer->set_side_data(input_buffer->side_data());
+ converted_buffer->set_timestamp(input_buffer->timestamp());
+ converted_buffer->set_dts(input_buffer->dts());
+ converted_buffer->set_duration(input_buffer->duration());
+ converted_buffer->set_is_key_frame(input_buffer->is_key_frame());
+ if (input_buffer->decrypt_config()) {
+ converted_buffer->set_decrypt_config(
+ input_buffer->decrypt_config()->Clone());
+ }
+
+ return converted_buffer;
+}
+
+H26XBitstreamConverter::H26XBitstreamConverter(
+ MediaVideoCodec codec,
+ const std::vector<uint8_t>& extradata)
+ : impl_(std::make_unique<Impl>()), codec_(codec) {
+ TIZEN_MEDIA_LOG_ASSERT(codec_ == MediaVideoCodec::kCodecH264 ||
+ codec_ == MediaVideoCodec::kCodecHEVC);
+
+ switch (codec_) {
+ case media::MediaVideoCodec::kCodecH264: {
+ impl_->ParseConfiguration<mp4::AVCDecoderConfigurationRecord,
+ H264ToAnnexBBitstreamConverter>(extradata);
+ break;
+ }
+ case media::MediaVideoCodec::kCodecHEVC: {
+ impl_->ParseConfiguration<mp4::HEVCDecoderConfigurationRecord,
+ H265ToAnnexBBitstreamConverter>(extradata);
+ break;
+ }
+ default:
+ NOTREACHED();
+ }
+}
+
+H26XBitstreamConverter::~H26XBitstreamConverter() = default;
+
+scoped_refptr<DecoderBuffer> H26XBitstreamConverter::ConvertBuffer(
+ scoped_refptr<DecoderBuffer> input_buffer) {
+ TIZEN_MEDIA_LOG_ASSERT(codec_ == MediaVideoCodec::kCodecH264 ||
+ codec_ == MediaVideoCodec::kCodecHEVC);
+
+ switch (codec_) {
+ case media::MediaVideoCodec::kCodecH264: {
+ return impl_->ConvertBufferInternal<mp4::AVCDecoderConfigurationRecord,
+ H264ToAnnexBBitstreamConverter>(
+ std::move(input_buffer));
+ }
+ case media::MediaVideoCodec::kCodecHEVC: {
+ return impl_->ConvertBufferInternal<mp4::HEVCDecoderConfigurationRecord,
+ H265ToAnnexBBitstreamConverter>(
+ std::move(input_buffer));
+ }
+ default:
+ NOTREACHED();
+ }
+
+ return nullptr;
+}
+
+} // namespace media
#include "media/base/waiting.h"
#include "media/filters/tizen/cdm_utils.h"
#include "media/filters/tizen/decoder_promotion.h"
+#include "media/filters/tizen/h26x_bitstream_converter.h"
#include "media/filters/tizen/media_video_codec.h"
#include "media/filters/tizen/tizen_cdm_bridge.h"
#include "media/filters/tizen/video_decoder_config_updater.h"
CHECK(decoder_task_runner_->RunsTasksInCurrentSequence())
<< "Not on decoder thread";
+ if (h26x_bitstream_converter_) {
+ buffer = h26x_bitstream_converter_->ConvertBuffer(std::move(buffer));
+ }
+
if (buffer->timestamp() == kNoTimestamp) {
TIZEN_MEDIA_LOG(ERROR) << "Buffer with invalid timestamp provided.";
std::move(decode_cb).Run(DecoderStatus::Codes::kMissingTimestamp);
VideoDecoder::InitCB init_cb,
VideoDecoder::OutputCB output_cb,
WaitingCB waiting_cb) {
+ codec_ = VideoCodecToMediaVideoCodec(config.codec());
+ if (codec_ == MediaVideoCodec::kCodecUnknown) {
+ TIZEN_MEDIA_LOG(ERROR) << "Not supported video codec: " << config.codec();
+ }
+
+ if (codec_ == MediaVideoCodec::kCodecH264 ||
+ codec_ == MediaVideoCodec::kCodecHEVC) {
+ // In case of H264 and H265 codecs it is possible that data will be
+ // provided in AVCC format. In such case, we need to convert input data
+ // from AVCC to AnnexB format that is supported by platform HW decoder.
+ // To make it possible, we need to initialize H.26X bitstream converter.
+ h26x_bitstream_converter_ =
+ std::make_unique<H26XBitstreamConverter>(codec_, config.extra_data());
+ } else {
+ h26x_bitstream_converter_.reset();
+ }
+
initialized_ = true;
waiting_for_key_ = false;
first_frame_done_ = false;
callbacks.switch_cb = base::BindPostTaskToCurrentDefault(base::BindRepeating(
&TTvdVideoDecoderImpl::SwitchDecoder, weak_factory_.GetWeakPtr()));
- codec_ = VideoCodecToMediaVideoCodec(config_.codec());
- if (codec_ == MediaVideoCodec::kCodecUnknown) {
- TIZEN_MEDIA_LOG(ERROR) << "Not supported video codec: " << config_.codec();
- }
-
// Use picture size instead of |coded_size| as resource manager might
// return that valid resolutions (e.g 3840x2176) are not supported on
// several boards.
namespace media {
+class H26XBitstreamConverter;
class TizenCdmBridge;
class DecodedCollectionOnGpu : public gfx::TizenOverlayPlaneCollection {
// It might be also changed be calls to |Initialize|.
ExtendedVideoDecoderConfig config_;
- MediaVideoCodec codec_;
+ MediaVideoCodec codec_ = MediaVideoCodec::kCodecUnknown;
// Tracks if |Initialize| was ever called for this instance. It exists
/// because it's hard to track using |decoder_state_|.
bool request_keyframe_after_initialization_ = false;
+ // H.264 (AVC) and H.265 (HEVC) data can be provided to decoder in AVCC
+ // format. HW decoder requests data to be provided in AnnexB format, so
+ // in case of receiving data in AVCC format, AVCC -> AnnexB conversion needs
+ // to be performed.
+ //
+ // We cannot use |VideoDecoder::NeedsBitstreamConversion| for automatic
+ // conversion, because it is possible, that HW decoder initialization may fail
+ // (eg. due to lack of available HW resources) and SW fallback (which requires
+ // AVCC format) will be performed.
+ std::unique_ptr<H26XBitstreamConverter> h26x_bitstream_converter_;
+
// Token from GPU collection, needed to prepare surface to render on.
base::UnguessableToken collection_token_;