Example #1
0
static GstFlowReturn
gst_rsvg_dec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame,
    GstAdapter * adapter, gboolean at_eos)
{
  GstRsvgDec *rsvg = GST_RSVG_DEC (decoder);
  gboolean completed = FALSE;
  const guint8 *data;
  guint size;
  guint i;

  GST_LOG_OBJECT (rsvg, "parse start");
  size = gst_adapter_available (adapter);

  /* "<svg></svg>" */
  if (size < 5 + 6)
    return GST_VIDEO_DECODER_FLOW_NEED_DATA;

  data = gst_adapter_map (adapter, size);
  if (data == NULL) {
    GST_ERROR_OBJECT (rsvg, "Unable to map memory");
    return GST_FLOW_ERROR;
  }
  for (i = 0; i < size - 4; i++) {
    if (memcmp (data + i, "<svg", 4) == 0) {
      gst_adapter_flush (adapter, i);

      size = gst_adapter_available (adapter);
      if (size < 5 + 6)
        return GST_VIDEO_DECODER_FLOW_NEED_DATA;
      data = gst_adapter_map (adapter, size);
      if (data == NULL) {
        GST_ERROR_OBJECT (rsvg, "Unable to map memory");
        return GST_FLOW_ERROR;
      }
      break;
    }
  }
  /* If start wasn't found: */
  if (i == size - 4) {
    gst_adapter_flush (adapter, size - 4);
    return GST_VIDEO_DECODER_FLOW_NEED_DATA;
  }

  for (i = size - 6; i >= 5; i--) {
    if (memcmp (data + i, "</svg>", 6) == 0) {
      completed = TRUE;
      size = i + 6;
      break;
    }
  }

  if (completed) {

    GST_LOG_OBJECT (rsvg, "have complete svg of %u bytes", size);

    gst_video_decoder_add_to_frame (decoder, size);
    return gst_video_decoder_have_frame (decoder);
  }
  return GST_VIDEO_DECODER_FLOW_NEED_DATA;
}
Example #2
0
static gint
multipart_find_boundary (GstMultipartDemux * multipart, gint * datalen)
{
  /* Adaptor is positioned at the start of the data */
  const guint8 *data, *pos;
  const guint8 *dataend;
  gint len;

  if (multipart->content_length >= 0) {
    /* fast path, known content length :) */
    len = multipart->content_length;
    if (gst_adapter_available (multipart->adapter) >= len + 2) {
      *datalen = len;
      data = gst_adapter_map (multipart->adapter, len + 1);

      /* If data[len] contains \r then assume a newline is \r\n */
      if (data[len] == '\r')
        len += 2;
      else if (data[len] == '\n')
        len += 1;

      gst_adapter_unmap (multipart->adapter);
      /* Don't check if boundary is actually there, but let the header parsing
       * bail out if it isn't */
      return len;
    } else {
      /* need more data */
      return MULTIPART_NEED_MORE_DATA;
    }
  }

  len = gst_adapter_available (multipart->adapter);
  if (len == 0)
    return MULTIPART_NEED_MORE_DATA;
  data = gst_adapter_map (multipart->adapter, len);
  dataend = data + len;

  for (pos = data + multipart->scanpos;
      pos <= dataend - multipart->boundary_len - 2; pos++) {
    if (*pos == '-' && pos[1] == '-' &&
        !strncmp ((gchar *) pos + 2,
            multipart->boundary, multipart->boundary_len)) {
      /* Found the boundary! Check if there was a newline before the boundary */
      len = pos - data;
      if (pos - 2 > data && pos[-2] == '\r')
        len -= 2;
      else if (pos - 1 > data && pos[-1] == '\n')
        len -= 1;
      *datalen = len;

      gst_adapter_unmap (multipart->adapter);
      multipart->scanpos = 0;
      return pos - data;
    }
  }
  gst_adapter_unmap (multipart->adapter);
  multipart->scanpos = pos - data;
  return MULTIPART_NEED_MORE_DATA;
}
Example #3
0
static gboolean
theora_enc_read_multipass_cache (GstTheoraEnc * enc)
{
    GstBuffer *cache_buf;
    const guint8 *cache_data;
    gsize bytes_read = 0;
    gssize bytes_consumed = 0;
    GIOStatus stat = G_IO_STATUS_NORMAL;
    gboolean done = FALSE;

    while (!done) {
        if (gst_adapter_available (enc->multipass_cache_adapter) == 0) {
            GstMapInfo minfo;

            cache_buf = gst_buffer_new_allocate (NULL, 512, NULL);

            gst_buffer_map (cache_buf, &minfo, GST_MAP_WRITE);

            stat = g_io_channel_read_chars (enc->multipass_cache_fd,
                                            (gchar *) minfo.data, minfo.size, &bytes_read, NULL);

            if (bytes_read <= 0) {
                gst_buffer_unmap (cache_buf, &minfo);
                gst_buffer_unref (cache_buf);
                break;
            } else {
                gst_buffer_unmap (cache_buf, &minfo);
                gst_buffer_resize (cache_buf, 0, bytes_read);

                gst_adapter_push (enc->multipass_cache_adapter, cache_buf);
            }
        }
        if (gst_adapter_available (enc->multipass_cache_adapter) == 0)
            break;

        bytes_read =
            MIN (gst_adapter_available (enc->multipass_cache_adapter), 512);

        cache_data = gst_adapter_map (enc->multipass_cache_adapter, bytes_read);

        bytes_consumed =
            th_encode_ctl (enc->encoder, TH_ENCCTL_2PASS_IN, (guint8 *) cache_data,
                           bytes_read);
        gst_adapter_unmap (enc->multipass_cache_adapter);

        done = bytes_consumed <= 0;
        if (bytes_consumed > 0)
            gst_adapter_flush (enc->multipass_cache_adapter, bytes_consumed);
    }

    if (stat == G_IO_STATUS_ERROR || (stat == G_IO_STATUS_EOF && bytes_read == 0)
            || bytes_consumed < 0) {
        GST_ELEMENT_ERROR (enc, RESOURCE, READ, (NULL),
                           ("Failed to read multipass cache file"));
        return FALSE;
    }
    return TRUE;
}
HRESULT
Output::RenderAudioSamples (bool preroll)
{
  uint32_t samplesWritten;

  // guint64 samplesToWrite;

  if (decklinksink->stop) {
    GST_DEBUG ("decklinksink->stop set TRUE!");
    decklinksink->output->BeginAudioPreroll ();
    // running = true;
  } else {
    gconstpointer data;
    int n;

    g_mutex_lock (&decklinksink->audio_mutex);

    n = gst_adapter_available (decklinksink->audio_adapter);
    if (n > 0) {
      data = gst_adapter_map (decklinksink->audio_adapter, n);

      decklinksink->output->ScheduleAudioSamples ((void *) data, n / 4,
          0, 0, &samplesWritten);

      gst_adapter_unmap (decklinksink->audio_adapter);
      gst_adapter_flush (decklinksink->audio_adapter, samplesWritten * 4);
      GST_DEBUG ("wrote %d samples, %d available", samplesWritten, n / 4);

      g_cond_signal (&decklinksink->audio_cond);
    } else {
      if (decklinksink->audio_eos) {
        GstMessage *message;

        message = gst_message_new_eos (GST_OBJECT_CAST (decklinksink));
        gst_message_set_seqnum (message, decklinksink->audio_seqnum);
        gst_element_post_message (GST_ELEMENT_CAST (decklinksink), message);
      }
    }
    g_mutex_unlock (&decklinksink->audio_mutex);

  }

  GST_DEBUG ("RenderAudioSamples");

  return S_OK;
}
static GstFlowReturn
gst_dtsdec_parse (GstAudioDecoder * bdec, GstAdapter * adapter,
    gint * _offset, gint * len)
{
  GstDtsDec *dts;
  guint8 *data;
  gint av, size;
  gint length = 0, flags, sample_rate, bit_rate, frame_length;
  GstFlowReturn result = GST_FLOW_EOS;

  dts = GST_DTSDEC (bdec);

  size = av = gst_adapter_available (adapter);
  data = (guint8 *) gst_adapter_map (adapter, av);

  /* find and read header */
  bit_rate = dts->bit_rate;
  sample_rate = dts->sample_rate;
  flags = 0;
  while (size >= 7) {
    length = dca_syncinfo (dts->state, data, &flags,
        &sample_rate, &bit_rate, &frame_length);

    if (length == 0) {
      /* shift window to re-find sync */
      data++;
      size--;
    } else if (length <= size) {
      GST_LOG_OBJECT (dts, "Sync: frame size %d", length);
      result = GST_FLOW_OK;
      break;
    } else {
      GST_LOG_OBJECT (dts, "Not enough data available (needed %d had %d)",
          length, size);
      break;
    }
  }
  gst_adapter_unmap (adapter);

  *_offset = av - size;
  *len = length;

  return result;
}
static GstFlowReturn
theora_dec_parse (GstVideoDecoder * decoder,
    GstVideoCodecFrame * frame, GstAdapter * adapter, gboolean at_eos)
{
  gint av;
  const guint8 *data;

  av = gst_adapter_available (adapter);

  data = gst_adapter_map (adapter, 1);
  /* check for keyframe; must not be header packet */
  if (!(data[0] & 0x80) && (data[0] & 0x40) == 0)
    GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
  gst_adapter_unmap (adapter);

  /* and pass along all */
  gst_video_decoder_add_to_frame (decoder, av);
  return gst_video_decoder_have_frame (decoder);
}
Example #7
0
static GstFlowReturn
gst_mim_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
  GstMimDec *mimdec = GST_MIM_DEC (parent);
  GstBuffer *out_buf;
  const guchar *header, *frame_body;
  guint32 fourcc;
  guint16 header_size;
  gint width, height;
  GstCaps *caps;
  GstFlowReturn res = GST_FLOW_OK;
  GstClockTime in_time = GST_BUFFER_TIMESTAMP (buf);
  GstEvent *event = NULL;
  gboolean result = TRUE;
  guint32 payload_size;
  guint32 current_ts;
  GstMapInfo map;

  gst_adapter_push (mimdec->adapter, buf);


  /* do we have enough bytes to read a header */
  while (gst_adapter_available (mimdec->adapter) >= 24) {
    header = gst_adapter_map (mimdec->adapter, 24);
    header_size = header[0];
    if (header_size != 24) {
      gst_adapter_unmap (mimdec->adapter);
      gst_adapter_flush (mimdec->adapter, 24);
      GST_ELEMENT_ERROR (mimdec, STREAM, DECODE, (NULL),
          ("invalid frame: header size %d incorrect", header_size));
      return GST_FLOW_ERROR;
    }

    if (header[1] == 1) {
      /* This is a a paused frame, skip it */
      gst_adapter_unmap (mimdec->adapter);
      gst_adapter_flush (mimdec->adapter, 24);
      continue;
    }

    fourcc = GUINT32_FROM_LE (*((guint32 *) (header + 12)));
    if (GST_MAKE_FOURCC ('M', 'L', '2', '0') != fourcc) {
      gst_adapter_unmap (mimdec->adapter);
      gst_adapter_flush (mimdec->adapter, 24);
      GST_ELEMENT_ERROR (mimdec, STREAM, WRONG_TYPE, (NULL),
          ("invalid frame: unknown FOURCC code %X (%" GST_FOURCC_FORMAT ")",
              fourcc, GST_FOURCC_ARGS (fourcc)));
      return GST_FLOW_ERROR;
    }

    payload_size = GUINT32_FROM_LE (*((guint32 *) (header + 8)));

    current_ts = GUINT32_FROM_LE (*((guint32 *) (header + 20)));

    gst_adapter_unmap (mimdec->adapter);

    GST_LOG_OBJECT (mimdec, "Got packet, payload size %d", payload_size);

    if (gst_adapter_available (mimdec->adapter) < payload_size + 24)
      return GST_FLOW_OK;

    /* We have a whole packet and have read the header, lets flush it out */
    gst_adapter_flush (mimdec->adapter, 24);

    frame_body = gst_adapter_map (mimdec->adapter, payload_size);

    if (mimdec->buffer_size < 0) {
      /* Check if its a keyframe, otherwise skip it */
      if (GUINT32_FROM_LE (*((guint32 *) (frame_body + 12))) != 0) {
        gst_adapter_unmap (mimdec->adapter);
        gst_adapter_flush (mimdec->adapter, payload_size);
        return GST_FLOW_OK;
      }

      if (!mimic_decoder_init (mimdec->dec, frame_body)) {
        gst_adapter_unmap (mimdec->adapter);
        gst_adapter_flush (mimdec->adapter, payload_size);
        GST_ELEMENT_ERROR (mimdec, LIBRARY, INIT, (NULL),
            ("mimic_decoder_init error"));
        return GST_FLOW_ERROR;
      }

      if (!mimic_get_property (mimdec->dec, "buffer_size",
              &mimdec->buffer_size)) {
        gst_adapter_unmap (mimdec->adapter);
        gst_adapter_flush (mimdec->adapter, payload_size);
        GST_ELEMENT_ERROR (mimdec, LIBRARY, INIT, (NULL),
            ("mimic_get_property('buffer_size') error"));
        return GST_FLOW_ERROR;
      }

      mimic_get_property (mimdec->dec, "width", &width);
      mimic_get_property (mimdec->dec, "height", &height);
      GST_DEBUG_OBJECT (mimdec,
          "Initialised decoder with %d x %d payload size %d buffer_size %d",
          width, height, payload_size, mimdec->buffer_size);
      caps = gst_caps_new_simple ("video/x-raw",
          "format", G_TYPE_STRING, "RGB",
          "framerate", GST_TYPE_FRACTION, 0, 1,
          "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
      gst_pad_set_caps (mimdec->srcpad, caps);
      gst_caps_unref (caps);
    }


    if (mimdec->need_segment) {
      GstSegment segment;

      gst_segment_init (&segment, GST_FORMAT_TIME);

      if (GST_CLOCK_TIME_IS_VALID (in_time))
        segment.start = in_time;
      else
        segment.start = current_ts * GST_MSECOND;
      event = gst_event_new_segment (&segment);
    }
    mimdec->need_segment = FALSE;

    if (event)
      result = gst_pad_push_event (mimdec->srcpad, event);
    event = NULL;

    if (!result) {
      GST_WARNING_OBJECT (mimdec, "gst_pad_push_event failed");
      return GST_FLOW_ERROR;
    }


    out_buf = gst_buffer_new_allocate (NULL, mimdec->buffer_size, NULL);
    gst_buffer_map (out_buf, &map, GST_MAP_READWRITE);

    if (!mimic_decode_frame (mimdec->dec, frame_body, map.data)) {
      GST_WARNING_OBJECT (mimdec, "mimic_decode_frame error\n");

      gst_adapter_flush (mimdec->adapter, payload_size);

      gst_buffer_unmap (out_buf, &map);
      gst_buffer_unref (out_buf);
      GST_ELEMENT_ERROR (mimdec, STREAM, DECODE, (NULL),
          ("mimic_decode_frame error"));
      return GST_FLOW_ERROR;
    }
    gst_buffer_unmap (out_buf, &map);
    gst_adapter_flush (mimdec->adapter, payload_size);

    if (GST_CLOCK_TIME_IS_VALID (in_time))
      GST_BUFFER_TIMESTAMP (out_buf) = in_time;
    else
      GST_BUFFER_TIMESTAMP (out_buf) = current_ts * GST_MSECOND;

    res = gst_pad_push (mimdec->srcpad, out_buf);

    if (res != GST_FLOW_OK)
      break;
  }

  return res;
}
static GstVaapiDecoderStatus
gst_vaapi_decoder_jpeg_parse (GstVaapiDecoder * base_decoder,
    GstAdapter * adapter, gboolean at_eos, GstVaapiDecoderUnit * unit)
{
  GstVaapiDecoderJpeg *const decoder =
      GST_VAAPI_DECODER_JPEG_CAST (base_decoder);
  GstVaapiDecoderJpegPrivate *const priv = &decoder->priv;
  GstVaapiParserState *const ps = GST_VAAPI_PARSER_STATE (base_decoder);
  GstVaapiDecoderStatus status;
  GstJpegMarker marker;
  GstJpegSegment seg;
  const guchar *buf;
  guint buf_size, flags;
  gint ofs1, ofs2;

  status = ensure_decoder (decoder);
  if (status != GST_VAAPI_DECODER_STATUS_SUCCESS)
    return status;

  /* Expect at least 2 bytes for the marker */
  buf_size = gst_adapter_available (adapter);
  if (buf_size < 2)
    return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;

  buf = gst_adapter_map (adapter, buf_size);
  if (!buf)
    return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;

  ofs1 = ps->input_offset1 - 2;
  if (ofs1 < 0)
    ofs1 = 0;

  for (;;) {
    // Skip any garbage until we reach SOI, if needed
    if (!gst_jpeg_parse (&seg, buf, buf_size, ofs1)) {
      gst_adapter_unmap (adapter);
      ps->input_offset1 = buf_size;
      return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
    }
    ofs1 = seg.offset;

    marker = seg.marker;
    if (!VALID_STATE (parser, GOT_SOI) && marker != GST_JPEG_MARKER_SOI)
      continue;
    if (marker == GST_JPEG_MARKER_SOS) {
      ofs2 = ps->input_offset2 - 2;
      if (ofs2 < ofs1 + seg.size)
        ofs2 = ofs1 + seg.size;

      // Parse the whole scan + ECSs, including RSTi
      for (;;) {
        if (!gst_jpeg_parse (&seg, buf, buf_size, ofs2)) {
          gst_adapter_unmap (adapter);
          ps->input_offset1 = ofs1;
          ps->input_offset2 = buf_size;
          return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
        }

        if (is_scan_complete (seg.marker))
          break;
        ofs2 = seg.offset + seg.size;
      }
      ofs2 = seg.offset - 2;
    } else {
      // Check that the whole segment is actually available (in buffer)
      ofs2 = ofs1 + seg.size;
      if (ofs2 > buf_size) {
        gst_adapter_unmap (adapter);
        ps->input_offset1 = ofs1;
        return GST_VAAPI_DECODER_STATUS_ERROR_NO_DATA;
      }
    }
    break;
  }
  gst_adapter_unmap (adapter);

  unit->size = ofs2 - ofs1;
  unit_set_marker_code (unit, marker);
  gst_adapter_flush (adapter, ofs1);
  ps->input_offset1 = 2;
  ps->input_offset2 = 2;

  flags = 0;
  switch (marker) {
    case GST_JPEG_MARKER_SOI:
      flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_START;
      priv->parser_state |= GST_JPEG_VIDEO_STATE_GOT_SOI;
      break;
    case GST_JPEG_MARKER_EOI:
      flags |= GST_VAAPI_DECODER_UNIT_FLAG_FRAME_END;
      priv->parser_state = 0;
      break;
    case GST_JPEG_MARKER_SOS:
      flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE;
      priv->parser_state |= GST_JPEG_VIDEO_STATE_GOT_SOS;
      break;
    case GST_JPEG_MARKER_DAC:
    case GST_JPEG_MARKER_DHT:
    case GST_JPEG_MARKER_DQT:
      if (priv->parser_state & GST_JPEG_VIDEO_STATE_GOT_SOF)
        flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE;
      break;
    case GST_JPEG_MARKER_DRI:
      if (priv->parser_state & GST_JPEG_VIDEO_STATE_GOT_SOS)
        flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE;
      break;
    case GST_JPEG_MARKER_DNL:
      flags |= GST_VAAPI_DECODER_UNIT_FLAG_SLICE;
      break;
    case GST_JPEG_MARKER_COM:
      flags |= GST_VAAPI_DECODER_UNIT_FLAG_SKIP;
      break;
    default:
      /* SOFn segments */
      if (marker >= GST_JPEG_MARKER_SOF_MIN &&
          marker <= GST_JPEG_MARKER_SOF_MAX)
        priv->parser_state |= GST_JPEG_VIDEO_STATE_GOT_SOF;

      /* Application segments */
      else if (marker >= GST_JPEG_MARKER_APP_MIN &&
          marker <= GST_JPEG_MARKER_APP_MAX)
        flags |= GST_VAAPI_DECODER_UNIT_FLAG_SKIP;

      /* Reserved */
      else if (marker >= 0x02 && marker <= 0xbf)
        flags |= GST_VAAPI_DECODER_UNIT_FLAG_SKIP;
      break;
  }
  GST_VAAPI_DECODER_UNIT_FLAG_SET (unit, flags);
  return GST_VAAPI_DECODER_STATUS_SUCCESS;
}
Example #9
0
static GstFlowReturn
gst_pngdec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame,
    GstAdapter * adapter, gboolean at_eos)
{
  gsize toadd = 0;
  GstByteReader reader;
  gconstpointer data;
  guint64 signature;
  gsize size;
  GstPngDec *pngdec = (GstPngDec *) decoder;

  GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);

  /* FIXME : The overhead of using scan_uint32 is massive */

  size = gst_adapter_available (adapter);
  GST_DEBUG ("Parsing PNG image data (%" G_GSIZE_FORMAT " bytes)", size);

  if (size < 8)
    goto need_more_data;

  data = gst_adapter_map (adapter, size);
  gst_byte_reader_init (&reader, data, size);

  if (pngdec->read_data == 0) {
    if (!gst_byte_reader_peek_uint64_be (&reader, &signature))
      goto need_more_data;

    if (signature != PNG_SIGNATURE) {
      for (;;) {
        guint offset;

        offset = gst_byte_reader_masked_scan_uint32 (&reader, 0xffffffff,
            0x89504E47, 0, gst_byte_reader_get_remaining (&reader));

        if (offset == -1) {
          gst_adapter_flush (adapter,
              gst_byte_reader_get_remaining (&reader) - 4);
          goto need_more_data;
        }

        if (!gst_byte_reader_skip (&reader, offset))
          goto need_more_data;

        if (!gst_byte_reader_peek_uint64_be (&reader, &signature))
          goto need_more_data;

        if (signature == PNG_SIGNATURE) {
          /* We're skipping, go out, we'll be back */
          gst_adapter_flush (adapter, gst_byte_reader_get_pos (&reader));
          goto need_more_data;
        }
        if (!gst_byte_reader_skip (&reader, 4))
          goto need_more_data;
      }
    }
    pngdec->read_data = 8;
  }

  if (!gst_byte_reader_skip (&reader, pngdec->read_data))
    goto need_more_data;

  for (;;) {
    guint32 length;
    guint32 code;

    if (!gst_byte_reader_get_uint32_be (&reader, &length))
      goto need_more_data;
    if (!gst_byte_reader_get_uint32_le (&reader, &code))
      goto need_more_data;

    if (!gst_byte_reader_skip (&reader, length + 4))
      goto need_more_data;

    if (code == GST_MAKE_FOURCC ('I', 'E', 'N', 'D')) {
      /* Have complete frame */
      toadd = gst_byte_reader_get_pos (&reader);
      GST_DEBUG_OBJECT (decoder, "Have complete frame of size %" G_GSIZE_FORMAT,
          toadd);
      pngdec->read_data = 0;
      goto have_full_frame;
    } else
      pngdec->read_data += length + 12;
  }

  g_assert_not_reached ();
  return GST_FLOW_ERROR;

