mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-27 12:11:13 +00:00
Port cdxaparse, makes VCD playback work.
Original commit message from CVS: * configure.ac: * gst/cdxaparse/Makefile.am: * gst/cdxaparse/gstcdxaparse.c: * gst/cdxaparse/gstcdxaparse.h: Port cdxaparse, makes VCD playback work.
This commit is contained in:
parent
f6c865ba05
commit
1ca7ef34c2
5 changed files with 479 additions and 214 deletions
|
@ -1,3 +1,11 @@
|
||||||
|
2006-02-10 Tim-Philipp Müller <tim at centricular dot net>
|
||||||
|
|
||||||
|
* configure.ac:
|
||||||
|
* gst/cdxaparse/Makefile.am:
|
||||||
|
* gst/cdxaparse/gstcdxaparse.c:
|
||||||
|
* gst/cdxaparse/gstcdxaparse.h:
|
||||||
|
Port cdxaparse, makes VCD playback work.
|
||||||
|
|
||||||
2006-02-07 Thomas Vander Stichele <thomas at apestaart dot org>
|
2006-02-07 Thomas Vander Stichele <thomas at apestaart dot org>
|
||||||
|
|
||||||
* gst/qtdemux/qtdemux.c: (gst_qtdemux_loop_header),
|
* gst/qtdemux/qtdemux.c: (gst_qtdemux_loop_header),
|
||||||
|
|
|
@ -280,6 +280,7 @@ AC_SUBST(GST_PLUGIN_LDFLAGS)
|
||||||
|
|
||||||
dnl these are all the gst plug-ins, compilable without additional libs
|
dnl these are all the gst plug-ins, compilable without additional libs
|
||||||
GST_PLUGINS_ALL="\
|
GST_PLUGINS_ALL="\
|
||||||
|
cdxaparse \
|
||||||
speed \
|
speed \
|
||||||
qtdemux \
|
qtdemux \
|
||||||
tta \
|
tta \
|
||||||
|
@ -651,6 +652,7 @@ AC_CONFIG_FILES(
|
||||||
Makefile
|
Makefile
|
||||||
gst-plugins-bad.spec
|
gst-plugins-bad.spec
|
||||||
gst/Makefile
|
gst/Makefile
|
||||||
|
gst/cdxaparse/Makefile
|
||||||
gst/speed/Makefile
|
gst/speed/Makefile
|
||||||
gst/qtdemux/Makefile
|
gst/qtdemux/Makefile
|
||||||
gst/tta/Makefile
|
gst/tta/Makefile
|
||||||
|
|
|
@ -1,13 +1,22 @@
|
||||||
plugin_LTLIBRARIES = libgstcdxaparse.la
|
plugin_LTLIBRARIES = libgstcdxaparse.la
|
||||||
|
|
||||||
|
# gstcdxastrip.c
|
||||||
|
# gstcdxastrip.h
|
||||||
|
|
||||||
libgstcdxaparse_la_SOURCES = \
|
libgstcdxaparse_la_SOURCES = \
|
||||||
gstcdxaparse.c \
|
gstcdxaparse.c
|
||||||
gstcdxastrip.c
|
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
gstcdxaparse.h \
|
gstcdxaparse.h
|
||||||
gstcdxastrip.h
|
|
||||||
|
libgstcdxaparse_la_CFLAGS = \
|
||||||
|
$(GST_CFLAGS) \
|
||||||
|
$(GST_PLUGINS_BASE_CFLAGS)
|
||||||
|
|
||||||
|
libgstcdxaparse_la_LIBADD = \
|
||||||
|
$(GST_LIBS) \
|
||||||
|
$(GST_PLUGINS_BASE_LIBS) \
|
||||||
|
-lgstriff-@GST_MAJORMINOR@
|
||||||
|
|
||||||
libgstcdxaparse_la_CFLAGS = $(GST_CFLAGS)
|
|
||||||
libgstcdxaparse_la_LIBADD =
|
|
||||||
libgstcdxaparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
libgstcdxaparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/* GStreamer
|
/* GStreamer
|
||||||
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
* <2002> Wim Taymans <wim.taymans@chello.be>
|
* <2002> Wim Taymans <wim.taymans@chello.be>
|
||||||
|
* <2006> Tim-Philipp Müller <tim centricular net>
|
||||||
*
|
*
|
||||||
* This library is free software; you can redistribute it and/or
|
* This library is free software; you can redistribute it and/or
|
||||||
* modify it under the terms of the GNU Library General Public
|
* modify it under the terms of the GNU Library General Public
|
||||||
|
@ -24,17 +25,22 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "gstcdxaparse.h"
|
#include "gstcdxaparse.h"
|
||||||
#include "gstcdxastrip.h"
|
/* #include "gstcdxastrip.h" */
|
||||||
#include "gst/riff/riff-ids.h"
|
#include <gst/riff/riff-ids.h>
|
||||||
#include "gst/riff/riff-media.h"
|
#include <gst/riff/riff-read.h>
|
||||||
|
|
||||||
static void gst_cdxaparse_base_init (gpointer g_class);
|
GST_DEBUG_CATEGORY_STATIC (cdxaparse_debug);
|
||||||
static void gst_cdxaparse_class_init (GstCDXAParseClass * klass);
|
#define GST_CAT_DEFAULT cdxaparse_debug
|
||||||
static void gst_cdxaparse_init (GstCDXAParse * cdxaparse);
|
|
||||||
|
|
||||||
static void gst_cdxaparse_loop (GstElement * element);
|
static gboolean gst_cdxa_parse_sink_activate (GstPad * sinkpad);
|
||||||
static GstStateChangeReturn gst_cdxaparse_change_state (GstElement * element,
|
static void gst_cdxa_parse_loop (GstPad * sinkpad);
|
||||||
|
static gboolean gst_cdxa_parse_sink_activate_pull (GstPad * sinkpad,
|
||||||
|
gboolean active);
|
||||||
|
static gboolean gst_cdxa_parse_sink_activate (GstPad * sinkpad);
|
||||||
|
static GstStateChangeReturn gst_cdxa_parse_change_state (GstElement * element,
|
||||||
GstStateChange transition);
|
GstStateChange transition);
|
||||||
|
static gboolean gst_cdxa_parse_src_event (GstPad * srcpad, GstEvent * event);
|
||||||
|
static gboolean gst_cdxa_parse_src_query (GstPad * srcpad, GstQuery * query);
|
||||||
|
|
||||||
static GstStaticPadTemplate sink_template_factory =
|
static GstStaticPadTemplate sink_template_factory =
|
||||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||||
|
@ -50,46 +56,18 @@ GST_STATIC_PAD_TEMPLATE ("src",
|
||||||
GST_STATIC_CAPS ("video/mpeg, " "systemstream = (boolean) TRUE")
|
GST_STATIC_CAPS ("video/mpeg, " "systemstream = (boolean) TRUE")
|
||||||
);
|
);
|
||||||
|
|
||||||
static GstRiffReadClass *parent_class = NULL;
|
GST_BOILERPLATE (GstCDXAParse, gst_cdxa_parse, GstElement, GST_TYPE_ELEMENT)
|
||||||
|
|
||||||
GType
|
static void gst_cdxa_parse_base_init (gpointer g_class)
|
||||||
gst_cdxaparse_get_type (void)
|
|
||||||
{
|
|
||||||
static GType cdxaparse_type = 0;
|
|
||||||
|
|
||||||
if (!cdxaparse_type) {
|
|
||||||
static const GTypeInfo cdxaparse_info = {
|
|
||||||
sizeof (GstCDXAParseClass),
|
|
||||||
gst_cdxaparse_base_init,
|
|
||||||
NULL,
|
|
||||||
(GClassInitFunc) gst_cdxaparse_class_init,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
sizeof (GstCDXAParse),
|
|
||||||
0,
|
|
||||||
(GInstanceInitFunc) gst_cdxaparse_init,
|
|
||||||
};
|
|
||||||
|
|
||||||
cdxaparse_type =
|
|
||||||
g_type_register_static (GST_TYPE_RIFF_READ, "GstCDXAParse",
|
|
||||||
&cdxaparse_info, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return cdxaparse_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
gst_cdxaparse_base_init (gpointer g_class)
|
|
||||||
{
|
{
|
||||||
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
|
||||||
static GstElementDetails gst_cdxaparse_details =
|
static GstElementDetails gst_cdxa_parse_details =
|
||||||
GST_ELEMENT_DETAILS (".dat parser",
|
GST_ELEMENT_DETAILS (".dat parser",
|
||||||
"Codec/Parser",
|
"Codec/Parser",
|
||||||
"Parse a .dat file (VCD) into raw mpeg1",
|
"Parse a .dat file (VCD) into raw mpeg1",
|
||||||
"Wim Taymans <wim.taymans@tvd.be>");
|
"Wim Taymans <wim.taymans@tvd.be>");
|
||||||
|
|
||||||
gst_element_class_set_details (element_class, &gst_cdxaparse_details);
|
gst_element_class_set_details (element_class, &gst_cdxa_parse_details);
|
||||||
|
|
||||||
/* register src pads */
|
/* register src pads */
|
||||||
gst_element_class_add_pad_template (element_class,
|
gst_element_class_add_pad_template (element_class,
|
||||||
|
@ -99,224 +77,491 @@ gst_cdxaparse_base_init (gpointer g_class)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_cdxaparse_class_init (GstCDXAParseClass * klass)
|
gst_cdxa_parse_class_init (GstCDXAParseClass * klass)
|
||||||
{
|
{
|
||||||
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
||||||
|
|
||||||
parent_class = g_type_class_ref (GST_TYPE_RIFF_READ);
|
gstelement_class->change_state =
|
||||||
|
GST_DEBUG_FUNCPTR (gst_cdxa_parse_change_state);
|
||||||
gstelement_class->change_state = gst_cdxaparse_change_state;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_cdxaparse_init (GstCDXAParse * cdxaparse)
|
gst_cdxa_parse_init (GstCDXAParse * cdxaparse, GstCDXAParseClass * klass)
|
||||||
{
|
{
|
||||||
/* FIXME: this element needs to be event aware */
|
GstCaps *caps;
|
||||||
|
|
||||||
cdxaparse->sinkpad =
|
cdxaparse->sinkpad =
|
||||||
gst_pad_new_from_template (gst_static_pad_template_get
|
gst_pad_new_from_template (gst_static_pad_template_get
|
||||||
(&sink_template_factory), "sink");
|
(&sink_template_factory), "sink");
|
||||||
GST_RIFF_READ (cdxaparse)->sinkpad = cdxaparse->sinkpad;
|
gst_pad_set_activate_function (cdxaparse->sinkpad,
|
||||||
|
GST_DEBUG_FUNCPTR (gst_cdxa_parse_sink_activate));
|
||||||
|
gst_pad_set_activatepull_function (cdxaparse->sinkpad,
|
||||||
|
GST_DEBUG_FUNCPTR (gst_cdxa_parse_sink_activate_pull));
|
||||||
|
|
||||||
gst_element_add_pad (GST_ELEMENT (cdxaparse), cdxaparse->sinkpad);
|
gst_element_add_pad (GST_ELEMENT (cdxaparse), cdxaparse->sinkpad);
|
||||||
|
|
||||||
cdxaparse->srcpad =
|
cdxaparse->srcpad =
|
||||||
gst_pad_new_from_template (gst_static_pad_template_get
|
gst_pad_new_from_template (gst_static_pad_template_get
|
||||||
(&src_template_factory), "src");
|
(&src_template_factory), "src");
|
||||||
/* FIXME: event, query */
|
|
||||||
|
gst_pad_set_event_function (cdxaparse->srcpad,
|
||||||
|
GST_DEBUG_FUNCPTR (gst_cdxa_parse_src_event));
|
||||||
|
gst_pad_set_query_function (cdxaparse->srcpad,
|
||||||
|
GST_DEBUG_FUNCPTR (gst_cdxa_parse_src_query));
|
||||||
|
|
||||||
|
caps = gst_caps_new_simple ("video/mpeg",
|
||||||
|
"systemstream", G_TYPE_BOOLEAN, TRUE, NULL);
|
||||||
|
gst_pad_use_fixed_caps (cdxaparse->srcpad);
|
||||||
|
gst_pad_set_caps (cdxaparse->srcpad, caps);
|
||||||
|
gst_caps_unref (caps);
|
||||||
gst_element_add_pad (GST_ELEMENT (cdxaparse), cdxaparse->srcpad);
|
gst_element_add_pad (GST_ELEMENT (cdxaparse), cdxaparse->srcpad);
|
||||||
|
|
||||||
gst_element_set_loop_function (GST_ELEMENT (cdxaparse), gst_cdxaparse_loop);
|
|
||||||
|
|
||||||
cdxaparse->state = GST_CDXAPARSE_START;
|
cdxaparse->state = GST_CDXA_PARSE_START;
|
||||||
cdxaparse->seek_pending = FALSE;
|
cdxaparse->offset = 0;
|
||||||
cdxaparse->seek_offset = 0;
|
cdxaparse->datasize = 0;
|
||||||
|
cdxaparse->datastart = -1;
|
||||||
|
}
|
||||||
|
|
||||||
GST_OBJECT_FLAG_SET (cdxaparse, GST_ELEMENT_EVENT_AWARE);
|
#define HAVE_FOURCC(data,fourcc) (GST_READ_UINT32_LE((data))==(fourcc))
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_cdxa_parse_stream_init (GstCDXAParse * cdxa)
|
||||||
|
{
|
||||||
|
GstFlowReturn flow_ret;
|
||||||
|
GstBuffer *buf = NULL;
|
||||||
|
guint8 *data;
|
||||||
|
|
||||||
|
flow_ret = gst_pad_pull_range (cdxa->sinkpad, cdxa->offset, 12, &buf);
|
||||||
|
if (flow_ret != GST_FLOW_OK)
|
||||||
|
return flow_ret;
|
||||||
|
|
||||||
|
if (GST_BUFFER_SIZE (buf) < 12)
|
||||||
|
goto wrong_type;
|
||||||
|
|
||||||
|
data = GST_BUFFER_DATA (buf);
|
||||||
|
if (!HAVE_FOURCC (data, GST_RIFF_TAG_RIFF)) {
|
||||||
|
GST_ERROR_OBJECT (cdxa, "Not a RIFF file");
|
||||||
|
goto wrong_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HAVE_FOURCC (data + 8, GST_RIFF_RIFF_CDXA)) {
|
||||||
|
GST_ERROR_OBJECT (cdxa, "RIFF file does not have CDXA content");
|
||||||
|
goto wrong_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
cdxa->offset += 12;
|
||||||
|
gst_buffer_unref (buf);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
wrong_type:
|
||||||
|
|
||||||
|
GST_ELEMENT_ERROR (cdxa, STREAM, WRONG_TYPE, (NULL), (NULL));
|
||||||
|
gst_buffer_unref (buf);
|
||||||
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
gst_cdxaparse_stream_init (GstCDXAParse * cdxa)
|
gst_cdxa_parse_sink_activate (GstPad * sinkpad)
|
||||||
{
|
{
|
||||||
GstRiffRead *riff = GST_RIFF_READ (cdxa);
|
GstCDXAParse *cdxa = GST_CDXA_PARSE (GST_PAD_PARENT (sinkpad));
|
||||||
guint32 doctype;
|
|
||||||
|
|
||||||
if (!gst_riff_read_header (riff, &doctype))
|
if (!gst_pad_check_pull_range (sinkpad) ||
|
||||||
|
!gst_pad_activate_pull (sinkpad, TRUE)) {
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "No pull mode");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we can activate pull_range upstream, then read the header
|
||||||
|
* and see if it's really a RIFF CDXA file. */
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Activated pull mode. Reading RIFF header");
|
||||||
|
if (!gst_cdxa_parse_stream_init (cdxa))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
if (doctype != GST_RIFF_RIFF_CDXA) {
|
return TRUE;
|
||||||
GST_ELEMENT_ERROR (cdxa, STREAM, WRONG_TYPE, (NULL), (NULL));
|
}
|
||||||
return FALSE;
|
|
||||||
|
static gboolean
|
||||||
|
gst_cdxa_parse_sink_activate_pull (GstPad * sinkpad, gboolean active)
|
||||||
|
{
|
||||||
|
if (active) {
|
||||||
|
/* if we have a scheduler we can start the task */
|
||||||
|
gst_pad_start_task (sinkpad, (GstTaskFunction) gst_cdxa_parse_loop,
|
||||||
|
sinkpad);
|
||||||
|
} else {
|
||||||
|
gst_pad_stop_task (sinkpad);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read 'fmt ' header */
|
/*
|
||||||
static gboolean G_GNUC_UNUSED
|
* A sector is 2352 bytes long and is composed of:
|
||||||
gst_cdxaparse_fmt (GstCDXAParse * cdxa)
|
*
|
||||||
|
* ! sync ! header ! subheader ! data ... ! edc !
|
||||||
|
* ! 12 bytes ! 4 bytes ! 8 bytes ! 2324 bytes ! 4 bytes !
|
||||||
|
* !-------------------------------------------------------!
|
||||||
|
*
|
||||||
|
* We strip the data out of it and send it to the srcpad.
|
||||||
|
*
|
||||||
|
* sync : 00 FF FF FF FF FF FF FF FF FF FF 00
|
||||||
|
* header : hour minute second mode
|
||||||
|
* sub-header : track channel sub_mode coding repeat (4 bytes)
|
||||||
|
* edc : checksum
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* FIXME: use define from gstcdxastrip.h */
|
||||||
|
#define GST_CDXA_SECTOR_SIZE 2352
|
||||||
|
#define GST_CDXA_DATA_SIZE 2324
|
||||||
|
#define GST_CDXA_HEADER_SIZE 24
|
||||||
|
|
||||||
|
/* FIXME: use version from gstcdxastrip.c */
|
||||||
|
static GstBuffer *
|
||||||
|
gst_cdxa_parse_strip (GstBuffer * buf)
|
||||||
{
|
{
|
||||||
GstRiffRead *riff = GST_RIFF_READ (cdxa);
|
GstBuffer *sub;
|
||||||
gst_riff_strf_auds *header;
|
|
||||||
|
|
||||||
if (!gst_riff_read_strf_auds (riff, &header)) {
|
g_assert (GST_BUFFER_SIZE (buf) >= GST_CDXA_SECTOR_SIZE);
|
||||||
g_warning ("Not fmt");
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* As we don't know what is in this fmt field, we do nothing */
|
/* Skip CDXA headers, only keep data.
|
||||||
|
* FIXME: check sync, resync, ... */
|
||||||
|
sub = gst_buffer_create_sub (buf, GST_CDXA_HEADER_SIZE, GST_CDXA_DATA_SIZE);
|
||||||
|
gst_buffer_unref (buf);
|
||||||
|
|
||||||
return TRUE;
|
return sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean G_GNUC_UNUSED
|
/* -1 = no sync (discard buffer),
|
||||||
gst_cdxaparse_other (GstCDXAParse * cdxa)
|
* otherwise offset indicates syncpoint in buffer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static gint
|
||||||
|
gst_cdxa_parse_sync (GstBuffer * buf)
|
||||||
{
|
{
|
||||||
GstRiffRead *riff = GST_RIFF_READ (cdxa);
|
const guint8 sync_marker[12] = { 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||||
guint32 tag, length;
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00
|
||||||
|
};
|
||||||
|
guint8 *data;
|
||||||
|
guint size;
|
||||||
|
|
||||||
if (!gst_riff_peek_head (riff, &tag, &length, NULL)) {
|
size = GST_BUFFER_SIZE (buf);
|
||||||
return FALSE;
|
data = GST_BUFFER_DATA (buf);
|
||||||
|
|
||||||
|
while (size >= 12) {
|
||||||
|
if (memcmp (data, sync_marker, 12) == 0) {
|
||||||
|
return (gint) (data - GST_BUFFER_DATA (buf));
|
||||||
|
}
|
||||||
|
--size;
|
||||||
|
++data;
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
switch (tag) {
|
|
||||||
case GST_RIFF_TAG_data:
|
|
||||||
if (!gst_bytestream_flush (riff->bs, 8))
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
cdxa->state = GST_CDXAPARSE_DATA;
|
|
||||||
cdxa->dataleft = cdxa->datasize = (guint64) length;
|
|
||||||
cdxa->datastart = gst_bytestream_tell (riff->bs);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (!gst_riff_read_skip (riff))
|
|
||||||
return FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_cdxaparse_loop (GstElement * element)
|
gst_cdxa_parse_loop (GstPad * sinkpad)
|
||||||
{
|
{
|
||||||
GstCDXAParse *cdxa = GST_CDXAPARSE (element);
|
GstFlowReturn flow_ret;
|
||||||
GstRiffRead *riff = GST_RIFF_READ (cdxa);
|
GstCDXAParse *cdxa;
|
||||||
|
GstBuffer *buf = NULL;
|
||||||
|
gint sync_offset = -1;
|
||||||
|
|
||||||
if (cdxa->state == GST_CDXAPARSE_DATA) {
|
cdxa = GST_CDXA_PARSE (GST_PAD_PARENT (sinkpad));
|
||||||
if (cdxa->dataleft > 0) {
|
|
||||||
gint sync;
|
|
||||||
guint got_bytes, desired;
|
|
||||||
GstBuffer *buf = NULL;
|
|
||||||
GstBuffer *outbuf = NULL;
|
|
||||||
|
|
||||||
/* resync */
|
if (cdxa->datasize <= 0) {
|
||||||
desired = cdxa->dataleft;
|
GstFormat format = GST_FORMAT_BYTES;
|
||||||
if (desired > 1024)
|
GstPad *peer;
|
||||||
desired = 1024;
|
|
||||||
if (!(buf = gst_riff_peek_element_data (riff, desired, &got_bytes)))
|
if ((peer = gst_pad_get_peer (sinkpad))) {
|
||||||
return;
|
if (!gst_pad_query_duration (peer, &format, &cdxa->datasize)) {
|
||||||
sync = gst_cdxastrip_sync (buf);
|
GST_DEBUG_OBJECT (cdxa, "Failed to query upstream size!");
|
||||||
gst_buffer_unref (buf);
|
gst_object_unref (peer);
|
||||||
if (sync == -1) {
|
goto pause;
|
||||||
gst_bytestream_flush_fast (riff->bs, desired);
|
|
||||||
cdxa->dataleft -= desired;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (sync > 0) {
|
gst_object_unref (peer);
|
||||||
if (cdxa->dataleft < sync)
|
|
||||||
sync = cdxa->dataleft;
|
|
||||||
gst_bytestream_flush_fast (riff->bs, sync);
|
|
||||||
cdxa->dataleft -= sync;
|
|
||||||
if (cdxa->dataleft == 0)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get data */
|
|
||||||
desired = GST_CDXA_SECTOR_SIZE;
|
|
||||||
if (!(buf = gst_riff_read_element_data (riff, desired, &got_bytes)))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Skip CDXA headers, only keep data */
|
|
||||||
outbuf = gst_cdxastrip_strip (buf);
|
|
||||||
GST_DEBUG ("Pushing one buffer");
|
|
||||||
gst_pad_push (cdxa->srcpad, GST_DATA (outbuf));
|
|
||||||
|
|
||||||
if (got_bytes < cdxa->dataleft)
|
|
||||||
cdxa->dataleft -= got_bytes;
|
|
||||||
else
|
|
||||||
cdxa->dataleft = 0;
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
cdxa->state = GST_CDXAPARSE_OTHER;
|
|
||||||
}
|
}
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Upstream size: %" G_GINT64_FORMAT, cdxa->datasize);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (cdxa->state) {
|
do {
|
||||||
case GST_CDXAPARSE_START:
|
guint req;
|
||||||
if (!gst_cdxaparse_stream_init (cdxa))
|
|
||||||
return;
|
|
||||||
cdxa->state = GST_CDXAPARSE_DATA;
|
|
||||||
cdxa->dataleft = cdxa->datasize =
|
|
||||||
(guint64) gst_bytestream_length (riff->bs);
|
|
||||||
cdxa->datastart = gst_bytestream_tell (riff->bs);
|
|
||||||
break;
|
|
||||||
#if 0
|
|
||||||
cdxa->state = GST_CDXAPARSE_FMT;
|
|
||||||
/* fall-through */
|
|
||||||
|
|
||||||
case GST_CDXAPARSE_FMT:
|
req = 8 + GST_CDXA_SECTOR_SIZE; /* riff chunk header = 8 bytes */
|
||||||
if (0 && !gst_cdxaparse_fmt (cdxa))
|
|
||||||
return;
|
flow_ret = gst_pad_pull_range (cdxa->sinkpad, cdxa->offset, req, &buf);
|
||||||
cdxa->state = GST_CDXAPARSE_OTHER;
|
|
||||||
/* fall-through */
|
if (flow_ret != GST_FLOW_OK) {
|
||||||
#endif
|
GST_DEBUG_OBJECT (cdxa, "Pull flow: %s", gst_flow_get_name (flow_ret));
|
||||||
case GST_CDXAPARSE_OTHER:
|
goto pause;
|
||||||
if (!gst_cdxaparse_other (cdxa))
|
}
|
||||||
return;
|
|
||||||
|
if (GST_BUFFER_SIZE (buf) < req) {
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Short read, only got %u/%u bytes",
|
||||||
|
GST_BUFFER_SIZE (buf), req);
|
||||||
|
goto eos;
|
||||||
|
}
|
||||||
|
|
||||||
|
sync_offset = gst_cdxa_parse_sync (buf);
|
||||||
|
if (sync_offset >= 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GST_CDXAPARSE_DATA:
|
cdxa->offset += req;
|
||||||
default:
|
cdxa->bytes_skipped += req;
|
||||||
g_assert_not_reached ();
|
} while (1);
|
||||||
|
|
||||||
|
cdxa->offset += sync_offset;
|
||||||
|
cdxa->bytes_skipped += sync_offset;
|
||||||
|
|
||||||
|
/* first sync frame? */
|
||||||
|
if (cdxa->datastart < 0) {
|
||||||
|
GST_LOG_OBJECT (cdxa, "datastart=0x%" G_GINT64_MODIFIER "x", cdxa->offset);
|
||||||
|
cdxa->datastart = cdxa->offset;
|
||||||
|
cdxa->bytes_skipped = 0;
|
||||||
|
cdxa->bytes_sent = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "pulling buffer at offset 0x%" G_GINT64_MODIFIER "x",
|
||||||
|
cdxa->offset);
|
||||||
|
|
||||||
|
flow_ret = gst_pad_pull_range (cdxa->sinkpad, cdxa->offset,
|
||||||
|
GST_CDXA_SECTOR_SIZE, &buf);
|
||||||
|
|
||||||
|
if (flow_ret != GST_FLOW_OK) {
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Flow: %s", gst_flow_get_name (flow_ret));
|
||||||
|
goto pause;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GST_BUFFER_SIZE (buf) < GST_CDXA_SECTOR_SIZE) {
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Short read, only got %u/%u bytes",
|
||||||
|
GST_BUFFER_SIZE (buf), GST_CDXA_SECTOR_SIZE);
|
||||||
|
goto eos;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = gst_cdxa_parse_strip (buf);
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "pushing buffer %p", buf);
|
||||||
|
gst_buffer_set_caps (buf, GST_PAD_CAPS (cdxa->srcpad));
|
||||||
|
|
||||||
|
cdxa->offset += GST_BUFFER_SIZE (buf);
|
||||||
|
cdxa->bytes_sent += GST_BUFFER_SIZE (buf);
|
||||||
|
|
||||||
|
flow_ret = gst_pad_push (cdxa->srcpad, buf);
|
||||||
|
if (flow_ret != GST_FLOW_OK) {
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Push flow: %s", gst_flow_get_name (flow_ret));
|
||||||
|
goto pause;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
eos:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Sending EOS");
|
||||||
|
gst_pad_push_event (cdxa->srcpad, gst_event_new_eos ());
|
||||||
|
/* fallthrough */
|
||||||
|
}
|
||||||
|
pause:
|
||||||
|
{
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Pausing");
|
||||||
|
gst_pad_pause_task (cdxa->sinkpad);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static GstStateChangeReturn
|
static gint64
|
||||||
gst_cdxaparse_change_state (GstElement * element, GstStateChange transition)
|
gst_cdxa_parse_convert_src_to_sink_offset (GstCDXAParse * cdxa, gint64 src)
|
||||||
{
|
{
|
||||||
GstCDXAParse *cdxa = GST_CDXAPARSE (element);
|
gint64 sink;
|
||||||
|
|
||||||
|
sink = src + cdxa->datastart;
|
||||||
|
sink = gst_util_uint64_scale (sink, GST_CDXA_SECTOR_SIZE, GST_CDXA_DATA_SIZE);
|
||||||
|
|
||||||
|
/* FIXME: take into account skipped bytes */
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "src offset=%" G_GINT64_FORMAT ", sink offset=%"
|
||||||
|
G_GINT64_FORMAT, src, sink);
|
||||||
|
|
||||||
|
return sink;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gint64
|
||||||
|
gst_cdxa_parse_convert_sink_to_src_offset (GstCDXAParse * cdxa, gint64 sink)
|
||||||
|
{
|
||||||
|
gint64 src;
|
||||||
|
|
||||||
|
src = sink - cdxa->datastart;
|
||||||
|
src = gst_util_uint64_scale (src, GST_CDXA_DATA_SIZE, GST_CDXA_SECTOR_SIZE);
|
||||||
|
|
||||||
|
/* FIXME: take into account skipped bytes */
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "sink offset=%" G_GINT64_FORMAT ", src offset=%"
|
||||||
|
G_GINT64_FORMAT, sink, src);
|
||||||
|
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_cdxa_parse_do_seek (GstCDXAParse * cdxa, GstEvent * event)
|
||||||
|
{
|
||||||
|
GstSeekFlags flags;
|
||||||
|
GstSeekType start_type;
|
||||||
|
GstFormat format;
|
||||||
|
gint64 start, off, upstream_size;
|
||||||
|
|
||||||
|
gst_event_parse_seek (event, NULL, &format, &flags, &start_type, &start,
|
||||||
|
NULL, NULL);
|
||||||
|
|
||||||
|
if (format != GST_FORMAT_BYTES) {
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Can only handle seek in BYTES format");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (format != GST_SEEK_TYPE_SET) {
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Can only handle seek from start (SEEK_TYPE_SET)");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (cdxa);
|
||||||
|
off = gst_cdxa_parse_convert_src_to_sink_offset (cdxa, start);
|
||||||
|
upstream_size = cdxa->datasize;
|
||||||
|
GST_OBJECT_UNLOCK (cdxa);
|
||||||
|
|
||||||
|
if (off >= upstream_size) {
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Invalid target offset %" G_GINT64_FORMAT ", file "
|
||||||
|
"is only %" G_GINT64_FORMAT " bytes in size", off, upstream_size);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* unlock upstream pull_range */
|
||||||
|
gst_pad_push_event (cdxa->sinkpad, gst_event_new_flush_start ());
|
||||||
|
|
||||||
|
/* make sure our loop function exits */
|
||||||
|
gst_pad_push_event (cdxa->srcpad, gst_event_new_flush_start ());
|
||||||
|
|
||||||
|
/* wait for streaming to finish */
|
||||||
|
GST_PAD_STREAM_LOCK (cdxa->sinkpad);
|
||||||
|
|
||||||
|
/* prepare for streaming again */
|
||||||
|
gst_pad_push_event (cdxa->sinkpad, gst_event_new_flush_stop ());
|
||||||
|
gst_pad_push_event (cdxa->srcpad, gst_event_new_flush_stop ());
|
||||||
|
|
||||||
|
gst_pad_push_event (cdxa->srcpad,
|
||||||
|
gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES,
|
||||||
|
start, GST_CLOCK_TIME_NONE, 0));
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (cdxa);
|
||||||
|
cdxa->offset = off;
|
||||||
|
GST_OBJECT_UNLOCK (cdxa);
|
||||||
|
|
||||||
|
/* and restart */
|
||||||
|
gst_pad_start_task (cdxa->sinkpad,
|
||||||
|
(GstTaskFunction) gst_cdxa_parse_loop, cdxa->sinkpad);
|
||||||
|
|
||||||
|
GST_PAD_STREAM_UNLOCK (cdxa->sinkpad);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_cdxa_parse_src_event (GstPad * srcpad, GstEvent * event)
|
||||||
|
{
|
||||||
|
GstCDXAParse *cdxa = GST_CDXA_PARSE (gst_pad_get_parent (srcpad));
|
||||||
|
gboolean res = FALSE;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Handling %s event", GST_EVENT_TYPE_NAME (event));
|
||||||
|
|
||||||
|
switch (GST_EVENT_TYPE (event)) {
|
||||||
|
case GST_EVENT_SEEK:
|
||||||
|
res = gst_cdxa_parse_do_seek (cdxa, event);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
res = gst_pad_event_default (srcpad, event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_object_unref (cdxa);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
gst_cdxa_parse_src_query (GstPad * srcpad, GstQuery * query)
|
||||||
|
{
|
||||||
|
GstCDXAParse *cdxa = GST_CDXA_PARSE (gst_pad_get_parent (srcpad));
|
||||||
|
gboolean res = FALSE;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (cdxa, "Handling %s query",
|
||||||
|
gst_query_type_get_name (GST_QUERY_TYPE (query)));
|
||||||
|
|
||||||
|
res = gst_pad_query_default (srcpad, query);
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
GstFormat format;
|
||||||
|
gint64 val;
|
||||||
|
|
||||||
|
switch (GST_QUERY_TYPE (query)) {
|
||||||
|
case GST_QUERY_DURATION:
|
||||||
|
gst_query_parse_duration (query, &format, &val);
|
||||||
|
if (format == GST_FORMAT_BYTES) {
|
||||||
|
val = gst_cdxa_parse_convert_sink_to_src_offset (cdxa, val);
|
||||||
|
gst_query_set_duration (query, format, val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_QUERY_POSITION:
|
||||||
|
gst_query_parse_position (query, &format, &val);
|
||||||
|
if (format == GST_FORMAT_BYTES) {
|
||||||
|
val = gst_cdxa_parse_convert_sink_to_src_offset (cdxa, val);
|
||||||
|
gst_query_set_position (query, format, val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gst_object_unref (cdxa);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstStateChangeReturn
|
||||||
|
gst_cdxa_parse_change_state (GstElement * element, GstStateChange transition)
|
||||||
|
{
|
||||||
|
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
|
||||||
|
GstCDXAParse *cdxa = GST_CDXA_PARSE (element);
|
||||||
|
|
||||||
switch (transition) {
|
switch (transition) {
|
||||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||||
cdxa->state = GST_CDXAPARSE_START;
|
cdxa->state = GST_CDXA_PARSE_START;
|
||||||
break;
|
|
||||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
|
||||||
cdxa->state = GST_CDXAPARSE_START;
|
|
||||||
cdxa->seek_pending = FALSE;
|
|
||||||
cdxa->seek_offset = 0;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GST_ELEMENT_CLASS (parent_class)->change_state)
|
if (GST_ELEMENT_CLASS (parent_class)->change_state)
|
||||||
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||||
|
|
||||||
return GST_STATE_CHANGE_SUCCESS;
|
switch (transition) {
|
||||||
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||||
|
cdxa->state = GST_CDXA_PARSE_START;
|
||||||
|
cdxa->datasize = 0;
|
||||||
|
cdxa->datastart = -1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
plugin_init (GstPlugin * plugin)
|
plugin_init (GstPlugin * plugin)
|
||||||
{
|
{
|
||||||
if (!gst_library_load ("riff") || !gst_library_load ("gstbytestream")) {
|
if (!gst_element_register (plugin, "cdxaparse", GST_RANK_PRIMARY, GST_TYPE_CDXA_PARSE) /* ||
|
||||||
|
!gst_element_register (plugin, "cdxastrip", GST_RANK_PRIMARY,
|
||||||
|
GST_TYPE_CDXASTRIP) */ ) {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return gst_element_register (plugin, "cdxaparse", GST_RANK_PRIMARY,
|
GST_DEBUG_CATEGORY_INIT (cdxaparse_debug, "cdxaparse", 0, "CDXA Parser");
|
||||||
GST_TYPE_CDXAPARSE) &&
|
|
||||||
gst_element_register (plugin, "cdxastrip",
|
return TRUE;
|
||||||
GST_RANK_PRIMARY, GST_TYPE_CDXASTRIP);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||||
|
|
|
@ -18,57 +18,58 @@
|
||||||
* Boston, MA 02111-1307, USA.
|
* Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __GST_CDXAPARSE_H__
|
#ifndef __GST_CDXA_PARSE_H__
|
||||||
#define __GST_CDXAPARSE_H__
|
#define __GST_CDXA_PARSE_H__
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include "gst/riff/riff-ids.h"
|
|
||||||
#include "gst/riff/riff-read.h"
|
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
#define GST_TYPE_CDXAPARSE \
|
#define GST_TYPE_CDXA_PARSE \
|
||||||
(gst_cdxaparse_get_type())
|
(gst_cdxa_parse_get_type())
|
||||||
#define GST_CDXAPARSE(obj) \
|
#define GST_CDXA_PARSE(obj) \
|
||||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CDXAPARSE,GstCDXAParse))
|
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_CDXA_PARSE,GstCDXAParse))
|
||||||
#define GST_CDXAPARSE_CLASS(klass) \
|
#define GST_CDXA_PARSE_CLASS(klass) \
|
||||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CDXAPARSE,GstCDXAParse))
|
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_CDXA_PARSE,GstCDXAParse))
|
||||||
#define GST_IS_CDXAPARSE(obj) \
|
#define GST_IS_CDXA_PARSE(obj) \
|
||||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CDXAPARSE))
|
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_CDXA_PARSE))
|
||||||
#define GST_IS_CDXAPARSE_CLASS(obj) \
|
#define GST_IS_CDXA_PARSE_CLASS(obj) \
|
||||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CDXAPARSE))
|
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_CDXA_PARSE))
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GST_CDXAPARSE_START,
|
GST_CDXA_PARSE_START,
|
||||||
GST_CDXAPARSE_FMT,
|
GST_CDXA_PARSE_FMT,
|
||||||
GST_CDXAPARSE_OTHER,
|
GST_CDXA_PARSE_OTHER,
|
||||||
GST_CDXAPARSE_DATA,
|
GST_CDXA_PARSE_DATA,
|
||||||
} GstCDXAParseState;
|
} GstCDXAParseState;
|
||||||
|
|
||||||
typedef struct _GstCDXAParse GstCDXAParse;
|
typedef struct _GstCDXAParse GstCDXAParse;
|
||||||
typedef struct _GstCDXAParseClass GstCDXAParseClass;
|
typedef struct _GstCDXAParseClass GstCDXAParseClass;
|
||||||
|
|
||||||
struct _GstCDXAParse {
|
struct _GstCDXAParse {
|
||||||
GstRiffRead parent;
|
GstElement element;
|
||||||
|
|
||||||
/* pads */
|
/* pads */
|
||||||
GstPad *sinkpad,*srcpad;
|
GstPad *sinkpad;
|
||||||
|
GstPad *srcpad;
|
||||||
|
|
||||||
/* CDXA decoding state */
|
/* CDXA decoding state */
|
||||||
GstCDXAParseState state;
|
GstCDXAParseState state;
|
||||||
|
|
||||||
guint64 dataleft, datasize, datastart;
|
gint64 offset; /* current byte offset in file */
|
||||||
|
gint64 datasize; /* upstream size in bytes */
|
||||||
gboolean seek_pending;
|
gint64 datastart; /* byte offset of first frame sync */
|
||||||
guint64 seek_offset;
|
gint64 bytes_skipped;
|
||||||
|
gint64 bytes_sent;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _GstCDXAParseClass {
|
struct _GstCDXAParseClass {
|
||||||
GstElementClass parent_class;
|
GstElementClass parent_class;
|
||||||
};
|
};
|
||||||
|
|
||||||
GType gst_cdxaparse_get_type (void);
|
GType gst_cdxa_parse_get_type (void);
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __GST_CDXAPARSE_H__ */
|
#endif /* __GST_CDXA_PARSE_H__ */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue