mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-02-05 14:02:26 +00:00
adding new quicktime parser:
Original commit message from CVS: adding new quicktime parser: - openquicktime free (hense gst/qtdemux) - no more seeks for parsing -> better for network streams - uses GstByteStream - less memcpy's - long ChangeLog record in pompous style
This commit is contained in:
parent
71d810adff
commit
56efeff3b4
6 changed files with 886 additions and 2 deletions
2
common
2
common
|
@ -1 +1 @@
|
|||
Subproject commit 745e216da016ee67bb498de56969c266ffa29d8c
|
||||
Subproject commit e5997d9e2b4e162ad423f9b9ec3ac9b29e12bb05
|
|
@ -174,7 +174,7 @@ GST_PLUGINS_ALL="\
|
|||
cutter deinterlace flx goom intfloat law level\
|
||||
median mpeg1enc mpeg1sys mpeg1videoparse mpeg2enc mpeg2sub\
|
||||
mpegaudio mpegaudioparse mpegstream mpegtypes modplug\
|
||||
monoscope passthrough playondemand rtjpeg silence sine\
|
||||
monoscope passthrough playondemand qtdemux rtjpeg silence sine\
|
||||
smooth spectrum speed stereo stereomono\
|
||||
synaesthesia udp videoscale volenv volume vumeter wavparse y4m"
|
||||
|
||||
|
@ -773,6 +773,7 @@ gst/modplug/libmodplug/Makefile
|
|||
gst/monoscope/Makefile
|
||||
gst/passthrough/Makefile
|
||||
gst/playondemand/Makefile
|
||||
gst/qtdemux/Makefile
|
||||
gst/rtjpeg/Makefile
|
||||
gst/silence/Makefile
|
||||
gst/sine/Makefile
|
||||
|
|
13
gst/qtdemux/Makefile.am
Normal file
13
gst/qtdemux/Makefile.am
Normal file
|
@ -0,0 +1,13 @@
|
|||
plugindir = $(libdir)/gst
|
||||
|
||||
plugin_LTLIBRARIES = libqtdemux.la
|
||||
|
||||
libqtdemux_la_CFLAGS = ${GST_CFLAGS}
|
||||
libqtdemux_la_LIBADD =
|
||||
libqtdemux_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS}
|
||||
|
||||
libqtdemux_la_SOURCES = qtdemux.c
|
||||
|
||||
noinst_HEADERS = qtdemux.h
|
||||
|
||||
EXTRA_DIST = README
|
0
gst/qtdemux/README
Normal file
0
gst/qtdemux/README
Normal file
702
gst/qtdemux/qtdemux.c
Normal file
702
gst/qtdemux/qtdemux.c
Normal file
|
@ -0,0 +1,702 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "qtdemux.h"
|
||||
|
||||
/* elementfactory information */
|
||||
static GstElementDetails
|
||||
gst_qtdemux_details =
|
||||
{
|
||||
"quicktime parser",
|
||||
"Parser/Video",
|
||||
"Parses a quicktime stream into audio and video substreams",
|
||||
VERSION,
|
||||
"A.Baguinski <artm@v2.nl>",
|
||||
"(C) 2002",
|
||||
};
|
||||
|
||||
static GstCaps* quicktime_type_find (GstBuffer *buf, gpointer private);
|
||||
|
||||
/* typefactory for 'quicktime' */
|
||||
static GstTypeDefinition quicktimedefinition = {
|
||||
"qtdemux_video/quicktime",
|
||||
"video/quicktime",
|
||||
".mov",
|
||||
quicktime_type_find,
|
||||
};
|
||||
|
||||
enum {
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
enum {
|
||||
ARG_0
|
||||
};
|
||||
|
||||
GST_PAD_TEMPLATE_FACTORY (sink_templ,
|
||||
"sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_CAPS_NEW (
|
||||
"qtdemux_sink",
|
||||
"video/quicktime",
|
||||
NULL
|
||||
)
|
||||
);
|
||||
|
||||
/*
|
||||
* so far i only support Photo Jpeg videos and no audio.
|
||||
* after this one works ok, i'll see what's next.
|
||||
*/
|
||||
GST_PAD_TEMPLATE_FACTORY (src_video_templ,
|
||||
"video_%02d",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_SOMETIMES,
|
||||
GST_CAPS_NEW (
|
||||
"qtdemux_src_video",
|
||||
"video/jpeg",
|
||||
"width", GST_PROPS_INT_RANGE (16, 4096),
|
||||
"height", GST_PROPS_INT_RANGE (16, 4096)
|
||||
)
|
||||
);
|
||||
|
||||
static GstElementClass *parent_class = NULL;
|
||||
/*
|
||||
* contains correspondence between atom types and
|
||||
* GstQtpAtomType structures
|
||||
*/
|
||||
static GHashTable * gst_qtp_type_registry;
|
||||
|
||||
typedef struct {
|
||||
guint32 type;
|
||||
GstQtpAtomType atype;
|
||||
} GstQtpTypePair;
|
||||
|
||||
static void gst_qtp_trak_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter);
|
||||
static void gst_qtp_tkhd_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter);
|
||||
static void gst_qtp_hdlr_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter);
|
||||
static void gst_qtp_stsd_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter);
|
||||
static void gst_qtp_stts_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter);
|
||||
static void gst_qtp_stsc_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter);
|
||||
static void gst_qtp_stsz_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter);
|
||||
static void gst_qtp_stco_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter);
|
||||
static void gst_qtp_mdhd_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter);
|
||||
static void gst_qtp_mdat_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter);
|
||||
|
||||
GstQtpTypePair gst_qtp_type_table[] = {
|
||||
{ GST_MAKE_FOURCC('m','o','o','v'), {GST_QTP_CONTAINER_ATOM,NULL} },
|
||||
{ GST_MAKE_FOURCC('t','r','a','k'), {GST_QTP_CONTAINER_ATOM,gst_qtp_trak_handler} },
|
||||
{ GST_MAKE_FOURCC('e','d','t','s'), {GST_QTP_CONTAINER_ATOM,NULL} },
|
||||
{ GST_MAKE_FOURCC('m','d','i','a'), {GST_QTP_CONTAINER_ATOM,NULL} },
|
||||
{ GST_MAKE_FOURCC('m','i','n','f'), {GST_QTP_CONTAINER_ATOM,NULL} },
|
||||
{ GST_MAKE_FOURCC('d','i','n','f'), {GST_QTP_CONTAINER_ATOM,NULL} },
|
||||
{ GST_MAKE_FOURCC('s','t','b','l'), {GST_QTP_CONTAINER_ATOM,NULL} },
|
||||
{ GST_MAKE_FOURCC('m','d','a','t'), {0,gst_qtp_mdat_handler} },
|
||||
{ GST_MAKE_FOURCC('m','v','h','d'), {0,NULL} },
|
||||
{ GST_MAKE_FOURCC('t','k','h','d'), {0,gst_qtp_tkhd_handler} },
|
||||
{ GST_MAKE_FOURCC('e','l','s','t'), {0,NULL} },
|
||||
{ GST_MAKE_FOURCC('m','d','h','d'), {0,gst_qtp_mdhd_handler} },
|
||||
{ GST_MAKE_FOURCC('h','d','l','r'), {0,gst_qtp_hdlr_handler} },
|
||||
{ GST_MAKE_FOURCC('v','m','h','d'), {0,NULL} },
|
||||
{ GST_MAKE_FOURCC('d','r','e','f'), {0,NULL} },
|
||||
{ GST_MAKE_FOURCC('s','t','t','s'), {0,gst_qtp_stts_handler} },
|
||||
{ GST_MAKE_FOURCC('s','t','s','d'), {0,gst_qtp_stsd_handler} },
|
||||
{ GST_MAKE_FOURCC('s','t','s','z'), {0,gst_qtp_stsz_handler} },
|
||||
{ GST_MAKE_FOURCC('s','t','s','c'), {0,gst_qtp_stsc_handler} },
|
||||
{ GST_MAKE_FOURCC('s','t','c','o'), {0,gst_qtp_stco_handler} }
|
||||
};
|
||||
|
||||
#define GST_QTP_TYPE_CNT sizeof(gst_qtp_type_table)/sizeof(GstQtpTypePair)
|
||||
|
||||
static void gst_qtdemux_class_init (GstQTDemuxClass *klass);
|
||||
static void gst_qtdemux_init (GstQTDemux *quicktime_demux);
|
||||
static void gst_qtdemux_loop (GstElement *element);
|
||||
static GstElementStateReturn gst_qtdemux_change_state (GstElement * element);
|
||||
static gint gst_guint32_compare(gconstpointer _a, gconstpointer _b);
|
||||
|
||||
static GType
|
||||
gst_qtdemux_get_type (void)
|
||||
{
|
||||
static GType qtdemux_type = 0;
|
||||
|
||||
if (!qtdemux_type) {
|
||||
static const GTypeInfo qtdemux_info = {
|
||||
sizeof(GstQTDemuxClass), NULL, NULL,
|
||||
(GClassInitFunc)gst_qtdemux_class_init,
|
||||
NULL, NULL, sizeof(GstQTDemux), 0,
|
||||
(GInstanceInitFunc)gst_qtdemux_init,
|
||||
};
|
||||
qtdemux_type = g_type_register_static (GST_TYPE_ELEMENT, "GstQTDemux", &qtdemux_info, 0);
|
||||
}
|
||||
return qtdemux_type;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qtdemux_class_init (GstQTDemuxClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstElementClass *gstelement_class;
|
||||
int i;
|
||||
|
||||
gobject_class = (GObjectClass*)klass;
|
||||
gstelement_class = (GstElementClass*)klass;
|
||||
|
||||
parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
|
||||
|
||||
gstelement_class->change_state = gst_qtdemux_change_state;
|
||||
|
||||
gst_qtp_type_registry = g_hash_table_new(g_int_hash,g_int_equal);
|
||||
for(i=0;i<GST_QTP_TYPE_CNT;i++) {
|
||||
g_hash_table_insert(gst_qtp_type_registry,&(gst_qtp_type_table[i].type),&(gst_qtp_type_table[i].atype));
|
||||
}
|
||||
}
|
||||
|
||||
static GstElementStateReturn
|
||||
gst_qtdemux_change_state (GstElement * element)
|
||||
{
|
||||
GstQTDemux * qtdemux = GST_QTDEMUX (element);
|
||||
|
||||
switch (GST_STATE_TRANSITION (element)) {
|
||||
case GST_STATE_READY_TO_PAUSED:
|
||||
qtdemux->bs = gst_bytestream_new (qtdemux->sinkpad);
|
||||
break;
|
||||
case GST_STATE_PAUSED_TO_READY:
|
||||
gst_bytestream_destroy (qtdemux->bs);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
parent_class->change_state (element);
|
||||
return GST_STATE_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qtdemux_init (GstQTDemux *qtdemux)
|
||||
{
|
||||
guint i;
|
||||
|
||||
qtdemux->sinkpad = gst_pad_new_from_template (GST_PAD_TEMPLATE_GET (sink_templ), "sink");
|
||||
gst_element_set_loop_function (GST_ELEMENT (qtdemux), gst_qtdemux_loop);
|
||||
gst_element_add_pad (GST_ELEMENT (qtdemux), qtdemux->sinkpad);
|
||||
|
||||
for (i=0; i<GST_QTDEMUX_MAX_VIDEO_PADS; i++)
|
||||
qtdemux->video_pad[i] = NULL;
|
||||
qtdemux->num_video_pads = 0;
|
||||
|
||||
qtdemux->bs_pos = 0;
|
||||
qtdemux->nested = NULL;
|
||||
qtdemux->nested_cnt = 0;
|
||||
qtdemux->tracks = NULL;
|
||||
qtdemux->samples = NULL;
|
||||
}
|
||||
|
||||
static GstCaps*
|
||||
quicktime_type_find (GstBuffer *buf,
|
||||
gpointer private)
|
||||
{
|
||||
gchar *data = GST_BUFFER_DATA (buf);
|
||||
|
||||
/* exactly like in the old version */
|
||||
if (!strncmp (&data[4], "wide", 4) ||
|
||||
!strncmp (&data[4], "moov", 4) ||
|
||||
!strncmp (&data[4], "mdat", 4)) {
|
||||
return gst_caps_new ("quicktime_type_find",
|
||||
"video/quicktime",
|
||||
NULL);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_init (GModule *module, GstPlugin *plugin)
|
||||
{
|
||||
GstElementFactory *factory;
|
||||
GstTypeFactory *type;
|
||||
|
||||
if (!gst_library_load("gstbytestream")) {
|
||||
gst_info("qtdemux: could not load support library 'gstbytestream'\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
factory = gst_element_factory_new("qtdemux",GST_TYPE_QTDEMUX,
|
||||
&gst_qtdemux_details);
|
||||
g_return_val_if_fail(factory != NULL, FALSE);
|
||||
|
||||
gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (sink_templ));
|
||||
gst_element_factory_add_pad_template (factory, GST_PAD_TEMPLATE_GET (src_video_templ));
|
||||
|
||||
type = gst_type_factory_new (&quicktimedefinition);
|
||||
gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (type));
|
||||
|
||||
gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GstPluginDesc plugin_desc = {
|
||||
GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
"qtdemux",
|
||||
plugin_init
|
||||
};
|
||||
|
||||
static gboolean
|
||||
gst_qtdemux_handle_event (GstQTDemux * qtdemux)
|
||||
{
|
||||
guint32 remaining;
|
||||
GstEvent * event;
|
||||
GstEventType type;
|
||||
|
||||
gst_bytestream_get_status (qtdemux->bs,&remaining,&event);
|
||||
type = event? GST_EVENT_TYPE (event) : GST_EVENT_UNKNOWN;
|
||||
|
||||
switch (type) {
|
||||
case GST_EVENT_EOS:
|
||||
gst_pad_event_default (qtdemux->sinkpad,event);
|
||||
break;
|
||||
case GST_EVENT_DISCONTINUOUS:
|
||||
gst_bytestream_flush_fast (qtdemux->bs, remaining);
|
||||
default:
|
||||
gst_pad_event_default (qtdemux->sinkpad,event);
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean gst_qtp_read_bytes_atom_head(GstQTDemux * qtdemux,GstQtpAtom * atom);
|
||||
static gboolean gst_qtp_skip(GstQTDemux * qtdemux, guint64 skip);
|
||||
static gboolean gst_qtp_skip_atom(GstQTDemux * qtdemux, GstQtpAtom * atom);
|
||||
static gboolean gst_qtp_skip_container(GstQTDemux * qtdemux, guint32 type);
|
||||
|
||||
/* new track emerges here */
|
||||
static GstQtpTrack * track_to_be = NULL;
|
||||
|
||||
/*
|
||||
* - gst_qtp_* functions together with gst_qtdemux_loop implement quicktime
|
||||
* parser.
|
||||
*/
|
||||
|
||||
static void
|
||||
gst_qtdemux_loop (GstElement *element)
|
||||
{
|
||||
GstQTDemux * qtdemux = GST_QTDEMUX (element);
|
||||
GstQtpAtom atom;
|
||||
GstQtpAtomType * atom_type;
|
||||
|
||||
/* ain't we out of the current container? */
|
||||
if (qtdemux->nested) {
|
||||
GstQtpAtom * current = (GstQtpAtom *) qtdemux->nested->data;
|
||||
while (current && (current->size!=0) && (current->start+current->size <= qtdemux->bs_pos)) {
|
||||
/* indeed we are! */
|
||||
qtdemux->nested = qtdemux->nested->next;
|
||||
qtdemux->nested_cnt--;
|
||||
/* if atom type has a handler call it with enter=FALSE (i.e. leave) */
|
||||
atom_type = g_hash_table_lookup (gst_qtp_type_registry,&(current->type));
|
||||
if (atom_type && atom_type->handler)
|
||||
atom_type->handler(qtdemux,current,FALSE);
|
||||
free(current);
|
||||
current = qtdemux->nested?(GstQtpAtom *) qtdemux->nested->data:NULL;
|
||||
}
|
||||
}
|
||||
|
||||
gst_qtp_read_bytes_atom_head(qtdemux,&atom);
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtdemux_loop: atom(%c%c%c%c,%llu,%llu)\n",GST_FOURCC_TO_CHARSEQ(atom.type),atom.start,atom.size);
|
||||
|
||||
atom_type = g_hash_table_lookup (gst_qtp_type_registry,&atom.type);
|
||||
if (!atom_type) {
|
||||
gst_qtp_skip_atom(qtdemux,&atom);
|
||||
return;
|
||||
}
|
||||
|
||||
if (atom_type->flags & GST_QTP_CONTAINER_ATOM) {
|
||||
GstQtpAtom * new_atom;
|
||||
new_atom = malloc(sizeof(GstQtpAtom));
|
||||
memcpy(new_atom,&atom,sizeof(GstQtpAtom));
|
||||
qtdemux->nested_cnt++;
|
||||
qtdemux->nested = g_slist_prepend (qtdemux->nested, new_atom);
|
||||
if (atom_type->handler)
|
||||
atom_type->handler(qtdemux,&atom,TRUE);
|
||||
} else {
|
||||
/* leaf atom */
|
||||
if (atom_type->handler)
|
||||
atom_type->handler(qtdemux,&atom,TRUE);
|
||||
/*
|
||||
* if there wasn't a handler - we skip the whole atom
|
||||
* if there was - ensure that next thing read will be after the atom
|
||||
* (handler isn't obligated to read anything)
|
||||
*/
|
||||
gst_qtp_skip_atom(qtdemux,&atom);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* peeks an atom header,
|
||||
* advances qtdemux->bs_pos (cause bytestream won't tell)
|
||||
* flushes bytestream
|
||||
*/
|
||||
static gboolean
|
||||
gst_qtp_read_bytes_atom_head(GstQTDemux * qtdemux,GstQtpAtom * atom)
|
||||
{
|
||||
GstByteStream * bs = qtdemux->bs;
|
||||
GstQtpAtomMinHeader * amh = NULL;
|
||||
guint64 * esize=NULL;
|
||||
|
||||
/* FIXME this can't be right, rewrite with _read */
|
||||
do { /* do ... while (event()) is necessary for bytestream events */
|
||||
if (!amh) {
|
||||
if ((amh = (GstQtpAtomMinHeader*) gst_bytestream_peek_bytes (bs, 8))) {
|
||||
atom->size = GUINT32_FROM_BE(amh->size);
|
||||
atom->type = amh->type; /* don't need to turn this around magicly FIXME this can depend on endiannes */
|
||||
atom->start = qtdemux->bs_pos;
|
||||
gst_bytestream_flush (bs, 8);
|
||||
qtdemux->bs_pos += 8;
|
||||
}
|
||||
}
|
||||
if (amh) {
|
||||
if (atom->size == 1) { /* need to peek extended size field */
|
||||
if ((esize = (guint64*) gst_bytestream_peek_bytes (bs, 8))) {
|
||||
atom->size = GUINT64_FROM_BE(*esize);
|
||||
gst_bytestream_flush (bs, 8);
|
||||
qtdemux->bs_pos += 8;
|
||||
return TRUE;
|
||||
}
|
||||
} else {
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
} while (gst_qtdemux_handle_event (qtdemux));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qtp_read_bytes(GstQTDemux * qtdemux, void * buffer, size_t size)
|
||||
{
|
||||
void * data;
|
||||
GstByteStream * bs = qtdemux->bs;
|
||||
do {
|
||||
if ((data = gst_bytestream_peek_bytes (bs,size))) {
|
||||
memcpy(buffer,data,size);
|
||||
gst_bytestream_flush(bs,size);
|
||||
qtdemux->bs_pos += size;
|
||||
return;
|
||||
}
|
||||
} while (gst_qtdemux_handle_event (qtdemux));
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_qtp_read(GstQTDemux * qtdemux, size_t size)
|
||||
{
|
||||
GstBuffer * buf;
|
||||
GstByteStream * bs = qtdemux->bs;
|
||||
do {
|
||||
if ((buf = gst_bytestream_read (bs,size))) {
|
||||
qtdemux->bs_pos += size;
|
||||
return buf;
|
||||
}
|
||||
} while (gst_qtdemux_handle_event (qtdemux));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* skips some input (e.g. to ignore unknown atom)
|
||||
*/
|
||||
static gboolean
|
||||
gst_qtp_skip(GstQTDemux * qtdemux, guint64 skip)
|
||||
{
|
||||
GstByteStream * bs = qtdemux->bs;
|
||||
|
||||
if (skip) {
|
||||
gst_bytestream_flush(bs,skip);
|
||||
qtdemux->bs_pos += skip;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* convenience function for skipping the given atom */
|
||||
static gboolean
|
||||
gst_qtp_skip_atom(GstQTDemux * qtdemux, GstQtpAtom * atom)
|
||||
{
|
||||
if (qtdemux->bs_pos < atom->start + atom->size) {
|
||||
guint64 skip = atom->start + atom->size - qtdemux->bs_pos;
|
||||
return gst_qtp_skip(qtdemux,skip);
|
||||
} else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* skips the container with type 'type' if finds it in the nesting stack */
|
||||
static gboolean
|
||||
gst_qtp_skip_container(GstQTDemux * qtdemux, guint32 type)
|
||||
{
|
||||
GSList * iter = qtdemux->nested;
|
||||
|
||||
while (iter && ((GstQtpAtom*)(iter->data))->type != type)
|
||||
iter = iter->next;
|
||||
|
||||
if (iter)
|
||||
return gst_qtp_skip_atom(qtdemux,(GstQtpAtom*)iter->data);
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gint
|
||||
gst_guint32_compare(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
if ((guint32*)a < (guint32*)b)
|
||||
return -1;
|
||||
else if ((guint32*)a > (guint32*)b)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qtp_trak_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter)
|
||||
{
|
||||
if (enter) { /* enter trak */
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_trak_handler: enter\n");
|
||||
track_to_be = malloc(sizeof(GstQtpTrack));
|
||||
track_to_be->stsd = NULL;
|
||||
track_to_be->stts = NULL;
|
||||
track_to_be->stsc = NULL;
|
||||
track_to_be->stsz = NULL;
|
||||
track_to_be->stco = NULL;
|
||||
track_to_be->samples = NULL;
|
||||
track_to_be->pad = NULL;
|
||||
} else { /* leave trak */
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_trak_handler: leave\n");
|
||||
if (track_to_be) { /* if we didnt discard this track earlier */
|
||||
GstQtpStscRec * stsc;
|
||||
guint32 * stsz, * stco, offset;
|
||||
int chunk,sample,nchunks,nsamples,stsc_idx,nstsc;
|
||||
GstCaps * newcaps = NULL;
|
||||
|
||||
/* process sample tables */
|
||||
|
||||
/*
|
||||
* FIXME have to check which sample tables are present and which are not
|
||||
* and skip the track if there's not enough tables or set default values
|
||||
* if some optional tables are missing
|
||||
*/
|
||||
|
||||
/*
|
||||
* FIXME i assume that there's only one of each stsd record and stts
|
||||
* record in the tables that's not always true, this must be changed
|
||||
* later, as soon as i encounter qt file with bigger tables.
|
||||
*/
|
||||
track_to_be->format = ((GstQtpStsdRec*)GST_BUFFER_DATA(track_to_be->stsd))->format;
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_trak_handler: format: %c%c%c%c\n",GST_FOURCC_TO_CHARSEQ(track_to_be->format));
|
||||
track_to_be->sample_duration = GUINT32_FROM_BE(((GstQtpSttsRec*)GST_BUFFER_DATA(track_to_be->stts))->duration);
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_trak_handler: sample duration: %d\n",track_to_be->sample_duration);
|
||||
/*
|
||||
* depending on format we can decide to refuse this track all together
|
||||
* if we don't know what for format that it.
|
||||
*/
|
||||
switch (track_to_be->format) {
|
||||
case GST_MAKE_FOURCC('j','p','e','g'):
|
||||
track_to_be->pad = gst_pad_new_from_template(
|
||||
GST_PAD_TEMPLATE_GET(src_video_templ),
|
||||
g_strdup_printf("video_%02d",qtdemux->num_video_pads++));
|
||||
newcaps = GST_CAPS_NEW(
|
||||
"qtdemux_video_src",
|
||||
"video/jpeg",
|
||||
"width", GST_PROPS_INT(track_to_be->width),
|
||||
"height", GST_PROPS_INT(track_to_be->height));
|
||||
gst_pad_try_set_caps(track_to_be->pad,newcaps);
|
||||
gst_element_add_pad(GST_ELEMENT(qtdemux),track_to_be->pad);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* now let's find all about individual samples and put them into samples
|
||||
* tree
|
||||
*/
|
||||
if (!qtdemux->samples) {
|
||||
qtdemux->samples = g_tree_new(gst_guint32_compare);
|
||||
}
|
||||
stsc = (GstQtpStscRec*)GST_BUFFER_DATA(track_to_be->stsc);
|
||||
stsz = (guint32*)GST_BUFFER_DATA(track_to_be->stsz);
|
||||
stco = (guint32*)GST_BUFFER_DATA(track_to_be->stco);
|
||||
nchunks = GST_BUFFER_SIZE(track_to_be->stco)/sizeof(guint32);
|
||||
nsamples = GST_BUFFER_SIZE(track_to_be->stsz)/sizeof(guint32);
|
||||
nstsc = GST_BUFFER_SIZE(track_to_be->stsc)/sizeof(GstQtpStscRec);
|
||||
|
||||
track_to_be->samples = malloc(nsamples*sizeof(GstQtpSample));
|
||||
for(chunk=0,sample=0,stsc_idx=0;
|
||||
chunk<nchunks;
|
||||
chunk++) {
|
||||
int i;
|
||||
offset = GUINT32_FROM_BE(stco[chunk]);
|
||||
if (stsc_idx+1<nstsc && chunk+1==GUINT32_FROM_BE(stsc[stsc_idx+1].first_chunk)) {
|
||||
stsc_idx++;
|
||||
}
|
||||
for(i=0;i<GUINT32_FROM_BE(stsc[stsc_idx].samples_per_chunk);i++,sample++) {
|
||||
guint32 size = GUINT32_FROM_BE(stsz[sample]);
|
||||
track_to_be->samples[sample].offset = offset;
|
||||
track_to_be->samples[sample].size = size;
|
||||
track_to_be->samples[sample].timestamp = sample*((1000000*track_to_be->sample_duration)/track_to_be->time_scale);
|
||||
track_to_be->samples[sample].track = track_to_be;
|
||||
g_tree_insert(qtdemux->samples,&(track_to_be->samples[sample].offset),&(track_to_be->samples[sample]));
|
||||
offset += size;
|
||||
}
|
||||
}
|
||||
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_trak_handler: trak added to the list\n");
|
||||
qtdemux->tracks = g_list_prepend(qtdemux->tracks,track_to_be);
|
||||
|
||||
gst_buffer_unref(track_to_be->stsd);
|
||||
gst_buffer_unref(track_to_be->stts);
|
||||
gst_buffer_unref(track_to_be->stsc);
|
||||
gst_buffer_unref(track_to_be->stsz);
|
||||
gst_buffer_unref(track_to_be->stco);
|
||||
track_to_be = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* weird formats they apple guys are using
|
||||
* weird conversion copied from openquicktime
|
||||
* FIXME either it can be done more beautiful/fast way or this fixme has to go
|
||||
*/
|
||||
static float
|
||||
fixed32_to_float(guint32 fixed)
|
||||
{
|
||||
unsigned char * data = (unsigned char*)&fixed;
|
||||
guint32 a, b, c, d;
|
||||
a = data[0];
|
||||
b = data[1];
|
||||
c = data[2];
|
||||
d = data[3];
|
||||
a = (a << 8) + b;
|
||||
b = (c << 8) + d;
|
||||
return (float)a + (float)b / 65536;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qtp_tkhd_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter)
|
||||
{
|
||||
guint32 wh[2];
|
||||
/* if we get here track_to_be must be not NULL */
|
||||
g_assert(track_to_be);
|
||||
|
||||
gst_qtp_skip(qtdemux,76); /* don't need those values */
|
||||
gst_qtp_read_bytes(qtdemux,wh,8);
|
||||
track_to_be->width = (guint32) fixed32_to_float(wh[0]);
|
||||
track_to_be->height = (guint32) fixed32_to_float(wh[1]);
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_tkhd_handler: track dimmensions: %dx%d\n",track_to_be->width,track_to_be->height);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qtp_hdlr_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter)
|
||||
{
|
||||
guint32 a[3];
|
||||
|
||||
gst_qtp_read_bytes(qtdemux,a,12);
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_hdlr_handler: %c%c%c%c %c%c%c%c\n",GST_FOURCC_TO_CHARSEQ(a[1]),GST_FOURCC_TO_CHARSEQ(a[2]));
|
||||
if (a[1]==GST_MAKE_FOURCC('m','h','l','r') && a[2]!=GST_MAKE_FOURCC('v','i','d','e')) {
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_hdlr_handler: rejecting the track\n");
|
||||
/* forget about this track! */
|
||||
free(track_to_be);
|
||||
track_to_be = NULL;
|
||||
gst_qtp_skip_container(qtdemux,GST_MAKE_FOURCC('t','r','a','k'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qtp_stsd_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter)
|
||||
{
|
||||
guint32 a[2];
|
||||
gst_qtp_read_bytes(qtdemux,a,8);
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_stsd_handler: %d entries in the table\n",GUINT32_FROM_BE(a[1]));
|
||||
/* just put the rest of the atom into sample description table */
|
||||
track_to_be->stsd = gst_qtp_read(qtdemux,atom->start + atom->size - qtdemux->bs_pos);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qtp_stts_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter)
|
||||
{
|
||||
guint32 a[2];
|
||||
gst_qtp_read_bytes(qtdemux,a,8);
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_stts_handler: %d entries in the table\n",GUINT32_FROM_BE(a[1]));
|
||||
track_to_be->stts = gst_qtp_read(qtdemux,GUINT32_FROM_BE(a[1])*sizeof(GstQtpSttsRec));
|
||||
}
|
||||
static void
|
||||
gst_qtp_stsc_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter)
|
||||
{
|
||||
guint32 a[2];
|
||||
gst_qtp_read_bytes(qtdemux,a,8);
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_stsc_handler: %d entries in the table\n",GUINT32_FROM_BE(a[1]));
|
||||
track_to_be->stsc = gst_qtp_read(qtdemux,GUINT32_FROM_BE(a[1])*sizeof(GstQtpStscRec));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qtp_stsz_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter)
|
||||
{
|
||||
guint32 a[3];
|
||||
gst_qtp_read_bytes(qtdemux,a,12);
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_stsz_handler: %d entries in the table\n",GUINT32_FROM_BE(a[2]));
|
||||
/* FIXME have to chech a[2], it contains size if all samples if they are the same size */
|
||||
track_to_be->stsz = gst_qtp_read(qtdemux,GUINT32_FROM_BE(a[2])*sizeof(guint32));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qtp_stco_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter)
|
||||
{
|
||||
guint32 a[2];
|
||||
gst_qtp_read_bytes(qtdemux,a,8);
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_stco_handler: %d entries in the table\n",GUINT32_FROM_BE(a[1]));
|
||||
track_to_be->stco = gst_qtp_read(qtdemux,GUINT32_FROM_BE(a[1])*sizeof(guint32));
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qtp_mdhd_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter)
|
||||
{
|
||||
guint32 a[4];
|
||||
gst_qtp_read_bytes(qtdemux,a,16);
|
||||
track_to_be->time_scale = GUINT32_FROM_BE(a[3]);
|
||||
GST_INFO (GST_CAT_PLUGIN_INFO,"gst_qtp_mdhd_handler: time scale: %d\n",track_to_be->time_scale);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_qtp_traverse(gpointer poffs,gpointer value,gpointer data)
|
||||
{
|
||||
GstQtpSample * sample = (GstQtpSample*)value;
|
||||
GstQTDemux * qtdemux = (GstQTDemux*)data;
|
||||
|
||||
if (qtdemux->bs_pos < sample->offset) {
|
||||
gst_qtp_skip(qtdemux,sample->offset - qtdemux->bs_pos);
|
||||
if (sample->track->pad && GST_PAD_IS_CONNECTED(sample->track->pad)) {
|
||||
GstBuffer * buf;
|
||||
buf = gst_qtp_read(qtdemux,sample->size);
|
||||
GST_BUFFER_TIMESTAMP(buf) = sample->timestamp;
|
||||
gst_pad_push(sample->track->pad,buf);
|
||||
}
|
||||
}
|
||||
return FALSE; /* == keep going (TRUE to stop) */
|
||||
}
|
||||
|
||||
static void
|
||||
gst_qtp_mdat_handler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter)
|
||||
{
|
||||
/* actually playing */
|
||||
g_tree_foreach(qtdemux->samples,gst_qtp_traverse,qtdemux);
|
||||
}
|
168
gst/qtdemux/qtdemux.h
Normal file
168
gst/qtdemux/qtdemux.h
Normal file
|
@ -0,0 +1,168 @@
|
|||
/* 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.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GST_QTDEMUX_H__
|
||||
#define __GST_QTDEMUX_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/bytestream/bytestream.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif /* __cplusplus */
|
||||
|
||||
|
||||
#define GST_TYPE_QTDEMUX \
|
||||
(gst_qtdemux_get_type())
|
||||
#define GST_QTDEMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_QTDEMUX,GstQTDemux))
|
||||
#define GST_QTDEMUX_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_QTDEMUX,GstQTDemux))
|
||||
#define GST_IS_QTDEMUX(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_QTDEMUX))
|
||||
#define GST_IS_QTDEMUX_CLASS(obj) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_QTDEMUX))
|
||||
|
||||
#define GST_QTDEMUX_MAX_AUDIO_PADS 8
|
||||
#define GST_QTDEMUX_MAX_VIDEO_PADS 8
|
||||
|
||||
/*
|
||||
* smartass macross that turns guint32 into sequence of bytes separated with comma
|
||||
* to be used in printf("%c%c%c%c",GST_FOURCC_TO_CHARSEQ(fourcc)) fashion
|
||||
*/
|
||||
#define GST_FOURCC_TO_CHARSEQ(f) f&0xff, (f>>8)&0xff , (f>>16)&0xff, f>>24
|
||||
|
||||
typedef struct _GstQTDemux GstQTDemux;
|
||||
typedef struct _GstQTDemuxClass GstQTDemuxClass;
|
||||
|
||||
typedef struct {
|
||||
guint64 start;
|
||||
guint64 size; /* if 0, lasts till the end of file */
|
||||
guint32 type;
|
||||
} GstQtpAtom;
|
||||
|
||||
typedef struct {
|
||||
guint32 size;
|
||||
guint32 type;
|
||||
} GstQtpAtomMinHeader;
|
||||
|
||||
#define GST_QTP_CONTAINER_ATOM 1
|
||||
|
||||
typedef void GstQtpAtomTypeHandler (GstQTDemux * qtdemux,GstQtpAtom * atom,gboolean enter);
|
||||
|
||||
typedef struct {
|
||||
guint32 flags;
|
||||
GstQtpAtomTypeHandler * handler;
|
||||
} GstQtpAtomType;
|
||||
|
||||
typedef struct {
|
||||
guint64 offset;
|
||||
guint32 size;
|
||||
guint32 timestamp;
|
||||
struct _GstQtpTrack * track;
|
||||
} GstQtpSample;
|
||||
|
||||
typedef struct _GstQtpTrack {
|
||||
guint32 format;
|
||||
guint32 width;
|
||||
guint32 height;
|
||||
guint32 time_scale; /* units per second */
|
||||
guint32 sample_duration; /* units in sample */
|
||||
|
||||
/* temporary buffers with sample tables */
|
||||
GstBuffer * stsd, * stts, * stsc, * stsz, * stco;
|
||||
|
||||
/* this track samples in array */
|
||||
GstQtpSample * samples;
|
||||
|
||||
GstPad * pad;
|
||||
} GstQtpTrack;
|
||||
|
||||
typedef struct {
|
||||
guint32 size;
|
||||
guint32 format;
|
||||
char reserved[6];
|
||||
guint16 dataref;
|
||||
} __attribute__((packed)) /* FIXME may it wasn't necessary? */ GstQtpStsdRec;
|
||||
|
||||
typedef struct {
|
||||
guint32 size;
|
||||
guint32 format;
|
||||
char reserved[6];
|
||||
guint16 dataref;
|
||||
guint16 version;
|
||||
guint16 rev_level;
|
||||
guint32 vendor;
|
||||
guint32 temporal_quality;
|
||||
guint32 spatial_quality;
|
||||
guint16 width;
|
||||
guint16 height;
|
||||
guint32 hres;
|
||||
guint32 vres;
|
||||
guint32 data_size;
|
||||
guint16 frame_count; /* frames per sample */
|
||||
guint32 compressor_name;
|
||||
guint16 depth;
|
||||
guint16 color_table_id;
|
||||
} __attribute__((packed)) /* FIXME may it wasn't necessary? */ GstQtpStsdVideRec;
|
||||
|
||||
typedef struct {
|
||||
guint32 count;
|
||||
guint32 duration;
|
||||
} GstQtpSttsRec;
|
||||
|
||||
typedef struct {
|
||||
guint32 first_chunk;
|
||||
guint32 samples_per_chunk;
|
||||
guint32 sample_desc;
|
||||
} GstQtpStscRec;
|
||||
|
||||
struct _GstQTDemux {
|
||||
GstElement element;
|
||||
|
||||
/* pads */
|
||||
GstPad *sinkpad;
|
||||
GstPad *video_pad[GST_QTDEMUX_MAX_VIDEO_PADS];
|
||||
int num_video_pads;
|
||||
|
||||
GstByteStream *bs;
|
||||
guint64 bs_pos; /* current position in bs (coz bs won't tell) */
|
||||
|
||||
/*
|
||||
* nesting stack: everytime parser reads a header
|
||||
* of a container atom it is added to the stack until
|
||||
* and removed whenever it's over (read completely)
|
||||
*/
|
||||
GSList * nested;
|
||||
int nested_cnt; /* just to make it simpler */
|
||||
|
||||
GList * tracks;
|
||||
GTree * samples; /* samples of all the tracks ordered by the offset */
|
||||
};
|
||||
|
||||
struct _GstQTDemuxClass {
|
||||
GstElementClass parent_class;
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
||||
#endif /* __GST_QTDEMUX_H__ */
|
Loading…
Reference in a new issue