need_more_data:
  return GST_VIDEO_DECODER_FLOW_NEED_DATA;

have_full_frame:
  if (toadd)
    gst_video_decoder_add_to_frame (decoder, toadd);
  return gst_video_decoder_have_frame (decoder);
}
Example #10
0
static GstFlowReturn
gst_asf_parse_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
  GstAsfParse *asfparse;
  GstFlowReturn ret = GST_FLOW_OK;

  asfparse = GST_ASF_PARSE (parent);
  gst_adapter_push (asfparse->adapter, buffer);

  switch (asfparse->parse_state) {
    case ASF_PARSING_HEADERS:
      if (asfparse->headers_size == 0 &&
          gst_adapter_available (asfparse->adapter) >= ASF_GUID_OBJSIZE_SIZE) {

        /* we can peek at the object size */
        asfparse->headers_size =
            gst_asf_match_and_peek_obj_size (gst_adapter_map
            (asfparse->adapter, ASF_GUID_OBJSIZE_SIZE),
            &(guids[ASF_HEADER_OBJECT_INDEX]));
        gst_adapter_unmap (asfparse->adapter);

        if (asfparse->headers_size == 0) {
          /* something is wrong, this probably ain't an ASF stream */
          GST_ERROR_OBJECT (asfparse, "ASF starting identifier missing");
          ret = GST_FLOW_ERROR;
          goto end;
        }
      }
      if (gst_adapter_available (asfparse->adapter) >= asfparse->headers_size) {
        GstBuffer *headers = gst_adapter_take_buffer (asfparse->adapter,
            asfparse->headers_size);
        if (gst_asf_parse_headers (headers, asfparse->asfinfo)) {
          ret = gst_asf_parse_push (asfparse, headers);
          asfparse->parse_state = ASF_PARSING_DATA;
        } else {
          ret = GST_FLOW_ERROR;
          GST_ERROR_OBJECT (asfparse, "Failed to parse headers");
        }
      }
      break;
    case ASF_PARSING_DATA:
      if (asfparse->data_size == 0 &&
          gst_adapter_available (asfparse->adapter) >= ASF_GUID_OBJSIZE_SIZE) {

        /* we can peek at the object size */
        asfparse->data_size =
            gst_asf_match_and_peek_obj_size (gst_adapter_map
            (asfparse->adapter, ASF_GUID_OBJSIZE_SIZE),
            &(guids[ASF_DATA_OBJECT_INDEX]));
        gst_adapter_unmap (asfparse->adapter);

        if (asfparse->data_size == 0) {
          /* something is wrong */
          GST_ERROR_OBJECT (asfparse, "Unexpected object after headers, was "
              "expecting a data object");
          ret = GST_FLOW_ERROR;
          goto end;
        }
      }
      /* if we have received the full data object headers */
      if (gst_adapter_available (asfparse->adapter) >= ASF_DATA_OBJECT_SIZE) {
        ret = gst_asf_parse_parse_data_object (asfparse,
            gst_adapter_take_buffer (asfparse->adapter, ASF_DATA_OBJECT_SIZE));
        if (ret != GST_FLOW_OK) {
          goto end;
        }
        asfparse->parse_state = ASF_PARSING_PACKETS;
      }
      break;
    case ASF_PARSING_PACKETS:
      g_assert (asfparse->asfinfo->packet_size);
      while ((asfparse->asfinfo->broadcast ||
              asfparse->parsed_packets < asfparse->asfinfo->packets_count) &&
          gst_adapter_available (asfparse->adapter) >=
          asfparse->asfinfo->packet_size) {
        GstBuffer *packet = gst_adapter_take_buffer (asfparse->adapter,
            asfparse->asfinfo->packet_size);
        asfparse->parsed_packets++;
        ret = gst_asf_parse_parse_packet (asfparse, packet);
        if (ret != GST_FLOW_OK)
          goto end;
      }
      if (!asfparse->asfinfo->broadcast &&
          asfparse->parsed_packets >= asfparse->asfinfo->packets_count) {
        GST_INFO_OBJECT (asfparse, "Finished parsing packets");
        asfparse->parse_state = ASF_PARSING_INDEXES;
      }
      break;
    case ASF_PARSING_INDEXES:
      /* we currently don't care about any of those objects */
      if (gst_adapter_available (asfparse->adapter) >= ASF_GUID_OBJSIZE_SIZE) {
        guint64 obj_size;
        /* we can peek at the object size */
        obj_size = gst_asf_match_and_peek_obj_size (gst_adapter_map
            (asfparse->adapter, ASF_GUID_OBJSIZE_SIZE), NULL);
        gst_adapter_unmap (asfparse->adapter);
        if (gst_adapter_available (asfparse->adapter) >= obj_size) {
          GST_DEBUG_OBJECT (asfparse, "Skiping object");
          ret = gst_asf_parse_push (asfparse,
              gst_adapter_take_buffer (asfparse->adapter, obj_size));
          if (ret != GST_FLOW_OK) {
            goto end;
          }
        }
      }
      break;
    default:
      break;
  }

