mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 13:41:48 +00:00
c825677105
Original commit message from CVS: Lower the rank, the typefind functions are broken
1356 lines
38 KiB
C
1356 lines
38 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.
|
|
*/
|
|
|
|
|
|
//#define GST_DEBUG_ENABLED
|
|
#include "gstasfdemux.h"
|
|
|
|
enum {
|
|
ASF_OBJ_UNDEFINED = 0,
|
|
ASF_OBJ_STREAM,
|
|
ASF_OBJ_DATA,
|
|
ASF_OBJ_FILE,
|
|
ASF_OBJ_HEADER,
|
|
ASF_OBJ_CONCEAL_NONE,
|
|
ASF_OBJ_COMMENT,
|
|
ASF_OBJ_CODEC_COMMENT,
|
|
ASF_OBJ_INDEX,
|
|
ASF_OBJ_HEAD1,
|
|
ASF_OBJ_HEAD2,
|
|
ASF_OBJ_PADDING,
|
|
};
|
|
|
|
enum {
|
|
ASF_STREAM_UNDEFINED = 0,
|
|
ASF_STREAM_VIDEO,
|
|
ASF_STREAM_AUDIO,
|
|
};
|
|
|
|
enum {
|
|
ASF_CORRECTION_UNDEFINED = 0,
|
|
ASF_CORRECTION_ON,
|
|
ASF_CORRECTION_OFF,
|
|
};
|
|
|
|
static GstASFGuidHash asf_correction_guids[] = {
|
|
{ ASF_CORRECTION_ON, { 0xBFC3CD50, 0x11CF618F, 0xAA00B28B, 0x20E2B400 }},
|
|
{ ASF_CORRECTION_OFF, { 0x20FB5700, 0x11CF5B55, 0x8000FDA8, 0x2B445C5F }},
|
|
{ ASF_CORRECTION_UNDEFINED, { 0, 0, 0, 0 }},
|
|
};
|
|
|
|
static GstASFGuidHash asf_stream_guids[] = {
|
|
{ ASF_STREAM_VIDEO, { 0xBC19EFC0, 0x11CF5B4D, 0x8000FDA8, 0x2B445C5F }},
|
|
{ ASF_STREAM_AUDIO, { 0xF8699E40, 0x11CF5B4D, 0x8000FDA8, 0x2B445C5F }},
|
|
{ ASF_STREAM_UNDEFINED, { 0, 0, 0, 0 }},
|
|
};
|
|
|
|
static GstASFGuidHash asf_object_guids[] = {
|
|
{ ASF_OBJ_STREAM, { 0xB7DC0791, 0x11CFA9B7, 0xC000E68E, 0x6553200C }},
|
|
{ ASF_OBJ_DATA, { 0x75b22636, 0x11cf668e, 0xAA00D9a6, 0x6Cce6200 }},
|
|
{ ASF_OBJ_FILE, { 0x8CABDCA1, 0x11CFA947, 0xC000E48E, 0x6553200C }},
|
|
{ ASF_OBJ_HEADER, { 0x75B22630, 0x11CF668E, 0xAA00D9A6, 0x6CCE6200 }},
|
|
{ ASF_OBJ_CONCEAL_NONE, { 0x20fb5700, 0x11cf5b55, 0x8000FDa8, 0x2B445C5f }},
|
|
{ ASF_OBJ_COMMENT, { 0x75b22633, 0x11cf668e, 0xAA00D9a6, 0x6Cce6200 }},
|
|
{ ASF_OBJ_CODEC_COMMENT, { 0x86D15240, 0x11D0311D, 0xA000A4A3, 0xF64803C9 }},
|
|
{ ASF_OBJ_CODEC_COMMENT, { 0x86d15241, 0x11d0311d, 0xA000A4a3, 0xF64803c9 }},
|
|
{ ASF_OBJ_INDEX, { 0x33000890, 0x11cfe5b1, 0xA000F489, 0xCB4903c9 }},
|
|
{ ASF_OBJ_HEAD1, { 0x5fbf03b5, 0x11cfa92e, 0xC000E38e, 0x6553200c }},
|
|
{ ASF_OBJ_HEAD2, { 0xabd3d211, 0x11cfa9ba, 0xC000E68e, 0x6553200c }},
|
|
{ ASF_OBJ_PADDING, { 0x1806D474, 0x4509CADF, 0xAB9ABAA4, 0xE8AA96CD }},
|
|
{ ASF_OBJ_UNDEFINED, { 0, 0, 0, 0 }},
|
|
};
|
|
|
|
struct _asf_obj_header {
|
|
guint32 num_objects;
|
|
guint8 unknown1;
|
|
guint8 unknown2;
|
|
};
|
|
|
|
typedef struct _asf_obj_header asf_obj_header;
|
|
|
|
struct _asf_obj_file {
|
|
GstASFGuid file_id;
|
|
guint64 file_size;
|
|
guint64 creation_time;
|
|
guint64 packets_count;
|
|
guint64 play_time;
|
|
guint64 send_time;
|
|
guint64 preroll;
|
|
guint32 flags;
|
|
guint32 min_pktsize;
|
|
guint32 max_pktsize;
|
|
guint32 min_bitrate;
|
|
};
|
|
|
|
typedef struct _asf_obj_file asf_obj_file;
|
|
|
|
struct _asf_obj_stream {
|
|
GstASFGuid type;
|
|
GstASFGuid correction;
|
|
guint64 offset;
|
|
guint32 unknown1;
|
|
guint32 unknown2;
|
|
guint16 flags;
|
|
guint32 unknown3;
|
|
};
|
|
|
|
typedef struct _asf_obj_stream asf_obj_stream;
|
|
|
|
struct _asf_stream_audio {
|
|
guint16 codec_tag;
|
|
guint16 channels;
|
|
guint32 sample_rate;
|
|
guint32 byte_rate;
|
|
guint16 block_align;
|
|
guint16 word_size;
|
|
guint16 size;
|
|
};
|
|
|
|
typedef struct _asf_stream_audio asf_stream_audio;
|
|
|
|
struct _asf_stream_correction {
|
|
guint8 span;
|
|
guint16 packet_size;
|
|
guint16 chunk_size;
|
|
guint16 data_size;
|
|
guint8 silence_data;
|
|
};
|
|
|
|
typedef struct _asf_stream_correction asf_stream_correction;
|
|
|
|
struct _asf_stream_video {
|
|
guint32 width;
|
|
guint32 height;
|
|
guint8 unknown;
|
|
guint16 size;
|
|
};
|
|
|
|
typedef struct _asf_stream_video asf_stream_video;
|
|
|
|
struct _asf_stream_video_format {
|
|
guint32 size;
|
|
guint32 width;
|
|
guint32 height;
|
|
guint16 panes;
|
|
guint16 depth;
|
|
guint32 tag;
|
|
guint32 unknown1;
|
|
guint32 unknown2;
|
|
guint32 unknown3;
|
|
guint32 unknown4;
|
|
guint32 unknown5;
|
|
};
|
|
|
|
typedef struct _asf_stream_video_format asf_stream_video_format;
|
|
|
|
struct _asf_obj_data {
|
|
GstASFGuid file_id;
|
|
guint64 packets;
|
|
guint8 unknown1;
|
|
guint8 unknown2;
|
|
guint8 correction;
|
|
};
|
|
|
|
typedef struct _asf_obj_data asf_obj_data;
|
|
|
|
struct _asf_obj_data_correction {
|
|
guint8 type;
|
|
guint8 cycle;
|
|
};
|
|
|
|
typedef struct _asf_obj_data_correction asf_obj_data_correction;
|
|
|
|
struct _asf_obj_data_packet {
|
|
guint8 flags;
|
|
guint8 property;
|
|
};
|
|
|
|
typedef struct _asf_obj_data_packet asf_obj_data_packet;
|
|
|
|
struct _asf_packet_info {
|
|
guint32 padsize;
|
|
guint8 replicsizetype;
|
|
guint8 fragoffsettype;
|
|
guint8 seqtype;
|
|
guint8 segsizetype;
|
|
gboolean multiple;
|
|
guint32 size_left;
|
|
};
|
|
|
|
typedef struct _asf_packet_info asf_packet_info;
|
|
|
|
struct _asf_segment_info {
|
|
guint8 stream_number;
|
|
guint32 chunk_size;
|
|
guint32 frag_offset;
|
|
guint32 sequence;
|
|
gboolean compressed;
|
|
};
|
|
|
|
typedef struct _asf_segment_info asf_segment_info;
|
|
|
|
|
|
struct _asf_replicated_data {
|
|
guint32 object_size;
|
|
guint32 frag_timestamp;
|
|
};
|
|
|
|
typedef struct _asf_replicated_data asf_replicated_data;
|
|
|
|
|
|
/* elementfactory information */
|
|
static GstElementDetails gst_asf_demux_details = {
|
|
"ASF Demuxer",
|
|
"Codec/Demuxer",
|
|
"LGPL",
|
|
"Demultiplexes ASF Streams",
|
|
VERSION,
|
|
"Owen Fraser-Green <owen@discobabe.net>",
|
|
"(C) 2002",
|
|
};
|
|
|
|
static GstCaps* asf_asf_type_find (GstBuffer *buf, gpointer private);
|
|
static GstCaps* asf_wma_type_find (GstBuffer *buf, gpointer private);
|
|
static GstCaps* asf_wax_type_find (GstBuffer *buf, gpointer private);
|
|
static GstCaps* asf_wmv_type_find (GstBuffer *buf, gpointer private);
|
|
static GstCaps* asf_wvx_type_find (GstBuffer *buf, gpointer private);
|
|
static GstCaps* asf_wm_type_find (GstBuffer *buf, gpointer private);
|
|
|
|
/* typefactory for 'asf' */
|
|
static GstTypeDefinition asf_type_definitions[] = {
|
|
{ "asfdemux_video/asf",
|
|
"video/x-ms-asf",
|
|
".asf .asx",
|
|
asf_asf_type_find },
|
|
{ "asfdemux_video/wma",
|
|
"video/x-ms-wma",
|
|
".wma",
|
|
asf_wma_type_find },
|
|
{ "asfdemux_video/wax",
|
|
"video/x-ms-wax",
|
|
".wax",
|
|
asf_wax_type_find },
|
|
{ "asfdemux_video/wmv",
|
|
"video/x-ms-wmv",
|
|
".wmv",
|
|
asf_wmv_type_find },
|
|
{ "asfdemux_video/wvx",
|
|
"video/x-ms-wvx",
|
|
".wvx",
|
|
asf_wvx_type_find },
|
|
{ "asfdemux_video/wm",
|
|
"video/x-ms-wm",
|
|
".wm",
|
|
asf_wm_type_find },
|
|
{ NULL, NULL, NULL, NULL }
|
|
};
|
|
|
|
static GstCaps*
|
|
asf_asf_type_find (GstBuffer *buf, gpointer private)
|
|
{
|
|
GstCaps *new;
|
|
|
|
/*!!! Check here if the header is a valid ASF 1.0. return NULL otherwise */
|
|
|
|
new = gst_caps_new (
|
|
"asf_type_find",
|
|
"video/x-ms-asf",
|
|
gst_props_new ("asfversion",
|
|
GST_PROPS_INT (1),
|
|
NULL));
|
|
return new;
|
|
}
|
|
|
|
static GstCaps*
|
|
asf_wma_type_find (GstBuffer *buf, gpointer private)
|
|
{
|
|
GstCaps *new;
|
|
|
|
/*!!! Check here if the header is a valid ASF 1.0. return NULL otherwise */
|
|
|
|
new = gst_caps_new (
|
|
"asf_type_find",
|
|
"video/x-ms-asf",
|
|
gst_props_new ("asfversion",
|
|
GST_PROPS_INT (1),
|
|
NULL));
|
|
return new;
|
|
}
|
|
|
|
static GstCaps*
|
|
asf_wax_type_find (GstBuffer *buf, gpointer private)
|
|
{
|
|
GstCaps *new;
|
|
|
|
/*!!! Check here if the header is a valid ASF 1.0. return NULL otherwise */
|
|
|
|
new = gst_caps_new (
|
|
"asf_type_find",
|
|
"video/x-ms-asf",
|
|
gst_props_new ("asfversion",
|
|
GST_PROPS_INT (1),
|
|
NULL));
|
|
return new;
|
|
}
|
|
|
|
static GstCaps*
|
|
asf_wmv_type_find (GstBuffer *buf, gpointer private)
|
|
{
|
|
GstCaps *new;
|
|
|
|
/*!!! Check here if the header is a valid ASF 1.0. return NULL otherwise */
|
|
|
|
new = gst_caps_new (
|
|
"asf_type_find",
|
|
"video/x-ms-asf",
|
|
gst_props_new ("asfversion",
|
|
GST_PROPS_INT (1),
|
|
NULL));
|
|
return new;
|
|
}
|
|
|
|
static GstCaps*
|
|
asf_wvx_type_find (GstBuffer *buf, gpointer private)
|
|
{
|
|
GstCaps *new;
|
|
|
|
/*!!! Check here if the header is a valid ASF 1.0. return NULL otherwise */
|
|
|
|
new = gst_caps_new (
|
|
"asf_type_find",
|
|
"video/x-ms-asf",
|
|
gst_props_new ("asfversion",
|
|
GST_PROPS_INT (1),
|
|
NULL));
|
|
return new;
|
|
}
|
|
|
|
static GstCaps*
|
|
asf_wm_type_find (GstBuffer *buf, gpointer private)
|
|
{
|
|
GstCaps *new;
|
|
|
|
/*!!! Check here if the header is a valid ASF 1.0. return NULL otherwise */
|
|
|
|
new = gst_caps_new (
|
|
"asf_type_find",
|
|
"video/x-ms-asf",
|
|
gst_props_new ("asfversion",
|
|
GST_PROPS_INT (1),
|
|
NULL));
|
|
return new;
|
|
}
|
|
|
|
GST_PAD_TEMPLATE_FACTORY (sink_factory,
|
|
"sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_CAPS_NEW ("asf_asf_demux_sink",
|
|
"video/x-ms-asf",
|
|
"asfversion", GST_PROPS_INT_RANGE (1, 1)
|
|
),
|
|
GST_CAPS_NEW ("asf_wma_demux_sink",
|
|
"video/x-ms-wma",
|
|
"asfversion", GST_PROPS_INT_RANGE (1, 1)
|
|
),
|
|
GST_CAPS_NEW ("asf_wax_demux_sink",
|
|
"video/x-ms-wax",
|
|
"asfversion", GST_PROPS_INT_RANGE (1, 1)
|
|
),
|
|
GST_CAPS_NEW ("asf_wmv_demux_sink",
|
|
"video/x-ms-wmv",
|
|
"asfversion", GST_PROPS_INT_RANGE (1, 1)
|
|
),
|
|
GST_CAPS_NEW ("asf_wvx_demux_sink",
|
|
"video/x-ms-wvx",
|
|
"asfversion", GST_PROPS_INT_RANGE (1, 1)
|
|
),
|
|
GST_CAPS_NEW ("asf_wm_demux_sink",
|
|
"video/x-ms-wm",
|
|
"asfversion", GST_PROPS_INT_RANGE (1, 1)
|
|
)
|
|
);
|
|
|
|
GST_PAD_TEMPLATE_FACTORY (audio_factory,
|
|
"audio_%02d",
|
|
GST_PAD_SRC,
|
|
GST_PAD_SOMETIMES,
|
|
GST_CAPS_NEW ("asf_demux_audio",
|
|
"audio/x-wav",
|
|
NULL
|
|
)
|
|
);
|
|
|
|
GST_PAD_TEMPLATE_FACTORY (video_factory,
|
|
"video_%02d",
|
|
GST_PAD_SRC,
|
|
GST_PAD_SOMETIMES,
|
|
GST_CAPS_NEW ("asf_demux_video_mpeg4",
|
|
"video/mpeg",
|
|
"mpegversion", GST_PROPS_INT (4),
|
|
"systemstream", GST_PROPS_BOOLEAN (FALSE),
|
|
NULL
|
|
)
|
|
);
|
|
|
|
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,
|
|
guint64 *filepos);
|
|
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,
|
|
GstASFGuidHash *guids,
|
|
GstASFGuid *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_src_event (GstPad *pad,
|
|
GstEvent *event);
|
|
static const GstFormat* gst_asf_demux_get_src_formats (GstPad *pad);
|
|
static const GstPadQueryType* gst_asf_demux_get_src_query_types (GstPad *pad);
|
|
static gboolean gst_asf_demux_handle_src_query (GstPad *pad,
|
|
GstPadQueryType type,
|
|
GstFormat *format, gint64 *value);
|
|
|
|
static GstElementStateReturn
|
|
gst_asf_demux_change_state (GstElement *element);
|
|
|
|
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),
|
|
NULL,
|
|
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_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)
|
|
{
|
|
gint 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);
|
|
|
|
/* i think everything is already zero'd, but oh well*/
|
|
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;
|
|
guint64 filepos = 0;
|
|
|
|
g_return_if_fail (element != NULL);
|
|
g_return_if_fail (GST_IS_ASF_DEMUX (element));
|
|
|
|
asf_demux = GST_ASF_DEMUX (element);
|
|
|
|
asf_demux->restart = FALSE;
|
|
|
|
g_print ("asf_demux_loop called\n");
|
|
/* this is basically an infinite loop */
|
|
if (!gst_asf_demux_process_object (asf_demux, &filepos)) {
|
|
gst_element_error (element, "This doesn't appear to be an ASF stream");
|
|
return;
|
|
}
|
|
if (!asf_demux->restart)
|
|
/* if we exit the loop we are EOS */
|
|
gst_pad_event_default (asf_demux->sinkpad, gst_event_new (GST_EVENT_EOS));
|
|
}
|
|
|
|
static inline gboolean
|
|
gst_asf_demux_read_object_header (GstASFDemux *asf_demux, guint32 *obj_id, guint64 *obj_size)
|
|
{
|
|
guint32 got_bytes;
|
|
GstASFGuid *guid;
|
|
guint64 *size;
|
|
GstByteStream *bs = asf_demux->bs;
|
|
|
|
|
|
/* First get the GUID */
|
|
got_bytes = gst_bytestream_peek_bytes (bs, (guint8**)&guid, sizeof(GstASFGuid));
|
|
if (got_bytes < sizeof (GstASFGuid)) {
|
|
guint32 remaining;
|
|
GstEvent *event;
|
|
|
|
gst_bytestream_get_status (bs, &remaining, &event);
|
|
gst_event_unref (event);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
*obj_id = gst_asf_demux_identify_guid (asf_demux, asf_object_guids, guid);
|
|
|
|
gst_bytestream_flush (bs, sizeof (GstASFGuid));
|
|
|
|
if (*obj_id == ASF_OBJ_UNDEFINED) {
|
|
GST_INFO (GST_CAT_PLUGIN_INFO, "Object found with unknown GUID %08x %08x %08x %08x", guid->v1, guid->v2, guid->v3, guid->v4);
|
|
gst_element_error (GST_ELEMENT (asf_demux), "Could not identify object");
|
|
return FALSE;
|
|
}
|
|
|
|
/* Now get the object size */
|
|
got_bytes = gst_bytestream_peek_bytes (bs, (guint8**)&size, sizeof (guint64));
|
|
while (got_bytes < sizeof (guint64)) {
|
|
guint32 remaining;
|
|
GstEvent *event;
|
|
|
|
gst_bytestream_get_status (bs, &remaining, &event);
|
|
gst_event_unref (event);
|
|
|
|
got_bytes = gst_bytestream_peek_bytes (bs, (guint8**)&size, sizeof (guint64));
|
|
}
|
|
|
|
*obj_size = GUINT64_FROM_LE (*size);
|
|
gst_bytestream_flush (bs, sizeof (guint64));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static guint32 gst_asf_demux_read_var_length (GstASFDemux *asf_demux, guint8 type, guint32 *rsize)
|
|
{
|
|
guint32 got_bytes;
|
|
guint32 *var;
|
|
guint32 ret = 0;
|
|
GstByteStream *bs = asf_demux->bs;
|
|
|
|
if (type == 0) {
|
|
return 0;
|
|
}
|
|
|
|
got_bytes = gst_bytestream_peek_bytes (bs, (guint8**)&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, (guint8**)&var, 4);
|
|
}
|
|
|
|
switch (type) {
|
|
case 1:
|
|
ret = GUINT32_FROM_LE(*var) & 0xff;
|
|
gst_bytestream_flush (bs, 1);
|
|
*rsize += 1;
|
|
break;
|
|
case 2:
|
|
ret = GUINT32_FROM_LE(*var) & 0xffff;
|
|
gst_bytestream_flush (bs, 2);
|
|
*rsize += 2;
|
|
break;
|
|
case 3:
|
|
ret = GUINT32_FROM_LE(*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;
|
|
|
|
got_bytes = gst_bytestream_peek_bytes (bs, buf, size);
|
|
while (got_bytes < size) {
|
|
guint32 remaining;
|
|
GstEvent *event;
|
|
|
|
gst_bytestream_get_status (bs, &remaining, &event);
|
|
gst_event_unref (event);
|
|
|
|
got_bytes = gst_bytestream_peek_bytes (bs, buf, size);
|
|
}
|
|
|
|
gst_bytestream_flush (bs, size);
|
|
}
|
|
|
|
static inline gboolean
|
|
gst_asf_demux_process_file (GstASFDemux *asf_demux, guint64 *filepos, guint64 *obj_size)
|
|
{
|
|
asf_obj_file *object;
|
|
guint64 packets;
|
|
|
|
/* Get the rest of the header's header */
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&object, 80);
|
|
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;
|
|
|
|
GST_INFO (GST_CAT_PLUGIN_INFO, "Object is a file with %llu data packets", packets);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static inline gboolean
|
|
gst_asf_demux_process_header (GstASFDemux *asf_demux, guint64 *filepos, guint64 *obj_size)
|
|
{
|
|
guint32 num_objects;
|
|
asf_obj_header *object;
|
|
guint32 i;
|
|
|
|
|
|
/* Get the rest of the header's header */
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&object, 6);
|
|
num_objects = GUINT32_FROM_LE (object->num_objects);
|
|
|
|
GST_INFO (GST_CAT_PLUGIN_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, filepos)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline 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_timestamp;
|
|
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 (GST_CAT_PLUGIN_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 (GST_CAT_PLUGIN_INFO, "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;
|
|
|
|
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, (guint8**)&replicated_data_header, 8);
|
|
frag_timestamp = replicated_data_header->frag_timestamp;
|
|
|
|
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;
|
|
} else {
|
|
segment_info.compressed = FALSE;
|
|
}
|
|
|
|
time_start = segment_info.frag_offset;
|
|
segment_info.frag_offset = 0;
|
|
frag_timestamp = asf_demux->timestamp;
|
|
}
|
|
|
|
GST_DEBUG (GST_CAT_PLUGIN_INFO, "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 (GST_CAT_PLUGIN_INFO, "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;
|
|
|
|
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 inline gboolean
|
|
gst_asf_demux_process_data (GstASFDemux *asf_demux, guint64 *filepos, 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;
|
|
|
|
/* Get the rest of the header */
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&object, 26);
|
|
packets = GUINT64_FROM_LE (object->packets);
|
|
|
|
GST_INFO (GST_CAT_PLUGIN_INFO, "Object is data with %llu packets", packets);
|
|
|
|
for (packet = 0; packet < packets; packet++) {
|
|
GST_INFO (GST_CAT_PLUGIN_INFO, "Process packet %llu", packet);
|
|
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&buf, 1); rsize=1;
|
|
if (*buf & 0x80) {
|
|
asf_obj_data_correction *correction_object;
|
|
|
|
/* Uses error correction */
|
|
correction = TRUE;
|
|
GST_DEBUG (GST_CAT_PLUGIN_INFO, "Data has error correction (%x)", *buf);
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&correction_object, 2); rsize += 2;
|
|
}
|
|
|
|
/* Read the packet flags */
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&packet_properties_object, 2); rsize += 2;
|
|
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 (GST_CAT_PLUGIN_INFO, "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, (guint8**)×tamp, 4);
|
|
asf_demux->timestamp = *timestamp;
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&duration, 2);
|
|
|
|
rsize += 6;
|
|
|
|
GST_DEBUG (GST_CAT_PLUGIN_INFO, "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 (GST_CAT_PLUGIN_INFO, "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 (GST_CAT_PLUGIN_INFO, "Remaining size left: %u", packet_info.size_left);
|
|
|
|
if (packet_info.size_left > 0) {
|
|
gst_element_error (GST_ELEMENT (asf_demux), "The packet's header indicated a length longer than its contents");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline gboolean
|
|
gst_asf_demux_process_stream (GstASFDemux *asf_demux, guint64 *filepos, 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;
|
|
GstPad **outpad = NULL;
|
|
GstPadTemplate *new_template = NULL;
|
|
gchar *name = NULL;
|
|
asf_stream_context *stream = NULL;
|
|
|
|
/* Get the rest of the header's header */
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&object, 54);
|
|
|
|
/* 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));
|
|
|
|
|
|
switch (stream_id) {
|
|
case ASF_STREAM_AUDIO:
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&audio_object, 18);
|
|
size = GUINT16_FROM_LE (audio_object->size);
|
|
|
|
name = g_strdup_printf ("audio_%02d", asf_demux->num_audio_streams);
|
|
outpad = &asf_demux->audio_pad[asf_demux->num_audio_streams++];
|
|
new_template = GST_PAD_TEMPLATE_GET (audio_factory);
|
|
|
|
GST_INFO (GST_CAT_PLUGIN_INFO, "Object is an audio stream with %u bytes of additional data. Assigned to pad '%s'", size, name);
|
|
switch (correction) {
|
|
case ASF_CORRECTION_ON:
|
|
GST_INFO (GST_CAT_PLUGIN_INFO, "Using error correction");
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&correction_object, 8);
|
|
break;
|
|
case ASF_CORRECTION_OFF:
|
|
break;
|
|
default:
|
|
gst_element_error (GST_ELEMENT (asf_demux), "Audio stream using unknown error correction");
|
|
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;
|
|
case ASF_STREAM_VIDEO:
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&video_object, 11);
|
|
size = GUINT16_FROM_BE(video_object->size) - 40; /* Byte order gets
|
|
* offset by single
|
|
* byte */
|
|
name = g_strdup_printf ("video_%02d", asf_demux->num_video_streams);
|
|
outpad = &asf_demux->video_pad[asf_demux->num_video_streams++];
|
|
new_template = GST_PAD_TEMPLATE_GET (video_factory);
|
|
|
|
/*!!! Set for MJPG too */
|
|
|
|
GST_INFO (GST_CAT_PLUGIN_INFO, "Object is a video stream with %u bytes of additional data. Assigned to pad '%s'", size, name);
|
|
gst_asf_demux_read_object_header_rest (asf_demux, (guint8**)&video_format_object, 40);
|
|
/* 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;
|
|
}
|
|
|
|
*outpad = gst_pad_new_from_template (new_template, name);
|
|
|
|
/* Set up the pad */
|
|
gst_pad_try_set_caps (*outpad, gst_pad_get_pad_template_caps (*outpad));
|
|
gst_pad_set_formats_function (*outpad, gst_asf_demux_get_src_formats);
|
|
gst_pad_set_event_mask_function (*outpad, gst_asf_demux_get_src_event_mask);
|
|
gst_pad_set_event_function (*outpad, gst_asf_demux_handle_src_event);
|
|
gst_pad_set_query_type_function (*outpad, gst_asf_demux_get_src_query_types);
|
|
gst_pad_set_query_function (*outpad, gst_asf_demux_handle_src_query);
|
|
|
|
/* Initialise the stream context */
|
|
stream = &asf_demux->stream[asf_demux->num_streams];
|
|
stream->pad = *outpad;
|
|
stream->frag_offset = 0;
|
|
stream->sequence = 0;
|
|
gst_pad_set_element_private (*outpad, stream);
|
|
asf_demux->num_streams++;
|
|
|
|
/* Add the pad to the element */
|
|
gst_element_add_pad (GST_ELEMENT (asf_demux), *outpad);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_skip_object (GstASFDemux *asf_demux, guint64 *filepos, guint64 *obj_size)
|
|
{
|
|
GstByteStream *bs = asf_demux->bs;
|
|
|
|
GST_INFO (GST_CAT_PLUGIN_INFO, "Skipping object...");
|
|
|
|
gst_bytestream_flush (bs, *obj_size - 24);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_process_object (GstASFDemux *asf_demux,
|
|
guint64 *filepos) {
|
|
|
|
guint32 obj_id;
|
|
guint64 obj_size;
|
|
|
|
if (!gst_asf_demux_read_object_header (asf_demux, &obj_id, &obj_size)) {
|
|
g_print (" ***** Error reading object at filepos 0x%08llx\n", *filepos);
|
|
return FALSE;
|
|
}
|
|
|
|
GST_INFO (GST_CAT_PLUGIN_INFO, "Found object %u with size %llx", obj_id, obj_size);
|
|
|
|
switch (obj_id) {
|
|
case ASF_OBJ_STREAM:
|
|
gst_asf_demux_process_stream (asf_demux, filepos, &obj_size);
|
|
break;
|
|
case ASF_OBJ_DATA:
|
|
gst_asf_demux_process_data (asf_demux, filepos, &obj_size);
|
|
break;
|
|
case ASF_OBJ_FILE:
|
|
gst_asf_demux_process_file (asf_demux, filepos, &obj_size);
|
|
break;
|
|
case ASF_OBJ_HEADER:
|
|
gst_asf_demux_process_header (asf_demux, filepos, &obj_size);
|
|
break;
|
|
case ASF_OBJ_CONCEAL_NONE:
|
|
break;
|
|
case ASF_OBJ_COMMENT:
|
|
gst_asf_demux_skip_object (asf_demux, filepos, &obj_size);
|
|
break;
|
|
case ASF_OBJ_CODEC_COMMENT:
|
|
gst_asf_demux_skip_object (asf_demux, filepos, &obj_size);
|
|
break;
|
|
case ASF_OBJ_INDEX:
|
|
gst_asf_demux_skip_object (asf_demux, filepos, &obj_size);
|
|
break;
|
|
case ASF_OBJ_HEAD1:
|
|
gst_asf_demux_skip_object (asf_demux, filepos, &obj_size);
|
|
break;
|
|
case ASF_OBJ_HEAD2:
|
|
break;
|
|
case ASF_OBJ_PADDING:
|
|
gst_asf_demux_skip_object (asf_demux, filepos, &obj_size);
|
|
break;
|
|
default:
|
|
gst_element_error (GST_ELEMENT (asf_demux), "Unknown ASF object");
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean gst_asf_demux_process_chunk (GstASFDemux *asf_demux,
|
|
asf_packet_info *packet_info,
|
|
asf_segment_info *segment_info)
|
|
{
|
|
asf_stream_context *stream;
|
|
GstFormat format;
|
|
guint64 next_ts;
|
|
guint32 got_bytes;
|
|
GstByteStream *bs = asf_demux->bs;
|
|
|
|
if (segment_info->stream_number > asf_demux->num_streams) {
|
|
gst_element_error (GST_ELEMENT (asf_demux), "Segment found for stream out of range");
|
|
return FALSE;
|
|
}
|
|
stream = &asf_demux->stream[segment_info->stream_number - 1];
|
|
|
|
GST_DEBUG (GST_CAT_PLUGIN_INFO, "Processing chunk of size %u", segment_info->chunk_size);
|
|
|
|
if (stream->frag_offset == 0) {
|
|
/* new packet */
|
|
stream->sequence = segment_info->sequence;
|
|
} else {
|
|
if (segment_info->sequence == stream->sequence &&
|
|
segment_info->frag_offset == stream->frag_offset) {
|
|
/* continuing packet */
|
|
} else {
|
|
/* cannot continue current packet: free it */
|
|
stream->frag_offset = 0;
|
|
if (segment_info->frag_offset != 0) {
|
|
/* cannot create new packet */
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
format = GST_FORMAT_TIME;
|
|
gst_pad_query (stream->pad, GST_PAD_QUERY_POSITION, &format, &next_ts);
|
|
|
|
if (GST_PAD_IS_CONNECTED (stream->pad)) {
|
|
GstBuffer *buf;
|
|
|
|
got_bytes = gst_bytestream_peek (bs, &buf, segment_info->chunk_size);
|
|
|
|
GST_BUFFER_TIMESTAMP (buf) = next_ts;
|
|
|
|
/*!!! Should handle flush events here? */
|
|
|
|
gst_pad_push (stream->pad, buf);
|
|
}
|
|
|
|
gst_bytestream_flush (bs, segment_info->chunk_size);
|
|
packet_info->size_left -= segment_info->chunk_size;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_handle_src_event (GstPad *pad, GstEvent *event)
|
|
{
|
|
GST_DEBUG (0,"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 GstPadQueryType*
|
|
gst_asf_demux_get_src_query_types (GstPad *pad) {
|
|
static const GstPadQueryType types[] = {
|
|
GST_PAD_QUERY_TOTAL,
|
|
GST_PAD_QUERY_POSITION,
|
|
0
|
|
};
|
|
|
|
return types;
|
|
}
|
|
|
|
static gboolean
|
|
gst_asf_demux_handle_src_query (GstPad *pad,
|
|
GstPadQueryType 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_PAD_QUERY_TOTAL:
|
|
switch (*format) {
|
|
case GST_FORMAT_DEFAULT:
|
|
*format = GST_FORMAT_TIME;
|
|
/* fall through */
|
|
case GST_FORMAT_TIME:
|
|
*value = (GST_SECOND / 1000) * asf_demux->play_time;
|
|
break;
|
|
default:
|
|
res = FALSE;
|
|
}
|
|
break;
|
|
case GST_PAD_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);
|
|
break;
|
|
case GST_STATE_PAUSED_TO_READY:
|
|
gst_bytestream_destroy (asf_demux->bs);
|
|
asf_demux->restart = TRUE;
|
|
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,
|
|
GstASFGuidHash *guids,
|
|
GstASFGuid *guid_raw)
|
|
{
|
|
guint32 i;
|
|
GstASFGuid 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;
|
|
}
|
|
|
|
static gboolean
|
|
plugin_init (GModule *module, GstPlugin *plugin)
|
|
{
|
|
GstElementFactory *factory;
|
|
GstTypeFactory *type;
|
|
gint i = 0;
|
|
|
|
/* this filter needs bytestream */
|
|
if (!gst_library_load ("gstbytestream")) {
|
|
gst_info("asfdemux: could not load support library: 'gstbytestream'\n");
|
|
return FALSE;
|
|
}
|
|
|
|
/* create an elementfactory for the asf_demux element */
|
|
factory = gst_element_factory_new ("asfdemux",GST_TYPE_ASF_DEMUX,
|
|
&gst_asf_demux_details);
|
|
|
|
while (asf_type_definitions[i].name) {
|
|
type = gst_type_factory_new (&asf_type_definitions[i]);
|
|
gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));
|
|
i++;
|
|
}
|
|
|
|
g_return_val_if_fail (factory != NULL, FALSE);
|
|
gst_element_factory_set_rank (factory, GST_ELEMENT_RANK_NONE);
|
|
|
|
gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (audio_factory));
|
|
gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (video_factory));
|
|
gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (sink_factory));
|
|
|
|
gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GstPluginDesc plugin_desc = {
|
|
GST_VERSION_MAJOR,
|
|
GST_VERSION_MINOR,
|
|
"asfdemux",
|
|
plugin_init
|
|
};
|