// 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;

// CreateDecoder_Params
//
// Input parameters for creating a decoder (audio or video).
//
// TODO(dustingreen): Switch this to a FIDL table instead when possible.
struct CreateDecoder_Params {
  // Input mime type for a decoder.
  //
  // The recognized mime types for now:
  // audio/aac
  //   input_details.codec_oob_bytes must be an AudioSpecificConfig() as defined
  //   by AAC spec.
  // audio/aac-adts
  //   On a temporary basis, input_details.codec_oob_bytes must be an
  //   AudioSpecificConfig() extracted from the ADTS stream data by the client.
  //   This is temporary.  Later, codec_oob_bytes will be ignored and allowed to
  //   be null.  On a temporary basis, see
  //   make_AudioSpecificConfig_from_ADTS_header() for some self-contained
  //   conversion code.
  //   TODO(dustingreen): fix this up server side and make most of this
  //   paragraph go away, possibly by also parsing ADTS before it hits OMX
  //   since the parsing code there doesn't tolerate split ADTS headers.
  CodecFormatDetails input_details;

  // The settings below nail down more details.

  // This must be true in order for the client to be permitted to put a
  // timestamp on an input packet, which is in turn required to get any
  // timestamps on any output packets.
  //
  // It is always legal to provide separate Access Units (henceforth AUs) to a
  // decoder, but this boolean must be true for a decoder to accept and
  // propagate timestamp values.
  //
  // This must be true when creating a video encoder, or the CodecFactory
  // channel will close.
  bool promise_separate_access_units_on_input = false;

  // "require" fields:
  //
  // Specifying any of these "require" fields can result in failure to get a
  // Codec if there's no suitable codec.  None of these correspond to any
  // required features of a codec server.
  //
  // TODO(dustingreen): implement filtering codecs based on these fields.

  // Require that the selected codec be capable of accepting input where
  // AUs are not separated into separate packets.
  //
  // This does not imply that the decoder can find the start of the first AU;
  // for that see require_can_find_start.  This does not imply that the decoder
  // can re-sync on its own if the stream data is damaged; for that see
  // require_can_re_sync.
  //
  // If both promise_separate_access_units_on_input and
  // require_can_stream_bytes_input are true, the CodecFactory channel will
  // close.
  //
  // If this is false, the client must feed separate AUs on the fuchsia.ui.input.  This
  // must be false for a video encoder, and if true the CodecFactory channel
  // will close.
  //
  // Unless a client demands a decoder capable of taking concatenated AUs
  // (require_can_stream_bytes_input true), the client must feed a decoder
  // separate AUs.  This means the client cannot have parts of two separate AUs
  // in the same packet, unless require_can_stream_bytes_input is true.
  bool require_can_stream_bytes_input = false;

  // A decoder is allowed to be capable of streaming bytes but not capable of
  // searching for the start of the first usable AU.  To require both, set both
  // require_can_stream_bytes_input and require_can_find_start.  Setting
  // require_can_find_start without require_can_stream_bytes_input is invalid.
  //
  // With require_can_stream_bytes_input true but require_can_find_start false,
  // the client must start the first packet with the start of an AU, but can
  // send a stream of bytes after that.
  bool require_can_find_start = false;

  // On problematic input data, all decoders are expected to at least be able to
  // close the channel rather than getting stuck in a failed and/or broken
  // state.
  //
  // A decoder returned from a request with require_can_re_sync is potentially
  // able to handle damaged input without closing the Codec channel.  Such a
  // Codec is encouraged, but not required, to also satisfy requirements of
  // require_report_all_detected_errors.
  bool require_can_re_sync = false;

  // Sometimes a client would rather fail an overall use of a decoder than fail
  // to notice data corruption.  For such scenarios, the client can specify
  // require_report_all_detected_errors.  For any codec returned from a
  // request with require_report_all_detected_errors set, on detection of
  // any input data corruption the codec will report in one or more of these
  // ways:
  //   * closing the Codec channel
  //   * OnStreamFailed()
  //   * error_detected_before
  //   * error_detected_during
  //
  // If false, a codec may silently skip past corrupted input data.
  //
  // No decoder can detect all corruption, because some corruption can look like
  // valid stream data.  This requirement is only to request a codec that
  // is written to attempt to detect _and report_ input stream corruption.
  //
  // This flag is not intended to be 100% bulletproof.  If a client needs robust
  // assurance that _all_ detectable stream corruption is _always_ detected,
  // this flag is not enough of a guarantee to achieve that.  Since some stream
  // corruption is inherently non-detectable in any case, such a client should
  // consider using stronger techniques upstream to ensure that corruption can
  // be detected with the needed probability very close to 1.
  //
  // This flag being true doesn't imply anything about whether the codec will
  // discard damaged data vs. producing corresponding damaged output.  Only that
  // the codec will set error_detected_* bools to true when appropriate.
  //
  // Regardless of this setting, not all timstamp_ish values provided on input
  // are guaranteed to show up on ouput.
  bool require_report_all_detected_errors = false;

  // If true, require that the returned codec is HW-accelerated.
  bool require_hw = false;

  // permit_lack_of_split_header_handling
  //
  // This field is a temporary field that will be going away.
  //
  // TODO(dustingreen): Remove this field once we're down to zero codecs with
  // problems handling split headers.
  //
  // By default, a Codec instance is required to handle "split headers", meaning
  // that a client is allowed to deliver parts of an AU one byte at a time,
  // including parts near the beginning of the AU, and the codec is required to
  // tolerate and handle that properly.  However, unfortunately not all codecs
  // properly support split headers.  If a client is willing to permit such a
  // codec to be used, the client can set this to true.  Clients are not
  // encouraged to set this, but setting it may be necessary to find a codec for
  // some formats _for now_.  If a client sets this to true, the client should
  // deliver data of each AU with many contiguous non-split bytes from the start
  // of each AU.  The client is not strictly required to deliver one AU at a
  // time, only to ensure that either all the AU bytes are in a single packet or
  // that many bytes at the start of each AU are in a single packet.
  //
  // The specification for how a client should use this and how a client should
  // behave if setting this to true is intentionally vague, because lack of
  // support for header splitting is not ideal, and is expected to be
  // temporary, and all codecs should handle split headers in the long run.
  // The main intent of this field is to avoid giving an innocent client using
  // default value of false here a codec that can't properly handle split
  // headers.  This is not an attempt at a mechanism to fully work around a
  // codec that doesn't handle split headers.
  //
  // TODO(dustingreen): In the near term, wire this up so that SoftAAC2.cpp
  // used for ADTS is not selected when this field is false, even if there is
  // no other suitable codec.  In the long term, fix or work around the header
  // handling behavior of SoftAAC2 when used in ADTS mode (and any other
  // similar issues in other codecs) and remove this field.
  bool permit_lack_of_split_header_handling = false;
};

enum CodecType {
  DECODER = 0;
  ENCODER = 1;
};

struct CodecDescription {
  // Decoder or encoder.
  CodecType codec_type;
  // The mime type of the compressed format.  For decoders this is the mime
  // type of the input.  For encoders, this is the mime type of the output.
  string mime_type;

  // TODO(dustingreen): All these fields should be optional.
  //
  // TODO(dustingreen): Re-evaluate this for encoders.
  //
  // For each of these fields, the default is the most-capable setting, but if a
  // codec doesn't support the most-capable behavior, then the codec must
  // override the default.
  bool can_stream_bytes_input = true;
  bool can_find_start = true;
  bool can_re_sync = true;
  bool will_report_all_detected_errors = true;
  bool is_hw = true;
  bool split_header_handling = true;
};

// CodecFactory
//
// The purpose of the media::CodecFactory interface is to create media::Codec
// instances.
//
// The interface methods don't attempt to homogenize all codec types, preferring
// to have a separate dedicated message for decoders.  TBD whether calls for
// creating encoders will split up audio vs. video encoders, or be combined.
//
// Each create request is self-contained, in the sense that the interface is not
// stateful between create requests.
[Discoverable]
interface CodecFactory {
  // Driver-based local CodecFactory(s) will send this once shortly after the
  // main CodecFactory connects to the driver-local CodecFactory.
  //
  // For now, the main CodecFactory will not send this.
  //
  // A SW-based local CodecFactory(s) will not send this event.
  //
  // Each codec in the list must be separately-described, for clean aggregation.
  1: -> OnCodecList(vector<CodecDescription> codecs);

  // Rough sequence to create a decoder:
  //
  // factory = ConnectToEnvironmentService(CodecFactory);
  // CreateDecoder_Params params;
  // [fill out params]
  // CreateDecoder(params, decoder_request);
  //
  // See use_media_decoder code for more detail.
  //
  // TODO(dustingreen): More detail in this comment block.

  // Requests:

  // CreateDecoder:
  //
  // decoder_params - See CreateDecoder_Params comments for required
  // and optional parameters for creating a decoder.
  //
  // decoder - a Codec.NewRequest() which will hopefully be connected to
  // a Codec server, or the Codec channel will get closed if no suitable codec
  // can be found.  We don't return any additional Codec-specific status here
  // because finding the Codec is allowed to be fully async, so we don't
  // necessarily yet know on return from this method which Codec will be
  // selected, if any.
  2: CreateDecoder(
    CreateDecoder_Params decoder_params,
    request<Codec> decoder);

  // TODO(dustingreen):
  // CreateAudioEncoder
  // CreateVideoEncoder
  // (or combined)
};