end:
  return ret;
}
Example #11
0
static GstFlowReturn
gst_flxdec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
{
  GstCaps *caps;
  guint avail;
  GstFlowReturn res = GST_FLOW_OK;

  GstFlxDec *flxdec;
  FlxHeader *flxh;

  g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR);
  flxdec = (GstFlxDec *) parent;
  g_return_val_if_fail (flxdec != NULL, GST_FLOW_ERROR);

  gst_adapter_push (flxdec->adapter, buf);
  avail = gst_adapter_available (flxdec->adapter);

  if (flxdec->state == GST_FLXDEC_READ_HEADER) {
    if (avail >= FlxHeaderSize) {
      const guint8 *data = gst_adapter_map (flxdec->adapter, FlxHeaderSize);
      GstCaps *templ;

      memcpy ((gchar *) & flxdec->hdr, data, FlxHeaderSize);
      FLX_HDR_FIX_ENDIANNESS (&(flxdec->hdr));
      gst_adapter_unmap (flxdec->adapter);
      gst_adapter_flush (flxdec->adapter, FlxHeaderSize);

      flxh = &flxdec->hdr;

      /* check header */
      if (flxh->type != FLX_MAGICHDR_FLI &&
          flxh->type != FLX_MAGICHDR_FLC && flxh->type != FLX_MAGICHDR_FLX)
        goto wrong_type;

      GST_LOG ("size      :  %d", flxh->size);
      GST_LOG ("frames    :  %d", flxh->frames);
      GST_LOG ("width     :  %d", flxh->width);
      GST_LOG ("height    :  %d", flxh->height);
      GST_LOG ("depth     :  %d", flxh->depth);
      GST_LOG ("speed     :  %d", flxh->speed);

      flxdec->next_time = 0;

      if (flxh->type == FLX_MAGICHDR_FLI) {
        flxdec->frame_time = JIFFIE * flxh->speed;
      } else if (flxh->speed == 0) {
        flxdec->frame_time = GST_SECOND / 70;
      } else {
        flxdec->frame_time = flxh->speed * GST_MSECOND;
      }

      flxdec->duration = flxh->frames * flxdec->frame_time;
      GST_LOG ("duration   :  %" GST_TIME_FORMAT,
          GST_TIME_ARGS (flxdec->duration));

      templ = gst_pad_get_pad_template_caps (flxdec->srcpad);
      caps = gst_caps_copy (templ);
      gst_caps_unref (templ);
      gst_caps_set_simple (caps,
          "width", G_TYPE_INT, flxh->width,
          "height", G_TYPE_INT, flxh->height,
          "framerate", GST_TYPE_FRACTION, (gint) GST_MSECOND,
          (gint) flxdec->frame_time / 1000, NULL);

      gst_pad_set_caps (flxdec->srcpad, caps);
      gst_caps_unref (caps);

      if (flxh->depth <= 8)
        flxdec->converter =
            flx_colorspace_converter_new (flxh->width, flxh->height);

      if (flxh->type == FLX_MAGICHDR_FLC || flxh->type == FLX_MAGICHDR_FLX) {
        GST_LOG ("(FLC) aspect_dx :  %d", flxh->aspect_dx);
        GST_LOG ("(FLC) aspect_dy :  %d", flxh->aspect_dy);
        GST_LOG ("(FLC) oframe1   :  0x%08x", flxh->oframe1);
        GST_LOG ("(FLC) oframe2   :  0x%08x", flxh->oframe2);
      }

      flxdec->size = (flxh->width * flxh->height);

      /* create delta and output frame */
      flxdec->frame_data = g_malloc (flxdec->size);
      flxdec->delta_data = g_malloc (flxdec->size);

      flxdec->state = GST_FLXDEC_PLAYING;
    }
  } else if (flxdec->state == GST_FLXDEC_PLAYING) {
    GstBuffer *out;

    /* while we have enough data in the adapter */
    while (avail >= FlxFrameChunkSize && res == GST_FLOW_OK) {
      FlxFrameChunk flxfh;
      guchar *chunk;
      const guint8 *data;
      GstMapInfo map;

      chunk = NULL;
      data = gst_adapter_map (flxdec->adapter, FlxFrameChunkSize);
      memcpy (&flxfh, data, FlxFrameChunkSize);
      FLX_FRAME_CHUNK_FIX_ENDIANNESS (&flxfh);
      gst_adapter_unmap (flxdec->adapter);

      switch (flxfh.id) {
        case FLX_FRAME_TYPE:
          /* check if we have the complete frame */
          if (avail < flxfh.size)
            goto need_more_data;

          /* flush header */
          gst_adapter_flush (flxdec->adapter, FlxFrameChunkSize);

          chunk = gst_adapter_take (flxdec->adapter,
              flxfh.size - FlxFrameChunkSize);
          FLX_FRAME_TYPE_FIX_ENDIANNESS ((FlxFrameType *) chunk);
          if (((FlxFrameType *) chunk)->chunks == 0)
            break;

          /* create 32 bits output frame */
//          res = gst_pad_alloc_buffer_and_set_caps (flxdec->srcpad,
//              GST_BUFFER_OFFSET_NONE,
//              flxdec->size * 4, GST_PAD_CAPS (flxdec->srcpad), &out);
//          if (res != GST_FLOW_OK)
//            break;

          out = gst_buffer_new_and_alloc (flxdec->size * 4);

          /* decode chunks */
          flx_decode_chunks (flxdec,
              ((FlxFrameType *) chunk)->chunks,
              chunk + FlxFrameTypeSize, flxdec->frame_data);

          /* save copy of the current frame for possible delta. */
          memcpy (flxdec->delta_data, flxdec->frame_data, flxdec->size);

          gst_buffer_map (out, &map, GST_MAP_WRITE);
          /* convert current frame. */
          flx_colorspace_convert (flxdec->converter, flxdec->frame_data,
              map.data);
          gst_buffer_unmap (out, &map);

          GST_BUFFER_TIMESTAMP (out) = flxdec->next_time;
          flxdec->next_time += flxdec->frame_time;

          res = gst_pad_push (flxdec->srcpad, out);
          break;
        default:
          /* check if we have the complete frame */
          if (avail < flxfh.size)
            goto need_more_data;

          gst_adapter_flush (flxdec->adapter, flxfh.size);
          break;
      }

      if (chunk)
        g_free (chunk);

      avail = gst_adapter_available (flxdec->adapter);
    }
  }
need_more_data:
  return res;

  /* ERRORS */
wrong_type:
  {
    GST_ELEMENT_ERROR (flxdec, STREAM, WRONG_TYPE, (NULL),
        ("not a flx file (type %x)", flxh->type));
    gst_object_unref (flxdec);
    return GST_FLOW_ERROR;
  }
}
static GstFlowReturn
gst_monoscope_chain (GstPad * pad, GstObject * parent, GstBuffer * inbuf)
{
  GstFlowReturn flow_ret = GST_FLOW_OK;
  GstMonoscope *monoscope;

  monoscope = GST_MONOSCOPE (parent);

  if (monoscope->rate == 0) {
    gst_buffer_unref (inbuf);
    flow_ret = GST_FLOW_NOT_NEGOTIATED;
    goto out;
  }

  /* Make sure have an output format */
  flow_ret = ensure_negotiated (monoscope);
  if (flow_ret != GST_FLOW_OK) {
    gst_buffer_unref (inbuf);
    goto out;
  }

  /* don't try to combine samples from discont buffer */
  if (GST_BUFFER_FLAG_IS_SET (inbuf, GST_BUFFER_FLAG_DISCONT)) {
    gst_adapter_clear (monoscope->adapter);
    monoscope->next_ts = GST_CLOCK_TIME_NONE;
  }

  /* Match timestamps from the incoming audio */
  if (GST_BUFFER_TIMESTAMP (inbuf) != GST_CLOCK_TIME_NONE)
    monoscope->next_ts = GST_BUFFER_TIMESTAMP (inbuf);

  GST_LOG_OBJECT (monoscope, "in buffer has %d samples, ts=%" GST_TIME_FORMAT,
      gst_buffer_get_size (inbuf) / monoscope->bps,
      GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (inbuf)));

  gst_adapter_push (monoscope->adapter, inbuf);
  inbuf = NULL;

  /* Collect samples until we have enough for an output frame */
  while (flow_ret == GST_FLOW_OK) {
    gint16 *samples;
    GstBuffer *outbuf = NULL;
    guint32 *pixels, avail, bytesperframe;

    avail = gst_adapter_available (monoscope->adapter);
    GST_LOG_OBJECT (monoscope, "bytes avail now %u", avail);

    bytesperframe = monoscope->spf * monoscope->bps;
    if (avail < bytesperframe)
      break;

    /* FIXME: something is wrong with QoS, we are skipping way too much
     * stuff even with very low CPU loads */
#if 0
    if (monoscope->next_ts != -1) {
      gboolean need_skip;
      gint64 qostime;

      qostime = gst_segment_to_running_time (&monoscope->segment,
          GST_FORMAT_TIME, monoscope->next_ts);

      GST_OBJECT_LOCK (monoscope);
      /* check for QoS, don't compute buffers that are known to be late */
      need_skip =
          GST_CLOCK_TIME_IS_VALID (monoscope->earliest_time) &&
          qostime <= monoscope->earliest_time;
      GST_OBJECT_UNLOCK (monoscope);

      if (need_skip) {
        GST_WARNING_OBJECT (monoscope,
            "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
            GST_TIME_ARGS (qostime), GST_TIME_ARGS (monoscope->earliest_time));
        goto skip;
      }
    }
#endif

    samples = (gint16 *) gst_adapter_map (monoscope->adapter, bytesperframe);

    if (monoscope->spf < 512) {
      gint16 in_data[512], i;

      for (i = 0; i < 512; ++i) {
        gdouble off;

        off = ((gdouble) i * (gdouble) monoscope->spf) / 512.0;
        in_data[i] = samples[MIN ((guint) off, monoscope->spf)];
      }
      pixels = monoscope_update (monoscope->visstate, in_data);
    } else {
      /* not really correct, but looks much prettier */
      pixels = monoscope_update (monoscope->visstate, samples);
    }

    GST_LOG_OBJECT (monoscope, "allocating output buffer");
    flow_ret = gst_buffer_pool_acquire_buffer (monoscope->pool, &outbuf, NULL);
    if (flow_ret != GST_FLOW_OK) {
      gst_adapter_unmap (monoscope->adapter);
      goto out;
    }

    gst_buffer_fill (outbuf, 0, pixels, monoscope->outsize);

    GST_BUFFER_TIMESTAMP (outbuf) = monoscope->next_ts;
    GST_BUFFER_DURATION (outbuf) = monoscope->frame_duration;

    flow_ret = gst_pad_push (monoscope->srcpad, outbuf);

#if 0
  skip:
#endif

    if (GST_CLOCK_TIME_IS_VALID (monoscope->next_ts))
      monoscope->next_ts += monoscope->frame_duration;

    gst_adapter_flush (monoscope->adapter, bytesperframe);
  }

out:

  return flow_ret;
}
Example #13
0
static GstFlowReturn
gst_goom_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
  GstGoom *goom;
  GstFlowReturn ret;
  GstBuffer *outbuf = NULL;

  goom = GST_GOOM (parent);
  if (goom->bps == 0) {
    gst_buffer_unref (buffer);
    ret = GST_FLOW_NOT_NEGOTIATED;
    goto beach;
  }

  /* Make sure have an output format */
  ret = ensure_negotiated (goom);
  if (ret != GST_FLOW_OK) {
    gst_buffer_unref (buffer);
    goto beach;
  }

  /* don't try to combine samples from discont buffer */
  if (GST_BUFFER_FLAG_IS_SET (buffer, GST_BUFFER_FLAG_DISCONT)) {
    gst_adapter_clear (goom->adapter);
  }

  GST_DEBUG_OBJECT (goom,
      "Input buffer has %" G_GSIZE_FORMAT " samples, time=%" G_GUINT64_FORMAT,
      gst_buffer_get_size (buffer) / goom->bps, GST_BUFFER_TIMESTAMP (buffer));

  /* Collect samples until we have enough for an output frame */
  gst_adapter_push (goom->adapter, buffer);

  ret = GST_FLOW_OK;

  while (TRUE) {
    const guint16 *data;
    guchar *out_frame;
    gint i;
    guint avail, to_flush;
    guint64 dist, timestamp;

    avail = gst_adapter_available (goom->adapter);
    GST_DEBUG_OBJECT (goom, "avail now %u", avail);

    /* we need GOOM_SAMPLES to get a meaningful result from goom. */
    if (avail < (GOOM_SAMPLES * goom->bps))
      break;

    /* we also need enough samples to produce one frame at least */
    if (avail < goom->bpf)
      break;

    GST_DEBUG_OBJECT (goom, "processing buffer");

    /* get timestamp of the current adapter byte */
    timestamp = gst_adapter_prev_timestamp (goom->adapter, &dist);
    if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
      /* convert bytes to time */
      dist /= goom->bps;
      timestamp += gst_util_uint64_scale_int (dist, GST_SECOND, goom->rate);
    }

    if (GST_CLOCK_TIME_IS_VALID (timestamp)) {
      gint64 qostime;
      gboolean need_skip;

      qostime = gst_segment_to_running_time (&goom->segment, GST_FORMAT_TIME,
          timestamp) + goom->duration;

      GST_OBJECT_LOCK (goom);
      /* check for QoS, don't compute buffers that are known to be late */
      need_skip = goom->earliest_time != -1 && qostime <= goom->earliest_time;
      GST_OBJECT_UNLOCK (goom);

      if (need_skip) {
        GST_WARNING_OBJECT (goom,
            "QoS: skip ts: %" GST_TIME_FORMAT ", earliest: %" GST_TIME_FORMAT,
            GST_TIME_ARGS (qostime), GST_TIME_ARGS (goom->earliest_time));
        goto skip;
      }
    }

    /* get next GOOM_SAMPLES, we have at least this amount of samples */
    data =
        (const guint16 *) gst_adapter_map (goom->adapter,
        GOOM_SAMPLES * goom->bps);

    if (goom->channels == 2) {
      for (i = 0; i < GOOM_SAMPLES; i++) {
        goom->datain[0][i] = *data++;
        goom->datain[1][i] = *data++;
      }
    } else {
      for (i = 0; i < GOOM_SAMPLES; i++) {
        goom->datain[0][i] = *data;
        goom->datain[1][i] = *data++;
      }
    }

    /* alloc a buffer if we don't have one yet, this happens
     * when we pushed a buffer in this while loop before */
    if (outbuf == NULL) {
      GST_DEBUG_OBJECT (goom, "allocating output buffer");
      ret = gst_buffer_pool_acquire_buffer (goom->pool, &outbuf, NULL);
      if (ret != GST_FLOW_OK) {
        gst_adapter_unmap (goom->adapter);
        goto beach;
      }
    }

    GST_BUFFER_TIMESTAMP (outbuf) = timestamp;
    GST_BUFFER_DURATION (outbuf) = goom->duration;

    out_frame = (guchar *) goom_update (goom->plugin, goom->datain, 0, 0);
    gst_buffer_fill (outbuf, 0, out_frame, goom->outsize);

    gst_adapter_unmap (goom->adapter);

    GST_DEBUG ("Pushing frame with time=%" GST_TIME_FORMAT ", duration=%"
        GST_TIME_FORMAT, GST_TIME_ARGS (timestamp),
        GST_TIME_ARGS (goom->duration));

    ret = gst_pad_push (goom->srcpad, outbuf);
    outbuf = NULL;

  skip:
    /* Now flush the samples we needed for this frame, which might be more than
     * the samples we used (GOOM_SAMPLES). */
    to_flush = goom->bpf;

    GST_DEBUG_OBJECT (goom, "finished frame, flushing %u bytes from input",
        to_flush);
    gst_adapter_flush (goom->adapter, to_flush);

    if (ret != GST_FLOW_OK)
      break;
  }

  if (outbuf != NULL)
    gst_buffer_unref (outbuf);

beach:

  return ret;
}
static void
test_seek_FORMAT_TIME_by_sample (const gchar * fn, GList * seek_positions)
{
  GstElement *pipeline, *src, *sink;
  GstAdapter *adapter;
  GstSample *sample;
  GstCaps *caps;
  gconstpointer answer;
  guint answer_size;

  pipeline = gst_parse_launch ("filesrc name=src ! decodebin ! "
      "audioconvert dithering=0 ! appsink name=sink", NULL);

  src = gst_bin_get_by_name (GST_BIN (pipeline), "src");
  g_object_set (src, "location", fn, NULL);
  gst_object_unref (src);

  sink = gst_bin_get_by_name (GST_BIN (pipeline), "sink");
  caps = gst_caps_new_simple ("audio/x-raw",
      "format", G_TYPE_STRING, GST_AUDIO_NE (S16),
      "rate", G_TYPE_INT, SAMPLE_FREQ, "channels", G_TYPE_INT, 2, NULL);
  g_object_set (sink, "caps", caps, "sync", FALSE, NULL);
  gst_caps_unref (caps);

  gst_element_set_state (pipeline, GST_STATE_PLAYING);

  /* wait for preroll, so we can seek */
  gst_bus_timed_pop_filtered (GST_ELEMENT_BUS (pipeline), GST_CLOCK_TIME_NONE,
      GST_MESSAGE_ASYNC_DONE);

  /* first, read entire file to end */
  adapter = gst_adapter_new ();
  while ((sample = gst_app_sink_pull_sample (GST_APP_SINK (sink)))) {
    gst_adapter_push (adapter, gst_buffer_ref (gst_sample_get_buffer (sample)));
    gst_sample_unref (sample);
  }
  answer_size = gst_adapter_available (adapter);
  answer = gst_adapter_map (adapter, answer_size);
  /* g_print ("%s: read %u bytes\n", fn, answer_size); */

  g_print ("%10s\t%10s\t%10s\n", "requested", "sample per ts", "actual(data)");

  while (seek_positions != NULL) {
    gconstpointer found;
    GstMapInfo map;
    GstBuffer *buf;
    gboolean ret;
    guint actual_position, buffer_timestamp_position;
    guint seek_sample;

    seek_sample = GPOINTER_TO_UINT (seek_positions->data);

    ret = gst_element_seek_simple (pipeline, GST_FORMAT_TIME,
        GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
        sample_to_nanotime (seek_sample));

    g_assert (ret);

    sample = gst_app_sink_pull_sample (GST_APP_SINK (sink));

    buf = gst_sample_get_buffer (sample);
    gst_buffer_map (buf, &map, GST_MAP_READ);
    found = memmem (answer, answer_size, map.data, map.size);
    gst_buffer_unmap (buf, &map);

    g_assert (found != NULL);
    actual_position = ((goffset) ((guint8 *) found - (guint8 *) answer)) / 4;
    buffer_timestamp_position = nanotime_to_sample (GST_BUFFER_PTS (buf));
    g_print ("%10u\t%10u\t%10u\n", seek_sample, buffer_timestamp_position,
        actual_position);
    gst_sample_unref (sample);

    seek_positions = seek_positions->next;
  }

  gst_element_set_state (pipeline, GST_STATE_NULL);
  gst_object_unref (sink);
  gst_object_unref (pipeline);
  g_object_unref (adapter);
}
Example #15
0
static GstFlowReturn
gst_pnmdec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame,
    GstAdapter * adapter, gboolean at_eos)
{
  gsize size;
  GstPnmdec *s = GST_PNMDEC (decoder);
  GstFlowReturn r = GST_FLOW_OK;
  guint offset = 0;
  GstVideoFormat format = GST_VIDEO_FORMAT_UNKNOWN;
  const guint8 *raw_data;
  GstVideoCodecState *output_state;

  GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);

  size = gst_adapter_available (adapter);
  if (size < 8) {
    goto need_more_data;
  }
  raw_data = gst_adapter_map (adapter, size);

  if (s->mngr.info.fields != GST_PNM_INFO_FIELDS_ALL) {
    GstPnmInfoMngrResult res;

    res = gst_pnm_info_mngr_scan (&s->mngr, raw_data, size);

    switch (res) {
      case GST_PNM_INFO_MNGR_RESULT_FAILED:
        r = GST_FLOW_ERROR;
        goto out;
      case GST_PNM_INFO_MNGR_RESULT_READING:
        r = GST_FLOW_OK;
        goto out;
      case GST_PNM_INFO_MNGR_RESULT_FINISHED:
        switch (s->mngr.info.type) {
          case GST_PNM_TYPE_BITMAP:
            if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
              r = GST_FLOW_ERROR;
              goto out;
            }
            s->size = s->mngr.info.width * s->mngr.info.height * 1;
            format = GST_VIDEO_FORMAT_GRAY8;
            break;
          case GST_PNM_TYPE_GRAYMAP:
            s->size = s->mngr.info.width * s->mngr.info.height * 1;
            format = GST_VIDEO_FORMAT_GRAY8;
            break;
          case GST_PNM_TYPE_PIXMAP:
            s->size = s->mngr.info.width * s->mngr.info.height * 3;
            format = GST_VIDEO_FORMAT_RGB;
            break;
        }
        output_state =
            gst_video_decoder_set_output_state (GST_VIDEO_DECODER (s), format,
            s->mngr.info.width, s->mngr.info.height, s->input_state);
        gst_video_codec_state_unref (output_state);
        if (gst_video_decoder_negotiate (GST_VIDEO_DECODER (s)) == FALSE) {
          r = GST_FLOW_NOT_NEGOTIATED;
          goto out;
        }

        if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
          s->mngr.data_offset++;
          /* It is not possible to know the size of input ascii data to parse.
             So we have to parse and know the number of pixels parsed and
             then finally decide when we have full frame */
          s->buf = gst_buffer_new_and_alloc (s->size);
        }
        offset = s->mngr.data_offset;
        gst_adapter_flush (adapter, offset);
        size = size - offset;
    }
  }

  if (s->mngr.info.encoding == GST_PNM_ENCODING_ASCII) {
    /* Parse ASCII data dn populate s->current_size with the number of 
       bytes actually parsed from the input data */
    r = gst_pnmdec_parse_ascii (s, raw_data + offset, size);
  } else {
    /* Bitmap Contains 8 pixels in a byte */
    if (s->mngr.info.type == GST_PNM_TYPE_BITMAP)
      s->current_size += (size * 8);
    else
      s->current_size += size;
  }

  gst_video_decoder_add_to_frame (decoder, size);
  if (s->size <= s->current_size) {
    goto have_full_frame;
  }

