// Copyright 2018 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. library fuchsia.mediacodec; // See codec.md for detailed interface documentation. The comments here are a // summary only. Client implementers should see codec.md for more detail on any // message that doesn't seem sufficiently-described here. Codec server // implementers should probably read codec.md before implementing. // Overview of operation: // // 1. Create // * create via CodecFactory - see CodecFactory // 2. Get input constraints // * OnInputConstraints() - sent unsolicited by codec shortly after codec // creation. // 3. Provide input buffers // * SetInputBufferSettings() / AddInputBuffer() // 4. Deliver input data // * 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 // OnInputConstraints() by some codecs, but a client must tolerate as late // as after substantial input data has been delivered including lots of // input packet recycling via OnFreeInputPacket(). // * This message can arrive more than once before the first output data. // 6. Provide output buffers // * SetOutputBufferSettings() / AddOutputBuffer() // 7. Data flows, with optional EndOfStream // * OnOutputPacket() / RecycleOutputPacket() / QueueInputPacket() / // OnFreeInputPacket() / QueueInputEndOfStream() / OnOutputEndOfStream() // // It's possible to re-use a Codec instance for another stream, and doing so // can sometimes skip over re-allocation of buffers. This can be a useful thing // to do for cases like seeking to a new location - at the Codec interface that // can look like switching to a new stream. // CodecBufferConstraints // // This struct helps ensure that packet count and buffer space are sufficient // to avoid major problems. For example, a video decoder needs sufficient // video frame buffers to hold all potential reference frames concurrently + // one more video buffer to decode into. Else, the whole video decode pipe can // easily deadlock. // // The secondary purpose of this struct is to help ensure that packet count and // buffer space are sufficient to achieve reasonably performant operation. // // There are separate instances of this struct for codec input and codec output. struct CodecBufferConstraints { // 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 // constraints sent by the server. The server won't emit output data until // the client has configured output settings and buffers with a // buffer_constraints_version_ordinal >= the latest // buffer_constraints_version_ordinal that had // buffer_constraints_action_required true. See // buffer_constraints_action_required comments for more. // // A buffer_constraints_version_ordinal of 0 is not permitted, to simplify // initial state handling. Other than 0, both odd and even version ordinals // are allowed (in constrast to the stream_lifetime_ordinal, neither the // client nor server ever has a reason to consider the latest version to be // stale, so there would be no benefit to disallowing even values). uint64 buffer_constraints_version_ordinal; // default_settings // // These settings are "default" settings, not "recommended" settings. // // These "default" settings can be passed to SetInputBufferSettings() / // SetOutputBufferSettings() as-is without modification, but a client doing // that must still obey the semantics of packet_count_for_client, despite the // codec server not having any way to really know the proper setting for // that field. // // For CodecBufferConstraints fields whose names end in "recommended", the // default_settings will have the corresponding setting field set to that // recommended value. // // The codec promises that these default settings as-is (except for // buffer_lifetime_ordinal) are guaranteed to // satisfy the constraints indicated by the other fields of // CodecBufferConstraints. While client-side checking that these // settings are within the constraints is likely unnecessary in the client, // the client should still check that these values are within client-side // reasonable-ness bounds before using these values, to avoid letting a codec // server cause problems for the client. // // This structure will always have single_buffer_mode false. See // single_buffer_mode_allowed for whether single_buffer_mode true is allowed. // // The client must set the buffer_lifetime_ordinal field to a proper value // before sending back to the server. The 0 initially in this field will be // rejected by the server if sent back as-is. See comments on // CodecPortBufferSettings.buffer_lifetime_ordinal. CodecPortBufferSettings default_settings; // For uncompresesd video, separate and complete frames, each in its own // separate buffer (buffer-per-packet mode), is always a requirement. // per_packet_buffer_bytes.*: // // These per-packet buffer bytes constraints apply to both buffer-per-packet // mode and single-buffer mode (see single_buffer_mode). If // buffer-per-packet mode, the constraints apply to each buffer separately. // If single-buffer mode, the constraints need to be multiplied by the number // of packets to determine the constraints on the single buffer. // per_packet_buffer_bytes_min: // // If a client is using buffer per packet mode, each buffer must be at least // this large. If a client is using single-buffer mode, the one buffer must // be at least per_packet_buffer_bytes_min * packet_count_for_codec_min in // size. uint32 per_packet_buffer_bytes_min; // Must be >= per_packet_buffer_bytes_min. Delivering more than // this per input packet might not perform any better, and in fact might // perform worse. uint32 per_packet_buffer_bytes_recommended; // Must be >= per_packet_buffer_bytes_recommended. Can be 0xFFFFFFFF if there // is no explicitly-enforced limit. uint32 per_packet_buffer_bytes_max; // Minimum number of packet_count_for_codec. // // Re. input and output: // // This is a strict min for packet_count_for_codec, but a client can use more // packets overall if the client wants to, by using a larger value for // packet_count_for_codec and/or using a non-zero packets_for_client. A good // reason to do the former would be if the client might tend to deliver a few // not-very-full buffers occasionally - or to have a few extra packets within // which to satisfy codec_input_bytes_min. A good reason to do the latter // would be if a client needs to hold onto some packets for any "extra" // duration. // // If a client specifies a larger packet_count_for_codec value than // packet_count_for_codec_min, a server is permitted (but not encouraged) to // not make progress until packet_count_for_codec are with the server, // not merely packet_count_for_codec_min. // // For decoder input and audio encoder input: The packet_count_for_codec_min // may or may not contain enough data to allow the codec to make progress // without copying into an internal side buffer. If there isn't enough data // delivered in packet_count_for_codec_min packets to permit progress, the // codec must copy into its own side buffer internally to make progress. // // If a client intends to use extra packets for client-side purposes, the // client should specify the extra packets in packets_for_client instead of // packet_count_for_codec, but packet_count_for_codec must still be >= // packet_count_for_codec_min. // // See codec.md for more on packet_count_for_codec_min. uint32 packet_count_for_codec_min; // This must be at least packet_count_for_codec_min and at most // packet_count_for_codec_recommended_max. // // This value is likely to be used as-is by most clients, so if having one // additional packet is a big performance win in a large percentage of // scenarios, it can be good for the server to include that additional packet // in this value. uint32 packet_count_for_codec_recommended; // This can be the same as packet_count_for_codec_max or can be lower. // Values above this value and <= packet_count_for_codec_max are not // recommended by the codec, but should still work given sufficient resoures // available to both the client and the codec. uint32 packet_count_for_codec_recommended_max; // This can be 0xFFFFFFFF if there's no codec-enforced max, but codecs are // encouraged to set a large but still plausibly-workable max, and clients // are encouraged to request a number of packets that isn't excessively large // for the client's scenario. uint32 packet_count_for_codec_max; // Normally this would be an implicit 0, but for now we have a min so we can // force the total number of packets to be a specific number that we know // works for the moment. uint32 packet_count_for_client_min; // packet_count_for_client_max // // The client must set packet_count_for_client to be <= // packet_count_for_client_max. // // This value must be at least 1. This can be 0xFFFFFFFF if there's no // codec-enforced max. Clients are encouraged to request a number of // packets that isn't excessively large for the client's scenario. uint32 packet_count_for_client_max; // single_buffer_mode_allowed false allows a codec that's not required to // support single-buffer mode for a given input or output the ability to // decline to support single-buffer mode on that input/output. // // All encoder output, regardless of audio or video: server support for // single-buffer mode is optional. Please see codec.md for all the rules // regarding single-buffer mode on output before using single-buffer mode on // an output. // // Audio decoder output: server support for single-buffer mode is required. // // Video decoder output: There is little reason for a video decoder to // support single-buffer mode on output. Nearly all video decoders will set // this to false for their output. // // All decoder inputs: Servers must support single-buffer mode on input. The // client is responsible for managing the input buffer space such that // filling an input packet doesn't over-write any portion of an input packet // already in flight to the codec. // // Encoder inputs: Server support for single-buffer mode on encoder input is // optional. This is more often useful for audio than for video. // // Support for buffer-per-packet mode is always required on both input and // output, regardless of codec type. 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(). 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 Codec client, but until the // BufferAllocator can be reached from a driver, this is to grant the client // special powers it really shouldn't have, very temporarily until // BufferAllocator is hooked up properly at which point this can be removed. // Strictly speaking we could reverse which end allocates buffers in the Codec // interface to avoid this hack even before BufferAllocator, but the overall // path seems shorter if we jump directly from this to using BufferAllocator. handle? very_temp_kludge_bti_handle; }; // CodecOutputConfig // // The codec-controlled output configuration, including both // CodecBufferConstraints for the output and CodecFormatDetails for the output. // // TODO(dustingreen): Need a better name for this struct, but still short // hopefully. It's stuff the codec gets to control, not the client. 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 specifiy for output of an encoder. struct CodecOutputConfig { // A client which always immediately re-configures output buffers on reciept // of OnOutputConfig() with buffer_constraints_action_required true can safely // ignore this field. // // A client is permitted to ignore an OnOutputConfig() 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 // buffer_constraints_action_required true, if the the most recent completed // server-side output config isn't what the server wants/needs yet for the // new stream. uint64 stream_lifetime_ordinal; // buffer_constraints_action_required // // When the buffer constraints are delivered, they indicate whether action is // required. A false value here permits delivery of constraints which are // fresher without forcing a buffer reconfiguration. If this is false, a // client cannot assume that it's safe to immediately re-configure output // buffers. If this is true, the client can assume it's safe to immediately // configure output buffers once. // // A client is permitted to ignore buffer constraint versions which have // buffer_constraints_action_required false. The server is not permitted to // change buffer_constraints_action_required from false to true for the same // buffer_constraints_version_ordinal. // // For each configuration, a client must use new buffers, never buffers that // were prevoiusly used for anything else, and never buffers previously used // for any other Codec purposes. This rule exists for multiple good reasons, // relevant to both mid-stream changes, and changes on stream boundaries. // A client should just use new buffers each time. // // When this is true, the server has already de-refed as many low-level // output buffers as the server can while still performing efficient // transition to the new buffers and will de-ref the rest asap. A Sync() is // not necessary to achieve non-overlap of resource usage to the extent // efficiently permitted by the formats involved. // // If buffer_constraints_action_required is true, the server _must_ not // deliver more output data until after output buffers have been configured // (or re-configured) by the client. // // See codec.md for more on buffer_constraints_action_required. bool buffer_constraints_action_required; CodecBufferConstraints buffer_constraints; // format_details // // It's up to the client to determine if a change in // format_details.format_details_version_ordinal implies any client action is // required, based on particular fields in format_details vs. any old value. // The server guarantees that if the format has changed, then // format_details.format_details_version_ordinal will change, but a change to // format_details.format_details_version_ordinal does not guarantee that the // format details actually changed. Servers are strongly encouraged to not // change format_details.format_details_version_ordinal other than before the // first output data of a stream unless there is a real mid-stream format // change in the stream. Unnecessary mid-stream format changes can cause // simpler clients that have no need to handle mid-stream format changes to // just close the channel. Format changes before the first output data of a // stream are not "mid-stream" in this context - those can be useful for // stream format detection / setup reasons. // // Note that in case output buffers don't really need to be re-configured // despite a format change, a server is encouraged, but not required, to // set buffer_constraints_action_required false on the message that conveys // the new format details. Simpler servers may just treat the whole output // situation as one big thing and demand output buffer reconfiguration on any // change in the output situation. // // A client may or may not actually handle a new buffer_constraints with // buffer_constraints_action_required false, but the client should always // track the latest format_details. // // An updated format_details is ordered with respect to emitted output // packets, and applies to all subsequent packets until the next // format_details with larger version_ordinal. A simple client that does not // intend to handle mid-stream format changes should still keep track of the // most recently received format_details until the first output packet // arrives, then lock down the format details, handle those format details, // and verify that any format_details.format_details_version_ordinal received // from the server is the same as the locked-down format_details, until the // client is done with the stream. Even such a simple client must tolerate // format_details.format_details_version_ordinal changing multiple times // before the start of data output from a stream (any stream - the first // stream or a subsequent stream). This allows a codec to request that // output buffers and output format be configured speculatively, and for the // output config to be optionally adjusted by the server before the first // data output from a stream after the server knows everything it needs to // know to fully establish the initial output format details. This // simplifies codec server implementation, and allows a clever codec server // to guess it's output config for lower latency before any input data, while // still being able to fix the output config (including format details) if // the guess turns out to be wrong. // // Whether the format_details.format_details_version_ordinal will actually // change mid-stream is a per-codec and per-stream detail that is not // specified in comments here, and in most cases also depends on whether the // format changes on the input to the codec. Probably it'll be fairly common // for a client to use a format which 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. CodecFormatDetails format_details; }; // Default values for input and output // CodecBufferConstraints.default_settings.packet_count_for_codec. // // These are defined as "const" in FIDL to avoid all server implementations // needing to separately define their own values, and these should be // reasonable as default values, but strictly speaking this is not intended to // promise that this value won't change from build to build. If a client cares // about a specific number, the client should separately define what that // number is and ensure that CodecPortBufferSettings.packet_count_for_client is // at least large enough. // // In contrast to packet_count_for_client, the packet_count_for_codec is much // more codec-specific, so this file has no numbers for that - each codec will // set those as appropriate for the specific codec. // // These are not "recommended" values, only "default" values, in the sense that // the codec doesn't really know what the correct setting for these values is // for a given client, and if the default is not appropriate for a client, // large problems could result such as deadlock. See the comments on // packet_count_for_client. // // Despite these defaults, every client should ideally care about the // packet_count_for_client setting and should ensure that the setting is at // least large enough to cover the number of packets the client might ever need // to camp on for any non-transient duration concurrently. The defaults are // only intended to be plausible for some clients, not all clients. // // One for the client to be filling and one in transit. const uint32 kDefaultInputPacketCountForClient = 2; // One for the client to be rendering, and one in transit. const uint32 kDefaultOutputPacketCountForClient = 2; // For input, this is the default on a fairly arbitrary basis. // // TODO(dustingreen): Do we want the default for audio encoding to be // single_buffer_mode true instead? If so, we may split this up by audio/video // encoder/decoder. const bool kDefaultInputIsSingleBufferMode = false; const bool kDefaultOutputIsSingleBufferMode = false; // CodecPortBufferSettings // // See relevant corresponding constraints in CodecBufferConstraints. The // settings must satisfy the constraints. // // The client informs the codec of these settings and then separately informs // the codec of each buffer. // // TODO(dustingreen): Rename from CodecPortBufferSettings to CodecBufferSettings // in a separate CL (after some other stuff is out of the way first). struct CodecPortBufferSettings { // 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 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 codec.md for more on buffer_lifetime_ordinal. 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 codec.md for more on buffer_constraints_version_ordinal. uint64 buffer_constraints_version_ordinal; // The total packet count is split into two pieces to disambiguate how many // packets are allocated for the client to hold onto for whatever reason, vs. // how many packets are allocated for the server to hold onto for whatever // reason. // // Extra packets to provide slack for peformance reasons can be in either // category, but typically packet_count_for_codec_recommended will already // include any performance-relevant slack for the server's benefit. // packet_count_for_codec // // How many packets the client is allocating for the codec server's use. // This must be >= CodecBufferConstraints.packet_count_for_codec_min. If // constraints change such that this would no longer be true, the server will // send an OnOutputConfig() event. // // The codec server is allowed to demand that all of packet_count_for_codec // become free before making further progress, even if // packet_count_for_codec is > packet_count_for_codec_min. // // A reasonable value for this is // CodecBufferConstraints.packet_count_for_codec_recommended. // // See codec.md for more on packet_count_for_codec. uint32 packet_count_for_codec; // packet_count_for_client // // This must be at least 1. The server will close the channel if this is 0. // // How many packets the client is allocating for the client's use. The // client may hold onto this many packets for arbitrarily-long duration // without handing these packets to the codec, and despite doing so, the // codec will continue to make progress and function normally without getting // stuck. The client holding onto additional packets transiently is ok, but // the client needs to hand those additional packets back to the codec // eventually if the client wants the codec to make further progress. // // In addition to this value needing to include at least as many packets as // the client ever intends to concurrently camp on indefinitely, any extra // slack to benefit client-side performance should also be included here. // // A typical value for this could be at least 2, but it depends strongly on // client implementation and overall client buffering goals. It is up to the // client to determine how many packets are needed in this category by any // parts of the overall system that will be holding onto packets for any // reason. Those parts of the system should have a documented and possibly // queryable defined value to help determine this number. Setting this value // lower than it actaully needs to be can result in the codec not making // progress as it sits waiting for packets, with the client unable to recycle // any more packets to the codec. That situation can be difficult to // diagnose, while excessively-large values here are wasteful, so care is // warranted to set this value properly. uint32 packet_count_for_client; // per_packet_buffer_bytes // // In buffer-per-packet mode, we require that each buffer have usable bytes // equal to per_packet_buffer_bytes. Use of differently-sized low-level // buffers is possible, but the size of the portion used via the Codec // interface per CodecBuffer must be the same for all the buffers. // // In single-buffer mode, we require the portion of the low-level buffer used // via the Codec interface to be size (packet_count_for_codec + // packet_count_for_client) * per_packet_buffer_bytes. // // TODO(dustingreen): determine if we need to relax these restrictions a bit // for convenience when using gralloc video buffers. uint32 per_packet_buffer_bytes; // If true, there is only one buffer with index 0 which all packets // implicitly refer to. If false, the packet_index and buffer_index are 1:1, // and each packet refers to its corresponding buffer. // // 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. bool single_buffer_mode; }; // CodecBuffer // // The CodecBuffer struct represents a pre-configured buffer. // // Both input and output uses CodecBuffer(s), but the two sets of buffers are // separate. // // The client uses SetInputBufferSettings() + AddInputBuffer() * N to inform // the codec about all the input buffers. // // The client uses SetOutputBufferSettings() + AddOutputBuffer() * N to inform // the codec about all the output buffers. // // When single_buffer_mode is true, there is only buffer_index 0 shared by all // CodecPacket(s) of the relevant input or output. When single_buffer_mode is // false, the buffer_index equals the packet_index. struct CodecBuffer { // When using AddOutputBuffer()/AddInputBuffer(), this must match the // buffer_lifetime_ordinal of the most recent // SetOutputBufferSettings()/SetInputBufferSettings(). uint64 buffer_lifetime_ordinal; // Buffers must be added via AddOutputBuffer() / AddInputBuffer() in order by // buffer_index, and the buffer_index is always equal to 0 or equal to the // packet_index (depending on single_buffer_mode true or false), but it's // still nice to have CodecBuffer include the buffer_index if only for easier // debugging. uint32 buffer_index; // For each new buffer_lifetime_ordinal, a client must use new low-level // buffers. This rule exists for multiple very good reasons, and is relevant // to mid-stream changes, changes on stream boundaries, and both input and // output buffers. A new buffer_lifetime_ordinal needs new low-level // buffers, not just new CodecBuffer(s). If you find yourself copying // compressed input data into new low-level input buffers solely due to this // rule, consider asking the source of the data for the ability to directly // fill new VMOs. The rule exists for good reasons, even for input buffers. // // The previous paragraph does not prohibit carving up VMOs into sub-pieces // and using different sub-pieces as different CodecBuffer(s), with some VMOs // used for more than one CodecBuffer and possibly others used for only one // CodecBuffer. While this is permitted and enables some optimizations, it's // not expected to be particularly common. // // See codec.md for more on CodecBufferData, and more on why we never re-use // the same low-level buffers for different buffer_lifetime_ordinal(s). CodecBufferData data; }; // CodecBufferData // // 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 CodecBufferData { CodecBufferDataVmo vmo; // TODO(dustingreen): add the gralloc way }; // CodecBufferDataVmo // // Details for a buffer backed by a VMO. struct CodecBufferDataVmo { // The same VMO can be used by more than one CodecBuffer (only of the same // buffer_lifetime_ordinal), but each vmo_handle must be a separate handle. handle<vmo> vmo_handle; // Offset within the VMO of the first usable byte. Must be < the VMO's size // in bytes. uint64 vmo_usable_start; // VMO-relative offset that's one past the last usable byte. This can point // one byte beyond the end of the VMO if desired. In other words, this can // be equal to the VMO's size, to indicate that the last byte of the VMO is // usable (and possibly many byte before that, depending on // vmo_usable_start). uint64 vmo_usable_size; }; // CodecPacketHeader // // When referring to a free packet, we use CodecPacketHeader alone instead of // CodecPacket, since while a packet is free it doesn't really have meaningful // offset or length etc. // // A populated CodecPacket also has a CodecPacketHeader. struct CodecPacketHeader { // This is which buffer configuration lifetime this header is referring to. // // A packet_index is only really meaningful with respect to a particular // buffer_lifetime_ordinal. // // See CodecPortBufferSettings.buffer_lifetime_ordinal. // // For QueueInputPacket(), a server receiving a buffer_lifetime_ordinal that // isn't the current input buffer_lifetime_ordinal will close the channel. // // For OnFreeInputPacket() and RecycleOutputPacket(), the reciever (client or // server) must ignore a message with stale buffer_lifetime_ordinal. uint64 buffer_lifetime_ordinal; // The overall set of packet_index values is densely packed from 0..count-1 // for input and output separately. They can be queued in any order. // // Both the client and server should validate the packet_index against the // known bound and disconnect if it's out of bounds. // // When running in buffer-per-packet mode, the packet_index is also the // buffer index. When running in single-buffer mode, the buffer index is // always 0 referring to the single buffer. // // The packet_index values don't imply anything about order of use of // packets. The client should not expect the ordering to remain the same over // time - the codec is free to hold on to an input or output packet for a // while during which other packet_index values may be used multiple times. // // For a given properly-functioning Codec instance, packet_index values will // be unique among concurrently-outstanding packets. Servers should validate // that a client isn't double-using a packet and clients should validate as // necessary to avoid undefined or unexpected client behavior. uint32 packet_index; }; // CodecPacket // // A CodecPacket represents a chunk of input or output data to or from a codec. // // codec output: // While the CodecPacket is outstanding with the client via OnOutputPacket(), // the codec will avoid modifying the referenced output data. After the client // calls RecycleOutputPacket(packet_index), the codec is notified that the // client is again ok with the referenced data changing. // // codec input: // The client initially has all packet_index(es) available to fill, and later // gets packet_index(s) that are again ready to fill via OnFreeInputPacket(). // The client must not modify the referenced data in between QueueInputPacket() // and OnFreeInputPacket(). struct CodecPacket { CodecPacketHeader header; // Which buffer this packet refers to. For single-buffer mode this will // always be 0, but for multi-buffer mode, a given in-flight interval of a // packet can refer to any buffer. The packet has an associated buffer only // while the packet is in-flight, not while the packet is free. // // The default value makes accidental inappropriate use of index 0 less // 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). uint32 buffer_index = 0x80000000; // stream_lifetime_ordinal // // The value 1 is the lowest permitted value after codec creation. Values // sent by the client must be odd. Values must only increase. // // A stream_lifetime_ordinal represents the lifetime of a stream. All // messages that are specific to a stream have the stream_lifetime_ordinal // value and the value is the same for all messages relating to a given // stream. // // See codec.md for more on stream_lifetime_ordinal. uint64 stream_lifetime_ordinal; // start_offset and valid_length_bytes // // Which part of the relevant buffer is this packet using. These are valid // for input data that's in-flight to the codec, and are valid for output data // from the codec. // // For compressed formats and uncompressed audio, the data in // [start_offset, start_offset + valid_length_bytes) is the contiguously valid // data refered to by this packet. // // For uncompressed video frames, CodecFormatDetails is the primary means of // determining which bytes are relevant. The offsets in CodecFormatDetails // are relative to the start_offset here. The valid_length_bytes must be // large enough to include the full last line of pixel data, including the // full line stride of the last line (not just the width in pixels of the // last line). // // Despite these being filled out, some uncompressed video buffers are of // types that are not readable by the CPU. These fields being here don't // imply there's any way for the CPU to read an uncompressed frame. // // TODO(dustingreen): Do we have any reason to require that these be filled // out for opaque uncompressed video frames that the CPU can't read? In that // case do we want to require them just so they can be potentially passed on // to a HW renderer in case the HW renderer has any use for them? Or more // likely, it may just be that these tend to refer to the whole size of an // uncompressed buffer, with format_details taking care of specifying which // bytes are actually relevant. uint32 start_offset; // valid_length_bytes // // This must be > 0. // // The semantics for valid data per packet vary depending on data type as // follows. // // uncompressed video - A video frame can't be split across packets. Each // packet is one video frame. // // uncompressed audio - Regardless of float or int, linear or uLaw, or number // of channels, a packet must contain an non-negative number of complete // audio frames, where a single audio frame consists of data for all the // channels for the same single point in time. Any codec-specific internal // details re. lower rate sampling for LFE channel or the like should be // hidden by the Codec server implementation. // // compressed data input - A packet must contain at least one byte of data. // See also codec_input_bytes_min. Splitting AUs at arbitrary byte // boundaries is permitted, including at boundaries that are in AU headers. // // compressed data output - The codec is not required to fully fill each // output packet's buffer. uint32 valid_length_bytes; // This value is not strictly speaking a timestamp. It is an arbitrary // unsigned 64-bit number that, under some circumstances, will be passed by a // codec unmodified from an input packet to the exactly-corresponding output // packet. // // For timestamp_ish values to be propagated from input to output the // following conditions must be true: // * promise_separate_access_units_on_input must be true // * has_timestamp_ish must be true for a given input packet, to have that // timestamp_ish value (potentially) propagate through to an output // * the Codec instance itself decides (async) that the input packet // generates an output packet - if a given input never generates an output // packet then the timestamp_ish value on the input will never show up on // any output packet - depending on the characteristics of the input and // output formats, and whether a decoder is willing to join mid-stream, etc // this can be more or less likely to occur, but clients should be written // to accomodate timestamp_ish values that are fed on input but never show // up on output, at least to a reasonable degree (not crashing, not // treating as an error). // // See codec.md for more on timestamp_ish. bool has_timestamp_ish; uint64 timestamp_ish; // start_access_unit // // If promise_separate_access_units_on_input (TODO(dustingreen): or any // similar mode for output) is true, this bool must be set appropriately // depending on whether byte 0 _is_ or _is not_ the start of an access unit. // The client is required to know, and required to set this boolean properly. // The server is allowed to infer that when this boolean is false, byte 0 is // the first byte of a continuation of a previously-started AU. (The byte at // start_offset is "byte 0".) // // If promise_separate_access_units_on_input is false, this boolean is // ignored. bool start_access_unit; // known_end_access_unit // // A client is never required to set this boolean to true. // // If promise_separate_access_units_on_input is true, for input data, this // boolean must be false if the last byte of this packet is not the last byte // of an AU, and this boolean _may_ be true if the last byte of this packet // is the last byte of an AU. A client delivering one AU at a time that's // interested in the lowest possible latency via the decoder should set this // boolean to true when it can be set to true. // // If promise_separate_access_units_on_input is false, this boolean is // ignored. bool known_end_access_unit; }; // Codec // // The Codec interface exists to anchor the configuration of input and output // buffers, and depending on Codec server hosting strategy, the Codec inteface // can, in some configurations, be 1:1 with a codec isolate (process), // especially when using SW codecs. The Codec can be used to process up to one // stream at a time. // // Descriptions of actions taken by methods of this interface and the states of // things are given as if the methods are synchronously executed by the codec // server, but in reality, as is typical of FIDL interfaces, the message // processing is async. The states described are to be read as the state from // the client's point of view unless otherwise stated. Events coming back from // the server are of course delivered async, and a client that processes more // than one stream per Codec instance needs to care whether a given event is // from the current stream vs. some older soon-to-be-gone stream. // // The Sync() method's main purpose is to enable the client to robustly prevent // having both old and new buffers allocated in the system at the same time, // since media buffers can be significantly large, depending. The Sync() method // achieves this by only delivering it's response when all previous calls to // the Codec interface have actually taken effect in the StreamControl ordering // domain. Sync() can also be used to wait for the codec server to catch up if // there's a possibility that a client might otherwise get too far ahead of the // Codec server, by for example requesting creation of a large number of // streams in a row. It can also be used during debugging to ensure that a // codec server hasn't gotten stuck. Calling Sync() is entirely optional and // never required for correctness - only potentially required to de-overlap // resource usage. // // Semi-trusted Codec server - SW decoders run in an isolate (with very few // capabilities) just in case the decoding SW has a vulnerability which could // be used to take over the Codec server. Clients of the codec interface using // decoders and processing streams of separate security contexts, to a greater // extent than some other interfaces, need to protect themselves against // invalid server behavior, such as double-free of a packet_index and any other // invalid server behavior. Having fed in compressed data of one security // context, don't place too much trust in a single Codec instance to not mix // data among any buffers that Codec server has ever been told about. Instead, // create separate Codec instances for use by security-separate client-side // contexts. While the picture for HW-based decoders looks somewhat different // and is out of scope of this paragraph, the client should always use separate // Codec instances for security-separate client-side contexts. interface Codec { // EnableOnStreamFailed() // // Permit the server to use OnStreamFailed() instead of the server just // closing the whole Codec channel on stream failure. // // If the server hasn't seen this message by the time a stream fails, the // server will close the Codec channel instead of sending OnStreamFailed(). 1: EnableOnStreamFailed(); // OnStreamFailed() // // The stream has failed, but the Codec instance is still usable for a new // stream. // // This message is only ever sent by the server if the client previously sent // EnableOnStreamFailed(). If the client didn't send EnableOnStreamFailed() // then the server closes the Codec channel instead. // // Codec server implementations are encouraged to handle stream errors (and // ideally to also report them via error_ bools of OnOutputPacket() and // OnOutputEndOfStream()) without failing the whole stream, but if a codec // server is unable to do that, but still can cleanly contain the failure to // the stream, the codec server can (assuming EnableOnStreamFailed() was // called) use OnStreamFailed() to indicate the stream failure to the client // without closing the Codec channel. // // An ideal Codec server handles problems with input data without sending // this message, but sending this message is preferred vs. closing the server // end of the Codec channel if the Codec server can 100% reliably contain the // stream failure to the stream, without any adverse impact to any later // stream. // // No further messages will arrive from the server regarding the failed // stream. This includes any OnOutputEndOfStream() that the client would // have otherwise expected. // // TODO(dustingreen): Add at least an error_message string and _maybe_ a // zx_status_t, though that might tend to encourage mis-use of zx_status_t so // maybe just error_message for quicker debugging on the client side. Also // plumb from CodecAdapterH264 and similar. 2: -> OnStreamFailed(uint64 stream_lifetime_ordinal); // OnInputConstraints() // // The server sends this shortly after Codec creation to indicate input // buffer constraints. The "min" and "max" input constraints don't change // for the life of the Codec. // // The "max" values for buffer size and count are large enough to support the // most demanding format the server supports on input. The "recommended" // values should be workable for use with the input CodecFormatDetails // conveyed during Codec creation. The "recommended" values are not // necessarily suitable if the client uses QueueInputFormatDetails() to // change the input format. In that case it's up to the client to determine // suitable values, either by creating a new Codec instance instead, or // knowing suitable values outside the scope of this protocol. // // See comments on CodecBufferConstraints. // // This message is guaranteed to be sent unsolicited to the Codec client // during or shortly after Codec creation. Clients should not depend on this // being the very first message to arrive at the client. OnOutputConfig() // may be sent first by some codecs that already know their initial output // config without any input data, to encourage (but not stictly 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 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 more details. // // The "min" and "max" input constraints are guaranteed not to change for a // given Codec instance. The "recommended" values may effectively change // when the server processes QueueInputFormatDetails(). There is not any way // in the protocol short of creating a new Codec instance for the client to // get those new "recommended" values. // // TODO(dustingreen): Maybe provide a way for the client to get updated // "recommended" values for input, maybe only on request rather than via this // event, to keep things simpler for simpler clients. Maybe separate the // recommendations from the constraints. 3: -> OnInputConstraints(CodecBufferConstraints input_constraints); // SetInputBufferSettings() and AddInputBuffer() // // Configuring input buffers consists of calling SetInputBufferSettings() // followed by a number of calls to AddInputBuffer() equal to the number of // buffers set via SetInputBufferSettings(). In buffer-per-packet mode, this // is the same as the number of packets. In single-buffer mode, this is 1. // // After OnInputConstraints(), the client uses these two methods to set up // input buffers and packets. // // Configuring input buffers is required before QueueInputPacket(). // // The client can also re-set-up input buffers any time there is no current // stream. The client need not wait until all previously-set-up input // buffers are with the client via OnFreeInputPacket(). The old // buffer_lifetime_ordinal just ends. See codec.md for more info on "buffer // lifetime". // // The recommended way to de-overlap resource usage (when/if the client wants // to) is to send CloseCurrentStream() with release_input_buffers true then // send Sync() and wait for its response before allocating any new buffers. // How to cause other parts of the system to release their references on // low-level buffers is outside the scope of this interface. // // This call ends any previous buffer_lifetime_ordinal, and starts a new one. 4: SetInputBufferSettings(CodecPortBufferSettings input_settings); // The client is required to add all the input buffers before sending any // message that starts a new stream else the codec will close the Codec // channel. // // When the last buffer is added with this message, all the input packets // effectively jump from non-existent to free with the client. The Codec // will not generate an OnFreeInputPacket() for each new input packet. The // client can immediately start sending QueueInputPacket() after sending the // last AddInputBuffer(). 5: AddInputBuffer(CodecBuffer buffer); // OnOutputConfig() // // 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 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. // // 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. // // For more on OnOutputConfig(), see cocec.md. 6: -> OnOutputConfig(CodecOutputConfig output_config); // SetOutputBufferSettings() and AddOutputBuffer() // // These are not permitted until after the first OnOutputConfig(). // // Roughly speaking, these messages are sent in response to OnOutputConfig() // with buffer_constraints_action_required true. // // Configuring output buffers consists of calling SetOutputBufferSettings() // followed by a number of calls to AddOutputBuffer() equal to the number of // buffers set via SetOutputBufferSettings(). In buffer-per-packet 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 // 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 // _permitted_ any time there is no current stream. // // For more on SetOutputBufferSettings() and AddOutputBuffer(), see codec.md. 7: SetOutputBufferSettings(CodecPortBufferSettings output_settings); 8: AddOutputBuffer(CodecBuffer buffer); // FlushEndOfStreamAndCloseStream() // // This message is optional. // // This message is only valid after QueueInputEndOfStream() for this stream. // The stream_lifetime_ordinal input parameter must match the // stream_lifetime_ordinal of the QueueInputEndOfStream(), else the server // will close the channel. // // A client can use this message to flush through (not discard) the last // input data of a stream so that the codec server generates corresponding // output data for all the input data before the server moves on to the next // stream, without forcing the client to wait for OnOutputEndOfStream() // before queueing data of another stream. // // The difference between QueueInputEndOfStream() and // FlushEndOfStreamAndCloseStream(): QueueInputEndOfStream() is a promise // from the client that there will not be any more input data for the stream // (and this info is needed by some codecs for the codec to ever emit the very // last output data). The QueueInputEndOfStream() having been sent doesn't // prevent the client from later completely discarding the rest of the // current stream by closing the current stream (with or without a stream // switch). In contrast, FlushEndOfStreamAndCloseStream() is a request from // the client that all the previously-queued input data be processed // including the logical "EndOfStream" showing up as OnOutputEndOfStream() // (in success case) before moving on to any newer stream - this essentially // changes the close-stream handling from discard to flush-through for this // stream only. // // A client using this message can start providing input data for a new // stream without that causing discard of old stream data. That's the purpose // of this message - to allow a client to flush through (not discard) the old // stream's last data (instead of the default when closing or switching // streams which is discard). // // 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 // done. The client will know the stream_lifetime_ordinal is done when // OnOutputEndOfStream(), OnStreamFailed(), or the Codec channel closes. // // For more on FlushEndOfStreamAndCloseStream(), see codec.md. 9: FlushEndOfStreamAndCloseStream(uint64 stream_lifetime_ordinal); // CloseCurrentStream() // // This "closes" the current stream, leaving no current stream. In addition, // this message can optionally release input buffers or output buffers. // // If there has never been any active stream, the stream_lifetime_ordinal must // be zero or the server will close the channel. If there has been an active // stream, the stream_lifetime_ordinal must be the most recent active stream // whether that stream is still active or not. Else the server will close the // channel. // // Multiple of this message without any new active stream in between is not // to be considered an error, which allows a client to use this message to // close the current stream to stop wasting processing power on a stream the // user no longer cares about, then later decide that buffers should be // released and send this message again with release_input_buffers and/or // release_output_buffers true to get the buffers released, if the client is // interested in trying to avoid overlap in resource usage between old // buffers and new buffers (not all clients are). // // See also Sync(). // // For more on CloseCurrentStream(), see codec.md. 10: CloseCurrentStream( uint64 stream_lifetime_ordinal, bool release_input_buffers, bool release_output_buffers); // Sync() -> () // // On completion, all previous Codec calls have done what they're going to do // server-side, _except_ for processing of data queued using // QueueInputPacket(). // // The main purpose of this call is to enable the client to wait until // CloseCurrentStream() with release_input_buffers and/or // release_output_buffers set to true to take effect, before the client // allocates new buffers and re-sets-up input and/or output buffers. This // de-overlapping of resource usage can be worthwhile for media buffers which // can consume resource types whose overall pools aren't necessarily vast in // comparsion to resources consumed. Especially if a client is reconfiguring // buffers multiple times. // // Note that Sync() prior to allocating new media buffers is not alone // sufficient to achieve non-overlap of media buffer resource usage system // wide, but it can be a useful part of achieving that. // // The Sync() transits the Output ordering domain and the StreamControl // ordering domain, but not the InputData ordering domain. For more on // ordering domains see codec.md. // // This request can be used to avoid hitting kMaxInFlightStreams which is // presently 10. A client that stays <= 8 in-flight streams will comfortably // stay under the limit of 10. While the protocol permits repeated // SetInputBufferSettings() and the like, a client that spams the channel // can expect that the channel will just close if the server or the channel // itself gets too far behind. 11: Sync() -> (); // OnOutputPacket() // // This is how the codec emits an output packet to the codec client. // // Order is significant. // // The client should eventually call RecycleOutputPacket() (possibly after // switching streams multiple times), unless the buffer_lifetime_ordinal has // moved on. A stream change doesn't change which packets are busy with // the client vs. free with the server. See "packet lifetime" in codec.md for // more. // // The relevant buffer is buffer 0 if running in single-buffer mode, or the // buffer index is the same as packet_index if running in buffer-per-packet // mode. // // For low-level buffer types that support it, a Codec is free to emit an // output packet before the low-level buffer actually has any usable data in // the buffer, with the mechanism for signalling the presence of data // separate from the OnOutputPacket() message. For such low-level buffer // types, downstream consumers of data from the emitted packet must // participate in the low-level buffer signalling mechanism to know when it's // safe to consume the data. This is most likely to be relevant when using a // video decoder and gralloc-style buffers. // // The error_ bool(s) allow (but do not require) a Codec server to report // errors that happen during an AU or between AUs. // // The scope of error_detected_before starts at the end of the last delivered // output packet on this stream, or the start of stream if there were no // previous output packets on this stream. The scope ends at the start of // the output_packet. // // The error_detected_before bool is separate so that discontinuities can be // indicated separately from whether the current packet is damaged. // // The scope of error_detected_during is from the start to the end of this // output_packet. 12: -> OnOutputPacket( CodecPacket output_packet, bool error_detected_before, bool error_detected_during); // RecycleOutputPacket() // // After the client is done with an output packet, the client needs to tell // the codec that the output packet can be re-used for more output, via this // method. // // It's not permitted to recycle an output packet that's already free with the // codec server. It's permitted but discouraged for a client to recycle an // output packet that has been deallocated by an explicit or implicit output // buffer de-configuration(). See buffer_lifetime_ordinal for more on that. // A server must ignore any such stale RecycleOutputPacket() calls. // // For more on RecycleOutputPacket(), see codec.md. 13: RecycleOutputPacket(CodecPacketHeader available_output_packet); // OnOutputEndOfStream() // // After QueueInputEndOfStream() is sent by the Codec client, within a // reasonable duration the corresponding OnOutputEndOfStream() will be sent by // the Codec server. Similar to QueueInputEndOfStream(), // OnOutputEndOfStream() is sent a maximum of once per stream. // // No more stream data for this stream will be sent after this message. All // input data for this stream was processed. // // While a Codec client is not required to QueueInputEndOfStream() (unless // the client wants to use FlushEndOfStreamAndCloseStream()), if a Codec // server receives QueueInputEndOfStream(), and the client hasn't closed the // stream, the Codec server must generate a corresponding // OnOutputEndOfStream() if nothing went wrong, or must send // OnStreamFailed(), or must close the server end of the Codec channel. An // ideal Codec server would handle and report stream errors via the error_ // flags and complete stream processing without sending OnStreamFailed(), but // in any case, the above-listed options are the only ways that an // OnOutputEndOfStream() won't happen after QueueInputEndOfStream(). // // There will be no more OnOutputPacket() or OnOutputConfig() messages for // this stream_lifetime_ordinal after this message - if a server doesn't // follow this rule, a client should close the Codec channel. // // The error_detected_before bool has the same semantics as the // error_detected_before bool in OnOutputPacket(). 14: -> OnOutputEndOfStream( uint64 stream_lifetime_ordinal, bool error_detected_before); // // Stream specific messages: // // QueueInputFormatDetails() // // If the input format details are still the same as specified during Codec // creation, this message is unnecessary and does not need to be sent. // // If the stream doesn't exist yet, this message creates the stream. // // All servers must permit QueueInputFormatDetails() at the start of a stream // without failing, as long as the new format is supported by the Codec // instance. Technically this allows for a server to only support the exact // input format set during Codec creation, and that is by design. A client // that tries to switch formats and gets a Codec channel failure should try // again one more time with a fresh Codec instance created with CodecFactory // using the new input format during creation, before giving up. // // These format details override the format details // specified during codec creation for this stream only. The next stream will // default back to the format details set during codec creation. // // For now, QueueInputFormatDetails() sent mid-stream will fail the Codec // channel. Clients shouldn't do this for now. // // This message is permitted at the start of the first stream (just like at // the start of any stream). The format specified need not match what was // specified during codec creation, but if it doesn't match, the Codec channel // might close as described above. 15: QueueInputFormatDetails( uint64 stream_lifetime_ordinal, CodecFormatDetails format_details); // QueueInputPacket() // // This message queues input data to the codec for processing. // // 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 stream // without output buffers configured yet. // // The client must continue to deliver input data via this message even if the // codec has not yet generated the first OnOutputConfig(), and even if the // Codec 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(). // // For more on QueueInputPacket(), see codec.md. 16: QueueInputPacket(CodecPacket packet); // OnFreeInputPacket() // // The server sends this message when the codec is done consuming this packet // and the packet can be re-filled by the client. // // This is not sent for all packets when a new buffer_lifetime_ordinal starts // as in that case all the packets are initially free with the client. // // See comments on QueueInputBuffer() and "packet lifetime" in codec.md for // for description of buffer lifetime and packet lifetime. // // After receiving the available input buffer via this event, the codec // client can call later call QueueInputBuffer with appropriate offset and // length set. // // TODO(dustingreen): At the moment, there is no guarantee re. the order of // these messages with respect to the order of QueueInputPacket(), but at // least for decoders, it might be worthwhile to require that servers preserve // the order vs. QueueInputPacket(), to make it easier to feed input from a // ring buffer or similar. For audio encoders it might still make sense. For // video encoders probably not. 17: -> OnFreeInputPacket(CodecPacketHeader free_input_packet); // Inform the server that all QueueInputPacket() messages for this stream // have been sent. // // If the stream isn't closed first (by the client, or by OnStreamFailed(), or // Codec channel closing), there will later be a corresponding // OnOutputEndOfStream(). // // The corresponding OnOutputEndOfStream() message will be generated only if // the server finishes processing the stream before the server sees the // client close the stream (such as by starting a new stream). A way to // force the server to finish the stream before closing is to use // FlushEndOfStreamAndCloseStream() after QueueInputEndOfStream() before any // new stream. Another way to force the server to finish the stream before // closing is to wait for the OnOutputEndOfStream() before taking any action // that closes the stream. // // In addition to serving as an "EndOfStream" marker to make it obvious // client-side when all input data has been processed, if a client never // sends QueueInputEndOfStream(), no amount of waiting will necessarily // result in all input data getting processed through to the output. Some // codecs have some internally-delayed data which only gets pushed through by // additional input data _or_ by this EndOfStream marker. In that sense, // this message can be viewed as a flush-through at InputData domain level, // but the flush-through only takes effect if the codec even gets that far // before the stream is just closed at StreamControl domain level. This // message is not alone sufficient to act as an overall flush-through at // StreamControl level. For that, send this message first and then send // FlushEndOfStreamAndCloseStream() (at which point it becomes possible to // queue input data for a new stream without causing discard of this older // stream's data), or wait for the OnOutputEndOfStream() before closing the // current stream. // // If a client sends QueueInputPacket(), QueueInputFormatDetails(), // QueueInputEndOfStream() for this stream after the first // QueueInputEndOfStream() for this stream, a server should close the Codec // channel. 18: QueueInputEndOfStream(uint64 stream_lifetime_ordinal); };