static gboolean
gst_opus_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps)
{
  GstOpusDec *dec = GST_OPUS_DEC (bdec);
  gboolean ret = TRUE;
  GstStructure *s;
  const GValue *streamheader;
  GstCaps *old_caps;

  GST_DEBUG_OBJECT (dec, "set_format: %" GST_PTR_FORMAT, caps);

  if ((old_caps = gst_pad_get_current_caps (GST_AUDIO_DECODER_SINK_PAD (bdec)))) {
    if (gst_caps_is_equal (caps, old_caps)) {
      gst_caps_unref (old_caps);
      GST_DEBUG_OBJECT (dec, "caps didn't change");
      goto done;
    }

    GST_DEBUG_OBJECT (dec, "caps have changed, resetting decoder");
    gst_opus_dec_reset (dec);
    gst_caps_unref (old_caps);
  }

  s = gst_caps_get_structure (caps, 0);
  if ((streamheader = gst_structure_get_value (s, "streamheader")) &&
      G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) &&
      gst_value_array_get_size (streamheader) >= 2) {
    const GValue *header, *vorbiscomment;
    GstBuffer *buf;
    GstFlowReturn res = GST_FLOW_OK;

    header = gst_value_array_get_value (streamheader, 0);
    if (header && G_VALUE_HOLDS (header, GST_TYPE_BUFFER)) {
      buf = gst_value_get_buffer (header);
      res = gst_opus_dec_parse_header (dec, buf);
      if (res != GST_FLOW_OK) {
        ret = FALSE;
        goto done;
      }
      gst_buffer_replace (&dec->streamheader, buf);
    }

    vorbiscomment = gst_value_array_get_value (streamheader, 1);
    if (vorbiscomment && G_VALUE_HOLDS (vorbiscomment, GST_TYPE_BUFFER)) {
      buf = gst_value_get_buffer (vorbiscomment);
      res = gst_opus_dec_parse_comments (dec, buf);
      if (res != GST_FLOW_OK) {
        ret = FALSE;
        goto done;
      }
      gst_buffer_replace (&dec->vorbiscomment, buf);
    }
  } else {
    const GstAudioChannelPosition *posn = NULL;

    if (!gst_codec_utils_opus_parse_caps (caps, &dec->sample_rate,
            &dec->n_channels, &dec->channel_mapping_family, &dec->n_streams,
            &dec->n_stereo_streams, dec->channel_mapping)) {
      ret = FALSE;
      goto done;
    }

    if (dec->channel_mapping_family == 1 && dec->n_channels <= 8)
      posn = gst_opus_channel_positions[dec->n_channels - 1];

    if (!gst_opus_dec_negotiate (dec, posn))
      return FALSE;
  }

done:
  return ret;
}
Beispiel #2
0
static GstFlowReturn
gst_opus_parse_parse_frame (GstBaseParse * base, GstBaseParseFrame * frame)
{
  guint64 duration;
  GstOpusParse *parse;
  gboolean is_idheader, is_commentheader;
  GstMapInfo map;
  GstAudioClippingMeta *cmeta =
      gst_buffer_get_audio_clipping_meta (frame->buffer);

  parse = GST_OPUS_PARSE (base);

  g_assert (!cmeta || cmeta->format == GST_FORMAT_DEFAULT);

  is_idheader = gst_opus_header_is_id_header (frame->buffer);
  is_commentheader = gst_opus_header_is_comment_header (frame->buffer);

  if (!parse->got_headers || !parse->header_sent) {
    GstCaps *caps;

    /* Opus streams can decode to 1 or 2 channels, so use the header
       value if we have one, or 2 otherwise */
    if (is_idheader) {
      gst_buffer_replace (&parse->id_header, frame->buffer);
      GST_DEBUG_OBJECT (parse, "Found ID header, keeping");
      return GST_BASE_PARSE_FLOW_DROPPED;
    } else if (is_commentheader) {
      gst_buffer_replace (&parse->comment_header, frame->buffer);
      GST_DEBUG_OBJECT (parse, "Found comment header, keeping");
      return GST_BASE_PARSE_FLOW_DROPPED;
    }

    parse->got_headers = TRUE;

    if (cmeta && cmeta->start) {
      parse->pre_skip += cmeta->start;

      gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
      duration = packet_duration_opus (map.data, map.size);
      gst_buffer_unmap (frame->buffer, &map);

      /* Queue frame for later once we know all initial padding */
      if (duration == cmeta->start) {
        frame->flags |= GST_BASE_PARSE_FRAME_FLAG_QUEUE;
      }
    }

    if (!(frame->flags & GST_BASE_PARSE_FRAME_FLAG_QUEUE)) {
      if (FALSE && parse->id_header && parse->comment_header) {
        guint16 pre_skip;

        gst_buffer_map (parse->id_header, &map, GST_MAP_READWRITE);
        pre_skip = GST_READ_UINT16_LE (map.data + 10);
        if (pre_skip != parse->pre_skip) {
          GST_DEBUG_OBJECT (parse,
              "Fixing up pre-skip %u -> %" G_GUINT64_FORMAT, pre_skip,
              parse->pre_skip);
          GST_WRITE_UINT16_LE (map.data + 10, parse->pre_skip);
        }
        gst_buffer_unmap (parse->id_header, &map);

        caps =
            gst_codec_utils_opus_create_caps_from_header (parse->id_header,
            parse->comment_header);
      } else {
        GstCaps *sink_caps;
        guint32 sample_rate = 48000;
        guint8 n_channels, n_streams, n_stereo_streams, channel_mapping_family;
        guint8 channel_mapping[256];
        GstBuffer *id_header;

        sink_caps = gst_pad_get_current_caps (GST_BASE_PARSE_SINK_PAD (parse));
        if (!sink_caps
            || !gst_codec_utils_opus_parse_caps (sink_caps, &sample_rate,
                &n_channels, &channel_mapping_family, &n_streams,
                &n_stereo_streams, channel_mapping)) {
          GST_INFO_OBJECT (parse,
              "No headers and no caps, blindly setting up canonical stereo");
          n_channels = 2;
          n_streams = 1;
          n_stereo_streams = 1;
          channel_mapping_family = 0;
          channel_mapping[0] = 0;
          channel_mapping[1] = 1;
        }
        if (sink_caps)
          gst_caps_unref (sink_caps);

        id_header =
            gst_codec_utils_opus_create_header (sample_rate, n_channels,
            channel_mapping_family, n_streams, n_stereo_streams,
            channel_mapping, parse->pre_skip, 0);
        caps = gst_codec_utils_opus_create_caps_from_header (id_header, NULL);
        gst_buffer_unref (id_header);
      }

      gst_buffer_replace (&parse->id_header, NULL);
      gst_buffer_replace (&parse->comment_header, NULL);

      gst_pad_set_caps (GST_BASE_PARSE_SRC_PAD (parse), caps);
      gst_caps_unref (caps);
      parse->header_sent = TRUE;
    }
  }

  GST_BUFFER_TIMESTAMP (frame->buffer) = parse->next_ts;

  gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
  duration = packet_duration_opus (map.data, map.size);
  gst_buffer_unmap (frame->buffer, &map);
  parse->next_ts += duration;

  GST_BUFFER_DURATION (frame->buffer) = duration;
  GST_BUFFER_OFFSET_END (frame->buffer) =
      gst_util_uint64_scale (parse->next_ts, 48000, GST_SECOND);
  GST_BUFFER_OFFSET (frame->buffer) = parse->next_ts;

  return GST_FLOW_OK;
}