need_more_data:
  return GST_VIDEO_DECODER_FLOW_NEED_DATA;

have_full_frame:
  return gst_video_decoder_have_frame (decoder);

out:
  return r;
}
Example #16
0
static GstFlowReturn
gst_rtp_h263p_pay_flush (GstRtpH263PPay * rtph263ppay)
{
  guint avail;
  GstBuffer *outbuf;
  GstFlowReturn ret;
  gboolean fragmented;

  avail = gst_adapter_available (rtph263ppay->adapter);
  if (avail == 0)
    return GST_FLOW_OK;

  fragmented = FALSE;
  /* This algorithm assumes the H263/+/++ encoder sends complete frames in each
   * buffer */
  /* With Fragmentation Mode at GST_FRAGMENTATION_MODE_NORMAL:
   *  This algorithm implements the Follow-on packets method for packetization.
   *  This assumes low packet loss network. 
   * With Fragmentation Mode at GST_FRAGMENTATION_MODE_SYNC:
   *  This algorithm separates large frames at synchronisation points (Segments)
   *  (See RFC 4629 section 6). It would be interesting to have a property such as network
   *  quality to select between both packetization methods */
  /* TODO Add VRC supprt (See RFC 4629 section 5.2) */

  while (avail > 0) {
    guint towrite;
    guint8 *payload;
    guint payload_len;
    gint header_len;
    guint next_gop = 0;
    gboolean found_gob = FALSE;
    GstRTPBuffer rtp = { NULL };

    if (rtph263ppay->fragmentation_mode == GST_FRAGMENTATION_MODE_SYNC) {
      /* start after 1st gop possible */
      guint parsed_len = 3;
      const guint8 *parse_data = NULL;

      parse_data = gst_adapter_map (rtph263ppay->adapter, avail);

      /* Check if we have a gob or eos , eossbs */
      /* FIXME EOS and EOSSBS packets should never contain any gobs and vice-versa */
      if (avail >= 3 && *parse_data == 0 && *(parse_data + 1) == 0
          && *(parse_data + 2) >= 0x80) {
        GST_DEBUG_OBJECT (rtph263ppay, " Found GOB header");
        found_gob = TRUE;
      }
      /* Find next and cut the packet accordingly */
      /* TODO we should get as many gobs as possible until MTU is reached, this
       * code seems to just get one GOB per packet */
      while (parsed_len + 2 < avail) {
        if (parse_data[parsed_len] == 0 && parse_data[parsed_len + 1] == 0
            && parse_data[parsed_len + 2] >= 0x80) {
          next_gop = parsed_len;
          GST_DEBUG_OBJECT (rtph263ppay, " Next GOB Detected at :  %d",
              next_gop);
          break;
        }
        parsed_len++;
      }
      gst_adapter_unmap (rtph263ppay->adapter);
    }

    /* for picture start frames (non-fragmented), we need to remove the first
     * two 0x00 bytes and set P=1 */
    header_len = (fragmented && !found_gob) ? 2 : 0;

    towrite = MIN (avail, gst_rtp_buffer_calc_payload_len
        (GST_RTP_BASE_PAYLOAD_MTU (rtph263ppay) - header_len, 0, 0));

    if (next_gop > 0)
      towrite = MIN (next_gop, towrite);

    payload_len = header_len + towrite;

    outbuf = gst_rtp_buffer_new_allocate (payload_len, 0, 0);

    gst_rtp_buffer_map (outbuf, GST_MAP_WRITE, &rtp);
    /* last fragment gets the marker bit set */
    gst_rtp_buffer_set_marker (&rtp, avail > towrite ? 0 : 1);

    payload = gst_rtp_buffer_get_payload (&rtp);

    gst_adapter_copy (rtph263ppay->adapter, &payload[header_len], 0, towrite);

    /*  0                   1
     *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     * |   RR    |P|V|   PLEN    |PEBIT|
     * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     */
    /* if fragmented or gop header , write p bit =1 */
    payload[0] = (fragmented && !found_gob) ? 0x00 : 0x04;
    payload[1] = 0;

    GST_BUFFER_TIMESTAMP (outbuf) = rtph263ppay->first_timestamp;
    GST_BUFFER_DURATION (outbuf) = rtph263ppay->first_duration;
    gst_rtp_buffer_unmap (&rtp);

    gst_adapter_flush (rtph263ppay->adapter, towrite);

    ret =
        gst_rtp_base_payload_push (GST_RTP_BASE_PAYLOAD (rtph263ppay), outbuf);

    avail -= towrite;
    fragmented = TRUE;
  }

  return ret;
}
Example #17
0
static GstFlowReturn
gst_real_audio_demux_parse_header (GstRealAudioDemux * demux)
{
  const guint8 *data;
  gchar *codec_name = NULL;
  GstCaps *caps = NULL;
  GstEvent *event;
  gchar *stream_id;
  guint avail;

  g_assert (demux->ra_version == 4 || demux->ra_version == 3);

  avail = gst_adapter_available (demux->adapter);
  if (avail < 16)
    return GST_FLOW_OK;

  if (!gst_real_audio_demux_get_data_offset_from_header (demux))
    return GST_FLOW_ERROR;      /* shouldn't happen */

  GST_DEBUG_OBJECT (demux, "data_offset  = %u", demux->data_offset);

  if (avail + 6 < demux->data_offset) {
    GST_DEBUG_OBJECT (demux, "Need %u bytes, but only %u available now",
        demux->data_offset - 6, avail);
    return GST_FLOW_OK;
  }

  data = gst_adapter_map (demux->adapter, demux->data_offset - 6);
  g_assert (data);

  switch (demux->ra_version) {
    case 3:
      demux->fourcc = GST_RM_AUD_14_4;
      demux->packet_size = 20;
      demux->sample_rate = 8000;
      demux->channels = 1;
      demux->sample_width = 16;
      demux->flavour = 1;
      demux->leaf_size = 0;
      demux->height = 0;
      break;
    case 4:
      demux->flavour = GST_READ_UINT16_BE (data + 16);
      /* demux->frame_size = GST_READ_UINT32_BE (data + 36); */
      demux->leaf_size = GST_READ_UINT16_BE (data + 38);
      demux->height = GST_READ_UINT16_BE (data + 34);
      demux->packet_size = GST_READ_UINT32_BE (data + 18);
      demux->sample_rate = GST_READ_UINT16_BE (data + 42);
      demux->sample_width = GST_READ_UINT16_BE (data + 46);
      demux->channels = GST_READ_UINT16_BE (data + 48);
      demux->fourcc = GST_READ_UINT32_LE (data + 56);
      demux->pending_tags = gst_rm_utils_read_tags (data + 63,
          demux->data_offset - 63, gst_rm_utils_read_string8);
      if (demux->pending_tags)
        gst_tag_list_set_scope (demux->pending_tags, GST_TAG_SCOPE_GLOBAL);
      break;
    default:
      g_assert_not_reached ();
#if 0
    case 5:
      demux->flavour = GST_READ_UINT16_BE (data + 16);
      /* demux->frame_size = GST_READ_UINT32_BE (data + 36); */
      demux->leaf_size = GST_READ_UINT16_BE (data + 38);
      demux->height = GST_READ_UINT16_BE (data + 34);

      demux->sample_rate = GST_READ_UINT16_BE (data + 48);
      demux->sample_width = GST_READ_UINT16_BE (data + 52);
      demux->n_channels = GST_READ_UINT16_BE (data + 54);
      demux->fourcc = RMDEMUX_FOURCC_GET (data + 60);
      break;
#endif
  }

  GST_INFO_OBJECT (demux, "packet_size  = %u", demux->packet_size);
  GST_INFO_OBJECT (demux, "sample_rate  = %u", demux->sample_rate);
  GST_INFO_OBJECT (demux, "sample_width = %u", demux->sample_width);
  GST_INFO_OBJECT (demux, "channels     = %u", demux->channels);
  GST_INFO_OBJECT (demux, "fourcc       = '%" GST_FOURCC_FORMAT "' (%08X)",
      GST_FOURCC_ARGS (demux->fourcc), demux->fourcc);

  switch (demux->fourcc) {
    case GST_RM_AUD_14_4:
      caps = gst_caps_new_simple ("audio/x-pn-realaudio", "raversion",
          G_TYPE_INT, 1, NULL);
      demux->byterate_num = 1000;
      demux->byterate_denom = 1;
      break;

    case GST_RM_AUD_28_8:
      /* FIXME: needs descrambling */
      caps = gst_caps_new_simple ("audio/x-pn-realaudio", "raversion",
          G_TYPE_INT, 2, NULL);
      break;

    case GST_RM_AUD_DNET:
      caps = gst_caps_new_simple ("audio/x-ac3", "rate", G_TYPE_INT,
          demux->sample_rate, NULL);
      if (demux->packet_size == 0 || demux->sample_rate == 0)
        goto broken_file;
      demux->byterate_num = demux->packet_size * demux->sample_rate;
      demux->byterate_denom = 1536;
      break;

      /* Sipro/ACELP.NET Voice Codec (MIME unknown) */
    case GST_RM_AUD_SIPR:
      caps = gst_caps_new_empty_simple ("audio/x-sipro");
      break;

    default:
      GST_WARNING_OBJECT (demux, "unknown fourcc %08X", demux->fourcc);
      break;
  }

  if (caps == NULL)
    goto unknown_fourcc;

  gst_caps_set_simple (caps,
      "flavor", G_TYPE_INT, demux->flavour,
      "rate", G_TYPE_INT, demux->sample_rate,
      "channels", G_TYPE_INT, demux->channels,
      "width", G_TYPE_INT, demux->sample_width,
      "leaf_size", G_TYPE_INT, demux->leaf_size,
      "packet_size", G_TYPE_INT, demux->packet_size,
      "height", G_TYPE_INT, demux->height, NULL);

  GST_INFO_OBJECT (demux, "Adding source pad, caps %" GST_PTR_FORMAT, caps);
  demux->srcpad = gst_pad_new_from_static_template (&src_template, "src");
  gst_pad_set_event_function (demux->srcpad,
      GST_DEBUG_FUNCPTR (gst_real_audio_demux_src_event));
  gst_pad_set_query_function (demux->srcpad,
      GST_DEBUG_FUNCPTR (gst_real_audio_demux_src_query));
  gst_pad_set_active (demux->srcpad, TRUE);
  gst_pad_use_fixed_caps (demux->srcpad);

  stream_id =
      gst_pad_create_stream_id (demux->srcpad, GST_ELEMENT_CAST (demux), NULL);

  event = gst_pad_get_sticky_event (demux->sinkpad, GST_EVENT_STREAM_START, 0);
  if (event) {
    if (gst_event_parse_group_id (event, &demux->group_id))
      demux->have_group_id = TRUE;
    else
      demux->have_group_id = FALSE;
    gst_event_unref (event);
  } else if (!demux->have_group_id) {
    demux->have_group_id = TRUE;
    demux->group_id = gst_util_group_id_next ();
  }

  event = gst_event_new_stream_start (stream_id);
  if (demux->have_group_id)
    gst_event_set_group_id (event, demux->group_id);

  gst_pad_push_event (demux->srcpad, event);
  g_free (stream_id);

  gst_pad_set_caps (demux->srcpad, caps);
  codec_name = gst_pb_utils_get_codec_description (caps);
  gst_caps_unref (caps);

  gst_element_add_pad (GST_ELEMENT (demux), demux->srcpad);

  if (demux->byterate_num > 0 && demux->byterate_denom > 0) {
    GstFormat bformat = GST_FORMAT_BYTES;
    gint64 size_bytes = 0;

    GST_INFO_OBJECT (demux, "byte rate = %u/%u = %u bytes/sec",
        demux->byterate_num, demux->byterate_denom,
        demux->byterate_num / demux->byterate_denom);

    if (gst_pad_peer_query_duration (demux->sinkpad, bformat, &size_bytes)) {
      demux->duration =
          gst_real_demux_get_timestamp_from_offset (demux, size_bytes);
      demux->upstream_size = size_bytes;
      GST_INFO_OBJECT (demux, "upstream_size = %" G_GUINT64_FORMAT,
          demux->upstream_size);
      GST_INFO_OBJECT (demux, "duration      = %" GST_TIME_FORMAT,
          GST_TIME_ARGS (demux->duration));
    }
  }

  demux->need_newsegment = TRUE;

  if (codec_name) {
    if (demux->pending_tags == NULL) {
      demux->pending_tags = gst_tag_list_new_empty ();
      gst_tag_list_set_scope (demux->pending_tags, GST_TAG_SCOPE_GLOBAL);
    }

    gst_tag_list_add (demux->pending_tags, GST_TAG_MERGE_REPLACE,
        GST_TAG_AUDIO_CODEC, codec_name, NULL);
    g_free (codec_name);
  }

  gst_adapter_unmap (demux->adapter);
  gst_adapter_flush (demux->adapter, demux->data_offset - 6);

  demux->state = REAL_AUDIO_DEMUX_STATE_DATA;
  demux->need_newsegment = TRUE;

  return GST_FLOW_OK;

/* ERRORS */
unknown_fourcc:
  {
    GST_ELEMENT_ERROR (GST_ELEMENT (demux), STREAM, DECODE, (NULL),
        ("Unknown fourcc '%" GST_FOURCC_FORMAT "'",
            GST_FOURCC_ARGS (demux->fourcc)));
    return GST_FLOW_ERROR;
  }
broken_file:
  {
    GST_ELEMENT_ERROR (GST_ELEMENT (demux), STREAM, DECODE, (NULL),
        ("Broken file - invalid sample_rate or other header value"));
    return GST_FLOW_ERROR;
  }

}
Example #18
0
static GstFlowReturn
gst_gdp_depay_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
  GstGDPDepay *this;
  GstFlowReturn ret = GST_FLOW_OK;
  GstCaps *caps;
  GstBuffer *buf;
  GstEvent *event;
  guint available;

  this = GST_GDP_DEPAY (parent);

  /* On DISCONT, get rid of accumulated data. We assume a buffer after the
   * DISCONT contains (part of) a new valid header, if not we error because we
   * lost sync */
  if (GST_BUFFER_IS_DISCONT (buffer)) {
    gst_adapter_clear (this->adapter);
    this->state = GST_GDP_DEPAY_STATE_HEADER;
  }
  gst_adapter_push (this->adapter, buffer);

  while (TRUE) {
    switch (this->state) {
      case GST_GDP_DEPAY_STATE_HEADER:
      {
        guint8 *header;

        /* collect a complete header, validate and store the header. Figure out
         * the payload length and switch to the PAYLOAD state */
        available = gst_adapter_available (this->adapter);
        if (available < GST_DP_HEADER_LENGTH)
          goto done;

        GST_LOG_OBJECT (this, "reading GDP header from adapter");
        header = gst_adapter_take (this->adapter, GST_DP_HEADER_LENGTH);
        if (!gst_dp_validate_header (GST_DP_HEADER_LENGTH, header)) {
          g_free (header);
          goto header_validate_error;
        }

        /* store types and payload length. Also store the header, which we need
         * to make the payload. */
        this->payload_length = gst_dp_header_payload_length (header);
        this->payload_type = gst_dp_header_payload_type (header);
        /* free previous header and store new one. */
        g_free (this->header);
        this->header = header;

        GST_LOG_OBJECT (this,
            "read GDP header, payload size %d, payload type %d, switching to state PAYLOAD",
            this->payload_length, this->payload_type);
        this->state = GST_GDP_DEPAY_STATE_PAYLOAD;
        break;
      }
      case GST_GDP_DEPAY_STATE_PAYLOAD:
      {
        /* in this state we wait for all the payload data to be available in the
         * adapter. Then we switch to the state where we actually process the
         * payload. */
        available = gst_adapter_available (this->adapter);
        if (available < this->payload_length)
          goto done;

        /* change state based on type */
        if (this->payload_type == GST_DP_PAYLOAD_BUFFER) {
          GST_LOG_OBJECT (this, "switching to state BUFFER");
          this->state = GST_GDP_DEPAY_STATE_BUFFER;
        } else if (this->payload_type == GST_DP_PAYLOAD_CAPS) {
          GST_LOG_OBJECT (this, "switching to state CAPS");
          this->state = GST_GDP_DEPAY_STATE_CAPS;
        } else if (this->payload_type >= GST_DP_PAYLOAD_EVENT_NONE) {
          GST_LOG_OBJECT (this, "switching to state EVENT");
          this->state = GST_GDP_DEPAY_STATE_EVENT;
        } else {
          goto wrong_type;
        }

        if (this->payload_length) {
          const guint8 *data;
          gboolean res;

          data = gst_adapter_map (this->adapter, this->payload_length);
          res = gst_dp_validate_payload (GST_DP_HEADER_LENGTH, this->header,
              data);
          gst_adapter_unmap (this->adapter);

          if (!res)
            goto payload_validate_error;
        }

        break;
      }
      case GST_GDP_DEPAY_STATE_BUFFER:
      {
        /* if we receive a buffer without caps first, we error out */
        if (!this->caps)
          goto no_caps;

        GST_LOG_OBJECT (this, "reading GDP buffer from adapter");
        buf = gst_dp_buffer_from_header (GST_DP_HEADER_LENGTH, this->header);
        if (!buf)
          goto buffer_failed;

        /* now take the payload if there is any */
        if (this->payload_length > 0) {
          GstMapInfo map;

          gst_buffer_map (buf, &map, GST_MAP_WRITE);
          gst_adapter_copy (this->adapter, map.data, 0, this->payload_length);
          gst_buffer_unmap (buf, &map);

          gst_adapter_flush (this->adapter, this->payload_length);
        }

        /* set caps and push */
        GST_LOG_OBJECT (this, "deserialized buffer %p, pushing, timestamp %"
            GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT
            ", offset %" G_GINT64_FORMAT ", offset_end %" G_GINT64_FORMAT
            ", size %" G_GSIZE_FORMAT ", flags 0x%x",
            buf,
            GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
            GST_TIME_ARGS (GST_BUFFER_DURATION (buf)),
            GST_BUFFER_OFFSET (buf), GST_BUFFER_OFFSET_END (buf),
            gst_buffer_get_size (buf), GST_BUFFER_FLAGS (buf));
        ret = gst_pad_push (this->srcpad, buf);
        if (ret != GST_FLOW_OK)
          goto push_error;

        GST_LOG_OBJECT (this, "switching to state HEADER");
        this->state = GST_GDP_DEPAY_STATE_HEADER;
        break;
      }
      case GST_GDP_DEPAY_STATE_CAPS:
      {
        guint8 *payload;

        /* take the payload of the caps */
        GST_LOG_OBJECT (this, "reading GDP caps from adapter");
        payload = gst_adapter_take (this->adapter, this->payload_length);
        caps = gst_dp_caps_from_packet (GST_DP_HEADER_LENGTH, this->header,
            payload);
        g_free (payload);
        if (!caps)
          goto caps_failed;

        GST_DEBUG_OBJECT (this, "deserialized caps %" GST_PTR_FORMAT, caps);
        gst_caps_replace (&(this->caps), caps);
        gst_pad_set_caps (this->srcpad, caps);
        /* drop the creation ref we still have */
        gst_caps_unref (caps);

        GST_LOG_OBJECT (this, "switching to state HEADER");
        this->state = GST_GDP_DEPAY_STATE_HEADER;
        break;
      }
      case GST_GDP_DEPAY_STATE_EVENT:
      {
        guint8 *payload;

        GST_LOG_OBJECT (this, "reading GDP event from adapter");

        /* adapter doesn't like 0 length payload */
        if (this->payload_length > 0)
          payload = gst_adapter_take (this->adapter, this->payload_length);
        else
          payload = NULL;
        event = gst_dp_event_from_packet (GST_DP_HEADER_LENGTH, this->header,
            payload);
        g_free (payload);
        if (!event)
          goto event_failed;

        GST_DEBUG_OBJECT (this, "deserialized event %p of type %s, pushing",
            event, gst_event_type_get_name (event->type));
        gst_pad_push_event (this->srcpad, event);

        GST_LOG_OBJECT (this, "switching to state HEADER");
        this->state = GST_GDP_DEPAY_STATE_HEADER;
        break;
      }
    }
  }

