mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-18 14:26:43 +00:00
77db559306
Original commit message from CVS: Riff, EBML, fourcc etc. work. Not fully finished, but better than what we used to have and definately worth a first broad testing. I've revived rifflib. Rifflib used to be a bytestream-for-riff, which just dup'ed bytestream. I've rewritten rifflib to be a modern riff- chunk parser that uses bytestream fully, plus adds some extra functions so that riff file parsing becomes extremely easy. It also contains some small usability functions for strh/strf and metadata parsing. Note that it doesn't use the new tagging yet, that's a TODO. Avidemux has been rewritten to use this. I think we all agreed that avidemux was pretty much a big mess, which is because it used all sort of bytestream magic all around the place. It was just ugly. This is a lot nicer, very complete and safe. I think this is far more robust than what the old avidemux could ever have been. Of course, it might contain bugs, please let me know. EBML writing has also been implemented. This is useful for matroska. I'm intending to modify avidemux (with a riffwriter) similarly. Maybe I'll change wavparse/-enc too to use rifflib. Lastly, several plugins have been modified to use rifflib's fourcc parsing instead of their own. this puts fourcc parsing in one central place, which should make it a lot simpler to add new fourccs. We might want to move this to its own lib instead of rifflib. Enjoy!
1644 lines
51 KiB
C
1644 lines
51 KiB
C
/* GStreamer
|
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public
|
|
* License along with this library; if not, write to the
|
|
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
* Boston, MA 02111-1307, USA.
|
|
*/
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
#include <gst/riff/riff-ids.h>
|
|
#include "gstasfdemux.h"
|
|
#include "asfheaders.h"
|
|
|
|
/* elementfactory information */
|
|
static GstElementDetails gst_asf_demux_details = {
|
|
"ASF Demuxer",
|
|
"Codec/Demuxer",
|
|
"Demultiplexes ASF Streams",
|
|
"Owen Fraser-Green <owen@discobabe.net>",
|
|
};
|
|
|
|
GST_PAD_TEMPLATE_FACTORY (sink_factory,
|
|
"sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_CAPS_NEW ("asf_asf_demux_sink",
|
|
"video/x-ms-asf",
|
|
NULL)
|
|
);
|
|
|
|
static void gst_asf_demux_base_init (gpointer g_class);
|
|
static void gst_asf_demux_class_init (GstASFDemuxClass *klass);
|
|
static void gst_asf_demux_init (GstASFDemux *asf_demux);
|
|
static gboolean gst_asf_demux_send_event (GstElement *element,
|
|
GstEvent *event);
|
|
static void gst_asf_demux_loop (GstElement *element);
|
|
static gboolean gst_asf_demux_process_object (GstASFDemux *asf_demux);
|
|
static void gst_asf_demux_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
static guint32 gst_asf_demux_identify_guid (GstASFDemux *asf_demux,
|
|
ASFGuidHash *guids,
|
|
ASFGuid *guid_raw);
|
|
static gboolean gst_asf_demux_process_chunk (GstASFDemux *asf_demux,
|
|
asf_packet_info *packet_info,
|
|
asf_segment_info *segment_info);
|
|
static const GstEventMask* gst_asf_demux_get_src_event_mask (GstPad *pad);
|
|
static gboolean gst_asf_demux_handle_sink_event (GstASFDemux *asf_demux,
|
|
GstEvent *event,
|
|
guint32 remaining);
|
|
static gboolean gst_asf_demux_handle_src_event (GstPad *pad,
|
|
GstEvent *event);
|
|
static const GstFormat* gst_asf_demux_get_src_formats (GstPad *pad);
|
|
static const GstQueryType* gst_asf_demux_get_src_query_types (GstPad *pad);
|
|
static gboolean gst_asf_demux_handle_src_query (GstPad *pad,
|
|
GstQueryType type,
|
|
GstFormat *format, gint64 *value);
|
|
static gboolean gst_asf_demux_add_video_stream (GstASFDemux *asf_demux,
|
|
asf_stream_video_format *video_format,
|
|
guint16 id);
|
|
static gboolean gst_asf_demux_add_audio_stream (GstASFDemux *asf_demux,
|
|
asf_stream_audio *audio,
|
|
guint16 id);
|
|
static gboolean gst_asf_demux_setup_pad (GstASFDemux *asf_demux,
|
|
GstPad *src_pad,
|
|
GstCaps *caps_list,
|
|
guint16 id);
|
|
|
|
static GstElementStateReturn gst_asf_demux_change_state (GstElement *element);
|
|
static GstCaps * gst_asf_demux_video_caps (guint32 codec_fcc,
|
|
asf_stream_video_format *video);
|
|
static GstCaps * gst_asf_demux_audio_caps (guint16 codec_id,
|
|
asf_stream_audio *audio, guint8 *extradata);
|
|
|
|
static GstPadTemplate *videosrctempl, *audiosrctempl;
|
|
static GstElementClass *parent_class = NULL;
|
|
|
|
GType
|
|
asf_demux_get_type (void)
|
|
{
|
|
static GType asf_demux_type = 0;
|
|
|
|
if (!asf_demux_type) {
|
|
static const GTypeInfo asf_demux_info = {
|
|
sizeof(GstASFDemuxClass),
|
|
gst_asf_demux_base_init,
|
|
NULL,
|
|
(GClassInitFunc)gst_asf_demux_class_init,
|
|
NULL,
|
|
NULL,
|
|
sizeof(GstASFDemux),
|
|
0,
|
|
(GInstanceInitFunc)gst_asf_demux_init,
|
|
};
|
|
asf_demux_type = g_type_register_static(GST_TYPE_ELEMENT, "GstASFDemux", &asf_demux_info, 0);
|
|
}
|
|
return asf_demux_type;
|
|
}
|
|
|
|
static void
|
|
gst_asf_demux_base_init (gpointer g_class)
|
|
{
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
|
int i;
|
|
GstCaps *audcaps = NULL, *vidcaps = NULL, *temp;
|
|
guint32 vid_list[] = {
|
|
GST_MAKE_FOURCC('I','4','2','0'),
|
|
GST_MAKE_FOURCC('Y','U','Y','2'),
|
|
GST_MAKE_FOURCC('M','J','P','G'),
|
|
GST_MAKE_FOURCC('D','V','S','D'),
|
|
GST_MAKE_FOURCC('W','M','V','1'),
|
|
GST_MAKE_FOURCC('W','M','V','2'),
|
|
GST_MAKE_FOURCC('M','P','G','4'),
|
|
GST_MAKE_FOURCC('M','P','4','2'),
|
|
GST_MAKE_FOURCC('M','P','4','3'),
|
|
GST_MAKE_FOURCC('D','I','V','3'),
|
|
GST_MAKE_FOURCC('D','X','5','0'),
|
|
0 /* end */
|
|
};
|
|
gint aud_list[] = {
|
|
GST_RIFF_WAVE_FORMAT_MPEGL3,
|
|
GST_RIFF_WAVE_FORMAT_MPEGL12,
|
|
GST_RIFF_WAVE_FORMAT_PCM,
|
|
GST_RIFF_WAVE_FORMAT_VORBIS1,
|
|
GST_RIFF_WAVE_FORMAT_A52,
|
|
GST_RIFF_WAVE_FORMAT_DIVX_WMAV1,
|
|
GST_RIFF_WAVE_FORMAT_DIVX_WMAV2,
|
|
GST_RIFF_WAVE_FORMAT_WMAV9,
|
|
-1 /* end */
|
|
};
|
|
|
|
for (i = 0; aud_list[i] != -1; i++) {
|
|
temp = gst_asf_demux_audio_caps (aud_list[i], NULL, NULL);
|
|
audcaps = gst_caps_append (audcaps, temp);
|
|
}
|
|
|
|
audiosrctempl = gst_pad_template_new ("audio_%02d",
|
|
GST_PAD_SRC,
|
|
GST_PAD_SOMETIMES,
|
|
audcaps, NULL);
|
|
for (i = 0; vid_list[i] != 0; i++) {
|
|
temp = gst_asf_demux_video_caps (vid_list[i], NULL);
|
|
vidcaps = gst_caps_append (vidcaps, temp);
|
|
}
|
|
|
|
videosrctempl = gst_pad_template_new ("video_%02d",
|
|
GST_PAD_SRC,
|
|
GST_PAD_SOMETIMES,
|
|
vidcaps, NULL);
|
|
gst_element_class_add_pad_template (element_class, audiosrctempl);
|
|
gst_element_class_add_pad_template (element_class, videosrctempl);
|
|
gst_element_class_add_pad_template (element_class,
|
|
GST_PAD_TEMPLATE_GET (sink_factory));
|
|
gst_element_class_set_details (element_class, &gst_asf_demux_details);
|
|
}
|
|
|
|
static void
|
|
gst_asf_demux_class_init (GstASFDemuxClass *klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
GstElementClass *gstelement_class;
|
|
|
|
gobject_class = (GObjectClass*)klass;
|
|
gstelement_class = (GstElementClass*)klass;
|
|
|
|
parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
|
|
|
|
gobject_class->get_property = gst_asf_demux_get_property;
|
|
|
|
gstelement_class->change_state = gst_asf_demux_change_state;
|
|
gstelement_class->send_event = gst_asf_demux_send_event;
|
|
}
|
|
|
|
static void
|
|
gst_asf_demux_init (GstASFDemux *asf_demux)
|
|
{
|
|
guint i;
|
|
|
|
asf_demux->sinkpad = gst_pad_new_from_template(
|
|
GST_PAD_TEMPLATE_GET (sink_factory), "sink");
|
|
gst_element_add_pad (GST_ELEMENT (asf_demux), asf_demux->sinkpad);
|
|
|
|
gst_element_set_loop_function (GST_ELEMENT (asf_demux), gst_asf_demux_loop);
|
|
|
|
/* We should zero everything to be on the safe side */
|
|
for (i = 0; i < GST_ASF_DEMUX_NUM_VIDEO_PADS; i++) {
|
|
asf_demux->video_pad[i] = NULL;
|
|
asf_demux->video_PTS[i] = 0;
|
|
}
|
|
|
|
for (i = 0; i < GST_ASF_DEMUX_NUM_AUDIO_PADS; i++) {
|
|
asf_demux->audio_pad[i] = NULL;
|
|
asf_demux->audio_PTS[i] = 0;
|
|
}
|
|
|
|
asf_demux->num_audio_streams = 0;
|
|
asf_demux->num_video_streams = 0;
|
|
asf_demux->num_streams = 0;
|
|
|
|
GST_FLAG_SET (asf_demux, GST_ELEMENT_EVENT_AWARE);
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_send_event (GstElement *element, GstEvent *event)
|
|
{
|
|
const GList *pads;
|
|
|
|
pads = gst_element_get_pad_list (element);
|
|
|
|
while (pads) {
|
|
GstPad *pad = GST_PAD (pads->data);
|
|
|
|
if (GST_PAD_DIRECTION (pad) == GST_PAD_SRC) {
|
|
/* we ref the event here as we might have to try again if the event
|
|
* failed on this pad */
|
|
gst_event_ref (event);
|
|
if (gst_asf_demux_handle_src_event (pad, event)) {
|
|
gst_event_unref (event);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
pads = g_list_next (pads);
|
|
}
|
|
|
|
gst_event_unref (event);
|
|
return FALSE;
|
|
}
|
|
|
|
static void
|
|
gst_asf_demux_loop (GstElement *element)
|
|
{
|
|
GstASFDemux *asf_demux;
|
|
|
|
g_return_if_fail (element != NULL);
|
|
g_return_if_fail (GST_IS_ASF_DEMUX (element));
|
|
|
|
asf_demux = GST_ASF_DEMUX (element);
|
|
|
|
/* this is basically an infinite loop */
|
|
gst_asf_demux_process_object (asf_demux);
|
|
}
|
|
|
|
static guint32 gst_asf_demux_read_var_length (GstASFDemux *asf_demux, guint8 type, guint32 *rsize)
|
|
{
|
|
guint32 got_bytes;
|
|
guint8 *var;
|
|
guint32 ret = 0;
|
|
GstByteStream *bs = asf_demux->bs;
|
|
|
|
if (type == 0) {
|
|
return 0;
|
|
}
|
|
|
|
got_bytes = gst_bytestream_peek_bytes (bs, &var, 4);
|
|
|
|
while (got_bytes < 4) {
|
|
guint32 remaining;
|
|
GstEvent *event;
|
|
|
|
gst_bytestream_get_status (bs, &remaining, &event);
|
|
gst_event_unref (event);
|
|
|
|
got_bytes = gst_bytestream_peek_bytes (bs, &var, 4);
|
|
}
|
|
|
|
switch (type) {
|
|
case 1:
|
|
ret = GUINT32_FROM_LE(*(guint32 *)var) & 0xff;
|
|
gst_bytestream_flush (bs, 1);
|
|
*rsize += 1;
|
|
break;
|
|
case 2:
|
|
ret = GUINT32_FROM_LE(*(guint32 *)var) & 0xffff;
|
|
gst_bytestream_flush (bs, 2);
|
|
*rsize += 2;
|
|
break;
|
|
case 3:
|
|
ret = GUINT32_FROM_LE(*(guint32 *)var);
|
|
gst_bytestream_flush (bs, 4);
|
|
*rsize += 4;
|
|
break;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void gst_asf_demux_read_object_header_rest (GstASFDemux *asf_demux, guint8 **buf, guint32 size) {
|
|
guint32 got_bytes;
|
|
GstByteStream *bs = asf_demux->bs;
|
|
gboolean ret;
|
|
GstEvent *event;
|
|
guint32 remaining;
|
|
|
|
do {
|
|
got_bytes = gst_bytestream_peek_bytes (bs, buf, size);
|
|
if (got_bytes == size) {
|
|
gst_bytestream_flush (bs, size);
|
|
return;
|
|
}
|
|
gst_bytestream_get_status (bs, &remaining, &event);
|
|
ret = gst_asf_demux_handle_sink_event (asf_demux, event, remaining);
|
|
} while (ret);
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_process_file (GstASFDemux *asf_demux, guint64 *obj_size)
|
|
{
|
|
asf_obj_file *object;
|
|
guint8 *ptr;
|
|
guint64 packets;
|
|
|
|
/* Get the rest of the header's header */
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 80);
|
|
object = (asf_obj_file *)ptr;
|
|
packets = GUINT64_FROM_LE (object->packets_count);
|
|
asf_demux->packet_size = GUINT32_FROM_LE (object->max_pktsize);
|
|
asf_demux->play_time = (guint32) GUINT64_FROM_LE (object->play_time) / 10;
|
|
asf_demux->preroll = GUINT64_FROM_LE (object->preroll);
|
|
|
|
GST_INFO ( "Object is a file with %" G_GUINT64_FORMAT " data packets", packets);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_process_bitrate_props_object (GstASFDemux *asf_demux, guint64 *obj_size)
|
|
{
|
|
guint32 got_bytes;
|
|
GstBuffer *buf;
|
|
guint16 num_streams;
|
|
guint8 stream_id;
|
|
guint16 i;
|
|
guint8 *ptr;
|
|
asf_bitrate_record *bitrate_record;
|
|
|
|
got_bytes = gst_bytestream_read (asf_demux->bs, &buf, 2);
|
|
num_streams = GUINT16_FROM_LE (*GST_BUFFER_DATA (buf));
|
|
|
|
GST_INFO ( "Object is a bitrate properties object with %u streams.", num_streams);
|
|
|
|
for (i = 0; i < num_streams; i++) {
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 6);
|
|
bitrate_record = (asf_bitrate_record *)ptr;
|
|
stream_id = GUINT16_FROM_LE (bitrate_record->stream_id) & 0x7f;
|
|
asf_demux->bitrate[stream_id] = GUINT32_FROM_LE (bitrate_record->bitrate);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_process_comment (GstASFDemux *asf_demux, guint64 *obj_size)
|
|
{
|
|
asf_obj_comment *object;
|
|
guint16 title_length;
|
|
guint16 author_length;
|
|
guint16 copyright_length;
|
|
guint16 description_length;
|
|
guint16 rating_length;
|
|
guint8 *ptr;
|
|
GstByteStream *bs = asf_demux->bs;
|
|
|
|
GST_INFO ( "Object is a comment.");
|
|
|
|
/* Get the rest of the comment's header */
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 10);
|
|
object = (asf_obj_comment *)ptr;
|
|
title_length = GUINT16_FROM_LE (object->title_length);
|
|
author_length = GUINT16_FROM_LE (object->author_length);
|
|
copyright_length = GUINT16_FROM_LE (object->copyright_length);
|
|
description_length = GUINT16_FROM_LE (object->description_length);
|
|
rating_length = GUINT16_FROM_LE (object->rating_length);
|
|
GST_DEBUG ("Comment lengths: title=%d author=%d copyright=%d description=%d rating=%d", title_length, author_length, copyright_length, description_length, rating_length);
|
|
|
|
/* We don't do anything with them at the moment so just skip them */
|
|
gst_bytestream_flush (bs, title_length);
|
|
gst_bytestream_flush (bs, author_length);
|
|
gst_bytestream_flush (bs, copyright_length);
|
|
gst_bytestream_flush (bs, description_length);
|
|
gst_bytestream_flush (bs, rating_length);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static gboolean
|
|
gst_asf_demux_process_header (GstASFDemux *asf_demux, guint64 *obj_size)
|
|
{
|
|
guint32 num_objects;
|
|
asf_obj_header *object;
|
|
guint32 i;
|
|
guint8 *ptr;
|
|
|
|
/* Get the rest of the header's header */
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 6);
|
|
object = (asf_obj_header *)ptr;
|
|
num_objects = GUINT32_FROM_LE (object->num_objects);
|
|
|
|
GST_INFO ( "Object is a header with %u parts", num_objects);
|
|
|
|
/* Loop through the header's objects, processing those */
|
|
for (i = 0; i < num_objects; i++) {
|
|
if (!gst_asf_demux_process_object (asf_demux)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_process_segment (GstASFDemux *asf_demux,
|
|
asf_packet_info *packet_info)
|
|
{
|
|
guint8 *byte;
|
|
gboolean key_frame;
|
|
guint32 replic_size;
|
|
guint8 time_delta;
|
|
guint32 time_start;
|
|
guint32 frag_size;
|
|
guint32 rsize;
|
|
asf_segment_info segment_info;
|
|
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&byte, 1); rsize = 1;
|
|
segment_info.stream_number = *byte & 0x7f;
|
|
key_frame = (*byte & 0x80) >> 7;
|
|
|
|
GST_INFO ( "Processing segment for stream %u", segment_info.stream_number);
|
|
segment_info.sequence = gst_asf_demux_read_var_length (asf_demux, packet_info->seqtype, &rsize);
|
|
segment_info.frag_offset = gst_asf_demux_read_var_length (asf_demux, packet_info->fragoffsettype, &rsize);
|
|
replic_size = gst_asf_demux_read_var_length (asf_demux, packet_info->replicsizetype, &rsize);
|
|
GST_DEBUG ("sequence = %x, frag_offset = %x, replic_size = %x", segment_info.sequence, segment_info.frag_offset, replic_size);
|
|
|
|
if (replic_size > 1) {
|
|
asf_replicated_data *replicated_data_header;
|
|
guint8 *replicated_data = NULL;
|
|
guint8 *ptr;
|
|
|
|
segment_info.compressed = FALSE;
|
|
|
|
/* It's uncompressed with replic data*/
|
|
if (replic_size < 8) {
|
|
gst_element_error (GST_ELEMENT (asf_demux), "The payload has replicated data but the size is less than 8");
|
|
return FALSE;
|
|
}
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 8);
|
|
replicated_data_header = (asf_replicated_data *)ptr;
|
|
segment_info.frag_timestamp = GUINT32_FROM_LE (replicated_data_header->frag_timestamp);
|
|
segment_info.segment_size = GUINT32_FROM_LE (replicated_data_header->object_size);
|
|
|
|
if (replic_size > 8) {
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &replicated_data, replic_size - 8);
|
|
}
|
|
|
|
rsize += replic_size;
|
|
} else {
|
|
if (replic_size == 1) {
|
|
/* It's compressed */
|
|
segment_info.compressed = TRUE;
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&byte, 1); rsize++;
|
|
time_delta = *byte;
|
|
GST_DEBUG ("time_delta %u", time_delta);
|
|
} else {
|
|
segment_info.compressed = FALSE;
|
|
}
|
|
|
|
time_start = segment_info.frag_offset;
|
|
segment_info.frag_offset = 0;
|
|
segment_info.frag_timestamp = asf_demux->timestamp;
|
|
}
|
|
|
|
GST_DEBUG ("multiple = %u compressed = %u", packet_info->multiple, segment_info.compressed);
|
|
|
|
if (packet_info->multiple) {
|
|
frag_size = gst_asf_demux_read_var_length (asf_demux, packet_info->segsizetype, &rsize);
|
|
} else {
|
|
frag_size = packet_info->size_left - rsize;
|
|
}
|
|
|
|
packet_info->size_left -= rsize;
|
|
|
|
GST_DEBUG ("size left = %u frag size = %u rsize = %u", packet_info->size_left, frag_size, rsize);
|
|
|
|
if (segment_info.compressed) {
|
|
while (frag_size > 0) {
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&byte, 1);
|
|
packet_info->size_left--;
|
|
segment_info.chunk_size = *byte;
|
|
segment_info.segment_size = segment_info.chunk_size;
|
|
|
|
if (segment_info.chunk_size > packet_info->size_left) {
|
|
gst_element_error (GST_ELEMENT (asf_demux), "Payload chunk overruns packet size.");
|
|
return FALSE;
|
|
}
|
|
|
|
gst_asf_demux_process_chunk (asf_demux, packet_info, &segment_info);
|
|
|
|
frag_size -= segment_info.chunk_size + 1;
|
|
}
|
|
} else {
|
|
segment_info.chunk_size = frag_size;
|
|
gst_asf_demux_process_chunk (asf_demux, packet_info, &segment_info);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_process_data (GstASFDemux *asf_demux, guint64 *obj_size)
|
|
{
|
|
asf_obj_data *object;
|
|
asf_obj_data_packet *packet_properties_object;
|
|
gboolean correction;
|
|
guint64 packets;
|
|
guint64 packet;
|
|
guint8 *buf;
|
|
guint32 sequence;
|
|
guint32 packet_length;
|
|
guint32 *timestamp;
|
|
guint16 *duration;
|
|
guint8 segment;
|
|
guint8 segments;
|
|
guint8 flags;
|
|
guint8 property;
|
|
asf_packet_info packet_info;
|
|
guint32 rsize;
|
|
guint8 *ptr;
|
|
|
|
/* Get the rest of the header */
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 26);
|
|
object = (asf_obj_data *)ptr;
|
|
packets = GUINT64_FROM_LE (object->packets);
|
|
|
|
GST_INFO ( "Object is data with %" G_GUINT64_FORMAT " packets", packets);
|
|
|
|
for (packet = 0; packet < packets; packet++) {
|
|
GST_INFO ( "\n\nProcess packet %" G_GUINT64_FORMAT, packet);
|
|
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&buf, 1); rsize=1;
|
|
if (*buf & 0x80) {
|
|
asf_obj_data_correction *correction_object;
|
|
guint8 *ptr;
|
|
|
|
/* Uses error correction */
|
|
correction = TRUE;
|
|
GST_DEBUG ("Data has error correction (%x)", *buf);
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 2); rsize += 2;
|
|
correction_object = (asf_obj_data_correction *)ptr;
|
|
}
|
|
|
|
/* Read the packet flags */
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 2); rsize += 2;
|
|
packet_properties_object = (asf_obj_data_packet *)ptr;
|
|
flags = packet_properties_object->flags;
|
|
property = packet_properties_object->property;
|
|
|
|
packet_info.multiple = flags & 0x01;
|
|
sequence = gst_asf_demux_read_var_length (asf_demux, (flags >> 1) & 0x03, &rsize);
|
|
packet_info.padsize =
|
|
gst_asf_demux_read_var_length (asf_demux, (flags >> 3) & 0x03, &rsize);
|
|
packet_length =
|
|
gst_asf_demux_read_var_length (asf_demux, (flags >> 5) & 0x03, &rsize);
|
|
if (packet_length == 0)
|
|
packet_length = asf_demux->packet_size;
|
|
|
|
GST_DEBUG ("Multiple = %u, Sequence = %u, Padsize = %u, Packet length = %u", packet_info.multiple, sequence, packet_info.padsize, packet_length);
|
|
|
|
/* Read the property flags */
|
|
packet_info.replicsizetype = property & 0x03;
|
|
packet_info.fragoffsettype = (property >> 2) & 0x03;
|
|
packet_info.seqtype = (property >> 4) & 0x03;
|
|
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 4);
|
|
timestamp = (guint32 *)ptr;
|
|
asf_demux->timestamp = *timestamp;
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 2);
|
|
duration = (guint16 *)ptr;
|
|
|
|
rsize += 6;
|
|
|
|
GST_DEBUG ("Timestamp = %x, Duration = %x", asf_demux->timestamp, *duration);
|
|
|
|
if (packet_info.multiple) {
|
|
/* There are multiple payloads */
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&buf, 1);
|
|
rsize++;
|
|
packet_info.segsizetype = (*buf >> 6) & 0x03;
|
|
segments = *buf & 0x3f;
|
|
} else {
|
|
packet_info.segsizetype = 2;
|
|
segments = 1;
|
|
}
|
|
|
|
packet_info.size_left = packet_length - packet_info.padsize - rsize;
|
|
|
|
GST_DEBUG ("rsize: %u size left: %u", rsize, packet_info.size_left);
|
|
|
|
for (segment = 0; segment < segments; segment++) {
|
|
if (!gst_asf_demux_process_segment (asf_demux, &packet_info))
|
|
return FALSE;
|
|
}
|
|
|
|
/* Skip the padding */
|
|
if (packet_info.padsize > 0)
|
|
gst_bytestream_flush (asf_demux->bs, packet_info.padsize);
|
|
|
|
|
|
GST_DEBUG ("Remaining size left: %u", packet_info.size_left);
|
|
|
|
if (packet_info.size_left > 0)
|
|
gst_bytestream_flush (asf_demux->bs, packet_info.size_left);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_process_stream (GstASFDemux *asf_demux, guint64 *obj_size)
|
|
{
|
|
asf_obj_stream *object;
|
|
guint32 stream_id;
|
|
guint32 correction;
|
|
asf_stream_audio *audio_object;
|
|
asf_stream_correction *correction_object;
|
|
asf_stream_video *video_object;
|
|
asf_stream_video_format *video_format_object;
|
|
guint16 size;
|
|
GstBuffer *buf;
|
|
guint32 got_bytes;
|
|
guint16 id;
|
|
guint8 *ptr;
|
|
|
|
/* Get the rest of the header's header */
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 54);
|
|
object = (asf_obj_stream *)ptr;
|
|
|
|
/* Identify the stream type */
|
|
stream_id = gst_asf_demux_identify_guid (asf_demux, asf_stream_guids, &(object->type));
|
|
correction = gst_asf_demux_identify_guid (asf_demux, asf_correction_guids, &(object->correction));
|
|
id = GUINT16_FROM_LE (object->id);
|
|
|
|
switch (stream_id) {
|
|
case ASF_STREAM_AUDIO:
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 18);
|
|
audio_object = (asf_stream_audio *)ptr;
|
|
size = GUINT16_FROM_LE (audio_object->size);
|
|
|
|
GST_INFO ("Object is an audio stream with %u bytes of additional data.", size);
|
|
|
|
if (!gst_asf_demux_add_audio_stream (asf_demux, audio_object, id))
|
|
return FALSE;
|
|
|
|
switch (correction) {
|
|
case ASF_CORRECTION_ON:
|
|
GST_INFO ( "Using error correction");
|
|
|
|
/* Have to read the first byte seperately to avoid endian problems */
|
|
got_bytes = gst_bytestream_read (asf_demux->bs, &buf, 1);
|
|
asf_demux->span = *GST_BUFFER_DATA (buf);
|
|
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 7);
|
|
correction_object = (asf_stream_correction *)ptr;
|
|
GST_DEBUG ("Descrambling: ps:%d cs:%d ds:%d s:%d sd:%d", GUINT16_FROM_LE(correction_object->packet_size), GUINT16_FROM_LE(correction_object->chunk_size), GUINT16_FROM_LE(correction_object->data_size), asf_demux->span, correction_object->silence_data);
|
|
|
|
if (asf_demux->span > 1) {
|
|
if (!correction_object->chunk_size || ((correction_object->packet_size / correction_object->chunk_size) <= 1))
|
|
/* Disable descrambling */
|
|
asf_demux->span = 0;
|
|
} else {
|
|
/* Descambling is enabled */
|
|
asf_demux->ds_packet_size = correction_object->packet_size;
|
|
asf_demux->ds_chunk_size = correction_object->chunk_size;
|
|
}
|
|
|
|
/* Now skip the rest of the silence data */
|
|
if (correction_object->data_size > 1)
|
|
gst_bytestream_flush (asf_demux->bs, correction_object->data_size - 1);
|
|
|
|
break;
|
|
case ASF_CORRECTION_OFF:
|
|
GST_INFO ( "Error correction off");
|
|
gst_bytestream_flush (asf_demux->bs, object->stream_specific_size);
|
|
break;
|
|
default:
|
|
gst_element_error (GST_ELEMENT (asf_demux), "Audio stream using unknown error correction");
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
case ASF_STREAM_VIDEO:
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 11);
|
|
video_object = (asf_stream_video *)ptr;
|
|
size = GUINT16_FROM_LE(video_object->size) - 40; /* Byte order gets
|
|
* offset by single
|
|
* byte */
|
|
GST_INFO ( "Object is a video stream with %u bytes of additional data.", size);
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &ptr, 40);
|
|
video_format_object = (asf_stream_video_format *)ptr;
|
|
|
|
if (!gst_asf_demux_add_video_stream (asf_demux, video_format_object, id))
|
|
return FALSE;
|
|
|
|
/* Read any additional information */
|
|
if (size) {
|
|
got_bytes = gst_bytestream_read (asf_demux->bs, &buf, size);
|
|
/* There is additional data */
|
|
while (got_bytes < size) {
|
|
guint32 remaining;
|
|
GstEvent *event;
|
|
|
|
gst_bytestream_get_status (asf_demux->bs, &remaining, &event);
|
|
gst_event_unref (event);
|
|
|
|
got_bytes = gst_bytestream_read (asf_demux->bs, &buf, size);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
gst_element_error (GST_ELEMENT (asf_demux), "Object is a stream of unrecognised type");
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_skip_object (GstASFDemux *asf_demux, guint64 *obj_size)
|
|
{
|
|
GstByteStream *bs = asf_demux->bs;
|
|
|
|
GST_INFO ( "Skipping object...");
|
|
|
|
gst_bytestream_flush (bs, *obj_size - 24);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gst_asf_demux_read_object_header (GstASFDemux *asf_demux, guint32 *obj_id, guint64 *obj_size)
|
|
{
|
|
guint32 got_bytes;
|
|
ASFGuid *guid;
|
|
GstByteStream *bs = asf_demux->bs;
|
|
guint8 *ptr;
|
|
|
|
|
|
/* First get the GUID */
|
|
if ((got_bytes = gst_bytestream_peek_bytes (bs, &ptr,
|
|
sizeof(ASFGuid))) < sizeof(ASFGuid))
|
|
return FALSE;
|
|
guid = (ASFGuid *) ptr;
|
|
*obj_id = gst_asf_demux_identify_guid (asf_demux, asf_object_guids, guid);
|
|
gst_bytestream_flush (bs, sizeof (ASFGuid));
|
|
|
|
if ((got_bytes = gst_bytestream_peek_bytes (bs, &ptr,
|
|
sizeof(guint64))) < sizeof(guint64))
|
|
return FALSE;
|
|
*obj_size = GUINT64_FROM_LE(* (guint64 *) ptr);
|
|
gst_bytestream_flush (bs, sizeof (guint64));
|
|
|
|
if (*obj_id == ASF_OBJ_UNDEFINED) {
|
|
g_warning ("Could not identify object (0x%08x/0x%08x/0x%08x/0x%08x) with size=%llu",
|
|
guid->v1, guid->v2, guid->v3, guid->v4, *obj_size);
|
|
return TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_process_object (GstASFDemux *asf_demux) {
|
|
|
|
guint32 obj_id;
|
|
guint64 obj_size;
|
|
|
|
if (!gst_asf_demux_read_object_header (asf_demux, &obj_id, &obj_size)) {
|
|
GST_DEBUG (" ***** Error reading object at filepos %" G_GUINT64_FORMAT " (EOS?)\n", /**filepos*/ gst_bytestream_tell (asf_demux->bs));
|
|
gst_asf_demux_handle_sink_event (asf_demux, gst_event_new (GST_EVENT_EOS), 0);
|
|
return FALSE;
|
|
}
|
|
|
|
GST_INFO ( "Found object %u with size %" G_GUINT64_FORMAT, obj_id, obj_size);
|
|
|
|
switch (obj_id) {
|
|
case ASF_OBJ_STREAM:
|
|
return gst_asf_demux_process_stream (asf_demux, &obj_size);
|
|
case ASF_OBJ_DATA:
|
|
gst_asf_demux_process_data (asf_demux, &obj_size);
|
|
/* This is the last object */
|
|
return FALSE;
|
|
case ASF_OBJ_FILE:
|
|
return gst_asf_demux_process_file (asf_demux, &obj_size);
|
|
case ASF_OBJ_HEADER:
|
|
return gst_asf_demux_process_header (asf_demux, &obj_size);
|
|
case ASF_OBJ_CONCEAL_NONE:
|
|
break;
|
|
case ASF_OBJ_COMMENT:
|
|
return gst_asf_demux_process_comment (asf_demux, &obj_size);
|
|
case ASF_OBJ_CODEC_COMMENT:
|
|
return gst_asf_demux_skip_object (asf_demux, &obj_size);
|
|
case ASF_OBJ_INDEX:
|
|
return gst_asf_demux_skip_object (asf_demux, &obj_size);
|
|
case ASF_OBJ_HEAD1:
|
|
return gst_asf_demux_skip_object (asf_demux, &obj_size);
|
|
case ASF_OBJ_HEAD2:
|
|
break;
|
|
case ASF_OBJ_PADDING:
|
|
return gst_asf_demux_skip_object (asf_demux, &obj_size);
|
|
case ASF_OBJ_EXT_CONTENT_DESC:
|
|
return gst_asf_demux_skip_object (asf_demux, &obj_size);
|
|
case ASF_OBJ_BITRATE_PROPS:
|
|
return gst_asf_demux_process_bitrate_props_object (asf_demux, &obj_size);
|
|
case ASF_OBJ_BITRATE_MUTEX:
|
|
return gst_asf_demux_skip_object (asf_demux, &obj_size);
|
|
case ASF_OBJ_UNDEFINED:
|
|
default:
|
|
/* unknown object read, just ignore it, we hate fatal errors */
|
|
return gst_asf_demux_skip_object (asf_demux, &obj_size);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static asf_stream_context *
|
|
gst_asf_demux_get_stream (GstASFDemux *asf_demux,
|
|
guint16 id)
|
|
{
|
|
guint8 i;
|
|
asf_stream_context *stream;
|
|
|
|
for (i = 0; i < asf_demux->num_streams; i++) {
|
|
stream = &asf_demux->stream[i];
|
|
if (stream->id == id) {
|
|
/* We've found the one with the matching id */
|
|
return &asf_demux->stream[i];
|
|
}
|
|
}
|
|
|
|
/* Base case if we haven't found one at all */
|
|
gst_element_error (GST_ELEMENT (asf_demux), "Segment found for undefined stream: (%d)", id);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static inline void
|
|
gst_asf_demux_descramble_segment (GstASFDemux *asf_demux,
|
|
asf_segment_info *segment_info,
|
|
asf_stream_context *stream) {
|
|
GstBuffer *scrambled_buffer;
|
|
GstBuffer *descrambled_buffer;
|
|
GstBuffer *sub_buffer;
|
|
guint offset;
|
|
guint off;
|
|
guint row;
|
|
guint col;
|
|
guint idx;
|
|
|
|
/* descrambled_buffer is initialised in the first iteration */
|
|
descrambled_buffer = NULL;
|
|
scrambled_buffer = stream->payload;
|
|
|
|
offset = 0;
|
|
|
|
for (offset = 0; offset < segment_info->segment_size; offset += asf_demux->ds_chunk_size) {
|
|
off = offset / asf_demux->ds_chunk_size;
|
|
row = off / asf_demux->span;
|
|
col = off % asf_demux->span;
|
|
idx = row + col * asf_demux->ds_packet_size / asf_demux->ds_chunk_size;
|
|
sub_buffer = gst_buffer_create_sub (scrambled_buffer, idx * asf_demux->ds_chunk_size, asf_demux->ds_chunk_size);
|
|
if (!offset) {
|
|
descrambled_buffer = sub_buffer;
|
|
} else {
|
|
gst_buffer_merge (descrambled_buffer, sub_buffer);
|
|
gst_buffer_unref (sub_buffer);
|
|
}
|
|
}
|
|
|
|
stream->payload = descrambled_buffer;
|
|
gst_buffer_unref (scrambled_buffer);
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_process_chunk (GstASFDemux *asf_demux,
|
|
asf_packet_info *packet_info,
|
|
asf_segment_info *segment_info)
|
|
{
|
|
asf_stream_context *stream;
|
|
guint32 got_bytes;
|
|
GstByteStream *bs = asf_demux->bs;
|
|
GstBuffer *buffer;
|
|
|
|
if (!(stream = gst_asf_demux_get_stream (asf_demux, segment_info->stream_number)))
|
|
return FALSE;
|
|
|
|
GST_DEBUG ("Processing chunk of size %u (fo = %d)", segment_info->chunk_size, stream->frag_offset);
|
|
|
|
if (stream->frag_offset == 0) {
|
|
/* new packet */
|
|
stream->sequence = segment_info->sequence;
|
|
asf_demux->pts = segment_info->frag_timestamp - asf_demux->preroll;
|
|
got_bytes = gst_bytestream_peek (bs, &buffer, segment_info->chunk_size);
|
|
GST_DEBUG ("BUFFER: Copied stream to buffer (%p - %d)", buffer, GST_BUFFER_REFCOUNT_VALUE(buffer));
|
|
stream->payload = buffer;
|
|
} else {
|
|
GST_DEBUG ("segment_info->sequence = %d, stream->sequence = %d, segment_info->frag_offset = %d, stream->frag_offset = %d", segment_info->sequence, stream->sequence, segment_info->frag_offset, stream->frag_offset);
|
|
if (segment_info->sequence == stream->sequence &&
|
|
segment_info->frag_offset == stream->frag_offset) {
|
|
GstBuffer *new_buffer;
|
|
/* continuing packet */
|
|
GST_INFO ( "A continuation packet");
|
|
got_bytes = gst_bytestream_peek (bs, &buffer, segment_info->chunk_size);
|
|
GST_DEBUG ("Copied stream to buffer (%p - %d)", buffer, GST_BUFFER_REFCOUNT_VALUE(buffer));
|
|
new_buffer = gst_buffer_merge (stream->payload, buffer);
|
|
GST_DEBUG ("BUFFER: Merged new_buffer (%p - %d) from stream->payload(%p - %d) and buffer (%p - %d)", new_buffer, GST_BUFFER_REFCOUNT_VALUE(new_buffer), stream->payload, GST_BUFFER_REFCOUNT_VALUE(stream->payload), buffer, GST_BUFFER_REFCOUNT_VALUE(buffer));
|
|
gst_buffer_unref (stream->payload);
|
|
gst_buffer_unref (buffer);
|
|
stream->payload = new_buffer;
|
|
} else {
|
|
/* cannot continue current packet: free it */
|
|
stream->frag_offset = 0;
|
|
if (segment_info->frag_offset != 0) {
|
|
/* cannot create new packet */
|
|
GST_DEBUG ("BUFFER: Freeing stream->payload (%p)", stream->payload);
|
|
gst_buffer_free(stream->payload);
|
|
gst_bytestream_flush (bs, segment_info->chunk_size);
|
|
packet_info->size_left -= segment_info->chunk_size;
|
|
return TRUE;
|
|
} else {
|
|
/* create new packet */
|
|
stream->sequence = segment_info->sequence;
|
|
}
|
|
}
|
|
}
|
|
|
|
stream->frag_offset +=segment_info->chunk_size;
|
|
|
|
GST_DEBUG ("frag_offset = %d segment_size = %d ", stream->frag_offset, segment_info->segment_size);
|
|
|
|
if (stream->frag_offset < segment_info->segment_size) {
|
|
/* We don't have the whole packet yet */
|
|
} else {
|
|
/* We have the whole packet now so we should push the packet to
|
|
the src pad now. First though we should check if we need to do
|
|
descrambling */
|
|
if (asf_demux->span > 1) {
|
|
gst_asf_demux_descramble_segment (asf_demux, segment_info, stream);
|
|
}
|
|
|
|
if (GST_PAD_IS_USABLE (stream->pad)) {
|
|
GST_DEBUG ("New buffer is at: %p size: %u", GST_BUFFER_DATA(stream->payload), GST_BUFFER_SIZE(stream->payload));
|
|
|
|
GST_BUFFER_TIMESTAMP (stream->payload) = (GST_SECOND / 1000) * asf_demux->pts;
|
|
|
|
/*!!! Should handle flush events here? */
|
|
GST_DEBUG ("Sending strem %d of size %d", stream->id , segment_info->chunk_size);
|
|
|
|
GST_INFO ( "Pushing pad");
|
|
gst_pad_push (stream->pad, GST_DATA (stream->payload));
|
|
}
|
|
|
|
stream->frag_offset = 0;
|
|
}
|
|
|
|
gst_bytestream_flush (bs, segment_info->chunk_size);
|
|
packet_info->size_left -= segment_info->chunk_size;
|
|
|
|
GST_DEBUG (" ");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* Event stuff
|
|
*/
|
|
|
|
static gboolean
|
|
gst_asf_demux_handle_sink_event (GstASFDemux *asf_demux,
|
|
GstEvent *event,
|
|
guint32 remaining)
|
|
{
|
|
GstEventType type;
|
|
|
|
type = event? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
|
|
|
|
switch (type) {
|
|
case GST_EVENT_EOS: {
|
|
asf_stream_context *stream;
|
|
GstEvent *event = gst_event_new (GST_EVENT_EOS);
|
|
gint n;
|
|
for (n = 0; n < asf_demux->num_streams; n++) {
|
|
stream = &asf_demux->stream[n];
|
|
gst_pad_push (stream->pad, GST_DATA (gst_event_ref (event)));
|
|
}
|
|
gst_event_unref (event);
|
|
gst_bytestream_flush (asf_demux->bs, remaining);
|
|
gst_element_set_eos (GST_ELEMENT (asf_demux));
|
|
break;
|
|
}
|
|
case GST_EVENT_FLUSH:
|
|
g_warning ("flush event");
|
|
break;
|
|
case GST_EVENT_DISCONTINUOUS:
|
|
{
|
|
gint i;
|
|
GstEvent *discont;
|
|
|
|
for (i = 0; i < asf_demux->num_streams; i++) {
|
|
asf_stream_context *stream = &asf_demux->stream[i];
|
|
|
|
if (GST_PAD_IS_USABLE (stream->pad)) {
|
|
GST_DEBUG ("sending discont on %d %" G_GINT64_FORMAT " + %" G_GINT64_FORMAT " = %" G_GINT64_FORMAT, i,
|
|
asf_demux->last_seek, stream->delay, asf_demux->last_seek + stream->delay);
|
|
discont = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME,
|
|
asf_demux->last_seek + stream->delay , NULL);
|
|
gst_pad_push (stream->pad, GST_DATA (discont));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
g_warning ("unhandled event %d", type);
|
|
break;
|
|
}
|
|
|
|
gst_event_unref (event);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_handle_src_event (GstPad *pad, GstEvent *event)
|
|
{
|
|
GST_DEBUG ("asfdemux: handle_src_event");
|
|
return FALSE;
|
|
}
|
|
|
|
static const GstEventMask*
|
|
gst_asf_demux_get_src_event_mask (GstPad *pad) {
|
|
static const GstEventMask masks[] = {
|
|
{ GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_KEY_UNIT },
|
|
{ 0, }
|
|
};
|
|
|
|
return masks;
|
|
}
|
|
|
|
static const GstFormat*
|
|
gst_asf_demux_get_src_formats (GstPad *pad) {
|
|
static const GstFormat formats[] = {
|
|
GST_FORMAT_TIME,
|
|
0
|
|
};
|
|
return formats;
|
|
}
|
|
|
|
static const GstQueryType*
|
|
gst_asf_demux_get_src_query_types (GstPad *pad) {
|
|
static const GstQueryType types[] = {
|
|
GST_QUERY_TOTAL,
|
|
GST_QUERY_POSITION,
|
|
0
|
|
};
|
|
|
|
return types;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_handle_src_query (GstPad *pad,
|
|
GstQueryType type,
|
|
GstFormat *format, gint64 *value)
|
|
{
|
|
GstASFDemux *asf_demux;
|
|
gboolean res = TRUE;
|
|
|
|
asf_demux = GST_ASF_DEMUX (gst_pad_get_parent (pad));
|
|
|
|
switch (type) {
|
|
case GST_QUERY_TOTAL:
|
|
switch (*format) {
|
|
case GST_FORMAT_DEFAULT:
|
|
*format = GST_FORMAT_TIME;
|
|
/* fall through */
|
|
case GST_FORMAT_TIME:
|
|
*value = (GST_SECOND / 1000) * asf_demux->pts;
|
|
break;
|
|
default:
|
|
res = FALSE;
|
|
}
|
|
break;
|
|
case GST_QUERY_POSITION:
|
|
switch (*format) {
|
|
case GST_FORMAT_DEFAULT:
|
|
*format = GST_FORMAT_TIME;
|
|
/* fall through */
|
|
case GST_FORMAT_TIME:
|
|
*value = (GST_SECOND / 1000) * asf_demux->timestamp;
|
|
break;
|
|
default:
|
|
res = FALSE;
|
|
}
|
|
default:
|
|
res = FALSE;
|
|
break;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
|
|
static void
|
|
gst_asf_demux_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GstASFDemux *src;
|
|
|
|
g_return_if_fail (GST_IS_ASF_DEMUX (object));
|
|
|
|
src = GST_ASF_DEMUX (object);
|
|
|
|
switch (prop_id) {
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GstElementStateReturn
|
|
gst_asf_demux_change_state (GstElement *element)
|
|
{
|
|
GstASFDemux *asf_demux = GST_ASF_DEMUX (element);
|
|
gint i;
|
|
|
|
switch (GST_STATE_TRANSITION (element)) {
|
|
case GST_STATE_READY_TO_PAUSED:
|
|
asf_demux->bs = gst_bytestream_new (asf_demux->sinkpad);
|
|
asf_demux->last_seek = 0;
|
|
break;
|
|
case GST_STATE_PAUSED_TO_READY:
|
|
gst_bytestream_destroy (asf_demux->bs);
|
|
for (i = 0 ; i < GST_ASF_DEMUX_NUM_VIDEO_PADS; i++) {
|
|
asf_demux->video_PTS[i] = 0;
|
|
}
|
|
for (i = 0 ; i < GST_ASF_DEMUX_NUM_AUDIO_PADS;i++) {
|
|
asf_demux->audio_PTS[i] = 0;
|
|
}
|
|
break;
|
|
case GST_STATE_READY_TO_NULL:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return GST_ELEMENT_CLASS (parent_class)->change_state (element);
|
|
}
|
|
|
|
static guint32
|
|
gst_asf_demux_identify_guid (GstASFDemux *asf_demux,
|
|
ASFGuidHash *guids,
|
|
ASFGuid *guid_raw)
|
|
{
|
|
guint32 i;
|
|
ASFGuid guid;
|
|
|
|
/* guid_raw is passed in 'as-is' from the file
|
|
* so we must convert it's endianess */
|
|
guid.v1 = GUINT32_FROM_LE (guid_raw->v1);
|
|
guid.v2 = GUINT32_FROM_LE (guid_raw->v2);
|
|
guid.v3 = GUINT32_FROM_LE (guid_raw->v3);
|
|
guid.v4 = GUINT32_FROM_LE (guid_raw->v4);
|
|
|
|
i = 0;
|
|
while (guids[i].obj_id != ASF_OBJ_UNDEFINED) {
|
|
if (guids[i].guid.v1 == guid.v1 &&
|
|
guids[i].guid.v2 == guid.v2 &&
|
|
guids[i].guid.v3 == guid.v3 &&
|
|
guids[i].guid.v4 == guid.v4) {
|
|
return guids[i].obj_id;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
/* The base case if none is found */
|
|
return ASF_OBJ_UNDEFINED;
|
|
}
|
|
|
|
|
|
/*
|
|
* Stream and pad setup code
|
|
*/
|
|
|
|
#ifdef G_HAVE_ISO_VARARGS
|
|
|
|
#define GST_ASF_AUD_CAPS_NEW(name, mimetype, ...) \
|
|
(audio != NULL) ? \
|
|
GST_CAPS_NEW (name, \
|
|
mimetype, \
|
|
"rate", GST_PROPS_INT ( \
|
|
GUINT32_FROM_LE (audio->sample_rate)), \
|
|
"channels", GST_PROPS_INT ( \
|
|
GUINT16_FROM_LE (audio->channels)), \
|
|
__VA_ARGS__) \
|
|
: \
|
|
GST_CAPS_NEW (name, \
|
|
mimetype, \
|
|
"rate", GST_PROPS_INT_RANGE (8000, 96000), \
|
|
"channels", GST_PROPS_INT_RANGE (1, 2), \
|
|
__VA_ARGS__)
|
|
|
|
#elif defined(G_HAVE_GNUC_VARARGS)
|
|
|
|
#define GST_ASF_AUD_CAPS_NEW(name, mimetype, props...) \
|
|
(audio != NULL) ? \
|
|
GST_CAPS_NEW (name, \
|
|
mimetype, \
|
|
"rate", GST_PROPS_INT ( \
|
|
GUINT32_FROM_LE (audio->sample_rate)), \
|
|
"channels", GST_PROPS_INT ( \
|
|
GUINT16_FROM_LE (audio->channels)), \
|
|
##props) \
|
|
: \
|
|
GST_CAPS_NEW (name, \
|
|
mimetype, \
|
|
"rate", GST_PROPS_INT_RANGE (8000, 96000), \
|
|
"channels", GST_PROPS_INT_RANGE (1, 2), \
|
|
##props)
|
|
#endif
|
|
|
|
static GstCaps *
|
|
gst_asf_demux_audio_caps (guint16 codec_id,
|
|
asf_stream_audio *audio, guint8 *extradata)
|
|
{
|
|
GstCaps *caps = NULL;
|
|
gint flags1, flags2;
|
|
|
|
flags1 = 0;
|
|
flags2 = 0;
|
|
|
|
switch (codec_id) {
|
|
case GST_RIFF_WAVE_FORMAT_MPEGL3: /* mp3 */
|
|
caps = GST_ASF_AUD_CAPS_NEW ("asf_demux_audio_src_mp3",
|
|
"audio/mpeg",
|
|
"layer", GST_PROPS_INT (3));
|
|
break;
|
|
|
|
case GST_RIFF_WAVE_FORMAT_MPEGL12: /* mp1 or mp2 */
|
|
caps = GST_ASF_AUD_CAPS_NEW ("asf_demux_audio_src_mp12",
|
|
"audio/mpeg",
|
|
"layer", GST_PROPS_INT (2));
|
|
break;
|
|
|
|
case GST_RIFF_WAVE_FORMAT_PCM: /* PCM/wav */ {
|
|
GstPropsEntry *width, *depth, *signedness;
|
|
|
|
if (audio != NULL) {
|
|
gint ba = GUINT16_FROM_LE (audio->block_align);
|
|
gint ch = GUINT16_FROM_LE (audio->channels);
|
|
gint ws = GUINT16_FROM_LE (audio->word_size);
|
|
width = gst_props_entry_new ("width",
|
|
GST_PROPS_INT (ba * 8 / ch));
|
|
depth = gst_props_entry_new ("depth",
|
|
GST_PROPS_INT (ws));
|
|
signedness = gst_props_entry_new ("signedness",
|
|
GST_PROPS_BOOLEAN (ws != 8));
|
|
} else {
|
|
signedness = gst_props_entry_new ("signed",
|
|
GST_PROPS_LIST (
|
|
GST_PROPS_BOOLEAN (TRUE),
|
|
GST_PROPS_BOOLEAN (FALSE)));
|
|
width = gst_props_entry_new ("width",
|
|
GST_PROPS_LIST (
|
|
GST_PROPS_INT (8),
|
|
GST_PROPS_INT (16)));
|
|
depth = gst_props_entry_new ("depth",
|
|
GST_PROPS_LIST (
|
|
GST_PROPS_INT (8),
|
|
GST_PROPS_INT (16)));
|
|
}
|
|
|
|
caps = GST_ASF_AUD_CAPS_NEW ("asf_demux_audio_src_pcm",
|
|
"audio/x-raw-int",
|
|
"endianness",
|
|
GST_PROPS_INT (G_LITTLE_ENDIAN));
|
|
gst_props_add_entry (caps->properties, width);
|
|
gst_props_add_entry (caps->properties, depth);
|
|
gst_props_add_entry (caps->properties, signedness);
|
|
}
|
|
break;
|
|
|
|
case GST_RIFF_WAVE_FORMAT_VORBIS1: /* vorbis mode 1 */
|
|
case GST_RIFF_WAVE_FORMAT_VORBIS2: /* vorbis mode 2 */
|
|
case GST_RIFF_WAVE_FORMAT_VORBIS3: /* vorbis mode 3 */
|
|
case GST_RIFF_WAVE_FORMAT_VORBIS1PLUS: /* vorbis mode 1+ */
|
|
case GST_RIFF_WAVE_FORMAT_VORBIS2PLUS: /* vorbis mode 2+ */
|
|
case GST_RIFF_WAVE_FORMAT_VORBIS3PLUS: /* vorbis mode 3+ */
|
|
caps = GST_ASF_AUD_CAPS_NEW ("asf_demux_audio_src_vorbis",
|
|
"audio/x-vorbis",
|
|
NULL);
|
|
break;
|
|
|
|
case GST_RIFF_WAVE_FORMAT_A52:
|
|
caps = GST_ASF_AUD_CAPS_NEW ("asf_demux_audio_src_ac3",
|
|
"audio/x-ac3",
|
|
NULL);
|
|
break;
|
|
|
|
case GST_RIFF_WAVE_FORMAT_DIVX_WMAV1:
|
|
/* get flags1 and flags2 ripped from ffmpeg (wmadec.c) */
|
|
if (audio && audio->size >= 4) {
|
|
flags1 = extradata[0] | (extradata[1] << 8);
|
|
flags2 = extradata[2] | (extradata[3] << 8);
|
|
}
|
|
if (audio != NULL)
|
|
caps = GST_ASF_AUD_CAPS_NEW ("asf_demux_audio_src_wmav1",
|
|
"audio/x-wma",
|
|
"wmaversion", GST_PROPS_INT (1),
|
|
"flags1", GST_PROPS_INT (flags1),
|
|
"flags2", GST_PROPS_INT (flags2),
|
|
"block_align", GST_PROPS_INT (audio->block_align),
|
|
"bitrate", GST_PROPS_INT (audio->byte_rate * 8));
|
|
else
|
|
caps = GST_ASF_AUD_CAPS_NEW ("asf_demux_audio_src_wmav1",
|
|
"audio/x-wma",
|
|
"wmaversion", GST_PROPS_INT (1),
|
|
"flags1", GST_PROPS_INT_RANGE (G_MININT, G_MAXINT),
|
|
"flags2", GST_PROPS_INT_RANGE (G_MININT, G_MAXINT),
|
|
"block_align", GST_PROPS_INT_RANGE (0, G_MAXINT),
|
|
"bitrate", GST_PROPS_INT_RANGE (0, G_MAXINT)
|
|
);
|
|
break;
|
|
|
|
case GST_RIFF_WAVE_FORMAT_DIVX_WMAV2:
|
|
/* get flags1 and flags2 ripped from ffmpeg (wmadec.c) */
|
|
if (audio && audio->size >= 6) {
|
|
flags1 = extradata[0] | (extradata[1] << 8) |
|
|
(extradata[2] << 16) | (extradata[3] << 24);
|
|
flags2 = extradata[4] | (extradata[5] << 8);
|
|
}
|
|
if (audio != NULL)
|
|
caps = GST_ASF_AUD_CAPS_NEW ("asf_demux_audio_src_wmav2",
|
|
"audio/x-wma",
|
|
"wmaversion", GST_PROPS_INT (2),
|
|
"flags1", GST_PROPS_INT (flags1),
|
|
"flags2", GST_PROPS_INT (flags2),
|
|
"block_align", GST_PROPS_INT (audio->block_align),
|
|
"bitrate", GST_PROPS_INT (audio->byte_rate * 8));
|
|
else
|
|
caps = GST_ASF_AUD_CAPS_NEW ("asf_demux_audio_src_wmav2",
|
|
"audio/x-wma",
|
|
"wmaversion", GST_PROPS_INT (2),
|
|
"flags1", GST_PROPS_INT_RANGE (G_MININT, G_MAXINT),
|
|
"flags2", GST_PROPS_INT_RANGE (G_MININT, G_MAXINT),
|
|
"block_align", GST_PROPS_INT_RANGE (0, G_MAXINT),
|
|
"bitrate", GST_PROPS_INT_RANGE (0, G_MAXINT)
|
|
);
|
|
break;
|
|
|
|
case GST_RIFF_WAVE_FORMAT_WMAV9:
|
|
caps = GST_ASF_AUD_CAPS_NEW ("asf_demux_audio_src_wmav9",
|
|
"audio/x-wma",
|
|
"wmaversion", GST_PROPS_INT (9));
|
|
break;
|
|
|
|
default:
|
|
g_warning ("asfdemux: unkown audio format 0x%04x",
|
|
codec_id);
|
|
break;
|
|
}
|
|
|
|
return caps;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_add_audio_stream (GstASFDemux *asf_demux,
|
|
asf_stream_audio *audio,
|
|
guint16 id)
|
|
{
|
|
GstPad *src_pad;
|
|
GstCaps *caps;
|
|
gchar *name = NULL;
|
|
guint16 size_left = 0;
|
|
guint8 *extradata=NULL;
|
|
|
|
size_left = GUINT16_FROM_LE(audio->size);
|
|
|
|
/* Create the audio pad */
|
|
name = g_strdup_printf ("audio_%02d", asf_demux->num_audio_streams);
|
|
|
|
src_pad = gst_pad_new_from_template (audiosrctempl, name);
|
|
g_free (name);
|
|
|
|
/* Swallow up any left over data */
|
|
if (size_left) {
|
|
g_warning ("asfdemux: Audio header contains %d bytes of surplus data", size_left);
|
|
gst_asf_demux_read_object_header_rest (asf_demux, &extradata, size_left);
|
|
// gst_bytestream_flush (asf_demux->bs, size_left);
|
|
}
|
|
|
|
/* Now set up the standard propertis from the header info */
|
|
caps = gst_asf_demux_audio_caps (GUINT16_FROM_LE(audio->codec_tag),
|
|
audio, extradata);
|
|
|
|
GST_INFO ("Adding audio stream %u codec %u (0x%x)",
|
|
asf_demux->num_video_streams,
|
|
GUINT16_FROM_LE(audio->codec_tag),
|
|
GUINT16_FROM_LE(audio->codec_tag));
|
|
|
|
asf_demux->num_audio_streams++;
|
|
|
|
return gst_asf_demux_setup_pad (asf_demux, src_pad, caps, id);
|
|
}
|
|
|
|
#ifdef G_HAVE_ISO_VARARGS
|
|
|
|
#define GST_ASF_VID_CAPS_NEW(name, mimetype, ...) \
|
|
(video != NULL) ? \
|
|
GST_CAPS_NEW (name, \
|
|
mimetype, \
|
|
"width", GST_PROPS_INT (width), \
|
|
"height", GST_PROPS_INT (height), \
|
|
"framerate", GST_PROPS_FLOAT (0),/* FIXME */ \
|
|
__VA_ARGS__) \
|
|
: \
|
|
GST_CAPS_NEW (name, \
|
|
mimetype, \
|
|
"width", GST_PROPS_INT_RANGE (16, 4096), \
|
|
"height", GST_PROPS_INT_RANGE (16, 4096), \
|
|
"framerate", GST_PROPS_FLOAT_RANGE (0, G_MAXFLOAT), \
|
|
__VA_ARGS__)
|
|
|
|
#elif defined(G_HAVE_GNUC_VARARGS)
|
|
|
|
#define GST_ASF_VID_CAPS_NEW(name, mimetype, props...) \
|
|
(video != NULL) ? \
|
|
GST_CAPS_NEW (name, \
|
|
mimetype, \
|
|
"width", GST_PROPS_INT (width), \
|
|
"height", GST_PROPS_INT (height), \
|
|
"framerate", GST_PROPS_FLOAT (0),/* FIXME */ \
|
|
##props) \
|
|
: \
|
|
GST_CAPS_NEW (name, \
|
|
mimetype, \
|
|
"width", GST_PROPS_INT_RANGE (16, 4096), \
|
|
"height", GST_PROPS_INT_RANGE (16, 4096), \
|
|
"framerate", GST_PROPS_FLOAT_RANGE (0, G_MAXFLOAT), \
|
|
##props)
|
|
#endif
|
|
|
|
static GstCaps *
|
|
gst_asf_demux_video_caps (guint32 codec_fcc,
|
|
asf_stream_video_format *video)
|
|
{
|
|
GstCaps *caps = NULL;
|
|
gint width = 0, height = 0;
|
|
|
|
if (video != NULL) {
|
|
width = GUINT32_FROM_LE (video->width);
|
|
height = GUINT32_FROM_LE (video->height);
|
|
}
|
|
|
|
switch (codec_fcc) {
|
|
case GST_MAKE_FOURCC('I','4','2','0'):
|
|
case GST_MAKE_FOURCC('Y','U','Y','2'):
|
|
caps = GST_ASF_VID_CAPS_NEW ("asf_demux_video_src_raw",
|
|
"video/x-raw-yuv",
|
|
"format", GST_PROPS_FOURCC (codec_fcc));
|
|
break;
|
|
|
|
case GST_MAKE_FOURCC('M','J','P','G'):
|
|
case GST_MAKE_FOURCC('J','P','E','G'):
|
|
case GST_MAKE_FOURCC('P','I','X','L'):
|
|
case GST_MAKE_FOURCC('V','I','X','L'):
|
|
caps = GST_ASF_VID_CAPS_NEW ("asf_demux_video_src_jpeg",
|
|
"video/x-jpeg",
|
|
NULL);
|
|
break;
|
|
|
|
case GST_MAKE_FOURCC('D','V','S','D'):
|
|
case GST_MAKE_FOURCC('d','v','s','d'):
|
|
caps = GST_ASF_VID_CAPS_NEW ("asf_demux_video_src_dv",
|
|
"video/x-dv",
|
|
"systemstream", GST_PROPS_BOOLEAN (FALSE));
|
|
break;
|
|
|
|
case GST_MAKE_FOURCC('W','M','V','1'):
|
|
caps = GST_ASF_VID_CAPS_NEW ("asf_demux_video_src_wmv1",
|
|
"video/x-wmv",
|
|
"wmvversion", GST_PROPS_INT (1));
|
|
break;
|
|
|
|
case GST_MAKE_FOURCC('W','M','V','2'):
|
|
caps = GST_ASF_VID_CAPS_NEW ("asf_demux_video_src_wmv2",
|
|
"video/x-wmv",
|
|
"wmvversion", GST_PROPS_INT (2));
|
|
break;
|
|
|
|
case GST_MAKE_FOURCC('M','P','G','4'):
|
|
caps = GST_ASF_VID_CAPS_NEW ("asf_demux_video_src_msmpeg41",
|
|
"video/x-msmpeg",
|
|
"msmpegversion", GST_PROPS_INT (41));
|
|
break;
|
|
|
|
case GST_MAKE_FOURCC('M','P','4','2'):
|
|
caps = GST_ASF_VID_CAPS_NEW ("asf_demux_video_src_msmpeg42",
|
|
"video/x-msmpeg",
|
|
"msmpegversion", GST_PROPS_INT (42));
|
|
break;
|
|
|
|
case GST_MAKE_FOURCC('M','P','4','3'):
|
|
caps = GST_ASF_VID_CAPS_NEW ("asf_demux_video_src_msmpeg43",
|
|
"video/x-msmpeg",
|
|
"msmpegversion", GST_PROPS_INT (43));
|
|
break;
|
|
|
|
case GST_MAKE_FOURCC('D','I','V','3'):
|
|
case GST_MAKE_FOURCC('D','I','V','4'):
|
|
case GST_MAKE_FOURCC('D','I','V','5'):
|
|
caps = GST_ASF_VID_CAPS_NEW ("asf_demux_video_src_div3",
|
|
"video/x-divx",
|
|
"divxversion", GST_PROPS_INT (3));
|
|
break;
|
|
|
|
case GST_MAKE_FOURCC('D','I','V','X'):
|
|
case GST_MAKE_FOURCC('d','i','v','x'):
|
|
case GST_MAKE_FOURCC('D','X','5','0'):
|
|
caps = GST_ASF_VID_CAPS_NEW ("asf_demux_video_src_div5",
|
|
"video/x-divx",
|
|
"divxversion", GST_PROPS_INT (5));
|
|
break;
|
|
|
|
default:
|
|
g_warning ("asfdemux: unkown video format " GST_FOURCC_FORMAT "(0x%08x)",
|
|
GST_FOURCC_ARGS(codec_fcc), codec_fcc);
|
|
break;
|
|
}
|
|
|
|
return caps;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_add_video_stream (GstASFDemux *asf_demux,
|
|
asf_stream_video_format *video,
|
|
guint16 id) {
|
|
GstPad *src_pad;
|
|
GstCaps *caps;
|
|
gchar *name = NULL;
|
|
|
|
/* Create the audio pad */
|
|
name = g_strdup_printf ("video_%02d", asf_demux->num_video_streams);
|
|
src_pad = gst_pad_new_from_template (videosrctempl, name);
|
|
g_free (name);
|
|
|
|
/* Now try some gstreamer formatted MIME types (from gst_avi_demux_strf_vids) */
|
|
caps = gst_asf_demux_video_caps (GUINT32_FROM_LE(video->tag),
|
|
video);
|
|
|
|
GST_INFO ("Adding video stream %u codec " GST_FOURCC_FORMAT " (0x%08x)",
|
|
asf_demux->num_video_streams,
|
|
GST_FOURCC_ARGS(GUINT32_FROM_LE(video->tag)),
|
|
GUINT32_FROM_LE(video->tag));
|
|
|
|
asf_demux->num_video_streams++;
|
|
|
|
return gst_asf_demux_setup_pad (asf_demux, src_pad, caps, id);
|
|
}
|
|
|
|
|
|
static gboolean
|
|
gst_asf_demux_setup_pad (GstASFDemux *asf_demux,
|
|
GstPad *src_pad,
|
|
GstCaps *caps_list,
|
|
guint16 id)
|
|
{
|
|
asf_stream_context *stream;
|
|
|
|
gst_pad_try_set_caps (src_pad, caps_list);
|
|
gst_pad_set_formats_function (src_pad, gst_asf_demux_get_src_formats);
|
|
gst_pad_set_event_mask_function (src_pad, gst_asf_demux_get_src_event_mask);
|
|
gst_pad_set_event_function (src_pad, gst_asf_demux_handle_src_event);
|
|
gst_pad_set_query_type_function (src_pad, gst_asf_demux_get_src_query_types);
|
|
gst_pad_set_query_function (src_pad, gst_asf_demux_handle_src_query);
|
|
|
|
stream = &asf_demux->stream[asf_demux->num_streams];
|
|
stream->pad = src_pad;
|
|
stream->id = id;
|
|
stream->frag_offset = 0;
|
|
stream->sequence = 0;
|
|
stream->delay = 0LL;
|
|
|
|
gst_pad_set_element_private (src_pad, stream);
|
|
|
|
GST_INFO ("Adding pad for stream %u",
|
|
asf_demux->num_streams);
|
|
|
|
|
|
asf_demux->num_streams++;
|
|
|
|
gst_element_add_pad (GST_ELEMENT (asf_demux), src_pad);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
plugin_init (GstPlugin *plugin)
|
|
{
|
|
if (!gst_library_load ("gstbytestream"))
|
|
return FALSE;
|
|
|
|
/* create an elementfactory for the asf_demux element */
|
|
if (!gst_element_register (plugin, "asfdemux", GST_RANK_PRIMARY, GST_TYPE_ASF_DEMUX))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GST_PLUGIN_DEFINE (
|
|
GST_VERSION_MAJOR,
|
|
GST_VERSION_MINOR,
|
|
"asfdemux",
|
|
"Demuxes ASF streams",
|
|
plugin_init,
|
|
VERSION,
|
|
"LGPL",
|
|
GST_PACKAGE,
|
|
GST_ORIGIN
|
|
)
|