Beispiel #1
0
static gboolean
gst_raw_parse_sink_activatepull (GstPad * sinkpad, gboolean active)
{
  GstRawParse *rp = GST_RAW_PARSE (gst_pad_get_parent (sinkpad));
  gboolean result;

  if (active) {
    GstFormat format;
    gint64 duration;

    /* get the duration in bytes */
    format = GST_FORMAT_BYTES;
    result = gst_pad_query_peer_duration (sinkpad, &format, &duration);
    if (result) {
      GST_DEBUG_OBJECT (rp, "got duration %" GST_TIME_FORMAT,
          GST_TIME_ARGS (duration));
      rp->upstream_length = duration;
      /* convert to time */
      gst_raw_parse_convert (rp, format, duration, GST_FORMAT_TIME, &duration);
    } else {
      rp->upstream_length = -1;
      duration = -1;
    }
    gst_segment_set_duration (&rp->segment, GST_FORMAT_TIME, duration);

    result = gst_raw_parse_handle_seek_pull (rp, NULL);
  } else {
    result = gst_pad_stop_task (sinkpad);
  }

  gst_object_unref (rp);

  return result;
}
Beispiel #2
0
static GstFlowReturn
gst_aiff_parse_stream_headers (GstAiffParse * aiff)
{
  GstFlowReturn res;
  GstBuffer *buf;
  guint32 tag, size;
  gboolean gotdata = FALSE;
  gboolean done = FALSE;
  GstEvent **event_p;
  GstFormat bformat;
  gint64 upstream_size = 0;

  bformat = GST_FORMAT_BYTES;
  gst_pad_query_peer_duration (aiff->sinkpad, &bformat, &upstream_size);
  GST_DEBUG_OBJECT (aiff, "upstream size %" G_GUINT64_FORMAT, upstream_size);

  /* loop headers until we get data */
  while (!done) {
    if (aiff->streaming) {
      if (!gst_aiff_parse_peek_chunk_info (aiff, &tag, &size))
        return GST_FLOW_OK;
    } else {
      if ((res =
              gst_pad_pull_range (aiff->sinkpad, aiff->offset, 8,
                  &buf)) != GST_FLOW_OK)
        goto header_read_error;
      tag = GST_READ_UINT32_LE (GST_BUFFER_DATA (buf));
      size = GST_READ_UINT32_BE (GST_BUFFER_DATA (buf) + 4);
    }

    GST_INFO_OBJECT (aiff,
        "Got TAG: %" GST_FOURCC_FORMAT ", offset %" G_GUINT64_FORMAT,
        GST_FOURCC_ARGS (tag), aiff->offset);

    /* We just keep reading chunks until we find the one we're interested in.
     */
    switch (tag) {
      case GST_MAKE_FOURCC ('C', 'O', 'M', 'M'):{
        if (aiff->streaming) {
          if (!gst_aiff_parse_peek_chunk (aiff, &tag, &size))
            return GST_FLOW_OK;

          gst_adapter_flush (aiff->adapter, 8);
          aiff->offset += 8;

          buf = gst_adapter_take_buffer (aiff->adapter, size);
        } else {
          if ((res = gst_aiff_parse_read_chunk (aiff,
                      &aiff->offset, &tag, &buf)) != GST_FLOW_OK)
            return res;
        }

        if (!gst_aiff_parse_parse_comm (aiff, buf)) {
          gst_buffer_unref (buf);
          goto parse_header_error;
        }

        gst_buffer_unref (buf);

        /* do sanity checks of header fields */
        if (aiff->channels == 0)
          goto no_channels;
        if (aiff->rate == 0)
          goto no_rate;

        GST_DEBUG_OBJECT (aiff, "creating the caps");

        aiff->caps = gst_aiff_parse_create_caps (aiff);
        if (!aiff->caps)
          goto unknown_format;

        gst_pad_set_caps (aiff->srcpad, aiff->caps);

        aiff->bytes_per_sample = aiff->channels * aiff->width / 8;
        aiff->bps = aiff->bytes_per_sample * aiff->rate;

        if (aiff->bytes_per_sample <= 0)
          goto no_bytes_per_sample;

        aiff->got_comm = TRUE;
        break;
      }
      case GST_MAKE_FOURCC ('S', 'S', 'N', 'D'):{
        GstFormat fmt;
        GstBuffer *ssndbuf = NULL;
        const guint8 *ssnddata = NULL;
        guint32 datasize;

        GST_DEBUG_OBJECT (aiff, "Got 'SSND' TAG, size : %d", size);

        /* Now, read the 8-byte header in the SSND chunk */
        if (aiff->streaming) {
          if (!gst_aiff_parse_peek_data (aiff, 16, &ssnddata))
            return GST_FLOW_OK;
        } else {
          gst_buffer_unref (buf);
          if ((res =
                  gst_pad_pull_range (aiff->sinkpad, aiff->offset, 16,
                      &ssndbuf)) != GST_FLOW_OK)
            goto header_read_error;
          ssnddata = GST_BUFFER_DATA (ssndbuf);
        }

        aiff->ssnd_offset = GST_READ_UINT32_BE (ssnddata + 8);
        aiff->ssnd_blocksize = GST_READ_UINT32_BE (ssnddata + 12);

        gotdata = TRUE;
        if (aiff->streaming) {
          gst_adapter_flush (aiff->adapter, 16);
        } else {
          gst_buffer_unref (ssndbuf);
        }
        /* 8 byte chunk header, 16 byte SSND header */
        aiff->offset += 24;

        datasize = size - 16;

        aiff->datastart = aiff->offset + aiff->ssnd_offset;
        /* file might be truncated */
        fmt = GST_FORMAT_BYTES;
        if (upstream_size) {
          size = MIN (datasize, (upstream_size - aiff->datastart));
        }
        aiff->datasize = (guint64) datasize;
        aiff->dataleft = (guint64) datasize;
        aiff->end_offset = datasize + aiff->datastart;
        if (!aiff->streaming) {
          /* We will continue looking at chunks until the end - to read tags,
           * etc. */
          aiff->offset += datasize;
        }
        GST_DEBUG_OBJECT (aiff, "datasize = %d", datasize);
        if (aiff->streaming) {
          done = TRUE;
        }
        break;
      }
      default:
        gst_aiff_parse_ignore_chunk (aiff, buf, tag, size);
    }

    if (upstream_size && (aiff->offset >= upstream_size)) {
      /* Now we have gone through the whole file */
      done = TRUE;
    }
  }

  /* We read all the chunks (in pull mode) or reached the SSND chunk
   * (in push mode). We must have both COMM and SSND now; error out 
   * otherwise.
   */
  if (!aiff->got_comm) {
    GST_WARNING_OBJECT (aiff, "Failed to find COMM chunk");
    goto no_header;
  }
  if (!gotdata) {
    GST_WARNING_OBJECT (aiff, "Failed to find SSND chunk");
    goto no_data;
  }

  GST_DEBUG_OBJECT (aiff, "Finished parsing headers");

  if (gst_aiff_parse_calculate_duration (aiff)) {
    gst_segment_init (&aiff->segment, GST_FORMAT_TIME);
    gst_segment_set_duration (&aiff->segment, GST_FORMAT_TIME, aiff->duration);
  } else {
    /* no bitrate, let downstream peer do the math, we'll feed it bytes. */
    gst_segment_init (&aiff->segment, GST_FORMAT_BYTES);
    gst_segment_set_duration (&aiff->segment, GST_FORMAT_BYTES, aiff->datasize);
  }

  /* now we have all the info to perform a pending seek if any, if no
   * event, this will still do the right thing and it will also send
   * the right newsegment event downstream. */
  gst_aiff_parse_perform_seek (aiff, aiff->seek_event);
  /* remove pending event */
  event_p = &aiff->seek_event;
  gst_event_replace (event_p, NULL);

  /* we just started, we are discont */
  aiff->discont = TRUE;

  aiff->state = AIFF_PARSE_DATA;

  return GST_FLOW_OK;

  /* ERROR */
no_header:
  {
    GST_ELEMENT_ERROR (aiff, STREAM, TYPE_NOT_FOUND, (NULL),
        ("Invalid AIFF header (no COMM found)"));
    return GST_FLOW_ERROR;
  }
no_data:
  {
    GST_ELEMENT_ERROR (aiff, STREAM, TYPE_NOT_FOUND, (NULL),
        ("Invalid AIFF: no SSND found"));
    return GST_FLOW_ERROR;
  }
parse_header_error:
  {
    GST_ELEMENT_ERROR (aiff, STREAM, DEMUX, (NULL),
        ("Couldn't parse audio header"));
    return GST_FLOW_ERROR;
  }
no_channels:
  {
    GST_ELEMENT_ERROR (aiff, STREAM, FAILED, (NULL),
        ("Stream claims to contain no channels - invalid data"));
    return GST_FLOW_ERROR;
  }
no_rate:
  {
    GST_ELEMENT_ERROR (aiff, STREAM, FAILED, (NULL),
        ("Stream with sample_rate == 0 - invalid data"));
    return GST_FLOW_ERROR;
  }
no_bytes_per_sample:
  {
    GST_ELEMENT_ERROR (aiff, STREAM, FAILED, (NULL),
        ("Could not caluclate bytes per sample - invalid data"));
    return GST_FLOW_ERROR;
  }
unknown_format:
  {
    GST_ELEMENT_ERROR (aiff, STREAM, TYPE_NOT_FOUND, (NULL),
        ("No caps found for format 0x%x, %d channels, %d Hz",
            aiff->format, aiff->channels, aiff->rate));
    return GST_FLOW_ERROR;
  }
header_read_error:
  {
    GST_ELEMENT_ERROR (aiff, STREAM, DEMUX, (NULL),
        ("Couldn't read in header"));
    return GST_FLOW_ERROR;
  }
}
Beispiel #3
0
static void
gst_soup_http_src_got_headers_cb (SoupMessage * msg, GstSoupHTTPSrc * src)
{
  const char *value;
  GstTagList *tag_list;
  GstBaseSrc *basesrc;
  guint64 newsize;
  GHashTable *params = NULL;

  GST_DEBUG_OBJECT (src, "got headers:");
  soup_message_headers_foreach (msg->response_headers,
      gst_soup_http_src_headers_foreach, src);

  if (msg->status_code == 407 && src->proxy_id && src->proxy_pw)
    return;

  if (src->automatic_redirect && SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
    GST_DEBUG_OBJECT (src, "%u redirect to \"%s\"", msg->status_code,
        soup_message_headers_get_one (msg->response_headers, "Location"));
    return;
  }

  if (msg->status_code == SOUP_STATUS_UNAUTHORIZED)
    return;

  src->session_io_status = GST_SOUP_HTTP_SRC_SESSION_IO_STATUS_RUNNING;

  /* Parse Content-Length. */
  if (soup_message_headers_get_encoding (msg->response_headers) ==
      SOUP_ENCODING_CONTENT_LENGTH) {
    newsize = src->request_position +
        soup_message_headers_get_content_length (msg->response_headers);
    if (!src->have_size || (src->content_size != newsize)) {
      src->content_size = newsize;
      src->have_size = TRUE;
      src->seekable = TRUE;
      GST_DEBUG_OBJECT (src, "size = %" G_GUINT64_FORMAT, src->content_size);

      basesrc = GST_BASE_SRC_CAST (src);
      gst_segment_set_duration (&basesrc->segment, GST_FORMAT_BYTES,
          src->content_size);
      gst_element_post_message (GST_ELEMENT (src),
          gst_message_new_duration (GST_OBJECT (src), GST_FORMAT_BYTES,
              src->content_size));
    }
  }

  /* Icecast stuff */
  tag_list = gst_tag_list_new ();

  if ((value =
          soup_message_headers_get_one (msg->response_headers,
              "icy-metaint")) != NULL) {
    gint icy_metaint = atoi (value);

    GST_DEBUG_OBJECT (src, "icy-metaint: %s (parsed: %d)", value, icy_metaint);
    if (icy_metaint > 0) {
      if (src->src_caps)
        gst_caps_unref (src->src_caps);

      src->src_caps = gst_caps_new_simple ("application/x-icy",
          "metadata-interval", G_TYPE_INT, icy_metaint, NULL);
    }
  }
  if ((value =
          soup_message_headers_get_content_type (msg->response_headers,
              &params)) != NULL) {
    GST_DEBUG_OBJECT (src, "Content-Type: %s", value);
    if (g_ascii_strcasecmp (value, "audio/L16") == 0) {
      gint channels = 2;
      gint rate = 44100;
      char *param;

      if (src->src_caps)
        gst_caps_unref (src->src_caps);

      param = g_hash_table_lookup (params, "channels");
      if (param != NULL)
        channels = atol (param);

      param = g_hash_table_lookup (params, "rate");
      if (param != NULL)
        rate = atol (param);

      src->src_caps = gst_caps_new_simple ("audio/x-raw-int",
          "channels", G_TYPE_INT, channels,
          "rate", G_TYPE_INT, rate,
          "width", G_TYPE_INT, 16,
          "depth", G_TYPE_INT, 16,
          "signed", G_TYPE_BOOLEAN, TRUE,
          "endianness", G_TYPE_INT, G_BIG_ENDIAN, NULL);
    } else {
      /* Set the Content-Type field on the caps */
      if (src->src_caps)
        gst_caps_set_simple (src->src_caps, "content-type", G_TYPE_STRING,
            value, NULL);
    }
  }

  if (params != NULL)
    g_hash_table_destroy (params);

  if ((value =
          soup_message_headers_get_one (msg->response_headers,
              "icy-name")) != NULL) {
    g_free (src->iradio_name);
    src->iradio_name = gst_soup_http_src_unicodify (value);
    if (src->iradio_name) {
      g_object_notify (G_OBJECT (src), "iradio-name");
      gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_ORGANIZATION,
          src->iradio_name, NULL);
    }
  }
  if ((value =
          soup_message_headers_get_one (msg->response_headers,
              "icy-genre")) != NULL) {
    g_free (src->iradio_genre);
    src->iradio_genre = gst_soup_http_src_unicodify (value);
    if (src->iradio_genre) {
      g_object_notify (G_OBJECT (src), "iradio-genre");
      gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_GENRE,
          src->iradio_genre, NULL);
    }
  }
  if ((value = soup_message_headers_get_one (msg->response_headers, "icy-url"))
      != NULL) {
    g_free (src->iradio_url);
    src->iradio_url = gst_soup_http_src_unicodify (value);
    if (src->iradio_url) {
      g_object_notify (G_OBJECT (src), "iradio-url");
      gst_tag_list_add (tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_LOCATION,
          src->iradio_url, NULL);
    }
  }
  if (!gst_tag_list_is_empty (tag_list)) {
    GST_DEBUG_OBJECT (src,
        "calling gst_element_found_tags with %" GST_PTR_FORMAT, tag_list);
    gst_element_found_tags (GST_ELEMENT_CAST (src), tag_list);
  } else {
    gst_tag_list_free (tag_list);
  }

  /* Handle HTTP errors. */
  gst_soup_http_src_parse_status (msg, src);

  /* Check if Range header was respected. */
  if (src->ret == GST_FLOW_CUSTOM_ERROR &&
      src->read_position && msg->status_code != SOUP_STATUS_PARTIAL_CONTENT) {
    src->seekable = FALSE;
    GST_ELEMENT_ERROR (src, RESOURCE, SEEK,
        (_("Server does not support seeking.")),
        ("Server does not accept Range HTTP header, URL: %s", src->location));
    src->ret = GST_FLOW_ERROR;
  }
}
Beispiel #4
0
static GstFlowReturn
gst_app_src_create (GstBaseSrc * bsrc, guint64 offset, guint size,
    GstBuffer ** buf)
{
  GstAppSrc *appsrc = GST_APP_SRC_CAST (bsrc);
  GstAppSrcPrivate *priv = appsrc->priv;
  GstFlowReturn ret;
  GstCaps *caps;

  GST_OBJECT_LOCK (appsrc);
  caps = priv->caps ? gst_caps_ref (priv->caps) : NULL;
  if (G_UNLIKELY (priv->size != bsrc->segment.duration &&
          bsrc->segment.format == GST_FORMAT_BYTES)) {
    GST_DEBUG_OBJECT (appsrc,
        "Size changed from %" G_GINT64_FORMAT " to %" G_GINT64_FORMAT,
        bsrc->segment.duration, priv->size);
    gst_segment_set_duration (&bsrc->segment, GST_FORMAT_BYTES, priv->size);
    GST_OBJECT_UNLOCK (appsrc);

    gst_element_post_message (GST_ELEMENT (appsrc),
        gst_message_new_duration (GST_OBJECT (appsrc), GST_FORMAT_BYTES,
            priv->size));
  } else {
    GST_OBJECT_UNLOCK (appsrc);
  }

  g_mutex_lock (priv->mutex);
  /* check flushing first */
  if (G_UNLIKELY (priv->flushing))
    goto flushing;

  if (priv->stream_type == GST_APP_STREAM_TYPE_RANDOM_ACCESS) {
    /* if we are dealing with a random-access stream, issue a seek if the offset
     * changed. */
    if (G_UNLIKELY (priv->offset != offset)) {
      gboolean res;

      /* do the seek */
      res = gst_app_src_emit_seek (appsrc, offset);

      if (G_UNLIKELY (!res))
        /* failing to seek is fatal */
        goto seek_error;

      priv->offset = offset;
    }
  }

  while (TRUE) {
    /* return data as long as we have some */
    if (!g_queue_is_empty (priv->queue)) {
      guint buf_size;

      *buf = g_queue_pop_head (priv->queue);
      buf_size = GST_BUFFER_SIZE (*buf);

      GST_DEBUG_OBJECT (appsrc, "we have buffer %p of size %u", *buf, buf_size);

      priv->queued_bytes -= buf_size;

      /* only update the offset when in random_access mode */
      if (priv->stream_type == GST_APP_STREAM_TYPE_RANDOM_ACCESS)
        priv->offset += buf_size;
      if (caps) {
        *buf = gst_buffer_make_metadata_writable (*buf);
        gst_buffer_set_caps (*buf, caps);
      }

      /* signal that we removed an item */
      g_cond_broadcast (priv->cond);

      /* see if we go lower than the empty-percent */
      if (priv->min_percent && priv->max_bytes) {
        if (priv->queued_bytes * 100 / priv->max_bytes <= priv->min_percent)
          /* ignore flushing state, we got a buffer and we will return it now.
           * Errors will be handled in the next round */
          gst_app_src_emit_need_data (appsrc, size);
      }
      ret = GST_FLOW_OK;
      break;
    } else {
      gst_app_src_emit_need_data (appsrc, size);

      /* we can be flushing now because we released the lock above */
      if (G_UNLIKELY (priv->flushing))
        goto flushing;

      /* if we have a buffer now, continue the loop and try to return it. In
       * random-access mode (where a buffer is normally pushed in the above
       * signal) we can still be empty because the pushed buffer got flushed or
       * when the application pushes the requested buffer later, we support both
       * possiblities. */
      if (!g_queue_is_empty (priv->queue))
        continue;

      /* no buffer yet, maybe we are EOS, if not, block for more data. */
    }

    /* check EOS */
    if (G_UNLIKELY (priv->is_eos))
      goto eos;

    /* nothing to return, wait a while for new data or flushing. */
    g_cond_wait (priv->cond, priv->mutex);
  }
  g_mutex_unlock (priv->mutex);
  if (caps)
    gst_caps_unref (caps);
  return ret;

  /* ERRORS */
flushing:
  {
    GST_DEBUG_OBJECT (appsrc, "we are flushing");
    g_mutex_unlock (priv->mutex);
    if (caps)
      gst_caps_unref (caps);
    return GST_FLOW_WRONG_STATE;
  }
eos:
  {
    GST_DEBUG_OBJECT (appsrc, "we are EOS");
    g_mutex_unlock (priv->mutex);
    if (caps)
      gst_caps_unref (caps);
    return GST_FLOW_UNEXPECTED;
  }
seek_error:
  {
    g_mutex_unlock (priv->mutex);
    if (caps)
      gst_caps_unref (caps);
    GST_ELEMENT_ERROR (appsrc, RESOURCE, READ, ("failed to seek"),
        GST_ERROR_SYSTEM);
    return GST_FLOW_ERROR;
  }
}
static gboolean
gst_musepack_stream_init (GstMusepackDec * musepackdec)
{
    mpc_streaminfo i;
    GstTagList *tags;
    GstCaps *caps;

    /* set up reading */
    gst_musepack_init_reader (musepackdec->r, musepackdec);

#ifdef MPC_IS_OLD_API
    /* streaminfo */
    mpc_streaminfo_init (&i);
    if (mpc_streaminfo_read (&i, musepackdec->r) < 0) {
        GST_ELEMENT_ERROR (musepackdec, STREAM, WRONG_TYPE, (NULL), (NULL));
        return FALSE;
    }

    /* decoding */
    mpc_decoder_setup (musepackdec->d, musepackdec->r);
    mpc_decoder_scale_output (musepackdec->d, 1.0);
    if (!mpc_decoder_initialize (musepackdec->d, &i)) {
        GST_ELEMENT_ERROR (musepackdec, STREAM, WRONG_TYPE, (NULL), (NULL));
        return FALSE;
    }
#else
    musepackdec->d = mpc_demux_init (musepackdec->r);
    if (!musepackdec->d) {
        GST_ELEMENT_ERROR (musepackdec, STREAM, WRONG_TYPE, (NULL), (NULL));
        return FALSE;
    }

    mpc_demux_get_info (musepackdec->d, &i);
#endif

    /* capsnego */
    caps = gst_caps_from_string (BASE_CAPS);
    gst_caps_set_simple (caps,
                         "endianness", G_TYPE_INT, G_BYTE_ORDER,
                         "channels", G_TYPE_INT, i.channels,
                         "rate", G_TYPE_INT, i.sample_freq, NULL);
    gst_pad_use_fixed_caps (musepackdec->srcpad);
    if (!gst_pad_set_caps (musepackdec->srcpad, caps)) {
        GST_ELEMENT_ERROR (musepackdec, CORE, NEGOTIATION, (NULL), (NULL));
        return FALSE;
    }

    g_atomic_int_set (&musepackdec->bps, 4 * i.channels);
    g_atomic_int_set (&musepackdec->rate, i.sample_freq);

    gst_segment_set_last_stop (&musepackdec->segment, GST_FORMAT_DEFAULT, 0);
    gst_segment_set_duration (&musepackdec->segment, GST_FORMAT_DEFAULT,
                              mpc_streaminfo_get_length_samples (&i));

    /* send basic tags */
    tags = gst_tag_list_new ();
    gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
                      GST_TAG_AUDIO_CODEC, "Musepack", NULL);

    if (i.encoder[0] != '\0' && i.encoder_version > 0) {
        gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
                          GST_TAG_ENCODER, i.encoder,
                          GST_TAG_ENCODER_VERSION, i.encoder_version, NULL);
    }

    if (i.bitrate > 0) {
        gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
                          GST_TAG_BITRATE, i.bitrate, NULL);
    } else if (i.average_bitrate > 0.0) {
        gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
                          GST_TAG_BITRATE, (guint) i.average_bitrate, NULL);
    }

    if (i.gain_title != 0 || i.gain_album != 0) {
        gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
                          GST_TAG_TRACK_GAIN, (gdouble) i.gain_title / 100.0,
                          GST_TAG_ALBUM_GAIN, (gdouble) i.gain_album / 100.0, NULL);
    }

    if (i.peak_title != 0 && i.peak_title != 32767 &&
            i.peak_album != 0 && i.peak_album != 32767) {
        gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE,
                          GST_TAG_TRACK_PEAK, (gdouble) i.peak_title / 32767.0,
                          GST_TAG_ALBUM_PEAK, (gdouble) i.peak_album / 32767.0, NULL);
    }

    GST_LOG_OBJECT (musepackdec, "Posting tags: %" GST_PTR_FORMAT, tags);
    gst_element_found_tags (GST_ELEMENT (musepackdec), tags);

    return TRUE;
}