done:
  return ret;

  /* ERRORS */
header_validate_error:
  {
    GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
        ("GDP packet header does not validate"));
    ret = GST_FLOW_ERROR;
    goto done;
  }
payload_validate_error:
  {
    GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
        ("GDP packet payload does not validate"));
    ret = GST_FLOW_ERROR;
    goto done;
  }
wrong_type:
  {
    GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
        ("GDP packet header is of wrong type"));
    ret = GST_FLOW_ERROR;
    goto done;
  }
no_caps:
  {
    GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
        ("Received a buffer without first receiving caps"));
    ret = GST_FLOW_NOT_NEGOTIATED;
    goto done;
  }
buffer_failed:
  {
    GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
        ("could not create buffer from GDP packet"));
    ret = GST_FLOW_ERROR;
    goto done;
  }
push_error:
  {
    GST_WARNING_OBJECT (this, "pushing depayloaded buffer returned %d", ret);
    goto done;
  }
caps_failed:
  {
    GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
        ("could not create caps from GDP packet"));
    ret = GST_FLOW_ERROR;
    goto done;
  }
event_failed:
  {
    GST_ELEMENT_ERROR (this, STREAM, DECODE, (NULL),
        ("could not create event from GDP packet"));
    ret = GST_FLOW_ERROR;
    goto done;
  }
}
Example #19
0
static gint
multipart_parse_header (GstMultipartDemux * multipart)
{
  const guint8 *data;
  const guint8 *dataend;
  gchar *boundary;
  int boundary_len;
  int datalen;
  guint8 *pos;
  guint8 *end, *next;

  datalen = gst_adapter_available (multipart->adapter);
  data = gst_adapter_map (multipart->adapter, datalen);
  dataend = data + datalen;

  /* Skip leading whitespace, pos endposition should at least leave space for
   * the boundary and a \n */
  for (pos = (guint8 *) data; pos < dataend - 4 && g_ascii_isspace (*pos);
      pos++);

  if (pos >= dataend - 4)
    goto need_more_data;

  if (G_UNLIKELY (pos[0] != '-' || pos[1] != '-')) {
    GST_DEBUG_OBJECT (multipart, "No boundary available");
    goto wrong_header;
  }

  /* First the boundary */
  if (!get_line_end (pos, dataend, &end, &next))
    goto need_more_data;

  /* Ignore the leading -- */
  boundary_len = end - pos - 2;
  boundary = (gchar *) pos + 2;
  if (boundary_len < 1) {
    GST_DEBUG_OBJECT (multipart, "No boundary available");
    goto wrong_header;
  }

  if (G_UNLIKELY (multipart->boundary == NULL)) {
    /* First time we see the boundary, copy it */
    multipart->boundary = g_strndup (boundary, boundary_len);
    multipart->boundary_len = boundary_len;
  } else if (G_UNLIKELY (boundary_len != multipart->boundary_len)) {
    /* Something odd is going on, either the boundary indicated EOS or it's
     * invalid */
    if (G_UNLIKELY (boundary_len == multipart->boundary_len + 2 &&
            !strncmp (boundary, multipart->boundary, multipart->boundary_len) &&
            !strncmp (boundary + multipart->boundary_len, "--", 2)))
      goto eos;

    GST_DEBUG_OBJECT (multipart,
        "Boundary length doesn't match detected boundary (%d <> %d",
        boundary_len, multipart->boundary_len);
    goto wrong_header;
  } else if (G_UNLIKELY (strncmp (boundary, multipart->boundary, boundary_len))) {
    GST_DEBUG_OBJECT (multipart, "Boundary doesn't match previous boundary");
    goto wrong_header;
  }

  pos = next;
  while (get_line_end (pos, dataend, &end, &next)) {
    guint len = end - pos;

    if (len == 0) {
      /* empty line, data starts behind us */
      GST_DEBUG_OBJECT (multipart,
          "Parsed the header - boundary: %s, mime-type: %s, content-length: %d",
          multipart->boundary, multipart->mime_type, multipart->content_length);
      gst_adapter_unmap (multipart->adapter);
      return next - data;
    }

    if (len >= 14 && !g_ascii_strncasecmp ("content-type:", (gchar *) pos, 13)) {
      guint mime_len;

      /* only take the mime type up to the first ; if any. After ; there can be
       * properties that we don't handle yet. */
      mime_len = get_mime_len (pos + 14, len - 14);

      g_free (multipart->mime_type);
      multipart->mime_type = g_ascii_strdown ((gchar *) pos + 14, mime_len);
    } else if (len >= 15 &&
        !g_ascii_strncasecmp ("content-length:", (gchar *) pos, 15)) {
      multipart->content_length =
          g_ascii_strtoull ((gchar *) pos + 15, NULL, 10);
    }
    pos = next;
  }

need_more_data:
  GST_DEBUG_OBJECT (multipart, "Need more data for the header");
  gst_adapter_unmap (multipart->adapter);

  return MULTIPART_NEED_MORE_DATA;

wrong_header:
  {
    GST_ELEMENT_ERROR (multipart, STREAM, DEMUX, (NULL),
        ("Boundary not found in the multipart header"));
    gst_adapter_unmap (multipart->adapter);
    return MULTIPART_DATA_ERROR;
  }
eos:
  {
    GST_DEBUG_OBJECT (multipart, "we are EOS");
    gst_adapter_unmap (multipart->adapter);
    return MULTIPART_DATA_EOS;
  }
}
static GstFlowReturn
gst_type_find_element_chain_do_typefinding (GstTypeFindElement * typefind,
    gboolean check_avail, gboolean at_eos)
{
  GstTypeFindProbability probability;
  GstCaps *caps = NULL;
  gsize avail;
  const guint8 *data;
  gboolean have_min, have_max;

  GST_OBJECT_LOCK (typefind);
  if (typefind->force_caps) {
    caps = gst_caps_ref (typefind->force_caps);
    probability = GST_TYPE_FIND_MAXIMUM;
  }

  if (!caps) {
    avail = gst_adapter_available (typefind->adapter);

    if (check_avail) {
      have_min = avail >= TYPE_FIND_MIN_SIZE;
      have_max = avail >= TYPE_FIND_MAX_SIZE;
    } else {
      have_min = avail > 0;
      have_max = TRUE;
    }

    if (!have_min)
      goto not_enough_data;

    /* map all available data */
    data = gst_adapter_map (typefind->adapter, avail);
    caps = gst_type_find_helper_for_data (GST_OBJECT (typefind),
        data, avail, &probability);
    gst_adapter_unmap (typefind->adapter);

    if (caps == NULL && have_max)
      goto no_type_found;
    else if (caps == NULL)
      goto wait_for_data;

    /* found a type */
    if (probability < typefind->min_probability)
      goto low_probability;
  }

  GST_OBJECT_UNLOCK (typefind);

  /* probability is good enough too, so let's make it known ... emiting this
   * signal calls our object handler which sets the caps. */
  gst_type_find_element_emit_have_type (typefind, probability, caps);

  /* .. and send out the accumulated data */
  stop_typefinding (typefind);
  gst_caps_unref (caps);

  return GST_FLOW_OK;

not_enough_data:
  {
    GST_OBJECT_UNLOCK (typefind);

    if (at_eos) {
      GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND,
          (_("Stream doesn't contain enough data.")),
          ("Can't typefind stream"));
      return GST_FLOW_ERROR;
    } else {
      GST_DEBUG_OBJECT (typefind, "not enough data for typefinding yet "
          "(%" G_GSIZE_FORMAT " bytes)", avail);
      return GST_FLOW_OK;
    }
  }
