[codec][sysmem] codec sysmem - soft transition
Plumb sysmem via StreamProcessor interface.
For now, OnOutputConfig is still sent to make this a soft transition.
Tested: use_media_decoder, use_h264_decoder_test, mediaplayer_test_util, chromium fuchsia_video_decoder test
Change-Id: I13e3ae4f4be66a121dc097ff00bd136e04a56442
diff --git a/examples/media/use_media_decoder/meta/use_h264_decoder_test.cmx b/examples/media/use_media_decoder/meta/use_h264_decoder_test.cmx
deleted file mode 100644
index f2c4543..0000000
--- a/examples/media/use_media_decoder/meta/use_h264_decoder_test.cmx
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "facets": {
- "fuchsia.test": {
- "injected-services": {
- "fuchsia.mediacodec.CodecFactory": "fuchsia-pkg://fuchsia.com/codec_factory#meta/codec_factory.cmx"
- }
- }
- },
- "program": {
- "binary": "test/use_h264_decoder_test"
- },
- "sandbox": {
- "services": [
- "fuchsia.mediacodec.CodecFactory"
- ]
- }
-}
diff --git a/garnet/bin/media/codec_factory/BUILD.gn b/garnet/bin/media/codec_factory/BUILD.gn
index a05c9a30..e4d9082 100644
--- a/garnet/bin/media/codec_factory/BUILD.gn
+++ b/garnet/bin/media/codec_factory/BUILD.gn
@@ -19,6 +19,6 @@
"//zircon/public/lib/trace-provider",
]
- # codec_factory spawns codec_runner_sw_omx(s) which is a peer package for now,
- # so no dep is needed here for codec_runner_sw_omx.
+ # codec_factory spawns codec_runner_sw_ffmpeg(s) which is a peer package for now,
+ # so no dep is needed here for codec_runner_sw_ffmpeg.
}
diff --git a/garnet/bin/media/codec_factory/codec_factory_impl.cc b/garnet/bin/media/codec_factory/codec_factory_impl.cc
index 9533597..e70c0ec 100644
--- a/garnet/bin/media/codec_factory/codec_factory_impl.cc
+++ b/garnet/bin/media/codec_factory/codec_factory_impl.cc
@@ -8,21 +8,20 @@
namespace {
-// TODO(dustingreen): Other types of isolates can exist. For some codecs we may
-// not use an isolate and instead delegate to the client end of a CodecFactory
-// instance that we got via other means (not by starting an isolate, but via a
-// factory registration initiated by a driver process, or by discovering a
-// device, or similar).
-const std::string kIsolateUrlOmx =
- "fuchsia-pkg://fuchsia.com/codec_runner_sw_omx#meta/"
- "codec_runner_sw_omx.cmx";
+// Other types of SW isolates can exist, but at the moment we only have one,
+// which uses ffmpeg for SW decode (or potentially encode).
+//
+// For HW-based codecs, we discover their "LocalCodecFactory" by watching for
+// their device and sending the server end of a (local) CodecFactory to the
+// driver.
const std::string kIsolateUrlFfmpeg =
"fuchsia-pkg://fuchsia.com/codec_runner_sw_ffmpeg#meta/"
"codec_runner_sw_ffmpeg.cmx";
-// TODO(turnage): Devise a better routing system between ffmpeg and omx codec
-// factories. Using this should be fine for now because omx does not service
-// this mime type and it is the first one we will roll out with ffmpeg.
+// TODO(turnage): Devise a better routing system between SW-based codec
+// factories. Using this should be fine for now since this is the first/only
+// type that we use ffmpeg for and we don't currently have any other SW-based
+// codecs.
const std::string kFfmpegMimeType = "video/h264";
} // namespace
@@ -63,6 +62,11 @@
void CodecFactoryImpl::OwnSelf(std::unique_ptr<CodecFactoryImpl> self) {
binding_ = std::make_unique<BindingType>(
std::move(self), std::move(channel_temp_), app_->loop()->dispatcher());
+ binding_->set_error_handler([this](zx_status_t status){
+ FXL_LOG(INFO) << "CodecFactoryImpl channel failed (INFO) - status: " << status;
+ // this will also ~this
+ binding_.reset();
+ });
}
void CodecFactoryImpl::CreateDecoder(
@@ -116,7 +120,8 @@
if (params.input_details().mime_type() == kFfmpegMimeType) {
url = kIsolateUrlFfmpeg;
} else {
- url = kIsolateUrlOmx;
+ // ~decoder
+ return;
}
launch_info.url = url;
launch_info.directory_request = services.NewRequest();
diff --git a/garnet/bin/media/codecs/meta/codec_runner_sw_ffmpeg.cmx b/garnet/bin/media/codecs/meta/codec_runner_sw_ffmpeg.cmx
index 0d40751..0a1060c 100644
--- a/garnet/bin/media/codecs/meta/codec_runner_sw_ffmpeg.cmx
+++ b/garnet/bin/media/codecs/meta/codec_runner_sw_ffmpeg.cmx
@@ -5,7 +5,8 @@
"sandbox": {
"services": [
"fuchsia.sys.Launcher",
- "fuchsia.tracelink.Registry"
+ "fuchsia.tracelink.Registry",
+ "fuchsia.sysmem.Allocator"
]
}
}
diff --git a/garnet/bin/media/codecs/sw/codec_adapter_sw.h b/garnet/bin/media/codecs/sw/codec_adapter_sw.h
index d7f94b5..ee0a3da 100644
--- a/garnet/bin/media/codecs/sw/codec_adapter_sw.h
+++ b/garnet/bin/media/codecs/sw/codec_adapter_sw.h
@@ -52,6 +52,14 @@
return false;
}
+ bool IsCoreCodecMappedBufferNeeded(CodecPort port) override {
+ return true;
+ }
+
+ bool IsCoreCodecHwBased() override {
+ return false;
+ }
+
void CoreCodecInit(const fuchsia::media::FormatDetails&
initial_input_format_details) override {
if (!initial_input_format_details.has_format_details_version_ordinal()) {
@@ -74,6 +82,21 @@
}
}
+ void CoreCodecAddBuffer(CodecPort port, const CodecBuffer* buffer) override {
+ if (port != kOutputPort) {
+ return;
+ }
+ // TODO(turnage): The core codec must not emit any output (format, AUs, or
+ // EOS) until CoreCodecMidStreamOutputBufferReConfigFinish has been called.
+ output_buffer_pool_.AddBuffer(buffer);
+ }
+
+ void CoreCodecConfigureBuffers(
+ CodecPort port,
+ const std::vector<std::unique_ptr<CodecPacket>>& packets) override {
+ // Nothing to do here.
+ }
+
void CoreCodecStartStream() override {
// It's ok for RecycleInputPacket to make a packet free anywhere in this
// sequence. Nothing else ought to be happening during CoreCodecStartStream
@@ -90,13 +113,6 @@
post_result);
}
- void CoreCodecAddBuffer(CodecPort port, const CodecBuffer* buffer) override {
- if (port != kOutputPort) {
- return;
- }
- output_buffer_pool_.AddBuffer(buffer);
- }
-
void CoreCodecQueueInputFormatDetails(
const fuchsia::media::FormatDetails& per_stream_override_format_details)
override {
@@ -139,12 +155,6 @@
}
}
- void CoreCodecConfigureBuffers(
- CodecPort port,
- const std::vector<std::unique_ptr<CodecPacket>>& packets) override {
- // Nothing to do here.
- }
-
void CoreCodecRecycleOutputPacket(CodecPacket* packet) override {
if (packet->buffer()) {
LocalOutput local_output;
@@ -187,21 +197,23 @@
void CoreCodecMidStreamOutputBufferReConfigFinish() override {
// Nothing to do here for now.
+ //
+ // TODO(turnage): The core codec must not emit any output (format, AUs, or
+ // EOS) until CoreCodecMidStreamOutputBufferReConfigFinish has been called.
}
- std::unique_ptr<const fuchsia::media::StreamOutputConfig>
- CoreCodecBuildNewOutputConfig(
+ std::unique_ptr<const fuchsia::media::StreamOutputConstraints>
+ CoreCodecBuildNewOutputConstraints(
uint64_t stream_lifetime_ordinal,
uint64_t new_output_buffer_constraints_version_ordinal,
- uint64_t new_output_format_details_version_ordinal,
bool buffer_constraints_action_required) override {
auto [format_details, per_packet_buffer_bytes] = OutputFormatDetails();
- auto config = std::make_unique<fuchsia::media::StreamOutputConfig>();
+ auto config = std::make_unique<fuchsia::media::StreamOutputConstraints>();
config->set_stream_lifetime_ordinal(stream_lifetime_ordinal);
- // For the moment, there will be only one StreamOutputConfig, and it'll need
+ // For the moment, there will be only one StreamOutputConstraints, and it'll need
// output buffers configured for it.
ZX_DEBUG_ASSERT(buffer_constraints_action_required);
config->set_buffer_constraints_action_required(
@@ -243,13 +255,22 @@
default_settings->set_per_packet_buffer_bytes(per_packet_buffer_bytes);
default_settings->set_single_buffer_mode(false);
- *config->mutable_format_details() = std::move(format_details);
- config->mutable_format_details()->set_format_details_version_ordinal(
- new_output_format_details_version_ordinal);
-
return config;
}
+ fuchsia::media::StreamOutputFormat CoreCodecGetOutputFormat(
+ uint64_t stream_lifetime_ordinal,
+ uint64_t new_output_format_details_version_ordinal) override {
+ fuchsia::media::StreamOutputFormat result;
+ // format_details is fuchsia::media::FormatDetails
+ auto [format_details, per_packet_buffer_bytes] = OutputFormatDetails();
+ result.set_stream_lifetime_ordinal(stream_lifetime_ordinal);
+ result.set_format_details(std::move(format_details));
+ result.mutable_format_details()->set_format_details_version_ordinal(
+ new_output_format_details_version_ordinal);
+ return result;
+ }
+
protected:
void WaitForInputProcessingLoopToEnd() {
ZX_DEBUG_ASSERT(thrd_current() != input_processing_thread_);
diff --git a/garnet/bin/media/codecs/sw/codec_runner_app.h b/garnet/bin/media/codecs/sw/codec_runner_app.h
index 538287a..e3dd0cf 100644
--- a/garnet/bin/media/codecs/sw/codec_runner_app.h
+++ b/garnet/bin/media/codecs/sw/codec_runner_app.h
@@ -43,9 +43,12 @@
.deprecated_services()
->RemoveService<fuchsia::mediacodec::CodecFactory>();
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem;
+ startup_context_->ConnectToEnvironmentService(sysmem.NewRequest());
+
codec_factory_ =
std::make_unique<LocalSingleCodecFactory<Decoder, Encoder>>(
- loop_.dispatcher(), std::move(request),
+ loop_.dispatcher(), std::move(sysmem), std::move(request),
[this](
std::unique_ptr<CodecImpl> created_codec_instance) {
// Own codec implementation and bind it.
@@ -75,4 +78,4 @@
std::unique_ptr<CodecImpl> codec_instance_;
};
-#endif // GARNET_BIN_MEDIA_CODECS_SW_CODEC_RUNNER_APP_H_
\ No newline at end of file
+#endif // GARNET_BIN_MEDIA_CODECS_SW_CODEC_RUNNER_APP_H_
diff --git a/garnet/bin/media/codecs/sw/ffmpeg/avcodec_context.cc b/garnet/bin/media/codecs/sw/ffmpeg/avcodec_context.cc
index e4baa42..038d9e6 100644
--- a/garnet/bin/media/codecs/sw/ffmpeg/avcodec_context.cc
+++ b/garnet/bin/media/codecs/sw/ffmpeg/avcodec_context.cc
@@ -173,10 +173,6 @@
frame->sample_aspect_ratio.den;
}
- // TODO(dustingreen): Switching to FIDL table should make this not be
- // required.
- uncompressed_format.special_formats.set_temp_field_todo_remove(0);
-
size_t buffer_bytes_needed = av_image_get_buffer_size(
static_cast<AVPixelFormat>(frame->format), frame->width, frame->height,
/*align=*/1);
diff --git a/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_decoder.cc b/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_decoder.cc
index 20f0a22..0ec0b6f 100644
--- a/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_decoder.cc
+++ b/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_decoder.cc
@@ -26,6 +26,19 @@
} // namespace
+namespace {
+
+// A client using the min shouldn't necessarily expect performance to be
+// acceptable when running higher bit-rates.
+constexpr uint32_t kInputPerPacketBufferBytesMin = 8 * 1024;
+// This is an arbitrary cap for now.
+constexpr uint32_t kInputPerPacketBufferBytesMax = 4 * 1024 * 1024;
+
+// For now, this is the forced packet count for output.
+static constexpr uint32_t kOutputPacketCount = 21;
+
+} // namespace
+
CodecAdapterFfmpegDecoder::CodecAdapterFfmpegDecoder(
std::mutex& lock, CodecAdapterEvents* codec_adapter_events)
: CodecAdapterSW(lock, codec_adapter_events) {}
@@ -136,7 +149,7 @@
}
if (should_config_output) {
- events_->onCoreCodecMidStreamOutputConfigChange(
+ events_->onCoreCodecMidStreamOutputConstraintsChange(
/*output_re_config_required=*/need_new_buffers);
}
@@ -224,4 +237,167 @@
/*error_detected_before=*/false,
/*error_detected_during=*/false);
}
-}
\ No newline at end of file
+}
+
+fuchsia::sysmem::BufferCollectionConstraints
+CodecAdapterFfmpegDecoder::CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) {
+ std::lock_guard<std::mutex> lock(lock_);
+
+ fuchsia::sysmem::BufferCollectionConstraints result;
+
+ // For now, we didn't report support for single_buffer_mode, and CodecImpl
+ // will have failed the codec already by this point if the client tried to
+ // use single_buffer_mode.
+ //
+ // TODO(dustingreen): Support single_buffer_mode on input (only).
+ ZX_DEBUG_ASSERT(!partial_settings.has_single_buffer_mode() || !partial_settings.single_buffer_mode());
+ // The CodecImpl won't hand us the sysmem token, so we shouldn't expect to
+ // have the token here.
+ ZX_DEBUG_ASSERT(!partial_settings.has_sysmem_token());
+
+ ZX_DEBUG_ASSERT(partial_settings.has_packet_count_for_server());
+ ZX_DEBUG_ASSERT(partial_settings.has_packet_count_for_client());
+ uint32_t packet_count = partial_settings.packet_count_for_server() + partial_settings.packet_count_for_client();
+
+ // For now this is true - when we plumb more flexible buffer count range this
+ // will change to account for a range.
+ ZX_DEBUG_ASSERT(port != kOutputPort || packet_count == kOutputPacketCount);
+
+ // TODO(MTWN-250): plumb/permit range of buffer count from further down,
+ // instead of single number frame_count, and set this to the actual
+ // stream-required # of reference frames + # that can concurrently decode.
+ // Packets and buffers are not the same thing, and we should permit the # of
+ // packets to be >= the # of buffers. We shouldn't be
+ // allocating buffers on behalf of the client here, but until we plumb the
+ // range of frame_count and are more flexible on # of allocated buffers, we
+ // have to make sure there are at least as many buffers as packets. We
+ // categorize the buffers as for camping and for slack. This should change to
+ // be just the buffers needed for camping and maybe 1 for shared slack. If
+ // the client wants more buffers the client can demand buffers in its own
+ // fuchsia::sysmem::BufferCollection::SetConstraints().
+ result.min_buffer_count_for_camping = partial_settings.packet_count_for_server();
+ ZX_DEBUG_ASSERT(result.min_buffer_count_for_dedicated_slack == 0);
+ ZX_DEBUG_ASSERT(result.min_buffer_count_for_shared_slack == 0);
+ // TODO: Uncap max_buffer_count, have both sides infer that packet count is
+ // at least as many as buffer_count.
+ result.max_buffer_count = packet_count;
+
+ uint32_t per_packet_buffer_bytes_min;
+ uint32_t per_packet_buffer_bytes_max;
+ if (port == kInputPort) {
+ per_packet_buffer_bytes_min = kInputPerPacketBufferBytesMin;
+ per_packet_buffer_bytes_max = kInputPerPacketBufferBytesMax;
+ } else {
+ ZX_ASSERT(decoded_output_info_.has_value());
+ auto& [uncompressed_format, per_packet_buffer_bytes] =
+ decoded_output_info_.value();
+
+ ZX_DEBUG_ASSERT(port == kOutputPort);
+ // NV12, based on min stride.
+ per_packet_buffer_bytes_min = uncompressed_format.primary_line_stride_bytes * uncompressed_format.primary_height_pixels * 3 / 2;
+ // At least for now, don't cap the per-packet buffer size for output. The
+ // HW only cares about the portion we set up for output anyway, and the
+ // client has no way to force output to occur into portions of the output
+ // buffer beyond what's implied by the max supported image dimensions.
+ per_packet_buffer_bytes_max = 0xFFFFFFFF;
+ }
+
+ result.has_buffer_memory_constraints = true;
+ result.buffer_memory_constraints.min_size_bytes = per_packet_buffer_bytes_min;
+ result.buffer_memory_constraints.max_size_bytes = per_packet_buffer_bytes_max;
+
+ // These are all false because SW decode.
+ result.buffer_memory_constraints.physically_contiguous_required = false;
+ result.buffer_memory_constraints.secure_required = false;
+ result.buffer_memory_constraints.secure_permitted = false;
+
+ if (port == kOutputPort) {
+ ZX_ASSERT(decoded_output_info_.has_value());
+ auto& [uncompressed_format, per_packet_buffer_bytes] =
+ decoded_output_info_.value();
+
+ result.image_format_constraints_count = 1;
+ fuchsia::sysmem::ImageFormatConstraints& image_constraints =
+ result.image_format_constraints[0];
+ image_constraints.pixel_format.type = fuchsia::sysmem::PixelFormatType::YV12;
+ // TODO(MTWN-251): confirm that REC709 is always what we want here, or plumb
+ // actual YUV color space if it can ever be REC601_*. Since 2020 and 2100
+ // are minimum 10 bits per Y sample and we're outputting NV12, 601 is the
+ // only other potential possibility here.
+ image_constraints.color_spaces_count = 1;
+ image_constraints.color_space[0].type =
+ fuchsia::sysmem::ColorSpaceType::REC709;
+
+ // The non-"required_" fields indicate the decoder's ability to potentially
+ // output frames at various dimensions as coded in the stream. Aside from
+ // the current stream being somewhere in these bounds, these have nothing to
+ // do with the current stream in particular.
+ image_constraints.min_coded_width = 16;
+ image_constraints.max_coded_width = 3840;
+ image_constraints.min_coded_height = 16;
+ // This intentionally isn't the height of a 4k frame. See
+ // max_coded_width_times_coded_height. We intentionally constrain the max
+ // dimension in width or height to the width of a 4k frame. While the HW
+ // might be able to go bigger than that as long as the other dimension is
+ // smaller to compensate, we don't really need to enable any larger than
+ // 4k's width in either dimension, so we don't.
+ image_constraints.max_coded_height = 3840;
+ image_constraints.min_bytes_per_row = 16;
+ // no hard-coded max stride, at least for now
+ image_constraints.max_bytes_per_row = 0xFFFFFFFF;
+ image_constraints.max_coded_width_times_coded_height = 3840 * 2160;
+ image_constraints.layers = 1;
+ image_constraints.coded_width_divisor = 16;
+ image_constraints.coded_height_divisor = 16;
+ image_constraints.bytes_per_row_divisor = 16;
+ // TODO(dustingreen): Since this is a producer that will always produce at
+ // offset 0 of a physical page, we don't really care if this field is
+ // consistent with any constraints re. what the HW can do.
+ image_constraints.start_offset_divisor = 1;
+ // Odd display dimensions are permitted, but these don't imply odd YV12
+ // dimensions - those are constrainted by coded_width_divisor and
+ // coded_height_divisor which are both 16.
+ image_constraints.display_width_divisor = 1;
+ image_constraints.display_height_divisor = 1;
+
+ // The decoder is producing frames and the decoder has no choice but to
+ // produce frames at their coded size. The decoder wants to potentially be
+ // able to support a stream with dynamic resolution, potentially including
+ // dimensions both less than and greater than the dimensions that led to the
+ // current need to allocate a BufferCollection. For this reason, the
+ // required_ fields are set to the exact current dimensions, and the
+ // permitted (non-required_) fields is set to the full potential range that
+ // the decoder could potentially output. If an initiator wants to require a
+ // larger range of dimensions that includes the required range indicated
+ // here (via a-priori knowledge of the potential stream dimensions), an
+ // initiator is free to do so.
+ image_constraints.required_min_coded_width = uncompressed_format.primary_width_pixels;
+ image_constraints.required_max_coded_width = uncompressed_format.primary_width_pixels;
+ image_constraints.required_min_coded_height = uncompressed_format.primary_height_pixels;
+ image_constraints.required_max_coded_height = uncompressed_format.primary_height_pixels;
+ // As needed we might want to plumb more flexibility for the stride.
+ image_constraints.required_min_bytes_per_row = uncompressed_format.primary_line_stride_bytes;
+ image_constraints.required_max_bytes_per_row = uncompressed_format.primary_line_stride_bytes;
+ } else {
+ ZX_DEBUG_ASSERT(result.image_format_constraints_count == 0);
+ }
+
+ // We don't have to fill out usage - CodecImpl takes care of that.
+ ZX_DEBUG_ASSERT(!result.usage.cpu);
+ ZX_DEBUG_ASSERT(!result.usage.display);
+ ZX_DEBUG_ASSERT(!result.usage.vulkan);
+ ZX_DEBUG_ASSERT(!result.usage.video);
+
+ return result;
+}
+
+void CodecAdapterFfmpegDecoder::CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) {
+ // TODO: Should uncap max_buffer_count and stop asserting this, or assert
+ // instead that buffer_count >= buffers for camping + dedicated slack.
+ ZX_DEBUG_ASSERT(port != kOutputPort || buffer_collection_info.buffer_count == kOutputPacketCount);
+}
diff --git a/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_decoder.h b/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_decoder.h
index 3619b30..5495a75 100644
--- a/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_decoder.h
+++ b/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_decoder.h
@@ -17,6 +17,16 @@
CodecAdapterEvents* codec_adapter_events);
~CodecAdapterFfmpegDecoder();
+ fuchsia::sysmem::BufferCollectionConstraints
+ CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) override;
+
+ void CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) override;
+
protected:
// Processes input in a loop. Should only execute on input_processing_thread_.
// Loops for the lifetime of a stream.
diff --git a/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_encoder.cc b/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_encoder.cc
index 4d80a0c..567798a 100644
--- a/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_encoder.cc
+++ b/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_encoder.cc
@@ -14,6 +14,20 @@
CodecAdapterFfmpegEncoder::~CodecAdapterFfmpegEncoder() = default;
+fuchsia::sysmem::BufferCollectionConstraints
+CodecAdapterFfmpegEncoder::CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) {
+ ZX_ASSERT_MSG(false, "Not implemented.");
+}
+
+void CodecAdapterFfmpegEncoder::CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) {
+ ZX_ASSERT(false && "not yet implemented");
+}
+
void CodecAdapterFfmpegEncoder::ProcessInputLoop() {
ZX_ASSERT_MSG(false, "Not implemented.");
}
@@ -26,4 +40,4 @@
CodecAdapterFfmpegEncoder::OutputFormatDetails() {
ZX_ASSERT_MSG(false, "Not implemented.");
return {fuchsia::media::FormatDetails{}, 0};
-}
\ No newline at end of file
+}
diff --git a/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_encoder.h b/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_encoder.h
index 8bccf7c..d644476 100644
--- a/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_encoder.h
+++ b/garnet/bin/media/codecs/sw/ffmpeg/codec_adapter_ffmpeg_encoder.h
@@ -16,6 +16,16 @@
CodecAdapterEvents* codec_adapter_events);
~CodecAdapterFfmpegEncoder();
+ fuchsia::sysmem::BufferCollectionConstraints
+ CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) override;
+
+ void CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) override;
+
protected:
// Processes input in a loop. Should only execute on input_processing_thread_.
// Loops for the lifetime of a stream.
diff --git a/garnet/bin/media/codecs/sw/local_single_codec_factory.h b/garnet/bin/media/codecs/sw/local_single_codec_factory.h
index 4bd7233d..4857ab1 100644
--- a/garnet/bin/media/codecs/sw/local_single_codec_factory.h
+++ b/garnet/bin/media/codecs/sw/local_single_codec_factory.h
@@ -25,11 +25,13 @@
public:
LocalSingleCodecFactory(
async_dispatcher_t* fidl_dispatcher,
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
fidl::InterfaceRequest<CodecFactory> request,
fit::function<void(std::unique_ptr<CodecImpl>)> factory_done_callback,
CodecAdmissionControl* codec_admission_control,
fit::function<void(zx_status_t)> error_handler)
: fidl_dispatcher_(fidl_dispatcher),
+ sysmem_(std::move(sysmem)),
binding_(this),
factory_done_callback_(std::move(factory_done_callback)),
codec_admission_control_(codec_admission_control) {
@@ -69,7 +71,14 @@
return;
}
+ if (!sysmem_) {
+ printf("VendCodecAdapter() only meant to be used once per LocalSingleCodecFactory\n");
+ // ~codec_request closes channel.
+ return;
+ }
+
auto codec_impl = std::make_unique<CodecImpl>(
+ std::move(sysmem_),
std::move(codec_admission), fidl_dispatcher_, thrd_current(),
std::make_unique<Params>(std::move(params)),
std::move(codec_request));
@@ -100,6 +109,7 @@
}
async_dispatcher_t* fidl_dispatcher_;
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem_;
fidl::Binding<CodecFactory, LocalSingleCodecFactory*> binding_;
// Returns the codec implementation and requests drop of self.
fit::function<void(std::unique_ptr<CodecImpl>)> factory_done_callback_;
diff --git a/garnet/bin/media/codecs/test/test/raw_frames_test.cc b/garnet/bin/media/codecs/test/test/raw_frames_test.cc
index 297b135..e7e884f 100644
--- a/garnet/bin/media/codecs/test/test/raw_frames_test.cc
+++ b/garnet/bin/media/codecs/test/test/raw_frames_test.cc
@@ -90,12 +90,12 @@
std::optional<RawFrames::Image> frame;
size_t frames_sent = 0;
while ((frame = raw_frames.Frame(frames_sent++))) {
- auto config = std::make_shared<fuchsia::media::StreamOutputConfig>();
- config->mutable_format_details()
+ auto format = std::make_shared<fuchsia::media::StreamOutputFormat>();
+ format->mutable_format_details()
->mutable_domain()
->video()
.set_uncompressed(std::move(frame->format));
- frame_sink->PutFrame(frames_sent, frame->vmo, frame->vmo_offset, config,
+ frame_sink->PutFrame(frames_sent, frame->vmo, frame->vmo_offset, format,
[] {});
}
diff --git a/garnet/drivers/video/amlogic-decoder/BUILD.gn b/garnet/drivers/video/amlogic-decoder/BUILD.gn
index 0c79433..5d599e7 100644
--- a/garnet/drivers/video/amlogic-decoder/BUILD.gn
+++ b/garnet/drivers/video/amlogic-decoder/BUILD.gn
@@ -110,6 +110,7 @@
]
public_deps = [
"//zircon/public/banjo/ddk-protocol-amlogiccanvas",
+ "//zircon/public/banjo/ddk-protocol-sysmem",
"//zircon/public/lib/async-cpp",
"//zircon/public/lib/async-loop-cpp",
"//zircon/public/lib/ddk",
diff --git a/garnet/drivers/video/amlogic-decoder/amlogic-video.cc b/garnet/drivers/video/amlogic-decoder/amlogic-video.cc
index b2cbe35..de1e885 100644
--- a/garnet/drivers/video/amlogic-decoder/amlogic-video.cc
+++ b/garnet/drivers/video/amlogic-decoder/amlogic-video.cc
@@ -658,27 +658,48 @@
video_decoder_->SwappedIn();
}
+fidl::InterfaceHandle<fuchsia::sysmem::Allocator> AmlogicVideo::ConnectToSysmem() {
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> client_end;
+ fidl::InterfaceRequest<fuchsia::sysmem::Allocator> server_end =
+ client_end.NewRequest();
+ zx_status_t connect_status =
+ sysmem_connect(&sysmem_, server_end.TakeChannel().release());
+ if (connect_status != ZX_OK) {
+ // failure
+ return fidl::InterfaceHandle<fuchsia::sysmem::Allocator>();
+ }
+ return client_end;
+}
+
zx_status_t AmlogicVideo::InitRegisters(zx_device_t* parent) {
parent_ = parent;
zx_status_t status =
device_get_protocol(parent_, ZX_PROTOCOL_PDEV, &pdev_);
-
if (status != ZX_OK) {
DECODE_ERROR("Failed to get parent protocol");
return ZX_ERR_NO_MEMORY;
}
+
+ status = device_get_protocol(parent_, ZX_PROTOCOL_SYSMEM, &sysmem_);
+ if (status != ZX_OK) {
+ DECODE_ERROR("Could not get SYSMEM protocol\n");
+ return status;
+ }
+
status = device_get_protocol(parent_, ZX_PROTOCOL_AMLOGIC_CANVAS, &canvas_);
if (status != ZX_OK) {
DECODE_ERROR("Could not get video CANVAS protocol\n");
return status;
}
+
pdev_device_info_t info;
status = pdev_get_device_info(&pdev_, &info);
if (status != ZX_OK) {
DECODE_ERROR("pdev_get_device_info failed");
return status;
}
+
switch (info.pid) {
case PDEV_PID_AMLOGIC_S912:
device_type_ = DeviceType::kGXM;
diff --git a/garnet/drivers/video/amlogic-decoder/amlogic-video.h b/garnet/drivers/video/amlogic-decoder/amlogic-video.h
index 3f916ca2..8bfdd9a 100644
--- a/garnet/drivers/video/amlogic-decoder/amlogic-video.h
+++ b/garnet/drivers/video/amlogic-decoder/amlogic-video.h
@@ -11,6 +11,7 @@
#include <ddk/driver.h>
#include <ddk/protocol/amlogiccanvas.h>
#include <ddk/protocol/platform/device.h>
+#include <ddk/protocol/sysmem.h>
#include <zircon/errors.h>
#include <zircon/syscalls.h>
#include <lib/zx/handle.h>
@@ -150,6 +151,10 @@
__WARN_UNUSED_RESULT zx_status_t AllocateStreamBuffer(StreamBuffer* buffer,
uint32_t size);
+ // This gets started connecting to sysmem, but returns an InterfaceHandle
+ // instead of InterfacePtr so that the caller can bind to the dispatcher.
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> ConnectToSysmem();
+
private:
friend class TestH264;
friend class TestMpeg2;
@@ -167,8 +172,9 @@
void SwapInCurrentInstance() __TA_REQUIRES(video_decoder_lock_);
zx_device_t* parent_ = nullptr;
- pdev_protocol_t pdev_;
- amlogic_canvas_protocol_t canvas_;
+ pdev_protocol_t pdev_{};
+ sysmem_protocol_t sysmem_{};
+ amlogic_canvas_protocol_t canvas_{};
DeviceType device_type_ = DeviceType::kUnknown;
std::unique_ptr<CbusRegisterIo> cbus_;
std::unique_ptr<DosRegisterIo> dosbus_;
diff --git a/garnet/drivers/video/amlogic-decoder/codec_adapter_h264.cc b/garnet/drivers/video/amlogic-decoder/codec_adapter_h264.cc
index 0d49c16..7e779e4 100644
--- a/garnet/drivers/video/amlogic-decoder/codec_adapter_h264.cc
+++ b/garnet/drivers/video/amlogic-decoder/codec_adapter_h264.cc
@@ -17,8 +17,8 @@
// the HW and send it to the Codec client, the other part to configure
// output buffers once the client has configured Codec output config based
// on the format info. Wire up so that
-// onCoreCodecMidStreamOutputConfigChange() gets called and so that
-// CoreCodecBuildNewOutputConfig() will pick up the correct current format
+// onCoreCodecMidStreamOutputConstraintsChange() gets called and so that
+// CoreCodecBuildNewOutputConstraints() will pick up the correct current format
// info (whether still mid-stream, or at the start of a new stream that's
// starting before the mid-stream format change was processed for the old
// stream).
@@ -86,6 +86,16 @@
(static_cast<uint32_t>(b) << 8) | static_cast<uint32_t>(a);
}
+// A client using the min shouldn't necessarily expect performance to be
+// acceptable when running higher bit-rates.
+//
+// TODO(MTWN-249): Set this to ~8k or so. For now, we have to boost the
+// per-packet buffer size up to fit the largest AUs we expect to decode, until
+// MTWN-249 is fixed, in case avcC format is used.
+constexpr uint32_t kInputPerPacketBufferBytesMin = 512 * 1024;
+// This is an arbitrary cap for now.
+constexpr uint32_t kInputPerPacketBufferBytesMax = 4 * 1024 * 1024;
+
} // namespace
CodecAdapterH264::CodecAdapterH264(std::mutex& lock,
@@ -113,6 +123,23 @@
return false;
}
+bool CodecAdapterH264::IsCoreCodecMappedBufferNeeded(CodecPort port) {
+ // If protected buffers, then only in-band AnnexB is supported, because in
+ // that case we can't implement conversion of OOB AnnexB-like oob_bytes to
+ // in-band AnnexB (at least not yet, since that would require one buffer to be
+ // in normal non-protected RAM), and we can't implement avcC format conversion
+ // to AnnexB, because that would require the CPU reading from input buffers.
+ //
+ // TODO(dustingreen): Make the previous paragraph true. For now we report
+ // true here to ensure we don't get secure buffers since the rest of this
+ // class doesn't yet constrain what it can do based on is_secure or not.
+ return true;
+}
+
+bool CodecAdapterH264::IsCoreCodecHwBased() {
+ return true;
+}
+
void CodecAdapterH264::CoreCodecInit(
const fuchsia::media::FormatDetails& initial_input_format_details) {
zx_status_t result = input_processing_loop_.StartThread(
@@ -324,18 +351,25 @@
void CodecAdapterH264::CoreCodecAddBuffer(CodecPort port,
const CodecBuffer* buffer) {
+ if (port != kOutputPort) {
+ return;
+ }
all_output_buffers_.push_back(buffer);
}
void CodecAdapterH264::CoreCodecConfigureBuffers(
CodecPort port, const std::vector<std::unique_ptr<CodecPacket>>& packets) {
- if (port == kOutputPort) {
- ZX_DEBUG_ASSERT(all_output_packets_.empty());
- ZX_DEBUG_ASSERT(!all_output_buffers_.empty());
- ZX_DEBUG_ASSERT(all_output_buffers_.size() == packets.size());
- for (auto& packet : packets) {
- all_output_packets_.push_back(packet.get());
- }
+ if (port != kOutputPort) {
+ return;
+ }
+ ZX_DEBUG_ASSERT(all_output_packets_.empty());
+ ZX_DEBUG_ASSERT(!all_output_buffers_.empty());
+ // TODO(dustingreen): Remove this assert - this CodecAdapter needs to stop
+ // forcing this to be true. Or, set packet count based on buffer collection
+ // buffer_count, or enforce that packet count is >= buffer_count.
+ ZX_DEBUG_ASSERT(all_output_buffers_.size() == packets.size());
+ for (auto& packet : packets) {
+ all_output_packets_.push_back(packet.get());
}
}
@@ -399,11 +433,10 @@
}
}
-std::unique_ptr<const fuchsia::media::StreamOutputConfig>
-CodecAdapterH264::CoreCodecBuildNewOutputConfig(
+std::unique_ptr<const fuchsia::media::StreamOutputConstraints>
+CodecAdapterH264::CoreCodecBuildNewOutputConstraints(
uint64_t stream_lifetime_ordinal,
uint64_t new_output_buffer_constraints_version_ordinal,
- uint64_t new_output_format_details_version_ordinal,
bool buffer_constraints_action_required) {
// bear.h264 decodes into 320x192 YUV buffers, but the video display
// dimensions are 320x180. A the bottom of the buffer only .25 of the last
@@ -429,17 +462,17 @@
// to camp on more frames than this.
constexpr uint32_t kDefaultPacketCountForClient = kPacketCountForClientForced;
- uint32_t per_packet_buffer_bytes = stride_ * height_ * 3 / 2;
+ uint32_t per_packet_buffer_bytes = min_stride_ * height_ * 3 / 2;
- std::unique_ptr<fuchsia::media::StreamOutputConfig> config =
- std::make_unique<fuchsia::media::StreamOutputConfig>();
+ std::unique_ptr<fuchsia::media::StreamOutputConstraints> config =
+ std::make_unique<fuchsia::media::StreamOutputConstraints>();
config->set_stream_lifetime_ordinal(stream_lifetime_ordinal);
auto* constraints = config->mutable_buffer_constraints();
auto* default_settings = constraints->mutable_default_settings();
- // For the moment, there will be only one StreamOutputConfig, and it'll need
+ // For the moment, there will be only one StreamOutputConstraints, and it'll need
// output buffers configured for it.
ZX_DEBUG_ASSERT(buffer_constraints_action_required);
config->set_buffer_constraints_action_required(
@@ -495,9 +528,174 @@
// not the client.
constraints->set_very_temp_kludge_bti_handle(std::move(very_temp_kludge_bti));
- config->mutable_format_details()->set_format_details_version_ordinal(
+ return config;
+}
+
+fuchsia::sysmem::BufferCollectionConstraints
+CodecAdapterH264::CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) {
+ fuchsia::sysmem::BufferCollectionConstraints result;
+
+ // For now, we didn't report support for single_buffer_mode, and CodecImpl
+ // will have failed the codec already by this point if the client tried to
+ // use single_buffer_mode.
+ //
+ // TODO(dustingreen): Support single_buffer_mode on input (only).
+ ZX_DEBUG_ASSERT(!partial_settings.has_single_buffer_mode() || !partial_settings.single_buffer_mode());
+ // The CodecImpl won't hand us the sysmem token, so we shouldn't expect to
+ // have the token here.
+ ZX_DEBUG_ASSERT(!partial_settings.has_sysmem_token());
+
+ ZX_DEBUG_ASSERT(partial_settings.has_packet_count_for_server());
+ ZX_DEBUG_ASSERT(partial_settings.has_packet_count_for_client());
+ uint32_t packet_count =
+ partial_settings.packet_count_for_server() +
+ partial_settings.packet_count_for_client();
+
+ // For now this is true - when we plumb more flexible buffer count range this
+ // will change to account for a range.
+ ZX_DEBUG_ASSERT(port != kOutputPort || packet_count == packet_count_total_);
+
+ // TODO(MTWN-250): plumb/permit range of buffer count from further down,
+ // instead of single number frame_count, and set this to the actual
+ // stream-required # of reference frames + # that can concurrently decode.
+ // For the moment we demand that buffer_count equals packet_count equals
+ // packet_count_for_server() + packet_count_for_client(), which is too
+ // inflexible. Also, we rely on the server setting exactly and only
+ // min_buffer_count_for_camping to packet_count_for_server() and the client
+ // setting exactly and only min_buffer_count_for_camping to
+ // packet_count_for_client().
+ result.min_buffer_count_for_camping =
+ partial_settings.packet_count_for_server();
+ // Some slack is nice overall, but avoid having each participant ask for
+ // dedicated slack. Using sysmem the client will ask for it's own buffers for
+ // camping and any slack, so the codec doesn't need to ask for any extra on
+ // behalf of the client.
+ ZX_DEBUG_ASSERT(result.min_buffer_count_for_dedicated_slack == 0);
+ ZX_DEBUG_ASSERT(result.min_buffer_count_for_shared_slack == 0);
+ result.max_buffer_count = packet_count;
+
+ uint32_t per_packet_buffer_bytes_min;
+ uint32_t per_packet_buffer_bytes_max;
+ if (port == kInputPort) {
+ per_packet_buffer_bytes_min = kInputPerPacketBufferBytesMin;
+ per_packet_buffer_bytes_max = kInputPerPacketBufferBytesMax;
+ } else {
+ ZX_DEBUG_ASSERT(port == kOutputPort);
+ // NV12, based on min stride.
+ per_packet_buffer_bytes_min = min_stride_ * height_ * 3 / 2;
+ // At least for now, don't cap the per-packet buffer size for output. The
+ // HW only cares about the portion we set up for output anyway, and the
+ // client has no way to force output to occur into portions of the output
+ // buffer beyond what's implied by the max supported image dimensions.
+ per_packet_buffer_bytes_max = 0xFFFFFFFF;
+ }
+
+ result.has_buffer_memory_constraints = true;
+ result.buffer_memory_constraints.min_size_bytes = per_packet_buffer_bytes_min;
+ result.buffer_memory_constraints.max_size_bytes = per_packet_buffer_bytes_max;
+ // amlogic requires physically contiguous on both input and output
+ result.buffer_memory_constraints.physically_contiguous_required = true;
+ result.buffer_memory_constraints.secure_required = false;
+ // This isn't expected to fully work at first, but allow getting as far as we
+ // can.
+ result.buffer_memory_constraints.secure_permitted = true;
+
+ if (port == kOutputPort) {
+ result.image_format_constraints_count = 1;
+ fuchsia::sysmem::ImageFormatConstraints& image_constraints =
+ result.image_format_constraints[0];
+ image_constraints.pixel_format.type = fuchsia::sysmem::PixelFormatType::NV12;
+ // TODO(MTWN-251): confirm that REC709 is always what we want here, or plumb
+ // actual YUV color space if it can ever be REC601_*. Since 2020 and 2100
+ // are minimum 10 bits per Y sample and we're outputting NV12, 601 is the
+ // only other potential possibility here.
+ image_constraints.color_spaces_count = 1;
+ image_constraints.color_space[0].type =
+ fuchsia::sysmem::ColorSpaceType::REC709;
+
+ // The non-"required_" fields indicate the decoder's ability to potentially
+ // output frames at various dimensions as coded in the stream. Aside from
+ // the current stream being somewhere in these bounds, these have nothing to
+ // do with the current stream in particular.
+ image_constraints.min_coded_width = 16;
+ image_constraints.max_coded_width = 3840;
+ image_constraints.min_coded_height = 16;
+ // This intentionally isn't the height of a 4k frame. See
+ // max_coded_width_times_coded_height. We intentionally constrain the max
+ // dimension in width or height to the width of a 4k frame. While the HW
+ // might be able to go bigger than that as long as the other dimension is
+ // smaller to compensate, we don't really need to enable any larger than
+ // 4k's width in either dimension, so we don't.
+ image_constraints.max_coded_height = 3840;
+ image_constraints.min_bytes_per_row = 16;
+ // no hard-coded max stride, at least for now
+ image_constraints.max_bytes_per_row = 0xFFFFFFFF;
+ image_constraints.max_coded_width_times_coded_height = 3840 * 2160;
+ image_constraints.layers = 1;
+ image_constraints.coded_width_divisor = 16;
+ image_constraints.coded_height_divisor = 16;
+ image_constraints.bytes_per_row_divisor = 16;
+ // TODO(dustingreen): Since this is a producer that will always produce at
+ // offset 0 of a physical page, we don't really care if this field is
+ // consistent with any constraints re. what the HW can do.
+ image_constraints.start_offset_divisor = 1;
+ // Odd display dimensions are permitted, but these don't imply odd NV12
+ // dimensions - those are constrainted by coded_width_divisor and
+ // coded_height_divisor which are both 16.
+ image_constraints.display_width_divisor = 1;
+ image_constraints.display_height_divisor = 1;
+
+ // The decoder is producing frames and the decoder has no choice but to
+ // produce frames at their coded size. The decoder wants to potentially be
+ // able to support a stream with dynamic resolution, potentially including
+ // dimensions both less than and greater than the dimensions that led to the
+ // current need to allocate a BufferCollection. For this reason, the
+ // required_ fields are set to the exact current dimensions, and the
+ // permitted (non-required_) fields is set to the full potential range that
+ // the decoder could potentially output. If an initiator wants to require a
+ // larger range of dimensions that includes the required range indicated
+ // here (via a-priori knowledge of the potential stream dimensions), an
+ // initiator is free to do so.
+ image_constraints.required_min_coded_width = width_;
+ image_constraints.required_max_coded_width = width_;
+ image_constraints.required_min_coded_height = height_;
+ image_constraints.required_max_coded_height = height_;
+ } else {
+ ZX_DEBUG_ASSERT(result.image_format_constraints_count == 0);
+ }
+
+ // We don't have to fill out usage - CodecImpl takes care of that.
+ ZX_DEBUG_ASSERT(!result.usage.cpu);
+ ZX_DEBUG_ASSERT(!result.usage.display);
+ ZX_DEBUG_ASSERT(!result.usage.vulkan);
+ ZX_DEBUG_ASSERT(!result.usage.video);
+
+ return result;
+}
+
+void CodecAdapterH264::CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) {
+ ZX_DEBUG_ASSERT(buffer_collection_info.settings.buffer_settings.is_physically_contiguous);
+ ZX_DEBUG_ASSERT(buffer_collection_info.settings.buffer_settings.coherency_domain == fuchsia::sysmem::CoherencyDomain::Cpu);
+ if (port == kOutputPort) {
+ ZX_DEBUG_ASSERT(buffer_collection_info.settings.has_image_format_constraints);
+ ZX_DEBUG_ASSERT(buffer_collection_info.settings.image_format_constraints.pixel_format.type == fuchsia::sysmem::PixelFormatType::NV12);
+ }
+}
+
+fuchsia::media::StreamOutputFormat CodecAdapterH264::CoreCodecGetOutputFormat(
+ uint64_t stream_lifetime_ordinal,
+ uint64_t new_output_format_details_version_ordinal) {
+ fuchsia::media::StreamOutputFormat result;
+ result.set_stream_lifetime_ordinal(stream_lifetime_ordinal);
+ result.mutable_format_details()->set_format_details_version_ordinal(
new_output_format_details_version_ordinal);
- config->mutable_format_details()->set_mime_type("video/raw");
+
+ result.mutable_format_details()->set_mime_type("video/raw");
// For the moment, we'll memcpy to NV12 without any extra padding.
fuchsia::media::VideoUncompressedFormat video_uncompressed;
@@ -510,11 +708,11 @@
// specify separately for primary / secondary.
video_uncompressed.planar = true;
video_uncompressed.swizzled = false;
- video_uncompressed.primary_line_stride_bytes = stride_;
- video_uncompressed.secondary_line_stride_bytes = stride_;
+ video_uncompressed.primary_line_stride_bytes = min_stride_;
+ video_uncompressed.secondary_line_stride_bytes = min_stride_;
video_uncompressed.primary_start_offset = 0;
- video_uncompressed.secondary_start_offset = stride_ * height_;
- video_uncompressed.tertiary_start_offset = stride_ * height_ + 1;
+ video_uncompressed.secondary_start_offset = min_stride_ * height_;
+ video_uncompressed.tertiary_start_offset = min_stride_ * height_ + 1;
video_uncompressed.primary_pixel_stride = 1;
video_uncompressed.secondary_pixel_stride = 2;
video_uncompressed.primary_display_width_pixels = display_width_;
@@ -523,17 +721,13 @@
video_uncompressed.pixel_aspect_ratio_width = sar_width_;
video_uncompressed.pixel_aspect_ratio_height = sar_height_;
- // TODO(dustingreen): Switching to FIDL table should make this not be
- // required.
- video_uncompressed.special_formats.set_temp_field_todo_remove(0);
-
fuchsia::media::VideoFormat video_format;
video_format.set_uncompressed(std::move(video_uncompressed));
- config->mutable_format_details()->mutable_domain()->set_video(
+ result.mutable_format_details()->mutable_domain()->set_video(
std::move(video_format));
- return config;
+ return result;
}
void CodecAdapterH264::CoreCodecMidStreamOutputBufferReConfigPrepare() {
@@ -566,7 +760,7 @@
}
width = width_;
height = height_;
- stride = stride_;
+ stride = min_stride_;
} // ~lock
{ // scope lock
std::lock_guard<std::mutex> lock(*video_->video_decoder_lock());
@@ -1026,7 +1220,7 @@
// during InitializeStream(). Maybe delaying configuring of a canvas would
// work, but in that case would the delayed configuring adversely impact
// decoding performance consistency? If we can do this, detect when we can,
- // and call onCoreCodecMidStreamOutputConfigChange() but pass false instead of
+ // and call onCoreCodecMidStreamOutputConstraintsChange() but pass false instead of
// true, and don't expect a response or block in here. Still have to return
// the vector of buffers, and will need to indicate which are actually
// available to decode into. The rest will get indicated via
@@ -1050,7 +1244,7 @@
packet_count_total_ = frame_count;
width_ = width;
height_ = height;
- stride_ = stride;
+ min_stride_ = stride;
display_width_ = display_width;
display_height_ = display_height;
has_sar_ = has_sar;
@@ -1062,7 +1256,7 @@
// CoreCodecMidStreamOutputBufferReConfigPrepare() and
// CoreCodecMidStreamOutputBufferReConfigFinish() from the StreamControl
// thread, _iff_ the client hasn't already moved on to a new stream by then.
- events_->onCoreCodecMidStreamOutputConfigChange(true);
+ events_->onCoreCodecMidStreamOutputConstraintsChange(true);
return ZX_OK;
}
diff --git a/garnet/drivers/video/amlogic-decoder/codec_adapter_h264.h b/garnet/drivers/video/amlogic-decoder/codec_adapter_h264.h
index 8319df2..cb52bd4 100644
--- a/garnet/drivers/video/amlogic-decoder/codec_adapter_h264.h
+++ b/garnet/drivers/video/amlogic-decoder/codec_adapter_h264.h
@@ -22,6 +22,9 @@
~CodecAdapterH264();
bool IsCoreCodecRequiringOutputConfigForFormatDetection() override;
+ bool IsCoreCodecMappedBufferNeeded(CodecPort port) override;
+
+ bool IsCoreCodecHwBased() override;
void CoreCodecInit(const fuchsia::media::FormatDetails&
initial_input_format_details) override;
void CoreCodecStartStream() override;
@@ -37,12 +40,22 @@
const std::vector<std::unique_ptr<CodecPacket>>& packets) override;
void CoreCodecRecycleOutputPacket(CodecPacket* packet) override;
void CoreCodecEnsureBuffersNotConfigured(CodecPort port) override;
- std::unique_ptr<const fuchsia::media::StreamOutputConfig>
- CoreCodecBuildNewOutputConfig(
+ std::unique_ptr<const fuchsia::media::StreamOutputConstraints>
+ CoreCodecBuildNewOutputConstraints(
uint64_t stream_lifetime_ordinal,
uint64_t new_output_buffer_constraints_version_ordinal,
- uint64_t new_output_format_details_version_ordinal,
bool buffer_constraints_action_required) override;
+ fuchsia::sysmem::BufferCollectionConstraints
+ CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) override;
+ void CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) override;
+ fuchsia::media::StreamOutputFormat CoreCodecGetOutputFormat(
+ uint64_t stream_lifetime_ordinal,
+ uint64_t new_output_format_details_version_ordinal) override;
void CoreCodecMidStreamOutputBufferReConfigPrepare() override;
void CoreCodecMidStreamOutputBufferReConfigFinish() override;
@@ -98,7 +111,7 @@
uint32_t packet_count_total_ = 0;
uint32_t width_ = 0;
uint32_t height_ = 0;
- uint32_t stride_ = 0;
+ uint32_t min_stride_ = 0;
uint32_t display_width_ = 0;
uint32_t display_height_ = 0;
bool has_sar_ = false;
diff --git a/garnet/drivers/video/amlogic-decoder/codec_adapter_mpeg2.cc b/garnet/drivers/video/amlogic-decoder/codec_adapter_mpeg2.cc
index ac98b314..055f54a 100644
--- a/garnet/drivers/video/amlogic-decoder/codec_adapter_mpeg2.cc
+++ b/garnet/drivers/video/amlogic-decoder/codec_adapter_mpeg2.cc
@@ -27,11 +27,37 @@
return false;
}
+bool CodecAdapterMpeg2::IsCoreCodecMappedBufferNeeded(CodecPort port) {
+ // Since protected memory input/output isn't supported for mpeg2, may as well
+ // claim we need mapped buffers for now, in case we end up needing to re-pack
+ // input or fix output.
+ return true;
+}
+
+bool CodecAdapterMpeg2::IsCoreCodecHwBased() {
+ return true;
+}
+
void CodecAdapterMpeg2::CoreCodecInit(
const fuchsia::media::FormatDetails& initial_input_format_details) {
ZX_DEBUG_ASSERT_MSG(false, "not yet implemented");
}
+fuchsia::sysmem::BufferCollectionConstraints
+CodecAdapterMpeg2::CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) {
+ ZX_DEBUG_ASSERT_MSG(false, "not yet implemented");
+ return fuchsia::sysmem::BufferCollectionConstraints();
+}
+
+void CodecAdapterMpeg2::CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) {
+ ZX_DEBUG_ASSERT_MSG(false, "not yet implemented");
+}
+
void CodecAdapterMpeg2::CoreCodecStartStream() {
ZX_DEBUG_ASSERT_MSG(false, "not yet implemented");
}
@@ -71,14 +97,21 @@
ZX_DEBUG_ASSERT_MSG(false, "not yet implemented");
}
-std::unique_ptr<const fuchsia::media::StreamOutputConfig>
-CodecAdapterMpeg2::CoreCodecBuildNewOutputConfig(
+std::unique_ptr<const fuchsia::media::StreamOutputConstraints>
+CodecAdapterMpeg2::CoreCodecBuildNewOutputConstraints(
uint64_t stream_lifetime_ordinal,
uint64_t new_output_buffer_constraints_version_ordinal,
- uint64_t new_output_format_details_version_ordinal,
bool buffer_constraints_action_required) {
ZX_DEBUG_ASSERT_MSG(false, "not yet implemented");
- return std::make_unique<const fuchsia::media::StreamOutputConfig>();
+ return std::make_unique<const fuchsia::media::StreamOutputConstraints>();
+}
+
+fuchsia::media::StreamOutputFormat
+CodecAdapterMpeg2::CoreCodecGetOutputFormat(
+ uint64_t stream_lifetime_ordinal,
+ uint64_t new_output_format_details_version_ordinal) {
+ ZX_DEBUG_ASSERT_MSG(false, "not yet implemented");
+ return fuchsia::media::StreamOutputFormat();
}
void CodecAdapterMpeg2::CoreCodecMidStreamOutputBufferReConfigPrepare() {
diff --git a/garnet/drivers/video/amlogic-decoder/codec_adapter_mpeg2.h b/garnet/drivers/video/amlogic-decoder/codec_adapter_mpeg2.h
index 0209c14..3f3aa25 100644
--- a/garnet/drivers/video/amlogic-decoder/codec_adapter_mpeg2.h
+++ b/garnet/drivers/video/amlogic-decoder/codec_adapter_mpeg2.h
@@ -18,8 +18,19 @@
~CodecAdapterMpeg2();
bool IsCoreCodecRequiringOutputConfigForFormatDetection() override;
+ bool IsCoreCodecMappedBufferNeeded(CodecPort port) override;
+ bool IsCoreCodecHwBased() override;
+
void CoreCodecInit(const fuchsia::media::FormatDetails&
initial_input_format_details) override;
+ fuchsia::sysmem::BufferCollectionConstraints
+ CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) override;
+ void CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) override;
void CoreCodecStartStream() override;
void CoreCodecQueueInputFormatDetails(
const fuchsia::media::FormatDetails& per_stream_override_format_details)
@@ -33,12 +44,15 @@
const std::vector<std::unique_ptr<CodecPacket>>& packets) override;
void CoreCodecRecycleOutputPacket(CodecPacket* packet) override;
void CoreCodecEnsureBuffersNotConfigured(CodecPort port) override;
- std::unique_ptr<const fuchsia::media::StreamOutputConfig>
- CoreCodecBuildNewOutputConfig(
+ std::unique_ptr<const fuchsia::media::StreamOutputConstraints>
+ CoreCodecBuildNewOutputConstraints(
uint64_t stream_lifetime_ordinal,
uint64_t new_output_buffer_constraints_version_ordinal,
- uint64_t new_output_format_details_version_ordinal,
bool buffer_constraints_action_required) override;
+ fuchsia::media::StreamOutputFormat
+ CoreCodecGetOutputFormat(
+ uint64_t stream_lifetime_ordinal,
+ uint64_t new_output_format_details_version_ordinal) override;
void CoreCodecMidStreamOutputBufferReConfigPrepare() override;
void CoreCodecMidStreamOutputBufferReConfigFinish() override;
diff --git a/garnet/drivers/video/amlogic-decoder/codec_adapter_vp9.cc b/garnet/drivers/video/amlogic-decoder/codec_adapter_vp9.cc
index 920e173..67be20f 100644
--- a/garnet/drivers/video/amlogic-decoder/codec_adapter_vp9.cc
+++ b/garnet/drivers/video/amlogic-decoder/codec_adapter_vp9.cc
@@ -18,8 +18,8 @@
// the HW and send it to the Codec client, the other part to configure
// output buffers once the client has configured Codec output config based
// on the format info. Wire up so that
-// onCoreCodecMidStreamOutputConfigChange() gets called and so that
-// CoreCodecBuildNewOutputConfig() will pick up the correct current format
+// onCoreCodecMidStreamOutputConstraintsChange() gets called and so that
+// CoreCodecBuildNewOutputConstraints() will pick up the correct current format
// info (whether still mid-stream, or at the start of a new stream that's
// starting before the mid-stream format change was processed for the old
// stream).
@@ -105,6 +105,20 @@
return false;
}
+bool CodecAdapterVp9::IsCoreCodecMappedBufferNeeded(CodecPort port) {
+ // If buffers are protected, the decoder should/will call secmem TA to re-pack
+ // VP9 headers in the input. Else the decoder will use a CPU mapping to do
+ // this repack.
+ //
+ // TODO(dustingreen): Make the previous paragraph true. For now we have to
+ // re-pack using the CPU on REE side.
+ return true;
+}
+
+bool CodecAdapterVp9::IsCoreCodecHwBased() {
+ return true;
+}
+
void CodecAdapterVp9::CoreCodecInit(
const fuchsia::media::FormatDetails& initial_input_format_details) {
zx_status_t result = input_processing_loop_.StartThread(
@@ -121,6 +135,26 @@
// currently, but we should do more here and less there.
}
+fuchsia::sysmem::BufferCollectionConstraints
+CodecAdapterVp9::CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) {
+ ZX_ASSERT_MSG(false, "not yet implemented");
+ return fuchsia::sysmem::BufferCollectionConstraints();
+}
+
+void CodecAdapterVp9::CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) {
+ ZX_DEBUG_ASSERT(buffer_collection_info.settings.buffer_settings.is_physically_contiguous);
+ ZX_DEBUG_ASSERT(buffer_collection_info.settings.buffer_settings.coherency_domain == fuchsia::sysmem::CoherencyDomain::Cpu);
+ if (port == kOutputPort) {
+ ZX_DEBUG_ASSERT(buffer_collection_info.settings.has_image_format_constraints);
+ ZX_DEBUG_ASSERT(buffer_collection_info.settings.image_format_constraints.pixel_format.type == fuchsia::sysmem::PixelFormatType::NV12);
+ }
+}
+
// TODO(dustingreen): A lot of the stuff created in this method should be able
// to get re-used from stream to stream. We'll probably want to factor out
// create/init from stream init further down.
@@ -378,11 +412,10 @@
}
}
-std::unique_ptr<const fuchsia::media::StreamOutputConfig>
-CodecAdapterVp9::CoreCodecBuildNewOutputConfig(
+std::unique_ptr<const fuchsia::media::StreamOutputConstraints>
+CodecAdapterVp9::CoreCodecBuildNewOutputConstraints(
uint64_t stream_lifetime_ordinal,
uint64_t new_output_buffer_constraints_version_ordinal,
- uint64_t new_output_format_details_version_ordinal,
bool buffer_constraints_action_required) {
// bear.vp9 decodes into 320x192 YUV buffers, but the video display
// dimensions are 320x180. A the bottom of the buffer only .25 of the last
@@ -410,14 +443,14 @@
uint32_t per_packet_buffer_bytes = stride_ * height_ * 3 / 2;
- auto config = std::make_unique<fuchsia::media::StreamOutputConfig>();
+ auto config = std::make_unique<fuchsia::media::StreamOutputConstraints>();
config->set_stream_lifetime_ordinal(stream_lifetime_ordinal);
auto* constraints = config->mutable_buffer_constraints();
auto* default_settings = constraints->mutable_default_settings();
- // For the moment, there will be only one StreamOutputConfig, and it'll need
+ // For the moment, there will be only one StreamOutputConstraints, and it'll need
// output buffers configured for it.
ZX_DEBUG_ASSERT(buffer_constraints_action_required);
config->set_buffer_constraints_action_required(
@@ -473,9 +506,18 @@
// not the client.
constraints->set_very_temp_kludge_bti_handle(std::move(very_temp_kludge_bti));
- config->mutable_format_details()->set_format_details_version_ordinal(
+ return config;
+}
+
+fuchsia::media::StreamOutputFormat
+CodecAdapterVp9::CoreCodecGetOutputFormat(
+ uint64_t stream_lifetime_ordinal,
+ uint64_t new_output_format_details_version_ordinal) {
+ fuchsia::media::StreamOutputFormat result;
+ result.set_stream_lifetime_ordinal(stream_lifetime_ordinal);
+ result.mutable_format_details()->set_format_details_version_ordinal(
new_output_format_details_version_ordinal);
- config->mutable_format_details()->set_mime_type("video/raw");
+ result.mutable_format_details()->set_mime_type("video/raw");
// For the moment, we'll memcpy to NV12 without any extra padding.
fuchsia::media::VideoUncompressedFormat video_uncompressed;
@@ -501,17 +543,13 @@
video_uncompressed.pixel_aspect_ratio_width = sar_width_;
video_uncompressed.pixel_aspect_ratio_height = sar_height_;
- // TODO(dustingreen): Switching to FIDL table should make this not be
- // required.
- video_uncompressed.special_formats.set_temp_field_todo_remove(0);
-
fuchsia::media::VideoFormat video_format;
video_format.set_uncompressed(std::move(video_uncompressed));
- config->mutable_format_details()->mutable_domain()->set_video(
+ result.mutable_format_details()->mutable_domain()->set_video(
std::move(video_format));
- return config;
+ return result;
}
void CodecAdapterVp9::CoreCodecMidStreamOutputBufferReConfigPrepare() {
@@ -826,7 +864,7 @@
// CoreCodecMidStreamOutputBufferReConfigPrepare() and
// CoreCodecMidStreamOutputBufferReConfigFinish() from the StreamControl
// thread, _iff_ the client hasn't already moved on to a new stream by then.
- events_->onCoreCodecMidStreamOutputConfigChange(true);
+ events_->onCoreCodecMidStreamOutputConstraintsChange(true);
return ZX_OK;
}
diff --git a/garnet/drivers/video/amlogic-decoder/codec_adapter_vp9.h b/garnet/drivers/video/amlogic-decoder/codec_adapter_vp9.h
index fbf8f972..86695dc1 100644
--- a/garnet/drivers/video/amlogic-decoder/codec_adapter_vp9.h
+++ b/garnet/drivers/video/amlogic-decoder/codec_adapter_vp9.h
@@ -28,8 +28,19 @@
~CodecAdapterVp9();
bool IsCoreCodecRequiringOutputConfigForFormatDetection() override;
+ bool IsCoreCodecMappedBufferNeeded(CodecPort port) override;
+ bool IsCoreCodecHwBased() override;
+
void CoreCodecInit(const fuchsia::media::FormatDetails&
initial_input_format_details) override;
+ fuchsia::sysmem::BufferCollectionConstraints
+ CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) override;
+ void CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) override;
void CoreCodecStartStream() override;
void CoreCodecQueueInputFormatDetails(
const fuchsia::media::FormatDetails& per_stream_override_format_details)
@@ -43,12 +54,15 @@
const std::vector<std::unique_ptr<CodecPacket>>& packets) override;
void CoreCodecRecycleOutputPacket(CodecPacket* packet) override;
void CoreCodecEnsureBuffersNotConfigured(CodecPort port) override;
- std::unique_ptr<const fuchsia::media::StreamOutputConfig>
- CoreCodecBuildNewOutputConfig(
+ std::unique_ptr<const fuchsia::media::StreamOutputConstraints>
+ CoreCodecBuildNewOutputConstraints(
uint64_t stream_lifetime_ordinal,
uint64_t new_output_buffer_constraints_version_ordinal,
- uint64_t new_output_format_details_version_ordinal,
bool buffer_constraints_action_required) override;
+ fuchsia::media::StreamOutputFormat
+ CoreCodecGetOutputFormat(
+ uint64_t stream_lifetime_ordinal,
+ uint64_t new_output_format_details_version_ordinal) override;
void CoreCodecMidStreamOutputBufferReConfigPrepare() override;
void CoreCodecMidStreamOutputBufferReConfigFinish() override;
diff --git a/garnet/drivers/video/amlogic-decoder/local_codec_factory.cc b/garnet/drivers/video/amlogic-decoder/local_codec_factory.cc
index 568f0d2..003d11b 100644
--- a/garnet/drivers/video/amlogic-decoder/local_codec_factory.cc
+++ b/garnet/drivers/video/amlogic-decoder/local_codec_factory.cc
@@ -231,7 +231,14 @@
return;
}
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem =
+ device_->video()->ConnectToSysmem();
+ if (!sysmem) {
+ return;
+ }
+
std::unique_ptr<CodecImpl> codec = std::make_unique<CodecImpl>(
+ std::move(sysmem),
std::move(codec_admission),
device_->driver()->shared_fidl_loop()->dispatcher(),
device_->driver()->shared_fidl_thread(),
diff --git a/garnet/examples/media/use_media_decoder/main.cc b/garnet/examples/media/use_media_decoder/main.cc
index 24e532f..dd032cf 100644
--- a/garnet/examples/media/use_media_decoder/main.cc
+++ b/garnet/examples/media/use_media_decoder/main.cc
@@ -51,6 +51,10 @@
->ConnectToEnvironmentService<fuchsia::mediacodec::CodecFactory>(
codec_factory.NewRequest());
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem;
+ startup_context->ConnectToEnvironmentService<fuchsia::sysmem::Allocator>(
+ sysmem.NewRequest());
+
std::string input_file = command_line.positional_args()[0];
std::string output_file;
if (command_line.positional_args().size() >= 2) {
@@ -103,22 +107,25 @@
fit::closure use_decoder;
if (command_line.HasOption("aac_adts")) {
use_decoder = [&main_loop, codec_factory = std::move(codec_factory),
+ sysmem = std::move(sysmem),
input_file, output_file, &md]() mutable {
- use_aac_decoder(&main_loop, std::move(codec_factory), input_file,
+ use_aac_decoder(&main_loop, std::move(codec_factory), std::move(sysmem), input_file,
output_file, md);
};
} else if (command_line.HasOption("h264")) {
use_decoder = [&main_loop, codec_factory = std::move(codec_factory),
+ sysmem = std::move(sysmem),
input_file, output_file, &md,
frame_sink = frame_sink.get()]() mutable {
- use_h264_decoder(&main_loop, std::move(codec_factory), input_file,
+ use_h264_decoder(&main_loop, std::move(codec_factory), std::move(sysmem), input_file,
output_file, md, nullptr, nullptr, frame_sink);
};
} else if (command_line.HasOption("vp9")) {
use_decoder = [&main_loop, codec_factory = std::move(codec_factory),
+ sysmem = std::move(sysmem),
input_file, output_file, &md,
frame_sink = frame_sink.get()]() mutable {
- use_vp9_decoder(&main_loop, std::move(codec_factory), input_file,
+ use_vp9_decoder(&main_loop, std::move(codec_factory), std::move(sysmem), input_file,
output_file, md, nullptr, frame_sink);
};
} else {
diff --git a/garnet/examples/media/use_media_decoder/meta/use_h264_decoder_test.cmx b/garnet/examples/media/use_media_decoder/meta/use_h264_decoder_test.cmx
index 19907f1..a364877 100644
--- a/garnet/examples/media/use_media_decoder/meta/use_h264_decoder_test.cmx
+++ b/garnet/examples/media/use_media_decoder/meta/use_h264_decoder_test.cmx
@@ -2,7 +2,8 @@
"facets": {
"fuchsia.test": {
"injected-services": {
- "fuchsia.mediacodec.CodecFactory": "fuchsia-pkg://fuchsia.com/codec_factory#meta/codec_factory.cmx"
+ "fuchsia.mediacodec.CodecFactory": "fuchsia-pkg://fuchsia.com/codec_factory#meta/codec_factory.cmx",
+ "fuchsia.sysmem.Allocator": "fuchsia-pkg://fuchsia.com/sysmem_connector#meta/sysmem_connector.cmx"
}
}
},
@@ -11,13 +12,15 @@
},
"sandbox": {
"dev": [
- "class/media-codec"
+ "class/media-codec",
+ "class/sysmem"
],
"services": [
"fuchsia.sys.Environment",
"fuchsia.mediacodec.CodecFactory",
"fuchsia.sys.Launcher",
- "fuchsia.tracelink.Registry"
+ "fuchsia.tracelink.Registry",
+ "fuchsia.sysmem.Allocator"
]
}
}
diff --git a/garnet/examples/media/use_media_decoder/meta/use_media_decoder.cmx b/garnet/examples/media/use_media_decoder/meta/use_media_decoder.cmx
index bddffad..443ecba 100644
--- a/garnet/examples/media/use_media_decoder/meta/use_media_decoder.cmx
+++ b/garnet/examples/media/use_media_decoder/meta/use_media_decoder.cmx
@@ -8,6 +8,7 @@
],
"services": [
"fuchsia.mediacodec.CodecFactory",
+ "fuchsia.sysmem.Allocator",
"fuchsia.ui.scenic.Scenic"
]
}
diff --git a/garnet/examples/media/use_media_decoder/test/use_h264_decoder_test.cc b/garnet/examples/media/use_media_decoder/test/use_h264_decoder_test.cc
index 4d7265a..11bdac8 100644
--- a/garnet/examples/media/use_media_decoder/test/use_h264_decoder_test.cc
+++ b/garnet/examples/media/use_media_decoder/test/use_h264_decoder_test.cc
@@ -28,10 +28,13 @@
const std::map<uint32_t, const char*> GoldenSha256s = {
{make_fourcc('Y', 'V', '1', '2'),
- "f40cd9c876ef429da421cf4ae4a5a0df1795c4519ddac098a5c3f427f5566281"},
+ "39e861466dede78e5be008f85dba53efcee23b7a064170e4c00361383e67690d"},
+ // YV12 without SHA256_Update_VideoParameters():
+ // f3116ef8cf0f69c3d9316246a3896f96684f513ce9664b9b55e195c964cc64a0
{make_fourcc('N', 'V', '1', '2'),
- "29c86f37972b5b70768620f8f702c37f3579e0cd84bec7159900680075026460"}};
-
+ "2ab4b1f47636ac367b5cc0da2bf8d901a9e2b5db40126b50f5f75ee5b3b8c8df"}};
+ // NV12 without SHA256_Update_VideoParameters():
+ // 84ae3e279d8b85d3a3b10c06489d9ffb0a968d99baa498d20f28788c0090c1d5
} // namespace
int main(int argc, char* argv[]) {
@@ -42,7 +45,7 @@
codec_factory.set_error_handler([](zx_status_t status) {
// TODO(dustingreen): get and print CodecFactory channel epitaph once that's
// possible.
- FXL_LOG(ERROR) << "codec_factory failed - unexpected";
+ FXL_LOG(FATAL) << "codec_factory failed - unexpected";
});
std::unique_ptr<component::StartupContext> startup_context =
@@ -51,13 +54,17 @@
->ConnectToEnvironmentService<fuchsia::mediacodec::CodecFactory>(
codec_factory.NewRequest());
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem;
+ startup_context->ConnectToEnvironmentService<fuchsia::sysmem::Allocator>(
+ sysmem.NewRequest());
+
printf("The test file is: %s\n", kInputFilePath);
printf("Decoding test file and computing sha256...\n");
uint8_t md[SHA256_DIGEST_LENGTH];
std::vector<std::pair<bool, uint64_t>> timestamps;
uint32_t fourcc;
- use_h264_decoder(&main_loop, std::move(codec_factory), kInputFilePath, "", md,
+ use_h264_decoder(&main_loop, std::move(codec_factory), std::move(sysmem), kInputFilePath, "", md,
×tamps, &fourcc, nullptr);
std::set<uint64_t> expected_timestamps;
diff --git a/garnet/examples/media/use_media_decoder/use_aac_decoder.cc b/garnet/examples/media/use_media_decoder/use_aac_decoder.cc
index 74131e8..7a84d6d 100644
--- a/garnet/examples/media/use_media_decoder/use_aac_decoder.cc
+++ b/garnet/examples/media/use_media_decoder/use_aac_decoder.cc
@@ -137,6 +137,7 @@
// out_md - SHA256_DIGEST_LENGTH bytes long
void use_aac_decoder(async::Loop* main_loop,
fuchsia::mediacodec::CodecFactoryPtr codec_factory,
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
const std::string& input_adts_file,
const std::string& output_wav_file, uint8_t* out_md) {
memset(out_md, 0, SHA256_DIGEST_LENGTH);
@@ -244,7 +245,7 @@
// loop is already running, and we want the error handler to be set up by
// CodecClient in advance of the channel potentially being closed.
VLOGF("before CodecClient::CodecClient()...\n");
- CodecClient codec_client(&loop);
+ CodecClient codec_client(&loop, std::move(sysmem));
async::PostTask(
main_loop->dispatcher(),
[&codec_factory, create_params = std::move(create_params),
@@ -392,13 +393,15 @@
bool is_wav_initialized = false;
SHA256_CTX sha256_ctx;
SHA256_Init(&sha256_ctx);
- // We allow the server to send multiple output format updates if it wants;
- // see implementation of BlockingGetEmittedOutput() which will hide
- // multiple configs before the first packet from this code.
+ // We allow the server to send multiple output constraints updates if it
+ // wants; see implementation of BlockingGetEmittedOutput() which will hide
+ // multiple constraints before the first packet from this code. In contrast
+ // we assert if the server sends multiple format updates without any
+ // packets in between, as that's not compliant with protocol rules.
//
// In this example, we only deal with one output format once we start seeing
// stream data show up, since WAV only supports a single format per file.
- std::shared_ptr<const fuchsia::media::StreamOutputConfig> stream_config;
+ std::shared_ptr<const fuchsia::media::StreamOutputFormat> stream_format;
while (true) {
std::unique_ptr<CodecOutput> output =
codec_client.BlockingGetEmittedOutput();
@@ -437,19 +440,21 @@
Exit("broken server sent packet without buffer index");
}
- std::shared_ptr<const fuchsia::media::StreamOutputConfig> config =
- output->config();
+ // We don't really care about output->constraints() here, for now.
+
+ std::shared_ptr<const fuchsia::media::StreamOutputFormat> format =
+ output->format();
// This will remain live long enough because this thread is the only
// thread that re-allocates output buffers.
const CodecBuffer& buffer =
codec_client.GetOutputBufferByIndex(packet.header().packet_index());
- ZX_ASSERT(!stream_config || stream_config->has_format_details());
- if (stream_config &&
- (!config->has_format_details() ||
- !config->format_details().has_format_details_version_ordinal() ||
- config->format_details().format_details_version_ordinal() !=
- stream_config->format_details()
+ ZX_ASSERT(!stream_format || stream_format->has_format_details());
+ if (stream_format &&
+ (!format->has_format_details() ||
+ !format->format_details().has_format_details_version_ordinal() ||
+ format->format_details().format_details_version_ordinal() !=
+ stream_format->format_details()
.format_details_version_ordinal())) {
Exit(
"codec server unexpectedly changed output format mid-stream - "
@@ -469,24 +474,24 @@
// We have a non-empty packet of the stream.
- if (!stream_config) {
+ if (!stream_format) {
// Every output has a config. This happens exactly once.
- stream_config = config;
+ stream_format = format;
- if (!stream_config->has_format_details()) {
+ if (!stream_format->has_format_details()) {
Exit("!format_details");
}
- const fuchsia::media::FormatDetails& format =
- stream_config->format_details();
- if (!format.has_domain()) {
+ const fuchsia::media::FormatDetails& format_details =
+ stream_format->format_details();
+ if (!format_details.has_domain()) {
Exit("!format.domain");
}
- if (!format.domain().is_audio()) {
+ if (!format_details.domain().is_audio()) {
Exit("!format.domain.is_audio() - unexpected");
}
- const fuchsia::media::AudioFormat& audio = format.domain().audio();
+ const fuchsia::media::AudioFormat& audio = format_details.domain().audio();
if (!audio.is_uncompressed()) {
Exit("!audio.is_uncompressed() - unexpected");
}
diff --git a/garnet/examples/media/use_media_decoder/use_aac_decoder.h b/garnet/examples/media/use_media_decoder/use_aac_decoder.h
index d56fe43..6d556b5 100644
--- a/garnet/examples/media/use_media_decoder/use_aac_decoder.h
+++ b/garnet/examples/media/use_media_decoder/use_aac_decoder.h
@@ -34,6 +34,7 @@
// be set.
void use_aac_decoder(async::Loop* main_loop,
fuchsia::mediacodec::CodecFactoryPtr codec_factory,
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
const std::string& input_adts_file,
const std::string& output_wav_file,
uint8_t out_md[SHA256_DIGEST_LENGTH]);
diff --git a/garnet/examples/media/use_media_decoder/use_video_decoder.cc b/garnet/examples/media/use_media_decoder/use_video_decoder.cc
index e3ffc75..c4d84c1 100644
--- a/garnet/examples/media/use_media_decoder/use_video_decoder.cc
+++ b/garnet/examples/media/use_media_decoder/use_video_decoder.cc
@@ -252,6 +252,7 @@
static void use_video_decoder(
async::Loop* main_loop, fuchsia::mediacodec::CodecFactoryPtr codec_factory,
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
Format format, const std::string& input_file,
const std::string& output_file, uint8_t md_out[SHA256_DIGEST_LENGTH],
std::vector<std::pair<bool, uint64_t>>* timestamps_out, uint32_t* fourcc,
@@ -260,7 +261,7 @@
FXL_DCHECK(!timestamps_out || timestamps_out->empty());
memset(md_out, 0, SHA256_DIGEST_LENGTH);
async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
- loop.StartThread("use_h264_decoder_loop");
+ loop.StartThread("use_video_decoder_loop");
// payload data for bear.h264 is 00 00 00 01 start code before each NAL, with
// SPS / PPS NALs and also frame NALs. We deliver to Codec NAL-by-NAL without
@@ -279,7 +280,7 @@
// document in codec.fidl how that's to be handled.
VLOGF("before CodecClient::CodecClient()...\n");
- CodecClient codec_client(&loop);
+ CodecClient codec_client(&loop, std::move(sysmem));
const char* mime_type;
switch (format) {
@@ -372,14 +373,16 @@
output_file.c_str());
SHA256_CTX sha256_ctx;
SHA256_Init(&sha256_ctx);
- // We allow the server to send multiple output format updates if it wants;
- // see implementation of BlockingGetEmittedOutput() which will hide
- // multiple configs before the first packet from this code.
+ // We allow the server to send multiple output constraint updates if it
+ // wants; see implementation of BlockingGetEmittedOutput() which will hide
+ // multiple constraint updates before the first packet from this code. In
+ // contrast we assert if the server sends multiple format updates with no
+ // packets in between since that's not compliant with the protocol rules.
//
// In this example, we only deal with one output format once we start seeing
// stream output data show up, since our raw_video_writer is only really
// meant to store one format per file.
- std::shared_ptr<const fuchsia::media::StreamOutputConfig> stream_config;
+ std::shared_ptr<const fuchsia::media::StreamOutputFormat> stream_format;
const fuchsia::media::VideoUncompressedFormat* raw = nullptr;
while (true) {
std::unique_ptr<CodecOutput> output =
@@ -418,8 +421,8 @@
// set state command, so if that occurs, exit.
codec_client.RecycleOutputPacket(std::move(packet_header));
});
- std::shared_ptr<const fuchsia::media::StreamOutputConfig> config =
- output->config();
+ std::shared_ptr<const fuchsia::media::StreamOutputFormat> format =
+ output->format();
if (!packet.has_buffer_index()) {
// The server should not generate any empty packets.
@@ -432,14 +435,14 @@
codec_client.GetOutputBufferByIndex(packet.buffer_index());
ZX_ASSERT(
- !stream_config ||
- (stream_config->has_format_details() &&
- stream_config->format_details().format_details_version_ordinal()));
- if (stream_config &&
- (!config->has_format_details() ||
- !config->format_details().has_format_details_version_ordinal() ||
- config->format_details().format_details_version_ordinal() !=
- stream_config->format_details()
+ !stream_format ||
+ (stream_format->has_format_details() &&
+ stream_format->format_details().format_details_version_ordinal()));
+ if (stream_format &&
+ (!format->has_format_details() ||
+ !format->format_details().has_format_details_version_ordinal() ||
+ format->format_details().format_details_version_ordinal() !=
+ stream_format->format_details()
.format_details_version_ordinal())) {
Exit(
"codec server unexpectedly changed output format mid-stream - "
@@ -459,27 +462,27 @@
// We have a non-empty packet of the stream.
- if (!stream_config) {
- // Every output has a config. This happens exactly once.
- stream_config = config;
+ if (!stream_format) {
+ // Every output has a format. This happens exactly once.
+ stream_format = format;
- ZX_ASSERT(config->format_details().has_domain());
+ ZX_ASSERT(format->format_details().has_domain());
- if (!stream_config->has_format_details()) {
+ if (!stream_format->has_format_details()) {
Exit("!format_details");
}
- const fuchsia::media::FormatDetails& format =
- stream_config->format_details();
- if (!format.has_domain()) {
+ const fuchsia::media::FormatDetails& format_details =
+ stream_format->format_details();
+ if (!format_details.has_domain()) {
Exit("!format.domain");
}
- if (!format.domain().is_video()) {
+ if (!format_details.domain().is_video()) {
Exit("!format.domain.is_video()");
}
const fuchsia::media::VideoFormat& video_format =
- format.domain().video();
+ format_details.domain().video();
if (!video_format.is_uncompressed()) {
Exit("!video.is_uncompressed()");
}
@@ -621,8 +624,8 @@
&vmo = buffer.vmo(),
vmo_offset = buffer.vmo_offset() + packet.start_offset() +
raw->primary_start_offset,
- config, cleanup = std::move(cleanup)]() mutable {
- frame_sink->PutFrame(image_id, vmo, vmo_offset, config,
+ format, cleanup = std::move(cleanup)]() mutable {
+ frame_sink->PutFrame(image_id, vmo, vmo_offset, format,
[cleanup = std::move(cleanup)] {
// The ~cleanup can run on any thread (the
// current thread is main_loop's thread),
@@ -746,7 +749,7 @@
loop.JoinThreads();
VLOGF("after loop.JoinThreads()\n");
- // Close the channel explicitly (just so we can more easily print messages
+ // Close the channels explicitly (just so we can more easily print messages
// before and after vs. ~codec_client).
VLOGF("before codec_client stop...\n");
codec_client.Stop();
@@ -773,24 +776,26 @@
void use_h264_decoder(async::Loop* main_loop,
fuchsia::mediacodec::CodecFactoryPtr codec_factory,
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
const std::string& input_file,
const std::string& output_file,
uint8_t md_out[SHA256_DIGEST_LENGTH],
std::vector<std::pair<bool, uint64_t>>* timestamps_out,
uint32_t* fourcc, FrameSink* frame_sink) {
- use_video_decoder(main_loop, std::move(codec_factory), Format::kH264,
+ use_video_decoder(main_loop, std::move(codec_factory), std::move(sysmem), Format::kH264,
input_file, output_file, md_out, timestamps_out, fourcc,
frame_sink);
}
void use_vp9_decoder(async::Loop* main_loop,
fuchsia::mediacodec::CodecFactoryPtr codec_factory,
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
const std::string& input_file,
const std::string& output_file,
uint8_t md_out[SHA256_DIGEST_LENGTH],
std::vector<std::pair<bool, uint64_t>>* timestamps_out,
FrameSink* frame_sink) {
- use_video_decoder(main_loop, std::move(codec_factory), Format::kVp9,
+ use_video_decoder(main_loop, std::move(codec_factory), std::move(sysmem), Format::kVp9,
input_file, output_file, md_out, timestamps_out, nullptr,
frame_sink);
}
diff --git a/garnet/examples/media/use_media_decoder/use_video_decoder.h b/garnet/examples/media/use_media_decoder/use_video_decoder.h
index 9413a7f..4223136 100644
--- a/garnet/examples/media/use_media_decoder/use_video_decoder.h
+++ b/garnet/examples/media/use_media_decoder/use_video_decoder.h
@@ -43,6 +43,7 @@
// call back when the frame has been released by the sink.
void use_h264_decoder(async::Loop* main_loop,
fuchsia::mediacodec::CodecFactoryPtr codec_factory,
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
const std::string& input_file,
const std::string& output_file,
uint8_t md_out[SHA256_DIGEST_LENGTH],
@@ -52,6 +53,7 @@
// The same as use_h264_decoder, but for a VP9 file wrapped in an IVF container.
void use_vp9_decoder(async::Loop* main_loop,
fuchsia::mediacodec::CodecFactoryPtr codec_factory,
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
const std::string& input_file,
const std::string& output_file,
uint8_t md_out[SHA256_DIGEST_LENGTH],
diff --git a/garnet/examples/media/use_media_decoder/util.cc b/garnet/examples/media/use_media_decoder/util.cc
index ae6174e..535f5ba 100644
--- a/garnet/examples/media/use_media_decoder/util.cc
+++ b/garnet/examples/media/use_media_decoder/util.cc
@@ -117,8 +117,6 @@
UpdateSha256(sha256_ctx, video.tertiary_start_offset);
UpdateSha256(sha256_ctx, video.primary_pixel_stride);
UpdateSha256(sha256_ctx, video.secondary_pixel_stride);
- UpdateSha256(sha256_ctx, video.special_formats.is_temp_field_todo_remove());
- UpdateSha256(sha256_ctx, video.special_formats.temp_field_todo_remove());
}
void SHA256_Update_VideoPlane(SHA256_CTX* sha256_ctx, uint8_t* start,
diff --git a/garnet/lib/media/codec_impl/codec_impl.cc b/garnet/lib/media/codec_impl/codec_impl.cc
index 0ea822f..58af372 100644
--- a/garnet/lib/media/codec_impl/codec_impl.cc
+++ b/garnet/lib/media/codec_impl/codec_impl.cc
@@ -62,8 +62,10 @@
constexpr uint32_t kInputDefaultPacketCountForCodec =
kInputPacketCountForCodecRecommended;
+constexpr uint32_t kInputPacketCountForClientMin = 1;
constexpr uint32_t kInputPacketCountForClientMax =
std::numeric_limits<uint32_t>::max();
+
// This is fairly arbitrary, but rough speaking, 1 to be filling, 1 to be in
// flight toward the codec, and 1 to be in flight from the codec. This doesn't
// intend to be large enough to ride out any hypothetical decoder performance
@@ -124,43 +126,30 @@
DISALLOW_COPY_ASSIGN_AND_MOVE(ScopedRelock);
};
-uint32_t PacketCountFromPortSettings(
- const fuchsia::media::StreamBufferSettings& settings) {
- ZX_DEBUG_ASSERT(settings.has_packet_count_for_server());
- ZX_DEBUG_ASSERT(settings.has_packet_count_for_client());
- return settings.packet_count_for_server() +
- settings.packet_count_for_client();
-}
-
-uint32_t BufferCountFromPortSettings(
- const fuchsia::media::StreamBufferSettings& settings) {
- if (settings.has_single_buffer_mode() && settings.single_buffer_mode()) {
- return 1;
- }
- return PacketCountFromPortSettings(settings);
-}
-
} // namespace
CodecImpl::CodecImpl(
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
std::unique_ptr<CodecAdmission> codec_admission,
async_dispatcher_t* shared_fidl_dispatcher, thrd_t shared_fidl_thread,
std::unique_ptr<fuchsia::mediacodec::CreateDecoder_Params> decoder_params,
fidl::InterfaceRequest<fuchsia::media::StreamProcessor> codec_request)
- : CodecImpl(std::move(codec_admission), shared_fidl_dispatcher,
- shared_fidl_thread, std::move(decoder_params), nullptr,
- std::move(codec_request)) {}
+ : CodecImpl(std::move(sysmem), std::move(codec_admission),
+ shared_fidl_dispatcher, shared_fidl_thread,
+ std::move(decoder_params), nullptr, std::move(codec_request)) {}
CodecImpl::CodecImpl(
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
std::unique_ptr<CodecAdmission> codec_admission,
async_dispatcher_t* shared_fidl_dispatcher, thrd_t shared_fidl_thread,
std::unique_ptr<fuchsia::mediacodec::CreateEncoder_Params> encoder_params,
fidl::InterfaceRequest<fuchsia::media::StreamProcessor> codec_request)
- : CodecImpl(std::move(codec_admission), shared_fidl_dispatcher,
- shared_fidl_thread, nullptr, std::move(encoder_params),
- std::move(codec_request)) {}
+ : CodecImpl(std::move(sysmem), std::move(codec_admission),
+ shared_fidl_dispatcher, shared_fidl_thread, nullptr,
+ std::move(encoder_params), std::move(codec_request)) {}
CodecImpl::CodecImpl(
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
std::unique_ptr<CodecAdmission> codec_admission,
async_dispatcher_t* shared_fidl_dispatcher, thrd_t shared_fidl_thread,
std::unique_ptr<fuchsia::mediacodec::CreateDecoder_Params> decoder_params,
@@ -171,18 +160,31 @@
codec_admission_(std::move(codec_admission)),
shared_fidl_dispatcher_(shared_fidl_dispatcher),
shared_fidl_thread_(shared_fidl_thread),
- // TODO(dustingreen): Maybe have another parameter for encoder params, or
- // maybe separate constructor.
decoder_params_(std::move(decoder_params)),
encoder_params_(std::move(encoder_params)),
+ tmp_sysmem_(std::move(sysmem)),
tmp_interface_request_(std::move(codec_request)),
binding_(this),
stream_control_loop_(&kAsyncLoopConfigNoAttachToThread) {
+ ZX_DEBUG_ASSERT(thrd_current() == fidl_thread());
ZX_DEBUG_ASSERT(!!decoder_params_ ^ !!encoder_params_);
+ ZX_DEBUG_ASSERT(tmp_sysmem_);
ZX_DEBUG_ASSERT(tmp_interface_request_);
+
+ // If the fuchsia::sysmem::Allocator connection dies, so does this CodecImpl.
+ sysmem_.set_error_handler([this](zx_status_t status) {
+ // This handler can't run until after sysmem_ is bound.
+ ZX_DEBUG_ASSERT(was_logically_bound_);
+ this->Fail("CodecImpl sysmem_ channel failed");
+ });
+
// This is the binding_'s error handler, not the owner_error_handler_ which
// is related but separate.
- binding_.set_error_handler([this](zx_status_t status) { this->Unbind(); });
+ binding_.set_error_handler([this](zx_status_t status) {
+ // This handler can't run until after binding_ is bound.
+ ZX_DEBUG_ASSERT(was_logically_bound_);
+ this->Fail("CodecImpl binding_ channel failed");
+ });
initial_input_format_details_ = decoder_params_
? &decoder_params_->input_details()
: &encoder_params_->input_format();
@@ -270,9 +272,16 @@
// of that dispatching would tend to land in FailLocked(). The concurrency
// is just worth keeping in mind for the rest of the current lambda is all.
PostToSharedFidl([this] {
- zx_status_t bind_result = binding_.Bind(std::move(tmp_interface_request_),
+ zx_status_t status = sysmem_.Bind(std::move(tmp_sysmem_), shared_fidl_dispatcher_);
+ if (status != ZX_OK) {
+ Fail("sysmem_.Bind() failed");
+ return;
+ }
+ ZX_DEBUG_ASSERT(!tmp_sysmem_);
+
+ status = binding_.Bind(std::move(tmp_interface_request_),
shared_fidl_dispatcher_);
- if (bind_result != ZX_OK) {
+ if (status != ZX_OK) {
Fail("binding_.Bind() failed");
return;
}
@@ -309,6 +318,8 @@
kInputPacketCountForCodecRecommended);
buffer_constraints.set_packet_count_for_server_max(
kInputPacketCountForCodecMax);
+ buffer_constraints.set_packet_count_for_client_min(
+ kInputPacketCountForClientMin);
buffer_constraints.set_packet_count_for_client_max(
kInputPacketCountForClientMax);
buffer_constraints.set_single_buffer_mode_allowed(
@@ -318,7 +329,7 @@
std::make_unique<fuchsia::media::StreamBufferConstraints>(
std::move(buffer_constraints));
- // If/when this sends OnOutputConfig(), it posts to do so.
+ // If/when this sends OnOutputConstraints(), it posts to do so.
onInputConstraintsReady();
sent_buffer_constraints_version_ordinal_[kInputPort] =
@@ -354,23 +365,19 @@
if (IsStoppingLocked()) {
return;
}
- if (IsStreamActiveLocked()) {
- Fail("client sent SetInputBufferSettings() with stream active");
- return;
- }
- SetBufferSettingsCommon(lock, kInputPort, std::move(input_settings),
- *input_constraints_);
+ SetInputBufferSettingsCommon(lock, &input_settings, nullptr);
} // ~lock
}
void CodecImpl::AddInputBuffer(fuchsia::media::StreamBuffer buffer) {
ZX_DEBUG_ASSERT(thrd_current() == fidl_thread());
PostToStreamControl([this, buffer = std::move(buffer)]() mutable {
- AddInputBuffer_StreamControl(std::move(buffer));
+ AddInputBuffer_StreamControl(true, std::move(buffer));
});
}
void CodecImpl::AddInputBuffer_StreamControl(
+ bool is_client,
fuchsia::media::StreamBuffer buffer) {
ZX_DEBUG_ASSERT(thrd_current() == stream_control_thread_);
if (IsStopping()) {
@@ -378,65 +385,177 @@
}
// We must check, because __WARN_UNUSED_RESULT, and it's worth it for the
// enforcement and consistency.
- if (!AddBufferCommon(kInputPort, std::move(buffer))) {
+ if (!AddBufferCommon(is_client, kInputPort, std::move(buffer))) {
return;
}
}
+void CodecImpl::SetInputBufferPartialSettings(
+ fuchsia::media::StreamBufferPartialSettings input_settings) {
+ ZX_DEBUG_ASSERT(thrd_current() == fidl_thread());
+ PostToStreamControl([this, input_settings = std::move(input_settings)]() mutable {
+ SetInputBufferPartialSettings_StreamControl(std::move(input_settings));
+ });
+}
+
+void CodecImpl::SetInputBufferPartialSettings_StreamControl(
+ fuchsia::media::StreamBufferPartialSettings input_partial_settings) {
+ ZX_DEBUG_ASSERT(thrd_current() == stream_control_thread_);
+ { // scope lock
+ std::unique_lock<std::mutex> lock(lock_);
+ if (!sysmem_) {
+ FailLocked("client sent SetInputBufferPartialSettings() to a CodecImpl that lacks sysmem_");
+ return;
+ }
+ SetInputBufferSettingsCommon(lock, nullptr, &input_partial_settings);
+ } // ~lock
+}
+
+void CodecImpl::SetInputBufferSettingsCommon(
+ std::unique_lock<std::mutex>& lock,
+ fuchsia::media::StreamBufferSettings* input_settings,
+ fuchsia::media::StreamBufferPartialSettings* input_partial_settings) {
+ if (IsStoppingLocked()) {
+ return;
+ }
+ if (IsStreamActiveLocked()) {
+ FailLocked("client sent SetInputBuffer*Settings() with stream active");
+ return;
+ }
+ SetBufferSettingsCommon(lock, kInputPort, input_settings,
+ input_partial_settings, *input_constraints_);
+}
+
void CodecImpl::SetOutputBufferSettings(
fuchsia::media::StreamBufferSettings output_settings) {
ZX_DEBUG_ASSERT(thrd_current() == fidl_thread());
-
{ // scope lock
std::unique_lock<std::mutex> lock(lock_);
-
- if (!output_config_) {
- // invalid client behavior
- //
- // client must have received at least the initial OnOutputConfig() first
- // before sending SetOutputBufferSettings().
- FailLocked(
- "client sent SetOutputBufferSettings() when no output_config_");
- return;
- }
-
- // For a mid-stream output format change, this also enforces that the client
- // can only catch up to the mid-stream format change once. In other words,
- // if the client has already caught up to the mid-stream config change, the
- // client no longer has an excuse to re-configure again with a stream
- // active.
- //
- // There's a check in SetBufferSettingsCommonLocked() that ignores this
- // message if the client's buffer_constraints_version_ordinal is behind
- // last_required_buffer_constraints_version_ordinal_, which gets updated
- // under the same lock hold interval as the server's de-configuring of
- // output buffers.
- //
- // There's a check in SetBufferSettingsCommonLocked() that closes the
- // channel if the client is sending a buffer_constraints_version_ordinal
- // that's newer than the last sent_buffer_constraints_version_ordinal_.
- if (IsOutputConfiguredLocked() && IsStreamActiveLocked()) {
- FailLocked(
- "client sent SetOutputBufferSettings() with IsStreamActiveLocked() + "
- "already-configured output");
- return;
- }
-
- SetBufferSettingsCommon(lock, kOutputPort, std::move(output_settings),
- output_config_->buffer_constraints());
+ SetOutputBufferSettingsCommon(lock, &output_settings, nullptr);
} // ~lock
}
+void CodecImpl::SetOutputBufferSettingsCommon(
+ std::unique_lock<std::mutex>& lock,
+ fuchsia::media::StreamBufferSettings* output_settings,
+ fuchsia::media::StreamBufferPartialSettings* output_partial_settings) {
+ if (!output_constraints_) {
+ // invalid client behavior
+ //
+ // client must have received at least the initial OnOutputConstraints() first
+ // before sending SetOutputBufferSettings().
+ FailLocked(
+ "client sent SetOutputBufferSettings()/SetOutputBufferPartialSettings()"
+ " when no output_constraints_");
+ return;
+ }
+
+ // For a mid-stream output format change, this also enforces that the client
+ // can only catch up to the mid-stream format change once. In other words,
+ // if the client has already caught up to the mid-stream config change, the
+ // client no longer has an excuse to re-configure again with a stream
+ // active.
+ //
+ // There's a check in SetBufferSettingsCommonLocked() that ignores this
+ // message if the client's buffer_constraints_version_ordinal is behind
+ // last_required_buffer_constraints_version_ordinal_, which gets updated
+ // under the same lock hold interval as the server's de-configuring of
+ // output buffers.
+ //
+ // There's a check in SetBufferSettingsCommonLocked() that closes the
+ // channel if the client is sending a buffer_constraints_version_ordinal
+ // that's newer than the last sent_buffer_constraints_version_ordinal_.
+ if (IsStreamActiveLocked() && IsOutputConfiguredLocked()) {
+ FailLocked(
+ "client sent SetOutputBufferSettings()/SetOutputBufferPartialSettings()"
+ " with IsStreamActiveLocked() + already-fully-configured output");
+ return;
+ }
+
+ SetBufferSettingsCommon(lock, kOutputPort, output_settings,
+ output_partial_settings,
+ output_constraints_->buffer_constraints());
+}
+
void CodecImpl::AddOutputBuffer(fuchsia::media::StreamBuffer buffer) {
ZX_DEBUG_ASSERT(thrd_current() == fidl_thread());
- bool output_done_configuring =
- AddBufferCommon(kOutputPort, std::move(buffer));
- if (output_done_configuring) {
+ AddOutputBufferInternal(true, std::move(buffer));
+}
+
+void CodecImpl::AddOutputBufferInternal(bool is_client, fuchsia::media::StreamBuffer buffer) {
+ ZX_DEBUG_ASSERT(thrd_current() == fidl_thread());
+ bool output_buffers_done_configuring =
+ AddBufferCommon(is_client, kOutputPort, std::move(buffer));
+ if (output_buffers_done_configuring) {
// The StreamControl domain _might_ be waiting for output to be configured.
wake_stream_control_condition_.notify_all();
}
}
+void CodecImpl::SetOutputBufferPartialSettings(
+ fuchsia::media::StreamBufferPartialSettings output_partial_settings) {
+ ZX_DEBUG_ASSERT(thrd_current() == fidl_thread());
+ { // scope lock
+ std::unique_lock<std::mutex> lock(lock_);
+ if (!sysmem_) {
+ FailLocked("client sent SetOutputBufferPartialSettings() to a CodecImpl "
+ "that lacks a sysmem_");
+ return;
+ }
+ SetOutputBufferSettingsCommon(lock, nullptr, &output_partial_settings);
+ } // ~lock
+}
+
+void CodecImpl::CompleteOutputBufferPartialSettings(
+ uint64_t buffer_lifetime_ordinal) {
+ ZX_DEBUG_ASSERT(thrd_current() == fidl_thread());
+ { // scope lock
+ std::unique_lock<std::mutex> lock(lock_);
+
+ if (buffer_lifetime_ordinal % 2 == 0) {
+ FailLocked(
+ "CompleteOutputBufferPartialSettings client sent even "
+ "buffer_lifetime_ordinal, but must be odd");
+ return;
+ }
+
+ if (buffer_lifetime_ordinal !=
+ protocol_buffer_lifetime_ordinal_[kOutputPort]) {
+ FailLocked(
+ "CompleteOutputBufferPartialSettings bad buffer_lifetime_ordinal");
+ return;
+ }
+
+ // If the server is not interested in the client's buffer_lifetime_ordinal,
+ // the client's buffer_lifetime_ordinal won't match the server's
+ // buffer_lifetime_ordinal_. The client will probably later catch up.
+ if (buffer_lifetime_ordinal != buffer_lifetime_ordinal_[kOutputPort]) {
+ // The case that ends up here is when a client's output configuration
+ // (whole or last part) is being ignored because it's not yet caught up
+ // with last_required_buffer_constraints_version_ordinal_.
+
+ // Ignore the client's message. The client will probably catch up later.
+ return;
+ }
+
+ if (!IsPortBuffersAtLeastPartiallyConfiguredLocked(kOutputPort)) {
+ FailLocked("CompleteOutputBufferPartialSettings seen without prior "
+ "SetOutputBufferPartialSettings");
+ return;
+ }
+
+ if (port_settings_[kOutputPort]->is_complete_seen_output()) {
+ FailLocked("CompleteOutputBufferPartialSettings permitted exactly once "
+ "after each SetOutputBufferPartialSettings");
+ return;
+ }
+
+ // This will cause IsOutputConfiguredLocked() to start returning true.
+ port_settings_[kOutputPort]->SetCompleteSeenOutput();
+ } // ~lock
+ wake_stream_control_condition_.notify_all();
+}
+
void CodecImpl::FlushEndOfStreamAndCloseStream(
uint64_t stream_lifetime_ordinal) {
ZX_DEBUG_ASSERT(thrd_current() == fidl_thread());
@@ -482,7 +601,7 @@
// At this point we know that the stream is not discarded, and not already
// flushed previously (because flush will discard the stream as there's
// nothing more that the stream is permitted to do).
- ZX_DEBUG_ASSERT(stream_);
+ ZX_DEBUG_ASSERT(IsStreamActiveLocked());
ZX_DEBUG_ASSERT(stream_->stream_lifetime_ordinal() ==
stream_lifetime_ordinal);
if (!stream_->input_end_of_stream()) {
@@ -493,7 +612,7 @@
}
while (!stream_->output_end_of_stream()) {
// While waiting, we'll continue to send OnOutputPacket(),
- // OnOutputConfig(), and continue to process RecycleOutputPacket(), until
+ // OnOutputConstraints(), and continue to process RecycleOutputPacket(), until
// the client catches up to the latest config (as needed) and we've
// started the send of output end_of_stream packet to the client.
//
@@ -572,7 +691,14 @@
// closed soon instead, and the client has to tolerate that.
return;
}
- callback();
+ // We post back to FIDL thread to respond to ensure we're not racing with
+ // channel close which could lead to attempting to send to handle value 0
+ // which can cause process termination. Also, because this fences
+ // BufferAllocation clean close which itself is done async from StreamControl
+ // to FIDL in some cases.
+ PostToSharedFidl([callback = std::move(callback)]{
+ callback();
+ });
}
void CodecImpl::RecycleOutputPacket(
@@ -635,7 +761,7 @@
// Before handing the packet to the core codec, clear some fields that the
// core codec is expected to set (or optionally set in the case of
// timestamp_ish). In addition to these parameters, a core codec can emit
- // output config changes via onCoreCodecMidStreamOutputConfigChange().
+ // output config changes via onCoreCodecMidStreamOutputConstraintsChange().
packet = all_packets_[kOutputPort][packet_index].get();
packet->ClearStartOffset();
packet->ClearValidLengthBytes();
@@ -689,6 +815,12 @@
if (!CheckStreamLifetimeOrdinalLocked(stream_lifetime_ordinal)) {
return;
}
+
+ if (!CheckWaitEnsureInputConfigured(lock)) {
+ ZX_DEBUG_ASSERT(IsStoppingLocked() || !stream_ || stream_->future_discarded());
+ return;
+ }
+
ZX_DEBUG_ASSERT(stream_lifetime_ordinal >= stream_lifetime_ordinal_);
if (stream_lifetime_ordinal > stream_lifetime_ordinal_) {
if (!StartNewStream(lock, stream_lifetime_ordinal)) {
@@ -788,12 +920,26 @@
return;
}
+ if (!packet.has_stream_lifetime_ordinal()) {
+ FailLocked(
+ "client QueueInputPacket() without packet stream_lifetime_ordinal.");
+ return;
+ }
+ if (!CheckStreamLifetimeOrdinalLocked(packet.stream_lifetime_ordinal())) {
+ return;
+ }
+
+ if (!CheckWaitEnsureInputConfigured(lock)) {
+ ZX_DEBUG_ASSERT(IsStoppingLocked() || (stream_ && stream_->future_discarded()));
+ return;
+ }
+
// For input, mid-stream config changes are not a thing and input buffers
// are never unilaterally de-configured by the Codec server.
ZX_DEBUG_ASSERT(buffer_lifetime_ordinal_[kInputPort] ==
port_settings_[kInputPort]->buffer_lifetime_ordinal());
- // For this message we're extra-strict re. buffer_lifetime_ordinal, at least
- // for now.
+
+ // For this message we're strict re. buffer_lifetime_ordinal.
//
// In contrast to output, the server doesn't use even values to track config
// changes that the client doesn't know about yet, since the server can't
@@ -810,15 +956,6 @@
return;
}
- if (!packet.has_stream_lifetime_ordinal()) {
- FailLocked(
- "client QueueInputPacket() without packet stream_lifetime_ordinal.");
- return;
- }
-
- if (!CheckStreamLifetimeOrdinalLocked(packet.stream_lifetime_ordinal())) {
- return;
- }
ZX_DEBUG_ASSERT(packet.stream_lifetime_ordinal() >=
stream_lifetime_ordinal_);
@@ -836,16 +973,12 @@
ZX_DEBUG_ASSERT(packet.stream_lifetime_ordinal() ==
stream_lifetime_ordinal_);
- if (!IsInputConfiguredLocked()) {
- FailLocked("client QueueInputPacket() with input buffers not configured");
- return;
- }
if (!packet.header().has_packet_index()) {
FailLocked("client QueueInputPacket() with packet has no packet index");
return;
}
if (packet.header().packet_index() >= all_packets_[kInputPort].size()) {
- FailLocked("client QueueInputPacket() with packet_index out of range");
+ FailLocked("client QueueInputPacket() with packet_index out of range - packet_index: %u size: %u", packet.header().packet_index(), all_packets_[kInputPort].size());
return;
}
if (!packet.has_buffer_index()) {
@@ -952,6 +1085,12 @@
if (!CheckStreamLifetimeOrdinalLocked(stream_lifetime_ordinal)) {
return;
}
+
+ if (!CheckWaitEnsureInputConfigured(lock)) {
+ ZX_DEBUG_ASSERT(IsStoppingLocked() || (stream_ && stream_->future_discarded()));
+ return;
+ }
+
ZX_DEBUG_ASSERT(stream_lifetime_ordinal >= stream_lifetime_ordinal_);
if (stream_lifetime_ordinal > stream_lifetime_ordinal_) {
// We start a new stream given an end-of-stream for a stream we've not
@@ -969,11 +1108,11 @@
stream_->SetInputEndOfStream();
if (stream_->future_discarded()) {
- // Don't queue to OMX. The stream_ may have never fully started, or may
- // have been future-discarded since. Either way, skip queueing to OMX. We
- // only really must do this because the stream may not have ever fully
- // started, in the case where the client moves on to a new stream before
- // catching up to latest output config.
+ // Don't queue to core codec. The stream_ may have never fully started,
+ // or may have been future-discarded since. Either way, skip queueing to
+ // core codec. We only really must do this because the stream may not have
+ // ever fully started, in the case where the client moves on to a new
+ // stream before catching up to latest output config.
return;
}
} // ~lock
@@ -981,6 +1120,74 @@
CoreCodecQueueInputEndOfStream();
}
+bool CodecImpl::CheckWaitEnsureInputConfigured(std::unique_lock<std::mutex>& lock) {
+ // Ensure/finish input configuration.
+ if (!IsPortBuffersAtLeastPartiallyConfiguredLocked(kInputPort)) {
+ FailLocked("client QueueInput*() with input buffers not at least partially configured");
+ return false;
+ }
+ ZX_DEBUG_ASSERT(buffer_lifetime_ordinal_[kInputPort] % 2 == 1);
+ // The client is required to know that sysmem is in fact done allocating the
+ // BufferCollection successfully before the client sends
+ // QueueInput...StreamControl. We can't trust a client to necessarily get
+ // that right however, so rather than just getting stuck indefinitely in that
+ // case, we detect by asking sysmem to verify that it has allocated the
+ // BufferCollection successfully. This verification happens async, but will
+ // shortly cause WaitEnsureSysmemReadyOnInput() to return and
+ // IsStoppingLocked() to return true if verification fails.
+ if (!IsInputConfiguredLocked()) {
+ PostToSharedFidl([this, buffer_lifetime_ordinal = buffer_lifetime_ordinal_[kInputPort]]{
+ std::unique_lock<std::mutex> lock(lock_);
+ if (IsStoppingLocked()) {
+ return;
+ }
+ if (buffer_lifetime_ordinal != buffer_lifetime_ordinal_[kInputPort]) {
+ // stale; no problem; old buffers were allocated fine and client already
+ // moved on after that.
+ return;
+ }
+ // Else previous buffer_lifetime_ordinal check would have noticed.
+ ZX_DEBUG_ASSERT(port_settings_[kInputPort]);
+ // paranoid check - assert above believed to be valid
+ if (!port_settings_[kInputPort]) {
+ return;
+ }
+ // Else IsStoppingLocked() check above would have returned.
+ ZX_DEBUG_ASSERT(port_settings_[kInputPort]->buffer_collection().is_bound());
+ // paranoid check - assert above believed to be valid
+ if (!port_settings_[kInputPort]->buffer_collection().is_bound()) {
+ return;
+ }
+ port_settings_[kInputPort]->buffer_collection()->CheckBuffersAllocated([this, buffer_lifetime_ordinal](zx_status_t status){
+ std::unique_lock<std::mutex> lock(lock_);
+ if (IsStoppingLocked()) {
+ return;
+ }
+ if (buffer_lifetime_ordinal != buffer_lifetime_ordinal_[kInputPort]) {
+ // stale; no problem; old buffers were allocated fine and client
+ // already moved on after that.
+ return;
+ }
+ if (status != ZX_OK) {
+ // This will cause any in-progress WaitEnsureSysmemReadyOnInput() to
+ // return shortly and IsStoppingLocked() will be true.
+ FailLocked("Probably client did QueueInput* before the client determined that sysmem was done successfully allocating buffers after most recent SetInputBufferPartialSettings()");
+ return;
+ }
+ });
+ });
+ if (!WaitEnsureSysmemReadyOnInput(lock)) {
+ ZX_DEBUG_ASSERT(IsStoppingLocked());
+ return false;
+ }
+ }
+ if (!IsInputConfiguredLocked()) {
+ FailLocked("client QueueInput*() with input buffers not configured");
+ return false;
+ }
+ return true;
+}
+
void CodecImpl::onInputConstraintsReady() {
ZX_DEBUG_ASSERT(thrd_current() == stream_control_thread_);
if (!IsCoreCodecRequiringOutputConfigForFormatDetection()) {
@@ -988,7 +1195,7 @@
}
std::unique_lock<std::mutex> lock(lock_);
StartIgnoringClientOldOutputConfig(lock);
- GenerateAndSendNewOutputConfig(lock, true);
+ GenerateAndSendNewOutputConstraints(lock, true);
}
void CodecImpl::UnbindLocked() {
@@ -1102,6 +1309,22 @@
// being safe to just delete.
stream_control_loop_.Shutdown();
+ { // scope lock
+ std::lock_guard<std::mutex> lock(lock_);
+ // This unbinds any BufferCollection bindings. This is effectively part
+ // of the overall unbind of anything generating activity on FIDL thread
+ // based on incoming channel messages.
+ if (port_settings_[kInputPort] && port_settings_[kInputPort]->is_partial_settings()) {
+ port_settings_[kInputPort]->UnbindBufferCollection();
+ }
+ if (port_settings_[kOutputPort] && port_settings_[kOutputPort]->is_partial_settings()) {
+ port_settings_[kOutputPort]->UnbindBufferCollection();
+ }
+ // Unbind the sysmem_ fuchsia::sysmem::Allocator connection - this also
+ // ensures that any in-flight requests' completions will not run.
+ sysmem_.Unbind();
+ } // ~lock
+
// Before calling the owner_error_handler_, we declare that unbind is
// done so that during the destructor we can check that unbind is done.
was_unbind_completed_ = true;
@@ -1135,7 +1358,10 @@
// fidl_thread() re. this CodecImpl to not re-post to the fidl_thread().
// In contrast, it is ok if a FIDL dispatch (on FIDL thread) re-posts to
// the fidl_thread(); this is ok because we've already stopped any more
- // FIDL dispatching by binding_.Unbind() above.
+ // FIDL dispatching by binding_.Unbind() above. It's also ok if a posted
+ // lambda starts a FIDL request that will complete async on the FIDL
+ // thread since the unbind above will prevent the completion from
+ // running.
PostSerial(shared_fidl_dispatcher_,
[client_error_handler = std::move(owner_error_handler_)] {
// This call deletes the CodecImpl.
@@ -1159,13 +1385,15 @@
}
bool CodecImpl::IsStreamActiveLocked() {
- return stream_lifetime_ordinal_ % 2 == 1;
+ ZX_DEBUG_ASSERT(!!stream_ == (stream_lifetime_ordinal_ % 2 == 1));
+ return !!stream_;
}
void CodecImpl::SetBufferSettingsCommon(
std::unique_lock<std::mutex>& lock, CodecPort port,
- fuchsia::media::StreamBufferSettings settings,
- const fuchsia::media::StreamBufferConstraints& constraints) {
+ fuchsia::media::StreamBufferSettings* settings,
+ fuchsia::media::StreamBufferPartialSettings* partial_settings,
+ const fuchsia::media::StreamBufferConstraints& stream_constraints) {
ZX_DEBUG_ASSERT(port == kInputPort &&
thrd_current() == stream_control_thread_ ||
port == kOutputPort && thrd_current() == fidl_thread());
@@ -1177,41 +1405,91 @@
// is either the last accepted from the client or one more than that as a way
// of cleanly permitting the server to unilaterally de-configure output
// buffers.
- if (!settings.has_buffer_lifetime_ordinal()) {
- FailLocked("settings do not have buffer lifetime ordinal");
- return;
+ if (settings) {
+ if (!settings->has_buffer_lifetime_ordinal()) {
+ FailLocked("settings missing buffer lifetime ordinal");
+ return;
+ }
+ if (!settings->has_buffer_constraints_version_ordinal()) {
+ FailLocked("settings missing buffer constraints version ordinal");
+ return;
+ }
+ if (!settings->has_packet_count_for_server()) {
+ FailLocked("settings missing packet_count_for_server");
+ return;
+ }
+ if (!settings->has_packet_count_for_client()) {
+ FailLocked("settings missing packet_count_for_client");
+ return;
+ }
+ if (!settings->has_per_packet_buffer_bytes()) {
+ FailLocked("settings missing per_packet_buffer_bytes");
+ return;
+ }
+ } else {
+ if (!partial_settings->has_buffer_lifetime_ordinal()) {
+ FailLocked("partial_settings do not have buffer lifetime ordinal");
+ return;
+ }
+ if (!partial_settings->has_buffer_constraints_version_ordinal()) {
+ FailLocked(
+ "partial_settings do not have buffer constraints version ordinal");
+ return;
+ }
+ if (!partial_settings->has_packet_count_for_server()) {
+ FailLocked("partial_settings missing packet_count_for_server");
+ return;
+ }
+ if (!partial_settings->has_packet_count_for_client()) {
+ FailLocked("partial_settings missing packet_count_for_client");
+ return;
+ }
+ if (!partial_settings->has_sysmem_token() || !partial_settings->sysmem_token().is_valid()) {
+ FailLocked("partial_settings missing valid sysmem_token");
+ return;
+ }
}
ZX_DEBUG_ASSERT(
- (!port_settings_[port && buffer_lifetime_ordinal_[port] == 0]) ||
+ !port_settings_[port] ||
(buffer_lifetime_ordinal_[port] >=
port_settings_[port]->buffer_lifetime_ordinal() &&
buffer_lifetime_ordinal_[port] <=
port_settings_[port]->buffer_lifetime_ordinal() + 1));
- if (settings.buffer_lifetime_ordinal() <=
+
+ // The caller may be providing StreamBufferSettings or
+ // StreamBufferPartialSettings, but not both.
+ ZX_DEBUG_ASSERT(!!settings ^ !!partial_settings);
+
+ // Extract buffer_lifetime_ordinal and buffer_constraints_version_ordinal from
+ // whichever of StreamBufferSettings or StreamBufferPartialSettings the caller
+ // is providing.
+ uint64_t buffer_lifetime_ordinal = settings ?
+ settings->buffer_lifetime_ordinal() :
+ partial_settings->buffer_lifetime_ordinal();
+
+ uint64_t buffer_constraints_version_ordinal = settings ?
+ settings->buffer_constraints_version_ordinal() :
+ partial_settings->buffer_constraints_version_ordinal();
+
+ if (buffer_lifetime_ordinal <=
protocol_buffer_lifetime_ordinal_[port]) {
FailLocked(
- "settings.buffer_lifetime_ordinal <= "
+ "buffer_lifetime_ordinal <= "
"protocol_buffer_lifetime_ordinal_[port] - port: %d",
port);
return;
}
- protocol_buffer_lifetime_ordinal_[port] = settings.buffer_lifetime_ordinal();
-
- if (settings.buffer_lifetime_ordinal() % 2 == 0) {
+ if (buffer_lifetime_ordinal % 2 == 0) {
FailLocked(
"Only odd values for buffer_lifetime_ordinal are permitted - port: %d "
"value %lu",
- port, settings.buffer_lifetime_ordinal());
+ port, buffer_lifetime_ordinal);
return;
}
+ protocol_buffer_lifetime_ordinal_[port] = buffer_lifetime_ordinal;
- if (!settings.has_buffer_constraints_version_ordinal()) {
- FailLocked("settings do not have buffer constraints version ordinal");
- return;
- }
-
- if (settings.buffer_constraints_version_ordinal() >
+ if (buffer_constraints_version_ordinal >
sent_buffer_constraints_version_ordinal_[port]) {
FailLocked(
"Client sent too-new buffer_constraints_version_ordinal - port: %d",
@@ -1219,37 +1497,292 @@
return;
}
- if (settings.buffer_constraints_version_ordinal() <
+ if (buffer_constraints_version_ordinal <
last_required_buffer_constraints_version_ordinal_[port]) {
// ignore - client will probably catch up later
return;
}
// We've peeled off too new and too old above.
- ZX_DEBUG_ASSERT(settings.buffer_constraints_version_ordinal() >=
+ ZX_DEBUG_ASSERT(buffer_constraints_version_ordinal >=
last_required_buffer_constraints_version_ordinal_[port] &&
- settings.buffer_constraints_version_ordinal() <=
+ buffer_constraints_version_ordinal <=
sent_buffer_constraints_version_ordinal_[port]);
// We've already checked above that the buffer_lifetime_ordinal is in
// sequence.
- ZX_DEBUG_ASSERT(!port_settings_[port] || settings.buffer_lifetime_ordinal() >
+ ZX_DEBUG_ASSERT(!port_settings_[port] || buffer_lifetime_ordinal >
buffer_lifetime_ordinal_[port]);
- if (!ValidateBufferSettingsVsConstraintsLocked(port, settings, constraints)) {
- // This assert is safe only because this thread still holds lock_.
- ZX_DEBUG_ASSERT(IsStoppingLocked());
- return;
+ if (settings) {
+ if (!ValidateBufferSettingsVsConstraintsLocked(port, *settings, stream_constraints)) {
+ // This assert is safe only because this thread still holds lock_. This
+ // is asserting that ValidateBufferSettingsVsConstraintsLocked() already
+ // called FailLocked().
+ ZX_DEBUG_ASSERT(IsStoppingLocked());
+ return;
+ }
+ } else {
+ if (!ValidatePartialBufferSettingsVsConstraintsLocked(port, *partial_settings, stream_constraints)) {
+ // This assert is safe only because this thread still holds lock_. This
+ // is asserting that ValidateBufferSettingsVsConstraintsLocked() already
+ // called FailLocked().
+ ZX_DEBUG_ASSERT(IsStoppingLocked());
+ return;
+ }
}
// Little if any reason to do this outside the lock.
EnsureBuffersNotConfigured(lock, port);
// This also starts the new buffer_lifetime_ordinal.
- port_settings_[port] = std::make_unique<fuchsia::media::StreamBufferSettings>(
- std::move(settings));
+ { // scope port_settings, to enforce not using it after we've moved it out
+ std::unique_ptr<PortSettings> port_settings;
+ if (settings) {
+ port_settings = std::make_unique<PortSettings>(this, port,
+ std::move(*settings));
+ } else {
+ port_settings = std::make_unique<PortSettings>(this, port,
+ std::move(*partial_settings));
+ }
+ port_settings_[port] = std::move(port_settings);
+ } // ~port_settings, which has been moved out, so we can't use it anyway
buffer_lifetime_ordinal_[port] =
port_settings_[port]->buffer_lifetime_ordinal();
+
+ // If partial_settings_param, the client won't be doing any AddInputBuffer()
+ // or AddOutputBuffer(). Instead, CodecImpl uses the token in
+ // partial_settings to get a fuchsia.sysmem.BufferCollection view,
+ // SetConstraints() on that BufferCollection, and then get allocated buffers
+ // from sysmem directly.
+
+ if (port_settings_[port]->is_partial_settings()) {
+ fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token =
+ port_settings_[port]->TakeToken();
+ // We intentionally don't want to hand the sysmem token directly to the core
+ // codec, at least for now (maybe later it'll be necessary).
+ ZX_DEBUG_ASSERT(!port_settings_[port]->partial_settings().has_sysmem_token());
+ fuchsia::sysmem::BufferCollectionConstraints buffer_collection_constraints = [this, port, &lock, &stream_constraints](){
+ // port_settings_[port] can only change on this thread so are safe to read
+ // outside the lock.
+ ScopedUnlock unlock(lock);
+ return CoreCodecGetBufferCollectionConstraints(
+ port, stream_constraints, port_settings_[port]->partial_settings());
+ }();
+ // The core codec doesn't fill out usage directly. Instead we fill it out
+ // here.
+ if (!FixupBufferCollectionConstraints(
+ port, port_settings_[port]->partial_settings(),
+ &buffer_collection_constraints)) {
+ // FixupBufferCollectionConstraints() already called Fail().
+ ZX_DEBUG_ASSERT(IsStopping());
+ return;
+ }
+ // For output, the only reason we re-post here is to share the lock
+ // acquisition code with input. Because we're presently on a FIDL handler
+ // it's ok to re-post back to the FIDL thread, so for output we pass true
+ // for promise_not_on_previously_posted_fidl_thread_lambda.
+ PostToSharedFidl([this, port, buffer_lifetime_ordinal = buffer_lifetime_ordinal_[port], token = std::move(token), buffer_collection_constraints = std::move(buffer_collection_constraints)]() mutable {
+ std::lock_guard<std::mutex> lock(lock_);
+ if (buffer_lifetime_ordinal != buffer_lifetime_ordinal_[port]) {
+ return;
+ }
+ if (!sysmem_) {
+ return;
+ }
+ if (IsStoppingLocked()) {
+ return;
+ }
+ sysmem_->BindSharedCollection(std::move(token), port_settings_[port]->NewBufferCollectionRequest(shared_fidl_dispatcher_));
+ port_settings_[port]->buffer_collection().set_error_handler(
+ [this, port, buffer_lifetime_ordinal](zx_status_t status){
+ std::lock_guard<std::mutex> lock(lock_);
+ if (buffer_lifetime_ordinal != buffer_lifetime_ordinal_[port]) {
+ // It's fine if a BufferCollection fails after we're already done
+ // using it.
+ return;
+ }
+ // We're intentionally picky about the BufferCollection failing too
+ // soon, as all clean closes should use Close(), which will avoid
+ // causing this. If we find a case where a client legitimately needs to
+ // try one way then if that fails try another way, we should see if we
+ // can avoid the need to do that by expressing in sysmem constraints, or
+ // more likely just accept that such a client will need to start with a
+ // new codec instance for the 2nd try.
+ FailLocked("CodecImpl's BufferCollection failed - status: %d", status);
+ });
+ port_settings_[port]->buffer_collection()->SetConstraints(
+ true, std::move(buffer_collection_constraints));
+ port_settings_[port]->buffer_collection()->WaitForBuffersAllocated([this, port, buffer_lifetime_ordinal](zx_status_t status, fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info) mutable {
+ OnBufferCollectionInfo(port, buffer_lifetime_ordinal, status, std::move(buffer_collection_info));
+ });
+ }, port == kOutputPort);
+ } else {
+ // This path probably won't stick around for very long, and involves a
+ // substantial amount of simulation of the new way based on old settings. To
+ // the degree that it's more indirect than usual, it's to try and
+ // use/simulate the new way as much as possible despite the client using the
+ // old way.
+
+ const fuchsia::media::StreamBufferSettings& settings =
+ port_settings_[port]->settings();
+
+ // If !port_settings_[port]->is_partial_settings(), we'll simulate the new
+ // way based on the old-style settings. This way we can get the core codec
+ // to fill out image_format_constraints, and can inform the core codec of
+ // BufferCollectionInfo_2, even if we're not actually using sysmem (in this
+ // path for now).
+ fuchsia::media::StreamBufferPartialSettings fake_partial_settings;
+ fake_partial_settings.set_buffer_lifetime_ordinal(settings.buffer_lifetime_ordinal());
+ fake_partial_settings.set_buffer_constraints_version_ordinal(settings.buffer_constraints_version_ordinal());
+ fake_partial_settings.set_packet_count_for_server(settings.packet_count_for_server());
+ fake_partial_settings.set_packet_count_for_client(settings.packet_count_for_client());
+ fake_partial_settings.set_single_buffer_mode(settings.has_single_buffer_mode() && settings.single_buffer_mode());
+ ZX_DEBUG_ASSERT(!fake_partial_settings.has_sysmem_token());
+
+ fuchsia::sysmem::BufferCollectionConstraints buffer_collection_constraints = [this, port, &lock, &stream_constraints, &fake_partial_settings]{
+ ScopedUnlock unlock(lock);
+ return CoreCodecGetBufferCollectionConstraints(port, stream_constraints, fake_partial_settings);
+ }();
+
+ // The core codec doesn't fill out usage directly. Instead we fill it out
+ // here.
+ if (!FixupBufferCollectionConstraints(
+ port, fake_partial_settings,
+ &buffer_collection_constraints)) {
+ // FixupBufferCollectionConstraints() already called Fail().
+ ZX_DEBUG_ASSERT(IsStopping());
+ return;
+ }
+
+ fuchsia::sysmem::BufferCollectionInfo_2 fake;
+ fake.buffer_count = fake_partial_settings.single_buffer_mode() ? 1 : fake_partial_settings.packet_count_for_server() + fake_partial_settings.packet_count_for_client();
+ fake.settings.buffer_settings.size_bytes = fake_partial_settings.single_buffer_mode() ? settings.per_packet_buffer_bytes() * fake.buffer_count : settings.per_packet_buffer_bytes();
+ fake.settings.buffer_settings.is_physically_contiguous = buffer_collection_constraints.has_buffer_memory_constraints && buffer_collection_constraints.buffer_memory_constraints.physically_contiguous_required;
+ // The client should use sysmem (and SetInputBufferPartialSettings /
+ // SetOutputBufferPartialSettings) if secure is required.
+ fake.settings.buffer_settings.is_secure = false;
+ if (buffer_collection_constraints.image_format_constraints_count != 0) {
+ fake.settings.has_image_format_constraints = true;
+ // pick option 0 arbitrarily (essentially picking PixelFormat 0 among possibly multiple PixelFormat(s))
+ fake.settings.image_format_constraints = fidl::Clone(buffer_collection_constraints.image_format_constraints[0]);
+ } else {
+ fake.settings.has_image_format_constraints = false;
+ }
+ CoreCodecSetBufferCollectionInfo(port, fake);
+ }
+}
+
+void CodecImpl::OnBufferCollectionInfo(
+ CodecPort port, uint64_t buffer_lifetime_ordinal, zx_status_t status,
+ fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info) {
+ ZX_DEBUG_ASSERT(thrd_current() == fidl_thread());
+
+ if (port == kInputPort) {
+ PostSysmemCompletion([this, port, buffer_lifetime_ordinal, status, buffer_collection_info = std::move(buffer_collection_info)]() mutable {
+ OnBufferCollectionInfoInternal(port, buffer_lifetime_ordinal, status, std::move(buffer_collection_info));
+ });
+ } else {
+ ZX_DEBUG_ASSERT(port == kOutputPort);
+ OnBufferCollectionInfoInternal(port, buffer_lifetime_ordinal, status, std::move(buffer_collection_info));
+ }
+}
+
+void CodecImpl::OnBufferCollectionInfoInternal(CodecPort port, uint64_t buffer_lifetime_ordinal, zx_status_t allocate_status, fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info) {
+ ZX_DEBUG_ASSERT(port == kInputPort && thrd_current() == stream_control_thread_ ||
+ port == kOutputPort && thrd_current() == fidl_thread());
+
+ { // scope lock
+ std::lock_guard<std::mutex> lock(lock_);
+ if (IsStoppingLocked()) {
+ return;
+ }
+
+ // The buffer_lifetime_ordinal_[port] can only change on the current thread.
+ if (buffer_lifetime_ordinal != buffer_lifetime_ordinal_[port]) {
+ // stale response
+ return;
+ }
+ if (allocate_status != ZX_OK) {
+ Fail("OnBufferCollectionInfoLocked() sees failure - port: %d allocate_status: %d", port, allocate_status);
+ return;
+ }
+ } // ~lock
+
+ uint32_t buffer_count = buffer_collection_info.buffer_count;
+
+ // This code trusts sysmem to really be sysmem and to behave correctly, but
+ // doesn't hurt to double-check some things in debug build.
+ ZX_DEBUG_ASSERT(buffer_count >= 1);
+ ZX_DEBUG_ASSERT(buffer_count <= countof(buffer_collection_info.buffers));
+ // Spot check that the boundary between valid and invalid handles is where it
+ // should be.
+ ZX_DEBUG_ASSERT(buffer_collection_info.buffers[buffer_count-1].vmo.is_valid());
+ ZX_DEBUG_ASSERT(buffer_count == countof(buffer_collection_info.buffers) ||
+ !buffer_collection_info.buffers[buffer_count].vmo.is_valid());
+
+ // Let's move the VMO handles out first, so that the BufferCollectionInfo_2 we
+ // send to the core codec doesn't have the VMO handles. We want the core
+ // codec to get its VMO handles via the CodecBuffer*(s) we'll provide shortly
+ // below.
+ zx::vmo vmos[countof(buffer_collection_info.buffers)];
+ for (uint32_t i = 0; i < buffer_count; ++i) {
+ vmos[i] = std::move(buffer_collection_info.buffers[i].vmo);
+ ZX_DEBUG_ASSERT(!buffer_collection_info.buffers[i].vmo.is_valid());
+ }
+
+ // Now we can tell the core codec about the collection info. The core codec
+ // can clone the FIDL struct if it wants, or can just copy out any info it
+ // wants from specific fields.
+ CoreCodecSetBufferCollectionInfo(port, buffer_collection_info);
+
+ { // scope lock
+ std::lock_guard<std::mutex> lock(lock_);
+
+ ZX_DEBUG_ASSERT(
+ buffer_lifetime_ordinal == buffer_lifetime_ordinal_[port]);
+
+ // The only way port_settings_[port] gets cleared is if
+ // buffer_lifetime_ordinal changes.
+ ZX_DEBUG_ASSERT(port_settings_[port]);
+
+ // This completes the settings, analogous to having completed
+ // SetInputBufferSettings()/SetOutputBufferSettings().
+ port_settings_[port]->SetBufferCollectionInfo(
+ std::move(buffer_collection_info));
+ }
+
+ // We convert the buffer_collection_info into AddInputBuffer_StreamControl()
+ // and AddOutputBufferInternal() calls, almost as if the client were adding
+ // the buffers itself (but without the check that the client isn't adding
+ // buffers itself while using sysmem).
+ for (uint32_t i = 0; i < buffer_count; i++) {
+ // While under the lock we'll move out the stuff we need into codec_buffer.
+ fuchsia::media::StreamBuffer stream_buffer;
+ { // scope lock
+ std::lock_guard<std::mutex> lock(lock_);
+
+ ZX_DEBUG_ASSERT(
+ buffer_lifetime_ordinal == buffer_lifetime_ordinal_[port]);
+ ZX_DEBUG_ASSERT(port_settings_[port]);
+
+ stream_buffer.set_buffer_lifetime_ordinal(buffer_lifetime_ordinal);
+ stream_buffer.set_buffer_index(i);
+ fuchsia::media::StreamBufferDataVmo data_vmo;
+ data_vmo.set_vmo_handle(std::move(vmos[i]));
+ data_vmo.set_vmo_usable_start(port_settings_[port]->vmo_usable_start(i));
+ data_vmo.set_vmo_usable_size(port_settings_[port]->vmo_usable_size());
+ fuchsia::media::StreamBufferData data;
+ data.set_vmo(std::move(data_vmo));
+ stream_buffer.set_data(std::move(data));
+ } // ~lock
+ if (port == kInputPort) {
+ AddInputBuffer_StreamControl(false, std::move(stream_buffer));
+ } else {
+ ZX_DEBUG_ASSERT(port == kOutputPort);
+ AddOutputBufferInternal(false, std::move(stream_buffer));
+ }
+ }
}
void CodecImpl::EnsureBuffersNotConfigured(std::unique_lock<std::mutex>& lock,
@@ -1261,17 +1794,29 @@
//
// On input, this can only be called on stream_control_thread_.
//
- // On output, this can be called on stream_control_thread_ or output_thread_.
- ZX_DEBUG_ASSERT(thrd_current() == stream_control_thread_ ||
- (port == kOutputPort && (thrd_current() == fidl_thread())));
+ // On output, this can be called on stream_control_thread_ or fidl_thread().
+ ZX_DEBUG_ASSERT(port == kInputPort && thrd_current() == stream_control_thread_ ||
+ port == kOutputPort && (thrd_current() == stream_control_thread_ || thrd_current() == fidl_thread()));
- is_port_configured_[port] = false;
+ is_port_buffers_configured_[port] = false;
+ if (buffer_lifetime_ordinal_[port] % 2 == 1) {
+ buffer_lifetime_ordinal_[port]++;
+ }
+ if (port_settings_[port]) {
+ // This will close the BufferCollection async cleanly, without causing the
+ // LogicalBufferCollection to fail. Mainly we care so we can more easily
+ // tell during debugging whether a LogicalBufferCollection was cleanly
+ // closed by all participants, vs. potentially getting failed by a
+ // participant exiting or non-cleanly closing. A Sync() by the client is
+ // sufficient to ensure this async close is done.
+ port_settings_[port].reset();
+ }
// Ensure that buffers aren't with the core codec.
{ // scope unlock
ScopedUnlock unlock(lock);
CoreCodecEnsureBuffersNotConfigured(port);
- }
+ } // ~unlock
// For mid-stream output config change, the caller is responsible for ensuring
// that buffers are not with the HW first.
@@ -1306,12 +1851,15 @@
FailLocked("packet_count_for_server > packet_count_for_server_max");
return false;
}
-
if (!settings.has_packet_count_for_client()) {
FailLocked("settings do not have packet count for client");
return false;
}
-
+ if (settings.packet_count_for_client() <
+ constraints.packet_count_for_client_min()) {
+ FailLocked("packet_count_for_client < packet_count_for_client_min - port: %d %d < %d", port, settings.packet_count_for_client(), constraints.packet_count_for_client_min());
+ return false;
+ }
if (settings.packet_count_for_client() >
constraints.packet_count_for_client_max()) {
FailLocked("packet_count_for_client > packet_count_for_client_max");
@@ -1343,17 +1891,75 @@
return true;
}
-bool CodecImpl::AddBufferCommon(CodecPort port,
+bool CodecImpl::ValidatePartialBufferSettingsVsConstraintsLocked(
+ CodecPort port, const fuchsia::media::StreamBufferPartialSettings& partial_settings,
+ const fuchsia::media::StreamBufferConstraints& constraints) {
+ // Most of the constraints will be handled by telling sysmem about them, not
+ // via the client, so there's not a ton to validate here.
+ if ((partial_settings.has_single_buffer_mode() && partial_settings.single_buffer_mode()) && !constraints.single_buffer_mode_allowed()) {
+ FailLocked("single_buffer_mode && !single_buffer_mode_allowed");
+ return false;
+ }
+ bool packet_count_needed = partial_settings.has_single_buffer_mode() && partial_settings.single_buffer_mode();
+ ZX_DEBUG_ASSERT(partial_settings.sysmem_token().is_valid());
+ if (packet_count_needed) {
+ if (!partial_settings.has_packet_count_for_server()) {
+ FailLocked("missing packet_count_for_server");
+ return false;
+ }
+ if (!partial_settings.has_packet_count_for_client()) {
+ FailLocked("missing packet_count_for_client");
+ return false;
+ }
+ } else {
+ // If packet count isn't required, it can still be provided, but if it's
+ // provided, it should be provided in two parts like usual.
+ if (partial_settings.has_packet_count_for_server() !=
+ partial_settings.has_packet_count_for_client()) {
+ FailLocked("has_packet_count_for_server != has_packet_count_for_client");
+ return false;
+ }
+ }
+ // if needed or provided anyway
+ if (partial_settings.has_packet_count_for_server()) {
+ if (partial_settings.packet_count_for_server() <
+ constraints.packet_count_for_server_min()) {
+ FailLocked("packet_count_for_server < packet_count_for_server_min");
+ return false;
+ }
+ if (partial_settings.packet_count_for_server() >
+ constraints.packet_count_for_server_max()) {
+ FailLocked("packet_count_for_server > packet_count_for_server_max");
+ return false;
+ }
+ }
+ // if needed or provided anyway
+ if (partial_settings.has_packet_count_for_client()) {
+ if (partial_settings.packet_count_for_client() <
+ constraints.packet_count_for_client_min()) {
+ FailLocked("packet_count_for_client > packet_count_for_client_max");
+ return false;
+ }
+ if (partial_settings.packet_count_for_client() >
+ constraints.packet_count_for_client_max()) {
+ FailLocked("packet_count_for_client > packet_count_for_client_max");
+ return false;
+ }
+ }
+ return true;
+}
+
+bool CodecImpl::AddBufferCommon(bool is_client, CodecPort port,
fuchsia::media::StreamBuffer buffer) {
ZX_DEBUG_ASSERT(port == kInputPort &&
(thrd_current() == stream_control_thread_) ||
port == kOutputPort && (thrd_current() == fidl_thread()));
- bool done_configuring = false;
+ bool buffers_done_configuring = false;
{ // scope lock
std::unique_lock<std::mutex> lock(lock_);
if (!buffer.has_buffer_lifetime_ordinal()) {
- FailFatalLocked("Client send a buffer without a buffer_lifetime_ordinal");
+ FailLocked("Client send a buffer without a buffer_lifetime_ordinal");
return false;
}
@@ -1392,6 +1998,11 @@
return false;
}
+ if (is_client && port_settings_[port]->is_partial_settings()) {
+ FailLocked(
+ "AddInputBuffer()/AddOutputBuffer() not permitted when using sysmem");
+ return false;
+ }
if (!buffer.has_buffer_index()) {
FailLocked("client sent buffer without index");
return false;
@@ -1404,8 +2015,7 @@
return false;
}
- uint32_t required_buffer_count =
- BufferCountFromPortSettings(*port_settings_[port]);
+ uint32_t required_buffer_count = port_settings_[port]->buffer_count();
if (buffer.buffer_index() >= required_buffer_count) {
FailLocked("AddOutputBuffer()/AddInputBuffer() extra buffer - port: %d",
port);
@@ -1441,15 +2051,13 @@
port_settings_[port]->buffer_constraints_version_ordinal();
// Now we allocate all_packets_[port].
ZX_DEBUG_ASSERT(all_packets_[port].empty());
- uint32_t packet_count =
- PacketCountFromPortSettings(*port_settings_[port]);
+ uint32_t packet_count = port_settings_[port]->packet_count();
for (uint32_t i = 0; i < packet_count; i++) {
// Private constructor to prevent core codec maybe creating its own
// Packet instances (which isn't the intent) seems worth the hassle of
// not using make_unique<>() here.
- all_packets_[port].push_back(
- std::unique_ptr<CodecPacket>(new CodecPacket(
- port_settings_[port]->buffer_lifetime_ordinal(), i)));
+ all_packets_[port].push_back(std::unique_ptr<CodecPacket>(
+ new CodecPacket(port_settings_[port]->buffer_lifetime_ordinal(), i)));
}
{ // scope unlock
@@ -1470,20 +2078,19 @@
}
} // ~unlock
- // For OMX case, we tell OMX about the potentially-new buffer count
- // separately later, just before moving from OMX loaded to OMX idle, or as
- // part of mid-stream output config change.
+ is_port_buffers_configured_[port] = true;
+ buffers_done_configuring = true;
- // For OMX case, we don't allocate OMX_BUFFERHEADERTYPE yet here by
- // calling OMX UseBuffer() yet, because we can be in OMX_StateLoaded
- // currently, and OMX UseBuffer() isn't valid until we're moving from
- // OMX_StateLoaded to OMX_StateIdle.
-
- is_port_configured_[port] = true;
- done_configuring = true;
+ // For client-called AddOutputBuffer(), the last buffer being added is
+ // analogous to CompleteOutputBufferPartialSettings(); we handle that
+ // analogous-ness in IsOutputConfiguredLocked() (not by pretending we got
+ // a CompleteOutputBufferPartialSettings() here), so
+ // is_port_buffers_configured_[port] = true above is enough to make
+ // IsOutputConfiguredLocked() return true if this is a client-driven
+ // AddOutputBuffer().
}
}
- return done_configuring;
+ return buffers_done_configuring;
}
bool CodecImpl::CheckOldBufferLifetimeOrdinalLocked(
@@ -1539,10 +2146,7 @@
}
EnsureStreamClosed(lock);
-
- ZX_DEBUG_ASSERT((stream_lifetime_ordinal_ % 2 == 0) &&
- "expecting no current stream");
- ZX_DEBUG_ASSERT(!stream_);
+ ZX_DEBUG_ASSERT(!IsStreamActiveLocked());
// Now it's time to start the new stream. We start the new stream at
// Codec layer first then core codec layer.
@@ -1630,8 +2234,7 @@
// detection.
is_new_config_needed = true;
} else if (IsOutputConfiguredLocked() &&
- port_settings_[kOutputPort]
- ->buffer_constraints_version_ordinal() <=
+ port_settings_[kOutputPort]->buffer_constraints_version_ordinal() <=
core_codec_meh_output_buffer_constraints_version_ordinal_) {
// The core codec previously expressed "meh" regarding the current config's
// buffer_constraints_version_ordinal, so to avoid mixing that with core
@@ -1652,13 +2255,13 @@
// at the start of a stream - it's still while a stream is active, and still
// prevents this stream from outputting any data to the Codec client until
// the Codec client re-configures output while this stream is active.
- GenerateAndSendNewOutputConfig(lock, true);
+ GenerateAndSendNewOutputConstraints(lock, true);
// Now we can wait for the client to catch up to the current output config
// or for the client to tell the server to discard the current stream.
while (!IsStoppingLocked() && !stream_->future_discarded() &&
!IsOutputConfiguredLocked()) {
- wake_stream_control_condition_.wait(lock);
+ RunAnySysmemCompletionsOrWait(lock);
}
if (IsStoppingLocked()) {
@@ -1703,9 +2306,7 @@
// Now close the old stream at the Codec layer.
EnsureCodecStreamClosedLockedInternal();
- ZX_DEBUG_ASSERT((stream_lifetime_ordinal_ % 2 == 0) &&
- "expecting no current stream");
- ZX_DEBUG_ASSERT(!stream_);
+ ZX_DEBUG_ASSERT(!IsStreamActiveLocked());
}
// The only valid caller of this is EnsureStreamClosed(). We have this in a
@@ -1726,6 +2327,87 @@
ZX_DEBUG_ASSERT(stream_lifetime_ordinal_ % 2 == 0);
}
+bool CodecImpl::RunAnySysmemCompletions(std::unique_lock<std::mutex>& lock) {
+ ZX_DEBUG_ASSERT(thrd_current() == stream_control_thread_);
+ // Typically this loop will run once, but on return we want the queue to be
+ // empty even if more showed up while in this method, for condition_variable
+ // signalling reasons.
+ bool any_ran = false;
+ while (!sysmem_completion_queue_.empty()) {
+ // We'll run them all, so extract all the items and run them all.
+ std::queue<fit::closure> local_batch_to_run;
+ local_batch_to_run.swap(sysmem_completion_queue_);
+ // The unlock doesn't cause queue re-ordering, though so far none of these
+ // items care anyway.
+ ScopedUnlock unlock(lock);
+ while (!local_batch_to_run.empty()) {
+ any_ran = true;
+ fit::closure to_run = std::move(local_batch_to_run.front());
+ local_batch_to_run.pop();
+ to_run();
+ }
+ }
+ return any_ran;
+}
+
+void CodecImpl::PostSysmemCompletion(fit::closure to_run) {
+ ZX_DEBUG_ASSERT(thrd_current() == fidl_thread());
+
+ { // scope lock
+ std::unique_lock<std::mutex> lock(lock_);
+ sysmem_completion_queue_.emplace(std::move(to_run));
+ // In case there is no WaitEnsureSysmemReadyOnInput(), we post to
+ // StreamControl to ensure that RunAnySysmemCompletions() runs soon.
+ // Don't let them accumulate though.
+ if (!is_sysmem_runner_pending_) {
+ is_sysmem_runner_pending_ = true;
+ PostToStreamControl([this]{
+ std::unique_lock<std::mutex> lock(lock_);
+ RunAnySysmemCompletions(lock);
+ ZX_DEBUG_ASSERT(sysmem_completion_queue_.empty());
+ is_sysmem_runner_pending_ = false;
+ });
+ }
+ } // ~lock
+
+ // In case to_run needs to get run by a QueueInput...StreamControl() method
+ // via WaitEnsureSysmemReadyOnInput(), we wake the StreamControl thread. We
+ // must do this even if is_sysmem_runner_pending_, in case that runner won't
+ // run for a while due to WaitEnsureSysmemReadyOnInput() blocking
+ // StreamControl.
+ wake_stream_control_condition_.notify_all();
+}
+
+bool CodecImpl::WaitEnsureSysmemReadyOnInput(std::unique_lock<std::mutex>& lock) {
+ ZX_DEBUG_ASSERT(thrd_current() == stream_control_thread_);
+ // Input buffer re-config is not permitted unless there's no current stream.
+ ZX_DEBUG_ASSERT(!IsStreamActiveLocked());
+ while (!IsInputConfiguredLocked()) {
+ RunAnySysmemCompletionsOrWait(lock);
+ // No need to check for stream switch since it's not permitted for a client
+ // to be sending any message that can cause a new stream until after the
+ // client is done configuring input buffers (enforced elsewhere).
+ if (IsStoppingLocked()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void CodecImpl::RunAnySysmemCompletionsOrWait(
+ std::unique_lock<std::mutex>& lock) {
+ // If any sysmem completions ran, we immediately return, so that conditions
+ // can be checked again in the caller immediately.
+ ZX_DEBUG_ASSERT(thrd_current() == stream_control_thread_);
+ bool any_completions_ran = RunAnySysmemCompletions(lock);
+ ZX_DEBUG_ASSERT(sysmem_completion_queue_.empty());
+ if (!any_completions_ran) {
+ // We know sysmem_completion_queue_.empty() and the lock is held just before
+ // this wait().
+ wake_stream_control_condition_.wait(lock);
+ }
+}
+
// This is called on Output ordering domain (FIDL thread) any time a message is
// received which would be able to start a new stream.
//
@@ -1836,10 +2518,10 @@
}
// This method is only called when buffer_constraints_action_required will be
-// true in an OnOutputConfig() message sent shortly after this method call.
+// true in an OnOutputConstraints() message sent shortly after this method call.
//
// Even if the client is switching streams rapidly without configuring output,
-// this method and GenerateAndSendNewOutputConfig() with
+// this method and GenerateAndSendNewOutputConstraints() with
// buffer_constraints_action_required true always run in pairs.
//
// This is what starts the interval during which
@@ -1863,12 +2545,11 @@
buffer_lifetime_ordinal_[kOutputPort]++;
ZX_DEBUG_ASSERT(buffer_lifetime_ordinal_[kOutputPort] % 2 == 0);
ZX_DEBUG_ASSERT(buffer_lifetime_ordinal_[kOutputPort] ==
- port_settings_[kOutputPort]->buffer_lifetime_ordinal() +
- 1);
+ port_settings_[kOutputPort]->buffer_lifetime_ordinal() + 1);
}
// When buffer_constraints_action_required true, we can assert in
- // GenerateAndSendNewOutputConfig() that this value is still the
+ // GenerateAndSendNewOutputConstraints() that this value is still the
// next_output_buffer_constraints_version_ordinal_ in that method.
last_required_buffer_constraints_version_ordinal_[kOutputPort] =
next_output_buffer_constraints_version_ordinal_;
@@ -1900,7 +2581,7 @@
ZX_DEBUG_ASSERT(is_output_ordering_domain_done_with_recycle_output_packet);
}
-void CodecImpl::GenerateAndSendNewOutputConfig(
+void CodecImpl::GenerateAndSendNewOutputConstraints(
std::unique_lock<std::mutex>& lock,
bool buffer_constraints_action_required) {
// When client action is required, this can only happen on the StreamControl
@@ -1914,8 +2595,6 @@
uint64_t current_stream_lifetime_ordinal = stream_lifetime_ordinal_;
uint64_t new_output_buffer_constraints_version_ordinal =
next_output_buffer_constraints_version_ordinal_++;
- uint64_t new_output_format_details_version_ordinal =
- next_output_format_details_version_ordinal_++;
// If buffer_constraints_action_required true, the caller bumped the
// last_required_buffer_constraints_version_ordinal_[kOutputPort] before
@@ -1928,67 +2607,87 @@
(last_required_buffer_constraints_version_ordinal_[kOutputPort] ==
new_output_buffer_constraints_version_ordinal));
- // printf("GenerateAndSendNewOutputConfig
+ // printf("GenerateAndSendNewOutputConstraints
// new_output_buffer_constraints_version_ordinal: %lu
// buffer_constraints_action_required: %d\n",
// new_output_buffer_constraints_version_ordinal,
// buffer_constraints_action_required);
- std::unique_ptr<const fuchsia::media::StreamOutputConfig> output_config;
+ std::unique_ptr<const fuchsia::media::StreamOutputConstraints>
+ output_constraints;
+ uint64_t next_output_format_details_version_ordinal =
+ next_output_format_details_version_ordinal_;
+ fuchsia::media::StreamOutputConfig output_config;
{ // scope unlock
ScopedUnlock unlock(lock);
+
// Don't call the core codec under the lock_, because we can avoid doing so,
// and to allow the core codec to use this thread to call back into
// CodecImpl using this stack if needed. So far we don't have any actual
// known examples of a core codec using this thread to call back into
// CodecImpl using this stack.
- output_config = CoreCodecBuildNewOutputConfig(
+ output_constraints = CoreCodecBuildNewOutputConstraints(
current_stream_lifetime_ordinal,
new_output_buffer_constraints_version_ordinal,
- new_output_format_details_version_ordinal,
buffer_constraints_action_required);
+
+ // Temporarily still generate config also by converting from constraints and
+ // getting format also - this only works for now because core codecs happen
+ // to be able to provide format this early because they can only generate 1
+ // for now etc - but for now it can work during the transition.
+ fuchsia::media::StreamOutputFormat output_format = CoreCodecGetOutputFormat(
+ current_stream_lifetime_ordinal,
+ next_output_format_details_version_ordinal);
+ output_config.set_stream_lifetime_ordinal(
+ output_constraints->stream_lifetime_ordinal());
+ output_config.set_buffer_constraints_action_required(
+ output_constraints->buffer_constraints_action_required());
+ output_config.set_buffer_constraints(
+ fidl::Clone(output_constraints->buffer_constraints()));
+ output_config.set_format_details(
+ fidl::Clone(output_format.format_details()));
} // ~unlock
- // We only call GenerateAndSendNewOutputConfig() from contexts that won't be
+
+ // We only call GenerateAndSendNewOutputConstraints() from contexts that won't be
// changing the stream_lifetime_ordinal_, so the fact that we released the
// lock above doesn't mean the stream_lifetime_ordinal_ could have changed, so
// we can assert here that it's still the same as above.
ZX_DEBUG_ASSERT(current_stream_lifetime_ordinal == stream_lifetime_ordinal_);
- output_config_ = std::move(output_config);
+ output_constraints_ = std::move(output_constraints);
- // Stay under lock after setting output_config_, to get proper ordering of
+ // Stay under lock after setting output_constraints_, to get proper ordering of
// sent messages even if a hostile client deduces the content of this message
// before we've sent it and manages to get the server to send another
- // subsequent OnOutputConfig().
+ // subsequent OnOutputConstraints().
ZX_DEBUG_ASSERT(sent_buffer_constraints_version_ordinal_[kOutputPort] + 1 ==
new_output_buffer_constraints_version_ordinal);
- ZX_DEBUG_ASSERT(sent_format_details_version_ordinal_[kOutputPort] + 1 ==
- new_output_format_details_version_ordinal);
// Setting this within same lock hold interval as we queue the message to be
- // sent in order vs. other OnOutputConfig() messages. This way we can verify
+ // sent in order vs. other OnOutputConstraints() messages. This way we can verify
// that the client's incoming messages are not trying to configure with
// respect to a buffer_constraints_version_ordinal that is newer than we've
// actually sent the client.
sent_buffer_constraints_version_ordinal_[kOutputPort] =
new_output_buffer_constraints_version_ordinal;
- sent_format_details_version_ordinal_[kOutputPort] =
- new_output_format_details_version_ordinal;
- // Intentional copy of fuchsia::media::OutputConfig output_config_ here,
- // as we want output_config_ to remain valid (at least for debugging reasons
- // for now).
+ // Intentional copy of fuchsia::media::OutputConfig output_constraints_ here,
+ // as we want output_constraints_ to remain valid (at least for debugging
+ // reasons for now).
PostToSharedFidl(
- [this, output_config = fidl::Clone(*output_config_)]() mutable {
+ [this, output_constraints = fidl::Clone(*output_constraints_),
+ output_config = std::move(output_config)]() mutable {
// See "is_bound_checks" comment up top.
if (binding_.is_bound()) {
+ binding_.events().OnOutputConstraints(std::move(output_constraints));
+ // DEPRECATED - keep sending this temporarily.
binding_.events().OnOutputConfig(std::move(output_config));
}
});
}
-void CodecImpl::MidStreamOutputConfigChange(uint64_t stream_lifetime_ordinal) {
+void CodecImpl::MidStreamOutputConstraintsChange(uint64_t stream_lifetime_ordinal) {
ZX_DEBUG_ASSERT(thrd_current() == stream_control_thread_);
{ // scope lock
std::unique_lock<std::mutex> lock(lock_);
@@ -2020,13 +2719,13 @@
EnsureBuffersNotConfigured(lock, kOutputPort);
- GenerateAndSendNewOutputConfig(lock, true);
+ GenerateAndSendNewOutputConstraints(lock, true);
// Now we can wait for the client to catch up to the current output config
// or for the client to tell the server to discard the current stream.
while (!IsStoppingLocked() && !stream_->future_discarded() &&
!IsOutputConfiguredLocked()) {
- wake_stream_control_condition_.wait(lock);
+ RunAnySysmemCompletionsOrWait(lock);
}
if (IsStoppingLocked()) {
@@ -2040,6 +2739,9 @@
// the start of the new stream.
return;
}
+
+ // For asserts.
+ stream_->ClearMidStreamOutputConstraintsChangeActive();
} // ~lock
CoreCodecMidStreamOutputBufferReConfigFinish();
@@ -2047,6 +2749,109 @@
VLOGF("Done with mid-stream format change.\n");
}
+bool CodecImpl::FixupBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings,
+ fuchsia::sysmem::BufferCollectionConstraints* buffer_collection_constraints) {
+ fuchsia::sysmem::BufferUsage& usage = buffer_collection_constraints->usage;
+
+ if (IsCoreCodecMappedBufferNeeded(port)) {
+ // Not surprisingly, both decoders and encoders read from input and write to
+ // output.
+ if (port == kInputPort) {
+ if (usage.cpu & ~(fuchsia::sysmem::cpuUsageRead | fuchsia::sysmem::cpuUsageReadOften)) {
+ Fail("Core codec set disallowed CPU usage bits (input port).");
+ return false;
+ }
+ usage.cpu |=
+ fuchsia::sysmem::cpuUsageRead | fuchsia::sysmem::cpuUsageReadOften;
+ } else {
+ if (usage.cpu & ~(fuchsia::sysmem::cpuUsageWrite | fuchsia::sysmem::cpuUsageWriteOften)) {
+ Fail("Core codec set disallowed CPU usage bit(s) (output port).");
+ return false;
+ }
+ usage.cpu |=
+ fuchsia::sysmem::cpuUsageWrite | fuchsia::sysmem::cpuUsageWriteOften;
+ }
+ } else {
+ if (usage.cpu) {
+ Fail("Core codec set usage.cpu despite !IsCoreCodecMappedBufferNeeded()");
+ return false;
+ }
+ // The CPU won't touch the buffers at all.
+ usage.cpu = 0;
+ }
+ if (usage.vulkan) {
+ Fail("Core codec set usage.vulkan bits");
+ return false;
+ }
+ ZX_DEBUG_ASSERT(!usage.vulkan);
+ if (usage.display) {
+ Fail("Core codec set usage.display bits");
+ return false;
+ }
+ ZX_DEBUG_ASSERT(!usage.display);
+ if (IsCoreCodecHwBased()) {
+ // Let's see if we can deprecate videoUsageHwProtected, since it's redundant
+ // with secure_required and secure_permitted.
+ if (usage.video & fuchsia::sysmem::videoUsageHwProtected) {
+ Fail("Core codec set deprecated videoUsageHwProtected - disallow");
+ return false;
+ }
+ uint32_t allowed_video_usage_bits = IsDecoder() ? fuchsia::sysmem::videoUsageHwDecoder : fuchsia::sysmem::videoUsageHwEncoder;
+ if (usage.video & ~allowed_video_usage_bits) {
+ Fail("Core codec set disallowed video usage bit(s) - port: %d, usage: 0x%08x, allowed: 0x%08x", port, usage.video, allowed_video_usage_bits);
+ return false;
+ }
+ if (IsDecoder()) {
+ usage.video |= fuchsia::sysmem::videoUsageHwDecoder;
+ } else {
+ usage.video |= fuchsia::sysmem::videoUsageHwEncoder;
+ }
+ } else {
+ // Despite being a video decoder or encoder, a SW decoder or encoder doesn't
+ // count as videoUsageHwDecoder or videoUsageHwEncoder. And definitely not
+ // videoUsageHwProtected.
+ usage.video = 0;
+ }
+
+ bool is_single_buffer_mode = partial_settings.has_single_buffer_mode() && partial_settings.single_buffer_mode();
+
+ uint32_t required_min_buffer_count_for_camping;
+ if (is_single_buffer_mode) {
+ required_min_buffer_count_for_camping =
+ static_cast<uint32_t>(partial_settings.packet_count_for_server() != 0);
+ } else {
+ required_min_buffer_count_for_camping =
+ partial_settings.packet_count_for_server();
+ }
+ if (buffer_collection_constraints->min_buffer_count_for_camping <
+ required_min_buffer_count_for_camping) {
+ Fail("Core codec set min_buffer_count_for_camping too low - min_buffer_count_for_camping: %lu required_min_buffer_count_for_camping: %lu", buffer_collection_constraints->min_buffer_count_for_camping, required_min_buffer_count_for_camping);
+ return false;
+ }
+
+ if (is_single_buffer_mode) {
+ if (buffer_collection_constraints->min_buffer_count_for_camping > 1) {
+ Fail("Core codec set min_buffer_count_for_camping too high for single_buffer_mode - min_buffer_count_for_camping: %lu", buffer_collection_constraints->min_buffer_count_for_camping);
+ return false;
+ }
+ if (buffer_collection_constraints->min_buffer_count_for_dedicated_slack != 0 ||
+ buffer_collection_constraints->min_buffer_count_for_shared_slack != 0) {
+ Fail("Core codec set slack with single_buffer_mode - min_buffer_count_for_dedicated_slack: %lu min_buffer_count_for_shared_slack: %lu", buffer_collection_constraints->min_buffer_count_for_dedicated_slack, buffer_collection_constraints->min_buffer_count_for_shared_slack);
+ return false;
+ }
+ if (buffer_collection_constraints->max_buffer_count != 1) {
+ Fail("CoreCodec must specify max_buffer_count 1 when single_buffer_mode");
+ return false;
+ }
+ }
+
+ // The rest of the constraints are entirely up to the core codec.
+
+ return true;
+}
+
thrd_t CodecImpl::fidl_thread() { return shared_fidl_thread_; }
void CodecImpl::SendFreeInputPacketLocked(fuchsia::media::PacketHeader header) {
@@ -2067,22 +2872,46 @@
}
bool CodecImpl::IsInputConfiguredLocked() {
- return IsPortConfiguredCommonLocked(kInputPort);
+ return IsPortBuffersConfiguredCommonLocked(kInputPort);
}
bool CodecImpl::IsOutputConfiguredLocked() {
- return IsPortConfiguredCommonLocked(kOutputPort);
+ if (!IsPortBuffersConfiguredCommonLocked(kOutputPort)) {
+ return false;
+ }
+ ZX_DEBUG_ASSERT(port_settings_[kOutputPort]);
+ if (!port_settings_[kOutputPort]->is_partial_settings()) {
+ return true;
+ }
+ if (!port_settings_[kOutputPort]->is_complete_seen_output()) {
+ return false;
+ }
+ return true;
}
-bool CodecImpl::IsPortConfiguredCommonLocked(CodecPort port) {
+bool CodecImpl::IsPortBuffersConfiguredCommonLocked(CodecPort port) {
// In addition to what we're able to assert here, when
- // is_port_configured_[port], the core codec also has the port
+ // is_port_buffers_configured_[port], the core codec also has the port
// configured.
- ZX_DEBUG_ASSERT(!is_port_configured_[port] ||
+ ZX_DEBUG_ASSERT(!is_port_buffers_configured_[port] ||
port_settings_[port] &&
- all_buffers_[port].size() ==
- BufferCountFromPortSettings(*port_settings_[port]));
- return is_port_configured_[port];
+ all_buffers_[port].size() == port_settings_[port]->buffer_count());
+ return is_port_buffers_configured_[port];
+}
+
+bool CodecImpl::IsPortBuffersAtLeastPartiallyConfiguredLocked(CodecPort port) {
+ if (IsPortBuffersConfiguredCommonLocked(port)) {
+ return true;
+ }
+ if (!port_settings_[port]) {
+ return false;
+ }
+ if (!port_settings_[port]->is_partial_settings()) {
+ return false;
+ }
+ ZX_DEBUG_ASSERT(port_settings_[port] && port_settings_[port]->is_partial_settings());
+ ZX_DEBUG_ASSERT(buffer_lifetime_ordinal_[port] % 2 == 1);
+ return true;
}
void CodecImpl::Fail(const char* format, ...) {
@@ -2118,7 +2947,7 @@
void CodecImpl::vFail(bool is_fatal, const char* format, va_list args) {
{ // scope lock
std::lock_guard<std::mutex> lock(lock_);
- vFailLocked(false, format, args);
+ vFailLocked(is_fatal, format, args);
} // ~lock
}
@@ -2202,6 +3031,16 @@
return IsStoppingLocked();
}
+bool CodecImpl::IsDecoder() {
+ ZX_DEBUG_ASSERT(!!decoder_params_ ^ !!encoder_params_);
+ return !!decoder_params_;
+}
+
+bool CodecImpl::IsEncoder() {
+ ZX_DEBUG_ASSERT(!!decoder_params_ ^ !!encoder_params_);
+ return !!encoder_params_;
+}
+
// true - maybe it's the core codec thread.
// false - it's definitely not the core codec thread.
bool CodecImpl::IsPotentiallyCoreCodecThread() {
@@ -2291,27 +3130,29 @@
} // ~lock
}
-void CodecImpl::onCoreCodecMidStreamOutputConfigChange(
+void CodecImpl::onCoreCodecMidStreamOutputConstraintsChange(
bool output_re_config_required) {
+ VLOGF("CodecImpl::onCoreCodecMidStreamOutputConstraintsChange(): %d\n",
+ output_re_config_required);
// For now, the core codec thread is the only thread this gets called from.
ZX_DEBUG_ASSERT(IsPotentiallyCoreCodecThread());
// For a OMX_EventPortSettingsChanged that doesn't demand output buffer
// re-config before more output data, this translates to an ordered emit
- // of a no-action-required OnOutputConfig() that just updates to the new
+ // of a no-action-required OnOutputConstraints() that just updates to the new
// format, without demanding output buffer re-config. HDR info can be
// conveyed this way, ordered with respect to output frames. OMX
// requires that we use this thread to collect OMX format info during
// EventHandler().
if (!output_re_config_required) {
std::unique_lock<std::mutex> lock(lock_);
- GenerateAndSendNewOutputConfig(
+ GenerateAndSendNewOutputConstraints(
lock,
false); // buffer_constraints_action_required
return;
}
- // We have an OMX_EventPortSettingsChanged that does demand output
- // buffer re-config before more output data.
+ // We have an output constraints change that does demand output buffer
+ // re-config before more output data.
ZX_DEBUG_ASSERT(output_re_config_required);
// We post over to StreamControl domain because we need to synchronize
@@ -2319,7 +3160,7 @@
// When we get over there to StreamControl, we'll check if we're still
// talking about the same stream_lifetime_ordinal, and if not, we ignore
// the event, because a new stream may or may not have the same output
- // settings, and we'll be re-generating an OnOutputConfig() as needed
+ // settings, and we'll be re-generating an OnOutputConstraints() as needed
// from current/later OMX output config anyway. Here are the
// possibilities:
// * Prior to the client moving to a new stream, we process this event
@@ -2332,16 +3173,29 @@
// StreamControl. In this case we ignore the event on StreamControl
// domain since its stale by that point, but instead we use
// omx_meh_output_buffer_constraints_version_ordinal_ to cause the
- // client's next stream to start with a new OnOutputConfig() that
+ // client's next stream to start with a new OnOutputConstraints() that
// the client must catch up to before the stream can fully start.
// This way we know we're not ignoring a potential change to
// nBufferCountMin or anything like that.
uint64_t local_stream_lifetime_ordinal;
{ // scope lock
std::lock_guard<std::mutex> lock(lock_);
+
+ // The core codec is only allowed to call this mehtod while there's an
+ // active stream.
+ ZX_DEBUG_ASSERT(IsStreamActiveLocked());
+
+ // The client is allowed to essentially forget what the format is on any
+ // mid-stream buffer config change, so remember to re-send the format to the
+ // client before the next output packet of this stream.
+ stream_->SetOutputFormatPending();
+
+ // For asserts.
+ stream_->SetMidStreamOutputConstraintsChangeActive();
+
// This part is not speculative. OMX has indicated that it's at least
// meh about the current output config, so ensure we do a required
- // OnOutputConfig() before the next stream starts, even if the client
+ // OnOutputConstraints() before the next stream starts, even if the client
// moves on to a new stream such that the speculative part below becomes
// stale.
core_codec_meh_output_buffer_constraints_version_ordinal_ =
@@ -2356,10 +3210,23 @@
} // ~lock
PostToStreamControl(
[this, stream_lifetime_ordinal = local_stream_lifetime_ordinal] {
- MidStreamOutputConfigChange(stream_lifetime_ordinal);
+ MidStreamOutputConstraintsChange(stream_lifetime_ordinal);
});
}
+void CodecImpl::onCoreCodecOutputFormatChange() {
+ ZX_DEBUG_ASSERT(IsPotentiallyCoreCodecThread());
+ std::lock_guard<std::mutex> lock(lock_);
+ ZX_DEBUG_ASSERT(IsStreamActiveLocked());
+ // In future we could relax this requirement, but for now we don't allow
+ // output format changes, output packets, or EOS while mid-stream constraints
+ // change is active.
+ ZX_DEBUG_ASSERT(!stream_->is_mid_stream_output_constraints_change_active());
+ // Next time the core codec asks to output a packet, we'll send the format
+ // first.
+ stream_->SetOutputFormatPending();
+}
+
void CodecImpl::onCoreCodecInputPacketDone(CodecPacket* packet) {
// Free/busy coherency from Codec interface to OMX doesn't involve trusting
// the client, so assert we're doing it right server-side.
@@ -2389,6 +3256,56 @@
void CodecImpl::onCoreCodecOutputPacket(CodecPacket* packet,
bool error_detected_before,
bool error_detected_during) {
+ ZX_DEBUG_ASSERT(IsPotentiallyCoreCodecThread());
+
+ { // scope lock
+ std::unique_lock<std::mutex> lock(lock_);
+
+ // The core codec shouldn't output a packet until after
+ // CoreCodecStartStream() and input data availability in the case that
+ // output buffer config was already suitable, or until after
+ // CoreCodecMidStreamOutputBufferReConfigFinish() in the case that output
+ // buffer config wasn't suitable (not configured or not suitable) or
+ // changed mid-stream. See also comments in codec_adapter.h.
+ ZX_DEBUG_ASSERT(IsOutputConfiguredLocked());
+
+ // Before we send the packet, we check whether the stream has output format
+ // pending, which means we need to send the output format before the output
+ // packet (and clear the pending state).
+ ZX_DEBUG_ASSERT(IsStreamActiveLocked());
+
+ ZX_DEBUG_ASSERT(!stream_->is_mid_stream_output_constraints_change_active());
+
+ if (stream_->output_format_pending()) {
+ stream_->ClearOutputFormatPending();
+ uint64_t stream_lifetime_ordinal = stream_lifetime_ordinal_;
+ uint64_t new_output_format_details_version_ordinal =
+ next_output_format_details_version_ordinal_++;
+ fuchsia::media::StreamOutputFormat output_format;
+ { // scope unlock
+ ScopedUnlock unlock(lock);
+ output_format = CoreCodecGetOutputFormat(
+ stream_lifetime_ordinal,
+ new_output_format_details_version_ordinal);
+ } // ~unlock
+ // Stream change while unlocked above won't happen because we're on
+ // InputData domain which is fenced as part of stream switch.
+ ZX_DEBUG_ASSERT(stream_lifetime_ordinal == stream_lifetime_ordinal_);
+ ZX_DEBUG_ASSERT(new_output_format_details_version_ordinal ==
+ next_output_format_details_version_ordinal_ - 1);
+ ZX_DEBUG_ASSERT(sent_format_details_version_ordinal_[kOutputPort] + 1 ==
+ new_output_format_details_version_ordinal);
+ sent_format_details_version_ordinal_[kOutputPort] =
+ new_output_format_details_version_ordinal;
+ PostToSharedFidl([this, output_format = std::move(output_format)]() mutable {
+ // See "is_bound_checks" comment up top.
+ if (binding_.is_bound()) {
+ binding_.events().OnOutputFormat(std::move(output_format));
+ }
+ });
+ }
+ }
+
{ // scope lock
std::unique_lock<std::mutex> lock(lock_);
// This helps verify that packet lifetimes are coherent, but we can't do the
@@ -2434,11 +3351,13 @@
}
void CodecImpl::onCoreCodecOutputEndOfStream(bool error_detected_before) {
+ VLOGF("CodecImpl::onCoreCodecOutputEndOfStream()\n");
{ // scope lock
std::unique_lock<std::mutex> lock(lock_);
+ ZX_DEBUG_ASSERT(IsStreamActiveLocked());
+ ZX_DEBUG_ASSERT(!stream_->is_mid_stream_output_constraints_change_active());
stream_->SetOutputEndOfStream();
output_end_of_stream_seen_.notify_all();
- VLOGF("sending OnOutputEndOfStream()\n");
PostSerial(shared_fidl_dispatcher_,
[this, stream_lifetime_ordinal = stream_lifetime_ordinal_,
error_detected_before] {
@@ -2520,6 +3439,190 @@
bool CodecImpl::Stream::failure_seen() { return failure_seen_; }
+void CodecImpl::Stream::SetOutputFormatPending() {
+ output_format_pending_ = true;
+}
+
+void CodecImpl::Stream::ClearOutputFormatPending() {
+ output_format_pending_ = false;
+}
+
+bool CodecImpl::Stream::output_format_pending() {
+ return output_format_pending_;
+}
+
+void CodecImpl::Stream::SetMidStreamOutputConstraintsChangeActive() {
+ ZX_DEBUG_ASSERT(!is_mid_stream_output_constraints_change_active_);
+ is_mid_stream_output_constraints_change_active_ = true;
+}
+
+void CodecImpl::Stream::ClearMidStreamOutputConstraintsChangeActive() {
+ ZX_DEBUG_ASSERT(is_mid_stream_output_constraints_change_active_);
+ is_mid_stream_output_constraints_change_active_ = false;
+}
+
+bool CodecImpl::Stream::is_mid_stream_output_constraints_change_active() {
+ return is_mid_stream_output_constraints_change_active_;
+}
+
+CodecImpl::PortSettings::PortSettings(CodecImpl* parent, CodecPort port, fuchsia::media::StreamBufferSettings settings)
+ : parent_(parent), port_(port), settings_(std::make_unique<fuchsia::media::StreamBufferSettings>(std::move(settings))) {
+ // nothing else to do here
+}
+
+CodecImpl::PortSettings::PortSettings(CodecImpl* parent, CodecPort port,
+ fuchsia::media::StreamBufferPartialSettings partial_settings)
+ : parent_(parent), port_(port), partial_settings_(
+ std::make_unique<fuchsia::media::StreamBufferPartialSettings>(
+ std::move(partial_settings))) {
+ // nothing else to do here
+}
+
+CodecImpl::PortSettings::~PortSettings() {
+ // To be safe, the unbind needs to occur on the FIDL thread. In addition, we
+ // want to send a clean Close() to avoid causing the LogicalBufferCollection
+ // to fail. Since we're not a crashing process, this is a clean close by
+ // definition.
+ if (buffer_collection_ && thrd_current() != parent_->fidl_thread()) {
+ parent_->PostToSharedFidl([buffer_collection = std::move(buffer_collection_)]{
+ // Sysmem will notice the Close() before the PEER_CLOSED.
+ buffer_collection->Close();
+ // ~buffer_collection on FIDL thread
+ });
+ }
+}
+
+void CodecImpl::PortSettings::SetBufferCollectionInfo(fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info) {
+ ZX_DEBUG_ASSERT(!buffer_collection_info_);
+ buffer_collection_info_ = std::make_unique<fuchsia::sysmem::BufferCollectionInfo_2>(std::move(buffer_collection_info));
+}
+
+const fuchsia::sysmem::BufferCollectionInfo_2& CodecImpl::PortSettings::buffer_collection_info() {
+ ZX_DEBUG_ASSERT(buffer_collection_info_);
+ return *buffer_collection_info_;
+}
+
+uint64_t CodecImpl::PortSettings::buffer_lifetime_ordinal() {
+ if (is_partial_settings()) {
+ return partial_settings_->buffer_lifetime_ordinal();
+ } else {
+ return settings_->buffer_lifetime_ordinal();
+ }
+}
+
+uint64_t CodecImpl::PortSettings::buffer_constraints_version_ordinal() {
+ if (is_partial_settings()) {
+ return partial_settings_->buffer_constraints_version_ordinal();
+ } else {
+ return settings_->buffer_constraints_version_ordinal();
+ }
+}
+
+uint32_t CodecImpl::PortSettings::packet_count() {
+ if (is_partial_settings()) {
+ // Asking before we have buffer_collection_info_ would potentially get the
+ // wrong answer.
+ ZX_DEBUG_ASSERT(buffer_collection_info_);
+ return std::max(
+ partial_settings_->packet_count_for_server() +
+ partial_settings_->packet_count_for_client(),
+ buffer_collection_info_->buffer_count);
+ } else {
+ return settings_->packet_count_for_server() + settings_->packet_count_for_client();
+ }
+}
+
+uint32_t CodecImpl::PortSettings::buffer_count() {
+ if (is_partial_settings()) {
+ ZX_DEBUG_ASSERT(buffer_collection_info_);
+ return buffer_collection_info_->buffer_count;
+ } else {
+ if (settings_->single_buffer_mode()) {
+ return 1;
+ }
+ return packet_count();
+ }
+}
+
+bool CodecImpl::PortSettings::is_partial_settings() {
+ // Exactly one of settings_ or partial_settings_ is set.
+ ZX_DEBUG_ASSERT(!!settings_ ^ !!partial_settings_);
+ return !!partial_settings_;
+}
+
+const fuchsia::media::StreamBufferPartialSettings& CodecImpl::PortSettings::partial_settings() {
+ ZX_DEBUG_ASSERT(is_partial_settings());
+ return *partial_settings_;
+}
+
+const fuchsia::media::StreamBufferSettings& CodecImpl::PortSettings::settings() {
+ ZX_DEBUG_ASSERT(!is_partial_settings());
+ return *settings_;
+}
+
+fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> CodecImpl::PortSettings::TakeToken() {
+ ZX_DEBUG_ASSERT(is_partial_settings());
+ ZX_DEBUG_ASSERT(partial_settings_->has_sysmem_token());
+ fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> token =
+ std::move(*partial_settings_->mutable_sysmem_token());
+ partial_settings_->clear_sysmem_token();
+ return token;
+}
+
+zx::vmo CodecImpl::PortSettings::TakeVmo(uint32_t buffer_index) {
+ ZX_DEBUG_ASSERT(is_partial_settings());
+ ZX_DEBUG_ASSERT(buffer_collection_info_);
+ ZX_DEBUG_ASSERT(buffer_index < buffer_collection_info_->buffer_count);
+ return std::move(buffer_collection_info_->buffers[buffer_index].vmo);
+}
+
+fidl::InterfaceRequest<fuchsia::sysmem::BufferCollection> CodecImpl::PortSettings::NewBufferCollectionRequest(async_dispatcher_t* dispatcher) {
+ ZX_DEBUG_ASSERT(thrd_current() == parent_->fidl_thread());
+ ZX_DEBUG_ASSERT(is_partial_settings());
+ ZX_DEBUG_ASSERT(!buffer_collection_);
+ return buffer_collection_.NewRequest(dispatcher);
+}
+
+fuchsia::sysmem::BufferCollectionPtr& CodecImpl::PortSettings::buffer_collection() {
+ ZX_DEBUG_ASSERT(thrd_current() == parent_->fidl_thread());
+ ZX_DEBUG_ASSERT(is_partial_settings());
+ return buffer_collection_;
+}
+
+void CodecImpl::PortSettings::UnbindBufferCollection() {
+ ZX_DEBUG_ASSERT(thrd_current() == parent_->fidl_thread());
+ ZX_DEBUG_ASSERT(is_partial_settings());
+ // return value intentionally ignored and deleted
+ buffer_collection_.Unbind();
+}
+
+bool CodecImpl::PortSettings::is_complete_seen_output() {
+ ZX_DEBUG_ASSERT(port_ == kOutputPort);
+ ZX_DEBUG_ASSERT(is_partial_settings());
+ return is_complete_seen_output_;
+}
+
+void CodecImpl::PortSettings::SetCompleteSeenOutput() {
+ ZX_DEBUG_ASSERT(port_ == kOutputPort);
+ ZX_DEBUG_ASSERT(thrd_current() == parent_->fidl_thread());
+ ZX_DEBUG_ASSERT(is_partial_settings());
+ ZX_DEBUG_ASSERT(!is_complete_seen_output_);
+ is_complete_seen_output_ = true;
+}
+
+uint64_t CodecImpl::PortSettings::vmo_usable_start(uint32_t buffer_index) {
+ ZX_DEBUG_ASSERT(is_partial_settings());
+ ZX_DEBUG_ASSERT(buffer_collection_info_);
+ ZX_DEBUG_ASSERT(buffer_index < buffer_collection_info_->buffer_count);
+ return buffer_collection_info_->buffers[buffer_index].vmo_usable_start;
+}
+
+uint64_t CodecImpl::PortSettings::vmo_usable_size() {
+ ZX_DEBUG_ASSERT(is_partial_settings());
+ ZX_DEBUG_ASSERT(buffer_collection_info_);
+ return buffer_collection_info_->settings.buffer_settings.size_bytes;
+}
+
//
// CoreCodec wrappers, for the asserts. These asserts, and the way we ensure
// at compile time that this class has a method for every method of
@@ -2535,6 +3638,31 @@
codec_adapter_->CoreCodecInit(initial_input_format_details);
}
+fuchsia::sysmem::BufferCollectionConstraints
+CodecImpl::CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) {
+ ZX_DEBUG_ASSERT(port == kInputPort &&
+ thrd_current() == stream_control_thread_ ||
+ port == kOutputPort && thrd_current() == fidl_thread());
+ // We don't intend to send the sysmem token to the core codec directly, just
+ // because it doesn't really need to participate directly that way, and this
+ // lets us keep direct interaction with sysmem in CodecImpl instead of each
+ // core codec.
+ ZX_DEBUG_ASSERT(!partial_settings.has_sysmem_token());
+ return codec_adapter_->CoreCodecGetBufferCollectionConstraints(port, stream_buffer_constraints, partial_settings);
+}
+
+void CodecImpl::CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) {
+ ZX_DEBUG_ASSERT(port == kInputPort &&
+ thrd_current() == stream_control_thread_ ||
+ port == kOutputPort && thrd_current() == fidl_thread());
+ codec_adapter_->CoreCodecSetBufferCollectionInfo(port, buffer_collection_info);
+}
+
void CodecImpl::CoreCodecAddBuffer(CodecPort port, const CodecBuffer* buffer) {
ZX_DEBUG_ASSERT(port == kInputPort &&
thrd_current() == stream_control_thread_ ||
@@ -2591,22 +3719,57 @@
return codec_adapter_->IsCoreCodecRequiringOutputConfigForFormatDetection();
}
+bool CodecImpl::IsCoreCodecMappedBufferNeeded(CodecPort port) {
+ ZX_DEBUG_ASSERT(port == kInputPort && thrd_current() == stream_control_thread_ ||
+ port == kOutputPort && thrd_current() == fidl_thread());
+ return codec_adapter_->IsCoreCodecMappedBufferNeeded(port);
+}
+
+bool CodecImpl::IsCoreCodecHwBased() {
+ return codec_adapter_->IsCoreCodecHwBased();
+}
+
// Caller must ensure that this is called only on one thread at a time, only
// during setup, during a core codec initiated mid-stream format change, or
// during stream start before any input data has been delivered for the new
// stream.
-std::unique_ptr<const fuchsia::media::StreamOutputConfig>
-CodecImpl::CoreCodecBuildNewOutputConfig(
+std::unique_ptr<const fuchsia::media::StreamOutputConstraints>
+CodecImpl::CoreCodecBuildNewOutputConstraints(
uint64_t stream_lifetime_ordinal,
uint64_t new_output_buffer_constraints_version_ordinal,
- uint64_t new_output_format_details_version_ordinal,
bool buffer_constraints_action_required) {
ZX_DEBUG_ASSERT(IsPotentiallyCoreCodecThread() ||
thrd_current() == stream_control_thread_);
- return codec_adapter_->CoreCodecBuildNewOutputConfig(
- stream_lifetime_ordinal, new_output_buffer_constraints_version_ordinal,
- new_output_format_details_version_ordinal,
- buffer_constraints_action_required);
+ std::unique_ptr<const fuchsia::media::StreamOutputConstraints> constraints =
+ codec_adapter_->CoreCodecBuildNewOutputConstraints(
+ stream_lifetime_ordinal,
+ new_output_buffer_constraints_version_ordinal,
+ buffer_constraints_action_required);
+ ZX_DEBUG_ASSERT(constraints);
+ ZX_DEBUG_ASSERT(constraints->has_stream_lifetime_ordinal());
+ ZX_DEBUG_ASSERT(constraints->stream_lifetime_ordinal() == stream_lifetime_ordinal);
+ ZX_DEBUG_ASSERT(constraints->has_buffer_constraints());
+ ZX_DEBUG_ASSERT(constraints->buffer_constraints().has_buffer_constraints_version_ordinal());
+ ZX_DEBUG_ASSERT(constraints->buffer_constraints().buffer_constraints_version_ordinal() == new_output_buffer_constraints_version_ordinal);
+ ZX_DEBUG_ASSERT(constraints->has_buffer_constraints_action_required());
+ ZX_DEBUG_ASSERT(constraints->buffer_constraints_action_required() == buffer_constraints_action_required);
+ return constraints;
+}
+
+fuchsia::media::StreamOutputFormat CodecImpl::CoreCodecGetOutputFormat(
+ uint64_t stream_lifetime_ordinal,
+ uint64_t new_output_format_details_version_ordinal) {
+ // TODO(dustingreen): Remove the 2nd part of this assert condition along with
+ // removal of SetOutputBufferSettings() and OnOutputConfig() messages.
+ ZX_DEBUG_ASSERT(IsPotentiallyCoreCodecThread() || thrd_current() == stream_control_thread_);
+ fuchsia::media::StreamOutputFormat format =
+ codec_adapter_->CoreCodecGetOutputFormat(stream_lifetime_ordinal, new_output_format_details_version_ordinal);
+ ZX_DEBUG_ASSERT(format.has_stream_lifetime_ordinal());
+ ZX_DEBUG_ASSERT(format.stream_lifetime_ordinal() == stream_lifetime_ordinal);
+ ZX_DEBUG_ASSERT(format.has_format_details());
+ ZX_DEBUG_ASSERT(format.format_details().has_format_details_version_ordinal());
+ ZX_DEBUG_ASSERT(format.format_details().format_details_version_ordinal() == new_output_format_details_version_ordinal);
+ return format;
}
void CodecImpl::CoreCodecMidStreamOutputBufferReConfigPrepare() {
diff --git a/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_adapter.h b/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_adapter.h
index 5230ab4..37d591e 100644
--- a/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_adapter.h
+++ b/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_adapter.h
@@ -59,6 +59,15 @@
// or may require an output config (false).
virtual bool IsCoreCodecRequiringOutputConfigForFormatDetection() = 0;
+ // If true, the codec requires that the buffer VMOs be mappable for direct
+ // access by the CPU.
+ virtual bool IsCoreCodecMappedBufferNeeded(CodecPort port) = 0;
+
+ // If true, the codec is HW-based, in the sense that at least some of the
+ // processing is performed by specialized processing HW running separately
+ // from any CPU execution context.
+ virtual bool IsCoreCodecHwBased() = 0;
+
// The initial input format details and later input format details will
// _often_ remain the same overall format, and only differ in ways that are
// reasonable on a format-specific basis. However, not always. A core codec
@@ -88,6 +97,78 @@
virtual void CoreCodecInit(
const fuchsia::media::FormatDetails& initial_input_format_details) = 0;
+ // All codecs must implement this for both ports. The returned structure will
+ // be sent to sysmem in a SetConstraints() call. This method can be called
+ // on the FIDL thread or the StreamControl domain (thread for now).
+ //
+ // Input:
+ //
+ // For now, a core codec has no way to trigger being asked for new input
+ // constraints, so the input constraints (for now) need to be generally
+ // applicable to any potential setting/property of the input.
+ //
+ // A decoder should permit a fairly wide range of buffer space, without
+ // worrying whether the min is enough to efficiently handle a high bitrate.
+ // The CodecImpl will own bumping up the min based on approximate bitrate
+ // provided in the initial decoder creation parameters and/or per-stream input
+ // format details (this logic is shared because it can reasonably be shared).
+ // A core codec that has special requirements for extra input buffer space
+ // given a particular bitrate can take it upon itself to set the input min
+ // buffer space, but the idea is that typically it won't be necessary for a
+ // decoder to increase the min based on input bitrate beyond what CodecImpl
+ // does, so needing to do this should be fairly rare.
+ //
+ // A video encoder which needs to vary it's input BufferCollectionConstraints
+ // based on encoder settings can do so, using the data provided to
+ // CoreCodecInit(). If later use of QueueInputFormatDetails() (per-stream)
+ // results in an input packet that conforms to the old
+ // BufferCollectionConstraints but does not conform to the effective new
+ // BufferCollectionConstraints, the core codec can use
+ // CodecAdapterEvents::onCoreCodecFailCodec() (the core codec should fail the
+ // codec instance in this case rather than attempt to handle input data that
+ // is outside the bounds that would have been indicated by the core codec had
+ // the current input format details been used as the initial format details).
+ // Clients that change the input format details on the fly should be willing
+ // to re-request a new codec instance at least once starting with the new
+ // input format details via the CodecFactory. This is true for additional
+ // reasons beyond this paragraph involving the possibility of accelerated but
+ // partial codec implementations. If a client needs to change input format
+ // details but doesn't want to concern itself with tracking whether the
+ // current codec was created with the current input format details, a client
+ // can instead choose to always create a new codec via CodecFactory on any
+ // change to the input format details.
+ //
+ // Output:
+ //
+ // A core codec can trigger this method to get called again by indicating an
+ // output format detection/change with action_required true via
+ // CoreCodecEvents::onCoreCodecMidStreamOutputConstraintsChange().
+ //
+ // Filling out the usage bits is optional. If the usage bits are not filled
+ // out (all still 0), the caller will fill them out based on
+ // IsCoreCodecMappedBufferNeeded() and IsCoreCodecHwBased(). The core codec
+ // must either leave usage set to all 0, or completely fill them out.
+ virtual fuchsia::sysmem::BufferCollectionConstraints
+ CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) = 0;
+
+ // There are no VMO handles in the buffer_collection_info. Those are instead
+ // provided via calls to CoreCodecAddBuffer(), as CodecImpl handles allocation
+ // of CodecBuffer instances (each of which has a VMO).
+ //
+ // This method allows a core codec to know things like buffer_count, whether
+ // sysmem selected CPU domain or RAM domain for sharing of buffers, whether
+ // protected buffers were allocated, etc.
+ //
+ // This call occurs regardless of whether "settings" or "partial settings" are
+ // set (regardless of whether the client is using sysmem), after the client
+ // sets input or output settings, and before the first buffer is added.
+ virtual void CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) = 0;
+
// Stream lifetime:
//
// The CoreCodecStartStream() and CoreCodecStopStream() calls bracket the
@@ -148,6 +229,12 @@
// Add input or output buffer.
//
+ // The buffers added via this method correspond to the buffers of the buffer
+ // collection - these are buffers of the collection most recently indicated via
+ // a call to CoreCodecSetBufferCollectionInfo(). While the VMOs are
+ // intentionally not included in that call, the VMOs are indicated here (this
+ // lets the CodecImpl own allocation of CodecBuffer instances).
+ //
// A core codec may be able to fully configure a buffer during this call and
// later ignore CoreCodecConfigureBuffers(), or a core codec may use
// CoreCodecConfigureBuffers() to finish configuring buffers.
@@ -183,7 +270,7 @@
// be called while there's no active stream, or after a stream is started but
// before any input data is queued, or during processing shortly after the
// core codec calling
- // onCoreCodecMidStreamOutputConfigChange(true), after
+ // onCoreCodecMidStreamOutputConstraintsChange(true), after
// CoreCodecMidStreamOutputBufferReConfigPrepare() and before
// CoreCodecMidStreamOutputBufferReConfigFinish().
//
@@ -207,22 +294,34 @@
// output_re_config_required false:
//
// This is called on the same thread and same stack as
- // onCoreCodecMidStreamOutputConfigChange() (and with same stream still
+ // onCoreCodecMidStreamOutputConstraintsChange() (and with same stream still
// active).
- virtual std::unique_ptr<const fuchsia::media::StreamOutputConfig>
- CoreCodecBuildNewOutputConfig(
+ virtual std::unique_ptr<const fuchsia::media::StreamOutputConstraints>
+ CoreCodecBuildNewOutputConstraints(
uint64_t stream_lifetime_ordinal,
uint64_t new_output_buffer_constraints_version_ordinal,
- uint64_t new_output_format_details_version_ordinal,
bool buffer_constraints_action_required) = 0;
+ // This will be called on the InputData domain, during the core codec's call
+ // to onCoreCodecOutputPacket(), so that the format will be delivered at most
+ // once before any packet which needs a new format to be indicated. The core
+ // codec can trigger this to occur during the next onCoreCodecOutputPacket()
+ // by calling onCoreCodecOutputFormatChange(). The tracking of pending
+ // output format is per-stream, and all streams start with a pending output
+ // format, so a core codec need not call onCoreCodecOutputFormatChange()
+ // unless the format change is mid-stream (but calling before the first packet
+ // is allowed and not harmful).
+ virtual fuchsia::media::StreamOutputFormat CoreCodecGetOutputFormat(
+ uint64_t stream_lifetime_ordinal,
+ uint64_t new_output_format_details_version_ordinal) = 0;
+
// CoreCodecMidStreamOutputBufferReConfigPrepare()
//
// For a mid-stream format change where output buffer re-configuration is
// needed (as initiated async by the core codec calling
- // CodecAdapterEvents::onCoreCodecMidStreamOutputConfigChange(true)), this
+ // CodecAdapterEvents::onCoreCodecMidStreamOutputConstraintsChange(true)), this
// method is called on the StreamControl thread before the client is notified
- // of the need for output buffer re-config (via OnOutputConfig() with
+ // of the need for output buffer re-config (via OnOutputConstraints() with
// buffer_constraints_action_required true).
//
// The core codec should do whatever is necessary to ensure that output
@@ -261,6 +360,16 @@
//
// The core codec should do whatever is necessary to get back into normal
// steady-state operation in this method.
+ //
+ // The core codec must not onCoreCodecOutputPacket() or
+ // onCoreCodecOutputEndOfStream() until this method has been called, or until
+ // CoreCodecStartStream() is called and some input is available, should the
+ // current stream be stopped before completing mid-stream output buffer
+ // re-config. This works partly because the CodecImpl guarantees that if a
+ // mid-stream re-config didn't finish, there will be a complete output
+ // re-config before the CoreCodecStartStream() - in other words this re-config
+ // is abandoned and a new one takes its place and is fully complete prior to
+ // the new stream starting.
virtual void CoreCodecMidStreamOutputBufferReConfigFinish() = 0;
protected:
diff --git a/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_adapter_events.h b/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_adapter_events.h
index b826256..e45f732 100644
--- a/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_adapter_events.h
+++ b/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_adapter_events.h
@@ -32,13 +32,23 @@
// call is propertly ordered with respect to onCoreCodecOutputPacket() and
// onCoreCodecOutputEndOfStream() calls.
//
- // A call to onCoreCodecMidStreamOutputConfigChange(true) must not be
+ // A call to onCoreCodecMidStreamOutputConstraintsChange(true) must not be
// followed by any more output (including EndOfStream) until the associated
// output re-config is completed by a call to
// CoreCodecMidStreamOutputBufferReConfigFinish().
- virtual void onCoreCodecMidStreamOutputConfigChange(
+ virtual void onCoreCodecMidStreamOutputConstraintsChange(
bool output_re_config_required) = 0;
+ // When the core codec calls this method, the CodecImpl will note that the
+ // format has changed, and on next onCoreCodecOutputPacket(), the CodecImpl
+ // will ask the core codec for the format and generate and send an
+ // OnOutputformat() message before that output packet. This way, the core
+ // codec is free to call onCoreCodecOutputFormat() repeatedly without any
+ // packet in between, with CodecImpl collapsing these into one
+ // OnOutputFormat() to avoid the extra message (so it doesn't have to be sent
+ // and doesn't have to be handled by clients).
+ virtual void onCoreCodecOutputFormatChange() = 0;
+
virtual void onCoreCodecInputPacketDone(CodecPacket* packet) = 0;
virtual void onCoreCodecOutputPacket(CodecPacket* packet,
diff --git a/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_impl.h b/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_impl.h
index 457ea2f..ad55ff9 100644
--- a/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_impl.h
+++ b/garnet/lib/media/codec_impl/include/lib/media/codec_impl/codec_impl.h
@@ -19,6 +19,7 @@
#include <zircon/compiler.h>
#include <list>
+#include <queue>
// The CodecImpl class can be used for both SW and HW codecs.
//
@@ -62,12 +63,21 @@
public CodecAdapterEvents,
private CodecAdapter {
public:
+ // The CodecImpl will take care of doing set_error_handler() on the sysmem
+ // connection. The sysmem connection should be set up to use the
+ // shared_fidl_dispatcher.
CodecImpl(
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
std::unique_ptr<CodecAdmission> codec_admission,
async_dispatcher_t* shared_fidl_dispatcher, thrd_t shared_fidl_thread,
std::unique_ptr<fuchsia::mediacodec::CreateDecoder_Params> decoder_params,
fidl::InterfaceRequest<fuchsia::media::StreamProcessor> codec_request);
+
+ // The CodecImpl will take care of doing set_error_handler() on the sysmem
+ // connection. The sysmem connection should be set up to use the
+ // shared_fidl_dispatcher.
CodecImpl(
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
std::unique_ptr<CodecAdmission> codec_admission,
async_dispatcher_t* shared_fidl_dispatcher, thrd_t shared_fidl_thread,
std::unique_ptr<fuchsia::mediacodec::CreateEncoder_Params> encoder_params,
@@ -126,9 +136,15 @@
void SetInputBufferSettings(
fuchsia::media::StreamBufferSettings input_settings) override;
void AddInputBuffer(fuchsia::media::StreamBuffer buffer) override;
+ void SetInputBufferPartialSettings(
+ fuchsia::media::StreamBufferPartialSettings input_settings) override;
void SetOutputBufferSettings(
fuchsia::media::StreamBufferSettings output_settings) override;
void AddOutputBuffer(fuchsia::media::StreamBuffer buffer) override;
+ void SetOutputBufferPartialSettings(
+ fuchsia::media::StreamBufferPartialSettings output_settings) override;
+ void CompleteOutputBufferPartialSettings(
+ uint64_t buffer_lifetime_ordinal) override;
void FlushEndOfStreamAndCloseStream(
uint64_t stream_lifetime_ordinal) override;
void CloseCurrentStream(uint64_t stream_lifetime_ordinal,
@@ -145,6 +161,7 @@
private:
CodecImpl(
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem,
std::unique_ptr<CodecAdmission> codec_admission,
async_dispatcher_t* shared_fidl_dispatcher, thrd_t shared_fidl_thread,
std::unique_ptr<fuchsia::mediacodec::CreateDecoder_Params> decoder_params,
@@ -160,7 +177,7 @@
// We keep a queue of Stream objects rather than just a single current stream
// object, so we can track which streams are future-discarded and which are
// not yet known to be future-discarded. This difference matters because
- // clients are not required to process OnOutputConfig() with
+ // clients are not required to process OnOutputConstraints() with
// stream_lifetime_ordinal of a stream that the client has since told the
// server to discard, so we don't want StreamControl ordering domain getting
// stuck waiting on a client to catch up to an output config that the client
@@ -224,6 +241,16 @@
void SetFailureSeen();
__WARN_UNUSED_RESULT bool failure_seen();
+ // These methods are called on the core codec processing domain. See also
+ // comments on output_format_pending_.
+ void SetOutputFormatPending();
+ void ClearOutputFormatPending();
+ __WARN_UNUSED_RESULT bool output_format_pending();
+
+ void SetMidStreamOutputConstraintsChangeActive();
+ void ClearMidStreamOutputConstraintsChangeActive();
+ __WARN_UNUSED_RESULT bool is_mid_stream_output_constraints_change_active();
+
private:
const uint64_t stream_lifetime_ordinal_ = 0;
bool future_discarded_ = false;
@@ -238,6 +265,107 @@
bool input_end_of_stream_ = false;
bool output_end_of_stream_ = false;
bool failure_seen_ = false;
+
+ // This defaults to _true_, so that we send OnOutputFormat() before the
+ // first OnOutputFormat() of a stream. We also set this back to true any
+ // time the core codec indicates onOutputFormat(), and any time the core
+ // codec indicates onCoreCodecMidStreamOutputConstraintsChange() with action
+ // required true.
+ bool output_format_pending_ = true;
+
+ // It's not permitted for the core codec to emit output while a mid-stream
+ // output constraints change is active.
+ bool is_mid_stream_output_constraints_change_active_ = false;
+ };
+
+ // PortSettings
+ //
+ // The PortSettings wraps/homogenizes the port settings regardless of whether
+ // the settings are specified by the client using StreamBufferSettings or
+ // StreamBufferPartialSettings. In addition, in the case of
+ // StreamBufferPartialSettings, this class tracks the settings that arrive
+ // later from sysmem (whether we've received them yet, and if so, what the
+ // values are).
+ class PortSettings {
+ public:
+ PortSettings(CodecImpl* parent, CodecPort port, fuchsia::media::StreamBufferSettings settings);
+ PortSettings(CodecImpl* parent, CodecPort port,
+ fuchsia::media::StreamBufferPartialSettings partial_settings);
+ ~PortSettings();
+
+ uint64_t buffer_lifetime_ordinal();
+
+ uint64_t buffer_constraints_version_ordinal();
+
+ uint32_t packet_count();
+ uint32_t buffer_count();
+
+ // If is_partial_settings(), the PortSettings are initially partial, with
+ // sysmem used to complete the settings. Along the way the PortSettings
+ // transiently also have the zx::vmo handles. In contrast, if
+ // !is_partial_settings(), the settings are complete from the start (aside
+ // from vmo handles which are never owned by PortSettings in this case).
+ bool is_partial_settings();
+
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings();
+
+ const fuchsia::media::StreamBufferSettings& settings();
+
+ fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> TakeToken();
+
+ // The caller should std::move() in the buffer_collection_info. This call
+ // is only valid if this instance was created from
+ // StreamBufferPartialSettings, and this method hasn't been called before
+ // on this instance.
+ void SetBufferCollectionInfo(
+ fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info);
+
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info();
+
+ // We use SetBufferCollectionInfo(), but then take the VMOs back. This
+ // just happens to be more convenient than taking the VMOs before doing
+ // SetBufferCollectionInfo().
+ zx::vmo TakeVmo(uint32_t buffer_index);
+
+ uint64_t vmo_usable_start(uint32_t buffer_index);
+ uint64_t vmo_usable_size();
+
+ // Only call from FIDL thread.
+ fidl::InterfaceRequest<fuchsia::sysmem::BufferCollection> NewBufferCollectionRequest(async_dispatcher_t* dispatcher);
+
+ // Only call from FIDL thread.
+ fuchsia::sysmem::BufferCollectionPtr& buffer_collection();
+
+ // Only call from FIDL thread.
+ void UnbindBufferCollection();
+
+ // This condition is necessary (but not sufficient) for
+ // IsOutputConfiguredLocked() to return true.
+ bool is_complete_seen_output();
+ void SetCompleteSeenOutput();
+
+ private:
+ CodecImpl* parent_ = nullptr;
+
+ CodecPort port_ = kInvalidPort;
+
+ // Only one or the other of settings_ or partial_settings_ is set.
+ std::unique_ptr<fuchsia::media::StreamBufferSettings> settings_;
+
+ // Only needed/set for the partial_settings_ case.
+ std::unique_ptr<const fuchsia::media::StreamBufferConstraints>
+ constraints_;
+ std::unique_ptr<fuchsia::media::StreamBufferPartialSettings> partial_settings_;
+
+ fuchsia::sysmem::BufferCollectionPtr buffer_collection_;
+
+ // In the case of partial_settings_, the remainder of the settings arrive
+ // from sysmem in a BufferCollectionInfo_2. When that arrives from
+ // sysmem, we move the VMOs into CodecBuffer(s), and the remainder of the
+ // settings get stored here.
+ std::unique_ptr<fuchsia::sysmem::BufferCollectionInfo_2> buffer_collection_info_;
+
+ bool is_complete_seen_output_ = false;
};
// While we list this first in the member variables to hint that this gets
@@ -252,19 +380,36 @@
// previous Codec channel, when there's a concurrency cap of 1 (for example).
std::unique_ptr<CodecAdmission> codec_admission_;
+ fuchsia::sysmem::AllocatorPtr sysmem_;
+
async_dispatcher_t* shared_fidl_dispatcher_;
thrd_t shared_fidl_thread_;
// Parts of CodecImpl are accessed from shared_fidl_thread(),
// stream_control_thread_, and decoder thread(s) such as interrupt handling
// thread(s).
+ //
+ // FXL_GUARDED_BY() is not directly usable in this class because this class
+ // takes advantage of for example being able to read outside the lock from
+ // something that can only be modified on the current thread. Also, which
+ // thread is relevant can vary by port, while FXL_GUARDED_BY() doesn't have
+ // any way to tag indexes of an array differently.
+ //
+ // TODO(dustingreen): Implement some lock-like contexts including reader vs.
+ // writer aspects so we can use FXL_GUARDED_BY() (just not with the lock
+ // directly).
+ //
+ // TODO(dustingreen): Switch to fbl::Mutex and fbl::ConditionVariable, because
+ // they complain instead of blocking if repeated acquisition is attempted, and
+ // because one can check whether the current thread holds the lock (for assert
+ // purposes).
std::mutex lock_;
//
// Setup/teardown aspects.
//
- // Will send an initial Codec.OnOutputConfig() if the codec can't tolerate
+ // Will send an initial Codec.OnOutputConstraints() if the codec can't tolerate
// null output config during format detection.
void onInputConstraintsReady();
@@ -332,6 +477,10 @@
// Held here temporarily until DeviceFidl is ready to handle errors so we can
// bind.
+ fidl::InterfaceHandle<fuchsia::sysmem::Allocator> tmp_sysmem_;
+
+ // Held here temporarily until DeviceFidl is ready to handle errors so we can
+ // bind.
fidl::InterfaceRequest<fuchsia::media::StreamProcessor>
tmp_interface_request_;
@@ -368,7 +517,10 @@
// StreamControl thread.
void SetInputBufferSettings_StreamControl(
fuchsia::media::StreamBufferSettings input_settings);
- void AddInputBuffer_StreamControl(fuchsia::media::StreamBuffer buffer);
+ void AddInputBuffer_StreamControl(
+ bool is_client, fuchsia::media::StreamBuffer buffer);
+ void SetInputBufferPartialSettings_StreamControl(
+ fuchsia::media::StreamBufferPartialSettings input_partial_settings);
void FlushEndOfStreamAndCloseStream_StreamControl(
uint64_t stream_lifetime_ordinal);
void CloseCurrentStream_StreamControl(uint64_t stream_lifetime_ordinal,
@@ -380,11 +532,27 @@
fuchsia::media::FormatDetails format_details);
void QueueInputPacket_StreamControl(fuchsia::media::Packet packet);
void QueueInputEndOfStream_StreamControl(uint64_t stream_lifetime_ordinal);
+ // This method returns false if input buffers aren't configured enough so far,
+ // or if sysmem-based buffers can't be confirmed to be allocated. On
+ // returning false, IsStoppingLocked() will already be true.
+ bool CheckWaitEnsureInputConfigured(std::unique_lock<std::mutex>& lock);
__WARN_UNUSED_RESULT bool IsStreamActiveLocked();
+
+ void SetInputBufferSettingsCommon(
+ std::unique_lock<std::mutex>& lock,
+ fuchsia::media::StreamBufferSettings* input_settings,
+ fuchsia::media::StreamBufferPartialSettings* input_partial_settings);
+
+ void SetOutputBufferSettingsCommon(
+ std::unique_lock<std::mutex>& lock,
+ fuchsia::media::StreamBufferSettings* output_settings,
+ fuchsia::media::StreamBufferPartialSettings* output_partial_settings);
+
void SetBufferSettingsCommon(
std::unique_lock<std::mutex>& lock, CodecPort port,
- fuchsia::media::StreamBufferSettings settings,
+ fuchsia::media::StreamBufferSettings* settings,
+ fuchsia::media::StreamBufferPartialSettings* partial_settings,
const fuchsia::media::StreamBufferConstraints& constraints);
void EnsureBuffersNotConfigured(std::unique_lock<std::mutex>& lock,
CodecPort port);
@@ -395,12 +563,26 @@
CodecPort port, const fuchsia::media::StreamBufferSettings& settings,
const fuchsia::media::StreamBufferConstraints& constraints);
+ // This is just validating that the _partial_ settings set by the client are
+ // valid with respect to the constraints indicated to the client, without any
+ // involvement of sysmem yet (but soon), so there's not a ton to validate
+ // here.
+ __WARN_UNUSED_RESULT bool ValidatePartialBufferSettingsVsConstraintsLocked(
+ CodecPort port, const fuchsia::media::StreamBufferPartialSettings& partial_settings,
+ const fuchsia::media::StreamBufferConstraints& constraints);
+
+ void AddInputBufferInternal(
+ bool is_client, fuchsia::media::StreamBuffer buffer);
+
+ void AddOutputBufferInternal(
+ bool is_client, fuchsia::media::StreamBuffer buffer);
+
// Returns true if the port is done configuring (last buffer was added).
// Returns false if the port is not done configuring or if Fail() was called;
// currently the caller doesn't need to tell the difference between these two
// very different cases.
__WARN_UNUSED_RESULT bool AddBufferCommon(
- CodecPort port, fuchsia::media::StreamBuffer buffer);
+ bool is_client, CodecPort port, fuchsia::media::StreamBuffer buffer);
// Return value of false means FailLocked() has already been called.
__WARN_UNUSED_RESULT bool CheckOldBufferLifetimeOrdinalLocked(
@@ -416,6 +598,20 @@
void EnsureStreamClosed(std::unique_lock<std::mutex>& lock);
void EnsureCodecStreamClosedLockedInternal();
+ // Run all items in the sysmem_completion_queue_. The item itself is run
+ // outside the lock. Returns true if any completions ran.
+ bool RunAnySysmemCompletions(std::unique_lock<std::mutex>& lock);
+
+ // Only sysmem completions get posted this way. These essentially cut in line
+ // before most of the body of all QueueInput...StreamControl methods when
+ // those are blocked waiting for sysmem completion.
+ void PostSysmemCompletion(fit::closure to_run);
+ // Returns false if IsStoppingLocked() is already true - just to save the
+ // caller the hassle of checking itself.
+ bool WaitEnsureSysmemReadyOnInput(std::unique_lock<std::mutex>& lock);
+ void RunAnySysmemCompletionsOrWait(
+ std::unique_lock<std::mutex>& lock);
+
bool is_on_stream_failed_enabled_ = false;
// This is the stream_lifetime_ordinal of the current stream as viewed from
@@ -433,9 +629,9 @@
// this queue. This queue is how the StreamControl ordering domain knows
// whether a stream is discarded or not. If a stream isn't discarded then the
// StreamControl domain can keep waiting for the client to process
- // OnOutputConfig() for that stream. If the stream has been discarded, then
+ // OnOutputConstraints() for that stream. If the stream has been discarded, then
// StreamControl ordering domain cannot expect the client to ever process
- // OnOutputConfig() for the stream, and the StreamControl ordering domain can
+ // OnOutputConstraints() for the stream, and the StreamControl ordering domain can
// instead move on to the next stream.
//
// In addition, this can allow the StreamControl ordering domain to skip past
@@ -449,11 +645,15 @@
std::unique_ptr<const fuchsia::media::StreamBufferConstraints>
input_constraints_;
- // This is the most recent settings received from the client and accepted,
- // received via SetInputBufferSettings() or SetOutputBufferSettings(). The
- // settings are as-received from the client.
- std::unique_ptr<const fuchsia::media::StreamBufferSettings>
- port_settings_[kPortCount];
+ // This holds the most recent settings received from the client and accepted,
+ // received via SetInputBufferSettings()/SetInputBufferPartialSettings() or
+ // SetOutputBufferSettings()/SetOutputBufferPartialSettings(). The settings
+ // are retained as-received from the client. In the case of the client
+ // sending StreamBufferPartialSettings, we discover some of the settings via
+ // sysmem (instead of from the client) and store those in port_settings_, to
+ // homogenize how we handle the settigns between StreamBufferSettings and
+ // StreamBufferPartialSettings.
+ std::unique_ptr<PortSettings> port_settings_[kPortCount];
// The most recent fully-configured input or output buffers had this
// buffer_constraints_version_ordinal. Even when !port_settings_[port], this
@@ -461,10 +661,10 @@
// last_required_buffer_constraints_version_ordinal_[port].
uint64_t last_provided_buffer_constraints_version_ordinal_[kPortCount] = {};
- // For CodecImpl, the initial StreamOutputConfig can be the first sent
- // message. If sent that early, the StreamOutputConfig is likely to change
+ // For CodecImpl, the initial StreamOutputConstraints can be the first sent
+ // message. If sent that early, the StreamOutputConstraints is likely to change
// again before any output data is emitted, but it _may not_.
- std::unique_ptr<const fuchsia::media::StreamOutputConfig> output_config_;
+ std::unique_ptr<const fuchsia::media::StreamOutputConstraints> output_constraints_;
// The core codec indicated that it didn't like an output config that had this
// buffer_constraints_version_ordinal set. Normally this would lead to
@@ -527,6 +727,31 @@
// This is set when stream_.output_end_of_stream is set.
std::condition_variable output_end_of_stream_seen_;
+ // This is a queue of lambdas that are to be run on the StreamControl domain
+ // before any further QueueInput... processing on StreamControl. Even before
+ // the sysmem completion is on this queue, QueueInput...StreamControl() will
+ // be blocked waiting for sysmem completion to be done, and helping run any
+ // items that show up on this queue.
+ //
+ // This line-cutting queue avoids forcing a round-trip to ensure the client
+ // isn't sending any input until after the codec knows about the allocated
+ // buffers. This also avoids un-binding the client's channel while we wait
+ // for sysmem allocation to be complete - this is worth avoiding because if we
+ // unbind then we also don't find out about PEER_CLOSED which would be at
+ // least somewhat problematic if the client didn't also cause sysmem
+ // allocation to fail.
+ //
+ // We use wake_stream_control_condition_ to wake any
+ // QueueInput...StreamControl waiter that's blocked and helping run items on
+ // this queue, since we of course also have to give up on the wait if we're
+ // shutting down, which is an aspect in common with other StreamControl waits
+ // so it's convenient to share the condition var.
+ std::queue<fit::closure> sysmem_completion_queue_;
+
+ // Avoid re-posting to StreamControl to run sysmem_completion_queue_ items if
+ // there's already a posted runner lambda that'll notice a newly-added item.
+ bool is_sysmem_runner_pending_ = false;
+
//
// Adapter-related
//
@@ -573,17 +798,31 @@
void StartIgnoringClientOldOutputConfig(std::unique_lock<std::mutex>& lock);
- void GenerateAndSendNewOutputConfig(std::unique_lock<std::mutex>& lock,
+ void GenerateAndSendNewOutputConstraints(std::unique_lock<std::mutex>& lock,
bool buffer_constraints_action_required);
- void MidStreamOutputConfigChange(uint64_t stream_lifetime_ordinal);
+ void MidStreamOutputConstraintsChange(uint64_t stream_lifetime_ordinal);
+
+ bool FixupBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings,
+ fuchsia::sysmem::BufferCollectionConstraints* buffer_collection_constraints);
+
+ void OnBufferCollectionInfo(
+ CodecPort port, uint64_t buffer_lifetime_ordinal, zx_status_t status,
+ fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info);
+
+ // When this method is called we know we're already on the correct thread per
+ // the port.
+ void OnBufferCollectionInfoInternal(
+ CodecPort port, uint64_t buffer_lifetime_ordinal, zx_status_t allocate_status, fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info);
// These are 1:1 with logical CodecBuffer(s).
std::vector<std::unique_ptr<CodecBuffer>> all_buffers_[kPortCount];
// For this bool to be true, there must be enough buffers in all_buffers_ and
// the core codec must also be fully configured with regard to those buffers.
- bool is_port_configured_[kPortCount] = {};
+ bool is_port_buffers_configured_[kPortCount] = {};
// This vector owns these buffers.
//
@@ -609,7 +848,13 @@
__WARN_UNUSED_RESULT bool IsInputConfiguredLocked();
__WARN_UNUSED_RESULT bool IsOutputConfiguredLocked();
- __WARN_UNUSED_RESULT bool IsPortConfiguredCommonLocked(CodecPort port);
+ __WARN_UNUSED_RESULT bool IsPortBuffersConfiguredCommonLocked(CodecPort port);
+
+ // Either completely configured one way or another, or at least partially
+ // configured using sysmem-style port settings. Else the client isn't
+ // behaving properly.
+ __WARN_UNUSED_RESULT bool IsPortBuffersAtLeastPartiallyConfiguredLocked(
+ CodecPort port);
// Complain sync, then Unbind() async. Even if more than one caller
// complains, the async Unbind() work will only run once (but in such cases it
@@ -651,6 +896,9 @@
__WARN_UNUSED_RESULT bool IsStoppingLocked();
__WARN_UNUSED_RESULT bool IsStopping();
+ __WARN_UNUSED_RESULT bool IsDecoder();
+ __WARN_UNUSED_RESULT bool IsEncoder();
+
//
// Core codec interfacing.
//
@@ -686,13 +934,15 @@
// call is properly ordered with respect to onCoreCodecOutputPacket() and
// onCoreCodecOutputEndOfStream() calls.
//
- // A call to onCoreCodecMidStreamOutputConfigChange(true) must not be
+ // A call to onCoreCodecMidStreamOutputConstraintsChange(true) must not be
// followed by any more output (including EndOfStream) until the associated
// output re-config is completed by a call to
// CoreCodecMidStreamOutputBufferReConfigFinish().
- void onCoreCodecMidStreamOutputConfigChange(
+ void onCoreCodecMidStreamOutputConstraintsChange(
bool output_re_config_required) override;
+ void onCoreCodecOutputFormatChange() override;
+
void onCoreCodecInputPacketDone(CodecPacket* packet) override;
void onCoreCodecOutputPacket(CodecPacket* packet, bool error_detected_before,
@@ -710,9 +960,28 @@
__WARN_UNUSED_RESULT bool IsCoreCodecRequiringOutputConfigForFormatDetection()
override;
+ __WARN_UNUSED_RESULT bool IsCoreCodecMappedBufferNeeded(CodecPort port)
+ override;
+
+ __WARN_UNUSED_RESULT bool IsCoreCodecHwBased() override;
+
void CoreCodecInit(const fuchsia::media::FormatDetails&
initial_input_format_details) override;
+ fuchsia::sysmem::BufferCollectionConstraints
+ CoreCodecGetBufferCollectionConstraints(
+ CodecPort port,
+ const fuchsia::media::StreamBufferConstraints& stream_buffer_constraints,
+ const fuchsia::media::StreamBufferPartialSettings& partial_settings) override;
+
+ void CoreCodecSetBufferCollectionInfo(
+ CodecPort port,
+ const fuchsia::sysmem::BufferCollectionInfo_2& buffer_collection_info) override;
+
+ fuchsia::media::StreamOutputFormat CoreCodecGetOutputFormat(
+ uint64_t stream_lifetime_ordinal,
+ uint64_t new_output_format_details_version_ordinal) override;
+
void CoreCodecStartStream() override;
void CoreCodecQueueInputFormatDetails(
@@ -736,11 +1005,10 @@
void CoreCodecEnsureBuffersNotConfigured(CodecPort port) override;
__WARN_UNUSED_RESULT
- std::unique_ptr<const fuchsia::media::StreamOutputConfig>
- CoreCodecBuildNewOutputConfig(
+ std::unique_ptr<const fuchsia::media::StreamOutputConstraints>
+ CoreCodecBuildNewOutputConstraints(
uint64_t stream_lifetime_ordinal,
uint64_t new_output_buffer_constraints_version_ordinal,
- uint64_t new_output_format_details_version_ordinal,
bool buffer_constraints_action_required) override;
void CoreCodecMidStreamOutputBufferReConfigPrepare() override;
diff --git a/garnet/lib/media/test/codec_buffer.cc b/garnet/lib/media/test/codec_buffer.cc
index 9b83a14..f5fa91e 100644
--- a/garnet/lib/media/test/codec_buffer.cc
+++ b/garnet/lib/media/test/codec_buffer.cc
@@ -24,7 +24,7 @@
FXL_CHECK(status == ZX_OK);
}
-bool CodecBuffer::Init() {
+bool CodecBuffer::AllocateInternal() {
zx::vmo local_vmo;
zx_status_t res;
@@ -104,7 +104,37 @@
result->SetPhysicallyContiguousRequired(
(constraints.very_temp_kludge_bti_handle()));
}
- if (!result->Init()) {
+ if (!result->AllocateInternal()) {
+ return nullptr;
+ }
+ return result;
+}
+
+bool CodecBuffer::CreateFromVmoInternal(zx::vmo vmo, uint32_t vmo_usable_start, uint32_t vmo_usable_size, bool need_write) {
+ zx_vm_option_t options = ZX_VM_PERM_READ;
+ if (need_write) {
+ options |= ZX_VM_PERM_WRITE;
+ }
+ uintptr_t tmp;
+ zx_status_t status = zx::vmar::root_self()->map(0, vmo, vmo_usable_start, vmo_usable_size, options, &tmp);
+ if (status != ZX_OK) {
+ FXL_LOG(WARNING) << "CodecBuffer::CreateFromVmoInternal failed to map VMO - status: " << status;
+ return false;
+ }
+ base_ = reinterpret_cast<uint8_t*>(tmp);
+ vmo_ = std::move(vmo);
+ return true;
+}
+
+std::unique_ptr<CodecBuffer> CodecBuffer::CreateFromVmo(
+ uint32_t buffer_index,
+ zx::vmo vmo,
+ uint32_t vmo_usable_start,
+ uint32_t vmo_usable_size,
+ bool need_write) {
+ std::unique_ptr<CodecBuffer> result(new CodecBuffer(
+ buffer_index, vmo_usable_size));
+ if (!result->CreateFromVmoInternal(std::move(vmo), vmo_usable_start, vmo_usable_size, need_write)) {
return nullptr;
}
return result;
diff --git a/garnet/lib/media/test/codec_client.cc b/garnet/lib/media/test/codec_client.cc
index 3a5b9d2b..4a77af9 100644
--- a/garnet/lib/media/test/codec_client.cc
+++ b/garnet/lib/media/test/codec_client.cc
@@ -28,7 +28,7 @@
} // namespace
-CodecClient::CodecClient(async::Loop* loop)
+CodecClient::CodecClient(async::Loop* loop, fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem)
: loop_(loop), dispatcher_(loop_->dispatcher()) {
// We haven't created a channel yet, but that's fine, and we want the error
// handler set up before any error can possibly be generated by the channel so
@@ -56,12 +56,21 @@
fit::bind_member(this, &CodecClient::OnInputConstraints);
codec_.events().OnFreeInputPacket =
fit::bind_member(this, &CodecClient::OnFreeInputPacket);
- codec_.events().OnOutputConfig =
- fit::bind_member(this, &CodecClient::OnOutputConfig);
+ codec_.events().OnOutputConstraints =
+ fit::bind_member(this, &CodecClient::OnOutputConstraints);
+ codec_.events().OnOutputFormat =
+ fit::bind_member(this, &CodecClient::OnOutputFormat);
codec_.events().OnOutputPacket =
fit::bind_member(this, &CodecClient::OnOutputPacket);
codec_.events().OnOutputEndOfStream =
fit::bind_member(this, &CodecClient::OnOutputEndOfStream);
+
+ // Bind sysmem_ using FIDL thread. This is ok because all communication with
+ // sysmem also happens via FIDL thread so will queue after this posted lambda.
+ PostToFidlThread([this, sysmem = std::move(sysmem)]() mutable {
+ zx_status_t bind_status = sysmem_.Bind(std::move(sysmem), dispatcher_);
+ ZX_ASSERT(bind_status == ZX_OK);
+ });
}
CodecClient::~CodecClient() { Stop(); }
@@ -115,77 +124,153 @@
FXL_CHECK(input_constraints_->has_packet_count_for_server_recommended());
FXL_CHECK(input_constraints_->has_packet_count_for_server_max());
- uint32_t packet_count_for_client = kMinExtraInputPacketsForClient;
+ uint32_t packet_count_for_client = std::max(
+ kMinExtraInputPacketsForClient,
+ input_constraints_->packet_count_for_client_min());
uint32_t packet_count_for_server =
input_constraints_->packet_count_for_server_recommended();
- uint32_t input_packet_count =
- packet_count_for_client + packet_count_for_server;
- if (input_packet_count < packet_count_for_server ||
- input_packet_count > input_constraints_->packet_count_for_server_max()) {
- FXL_LOG(FATAL) << "server can't easily accomodate "
+ if (packet_count_for_client > input_constraints_->packet_count_for_client_max()) {
+ FXL_LOG(FATAL) << "server can't accomodate "
"kMinExtraInputPacketsForClient - not "
"using server - exiting";
}
- { // scope input_settings, just for clarity
- fuchsia::media::StreamBufferSettings input_settings;
- input_settings.set_buffer_lifetime_ordinal(kInputBufferLifetimeOrdinal);
- input_settings.set_buffer_constraints_version_ordinal(
- input_constraints_->buffer_constraints_version_ordinal());
- input_settings.set_packet_count_for_server(packet_count_for_server);
- input_settings.set_packet_count_for_client(packet_count_for_client);
- input_settings.set_per_packet_buffer_bytes(
- input_constraints_->per_packet_buffer_bytes_recommended());
- input_settings.set_single_buffer_mode(false);
- async::PostTask(
- dispatcher_,
- [this, input_settings = std::move(input_settings)]() mutable {
- codec_->SetInputBufferSettings(std::move(input_settings));
- });
+
+ uint32_t input_packet_count;
+ fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info;
+ if (!ConfigurePortBufferCollection(
+ false, kInputBufferLifetimeOrdinal,
+ input_constraints_->buffer_constraints_version_ordinal(),
+ packet_count_for_server, packet_count_for_client, &input_packet_count,
+ &input_buffer_collection_, &buffer_collection_info)) {
+ FXL_LOG(FATAL) << "ConfigurePortBufferCollection failed (input)";
}
+
ZX_ASSERT(input_free_bits_.empty());
input_free_bits_.resize(input_packet_count, true);
all_input_buffers_.reserve(input_packet_count);
for (uint32_t i = 0; i < input_packet_count; i++) {
std::unique_ptr<CodecBuffer> local_buffer =
- CodecBuffer::Allocate(i, *input_constraints_);
+ CodecBuffer::CreateFromVmo(i, std::move(buffer_collection_info.buffers[i].vmo), buffer_collection_info.buffers[i].vmo_usable_start, buffer_collection_info.settings.buffer_settings.size_bytes, true);
if (!local_buffer) {
- FXL_LOG(FATAL) << "CodecBuffer::Allocate() failed";
- }
- zx::vmo dup_vmo;
- if (!local_buffer->GetDupVmo(false, &dup_vmo)) {
- FXL_LOG(FATAL) << "GetDupVmo() failed";
+ FXL_LOG(FATAL) << "CodecBuffer::CreateFromVmo() failed";
}
ZX_ASSERT(all_input_buffers_.size() == i);
all_input_buffers_.push_back(std::move(local_buffer));
-
- // May as well tell the Codec server about these incrementally.
- {
- fuchsia::media::StreamBuffer codec_buffer;
- codec_buffer.set_buffer_lifetime_ordinal(kInputBufferLifetimeOrdinal);
- codec_buffer.set_buffer_index(i);
- codec_buffer.mutable_data()->vmo().set_vmo_handle(std::move(dup_vmo));
- codec_buffer.mutable_data()->vmo().set_vmo_usable_start(0);
- codec_buffer.mutable_data()->vmo().set_vmo_usable_size(
- input_constraints_->per_packet_buffer_bytes_recommended());
- async::PostTask(dispatcher_,
- [this, codec_buffer = std::move(codec_buffer)]() mutable {
- codec_->AddInputBuffer(std::move(codec_buffer));
- });
- }
}
- // Now that the codec has all the input buffers, we effectively have just
- // allocated all the input packets. They all start as free with the Codec
- // client, per protocol.
+
+ // Now that we've SetInputBufferPartialSettings(), the codec will get the
+ // input buffers from sysmem. The input packets all start as free with the
+ // Codec client, per protocol. Same goes for input buffers - this client
+ // happens to track in terms of packets and have buffer_index == packet_index.
+ //
+ // TODO(dustingreen): Have CodecClient scramble the order of packets vs.
+ // buffers to check that CodecImpl is handling that correctly for input
+ // packets.
input_free_list_.reserve(input_packet_count);
for (uint32_t i = 0; i < input_packet_count; i++) {
input_free_list_.push_back(i);
}
}
+bool CodecClient::CreateAndSyncBufferCollection(
+ fuchsia::sysmem::BufferCollectionSyncPtr* out_buffer_collection,
+ fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>*
+ out_codec_sysmem_token) {
+ fuchsia::sysmem::BufferCollectionSyncPtr buffer_collection;
+ fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> codec_sysmem_token;
+
+ // Create client_token which will get converted into out_buffer_collection.
+ fuchsia::sysmem::BufferCollectionTokenSyncPtr client_token;
+ fidl::InterfaceRequest<fuchsia::sysmem::BufferCollectionToken>
+ client_token_request = client_token.NewRequest();
+
+ // Create codec_sysmem_token that'll get returned via out_codec_sysmem_token.
+ client_token->Duplicate(
+ std::numeric_limits<uint32_t>::max(), codec_sysmem_token.NewRequest());
+
+ // client_token gets converted into a buffer_collection.
+ //
+ // Start client_token connection and start converting it into a BufferCollection,
+ // so we can Sync() the previous Duplicate().
+ PostToFidlThread(
+ [this, client_token_request = std::move(client_token_request),
+ client_token = client_token.Unbind(),
+ buffer_collection_request = buffer_collection.NewRequest()]() mutable {
+ if (!sysmem_) {
+ return;
+ }
+ sysmem_->AllocateSharedCollection(std::move(client_token_request));
+ // codec_sysmem_token will be known to sysmem by the time client_token
+ // closure is seen by sysmem, which in turn is before
+ // buffer_collection_request will be hooked up, which is why
+ // buffer_collection->Sync() completion below is enough to prove that sysmem
+ // knows about codec_sysmem_token before codec_sysmem_token is sent to the
+ // codec.
+ sysmem_->BindSharedCollection(std::move(client_token), std::move(buffer_collection_request));
+ });
+
+ // After Sync() completes its round trip, we know that sysmem knows about
+ // codec_sysmem_token (causally), which is important because we'll shortly
+ // send codec_sysmem_token to the codec which will use codec_sysmem_token via
+ // a different sysmem channel.
+ zx_status_t sync_status = buffer_collection->Sync();
+ if (sync_status != ZX_OK) {
+ FXL_LOG(FATAL) << "buffer_collection->Sync() failed - status: " << sync_status;
+ }
+
+ *out_buffer_collection = std::move(buffer_collection);
+ *out_codec_sysmem_token = std::move(codec_sysmem_token);
+ return true;
+}
+
+bool CodecClient::WaitForSysmemBuffersAllocated(
+ fuchsia::sysmem::BufferCollectionSyncPtr* buffer_collection_param,
+ fuchsia::sysmem::BufferCollectionInfo_2* out_buffer_collection_info) {
+ // The style guide doesn't like non-const &, but the code in this method is
+ // easier to read with a non-const &, so treat it that way within this method.
+ fuchsia::sysmem::BufferCollectionSyncPtr& buffer_collection =
+ *buffer_collection_param;
+ fuchsia::sysmem::BufferCollectionInfo_2 result_buffer_collection_info;
+
+ // It's not permitted to send input data until the client knows that sysmem
+ // is done allocating. It's not required that the client know that the
+ // codec knows that sysmem is done allocating though - the server will
+ // verify that sysmem is done by communicating with sysmem directly as
+ // needed.
+ zx_status_t allocate_status;
+ zx_status_t call_status = buffer_collection->WaitForBuffersAllocated(
+ &allocate_status, &result_buffer_collection_info);
+ if (call_status != ZX_OK) {
+ FXL_LOG(ERROR) << "WaitForBuffersAllocated returned failure - status: " << call_status;
+ return false;
+ }
+ if (allocate_status != ZX_OK) {
+ FXL_LOG(ERROR) << "WaitForBuffersAllocated allocation failed - status: " << allocate_status;
+ return false;
+ }
+
+ *out_buffer_collection_info = std::move(result_buffer_collection_info);
+ return true;
+}
+
void CodecClient::Stop() {
if (codec_.is_bound()) {
codec_.Unbind();
}
+ if (sysmem_.is_bound()) {
+ sysmem_.Unbind();
+ }
+ if (input_buffer_collection_.is_bound()) {
+ input_buffer_collection_.Unbind();
+ }
+ if (output_buffer_collection_.is_bound()) {
+ output_buffer_collection_.Unbind();
+ }
+}
+
+void CodecClient::PostToFidlThread(fit::closure to_run) {
+ zx_status_t post_status = async::PostTask(dispatcher_, std::move(to_run));
+ ZX_ASSERT(post_status == ZX_OK);
}
void CodecClient::CallSyncAndWaitForResponse() {
@@ -224,6 +309,22 @@
ZX_ASSERT(is_sync_complete);
}
+void CodecClient::TrackOutputStreamLifetimeOrdinal(
+ uint64_t output_stream_lifetime_ordinal) {
+ // must be odd
+ ZX_ASSERT(output_stream_lifetime_ordinal % 2 == 1);
+ ZX_ASSERT(output_stream_lifetime_ordinal >= output_stream_lifetime_ordinal_);
+ if (output_stream_lifetime_ordinal > output_stream_lifetime_ordinal_) {
+ // We're allowed to forget format any time there's a stream change, so we
+ // do. This isn't critical for this test code, but it's closer to how a
+ // real client will likely track the output format on a per-stream basis.
+ ZX_ASSERT(!last_output_format_ || last_output_format_->stream_lifetime_ordinal() == output_stream_lifetime_ordinal_);
+ output_stream_lifetime_ordinal_ = output_stream_lifetime_ordinal;
+ last_output_format_ = nullptr;
+ // We intentionally don't reset is_packet_since_last_format_.
+ }
+}
+
void CodecClient::OnInputConstraints(
fuchsia::media::StreamBufferConstraints input_constraints) {
if (input_constraints_) {
@@ -312,7 +413,15 @@
void CodecClient::QueueInputPacket(
std::unique_ptr<fuchsia::media::Packet> packet) {
ZX_ASSERT(packet->has_header());
+ ZX_ASSERT(packet->header().has_buffer_lifetime_ordinal());
ZX_ASSERT(packet->header().has_packet_index());
+ ZX_ASSERT(packet->has_buffer_index());
+ ZX_ASSERT(packet->has_stream_lifetime_ordinal());
+ ZX_ASSERT(packet->has_start_offset());
+ ZX_ASSERT(packet->has_valid_length_bytes());
+ // timestamp_ish field is optional
+ // start_access_unit field is optional
+ // known_end_access_unit is optional
fuchsia::media::Packet local_packet = fidl::Clone(*packet);
{ // scope lock
// This packet is already not on the free list, but is still considered free
@@ -343,14 +452,14 @@
std::unique_ptr<CodecOutput> CodecClient::BlockingGetEmittedOutput() {
while (true) {
- // The rule is that a required pending config won't be followed by any more
- // output packets until it's no longer pending (in the sense that the output
- // buffers have been suitably re-configured). We verify the server is
- // following that rule elsewhere, which means we know here that when both
- // packets are pending and config is pending, the packets were delivered to
- // the client first. So we drain the packets first.
+ // The rule is that a required pending constraints won't be followed by any
+ // more output packets until it's no longer pending (in the sense that the
+ // output buffers have been suitably re-configured). We verify the server
+ // is following that rule elsewhere, which means we know here that when both
+ // packets are pending and constraints is pending, the packets were
+ // delivered to the client first. So we drain the packets first.
std::unique_ptr<CodecOutput> packet;
- std::shared_ptr<const fuchsia::media::StreamOutputConfig> config;
+ std::shared_ptr<const fuchsia::media::StreamOutputConstraints> constraints;
{ // scope lock
std::unique_lock<std::mutex> lock(lock_);
while (!output_pending_) {
@@ -365,11 +474,11 @@
output_pending_ = false;
}
} else {
- ZX_ASSERT(output_config_action_pending_);
- ZX_ASSERT(last_required_output_config_);
- config = last_required_output_config_;
+ ZX_ASSERT(output_constraints_action_pending_);
+ ZX_ASSERT(last_required_output_constraints_);
+ constraints = last_required_output_constraints_;
}
- }
+ } // ~lock
// Now we own a packet or have a required config to deal with, but not both,
// so it doesn't matter which order we check here, but for clarity we check
@@ -392,7 +501,7 @@
// The main mechanism used to detect that the server isn't sending output
// too soon is output_config_action_pending_. In contrast, the client code
// in this example permits itself to send RecycleOutputPacket() after the
- // client has already seen OnOutputConfig() with action required true, even
+ // client has already seen OnOutputConstraints() with action required true, even
// though the client could stop itself from doing so as a potential
// optimization. The client is allowed to send RecycleOutputPacket() up
// until the implied ReleaseOutputBuffers() at the start of
@@ -403,24 +512,27 @@
// Because of the client allowing itself to send RecycleOutputPacket() for a
// while longer than fundamentally necessary, we delay upkeep on
// output_free_bits_ until here. This upkeep isn't really fundamentally
- // necessary between OnOutputConfig() with action required true and the last
+ // necessary between OnOutputConstraints() with action required true and the last
// AddOutputBuffer() as part of output re-configuration, but ... this
// explicit delayed upkeep _may_ help illustrate how it's acceptable for a
// client to let the completion end of output processing send
// RecycleOutputPacket() as long as all those will be sent before
// SetOutputSettings().
+ std::shared_ptr<const fuchsia::media::StreamOutputConstraints>
+ snapped_constraints;
+ uint64_t new_output_buffer_lifetime_ordinal;
{ // scope lock
std::unique_lock<std::mutex> lock(lock_);
- // We know this because the previous OnOutputConfig() set this and because
- // we're only here if it's set.
- ZX_ASSERT(output_config_action_pending_);
+ // We know this because the previous OnOutputConstraints() set this and
+ // because we're only here if it's set.
+ ZX_ASSERT(output_constraints_action_pending_);
// We know this because we reject additional output from the server when
// output_config_action_pending_ is true, and because we've drained all
// previous output by this point.
ZX_ASSERT(emitted_output_.empty());
- // We know this because we're only here if we have a pending config.
- ZX_ASSERT(config);
+ // We know this because we're only here if we have pending constraints.
+ ZX_ASSERT(constraints);
// Not really critical to do this, as we'll just end up setting these
// back to true under the same lock hold interval as we set
@@ -431,143 +543,120 @@
// Think of this assignment as slightly more than a comment in this
// example, rather than any real need.
output_free_bits_.resize(0);
- } // ~lock
- // Free the old output buffers, if any.
- while (!all_output_buffers_.empty()) {
- std::unique_ptr<CodecBuffer> buffer =
- std::move(all_output_buffers_.back());
- all_output_buffers_.pop_back();
- }
+ // Free the old output buffers, if any.
+ all_output_buffers_.clear();
- // Here is where we snap which exact config version we'll actually use.
- //
- // For a client that's doing output buffer re-config on the FIDL thread
- // during OnOutputConfig with action required true, this will always just be
- // the config being presently received. But this example shows how to drive
- // the codec in a protocol-valid way without being forced to perform buffer
- // re-configuration on the FIDL thread.
-
- std::shared_ptr<const fuchsia::media::StreamOutputConfig> snapped_config;
- uint64_t new_output_buffer_lifetime_ordinal;
- { // scope lock
- std::unique_lock<std::mutex> lock(lock_);
- ZX_ASSERT(output_config_action_pending_);
- ZX_ASSERT(last_required_output_config_);
- ZX_ASSERT(last_output_config_);
- // We'll snap the last_output_config_, which is always at least as recent
- // as the last_required_output_config_.
- snapped_config = last_output_config_;
- ZX_ASSERT(snapped_config);
+ // Here is where we snap which exact constraints version we'll actually use.
+ //
+ // For a client that's doing output buffer re-config on the FIDL thread
+ // during OnOutputConstraints with action required true, this will always
+ // just be the constraints being presently received. But this example shows
+ // how to drive the codec in a protocol-valid way without being forced to
+ // perform buffer re-configuration on the FIDL thread.
+ ZX_ASSERT(output_constraints_action_pending_);
+ ZX_ASSERT(last_required_output_constraints_);
+ ZX_ASSERT(last_output_constraints_);
+ // We'll snap the last_output_constraints_, which is always at least as
+ // recent as the last_required_output_constraints_.
+ snapped_constraints = last_output_constraints_;
+ ZX_ASSERT(snapped_constraints);
new_output_buffer_lifetime_ordinal = next_output_buffer_lifetime_ordinal_;
next_output_buffer_lifetime_ordinal_ += 2;
} // ~lock
- // Tell the server about output settings.
-
- ZX_ASSERT(snapped_config->has_buffer_constraints());
- const fuchsia::media::StreamBufferConstraints& constraints =
- snapped_config->buffer_constraints();
- ZX_ASSERT(constraints.has_packet_count_for_server_recommended());
- uint32_t packet_count_for_server =
- constraints.packet_count_for_server_recommended();
- uint32_t packet_count_for_client = kMinExtraOutputPacketsForClient;
- uint32_t packet_count = packet_count_for_server + packet_count_for_client;
- // printf("Sending SetOutputBufferSettings - buffer_lifetime_ordinal: %lu
- // buffer_constraints_version_ordinal: %lu\n",
- // new_output_buffer_lifetime_ordinal,
- // constraints.buffer_constraints_version_ordinal);
- fuchsia::media::StreamBufferSettings settings;
- settings.set_buffer_lifetime_ordinal(new_output_buffer_lifetime_ordinal);
- settings.set_buffer_constraints_version_ordinal(
- constraints.buffer_constraints_version_ordinal());
- settings.set_packet_count_for_server(packet_count_for_server);
- settings.set_packet_count_for_client(packet_count_for_client);
- settings.set_per_packet_buffer_bytes(
- constraints.per_packet_buffer_bytes_recommended());
- settings.set_single_buffer_mode(false);
- async::PostTask(dispatcher_,
- [this, settings = std::move(settings)]() mutable {
- codec_->SetOutputBufferSettings(std::move(settings));
- });
-
- // Allocate new output buffers and tell the server about them. Telling the
- // server about the last buffer is significant in the protocol. See details
- // below.
//
- // This example doesn't try to skip creating and configuring the rest of the
- // buffers if a new action-required config has arrived, but doing so would
- // be legal behavior per the protocol.
+ // Tell the server about output settings.
+ //
- all_output_buffers_.reserve(packet_count);
- for (uint32_t i = 0; i < packet_count; i++) {
- std::unique_ptr<CodecBuffer> buffer =
- CodecBuffer::Allocate(i, constraints);
- if (!buffer) {
- FXL_LOG(FATAL) << "CodecBuffer::Allocate() failed (output)";
- }
- zx::vmo dup_vmo;
- if (!buffer->GetDupVmo(true, &dup_vmo)) {
- FXL_LOG(FATAL) << "GetDupVmo() failed (output)";
- }
- ZX_ASSERT(all_output_buffers_.size() == i);
- all_output_buffers_.push_back(std::move(buffer));
-
- // The last buffer being added is significant to the protocol.
- if (i == packet_count - 1) {
- std::unique_lock<std::mutex> lock(lock_);
- // The last message will potentially result in OnOutputPacket(), so we
- // need to be ready for that packet.
- //
- // This is non-harmful if output_config_action_pending_ will remain
- // true.
- output_free_bits_.resize(packet_count, true);
- }
-
- { // scope codec_buffer for clarity
- fuchsia::media::StreamBuffer codec_buffer;
- codec_buffer.set_buffer_lifetime_ordinal(
- new_output_buffer_lifetime_ordinal);
- codec_buffer.set_buffer_index(i);
- codec_buffer.mutable_data()->vmo().set_vmo_handle(std::move(dup_vmo));
- codec_buffer.mutable_data()->vmo().set_vmo_usable_start(0);
- codec_buffer.mutable_data()->vmo().set_vmo_usable_size(
- constraints.per_packet_buffer_bytes_recommended());
- // printf("Sending AddOutputBuffer - buffer_lifetime_ordinal: %lu\n",
- // new_output_buffer_lifetime_ordinal);
- async::PostTask(
- dispatcher_,
- [this, codec_buffer = std::move(codec_buffer)]() mutable {
- codec_->AddOutputBuffer(std::move(codec_buffer));
- });
- }
+ ZX_ASSERT(snapped_constraints->has_buffer_constraints());
+ const fuchsia::media::StreamBufferConstraints& buffer_constraints =
+ snapped_constraints->buffer_constraints();
+ ZX_ASSERT(buffer_constraints.has_packet_count_for_server_recommended());
+ uint32_t packet_count_for_server =
+ buffer_constraints.packet_count_for_server_recommended();
+ uint32_t packet_count_for_client = std::max(
+ kMinExtraOutputPacketsForClient,
+ buffer_constraints.packet_count_for_client_min());
+ if (packet_count_for_client > buffer_constraints.packet_count_for_client_max()) {
+ FXL_LOG(FATAL) << "server can't accomodate "
+ "kMinExtraOutputPacketsForClient - not "
+ "using server - exiting";
}
- // So, now that we're done with that output re-config, it's time to see if
- // that re-config was the last one we need to do, or if there's a newer
- // config that's action-required.
+ uint32_t packet_count;
+ fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info;
+ if (!ConfigurePortBufferCollection(
+ true, new_output_buffer_lifetime_ordinal,
+ buffer_constraints.buffer_constraints_version_ordinal(),
+ packet_count_for_server, packet_count_for_client,
+ &packet_count, &output_buffer_collection_, &buffer_collection_info)) {
+ FXL_LOG(FATAL) << "ConfigurePortBufferCollection failed (output)";
+ }
+
+ // Configure tracking for output buffers.
+ { // scope lock
+ std::lock_guard<std::mutex> lock(lock_);
+
+ all_output_buffers_.reserve(packet_count);
+ for (uint32_t i = 0; i < packet_count; i++) {
+ std::unique_ptr<CodecBuffer> buffer = CodecBuffer::CreateFromVmo(
+ i, std::move(buffer_collection_info.buffers[i].vmo),
+ buffer_collection_info.buffers[i].vmo_usable_start,
+ buffer_collection_info.settings.buffer_settings.size_bytes, false);
+ if (!buffer) {
+ FXL_LOG(FATAL) << "CodecBuffer::Allocate() failed (output)";
+ }
+ ZX_ASSERT(all_output_buffers_.size() == i);
+ all_output_buffers_.push_back(std::move(buffer));
+
+ if (i == packet_count - 1) {
+ output_free_bits_.resize(packet_count, true);
+ }
+ }
+
+ current_output_buffer_lifetime_ordinal_ =
+ new_output_buffer_lifetime_ordinal;
+ } // ~lock
+
+ // We're ready to receive output.
+ PostToFidlThread([this, output_buffer_lifetime_ordinal = new_output_buffer_lifetime_ordinal]{
+ if (!codec_) {
+ return;
+ }
+ if (output_buffer_lifetime_ordinal != current_output_buffer_lifetime_ordinal_) {
+ return;
+ }
+ codec_->CompleteOutputBufferPartialSettings(
+ output_buffer_lifetime_ordinal);
+ });
{ // scope lock
- std::unique_lock<std::mutex> lock(lock_);
- if (snapped_config->buffer_constraints()
+ std::lock_guard<std::mutex> lock(lock_);
+
+ // So, now that we're done with that output re-config, it's time to see if
+ // that re-config was the last one we need to do, or if there's a newer
+ // config that's action-required.
+
+ if (snapped_constraints->buffer_constraints()
.buffer_constraints_version_ordinal() >=
- last_required_output_config_->buffer_constraints()
+ last_required_output_constraints_->buffer_constraints()
.buffer_constraints_version_ordinal()) {
// Good. The client is caught up. The output_config_action_pending_
// can become false here, but may very shortly become true again if
- // another OnOutputConfig() is received after we release the lock
+ // another OnOutputConstraints() is received after we release the lock
// (roughly speaking; see code).
//
// It's ok that we didn't set output_config_action_pending_ to false
// before sending the last AddOutputBuffer() above, because
- // OnOutputConfig() was still able to update
+ // OnOutputConstraints() was still able to update
// last_required_output_config_ as needed, which it's been able to do
// all along during most of this whole method. If we had set to false
// up there, it would probably be less obvious why it works vs. here,
// but either can work.
FXL_VLOG(3) << "output_config_action_pending_ = false, because client "
"caught up";
- output_config_action_pending_ = false;
+ output_constraints_action_pending_ = false;
// Because this was true for at least pending config reason which we
// are only just clearing immediately above.
ZX_ASSERT(output_pending_);
@@ -577,20 +666,127 @@
output_pending_ = false;
}
} else {
- // We've received and even more recent config that's action-required, so
- // go around again without clearing output_config_action_pending_ or
- // output_pending_. Both remain true until we've caught up to a config
- // that's at least as new as the last_required_output_config_.
+ // We've received and even more recent constraints that's
+ // action-required, so go around again without clearing
+ // output_constraints_action_pending_ or output_pending_. Both remain
+ // true until we've caught up to a config that's at least as new as the
+ // last_required_output_constraints_.
FXL_VLOG(3)
- << "output_config_action_pending_ remains true because server has "
- "sent yet another action-required output config";
- ZX_ASSERT(output_config_action_pending_);
+ << "output_constraints_action_pending_ remains true because server "
+ "has sent yet another action-required output constraints";
+ ZX_ASSERT(output_constraints_action_pending_);
ZX_ASSERT(output_pending_);
}
} // ~lock
}
}
+bool CodecClient::ConfigurePortBufferCollection(
+ bool is_output, uint64_t new_buffer_lifetime_ordinal,
+ uint64_t buffer_constraints_version_ordinal,
+ uint32_t packet_count_for_server, uint32_t packet_count_for_client,
+ uint32_t* out_packet_count,
+ fuchsia::sysmem::BufferCollectionPtr* out_buffer_collection,
+ fuchsia::sysmem::BufferCollectionInfo_2* out_buffer_collection_info) {
+ uint32_t packet_count = packet_count_for_server + packet_count_for_client;
+
+ fuchsia::media::StreamBufferPartialSettings settings;
+ settings.set_buffer_lifetime_ordinal(new_buffer_lifetime_ordinal);
+ settings.set_buffer_constraints_version_ordinal(
+ buffer_constraints_version_ordinal);
+ settings.set_single_buffer_mode(false);
+ settings.set_packet_count_for_server(packet_count_for_server);
+ settings.set_packet_count_for_client(packet_count_for_client);
+
+ fuchsia::sysmem::BufferCollectionSyncPtr buffer_collection;
+ fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken> codec_sysmem_token;
+ if (!CreateAndSyncBufferCollection(&buffer_collection, &codec_sysmem_token)) {
+ FXL_LOG(FATAL) << "CreateAndSyncBufferCollection failed (output)";
+ return false;
+ }
+
+ settings.set_sysmem_token(std::move(codec_sysmem_token));
+
+ fuchsia::sysmem::BufferCollectionConstraints constraints;
+ constraints.usage.cpu = is_output ? fuchsia::sysmem::cpuUsageReadOften : fuchsia::sysmem::cpuUsageWriteOften;
+ // TODO(dustingreen): Make this more flexible once we're more flexible on
+ // frame_count on output of decoder.
+ constraints.min_buffer_count_for_camping = packet_count_for_client;
+ ZX_DEBUG_ASSERT(constraints.min_buffer_count_for_dedicated_slack == 0);
+ ZX_DEBUG_ASSERT(constraints.min_buffer_count_for_shared_slack == 0);
+
+ // 0 is treated as 0xFFFFFFFF.
+ ZX_DEBUG_ASSERT(constraints.max_buffer_count == 0);
+
+ constraints.has_buffer_memory_constraints = true;
+ // Sysmem has a built-in min_size_bytes of 1, so no need to really constrain
+ // min_size_bytes here.
+ ZX_DEBUG_ASSERT(constraints.buffer_memory_constraints.min_size_bytes == 0);
+ constraints.buffer_memory_constraints.max_size_bytes =
+ std::numeric_limits<uint32_t>::max();
+ constraints.buffer_memory_constraints.physically_contiguous_required = false;
+ constraints.buffer_memory_constraints.secure_required = false;
+ // This test client code has no way to produce or consume output data in
+ // protected memory.
+ constraints.buffer_memory_constraints.secure_permitted = false;
+
+ // Despite being a consumer of output uncompressed video frames (when
+ // decoding video and is_output), for now we intentionally don't constrain to
+ // the PixelFormatType(s) that we can consume, and instead fail later if we
+ // get something unexpected on output. That's just easier than plumbing
+ // PixelFormatType(s) to here for now.
+ ZX_DEBUG_ASSERT(constraints.image_format_constraints_count == 0);
+
+ PostToFidlThread([this, is_output, settings = std::move(settings)]() mutable {
+ if (!codec_) {
+ return;
+ }
+ if (is_output) {
+ codec_->SetOutputBufferPartialSettings(std::move(settings));
+ } else {
+ codec_->SetInputBufferPartialSettings(std::move(settings));
+ }
+ });
+
+ buffer_collection->SetConstraints(true, std::move(constraints));
+
+ fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info;
+ // This borrows buffer_collection during the call.
+ if (!WaitForSysmemBuffersAllocated(&buffer_collection,
+ &buffer_collection_info)) {
+ FXL_LOG(FATAL) << "WaitForSysmemBuffersAllocated failed";
+ return false;
+ }
+
+ packet_count = std::max(
+ packet_count, buffer_collection_info.buffer_count);
+
+ fuchsia::sysmem::BufferCollectionPtr buffer_collection_ptr;
+
+ // For the Bind() we probably don't strictly need to be on FIDL thread, so
+ // do the Bind() here. This does mean we need to set the error handler
+ // before the Bind() however.
+ buffer_collection_ptr.set_error_handler([is_output](zx_status_t status){
+ FXL_LOG(FATAL) << "BufferCollection failed - status: " << status << " is_output: " << is_output;
+ });
+
+ // This implicitly converts buffer_collection from
+ // fidl::SynchronousInterfacePtr<> to fidl::InterfaceHandle<>, which is
+ // what we want; we're moving handling of the BufferCollection from this
+ // thread to the FIDL thread.
+ zx_status_t bind_status = buffer_collection_ptr.Bind(
+ std::move(buffer_collection), dispatcher_);
+ if (bind_status != ZX_OK) {
+ FXL_LOG(FATAL) << "buffer_collection_ptr.Bind() failed - status: " << bind_status << " is_output: " << is_output;
+ return false;
+ }
+
+ *out_packet_count = packet_count;
+ *out_buffer_collection = std::move(buffer_collection_ptr);
+ *out_buffer_collection_info = std::move(buffer_collection_info);
+ return true;
+}
+
void CodecClient::RecycleOutputPacket(
fuchsia::media::PacketHeader free_packet) {
ZX_ASSERT(free_packet.has_packet_index());
@@ -604,52 +800,63 @@
});
}
-void CodecClient::OnOutputConfig(
- fuchsia::media::StreamOutputConfig output_config) {
+void CodecClient::OnOutputConstraints(
+ fuchsia::media::StreamOutputConstraints output_constraints) {
bool output_pending_notify_needed = false;
// Not that the std::move() actaully helps here, but that's what we're doing.
- std::shared_ptr<fuchsia::media::StreamOutputConfig> shared_config =
- std::make_shared<fuchsia::media::StreamOutputConfig>(
- std::move(output_config));
+ std::shared_ptr<fuchsia::media::StreamOutputConstraints> shared_constraints =
+ std::make_shared<fuchsia::media::StreamOutputConstraints>(
+ std::move(output_constraints));
{ // scope lock
std::unique_lock<std::mutex> lock(lock_);
- ZX_ASSERT(!last_output_config_ ||
- (last_output_config_->has_buffer_constraints() &&
- last_output_config_->buffer_constraints()
+
+ if (!shared_constraints->has_stream_lifetime_ordinal()) {
+ FXL_LOG(FATAL) << "StreamOutputConstraints missing stream_lifetime_ordinal";
+ }
+ uint64_t stream_lifetime_ordinal =
+ shared_constraints->stream_lifetime_ordinal();
+ TrackOutputStreamLifetimeOrdinal(stream_lifetime_ordinal);
+
+ ZX_ASSERT(!last_output_constraints_ ||
+ (last_output_constraints_->has_buffer_constraints() &&
+ last_output_constraints_->buffer_constraints()
.has_buffer_constraints_version_ordinal()));
uint64_t previous_buffer_constraints_version_ordinal =
- last_output_config_ ? last_output_config_->buffer_constraints()
+ last_output_constraints_ ? last_output_constraints_->buffer_constraints()
.buffer_constraints_version_ordinal()
: 0;
- ZX_ASSERT(shared_config->has_buffer_constraints() &&
- shared_config->buffer_constraints()
+ ZX_ASSERT(shared_constraints->has_buffer_constraints() &&
+ shared_constraints->buffer_constraints()
.has_buffer_constraints_version_ordinal());
- if (shared_config->buffer_constraints()
+ if (shared_constraints->buffer_constraints()
.buffer_constraints_version_ordinal() <
previous_buffer_constraints_version_ordinal) {
FXL_LOG(FATAL)
<< "broken server sent badly ordered buffer constraints ordinals";
}
- if ((shared_config->has_buffer_constraints_action_required() &&
- shared_config->buffer_constraints_action_required()) &&
- shared_config->buffer_constraints()
+ if ((shared_constraints->has_buffer_constraints_action_required() &&
+ shared_constraints->buffer_constraints_action_required()) &&
+ shared_constraints->buffer_constraints()
.buffer_constraints_version_ordinal() <=
previous_buffer_constraints_version_ordinal) {
FXL_LOG(FATAL)
<< "broken server sent buffer_constraints_action_required without "
"increasingbuffer_constraints_version_ordinal";
}
- last_output_config_ = shared_config;
- FXL_VLOG(3) << "OnOutputConfig buffer_constraints_version_ordinal: "
- << shared_config->buffer_constraints()
+ last_output_constraints_ = shared_constraints;
+ FXL_VLOG(3) << "OnOutputConstraints buffer_constraints_version_ordinal: "
+ << shared_constraints->buffer_constraints()
.buffer_constraints_version_ordinal()
<< "buffer_constraints_action_required: "
- << shared_config->buffer_constraints_action_required();
- if (shared_config->buffer_constraints_action_required()) {
- last_required_output_config_ = shared_config;
+ << shared_constraints->buffer_constraints_action_required();
+ if (shared_constraints->buffer_constraints_action_required()) {
+ last_required_output_constraints_ = shared_constraints;
+ // A client is allowed to forget the output format on any action required
+ // buffer constraints, so forget here.
+ last_output_format_ = nullptr;
FXL_VLOG(3) << "output_config_action_pending_ = true, because received a "
- "buffer_constraints_action_required config\n";
- output_config_action_pending_ = true;
+ "buffer_constraints_action_required constraints\n";
+ output_constraints_action_pending_ = true;
if (!output_pending_) {
output_pending_ = true;
output_pending_notify_needed = true;
@@ -661,6 +868,42 @@
}
}
+void CodecClient::OnOutputFormat(
+ fuchsia::media::StreamOutputFormat output_format) {
+ // We don't need to notify output_pending_condition_ since in contrast to
+ // constraints we don't need to take action, and nobody cares about the format
+ // until the next packet arrives.
+ std::shared_ptr<fuchsia::media::StreamOutputFormat> shared_format =
+ std::make_shared<fuchsia::media::StreamOutputFormat>(
+ std::move(output_format));
+ std::unique_lock<std::mutex> lock(lock_);
+
+ if (!shared_format->has_stream_lifetime_ordinal()) {
+ FXL_LOG(FATAL) << "StreamOutputFormat missing stream_lifetime_ordinal";
+ }
+ uint64_t stream_lifetime_ordinal =
+ shared_format->stream_lifetime_ordinal();
+ TrackOutputStreamLifetimeOrdinal(stream_lifetime_ordinal);
+
+ if (is_format_since_last_packet_) {
+ // A server can easily elide unnecessary OnOutputFormat by not sending
+ // OnOutputFormat until immediately before OnOutputPacket. The format is
+ // logically part of each output packet, with the optimization that we only
+ // send output format when it changes, in between packets, to avoid needing
+ // to send output format with every packet.
+ FXL_LOG(FATAL)
+ << "broken server sent two OnOutputFormat() in a row";
+ }
+ if (!shared_format->has_stream_lifetime_ordinal()) {
+ FXL_LOG(FATAL) << "OnOutputFormat !has_stream_lifetime_ordinal()";
+ }
+ if (!shared_format->has_format_details()) {
+ FXL_LOG(FATAL) << "OnOutputFormat !has_format_details()";
+ }
+ last_output_format_ = shared_format;
+ is_format_since_last_packet_ = true;
+}
+
void CodecClient::OnOutputPacket(fuchsia::media::Packet output_packet,
bool error_detected_before,
bool error_detected_during) {
@@ -671,21 +914,28 @@
std::unique_ptr<const fuchsia::media::Packet> local_packet =
std::make_unique<fuchsia::media::Packet>(std::move(output_packet));
uint32_t packet_index = local_packet->header().packet_index();
- // This is safe outside the lock, because last_output_config_ is only updated
- // by OnOutputConfig(), which can't happen simultaneously with
- // OnOutputPacket().
- uint64_t stream_lifetime_ordinal = local_packet->stream_lifetime_ordinal();
- std::unique_ptr<CodecOutput> output = std::make_unique<CodecOutput>(
- stream_lifetime_ordinal, last_output_config_, std::move(local_packet),
- false);
+
{ // scope lock
std::unique_lock<std::mutex> lock(lock_);
- if (output_config_action_pending_) {
+
+ if (!local_packet->has_stream_lifetime_ordinal()) {
+ FXL_LOG(FATAL) << "Packet missing stream_lifetime_ordinal";
+ }
+ uint64_t stream_lifetime_ordinal =
+ local_packet->stream_lifetime_ordinal();
+ TrackOutputStreamLifetimeOrdinal(stream_lifetime_ordinal);
+ if (!last_output_format_ || last_output_format_->stream_lifetime_ordinal() != stream_lifetime_ordinal) {
+ FXL_LOG(FATAL) << "OnOutputFormat required before OnOutputPacket, per-stream";
+ }
+
+ std::unique_ptr<CodecOutput> output = std::make_unique<CodecOutput>(
+ stream_lifetime_ordinal, last_output_constraints_, last_output_format_,
+ std::move(local_packet), false);
+ if (output_constraints_action_pending_) {
// FWIW, we wouldn't be able to detect this if we were using the
// async::Loop thread to perform output buffer re-configuration.
FXL_LOG(FATAL) << "server incorrectly sent output packet while required "
- "config change "
- "pending";
+ "constraints change pending";
}
if (!output_free_bits_[packet_index]) {
// The packet was emitted twice by the server without it becoming free in
@@ -699,6 +949,7 @@
// which case all the output packets start free with the server.
output_free_bits_[packet_index] = false;
emitted_output_.push_back(std::move(output));
+ is_format_since_last_packet_ = false;
if (!output_pending_) {
output_pending_ = true;
output_pending_notify_needed = true;
@@ -713,15 +964,14 @@
bool error_detected_before) {
bool output_pending_notify_needed = false;
std::unique_ptr<CodecOutput> output = std::make_unique<CodecOutput>(
- stream_lifetime_ordinal, nullptr, nullptr, true);
+ stream_lifetime_ordinal, nullptr, nullptr, nullptr, true);
{ // scope lock
std::unique_lock<std::mutex> lock(lock_);
- if (output_config_action_pending_) {
+ if (output_constraints_action_pending_) {
// FWIW, we wouldn't be able to detect this if we were using the
// async::Loop thread to perform output buffer re-configuration.
FXL_LOG(FATAL) << "server incorrectly sent OnOutputEndOfStream() while "
- "required config "
- "change pending";
+ "required constraints change pending";
}
emitted_output_.push_back(std::move(output));
if (!output_pending_) {
diff --git a/garnet/lib/media/test/codec_output.cc b/garnet/lib/media/test/codec_output.cc
index 17d17cb..5d322fc 100644
--- a/garnet/lib/media/test/codec_output.cc
+++ b/garnet/lib/media/test/codec_output.cc
@@ -9,11 +9,13 @@
CodecOutput::CodecOutput(
uint64_t stream_lifetime_ordinal,
- std::shared_ptr<const fuchsia::media::StreamOutputConfig> config,
+ std::shared_ptr<const fuchsia::media::StreamOutputConstraints> constraints,
+ std::shared_ptr<const fuchsia::media::StreamOutputFormat> format,
std::unique_ptr<const fuchsia::media::Packet> packet,
bool end_of_stream)
: stream_lifetime_ordinal_(stream_lifetime_ordinal),
- config_(config),
+ constraints_(constraints),
+ format_(format),
packet_(std::move(packet)),
end_of_stream_(end_of_stream) {
// nothing else to do here
diff --git a/garnet/lib/media/test/frame_sink.cc b/garnet/lib/media/test/frame_sink.cc
index be457df..d281bce 100644
--- a/garnet/lib/media/test/frame_sink.cc
+++ b/garnet/lib/media/test/frame_sink.cc
@@ -43,7 +43,7 @@
// Must be called on main_loop_'s thread.
void FrameSink::PutFrame(
uint32_t image_id, const zx::vmo& vmo, uint64_t vmo_offset,
- std::shared_ptr<const fuchsia::media::StreamOutputConfig> output_config,
+ std::shared_ptr<const fuchsia::media::StreamOutputFormat> output_format,
fit::closure on_done) {
// This method fans out to the views_, and runs on_done async when all the
// views_ are done with the frame.
@@ -67,13 +67,13 @@
auto shared_done_runner =
std::make_shared<decltype(done_runner)>(std::move(done_runner));
- FXL_DCHECK(output_config->has_format_details());
- FXL_DCHECK(output_config->format_details().has_domain());
- FXL_DCHECK(output_config->format_details().domain().is_video());
+ FXL_DCHECK(output_format->has_format_details());
+ FXL_DCHECK(output_format->format_details().has_domain());
+ FXL_DCHECK(output_format->format_details().domain().is_video());
FXL_DCHECK(
- output_config->format_details().domain().video().is_uncompressed());
+ output_format->format_details().domain().video().is_uncompressed());
const fuchsia::media::VideoUncompressedFormat& video_format =
- output_config->format_details().domain().video().uncompressed();
+ output_format->format_details().domain().video().uncompressed();
zx_time_t present_time;
if (last_requested_present_time_ == ZX_TIME_INFINITE_PAST) {
diff --git a/garnet/lib/media/test/include/lib/media/test/codec_buffer.h b/garnet/lib/media/test/include/lib/media/test/codec_buffer.h
index 33d323b..a7b01df 100644
--- a/garnet/lib/media/test/include/lib/media/test/codec_buffer.h
+++ b/garnet/lib/media/test/include/lib/media/test/codec_buffer.h
@@ -26,6 +26,13 @@
uint32_t buffer_index,
const fuchsia::media::StreamBufferConstraints& constraints);
+ static std::unique_ptr<CodecBuffer> CreateFromVmo(
+ uint32_t buffer_index,
+ zx::vmo vmo,
+ uint32_t vmo_usable_start,
+ uint32_t vmo_usable_size,
+ bool need_write);
+
// Each successful call to this method dups the VMO handle, with basic rights
// + read + optional write depending on is_for_write.
bool GetDupVmo(bool is_for_write, zx::vmo* out_vmo);
@@ -43,12 +50,15 @@
explicit CodecBuffer(uint32_t buffer_index, size_t size_bytes);
void SetPhysicallyContiguousRequired(
const ::zx::handle& very_temp_kludge_bti_handle);
- bool Init();
+ bool AllocateInternal();
+ bool CreateFromVmoInternal(zx::vmo vmo, uint32_t vmo_usable_start, uint32_t vmo_usable_size, bool need_write);
uint32_t buffer_index_ = 0;
size_t size_bytes_ = 0;
bool is_physically_contiguous_required_ = false;
+
+ // TODO(dustingreen): Remove this:
::zx::bti very_temp_kludge_bti_handle_;
zx::vmo vmo_;
diff --git a/garnet/lib/media/test/include/lib/media/test/codec_client.h b/garnet/lib/media/test/include/lib/media/test/codec_client.h
index 3f9682c..08e2adb 100644
--- a/garnet/lib/media/test/include/lib/media/test/codec_client.h
+++ b/garnet/lib/media/test/include/lib/media/test/codec_client.h
@@ -42,7 +42,7 @@
// we want to be very sure that we'll be posting to the correct loop to send
// messages using that loop's single thread, as ProxyController doesn't have
// a lock_ in it.
- CodecClient(async::Loop* loop);
+ CodecClient(async::Loop* loop, fidl::InterfaceHandle<fuchsia::sysmem::Allocator> sysmem);
~CodecClient();
// Separate from Start() because we don't wan this class to handle the Codec
@@ -113,8 +113,30 @@
private:
friend class CodecStream;
+ void PostToFidlThread(fit::closure to_run);
+
void CallSyncAndWaitForResponse();
+ void TrackOutputStreamLifetimeOrdinal(
+ uint64_t output_stream_lifetime_ordinal);
+
+ bool CreateAndSyncBufferCollection(
+ fuchsia::sysmem::BufferCollectionSyncPtr* out_buffer_collection,
+ fidl::InterfaceHandle<fuchsia::sysmem::BufferCollectionToken>*
+ out_codec_sysmem_token);
+
+ bool WaitForSysmemBuffersAllocated(
+ fuchsia::sysmem::BufferCollectionSyncPtr* buffer_collection_param,
+ fuchsia::sysmem::BufferCollectionInfo_2* out_buffer_collection_info);
+
+ bool ConfigurePortBufferCollection(
+ bool is_output, uint64_t new_buffer_lifetime_ordinal,
+ uint64_t buffer_constraints_version_ordinal,
+ uint32_t packet_count_for_server, uint32_t packet_count_for_client,
+ uint32_t* out_packet_count,
+ fuchsia::sysmem::BufferCollectionPtr* out_buffer_collection,
+ fuchsia::sysmem::BufferCollectionInfo_2* out_buffer_collection_info);
+
//
// Events:
//
@@ -137,7 +159,17 @@
// more than one of this message per Codec instance (though not quite to the
// degree needed to fully cover client handling of true mid-stream format
// changes).
- void OnOutputConfig(fuchsia::media::StreamOutputConfig output_config);
+ void OnOutputConstraints(
+ fuchsia::media::StreamOutputConstraints output_config);
+
+ // Ever output format is stream-specific with stream_lifetime_ordinal set, and
+ // the server is required to elide any unnecessary OnOutputFormat messages
+ // without any subsequent OnOutputPacket message to which the OnOutputFormat
+ // applies. The format continues to apply to all subsequent output packets of
+ // the same stream until the stream ends or a new OnOutputFormat message is
+ // sent by the server.
+ void OnOutputFormat(
+ fuchsia::media::StreamOutputFormat output_format);
// Every output packet is stream-specific with stream_lifetime_ordinal set.
void OnOutputPacket(fuchsia::media::Packet output_packet,
@@ -154,6 +186,11 @@
// requests give back a !is_valid() request.
fidl::InterfaceRequest<fuchsia::media::StreamProcessor> temp_codec_request_;
+ fuchsia::sysmem::AllocatorPtr sysmem_;
+
+ fuchsia::sysmem::BufferCollectionPtr input_buffer_collection_;
+ fuchsia::sysmem::BufferCollectionPtr output_buffer_collection_;
+
// We're use unique_ptr<> here only for it's optional-ness.
std::unique_ptr<fuchsia::media::StreamBufferConstraints> input_constraints_;
std::condition_variable input_constraints_exist_condition_;
@@ -202,42 +239,61 @@
// how we notice.
std::vector<bool> output_free_bits_;
+ // The server must order its output strictly by stream_lifetime_ordinal - once
+ // a new stream_lifetime_ordinal has started the server can't output anything
+ // with an older stream_lifetime_ordinal.
+ uint64_t output_stream_lifetime_ordinal_ = 0;
+
// In contrast to free input packets, we care about the content of emitted
- // output packets and their order. In addition, OnOutputConfig() is ordered
+ // output packets and their order. In addition, OnOutputConstraints() is ordered
// with respect to output packets, so we just queue those along with the
// output packets to avoid any ambiguity.
//
// A client that is immediately processing every output packet and just tracks
// the most recent output config would work as long as it always associates
- // an output packet with the closest prior StreamOutputConfig.
+ // an output packet with the closest prior StreamOutputConstraints.
std::list<std::unique_ptr<CodecOutput>> emitted_output_;
// For input, in this example we just know what the input format details are
// and we send those to CodecFactory as part of CreateAudioDecoder_Params,
// so we don't really need them as a member variable.
- // For output, we have StreamOutputConfig here as a shared_ptr<> so we can
+ // For output, we have StreamOutputConstraints here as a shared_ptr<> so we can
// explicitly associate each output packet with the config that applies to the
// output packet.
//
// Note that stream_lifetime_ordinal is nearly entirely orthogonal from which
// config applies. The only interaction is that sometimes a new stream will
// happen to have a different format so will cause format_details to update.
- std::shared_ptr<const fuchsia::media::StreamOutputConfig> last_output_config_;
- std::shared_ptr<const fuchsia::media::StreamOutputConfig>
- last_required_output_config_;
- // Becomes true when we get a new last_output_config_ with action required,
- // and becomes false just before taking the needed action based on
- // last_output_config_.
- bool output_config_action_pending_ = false;
+ std::shared_ptr<const fuchsia::media::StreamOutputConstraints> last_output_constraints_;
+ std::shared_ptr<const fuchsia::media::StreamOutputConstraints>
+ last_required_output_constraints_;
+ // Most non-test clients will want to track this per-stream, since a
+ // StreamOutputFormat from an old stream_lifetime_ordinal() isn't relevant to
+ // the current stream_lifetime_ordinal. A server is not allowed to send
+ // OnOutputFormat for an old stream_lifetime_ordinal.
+ std::shared_ptr<const fuchsia::media::StreamOutputFormat> last_output_format_;
+ // True if OnOutputFormat is more recent than OnOutputPacket. This is
+ // intentionally tracked regardless of whether last_output_format_ has
+ // since been "forgotten" (set to nullptr) and regardless of whether the
+ // stream has since switched to a new stream, because we require servers to
+ // elide all unnecessary OnOutputFormat messages. An OnOutputFormat without
+ // any subsequent OnOutputPacket of the same stream is by definition
+ // unnecessary.
+ bool is_format_since_last_packet_ = false;
+ // Becomes true when we get a new last_output_constraints_ with action
+ // required, and becomes false just before taking the needed action based on
+ // last_output_constraints_.
+ bool output_constraints_action_pending_ = false;
// Only odd values are allowed for buffer_lifetime_ordinal.
uint64_t next_output_buffer_lifetime_ordinal_ = 1;
+ uint64_t current_output_buffer_lifetime_ordinal_ = 0;
// Invariant:
// output_pending_ == (!emitted_output_.empty() ||
// output_config_action_pending_)
bool ComputeOutputPendingLocked() {
- return !emitted_output_.empty() || output_config_action_pending_;
+ return !emitted_output_.empty() || output_constraints_action_pending_;
}
bool output_pending_ = false;
std::condition_variable output_pending_condition_;
diff --git a/garnet/lib/media/test/include/lib/media/test/codec_output.h b/garnet/lib/media/test/include/lib/media/test/codec_output.h
index 6f2690a..10421d6 100644
--- a/garnet/lib/media/test/include/lib/media/test/codec_output.h
+++ b/garnet/lib/media/test/include/lib/media/test/codec_output.h
@@ -11,7 +11,7 @@
#include <memory>
// Each CodecOutput represents a Packet, and the correct associated
-// StreamOutputConfig for that packet. Since the CodecClient takes care of
+// StreamOutputConstraints for that packet. Since the CodecClient takes care of
// buffer_constraints_action_required true internally, the consumer of
// CodecOutput never has to deal with the situation where there's a new buffer
// constraints that's action required before any more output packets will show
@@ -25,23 +25,30 @@
public:
CodecOutput(
uint64_t stream_lifetime_ordinal,
- std::shared_ptr<const fuchsia::media::StreamOutputConfig> config,
+ std::shared_ptr<const fuchsia::media::StreamOutputConstraints> constraints,
+ std::shared_ptr<const fuchsia::media::StreamOutputFormat> format,
std::unique_ptr<const fuchsia::media::Packet> packet,
bool end_of_stream);
uint64_t stream_lifetime_ordinal() { return stream_lifetime_ordinal_; }
- std::shared_ptr<const fuchsia::media::StreamOutputConfig> config() {
+ std::shared_ptr<const fuchsia::media::StreamOutputConstraints> constraints() {
// Caller should only call this after checking end_of_stream() first.
- assert(config_);
- return config_;
+ ZX_ASSERT(constraints_);
+ return constraints_;
+ }
+
+ std::shared_ptr<const fuchsia::media::StreamOutputFormat> format() {
+ // Caller should only call this after checking end_of_stream() first.
+ ZX_ASSERT(format_);
+ return format_;
}
// The caller doesn't own the returned reference, and the caller must ensure
// the returned reference isn't retained beyond the lifetime of CodecOutput.
const fuchsia::media::Packet& packet() {
// Caller should only call this after checking end_of_stream() first.
- assert(packet_);
+ ZX_ASSERT(packet_);
return *packet_;
}
@@ -49,8 +56,10 @@
private:
uint64_t stream_lifetime_ordinal_ = 0;
- // The shared_ptr<> is just to optimize away copying an immutable config.
- std::shared_ptr<const fuchsia::media::StreamOutputConfig> config_;
+ // The shared_ptr<> is just to optimize away copying an immutable constraints.
+ std::shared_ptr<const fuchsia::media::StreamOutputConstraints> constraints_;
+ // The shared_ptr<> is just to optimize away copying an immutable format.
+ std::shared_ptr<const fuchsia::media::StreamOutputFormat> format_;
std::unique_ptr<const fuchsia::media::Packet> packet_;
bool end_of_stream_ = false;
diff --git a/garnet/lib/media/test/include/lib/media/test/frame_sink.h b/garnet/lib/media/test/include/lib/media/test/frame_sink.h
index cbf40a5..d04069b 100644
--- a/garnet/lib/media/test/include/lib/media/test/frame_sink.h
+++ b/garnet/lib/media/test/include/lib/media/test/frame_sink.h
@@ -47,8 +47,8 @@
// The on_done will get called on main_loop_'s thread. If the callee
// wants/needs to, the callee can post to a different thread.
void PutFrame(uint32_t image_id, const zx::vmo& vmo, uint64_t vmo_offset,
- std::shared_ptr<const fuchsia::media::StreamOutputConfig>
- output_config,
+ std::shared_ptr<const fuchsia::media::StreamOutputFormat>
+ output_format,
fit::closure on_done);
// (Quickly) cause all frames to eventually be returned (assuming the rest of
diff --git a/garnet/lib/ui/gfx/displays/display_manager.cc b/garnet/lib/ui/gfx/displays/display_manager.cc
index e56034b..e56f7d6 100644
--- a/garnet/lib/ui/gfx/displays/display_manager.cc
+++ b/garnet/lib/ui/gfx/displays/display_manager.cc
@@ -230,7 +230,7 @@
if (display_controller_->ImportBufferCollection(
buffer_collection_id, std::move(token), &status) != ZX_OK ||
status != ZX_OK) {
- FXL_DLOG(ERROR) << "ImportBufferCollection failed";
+ FXL_DLOG(ERROR) << "ImportBufferCollection failed - status: " << status;
return 0;
}
diff --git a/garnet/packages/examples/BUILD.gn b/garnet/packages/examples/BUILD.gn
index d19ea43..1def270 100644
--- a/garnet/packages/examples/BUILD.gn
+++ b/garnet/packages/examples/BUILD.gn
@@ -163,7 +163,6 @@
"//garnet/examples/media:simple_sine_sync",
"//garnet/examples/media:tones",
"//garnet/examples/media:tts",
- "//garnet/examples/media:use_aac_decoder_test",
"//garnet/examples/media:use_h264_decoder_test",
"//garnet/examples/media:use_media_decoder",
]
diff --git a/garnet/packages/prod/BUILD.gn b/garnet/packages/prod/BUILD.gn
index 802cb9c..399ed02 100644
--- a/garnet/packages/prod/BUILD.gn
+++ b/garnet/packages/prod/BUILD.gn
@@ -579,7 +579,6 @@
public_deps = [
"//garnet/bin/media:codec_factory",
"//garnet/bin/media:codec_runner_sw_ffmpeg",
- "//garnet/bin/media:codec_runner_sw_omx",
]
}
diff --git a/garnet/packages/prod/media_codec b/garnet/packages/prod/media_codec
new file mode 100644
index 0000000..0196e8c
--- /dev/null
+++ b/garnet/packages/prod/media_codec
@@ -0,0 +1,6 @@
+{
+ "packages": [
+ "//garnet/bin/media:codec_factory",
+ "//garnet/bin/media:codec_runner_sw_ffmpeg"
+ ]
+}
diff --git a/garnet/packages/tests/BUILD.gn b/garnet/packages/tests/BUILD.gn
index 03416f4..253e2ef 100644
--- a/garnet/packages/tests/BUILD.gn
+++ b/garnet/packages/tests/BUILD.gn
@@ -748,7 +748,7 @@
"//garnet/bin/media/audio_core/test:audio_fidl_tests",
"//garnet/bin/media/codecs/sw:mpsc_queue_tests",
"//garnet/bin/media/codecs/test:raw_frames_test",
- "//garnet/examples/media:use_aac_decoder_test",
+ "//garnet/examples/media:use_h264_decoder_test",
"//garnet/packages/prod:media_audio",
"//garnet/packages/testing:run_test_component",
"//garnet/packages/tests:virtual_audio",
diff --git a/garnet/public/lib/fostr/fidl/fuchsia.media/BUILD.gn b/garnet/public/lib/fostr/fidl/fuchsia.media/BUILD.gn
index f823c01..42c6430 100644
--- a/garnet/public/lib/fostr/fidl/fuchsia.media/BUILD.gn
+++ b/garnet/public/lib/fostr/fidl/fuchsia.media/BUILD.gn
@@ -10,5 +10,6 @@
deps = [
"//garnet/public/lib/fostr/fidl/fuchsia.images",
"//garnet/public/lib/fostr/fidl/fuchsia.media.audio",
+ "//garnet/public/lib/fostr/fidl/fuchsia.sysmem",
]
}
diff --git a/garnet/public/lib/fostr/fidl/fuchsia.sysmem/BUILD.gn b/garnet/public/lib/fostr/fidl/fuchsia.sysmem/BUILD.gn
new file mode 100644
index 0000000..4986f2c
--- /dev/null
+++ b/garnet/public/lib/fostr/fidl/fuchsia.sysmem/BUILD.gn
@@ -0,0 +1,9 @@
+# Copyright 2019 The Fuchsia Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+import("//garnet/public/build/fostr/fostr_fidl.gni")
+
+fostr_fidl("fuchsia.sysmem") {
+ fidl_target = "//zircon/public/fidl/fuchsia-sysmem"
+}
diff --git a/sdk/fidl/fuchsia.media/BUILD.gn b/sdk/fidl/fuchsia.media/BUILD.gn
index 8dd57c3..93468aa 100644
--- a/sdk/fidl/fuchsia.media/BUILD.gn
+++ b/sdk/fidl/fuchsia.media/BUILD.gn
@@ -24,5 +24,8 @@
public_deps = [
"//sdk/fidl/fuchsia.images",
"//sdk/fidl/fuchsia.media.audio",
+ "//zircon/public/fidl/fuchsia-io",
+ "//zircon/public/fidl/fuchsia-mem",
+ "//zircon/public/fidl/fuchsia-sysmem",
]
}
diff --git a/sdk/fidl/fuchsia.media/fuchsia.media.api b/sdk/fidl/fuchsia.media/fuchsia.media.api
index e5e9fc1..dc354e5 100644
--- a/sdk/fidl/fuchsia.media/fuchsia.media.api
+++ b/sdk/fidl/fuchsia.media/fuchsia.media.api
@@ -6,8 +6,8 @@
"fidl/fuchsia.media/audio_renderer.fidl": "c36b624d329a6e80f3e9432794a038e9",
"fidl/fuchsia.media/metadata.fidl": "fb11fd050505ea7a8fc661754b044012",
"fidl/fuchsia.media/stream.fidl": "92dd4376d67914a882c7ae46874e6ca1",
- "fidl/fuchsia.media/stream_common.fidl": "bdfa65aee3d20148233912c78101b4a8",
- "fidl/fuchsia.media/stream_processor.fidl": "483e50cf3eb2859d47a0932308587e18",
+ "fidl/fuchsia.media/stream_common.fidl": "766af335abfd6779bd1924fa75e15326",
+ "fidl/fuchsia.media/stream_processor.fidl": "37d7c04a0dbca243a2c344a6b9a5508d",
"fidl/fuchsia.media/stream_type.fidl": "84bed4307c847efb75e47bf49131d323",
"fidl/fuchsia.media/timeline_function.fidl": "5ada023b93836a20fb45b278f3822ee4"
}
\ No newline at end of file
diff --git a/sdk/fidl/fuchsia.media/stream_common.fidl b/sdk/fidl/fuchsia.media/stream_common.fidl
index 604eb4b..3ada02d 100644
--- a/sdk/fidl/fuchsia.media/stream_common.fidl
+++ b/sdk/fidl/fuchsia.media/stream_common.fidl
@@ -4,6 +4,8 @@
library fuchsia.media;
+using fuchsia.sysmem;
+
/// Value
///
/// Generic "value" for use within generic "Parameter" struct.
@@ -98,7 +100,7 @@
enum AudioBitrateMode {
// Used mainly when a client is configuring an encoder's output format. May
- // also be present in an OnOutputConfig() message from an encoder, but
+ // also be present in an OnOutputConstraints() message from an encoder, but
// should not be relied upon to be present by any consumer downstream of an
// encoder.
UNSPECIFIED = 0;
@@ -124,7 +126,7 @@
// max bits per second and min bits per second, with the aim in VBR being
// constant perceived quality.
//
- // In OnOutputConfig():
+ // In OnOutputConstraints():
//
// In CBR, the nominal bits per second. In VBR, the nominal max bits per
// second.
@@ -135,7 +137,7 @@
// If UNSPECIFIED, up to the encoder. If CBR or VBR, a hint to the encoder
// to use that mode.
//
- // In OnOutputConfig():
+ // In OnOutputConstraints():
//
// Actual mode being used. UNSPECIFIED means the source is not specifying
// which mode.
@@ -257,12 +259,9 @@
///
/// Compressed video format details.
///
-/// Mostly encoder settings will go under here.
-///
-// If a compressed video format is missing any fields here other than encoder
-// settings, it's because it's a good format and is already self-describing
-// given the mime_type + format-defined oob_bytes as appropriate +
-// in-band data.
+// If a compressed video format has no fields here, it's because it's a good
+// format and is already self-describing given the mime_type + format-defined
+// oob_bytes as appropriate + in-band data.
union VideoCompressedFormat {
// TODO(dustingreen): Any compressed video formats that aren't sufficiently
// self-describing to select and create a Codec instance to decode it?
@@ -272,20 +271,6 @@
uint32 temp_field_todo_remove;
};
-/// VideoUncompressedFormatSpecificDetails
-///
-/// Extended format-specific uncompressed video format details.
-///
-// TODO(dustingreen): Switch to FIDL table instead.
-union VideoUncompressedFormatSpecificDetails {
- // TODO(dustingreen): Which formats that we care about really require
- // special format-specific details here?
-
- // TODO(dustingreen): temp field to make the compiler happy until we have at
- // least one real field.
- uint32 temp_field_todo_remove;
-};
-
enum VideoColorSpace {
// TODO(dustingreen): add to this list
INVALID = 0;
@@ -296,7 +281,13 @@
/// Uncompressed video format details.
///
// TODO(dustingreen): Integrate with a system-wide structure for this purpose.
+// In progress - see image_format field below which will take the place of this
+// struct/table.
struct VideoUncompressedFormat {
+ // TODO(dustingreen): This will replace VideoUncompressedFormat (after
+ // struct to table change merges).
+ fuchsia.sysmem.ImageFormat_2 image_format;
+
// fourcc
//
// A human-readable fourcc like RGBA should be 0x41424752 in the fourcc
@@ -383,23 +374,13 @@
bool has_pixel_aspect_ratio = false;
uint32 pixel_aspect_ratio_width = 1;
uint32 pixel_aspect_ratio_height = 1;
-
- // TODO(dustingreen): Currently this assumes 8 bits per channel, but we'll
- // need fields to indicate more bits per pixel such as 10 or 12 bits per
- // pixel. Also, potentially a way to indicate different number of bits per
- // channel for 565 16 bit RGB + packing details. Also, potentially
- // endian-ness.
- //
- // TODO(dustingreen): Also, an easy way to get a template
- // VideoUncompressedFormat that's pre-populated with reasonably-plausible
- // values, based on a fourcc or enum value + maybe primary resolution.
-
- VideoUncompressedFormatSpecificDetails special_formats;
};
/// VideoFormat
///
-// Video (compress or uncompressed) format details.
+/// Video (compress or uncompressed) format details. In this context,
+/// "uncompressed" can include block-based image compression formats that still
+/// permit fairly fast random access to image data.
union VideoFormat {
VideoCompressedFormat compressed;
VideoUncompressedFormat uncompressed;
@@ -417,8 +398,8 @@
/// FormatDetails
///
-/// This describes/details the format on input or output of a StreamProcessor (separate
-/// instances for input vs. output).
+/// This describes/details the format on input or output of a StreamProcessor
+/// (separate instances for input vs. output).
table FormatDetails {
// Particular instances of FormatDetails will set this field to make it
// easier for a receiver to determine if any part of the format has changed
@@ -484,7 +465,7 @@
// The encoder's compressed data output typically needs some configuration
// (provided in this field) that's convenient to provide in a form that's
// not oob_bytes, and the codec can convert that config to oob_bytes on
- // encoder output via OnOutputConfig(). We retain these encoder settings
+ // encoder output via OnOutputConstraints(). We retain these encoder settings
// in the output FormatDetails to allow for cases where a downstream
// consumer knowing the encoder settings could be useful.
//
diff --git a/sdk/fidl/fuchsia.media/stream_processor.fidl b/sdk/fidl/fuchsia.media/stream_processor.fidl
index da4262f..b256793 100644
--- a/sdk/fidl/fuchsia.media/stream_processor.fidl
+++ b/sdk/fidl/fuchsia.media/stream_processor.fidl
@@ -4,6 +4,8 @@
library fuchsia.media;
+using fuchsia.sysmem;
+
// See stream_processor.md for detailed interface documentation. The comments
// here are a summary only. Client implementers should see stream_processor.md
// for more detail on any message that doesn't seem sufficiently-described
@@ -24,7 +26,7 @@
// * QueueInputPacket() + OnFreeInputPacket(), for as long as it takes,
// possibly working through all input packets repeatedly before...
// 5. Get output constraints and format
-// * OnOutputConfig() - may be delivered as early as before
+// * OnOutputConstraints() - may be delivered as early as before
// OnInputConstraints() by some stream processors, but a client must
// tolerate as late as after substantial input data has been delivered
// including lots of input packet recycling via OnFreeInputPacket().
@@ -54,6 +56,10 @@
// There are separate instances of this struct for stream input and stream
// output.
+//
+// TODO(dustingreen): Some of the buffer-focused fields in this structure will
+// go away in favor of using sysmem for those aspects. The packet count fields
+// will stay. The single-buffer fields will stay.
table StreamBufferConstraints {
// This is a version number the server sets on the constraints to allow the
// server to determine when the client has caught up with the latest
@@ -230,8 +236,11 @@
12: bool single_buffer_mode_allowed;
// If true, the buffers need to be physically contiguous pages, such as can
- // be allocated using zx_vmo_create_contiguous().
+ // be allocated using zx_vmo_create_contiguous() (this syscall requires a
+ // bti handle which many clients won't have - the client will most likely
+ // want to use sysmem to allocate buffers).
13: bool is_physically_contiguous_required;
+
// VERY TEMPORARY HACK / KLUDGE - we want the BufferAllocator (or one of
// the participant drivers that needs physically contiguous buffers) to
// call zx_vmo_create_contiguous(), definitely not the StreamProcessor
@@ -242,10 +251,14 @@
// allocates buffers in the StreamProcessor interface to avoid this hack
// even before BufferAllocator, but the overall path seems shorter if we
// jump directly from this to using BufferAllocator.
+ //
+ // TODO(dustingreen): remove once zero clients need this (zero clients that
+ // need to allocate physically contiguous buffers directly / all relevant
+ // clients using sysmem).
14: handle very_temp_kludge_bti_handle;
};
-// StreamOutputConfig
+// StreamOutputConstraints
//
// The stream-processor-controlled output configuration, including both
// StreamBufferConstraints for the output and FormatDetails for the output.
@@ -256,17 +269,17 @@
// It's different than output buffer settings, which the client does get to
// control to some extent. It's different than any configurable output
// settings the client might specify for output of an encoder.
-table StreamOutputConfig {
+table StreamOutputConstraints {
// A client which always immediately re-configures output buffers on
- // receipt of OnOutputConfig() with buffer_constraints_action_required true
+ // receipt of OnOutputConstraints() with buffer_constraints_action_required true
// can safely ignore this field.
- // A client is permitted to ignore an OnOutputConfig() message even with
+ // A client is permitted to ignore an OnOutputConstraints() message even with
// buffer_constraints_action_required true if the client knows the server
// has already been told to discard the remainder of the stream with the
// same stream_lifetime_ordinal or if this stream_lifetime_ordinal field is
// set to 0. The server is required to re-send needed output config via
- // OnOutputConfig() with new stream_lifetime_ordinal and
+ // OnOutputConstraints() with new stream_lifetime_ordinal and
// buffer_constraints_action_required true, if the most recent completed
// server-side output config isn't what the server wants/needs yet for the
// new stream.
@@ -306,6 +319,22 @@
// See stream_processor.md for more on buffer_constraints_action_required.
2: bool buffer_constraints_action_required;
3: StreamBufferConstraints buffer_constraints;
+};
+
+table StreamOutputFormat {
+ // A client is permitted to ignore an OnOutputFormat() message even with
+ // buffer_constraints_action_required true if the client knows the server
+ // has already been told to discard the remainder of the stream with the
+ // same stream_lifetime_ordinal or if this stream_lifetime_ordinal field is
+ // set to 0. The server is required to re-send needed output config via
+ // OnOutputConstraints() with new stream_lifetime_ordinal and
+ // buffer_constraints_action_required true, if the most recent completed
+ // server-side output config isn't what the server wants/needs yet for the
+ // new stream.
+ //
+ // The server is required to send an OnOutputFormat() before the first
+ // output packet of a stream.
+ 1: uint64 stream_lifetime_ordinal;
// format_details
//
@@ -366,6 +395,15 @@
// technically supports mid-stream format change, but the client happens to
// know that none of the streams the client intends to process will ever
// have a mid-stream format change.
+ 2: FormatDetails format_details;
+};
+
+// DEPRECATED - this is splitting into StreamOutputConstraints and
+// StreamOutputFormat instead. See those tables.
+table StreamOutputConfig {
+ 1: uint64 stream_lifetime_ordinal;
+ 2: bool buffer_constraints_action_required;
+ 3: StreamBufferConstraints buffer_constraints;
4: FormatDetails format_details;
};
@@ -470,7 +508,7 @@
// server's use. This must be >=
// StreamBufferConstraints.packet_count_for_server_min. If constraints
// change such that this would no longer be true, the server will send an
- // OnOutputConfig() event.
+ // OnOutputConstraints() event.
//
// The stream processor server is allowed to demand that all of
// packet_count_for_server become free before making further progress, even
@@ -539,6 +577,102 @@
6: bool single_buffer_mode;
};
+// StreamBufferPartialSettings
+//
+// This struct is used instead of StreamBufferSettings when sysmem is used to
+// allocate buffers. The settings in StreamBufferSettings that are missing from
+// StreamBufferPartialSettings can be conveyed from the client directly to
+// sysmem.
+//
+// TODO(dustingreen): Consider renaming this to StreamBufferSettings once that's
+// an option.
+table StreamBufferPartialSettings {
+ // buffer_lifetime_ordinal
+ //
+ // The containing message starts a new buffer_lifetime_ordinal.
+ //
+ // There is a separate buffer_lifetime_ordinal for input vs. output.
+ //
+ // Re-use of the same value is not allowed. Values must be odd. Values
+ // must only increase (increasing by more than 2 is permitted).
+ //
+ // A buffer_lifetime_ordinal lifetime starts at SetInputBufferSettings() or
+ // SetOutputBufferSettings(), and ends at the earlier of
+ // CloseCurrentStream() with release_input_buffers/release_output_buffers
+ // set or SetOutputBufferSettings() with new buffer_lifetime_ordinal in the
+ // case of mid-stream output config change.
+ //
+ // See stream_processor.md for more on buffer_lifetime_ordinal.
+ 1: uint64 buffer_lifetime_ordinal;
+
+ // buffer_constraints_version_ordinal
+ //
+ // This value indicates which version of constraints the client is/was aware
+ // of so far.
+ //
+ // For input, this must always be 0 because constraints don't change for
+ // input (settings can change, but there's no settings vs current
+ // constraints synchronization issue on input).
+ //
+ // For output, this allows the server to know when the client is
+ // sufficiently caught up before the server will generate any more output.
+ //
+ // When there is no active stream, a client is permitted to re-configure
+ // buffers again using the same buffer_constraints_version_ordinal.
+ //
+ // See stream_processor.md for more on buffer_constraints_version_ordinal.
+ 2: uint64 buffer_constraints_version_ordinal;
+
+ // If true, there is only one buffer, but still typically more than one
+ // packet. If false, the # of packets == the number of buffers.
+ //
+ // While it's possible to set up single_buffer_mode false with each buffer
+ // referring to the same underlying VMO, single_buffer_mode true is more
+ // efficient for that case since only one mapping is created.
+ //
+ // This setting is specified by the client, and influences the constraints
+ // delivered from the StreamProcessor to sysmem (whether there's more than
+ // one buffer allocated overall or not). For single_buffer_mode true, the
+ // StreamProcessor is the one to ask sysmem for a buffer - the client should
+ // refrain from doing so or the StreamProcessor will just fail when more
+ // than one buffer gets allocated by sysmem.
+ 3: bool single_buffer_mode;
+
+ // When single_buffer_mode is false:
+ //
+ // The actual packet count will be
+ // max(packet_count_for_server + packet_count_for_client, sysmem_buffers).
+ // The sysmem_buffers is BufferCollectionInfo.buffer_count from sysmem if
+ // using sysmem, or 0 if not using sysmem.
+ //
+ // When single_buffer_mode is true:
+ //
+ // The actual packet count is packet_count_for_server +
+ // packet_count_for_client.
+ //
+ // If not using sysmem, or if using single_buffer_mode, these fields must be
+ // set and consistent with correpsonding fields in StreamBufferConstraints.
+ //
+ // If single_buffer_mode false and using sysmem, these fields can both be
+ // non-set, or can both be set and consistent with correpsonding fields in
+ // StreamBufferConstraints. If not set, the value used for the fields in
+ // the "max" expression above is 0, so buffer_count.
+ //
+ // TODO(dustingreen): Try to change FIDL generated code to not require has
+ // if the field has a default specified in the FIDL file like these do.
+ 4: uint32 packet_count_for_server = 0;
+ 5: uint32 packet_count_for_client = 0;
+
+ // The client end of a BufferCollectionToken channel, which the
+ // StreamProcessor will use to deliver constraints to sysmem and learn of
+ // buffers allocated by sysmem.
+ //
+ // The client guarantees that the token is already known to sysmem (via
+ // BufferCollectionToken.Sync(), BufferCollection.Sync(), or
+ // BufferCollectionEvents.OnDuplicatedTokensKnownByServer()).
+ 6: fuchsia.sysmem.BufferCollectionToken sysmem_token;
+};
+
// StreamBuffer
//
// The StreamBuffer struct represents a pre-configured buffer.
@@ -591,25 +725,23 @@
3: StreamBufferData data;
};
-// StreamBufferData
-//
-// For the moment, a VMO per buffer is the only type of buffer.
-//
-
-// This is extremely likely to change significantly when adding gralloc stuff,
-// but the idea with this union is to have a struct per logical way of storing
-// the data. Any multi-domain storage within a gralloc buffer will likely be
-// only indirectly represented here.
+/// StreamBufferData
+///
+/// For the moment, a VMO per buffer is the only type of buffer.
+///
+/// This is extremely likely to change significantly when adding gralloc stuff,
+/// but the idea with this union is to have a struct per logical way of storing
+/// the data. Any multi-domain storage within a gralloc buffer will likely be
+/// only indirectly represented here.
union StreamBufferData {
StreamBufferDataVmo vmo;
// TODO(dustingreen): add the gralloc way
};
-// StreamBufferDataVmo
-//
-
-// Details for a buffer backed by a VMO.
+/// StreamBufferDataVmo
+///
+/// Details for a buffer backed by a VMO.
table StreamBufferDataVmo {
// The same VMO can be used by more than one StreamBuffer (only of the same
// buffer_lifetime_ordinal), but each vmo_handle must be a separate handle.
@@ -627,14 +759,13 @@
3: uint64 vmo_usable_size;
};
-// PacketHeader
-//
-// When referring to a free packet, we use PacketHeader alone instead of
-// Packet, since while a packet is free it doesn't really have meaningful
-// offset or length etc.
-//
-
-// A populated Packet also has a PacketHeader.
+/// PacketHeader
+///
+/// When referring to a free packet, we use PacketHeader alone instead of
+/// Packet, since while a packet is free it doesn't really have meaningful
+/// offset or length etc.
+///
+/// A populated Packet also has a PacketHeader.
table PacketHeader {
// This is which buffer configuration lifetime this header is referring to.
//
@@ -705,6 +836,10 @@
// likely (will tend to complain in an obvious way if not filled out
// instead of a non-obvious data corruption when decoding buffer 0
// repeatedly instead of the correct buffers).
+ //
+ // TODO(dustingreen): Try to make FIDL table defaults have meaning, and not
+ // complain about !has when accessing the field. For now the default
+ // specified here does nothing.
2: uint32 buffer_index = 0x80000000;
// stream_lifetime_ordinal
@@ -943,16 +1078,16 @@
// This message is guaranteed to be sent unsolicited to the StreamProcessor
// client during or shortly after StreamProcessor creation. Clients should
// not depend on this being the very first message to arrive at the client.
- // OnOutputConfig() may be sent first by some stream processors that
+ // OnOutputConstraints() may be sent first by some stream processors that
// already know their initial output config without any input data, to
// encourage (but not strictly require) the client to configure output
// buffers before feeding the first input, to avoid a wasteful
- // OnOutputConfig() being generated for that first stream if the client has
+ // OnOutputConstraints() being generated for that first stream if the client has
// started configuring output but isn't done configuring output before the
// client sends the first input data for the first stream. A client is free
- // to ignore OnOutputConfig() with a stale stream_lifetime_ordinal, but
- // handling OnOutputConfig() with stream_lifetime_ordinal 0 (if any are
- // sent) can help reduce latency to first output. See OnOutputConfig() for
+ // to ignore OnOutputConstraints() with a stale stream_lifetime_ordinal, but
+ // handling OnOutputConstraints() with stream_lifetime_ordinal 0 (if any are
+ // sent) can help reduce latency to first output. See OnOutputConstraints() for
// more details.
//
// The "min" and "max" input constraints are guaranteed not to change for a
@@ -1008,31 +1143,175 @@
// QueueInputPacket() after sending the last AddInputBuffer().
AddInputBuffer(StreamBuffer buffer);
- // OnOutputConfig()
+ // SetInputBufferPartialSettings()
//
- // This event informs the client of new output config. The server will
- // send at least one of these messages before the first output packet of a
- // stream, but that message might not have
+ // When the client is using sysmem to allocate buffers, this message is
+ // used instead of SetInputBufferSettings()+AddInputBuffer(). Instead, a
+ // single SetInputBufferPartialSettings() provides the StreamProcessor with
+ // the client-specified input settings and a BufferCollectionToken which
+ // the StreamProcessor will use to convey constraints to sysmem. Both the
+ // client and the StreamProcessor will be informed of the allocated buffers
+ // directly by sysmem via their BufferCollection channel (not via the
+ // StreamProcessor channel).
+ //
+ // The client must not QueueInput...() until after sysmem informs the client
+ // that buffer allocation has completed and was successful.
+ //
+ // The server should be prepared to see QueueInput...() before the server
+ // has necessarily heard from sysmem that the buffers are allocated - the
+ // server must tolerate either ordering, as the QueueInput...() and
+ // notification of sysmem allocation completion arrive on different
+ // channels, so the client having heard that allocation is complete doesn't
+ // mean the server knows that allocation is complete yet. However, the
+ // server can expect that allocation is in fact complete and can expect to
+ // get the allocation information from sysmem immediately upon requesting
+ // the information from sysmem.
+ SetInputBufferPartialSettings(StreamBufferPartialSettings input_settings);
+
+ // OnOutputConstraints()
+ //
+ // This event informs the client of new output constraints.
+ //
+ // This message is ordered with respect to other output (such as output
+ // packets, output format, output end-of-stream).
+ //
+ // Before the first OnOutputPacket() of a stream, the server guarantees that
+ // at least one OnOutputConstraints() and exactly one OnOutputFormat() will
+ // be sent. The server may not set buffer_constraints_action_required true
+ // in OnOutputConstraints() if the buffer config is already suitable for the
+ // stream (buffer_constraints_action_required false means the buffer config
+ // is already fine). The client must tolerate multiple
+ // OnOutputConstraints() (and 1 OnOutputFormat() message) before the first
+ // output packet. As long as the client hasn't moved to a new stream, the
+ // server won't send another OnOutputConstraints() until after the client
+ // has configured output buffers.
+ //
+ // This message can be sent mid-stream by a server. If
+ // buffer_constraints_action_required false, the message is safe to
+ // ignore, but a client may choose to stash the new constraints for
+ // later use the next time the client wants to unilaterally re-configure
+ // buffers (when allowed). If later the server needs the output config to
+ // change, the server may send a new OnOutputConstraints() with
// buffer_constraints_action_required true.
//
- // If buffer_constraints_action_required is true and the
- // stream_lifetime_ordinal matches the current stream, the client must react
- // by configuring or re-configuring output buffers.
+ // On buffer_constraints_action_required true, a client that does not wish
+ // to fully handle mid-stream output buffer config changes should either
+ // give up completely on the processing, or at least re-config the output
+ // as specified before starting a new stream (and possibly re-delivering
+ // input data, if the client wants). This avoids useless retry with a new
+ // stream starting from just before the output buffer config change which
+ // would hit the same mid-stream output config change again.
//
- // Some clients may prefer not to support mid-stream output config changes,
- // but even those clients are required to process OnOutputConfig() messages
- // up to the first output packet of each stream, as OnOutputConfig() is used
- // for stream format detection as well as for potential mid-stream output
- // config changes.
+ // Similarly, some servers may only partly support mid-stream format
+ // changes, or only support a mid-stream format change if the buffers are
+ // already large enough to handle both before and after the format change.
+ // Such servers should still indicate buffer_constraints_action_required
+ // true, but then send OnStreamFailed() after the client has re-configured
+ // output buffers (seamlessly dealing with the mid-stream output config
+ // change is even better of course, but is not always feasible depending on
+ // format). When the client retries with a new stream starting from a
+ // nearby location in the client's logical overall media timeline, the
+ // output buffers will already be suitable for the larger size output, so
+ // the new stream will not need any mid-stream output buffer re-config,
+ // only a mid-stream OnOutputFormat(). This strategy avoids the problem
+ // that would otherwise occur if a client were to retry with a new stream
+ // starting just before the mid-stream output buffer config change (the
+ // retry wouldn't be effective since the same need for an output buffer
+ // config change would be hit again). Servers are discouraged from sending
+ // OnStreamFailed() solely due to a mid-stream need for different output
+ // buffer config without first sending OnOutputConstraints() with
+ // buffer_constraints_action_required true and waiting for the client to
+ // re-configure output buffers (to avoid the useless client retry with a
+ // new stream from a logical location before the config change).
//
- // For more on OnOutputConfig(), see cocec.md.
+ // When buffer_constraints_action_required true, the server will not send
+ // any OnOutputPacket() for this stream until after the client has
+ // configured/re-configured output buffers.
+ //
+ // A client that gives up on processing on any mid-stream
+ // OnOutputConstraints() or mid-stream OnOuptutFormat() should completely
+ // ignore any OnOutputConstraints() with buffer_constraints_action_required
+ // false. Otherwise the client may needlessly fail processing, or server
+ // implementations might not be able to use
+ // buffer_constraints_action_required false for fear of simpler clients
+ // just disconnecting.
+ //
+ // All clients, even those which don't want to support any mid-stream
+ // output buffer re-config or mid-stream OnOutputFormat() are required to
+ // deal with 1..multiple OnOutputConstraints() messages before the first
+ // output packet, and 1 OnOutputFormat() messages before the first output
+ // packet.
+ //
+ // This message is ordered with respect to output packets, and with respect
+ // to OnOutputFormat().
+ //
+ // For more on OnOutputConstraints(), see cocec.md.
+ -> OnOutputConstraints(StreamOutputConstraints output_config);
+
+ // OnOutputFormat()
+ //
+ // This message is sent by the server before the first output packet of any
+ // stream, and potentially mid-stream between output packets of the stream,
+ // ordered with respect to output packets, and ordered with respect to
+ // OnOutputConstraints().
+ //
+ // The server guarantees that the first packet of every stream will be
+ // preceeded by an OnOutputFormat().
+ //
+ // The server guarantees that there will be an OnOutputFormat() between an
+ // OnOutputConstraints() with buffer_constraints_action_required true and an
+ // OnOutputPacket(). In other words, the client is essentially allowed to
+ // forget what the output format is on any OnOutputConstraints() with
+ // buffer_constraints_action_required true, because the server promises a
+ // subsequent OnOutputFormat() before any OnOutputPacket().
+ //
+ // If the server sets buffer_constraints_action_required true in
+ // OnOutputConstraints(), the server won't send OnOutputFormat() (and therefore
+ // also won't send OnOutputPacket()) until the client has re-configured
+ // output buffers.
+ //
+ // The server is allowed to send an OnOutputFormat() mid-stream between two
+ // output packets.
+ //
+ // A server won't send two adjacent OnOutputFormat() messages without any
+ // output packet in between. However an OnOutputFormat() message doesn't
+ // guarantee a subsequent packet, because for example the server could send
+ // OnOutputEndOfStream() or OnStreamFailed() instead.
+ //
+ // A client that does not wish to seamlessly handle mid-stream output format
+ // changes should either ensure that no stream processed by the client
+ // ever has any mid-stream format change, or the client should ensure that
+ // any retry of processing starts the new attempt at a point logically at or
+ // after the ponit where the old format has ended and the new format starts,
+ // else the client could just hit the same mid-stream format change again.
+ //
+ // An example of this message being sent mid-stream is mid-stream change
+ // of dimensions of video frames output from a video decoder.
+ //
+ // Not all servers will support seamless handling of format change. Those
+ // that do support seamless handling of format change may require that the
+ // format change not also require output buffer re-config, in order for the
+ // handling to be seamless. See the comment block for OnOutputConstraints() for
+ // more discussion of how servers and clients should behave - in particular
+ // when they don't seamlessly handle output constraint change and/or output
+ // format change.
+ //
+ // If this message isn't being sent by the server when expected at the
+ // start of a stream, the most common reason is that a previous
+ // OnOutputConstraints() with buffer_constraints_action_required true hasn't
+ // been processed by the client (by configuring output buffers using
+ // SetOutputBufferPartialSettings() etc).
+ -> OnOutputFormat(StreamOutputFormat output_format);
+
+ // DEPRECATED - this is temporarily still sent in addition to
+ // OnOutputConstraints and OnOutputConfig. Soon this will be removed.
-> OnOutputConfig(StreamOutputConfig output_config);
// SetOutputBufferSettings() and AddOutputBuffer()
//
- // These are not permitted until after the first OnOutputConfig().
+ // These are not permitted until after the first OnOutputConstraints().
//
- // Roughly speaking, these messages are sent in response to OnOutputConfig()
+ // Roughly speaking, these messages are sent in response to OnOutputConstraints()
// with buffer_constraints_action_required true.
//
// Configuring output buffers consists of calling SetOutputBufferSettings()
@@ -1041,7 +1320,7 @@
// mode, this is the same as the number of packets. In single-buffer mode,
// this is 1.
//
- // Configuring output buffers is _required_ after OnOutputConfig() is
+ // Configuring output buffers is _required_ after OnOutputConstraints() is
// received by the client with buffer_constraints_action_required true and
// stream_lifetime_ordinal equal to the client's current
// stream_lifetime_ordinal (even if there is an active stream), and is
@@ -1052,6 +1331,37 @@
SetOutputBufferSettings(StreamBufferSettings output_settings);
AddOutputBuffer(StreamBuffer buffer);
+ // SetOutputBufferPartialSettings()
+ //
+ // When the client is using sysmem to allocate buffers, this message is
+ // used instead of SetOutputBufferSettings()+AddOutputBuffer(). Instead, a
+ // single SetOutputBufferPartialSettings() provides the StreamProcessor
+ // with the client-specified output settings and a BufferCollectionToken
+ // which the StreamProcessor will use to convey constraints to sysmem.
+ // Both the client and the StreamProcessor will be informed of the
+ // allocated buffers directly by sysmem via their BufferCollection channel
+ // (not via the StreamProcessor channel).
+ //
+ // See also ClientReadyForOutput().
+ SetOutputBufferPartialSettings(StreamBufferPartialSettings output_settings);
+
+ // After SetOutputBufferPartialSettings(), the server won't send
+ // OnOutputConstraints(), OnOutputFormat(), OnOutputPacket(), or
+ // OnOutputEndOfStream() until after the client sends
+ // CompleteOutputBufferPartialSettings().
+ //
+ // Some clients may be able to send
+ // CompleteOutputBufferPartialSettings() immediately after
+ // SetOutputBufferPartialSettings() - in that case the client needs to be
+ // prepared to receive output without knowing the buffer count or packet
+ // count yet - such clients may internally delay processing the received
+ // output until the client has heard from sysmem (which is when the client
+ // will learn the buffer count and packet count).
+ //
+ // Other clients may first wait for sysmem to allocate, prepare to receive
+ // output, and then send CompleteOutputBufferPartialSettings().
+ CompleteOutputBufferPartialSettings(uint64 buffer_lifetime_ordinal);
+
// FlushEndOfStreamAndCloseStream()
//
// This message is optional.
@@ -1090,7 +1400,7 @@
//
// Because the old stream is not done processing yet and the old stream's
// data is not being discarded, the client must be prepared to continue to
- // process OnOutputConfig() messages until the stream_lifetime_ordinal is
+ // process OnOutputConstraints() messages until the stream_lifetime_ordinal is
// done. The client will know the stream_lifetime_ordinal is done when
// OnOutputEndOfStream(), OnStreamFailed(), or the StreamProcessor channel
// closes.
@@ -1242,7 +1552,7 @@
// the only ways that an OnOutputEndOfStream() won't happen after
// QueueInputEndOfStream().
//
- // There will be no more OnOutputPacket() or OnOutputConfig() messages for
+ // There will be no more OnOutputPacket() or OnOutputConstraints() messages for
// this stream_lifetime_ordinal after this message - if a server doesn't
// follow this rule, a client should close the StreamProcessor channel.
//
@@ -1258,6 +1568,9 @@
// QueueInputFormatDetails()
//
+ // TODO(dustingreen): Rename from QueueInputFormatDetails() to
+ // QueueInputFormat().
+ //
// If the input format details are still the same as specified during
// StreamProcessor creation, this message is unnecessary and does not need
// to be sent.
@@ -1294,15 +1607,15 @@
// If the stream doesn't exist yet, this message creates the new stream.
//
// The client is required to be willing to send QueueInputPacket() prior to
- // the server's first OnOutputConfig(), and is permitted to start a new
+ // the server's first OnOutputConstraints(), and is permitted to start a new
// stream without output buffers configured yet.
//
// The client must continue to deliver input data via this message even if
- // the stream processor has not yet generated the first OnOutputConfig(),
+ // the stream processor has not yet generated the first OnOutputConstraints(),
// and even if the StreamProcessor is generating OnFreeInputPacket() for
// previously-queued input packets. The input data must continue as long
// as there are free packets to be assured that the server will ever
- // generate the first OnOutputConfig().
+ // generate the first OnOutputConstraints().
//
// For more on QueueInputPacket(), see stream_processor.md.
QueueInputPacket(Packet packet);
diff --git a/sdk/fidl/fuchsia.sysmem/fuchsia.sysmem.api b/sdk/fidl/fuchsia.sysmem/fuchsia.sysmem.api
index 63bee83..0824158 100644
--- a/sdk/fidl/fuchsia.sysmem/fuchsia.sysmem.api
+++ b/sdk/fidl/fuchsia.sysmem/fuchsia.sysmem.api
@@ -2,11 +2,11 @@
"fidl/fuchsia.sysmem/allocator.fidl": "6914234653a1699ba7d63a4e13a53d17",
"fidl/fuchsia.sysmem/collection.fidl": "8f9c46318344c3b589266a97dc2786a4",
"fidl/fuchsia.sysmem/collections_deprecated.fidl": "4953fa82beb051ff8e12be5df4e3c1a7",
- "fidl/fuchsia.sysmem/constraints.fidl": "d1f97b74eca541caf5d585fc785ec321",
+ "fidl/fuchsia.sysmem/constraints.fidl": "92db2c9fbb47a53ab2af5a08167980ab",
"fidl/fuchsia.sysmem/driver_connector.fidl": "d2818e5d7c2a0eae18fb006c8d802bf4",
"fidl/fuchsia.sysmem/format_modifier.fidl": "72bd4509f27b9581544a9b3915240aac",
"fidl/fuchsia.sysmem/formats_deprecated.fidl": "8c47d0b4664069b45b4af650e1089dde",
- "fidl/fuchsia.sysmem/image_formats.fidl": "2e1325df30fa2a1f8e81f3e2d887a7c2",
+ "fidl/fuchsia.sysmem/image_formats.fidl": "802774efd1813df8fc72738e0c34199c",
"fidl/fuchsia.sysmem/image_formats_deprecated.fidl": "0660023fbaed2b5abca9e5038d809df8",
- "fidl/fuchsia.sysmem/usages.fidl": "3df34d5dc5e4e8d21df52f9eee8d1862"
+ "fidl/fuchsia.sysmem/usages.fidl": "64442fca34901fc3c7e45b5b640be209"
}
\ No newline at end of file
diff --git a/src/media/playback/mediaplayer/fidl/fidl_decoder.cc b/src/media/playback/mediaplayer/fidl/fidl_decoder.cc
index dbfc9c7..2a9065f 100644
--- a/src/media/playback/mediaplayer/fidl/fidl_decoder.cc
+++ b/src/media/playback/mediaplayer/fidl/fidl_decoder.cc
@@ -129,8 +129,10 @@
fit::bind_member(this, &FidlDecoder::OnStreamFailed);
outboard_decoder_.events().OnInputConstraints =
fit::bind_member(this, &FidlDecoder::OnInputConstraints);
- outboard_decoder_.events().OnOutputConfig =
- fit::bind_member(this, &FidlDecoder::OnOutputConfig);
+ outboard_decoder_.events().OnOutputConstraints =
+ fit::bind_member(this, &FidlDecoder::OnOutputConstraints);
+ outboard_decoder_.events().OnOutputFormat =
+ fit::bind_member(this, &FidlDecoder::OnOutputFormat);
outboard_decoder_.events().OnOutputPacket =
fit::bind_member(this, &FidlDecoder::OnOutputPacket);
outboard_decoder_.events().OnOutputEndOfStream =
@@ -424,14 +426,6 @@
return;
}
- if (!have_real_output_stream_type_) {
- if (pre_stream_type_packet_requests_remaining_ != 0) {
- --pre_stream_type_packet_requests_remaining_;
- } else {
- return;
- }
- }
-
RequestInputPacket();
}
}
@@ -466,56 +460,24 @@
InitSucceeded();
}
-void FidlDecoder::OnOutputConfig(fuchsia::media::StreamOutputConfig config) {
+void FidlDecoder::OnOutputConstraints(
+ fuchsia::media::StreamOutputConstraints constraints) {
FXL_DCHECK_CREATION_THREAD_IS_CURRENT(thread_checker_);
- if (!config.has_format_details()) {
- FXL_LOG(ERROR) << "Config has no format details.";
- InitFailed();
- return;
- }
-
- auto stream_type =
- fidl::To<std::unique_ptr<StreamType>>(config.format_details());
- if (!stream_type) {
- FXL_LOG(ERROR) << "Can't comprehend format details.";
- InitFailed();
- return;
- }
-
- if (!config.format_details().has_format_details_version_ordinal()) {
- FXL_LOG(ERROR) << "Format details do not have version ordinal.";
- InitFailed();
- return;
- }
-
- if (output_stream_type_) {
- if (output_format_details_version_ordinal_ !=
- config.format_details().format_details_version_ordinal()) {
- HandlePossibleOutputStreamTypeChange(*output_stream_type_, *stream_type);
- }
- }
-
- output_format_details_version_ordinal_ =
- config.format_details().format_details_version_ordinal();
-
- output_stream_type_ = std::move(stream_type);
- have_real_output_stream_type_ = true;
-
- if (config.has_buffer_constraints_action_required() &&
- config.buffer_constraints_action_required() &&
- !config.has_buffer_constraints()) {
- FXL_LOG(ERROR) << "OnOutputConfig: constraints action required but "
+ if (constraints.has_buffer_constraints_action_required() &&
+ constraints.buffer_constraints_action_required() &&
+ !constraints.has_buffer_constraints()) {
+ FXL_LOG(ERROR) << "OnOutputConstraints: constraints action required but "
"constraints missing";
InitFailed();
return;
}
- if (!config.has_buffer_constraints_action_required() ||
- !config.buffer_constraints_action_required()) {
+ if (!constraints.has_buffer_constraints_action_required() ||
+ !constraints.buffer_constraints_action_required()) {
if (init_callback_) {
- FXL_LOG(ERROR) << "OnOutputConfig: constraints action not required on "
- "initial config.";
+ FXL_LOG(ERROR) << "OnOutputConstraints: constraints action not required on "
+ "initial constraints.";
InitFailed();
return;
}
@@ -530,10 +492,10 @@
// Use a single VMO for audio, VMO per buffer for video.
const bool success = output_buffers_.ApplyConstraints(
- config.buffer_constraints(),
+ constraints.buffer_constraints(),
output_stream_type_->medium() == StreamType::Medium::kAudio);
if (!success) {
- FXL_LOG(ERROR) << "OnOutputConfig: Failed to apply constraints.";
+ FXL_LOG(ERROR) << "OnOutputConstraints: Failed to apply constraints.";
InitFailed();
return;
}
@@ -544,9 +506,9 @@
outboard_decoder_->SetOutputBufferSettings(
fidl::Clone(current_set.settings()));
- if (config.has_buffer_constraints() &&
- (!config.buffer_constraints().has_per_packet_buffer_bytes_max() ||
- config.buffer_constraints().per_packet_buffer_bytes_max() == 0)) {
+ if (constraints.has_buffer_constraints() &&
+ (!constraints.buffer_constraints().has_per_packet_buffer_bytes_max() ||
+ constraints.buffer_constraints().per_packet_buffer_bytes_max() == 0)) {
FXL_LOG(ERROR) << "Buffer constraints are missing non-zero per packet "
"buffer bytes max";
InitFailed();
@@ -555,9 +517,44 @@
// Create the VMOs when we're ready, and add them to the outboard decoder.
// Mutable so we can move the vmo handle out.
- MaybeConfigureOutput(config.mutable_buffer_constraints());
+ MaybeConfigureOutput(constraints.mutable_buffer_constraints());
}; // namespace media_player
+void FidlDecoder::OnOutputFormat(fuchsia::media::StreamOutputFormat format) {
+ if (!format.has_format_details()) {
+ FXL_LOG(ERROR) << "Config has no format details.";
+ InitFailed();
+ return;
+ }
+
+ auto stream_type =
+ fidl::To<std::unique_ptr<StreamType>>(format.format_details());
+ if (!stream_type) {
+ FXL_LOG(ERROR) << "Can't comprehend format details.";
+ InitFailed();
+ return;
+ }
+
+ if (!format.format_details().has_format_details_version_ordinal()) {
+ FXL_LOG(ERROR) << "Format details do not have version ordinal.";
+ InitFailed();
+ return;
+ }
+
+ if (output_stream_type_) {
+ if (output_format_details_version_ordinal_ !=
+ format.format_details().format_details_version_ordinal()) {
+ HandlePossibleOutputStreamTypeChange(*output_stream_type_, *stream_type);
+ }
+ }
+
+ output_format_details_version_ordinal_ =
+ format.format_details().format_details_version_ordinal();
+
+ output_stream_type_ = std::move(stream_type);
+ have_real_output_stream_type_ = true;
+}
+
void FidlDecoder::OnOutputPacket(fuchsia::media::Packet packet,
bool error_detected_before,
bool error_detected_during) {
@@ -585,7 +582,12 @@
}
if (!output_buffers_.has_current_set()) {
- FXL_LOG(FATAL) << "OnOutputPacket event without prior OnOutputConfig event";
+ FXL_LOG(FATAL) << "OnOutputPacket event without prior OnOutputConstraints event";
+ // TODO(dalesat): Report error rather than crashing.
+ }
+
+ if (!have_real_output_stream_type_) {
+ FXL_LOG(FATAL) << "OnOutputPacket event without prior OnOutputFormat event";
// TODO(dalesat): Report error rather than crashing.
}
diff --git a/src/media/playback/mediaplayer/fidl/fidl_decoder.h b/src/media/playback/mediaplayer/fidl/fidl_decoder.h
index 7d1f7e0..97c7fe97 100644
--- a/src/media/playback/mediaplayer/fidl/fidl_decoder.h
+++ b/src/media/playback/mediaplayer/fidl/fidl_decoder.h
@@ -76,7 +76,7 @@
void AddInputBuffers();
// Configures the output as appropriate. |ConfigureConnectors| calls this as
- // does |OnOutputConfig|. If |constraints| is supplied, this method will
+ // does |OnOutputConstraints|. If |constraints| is supplied, this method will
// either cache the value (if the node isn't ready) or use it to configure
// the output (if the node is ready). If |constraints| is null, there are
// cached constraints and the node is ready, this method will configure the
@@ -101,8 +101,11 @@
// |ConfigureConnectors| is called.
void OnInputConstraints(fuchsia::media::StreamBufferConstraints constraints);
- // Handles the |OnOutputConfig| event from the outboard decoder.
- void OnOutputConfig(fuchsia::media::StreamOutputConfig config);
+ // Handles the |OnOutputConstraints| event from the outboard decoder.
+ void OnOutputConstraints(fuchsia::media::StreamOutputConstraints config);
+
+ // Handles the |OnOutputFormat| event from the outboard decoder.
+ void OnOutputFormat(fuchsia::media::StreamOutputFormat format);
// Handles the |OnOutputPacket| event from the outboard decoder.
void OnOutputPacket(fuchsia::media::Packet output_packet,
@@ -127,7 +130,6 @@
fuchsia::media::FormatDetails input_format_details_;
fit::function<void(bool)> init_callback_;
bool have_real_output_stream_type_ = false;
- uint32_t pre_stream_type_packet_requests_remaining_ = 10;
std::unique_ptr<StreamType> output_stream_type_;
std::unique_ptr<StreamType> revised_output_stream_type_;
bool add_input_buffers_pending_ = false;
diff --git a/zircon/system/banjo/ddk-protocol-sysmem/sysmem.banjo b/zircon/system/banjo/ddk-protocol-sysmem/sysmem.banjo
index 8db1f65..b2f441d 100644
--- a/zircon/system/banjo/ddk-protocol-sysmem/sysmem.banjo
+++ b/zircon/system/banjo/ddk-protocol-sysmem/sysmem.banjo
@@ -9,7 +9,7 @@
[Layout = "ddk-protocol"]
protocol Sysmem {
/// Takes the server end of a FIDL connection that'll serve
- /// fuchsia.sysmem.Allocator2. If the connection fails, the channel will
+ /// fuchsia.sysmem.Allocator. If the connection fails, the channel will
/// close.
- Connect(handle<channel> allocator2_request) -> (zx.status s);
+ Connect(handle<channel> allocator_request) -> (zx.status s);
};
diff --git a/zircon/system/core/svchost/sysmem.cpp b/zircon/system/core/svchost/sysmem.cpp
index 36e8d5b..e1a45bc 100644
--- a/zircon/system/core/svchost/sysmem.cpp
+++ b/zircon/system/core/svchost/sysmem.cpp
@@ -20,11 +20,11 @@
static zx_status_t sysmem2_connect(
void* ctx, async_dispatcher_t* dispatcher, const char* service_name,
- zx_handle_t allocator2_request_param) {
- zx::channel allocator2_request(allocator2_request_param);
+ zx_handle_t allocator_request_param) {
+ zx::channel allocator_request(allocator_request_param);
sysmem_connector_t* connector = static_cast<sysmem_connector_t*>(ctx);
if (!strcmp(service_name, fuchsia_sysmem_Allocator_Name)) {
- sysmem_connector_queue_connection_request(connector, allocator2_request.release());
+ sysmem_connector_queue_connection_request(connector, allocator_request.release());
}
return ZX_ERR_NOT_SUPPORTED;
}
diff --git a/zircon/system/dev/bus/platform/platform-proxy-device.cpp b/zircon/system/dev/bus/platform/platform-proxy-device.cpp
index f9826c5..1b4fe72 100644
--- a/zircon/system/dev/bus/platform/platform-proxy-device.cpp
+++ b/zircon/system/dev/bus/platform/platform-proxy-device.cpp
@@ -251,12 +251,12 @@
return proxy_->Rpc(device_id_, &req.header, sizeof(req), &resp, sizeof(resp));
}
-zx_status_t ProxySysmem::SysmemConnect(zx::channel allocator2_request) {
+zx_status_t ProxySysmem::SysmemConnect(zx::channel allocator_request) {
platform_proxy_req_t req = {};
platform_proxy_rsp_t resp = {};
req.proto_id = ZX_PROTOCOL_SYSMEM;
req.op = SYSMEM_CONNECT;
- zx_handle_t handle = allocator2_request.release();
+ zx_handle_t handle = allocator_request.release();
return proxy_->Rpc(device_id_, &req, sizeof(req), &resp, sizeof(resp), &handle, 1, nullptr, 0,
nullptr);
diff --git a/zircon/system/dev/display/display/client.cpp b/zircon/system/dev/display/display/client.cpp
index 3b65ea7..47249f7 100644
--- a/zircon/system/dev/display/display/client.cpp
+++ b/zircon/system/dev/display/display/client.cpp
@@ -1789,6 +1789,8 @@
if (status != ZX_OK) {
// Not a fatal error, but BufferCollection functions won't work.
// TODO(ZX-3355) TODO: Fail creation once all drivers implement this.
+ zxlogf(ERROR, "GetSysmemConnection failed (continuing) - status: %d\n",
+ status);
sysmem_allocator_.reset();
}
diff --git a/zircon/system/dev/display/vim-display/vim-display.cpp b/zircon/system/dev/display/vim-display/vim-display.cpp
index 67b4828..ea0ade2 100644
--- a/zircon/system/dev/display/vim-display/vim-display.cpp
+++ b/zircon/system/dev/display/vim-display/vim-display.cpp
@@ -475,7 +475,7 @@
zx_status_t status = sysmem_connect(&display->sysmem, request_handle.release());
if (status != ZX_OK) {
- DISP_ERROR("Could not connect to sysmem\n");
+ DISP_ERROR("Could not connect to sysmem - status: %d\n", status);
return status;
}
diff --git a/zircon/system/dev/sysmem/sysmem/buffer_collection.cpp b/zircon/system/dev/sysmem/sysmem/buffer_collection.cpp
index 57cdb42..7190b28 100644
--- a/zircon/system/dev/sysmem/sysmem/buffer_collection.cpp
+++ b/zircon/system/dev/sysmem/sysmem/buffer_collection.cpp
@@ -59,7 +59,13 @@
};
BufferCollection::~BufferCollection() {
- // nothing else to do here
+ // Close() the SimpleBinding<> before deleting the list of pending Txn(s),
+ // so that ~Txn doesn't complain about being deleted without being
+ // completed.
+ //
+ // Don't run the error handler; if any error handler remains it'll just get
+ // deleted here.
+ (void)binding_.Close();
}
zx_status_t BufferCollection::SetEventSink(
diff --git a/zircon/system/dev/sysmem/sysmem/device.cpp b/zircon/system/dev/sysmem/sysmem/device.cpp
index d5f494b..3dc4154 100644
--- a/zircon/system/dev/sysmem/sysmem/device.cpp
+++ b/zircon/system/dev/sysmem/sysmem/device.cpp
@@ -39,9 +39,9 @@
}();
zx_status_t in_proc_sysmem_Connect(void* ctx,
- zx_handle_t allocator2_request_param) {
+ zx_handle_t allocator_request_param) {
Device* self = static_cast<Device*>(ctx);
- return self->Connect(allocator2_request_param);
+ return self->Connect(allocator_request_param);
}
// In-proc sysmem interface. Essentially an in-proc version of
diff --git a/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.cpp b/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.cpp
index 034d482..d5e64cd 100644
--- a/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.cpp
+++ b/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.cpp
@@ -11,6 +11,7 @@
#include <lib/image-format/image_format.h>
#include <limits.h> // PAGE_SIZE
+#include <limits> // std::numeric_limits
#include <zircon/assert.h>
namespace {
@@ -36,11 +37,22 @@
// TODO(dustingreen): Switch to FIDL C++ generated code (preferred) and remove
// this, or fully implement something like this for all fields that need 0 to
// imply a default value that isn't 0.
-template <typename T> T FieldDefault1(T value) {
- if (value == 0) {
- return 1;
+template <typename T> void FieldDefault1(T* value) {
+ if (*value == 0) {
+ *value = 1;
}
- return value;
+}
+
+template <typename T> void FieldDefaultMax(T* value) {
+ if (*value == 0) {
+ *value = std::numeric_limits<T>::max();
+ }
+}
+
+// This exists just to document the meaning for now, to make the conversion more
+// clear when we switch from FIDL struct to FIDL table.
+template <typename T> void FieldDefaultZero(T* value) {
+ // no-op
}
template <typename T> T AlignUp(T value, T divisor) {
@@ -488,7 +500,7 @@
return false;
}
- if (!CheckBufferCollectionConstraints(iter->get())) {
+ if (!CheckSanitizeBufferCollectionConstraints(iter->get())) {
return false;
}
@@ -500,7 +512,7 @@
if (!iter->get()) {
continue;
}
- if (!CheckBufferCollectionConstraints(iter->get())) {
+ if (!CheckSanitizeBufferCollectionConstraints(iter->get())) {
return false;
}
if (!AccumulateConstraintBufferCollection(result.get(),
@@ -511,14 +523,17 @@
}
}
- SanitizeBufferCollectionConstraints(result.get());
+ if (!CheckSanitizeBufferCollectionConstraints(result.get())) {
+ return false;
+ }
constraints_ = std::move(result);
return true;
}
-bool LogicalBufferCollection::CheckBufferCollectionConstraints(
- const fuchsia_sysmem_BufferCollectionConstraints* constraints) {
+bool LogicalBufferCollection::CheckSanitizeBufferCollectionConstraints(
+ fuchsia_sysmem_BufferCollectionConstraints* constraints) {
+ FieldDefaultMax(&constraints->max_buffer_count);
// At least one usage bit must be specified by any participant that
// specifies constraints.
if (constraints->usage.cpu == 0 && constraints->usage.vulkan == 0 &&
@@ -527,7 +542,7 @@
return false;
}
for (uint32_t i = 0; i < constraints->image_format_constraints_count; ++i) {
- if (!CheckImageFormatConstraints(
+ if (!CheckSanitizeImageFormatConstraints(
&constraints->image_format_constraints[i])) {
return false;
}
@@ -535,8 +550,8 @@
return true;
}
-bool LogicalBufferCollection::CheckImageFormatConstraints(
- const fuchsia_sysmem_ImageFormatConstraints* constraints) {
+bool LogicalBufferCollection::CheckSanitizeImageFormatConstraints(
+ fuchsia_sysmem_ImageFormatConstraints* constraints) {
if (constraints->pixel_format.type ==
fuchsia_sysmem_PixelFormatType_INVALID) {
LogError("PixelFormatType INVALID not allowed");
@@ -556,6 +571,13 @@
return false;
}
+ FieldDefault1(&constraints->coded_width_divisor);
+ FieldDefault1(&constraints->coded_height_divisor);
+ FieldDefault1(&constraints->bytes_per_row_divisor);
+ FieldDefault1(&constraints->start_offset_divisor);
+ FieldDefault1(&constraints->display_width_divisor);
+ FieldDefault1(&constraints->display_height_divisor);
+
if (!IsNonZeroPowerOf2(constraints->coded_width_divisor)) {
LogError("non-power-of-2 coded_width_divisor not supported");
return false;
@@ -588,6 +610,21 @@
return false;
}
}
+
+ FieldDefaultMax(&constraints->required_min_coded_width);
+ FieldDefaultZero(&constraints->required_max_coded_width);
+ FieldDefaultMax(&constraints->required_min_coded_height);
+ FieldDefaultZero(&constraints->required_max_coded_height);
+ FieldDefaultMax(&constraints->required_min_bytes_per_row);
+ FieldDefaultZero(&constraints->required_max_bytes_per_row);
+
+ uint32_t min_bytes_per_row_given_min_width =
+ ImageFormatStrideBytesPerWidthPixel(&constraints->pixel_format) *
+ constraints->min_coded_width;
+ constraints->min_bytes_per_row = std::max(
+ constraints->min_bytes_per_row,
+ min_bytes_per_row_given_min_width);
+
// TODO(dustingreen): Check compatibility of color_space[] entries vs. the
// pixel_format. In particular, 2020 and 2100 don't have 8 bpp, only 10 or
// 12 bpp, while a given PixelFormat.type is a specific bpp. There's
@@ -597,22 +634,6 @@
return true;
}
-void LogicalBufferCollection::SanitizeBufferCollectionConstraints(
- fuchsia_sysmem_BufferCollectionConstraints* constraints) {
- for (uint32_t i = 0; i < constraints->image_format_constraints_count; ++i) {
- // Ensure min_bytes_per_row is sufficient to fit min_coded_width.
- fuchsia_sysmem_ImageFormatConstraints* image_format_constraints =
- &constraints->image_format_constraints[i];
- uint32_t min_bytes_per_row_given_min_width =
- ImageFormatStrideBytesPerWidthPixel(
- &image_format_constraints->pixel_format) *
- image_format_constraints->min_coded_width;
- image_format_constraints->min_bytes_per_row =
- std::max(image_format_constraints->min_bytes_per_row,
- min_bytes_per_row_given_min_width);
- }
-}
-
LogicalBufferCollection::Constraints
LogicalBufferCollection::BufferCollectionConstraintsClone(
const fuchsia_sysmem_BufferCollectionConstraints* input) {
@@ -647,6 +668,13 @@
std::max(acc->min_buffer_count_for_shared_slack,
c->min_buffer_count_for_shared_slack);
+ // 0 is replaced with 0xFFFFFFFF in
+ // CheckSanitizeBufferCollectionConstraints.
+ ZX_DEBUG_ASSERT(acc->max_buffer_count != 0);
+ ZX_DEBUG_ASSERT(c->max_buffer_count != 0);
+ acc->max_buffer_count = std::min(
+ acc->max_buffer_count, c->max_buffer_count);
+
if (!acc->has_buffer_memory_constraints) {
if (c->has_buffer_memory_constraints) {
// struct copy
@@ -718,6 +746,13 @@
fuchsia_sysmem_BufferMemoryConstraints* acc,
const fuchsia_sysmem_BufferMemoryConstraints* c) {
acc->min_size_bytes = std::max(acc->min_size_bytes, c->min_size_bytes);
+ // Don't permit 0 as the overall min_size_bytes; that would be nonsense. No
+ // particular initiator should feel that it has to specify 1 in this field;
+ // that's just built into sysmem instead. While a VMO will have a minimum
+ // actual size of page size, we do permit treating buffers as if they're 1
+ // byte, mainly for testing reasons, and to avoid any unnecessary dependence
+ // or assumptions re. page size.
+ acc->min_size_bytes = std::max(acc->min_size_bytes, 1u);
acc->max_size_bytes = std::min(acc->max_size_bytes, c->max_size_bytes);
if (acc->min_size_bytes > acc->max_size_bytes) {
LogError("min_size_bytes > max_size_bytes");
@@ -882,6 +917,48 @@
acc->display_height_divisor =
std::max(acc->display_height_divisor, c->display_height_divisor);
+ // The required_ space is accumulated by taking the union, and must be fully
+ // within the non-required_ space, else fail. For example, this allows a
+ // video decoder to indicate that it's capable of outputting a wide range of
+ // output dimensions, but that it has specific current dimensions that are
+ // presently required_ (min == max) for decode to proceed.
+ ZX_DEBUG_ASSERT(acc->required_min_coded_width != 0);
+ ZX_DEBUG_ASSERT(c->required_min_coded_width != 0);
+ acc->required_min_coded_width = std::min(acc->required_min_coded_width, c->required_min_coded_width);
+ if (acc->required_min_coded_width < acc->min_coded_width) {
+ LogError("required_min_coded_width < min_coded_width");
+ return false;
+ }
+ acc->required_max_coded_width = std::max(acc->required_max_coded_width, c->required_max_coded_width);
+ if (acc->required_max_coded_width > acc->max_coded_width) {
+ LogError("required_max_coded_width > max_coded_width");
+ return false;
+ }
+ ZX_DEBUG_ASSERT(acc->required_min_coded_height != 0);
+ ZX_DEBUG_ASSERT(c->required_min_coded_height != 0);
+ acc->required_min_coded_height = std::min(acc->required_min_coded_height, c->required_min_coded_height);
+ if (acc->required_min_coded_height < acc->min_coded_height) {
+ LogError("required_min_coded_height < min_coded_height");
+ return false;
+ }
+ acc->required_max_coded_height = std::max(acc->required_max_coded_height, c->required_max_coded_height);
+ if (acc->required_max_coded_height > acc->max_coded_height) {
+ LogError("required_max_coded_height > max_coded_height");
+ return false;
+ }
+ ZX_DEBUG_ASSERT(acc->required_min_bytes_per_row != 0);
+ ZX_DEBUG_ASSERT(c->required_min_bytes_per_row != 0);
+ acc->required_min_bytes_per_row = std::min(acc->required_min_bytes_per_row, c->required_min_bytes_per_row);
+ if (acc->required_min_bytes_per_row < acc->min_bytes_per_row) {
+ LogError("required_min_bytes_per_row < min_bytes_per_row");
+ return false;
+ }
+ acc->required_max_bytes_per_row = std::max(acc->required_max_bytes_per_row, c->required_max_bytes_per_row);
+ if (acc->required_max_bytes_per_row > acc->max_bytes_per_row) {
+ LogError("required_max_bytes_per_row > max_bytes_per_row");
+ return false;
+ }
+
return true;
}
@@ -952,9 +1029,21 @@
BufferCollection::BufferCollectionInfo result(
BufferCollection::BufferCollectionInfo::Default);
- result->buffer_count = constraints_->min_buffer_count_for_camping +
- constraints_->min_buffer_count_for_dedicated_slack +
- constraints_->min_buffer_count_for_shared_slack;
+ uint32_t min_buffer_count =
+ constraints_->min_buffer_count_for_camping +
+ constraints_->min_buffer_count_for_dedicated_slack +
+ constraints_->min_buffer_count_for_shared_slack;
+ uint32_t max_buffer_count = constraints_->max_buffer_count;
+ if (min_buffer_count > max_buffer_count) {
+ LogError("aggregate min_buffer_count > aggregate max_buffer_count - "
+ "min: %u max: %u", min_buffer_count, max_buffer_count);
+ *allocation_result = ZX_ERR_NOT_SUPPORTED;
+ return BufferCollection::BufferCollectionInfo(
+ BufferCollection::BufferCollectionInfo::Null);
+ }
+ result->buffer_count = min_buffer_count;
+ ZX_DEBUG_ASSERT(result->buffer_count <= max_buffer_count);
+
uint64_t min_size_bytes = 0;
uint64_t max_size_bytes = std::numeric_limits<uint64_t>::max();
@@ -1033,7 +1122,7 @@
min_image.coded_width =
AlignUp(constraints->min_coded_width,
- FieldDefault1(constraints->coded_width_divisor));
+ constraints->coded_width_divisor);
if (min_image.coded_width > constraints->max_coded_width) {
LogError(
"coded_width_divisor caused coded_width > max_coded_width");
@@ -1043,7 +1132,7 @@
}
min_image.coded_height =
AlignUp(constraints->min_coded_height,
- FieldDefault1(constraints->coded_height_divisor));
+ constraints->coded_height_divisor);
if (min_image.coded_height > constraints->max_coded_height) {
LogError(
"coded_height_divisor caused coded_height > max_coded_height");
@@ -1053,7 +1142,7 @@
}
min_image.bytes_per_row =
AlignUp(constraints->min_bytes_per_row,
- FieldDefault1(constraints->bytes_per_row_divisor));
+ constraints->bytes_per_row_divisor);
if (min_image.bytes_per_row > constraints->max_bytes_per_row) {
LogError("bytes_per_row_divisor caused bytes_per_row > "
"max_bytes_per_row");
@@ -1130,6 +1219,11 @@
// Now that min_size_bytes accounts for any ImageFormatConstraints, we can
// just allocate min_size_bytes buffers.
+ //
+ // If an initiator (or a participant) wants to force buffers to be larger
+ // than the size implied by minimum image dimensions, the initiator can use
+ // BufferMemorySettings.min_size_bytes to force allocated buffers to be
+ // large enough.
buffer_settings->size_bytes = static_cast<uint32_t>(min_size_bytes);
for (uint32_t i = 0; i < result->buffer_count; ++i) {
diff --git a/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.h b/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.h
index 237a71e..dcd9065 100644
--- a/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.h
+++ b/zircon/system/dev/sysmem/sysmem/logical_buffer_collection.h
@@ -101,15 +101,12 @@
bool CombineConstraints();
- bool CheckBufferCollectionConstraints(
- const fuchsia_sysmem_BufferCollectionConstraints* constraints);
-
- bool CheckImageFormatConstraints(
- const fuchsia_sysmem_ImageFormatConstraints* constraints);
-
- void SanitizeBufferCollectionConstraints(
+ bool CheckSanitizeBufferCollectionConstraints(
fuchsia_sysmem_BufferCollectionConstraints* constraints);
+ bool CheckSanitizeImageFormatConstraints(
+ fuchsia_sysmem_ImageFormatConstraints* constraints);
+
Constraints BufferCollectionConstraintsClone(
const fuchsia_sysmem_BufferCollectionConstraints* input);
diff --git a/zircon/system/fidl/fuchsia-sysmem/constraints.fidl b/zircon/system/fidl/fuchsia-sysmem/constraints.fidl
index 9c1e8b8..c18b0f9 100644
--- a/zircon/system/fidl/fuchsia-sysmem/constraints.fidl
+++ b/zircon/system/fidl/fuchsia-sysmem/constraints.fidl
@@ -77,6 +77,9 @@
/// accounted for in min_buffer_count_for_camping.
uint32 min_buffer_count_for_shared_slack;
+ // 0 is treated as 0xFFFFFFFF.
+ uint32 max_buffer_count;
+
/// Constraints on BufferCollectionSettings.buffer_settings.
///
/// A participant that intends to specify image_format_constraints_count > 1
@@ -142,7 +145,7 @@
struct SingleBufferInfo {
SingleBufferSettings settings;
- handle<vmo>? vmo;
+ VmoBuffer buffer;
};
// After the initial buffer allocation, it's allowed to close old buffers and
@@ -218,8 +221,16 @@
uint32 color_spaces_count;
array<ColorSpace>:32 color_space;
- /// Minimum width in pixels. For example a video decoder participant may
- /// set this field to the coded_width specified by a stream.
+ /// Minimum permitted width in pixels.
+ ///
+ /// For example a video decoder participant may set this field to the
+ /// minimum coded_width that might potentially be specified by a stream. In
+ /// contrast, required_min_coded_width would be set to the current
+ /// coded_width specified by the stream. While min_coded_width aggregates
+ /// by taking the max, required_min_coded_width aggregates by taking the
+ /// min.
+ ///
+ /// See also required_min_coded_width.
uint32 min_coded_width;
/// Maximum width in pixels. For example Scenic may set this field
/// (directly or via sub-participants) to the maximum width that can be
@@ -265,6 +276,39 @@
/// display_height % display_height_divisor must be 0.
uint32 display_height_divisor = 1;
+
+ /// required_ dimension bounds.
+ ///
+ /// In contrast to the corresponding fields without "required_" at the
+ /// start, these fields (when set to non-zero values) express a requirement
+ /// that the resulting aggregated non-required_ fields specify a space that
+ /// fully contain the space expressed by each participant's required_
+ /// fields.
+ ///
+ /// For example, a producer video decoder is perfectly happy for the
+ /// consumer to be willing to accept anything, and the video decoder doesn't
+ /// really want to constrain the potential space of dimensions that might be
+ /// seen in a stream and may be acceptable to the consumer, but the video
+ /// decoder needs to ensure that the resulting dimension ranges contain
+ /// at least the current dimensions decoded from the stream.
+ ///
+ /// Similarly, an initiator with a particular dynamic-dimension scenario in
+ /// mind may wish to require up front that participants agree to handle at
+ /// least the range of dimensions expected by the initiator in that
+ /// scenario (else fail earlier rather than later, maybe trying again with
+ /// smaller required_ space).
+ ///
+ /// It's much more common for a producer or initiator to set these fields
+ /// than for a consumer to set these fields.
+ ///
+ /// While the non-required_ fields aggregate by taking the intersection, the
+ /// required_ fields aggregate by taking the union.
+ uint32 required_min_coded_width;
+ uint32 required_max_coded_width;
+ uint32 required_min_coded_height;
+ uint32 required_max_coded_height;
+ uint32 required_min_bytes_per_row;
+ uint32 required_max_bytes_per_row;
};
/// Describes how an image is represented.
@@ -297,4 +341,23 @@
/// Color space.
ColorSpace color_space;
+
+ // The pixel_aspect_ratio_width : pixel_aspect_ratio_height is the
+ // pixel aspect ratio (AKA sample aspect ratio aka SAR) for the luma
+ // (AKA Y) samples. A pixel_aspect_ratio of 1:1 mean square pixels. A
+ // pixel_aspect_ratio of 2:1 would mean pixels that are displayed twice
+ // as wide as they are tall. Codec implementation should ensure these
+ // two values are relatively prime by reducing the fraction (dividing
+ // both by GCF) if necessary.
+ //
+ // When has_pixel_aspect_ratio == false, pixel_aspect_ratio_width and
+ // pixel_aspect_ratio_height will both be 1, but in that case the
+ // pixel_aspect_ratio_width : pixel_aspect_ratio_height of 1:1 is just
+ // a very weak suggestion re. reasonable-ish handling, not in any way
+ // authoritative. In this case (or in any case really) the receiver of
+ // this message may have other OOB means to determine the actual
+ // pixel_aspect_ratio.
+ bool has_pixel_aspect_ratio = false;
+ uint32 pixel_aspect_ratio_width = 1;
+ uint32 pixel_aspect_ratio_height = 1;
};
diff --git a/zircon/system/fidl/fuchsia-sysmem/image_formats.fidl b/zircon/system/fidl/fuchsia-sysmem/image_formats.fidl
index 84cff51..83414aa 100644
--- a/zircon/system/fidl/fuchsia-sysmem/image_formats.fidl
+++ b/zircon/system/fidl/fuchsia-sysmem/image_formats.fidl
@@ -50,6 +50,9 @@
// for this is (including any variants that are specified / permitted /
// indicated in-band / prohibited).
MJPEG = 106; // For UVC compliance.
+
+ // YUV only, 8 bits per Y sample
+ YV12 = 107;
};
// Describes how the pixels within an image are meant to be presented.
diff --git a/zircon/system/fidl/fuchsia-sysmem/usages.fidl b/zircon/system/fidl/fuchsia-sysmem/usages.fidl
index ef5fdf19d..e23a1cd 100644
--- a/zircon/system/fidl/fuchsia-sysmem/usages.fidl
+++ b/zircon/system/fidl/fuchsia-sysmem/usages.fidl
@@ -39,4 +39,6 @@
// TODO(ZX-2259): Add more specific HwDecoder flags if needed.
const uint32 videoUsageHwDecoder = 1;
const uint32 videoUsageHwEncoder = 2;
+// TODO(dustingreen): This bit is likely redundant with secure_required and
+// secure_permitted bool(s); this bit might be able to be removed.
const uint32 videoUsageHwProtected = 4;
diff --git a/zircon/system/ulib/image-format/image_format.cpp b/zircon/system/ulib/image-format/image_format.cpp
index 2659efa..edea916 100644
--- a/zircon/system/ulib/image-format/image_format.cpp
+++ b/zircon/system/ulib/image-format/image_format.cpp
@@ -53,6 +53,7 @@
// 8 bits RGB when uncompressed - in this context, MJPEG is essentially
// pretending to be uncompressed.
{fuchsia_sysmem_PixelFormatType_MJPEG, {{8}, kColorType_RGB}},
+ {fuchsia_sysmem_PixelFormatType_YV12, {{8}, kColorType_YUV}},
};
class ImageFormatSet {
@@ -182,6 +183,7 @@
case fuchsia_sysmem_PixelFormatType_M420:
case fuchsia_sysmem_PixelFormatType_NV12:
case fuchsia_sysmem_PixelFormatType_YUY2:
+ case fuchsia_sysmem_PixelFormatType_YV12:
return true;
}
return false;
@@ -203,6 +205,8 @@
return coded_height * bytes_per_row * 3 / 2;
case fuchsia_sysmem_PixelFormatType_YUY2:
return coded_height * bytes_per_row;
+ case fuchsia_sysmem_PixelFormatType_YV12:
+ return coded_height * bytes_per_row * 3 / 2;
default:
return 0u;
}
@@ -287,6 +291,8 @@
return 12u;
case fuchsia_sysmem_PixelFormatType_YUY2:
return 2u * 8u;
+ case fuchsia_sysmem_PixelFormatType_YV12:
+ return 12u;
}
ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format->type));
return 0u;
@@ -313,6 +319,8 @@
return 1u;
case fuchsia_sysmem_PixelFormatType_YUY2:
return 2u;
+ case fuchsia_sysmem_PixelFormatType_YV12:
+ return 1u;
}
ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format->type));
return 0u;
@@ -348,6 +356,8 @@
return 2u;
case fuchsia_sysmem_PixelFormatType_YUY2:
return 2u;
+ case fuchsia_sysmem_PixelFormatType_YV12:
+ return 2u;
}
ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format->type));
return 0u;
@@ -374,6 +384,8 @@
return 2u;
case fuchsia_sysmem_PixelFormatType_YUY2:
return 2u;
+ case fuchsia_sysmem_PixelFormatType_YV12:
+ return 2u;
}
ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format->type));
return 0u;
@@ -400,6 +412,8 @@
return 2u;
case fuchsia_sysmem_PixelFormatType_YUY2:
return 2u;
+ case fuchsia_sysmem_PixelFormatType_YV12:
+ return 2u;
}
ZX_PANIC("Unknown Pixel Format: %d", static_cast<int>(pixel_format->type));
return 0u;