no_type_found:
  {
    GST_OBJECT_UNLOCK (typefind);
    GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
    stop_typefinding (typefind);
    return GST_FLOW_ERROR;
  }
wait_for_data:
  {
    GST_OBJECT_UNLOCK (typefind);

    if (at_eos) {
      GST_ELEMENT_ERROR (typefind, STREAM, TYPE_NOT_FOUND,
          (_("Stream doesn't contain enough data.")),
          ("Can't typefind stream"));
      return GST_FLOW_ERROR;
    } else {
      GST_DEBUG_OBJECT (typefind,
          "no caps found with %" G_GSIZE_FORMAT " bytes of data, "
          "waiting for more data", avail);
      return GST_FLOW_OK;
    }
  }
low_probability:
  {
    GST_DEBUG_OBJECT (typefind, "found caps %" GST_PTR_FORMAT ", but "
        "probability is %u which is lower than the required minimum of %u",
        caps, probability, typefind->min_probability);

    gst_caps_unref (caps);

    if (have_max)
      goto no_type_found;

    GST_OBJECT_UNLOCK (typefind);
    GST_DEBUG_OBJECT (typefind, "waiting for more data to try again");
    return GST_FLOW_OK;
  }
}
Example #21
0
static GstFlowReturn
gst_msdkdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame)
{
  GstMsdkDec *thiz = GST_MSDKDEC (decoder);
  GstMsdkDecClass *klass = GST_MSDKDEC_GET_CLASS (thiz);
  GstFlowReturn flow;
  GstBuffer *buffer, *input_buffer = NULL;
  GstVideoInfo alloc_info;
  MsdkDecTask *task = NULL;
  mfxBitstream bitstream;
  MsdkSurface *surface = NULL;
  mfxSession session;
  mfxStatus status;
  GstMapInfo map_info;
  guint i;
  gsize data_size;
  gboolean hard_reset = FALSE;

  /* configure the subclass in order to fill the CodecID field of
   * mfxVideoParam and also to load the PluginID for some of the
   * codecs which is mandatory to invoke the
   * MFXVideoDECODE_DecodeHeader API.
   *
   * For non packetized formats (currently only vc1), there
   * could be headers received as codec_data which are not available
   * instream and in that case subclass implementation will
   * push it to the internal adapter. We invoke the subclass configure
   * well early to make sure the codec_data received has been correctly
   * pushed to the adapter by the subclasses before doing
   * the DecodeHeader() later on
   */
  if (!thiz->initialized || thiz->do_renego) {
    /* Clear the internal adapter in renegotiation for non-packetized
     * formats */
    if (!thiz->is_packetized)
      gst_adapter_clear (thiz->adapter);

    if (!klass->configure || !klass->configure (thiz)) {
      flow = GST_FLOW_OK;
      goto error;
    }
  }

  /* Current frame-codec could be pushed and released before this
   * function ends -- because msdkdec pushes the oldest frame,
   * according its PTS, and it could be this very same frame-codec
   * among others pending frame-codecs.
   *
   * Instead of copying the input data into the mfxBitstream, let's
   * keep an extra reference to frame-codec's input buffer */
  input_buffer = gst_buffer_ref (frame->input_buffer);
  if (!gst_buffer_map (input_buffer, &map_info, GST_MAP_READ)) {
    gst_buffer_unref (input_buffer);
    return GST_FLOW_ERROR;
  }

  memset (&bitstream, 0, sizeof (bitstream));

  if (thiz->is_packetized) {
    /* Packetized stream: We prefer to have a parser as connected upstream
     * element to the decoder */
    bitstream.Data = map_info.data;
    bitstream.DataLength = map_info.size;
    bitstream.MaxLength = map_info.size;
    bitstream.DataFlag = MFX_BITSTREAM_COMPLETE_FRAME;
  } else {
    /* Non packetized streams: eg: vc1 advanced profile with per buffer bdu */
    gst_adapter_push (thiz->adapter, gst_buffer_ref (input_buffer));
    data_size = gst_adapter_available (thiz->adapter);

    bitstream.Data = (mfxU8 *) gst_adapter_map (thiz->adapter, data_size);
    bitstream.DataLength = (mfxU32) data_size;
    bitstream.MaxLength = bitstream.DataLength;
  }
  GST_INFO_OBJECT (thiz,
      "mfxBitStream=> DataLength:%d DataOffset:%d MaxLength:%d",
      bitstream.DataLength, bitstream.DataOffset, bitstream.MaxLength);

  session = gst_msdk_context_get_session (thiz->context);

  if (!thiz->initialized || thiz->do_renego) {

    /* gstreamer caps will not bring all the necessary parameters
     * required for optimal decode configuration. For eg: the required numbers
     * of surfaces to be allocated can be calculated based on H264 SEI header
     * and this information can't be retrieved from the negotiated caps.
     * So instead of introducing the codecparser dependency to parse the headers
     * inside msdk plugin, we simply use the mfx apis to extract header information */
    status = MFXVideoDECODE_DecodeHeader (session, &bitstream, &thiz->param);
    if (status == MFX_ERR_MORE_DATA) {
      flow = GST_FLOW_OK;
      goto done;
    }

    if (!thiz->initialized)
      hard_reset = TRUE;
    else if (thiz->allocation_caps) {
      gst_video_info_from_caps (&alloc_info, thiz->allocation_caps);

      /* Check whether we need complete reset for dynamic resolution change */
      if (thiz->param.mfx.FrameInfo.Width > GST_VIDEO_INFO_WIDTH (&alloc_info)
          || thiz->param.mfx.FrameInfo.Height >
          GST_VIDEO_INFO_HEIGHT (&alloc_info))
        hard_reset = TRUE;
    }

    /* if subclass requested for the force reset */
    if (thiz->force_reset_on_res_change)
      hard_reset = TRUE;

    /* Config changed dynamically and we are going to do a full reset,
     * this will unref the input frame which has the new configuration.
     * Keep a ref to the input_frame to keep it alive */
    if (thiz->initialized && thiz->do_renego)
      gst_video_codec_frame_ref (frame);

    if (!gst_msdkdec_negotiate (thiz, hard_reset)) {
      GST_ELEMENT_ERROR (thiz, CORE, NEGOTIATION,
          ("Could not negotiate the stream"), (NULL));
      flow = GST_FLOW_ERROR;
      goto error;
    }
  }

  for (;;) {
    task = &g_array_index (thiz->tasks, MsdkDecTask, thiz->next_task);
    flow = gst_msdkdec_finish_task (thiz, task);
    if (flow != GST_FLOW_OK)
      goto error;
    if (!surface) {
      flow = allocate_output_buffer (thiz, &buffer);
      if (flow != GST_FLOW_OK)
        goto error;
      surface = get_surface (thiz, buffer);
      if (!surface) {
        /* Can't get a surface for some reason, finish tasks to see if
           a surface becomes available. */
        for (i = 0; i < thiz->tasks->len - 1; i++) {
          thiz->next_task = (thiz->next_task + 1) % thiz->tasks->len;
          task = &g_array_index (thiz->tasks, MsdkDecTask, thiz->next_task);
          flow = gst_msdkdec_finish_task (thiz, task);
          if (flow != GST_FLOW_OK)
            goto error;
          surface = get_surface (thiz, buffer);
          if (surface)
            break;
        }
        if (!surface) {
          GST_ERROR_OBJECT (thiz, "Couldn't get a surface");
          flow = GST_FLOW_ERROR;
          goto error;
        }
      }
    }

    status =
        MFXVideoDECODE_DecodeFrameAsync (session, &bitstream, surface->surface,
        &task->surface, &task->sync_point);

    /* media-sdk requires complete reset since the surface is inadaquate to
     * do further decoding */
    if (status == MFX_ERR_INCOMPATIBLE_VIDEO_PARAM) {
      /* Requires memory re-allocation, do a hard reset */
      if (!gst_msdkdec_negotiate (thiz, TRUE))
        goto error;
      status =
          MFXVideoDECODE_DecodeFrameAsync (session, &bitstream,
          surface->surface, &task->surface, &task->sync_point);
    }

    if (G_LIKELY (status == MFX_ERR_NONE)
        || (status == MFX_WRN_VIDEO_PARAM_CHANGED)) {
      thiz->next_task = (thiz->next_task + 1) % thiz->tasks->len;

      if (surface->surface->Data.Locked > 0 || !thiz->use_video_memory)
        surface = NULL;

      if (bitstream.DataLength == 0) {
        flow = GST_FLOW_OK;
        break;
      }
    } else if (status == MFX_ERR_MORE_DATA) {
      if (task->surface) {
        task->decode_only = TRUE;
        thiz->next_task = (thiz->next_task + 1) % thiz->tasks->len;
      }

      if (surface->surface->Data.Locked > 0)
        surface = NULL;
      flow = GST_VIDEO_DECODER_FLOW_NEED_DATA;
      break;
    } else if (status == MFX_ERR_MORE_SURFACE) {
      surface = NULL;
      continue;
    } else if (status == MFX_WRN_DEVICE_BUSY) {
      /* If device is busy, wait 1ms and retry, as per MSDK's recomendation */
      g_usleep (1000);

      /* If the current surface is still busy, we should do sync oepration
       * then tries to decode again
       */
      thiz->next_task = (thiz->next_task + 1) % thiz->tasks->len;
    } else if (status < MFX_ERR_NONE) {
      GST_ERROR_OBJECT (thiz, "DecodeFrameAsync failed (%s)",
          msdk_status_to_string (status));
      flow = GST_FLOW_ERROR;
      break;
    }
  }

  if (!thiz->is_packetized) {
    /* flush out the data which is already consumed by msdk */
    gst_adapter_flush (thiz->adapter, bitstream.DataOffset);
    flow = GST_FLOW_OK;
  }

done:
  if (surface)
    free_surface (thiz, surface);

  gst_buffer_unmap (input_buffer, &map_info);
  gst_buffer_unref (input_buffer);
  return flow;

error:
  if (input_buffer) {
    gst_buffer_unmap (input_buffer, &map_info);
    gst_buffer_unref (input_buffer);
  }
  gst_video_decoder_drop_frame (decoder, frame);

  return flow;
}
Example #22
0
static GstFlowReturn
gst_flac_tag_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
{
    GstFlacTag *tag;
    GstFlowReturn ret;
    GstMapInfo map;
    gsize size;

    ret = GST_FLOW_OK;
    tag = GST_FLAC_TAG (parent);

    gst_adapter_push (tag->adapter, buffer);

    /* Initial state, we don't even know if we are dealing with a flac file */
    if (tag->state == GST_FLAC_TAG_STATE_INIT) {
        GstBuffer *id_buffer;

        if (gst_adapter_available (tag->adapter) < sizeof (FLAC_MAGIC))
            goto cleanup;

        id_buffer = gst_adapter_take_buffer (tag->adapter, FLAC_MAGIC_SIZE);
        GST_DEBUG_OBJECT (tag, "looking for " FLAC_MAGIC " identifier");
        if (gst_buffer_memcmp (id_buffer, 0, FLAC_MAGIC, FLAC_MAGIC_SIZE) == 0) {

            GST_DEBUG_OBJECT (tag, "pushing " FLAC_MAGIC " identifier buffer");
            ret = gst_pad_push (tag->srcpad, id_buffer);
            if (ret != GST_FLOW_OK)
                goto cleanup;

            tag->state = GST_FLAC_TAG_STATE_METADATA_BLOCKS;
        } else {
            /* FIXME: does that work well with FLAC files containing ID3v2 tags ? */
            gst_buffer_unref (id_buffer);
            GST_ELEMENT_ERROR (tag, STREAM, WRONG_TYPE, (NULL), (NULL));
            ret = GST_FLOW_ERROR;
        }
    }


    /* The fLaC magic string has been skipped, try to detect the beginning
     * of a metadata block
     */
    if (tag->state == GST_FLAC_TAG_STATE_METADATA_BLOCKS) {
        guint type;
        gboolean is_last;
        const guint8 *block_header;

        g_assert (tag->metadata_block_size == 0);
        g_assert (tag->metadata_last_block == FALSE);

        /* The header of a flac metadata block is 4 bytes long:
         * 1st bit: indicates whether this is the last metadata info block
         * 7 next bits: 4 if vorbis comment block
         * 24 next bits: size of the metadata to follow (big endian)
         */
        if (gst_adapter_available (tag->adapter) < 4)
            goto cleanup;

        block_header = gst_adapter_map (tag->adapter, 4);

        is_last = ((block_header[0] & 0x80) == 0x80);
        type = block_header[0] & 0x7F;
        size = (block_header[1] << 16)
               | (block_header[2] << 8)
               | block_header[3];
        gst_adapter_unmap (tag->adapter);

        /* The 4 bytes long header isn't included in the metadata size */
        tag->metadata_block_size = size + 4;
        tag->metadata_last_block = is_last;

        GST_DEBUG_OBJECT (tag,
                          "got metadata block: %" G_GSIZE_FORMAT " bytes, type %d, "
                          "is vorbiscomment: %d, is last: %d",
                          size, type, (type == 0x04), is_last);

        /* Metadata blocks of type 4 are vorbis comment blocks */
        if (type == 0x04) {
            tag->state = GST_FLAC_TAG_STATE_VC_METADATA_BLOCK;
        } else {
            tag->state = GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK;
        }
    }


    /* Reads a metadata block */
    if ((tag->state == GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK) ||
            (tag->state == GST_FLAC_TAG_STATE_VC_METADATA_BLOCK)) {
        GstBuffer *metadata_buffer;

        if (gst_adapter_available (tag->adapter) < tag->metadata_block_size)
            goto cleanup;

        metadata_buffer = gst_adapter_take_buffer (tag->adapter,
                          tag->metadata_block_size);
        /* clear the is-last flag, as the last metadata block will
         * be the vorbis comment block which we will build ourselves.
         */
        gst_buffer_map (metadata_buffer, &map, GST_MAP_READWRITE);
        map.data[0] &= (~0x80);
        gst_buffer_unmap (metadata_buffer, &map);

        if (tag->state == GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK) {
            GST_DEBUG_OBJECT (tag, "pushing metadata block buffer");
            ret = gst_pad_push (tag->srcpad, metadata_buffer);
            if (ret != GST_FLOW_OK)
                goto cleanup;
        } else {
            tag->vorbiscomment = metadata_buffer;
        }
        tag->metadata_block_size = 0;
        tag->state = GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK;
    }

    /* This state is mainly used to be able to stop as soon as we read
     * a vorbiscomment block from the flac file if we are in an only output
     * tags mode
     */
    if (tag->state == GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK) {
        /* Check if in the previous iteration we read a vorbis comment metadata
         * block, and stop now if the user only wants to read tags
         */
        if (tag->vorbiscomment != NULL) {
            guint8 id_data[4];
            /* We found some tags, try to parse them and notify the other elements
             * that we encountered some tags
             */
            GST_DEBUG_OBJECT (tag, "emitting vorbiscomment tags");
            gst_buffer_extract (tag->vorbiscomment, 0, id_data, 4);
            tag->tags = gst_tag_list_from_vorbiscomment_buffer (tag->vorbiscomment,
                        id_data, 4, NULL);
            if (tag->tags != NULL) {
                gst_pad_push_event (tag->srcpad,
                                    gst_event_new_tag (gst_tag_list_copy (tag->tags)));
            }

            gst_buffer_unref (tag->vorbiscomment);
            tag->vorbiscomment = NULL;
        }

        /* Skip to next state */
        if (tag->metadata_last_block == FALSE) {
            tag->state = GST_FLAC_TAG_STATE_METADATA_BLOCKS;
        } else {
            tag->state = GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT;
        }
    }


    /* Creates a vorbis comment block from the metadata which was set
     * on the gstreamer element, and add it to the flac stream
     */
    if (tag->state == GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT) {
        GstBuffer *buffer;
        const GstTagList *user_tags;
        GstTagList *merged_tags;

        /* merge the tag lists */
        user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (tag));
        if (user_tags != NULL) {
            merged_tags = gst_tag_list_merge (user_tags, tag->tags,
                                              gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (tag)));
        } else {
            merged_tags = gst_tag_list_copy (tag->tags);
        }

        if (merged_tags == NULL) {
            /* If we get a NULL list of tags, we must generate a padding block
             * which is marked as the last metadata block, otherwise we'll
             * end up with a corrupted flac file.
             */
            GST_WARNING_OBJECT (tag, "No tags found");
            buffer = gst_buffer_new_and_alloc (12);
            if (buffer == NULL)
                goto no_buffer;

            gst_buffer_map (buffer, &map, GST_MAP_WRITE);
            memset (map.data, 0, map.size);
            map.data[0] = 0x81;       /* 0x80 = Last metadata block,
                                 * 0x01 = padding block */
            gst_buffer_unmap (buffer, &map);
        } else {
            guchar header[4];
            guint8 fbit[1];

            memset (header, 0, sizeof (header));
            header[0] = 0x84;         /* 0x80 = Last metadata block,
                                 * 0x04 = vorbiscomment block */
            buffer = gst_tag_list_to_vorbiscomment_buffer (merged_tags, header,
                     sizeof (header), NULL);
            GST_DEBUG_OBJECT (tag, "Writing tags %" GST_PTR_FORMAT, merged_tags);
            gst_tag_list_free (merged_tags);
            if (buffer == NULL)
                goto no_comment;

            size = gst_buffer_get_size (buffer);
            if ((size < 4) || ((size - 4) > 0xFFFFFF))
                goto comment_too_long;

            fbit[0] = 1;
            /* Get rid of the framing bit at the end of the vorbiscomment buffer
             * if it exists since libFLAC seems to lose sync because of this
             * bit in gstflacdec
             */
            if (gst_buffer_memcmp (buffer, size - 1, fbit, 1) == 0) {
                buffer = gst_buffer_make_writable (buffer);
                gst_buffer_resize (buffer, 0, size - 1);
            }
        }

        /* The 4 byte metadata block header isn't accounted for in the total
         * size of the metadata block
         */
        gst_buffer_map (buffer, &map, GST_MAP_WRITE);
        map.data[1] = (((map.size - 4) & 0xFF0000) >> 16);
        map.data[2] = (((map.size - 4) & 0x00FF00) >> 8);
        map.data[3] = ((map.size - 4) & 0x0000FF);
        gst_buffer_unmap (buffer, &map);

        GST_DEBUG_OBJECT (tag, "pushing %" G_GSIZE_FORMAT " byte vorbiscomment "
                          "buffer", map.size);

        ret = gst_pad_push (tag->srcpad, buffer);
        if (ret != GST_FLOW_OK) {
            goto cleanup;
        }
        tag->state = GST_FLAC_TAG_STATE_AUDIO_DATA;
    }