gstreamer/gst/mpegstream/gstmpegparse.c

1147 lines
33 KiB
C
Raw Normal View History

/* 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*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstmpegparse.h"
#include "gstmpegclock.h"
static GstFormat scr_format;
GST_DEBUG_CATEGORY_STATIC (gstmpegparse_debug);
#define GST_CAT_DEFAULT (gstmpegparse_debug)
GST_DEBUG_CATEGORY_EXTERN (GST_CAT_SEEK);
#define MP_INVALID_SCR ((guint64)(-1))
#define MP_MUX_RATE_MULT 50
#define MP_MIN_VALID_BSS 8192
#define MP_MAX_VALID_BSS 16384
/*
* Hysteresis value to limit the
* total predicted time skipping about
*/
#define MP_SCR_RATE_HYST 0.08
/* elementfactory information */
static GstElementDetails mpeg_parse_details = {
"MPEG System Parser",
"Codec/Parser",
"Parses MPEG1 and MPEG2 System Streams",
"Erik Walthinsen <omega@cse.ogi.edu>\n" "Wim Taymans <wim.taymans@chello.be>"
};
#define CLASS(o) GST_MPEG_PARSE_CLASS (G_OBJECT_GET_CLASS (o))
#define DEFAULT_MAX_DISCONT 45000
/* GstMPEGParse signals and args */
enum
{
/* FILL ME */
LAST_SIGNAL
};
enum
{
ARG_0,
ARG_SYNC,
ARG_MAX_DISCONT,
ARG_DO_ADJUST
/* FILL ME */
};
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/mpeg, "
"mpegversion = (int) [ 1, 2 ], " "systemstream = (boolean) TRUE")
);
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/mpeg, "
"mpegversion = (int) [ 1, 2 ], " "systemstream = (boolean) TRUE")
);
static void gst_mpeg_parse_class_init (GstMPEGParseClass * klass);
static void gst_mpeg_parse_base_init (GstMPEGParseClass * klass);
static void gst_mpeg_parse_init (GstMPEGParse * mpeg_parse);
static GstStateChangeReturn gst_mpeg_parse_change_state (GstElement * element,
GstStateChange transition);
static void gst_mpeg_parse_set_clock (GstElement * element, GstClock * clock);
static gboolean gst_mpeg_parse_parse_packhead (GstMPEGParse * mpeg_parse,
GstBuffer * buffer);
static void gst_mpeg_parse_handle_discont (GstMPEGParse * mpeg_parse,
GstEvent * event);
static void gst_mpeg_parse_send_data (GstMPEGParse * mpeg_parse, GstData * data,
GstClockTime time);
static void gst_mpeg_parse_send_discont (GstMPEGParse * mpeg_parse,
GstClockTime time);
ext/dvdread/dvdreadsrc.c: Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat... Original commit message from CVS: * ext/dvdread/dvdreadsrc.c: (dvdreadsrc_class_init), (dvdreadsrc_init), (dvdreadsrc_dispose), (dvdreadsrc_set_property), (dvdreadsrc_get_property), (_open), (_seek), (_read), (dvdreadsrc_get), (dvdreadsrc_open_file), (dvdreadsrc_change_state): Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat sizeof(dvd) (several GB) before we see something. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_seek): Actually NULL'ify event after using it. * gst/matroska/ebml-read.c: (gst_ebml_read_use_event), (gst_ebml_read_handle_event), (gst_ebml_read_element_id), (gst_ebml_read_element_length), (gst_ebml_read_element_data), (gst_ebml_read_seek), (gst_ebml_read_skip): Handle events. * gst/mpegstream/gstdvddemux.c: (gst_dvd_demux_base_init), (gst_dvd_demux_init), (gst_dvd_demux_get_audio_stream), (gst_dvd_demux_get_subpicture_stream), (gst_dvd_demux_plugin_init): Fix timing (this will probably break if I seek using menus, but I didn't get there yet). VOBs and normal DVDs should now work. Add a mpeg2-only pad with high rank so this get autoplugged for MPEG-2 movies. * gst/mpegstream/gstmpegdemux.c: (gst_mpeg_demux_base_init), (gst_mpeg_demux_class_init), (gst_mpeg_demux_init), (gst_mpeg_demux_new_output_pad), (gst_mpeg_demux_get_video_stream), (gst_mpeg_demux_get_audio_stream), (gst_mpeg_demux_get_private_stream), (gst_mpeg_demux_parse_packet), (gst_mpeg_demux_parse_pes), (gst_mpeg_demux_plugin_init): Use this as second rank for MPEG-1 and MPEG-2. Still use this for MPEG-1 but use dvddemux for MPEG-2. * gst/mpegstream/gstmpegparse.c: (gst_mpeg_parse_class_init), (gst_mpeg_parse_init), (gst_mpeg_parse_new_pad), (gst_mpeg_parse_parse_packhead): Timing. Only add pad template if it exists. Add sink template from class and not from ourselves. This means we will always use the correct sink template even if it is not the one defined in this file.
2004-10-01 08:42:56 +00:00
static void gst_mpeg_parse_new_pad (GstElement * element, GstPad * pad);
static void gst_mpeg_parse_loop (GstElement * element);
static void gst_mpeg_parse_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec);
static void gst_mpeg_parse_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec);
static void gst_mpeg_parse_set_index (GstElement * element, GstIndex * index);
static GstIndex *gst_mpeg_parse_get_index (GstElement * element);
static gboolean gst_mpeg_parse_release_locks (GstElement * element);
static GstElementClass *parent_class = NULL;
/*static guint gst_mpeg_parse_signals[LAST_SIGNAL] = { 0 };*/
GType
gst_mpeg_parse_get_type (void)
{
static GType mpeg_parse_type = 0;
if (!mpeg_parse_type) {
static const GTypeInfo mpeg_parse_info = {
sizeof (GstMPEGParseClass),
(GBaseInitFunc) gst_mpeg_parse_base_init,
NULL,
(GClassInitFunc) gst_mpeg_parse_class_init,
NULL,
NULL,
sizeof (GstMPEGParse),
0,
(GInstanceInitFunc) gst_mpeg_parse_init,
};
mpeg_parse_type =
g_type_register_static (GST_TYPE_ELEMENT, "GstMPEGParse",
&mpeg_parse_info, 0);
GST_DEBUG_CATEGORY_INIT (gstmpegparse_debug, "mpegparse", 0,
"MPEG parser element");
}
return mpeg_parse_type;
}
static void
gst_mpeg_parse_base_init (GstMPEGParseClass * klass)
{
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
gst_element_class_set_details (element_class, &mpeg_parse_details);
}
static void
gst_mpeg_parse_class_init (GstMPEGParseClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gobject_class = (GObjectClass *) klass;
gstelement_class = (GstElementClass *) klass;
parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SYNC,
g_param_spec_boolean ("sync", "Sync", "Synchronize on the stream SCR",
FALSE, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_MAX_DISCONT,
g_param_spec_int ("max_discont", "Max Discont",
"The maximum allowed SCR discontinuity", 0, G_MAXINT,
DEFAULT_MAX_DISCONT, G_PARAM_READWRITE));
/* FIXME: Default is TRUE to make the behavior backwards compatible.
It probably should be FALSE. */
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_DO_ADJUST,
g_param_spec_boolean ("adjust", "adjust", "Adjust timestamps to "
"smooth discontinuities", TRUE, G_PARAM_READWRITE));
gobject_class->get_property = gst_mpeg_parse_get_property;
gobject_class->set_property = gst_mpeg_parse_set_property;
ext/dvdread/dvdreadsrc.c: Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat... Original commit message from CVS: * ext/dvdread/dvdreadsrc.c: (dvdreadsrc_class_init), (dvdreadsrc_init), (dvdreadsrc_dispose), (dvdreadsrc_set_property), (dvdreadsrc_get_property), (_open), (_seek), (_read), (dvdreadsrc_get), (dvdreadsrc_open_file), (dvdreadsrc_change_state): Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat sizeof(dvd) (several GB) before we see something. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_seek): Actually NULL'ify event after using it. * gst/matroska/ebml-read.c: (gst_ebml_read_use_event), (gst_ebml_read_handle_event), (gst_ebml_read_element_id), (gst_ebml_read_element_length), (gst_ebml_read_element_data), (gst_ebml_read_seek), (gst_ebml_read_skip): Handle events. * gst/mpegstream/gstdvddemux.c: (gst_dvd_demux_base_init), (gst_dvd_demux_init), (gst_dvd_demux_get_audio_stream), (gst_dvd_demux_get_subpicture_stream), (gst_dvd_demux_plugin_init): Fix timing (this will probably break if I seek using menus, but I didn't get there yet). VOBs and normal DVDs should now work. Add a mpeg2-only pad with high rank so this get autoplugged for MPEG-2 movies. * gst/mpegstream/gstmpegdemux.c: (gst_mpeg_demux_base_init), (gst_mpeg_demux_class_init), (gst_mpeg_demux_init), (gst_mpeg_demux_new_output_pad), (gst_mpeg_demux_get_video_stream), (gst_mpeg_demux_get_audio_stream), (gst_mpeg_demux_get_private_stream), (gst_mpeg_demux_parse_packet), (gst_mpeg_demux_parse_pes), (gst_mpeg_demux_plugin_init): Use this as second rank for MPEG-1 and MPEG-2. Still use this for MPEG-1 but use dvddemux for MPEG-2. * gst/mpegstream/gstmpegparse.c: (gst_mpeg_parse_class_init), (gst_mpeg_parse_init), (gst_mpeg_parse_new_pad), (gst_mpeg_parse_parse_packhead): Timing. Only add pad template if it exists. Add sink template from class and not from ourselves. This means we will always use the correct sink template even if it is not the one defined in this file.
2004-10-01 08:42:56 +00:00
gstelement_class->new_pad = gst_mpeg_parse_new_pad;
gstelement_class->change_state = gst_mpeg_parse_change_state;
gstelement_class->set_clock = gst_mpeg_parse_set_clock;
gstelement_class->get_index = gst_mpeg_parse_get_index;
gstelement_class->set_index = gst_mpeg_parse_set_index;
gstelement_class->release_locks = gst_mpeg_parse_release_locks;
klass->parse_packhead = gst_mpeg_parse_parse_packhead;
klass->parse_syshead = NULL;
klass->parse_packet = NULL;
klass->parse_pes = NULL;
klass->handle_discont = gst_mpeg_parse_handle_discont;
klass->send_data = gst_mpeg_parse_send_data;
klass->send_discont = gst_mpeg_parse_send_discont;
/* FIXME: this is a hack. We add the pad templates here instead
* in the base_init function, since the derived class (mpegdemux)
* uses different pads. IMO, this is wrong. */
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&src_factory));
gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&sink_factory));
}
static void
gst_mpeg_parse_init (GstMPEGParse * mpeg_parse)
{
ext/dvdread/dvdreadsrc.c: Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat... Original commit message from CVS: * ext/dvdread/dvdreadsrc.c: (dvdreadsrc_class_init), (dvdreadsrc_init), (dvdreadsrc_dispose), (dvdreadsrc_set_property), (dvdreadsrc_get_property), (_open), (_seek), (_read), (dvdreadsrc_get), (dvdreadsrc_open_file), (dvdreadsrc_change_state): Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat sizeof(dvd) (several GB) before we see something. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_seek): Actually NULL'ify event after using it. * gst/matroska/ebml-read.c: (gst_ebml_read_use_event), (gst_ebml_read_handle_event), (gst_ebml_read_element_id), (gst_ebml_read_element_length), (gst_ebml_read_element_data), (gst_ebml_read_seek), (gst_ebml_read_skip): Handle events. * gst/mpegstream/gstdvddemux.c: (gst_dvd_demux_base_init), (gst_dvd_demux_init), (gst_dvd_demux_get_audio_stream), (gst_dvd_demux_get_subpicture_stream), (gst_dvd_demux_plugin_init): Fix timing (this will probably break if I seek using menus, but I didn't get there yet). VOBs and normal DVDs should now work. Add a mpeg2-only pad with high rank so this get autoplugged for MPEG-2 movies. * gst/mpegstream/gstmpegdemux.c: (gst_mpeg_demux_base_init), (gst_mpeg_demux_class_init), (gst_mpeg_demux_init), (gst_mpeg_demux_new_output_pad), (gst_mpeg_demux_get_video_stream), (gst_mpeg_demux_get_audio_stream), (gst_mpeg_demux_get_private_stream), (gst_mpeg_demux_parse_packet), (gst_mpeg_demux_parse_pes), (gst_mpeg_demux_plugin_init): Use this as second rank for MPEG-1 and MPEG-2. Still use this for MPEG-1 but use dvddemux for MPEG-2. * gst/mpegstream/gstmpegparse.c: (gst_mpeg_parse_class_init), (gst_mpeg_parse_init), (gst_mpeg_parse_new_pad), (gst_mpeg_parse_parse_packhead): Timing. Only add pad template if it exists. Add sink template from class and not from ourselves. This means we will always use the correct sink template even if it is not the one defined in this file.
2004-10-01 08:42:56 +00:00
GstElementClass *klass = GST_ELEMENT_GET_CLASS (mpeg_parse);
GstPadTemplate *templ;
templ = gst_element_class_get_pad_template (klass, "sink");
mpeg_parse->sinkpad = gst_pad_new_from_template (templ, "sink");
gst_element_add_pad (GST_ELEMENT (mpeg_parse), mpeg_parse->sinkpad);
gst_pad_set_formats_function (mpeg_parse->sinkpad,
gst_mpeg_parse_get_src_formats);
gst_pad_set_convert_function (mpeg_parse->sinkpad,
gst_mpeg_parse_convert_src);
ext/dvdread/dvdreadsrc.c: Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat... Original commit message from CVS: * ext/dvdread/dvdreadsrc.c: (dvdreadsrc_class_init), (dvdreadsrc_init), (dvdreadsrc_dispose), (dvdreadsrc_set_property), (dvdreadsrc_get_property), (_open), (_seek), (_read), (dvdreadsrc_get), (dvdreadsrc_open_file), (dvdreadsrc_change_state): Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat sizeof(dvd) (several GB) before we see something. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_seek): Actually NULL'ify event after using it. * gst/matroska/ebml-read.c: (gst_ebml_read_use_event), (gst_ebml_read_handle_event), (gst_ebml_read_element_id), (gst_ebml_read_element_length), (gst_ebml_read_element_data), (gst_ebml_read_seek), (gst_ebml_read_skip): Handle events. * gst/mpegstream/gstdvddemux.c: (gst_dvd_demux_base_init), (gst_dvd_demux_init), (gst_dvd_demux_get_audio_stream), (gst_dvd_demux_get_subpicture_stream), (gst_dvd_demux_plugin_init): Fix timing (this will probably break if I seek using menus, but I didn't get there yet). VOBs and normal DVDs should now work. Add a mpeg2-only pad with high rank so this get autoplugged for MPEG-2 movies. * gst/mpegstream/gstmpegdemux.c: (gst_mpeg_demux_base_init), (gst_mpeg_demux_class_init), (gst_mpeg_demux_init), (gst_mpeg_demux_new_output_pad), (gst_mpeg_demux_get_video_stream), (gst_mpeg_demux_get_audio_stream), (gst_mpeg_demux_get_private_stream), (gst_mpeg_demux_parse_packet), (gst_mpeg_demux_parse_pes), (gst_mpeg_demux_plugin_init): Use this as second rank for MPEG-1 and MPEG-2. Still use this for MPEG-1 but use dvddemux for MPEG-2. * gst/mpegstream/gstmpegparse.c: (gst_mpeg_parse_class_init), (gst_mpeg_parse_init), (gst_mpeg_parse_new_pad), (gst_mpeg_parse_parse_packhead): Timing. Only add pad template if it exists. Add sink template from class and not from ourselves. This means we will always use the correct sink template even if it is not the one defined in this file.
2004-10-01 08:42:56 +00:00
if ((templ = gst_element_class_get_pad_template (klass, "src"))) {
mpeg_parse->srcpad = gst_pad_new_from_template (templ, "src");
gst_element_add_pad (GST_ELEMENT (mpeg_parse), mpeg_parse->srcpad);
gst_pad_set_formats_function (mpeg_parse->srcpad,
gst_mpeg_parse_get_src_formats);
gst_pad_set_convert_function (mpeg_parse->srcpad,
gst_mpeg_parse_convert_src);
gst_pad_set_event_mask_function (mpeg_parse->srcpad,
gst_mpeg_parse_get_src_event_masks);
gst_pad_set_event_function (mpeg_parse->srcpad,
gst_mpeg_parse_handle_src_event);
gst_pad_set_query_type_function (mpeg_parse->srcpad,
gst_mpeg_parse_get_src_query_types);
gst_pad_set_query_function (mpeg_parse->srcpad,
gst_mpeg_parse_handle_src_query);
gst_pad_use_explicit_caps (mpeg_parse->srcpad);
}
gst_element_set_loop_function (GST_ELEMENT (mpeg_parse), gst_mpeg_parse_loop);
mpeg_parse->packetize = NULL;
mpeg_parse->sync = FALSE;
mpeg_parse->id = NULL;
mpeg_parse->max_discont = DEFAULT_MAX_DISCONT;
mpeg_parse->do_adjust = TRUE;
ext/a52dec/gsta52dec.c: Add some debug output. Check that a discont has a valid time associated. Original commit message from CVS: * ext/a52dec/gsta52dec.c: (gst_a52dec_push), (gst_a52dec_handle_event), (gst_a52dec_chain): Add some debug output. Check that a discont has a valid time associated. * ext/alsa/gstalsasink.c: (gst_alsa_sink_check_event), (gst_alsa_sink_loop): Ignore TAG events. A little extra debug for broken timestamps. * ext/dvdnav/dvdnavsrc.c: (dvdnavsrc_init), (dvdnavsrc_loop), (dvdnavsrc_change_state): Ensure we send a discont to engage the link before we send any other events. * ext/dvdread/dvdreadsrc.c: (dvdreadsrc_init), (dvdreadsrc_finalize), (_close), (_open), (_seek_title), (_seek_chapter), (seek_sector), (dvdreadsrc_get), (dvdreadsrc_uri_get_uri), (dvdreadsrc_uri_set_uri): Handle URI of the form dvd://title[,chapter[,angle]]. Currently only dvd://title works in totem because typefinding sends a seek that ends up going back to chapter 1 regardless. * ext/mpeg2dec/gstmpeg2dec.c: * ext/mpeg2dec/gstmpeg2dec.h: Output correct timestamps and handle disconts. * ext/ogg/gstoggdemux.c: (get_relative): Small guard against a null dereference. * ext/pango/gsttextoverlay.c: (gst_textoverlay_finalize), (gst_textoverlay_set_property): Free memory when done. Don't call gst_event_filler_get_duration on EOS events. Use GST_LOG and GST_WARNING instead of g_message and g_warning. * ext/smoothwave/gstsmoothwave.c: (gst_smoothwave_init), (draw_line), (gst_smoothwave_dispose), (gst_sw_sinklink), (gst_sw_srclink), (gst_smoothwave_chain): Draw solid lines, prettier colours. * gst/mpeg2sub/gstmpeg2subt.c: (gst_mpeg2subt_init): Add a default palette that'll work for some movies. * gst/mpegstream/gstdvddemux.c: (gst_dvd_demux_init), (gst_dvd_demux_handle_dvd_event), (gst_dvd_demux_send_discont), (gst_dvd_demux_send_subbuffer), (gst_dvd_demux_reset): * gst/mpegstream/gstdvddemux.h: * gst/mpegstream/gstmpegdemux.c: (gst_mpeg_demux_send_discont), (gst_mpeg_demux_parse_syshead), (gst_mpeg_demux_parse_pes): * gst/mpegstream/gstmpegparse.c: (gst_mpeg_parse_init), (gst_mpeg_parse_handle_discont), (gst_mpeg_parse_parse_packhead): * gst/mpegstream/gstmpegparse.h: Use PTM/NAV events when for timestamp adjustment when connected to dvdnavsrc. Don't use many discont events where one suffices. * gst/playback/gstplaybasebin.c: (group_destroy), (gen_preroll_element), (gst_play_base_bin_add_element): * gst/playback/gstplaybasebin.h: Make sure we remove subtitles from the same bin we put them in. * gst/subparse/gstsubparse.c: (convert_encoding), (parse_subrip), (gst_subparse_buffer_format_autodetect), (gst_subparse_change_state): Fix some memleaks and invalid accesses. * gst/typefind/gsttypefindfunctions.c: (ogganx_type_find), (oggskel_type_find), (cmml_type_find), (plugin_init): Some typefind functions for Annodex v3.0 files * gst/wavparse/gstwavparse.h: GstRiffReadClass is the correct parent class.
2005-01-25 15:34:08 +00:00
mpeg_parse->use_adjust = TRUE;
GST_OBJECT_FLAG_SET (mpeg_parse, GST_ELEMENT_EVENT_AWARE);
}
static void
gst_mpeg_parse_set_clock (GstElement * element, GstClock * clock)
{
GstMPEGParse *parse = GST_MPEG_PARSE (element);
parse->clock = clock;
}
#if 0
static void
gst_mpeg_parse_update_streaminfo (GstMPEGParse * mpeg_parse)
{
GstProps *props;
GstPropsEntry *entry;
gboolean mpeg2 = GST_MPEG_PACKETIZE_IS_MPEG2 (mpeg_parse->packetize);
GstCaps *caps;
props = gst_props_empty_new ();
entry = gst_props_entry_new ("mpegversion", G_TYPE_INT (mpeg2 ? 2 : 1));
gst_props_add_entry (props, (GstPropsEntry *) entry);
entry =
gst_props_entry_new ("bitrate", G_TYPE_INT (mpeg_parse->mux_rate * 8));
gst_props_add_entry (props, (GstPropsEntry *) entry);
caps = gst_caps_new ("mpeg_streaminfo",
"application/x-gst-streaminfo", props);
gst_caps_replace_sink (&mpeg_parse->streaminfo, caps);
g_object_notify (G_OBJECT (mpeg_parse), "streaminfo");
}
#endif
static void
gst_mpeg_parse_reset (GstMPEGParse * mpeg_parse)
{
mpeg_parse->current_scr = 0;
mpeg_parse->bytes_since_scr = 0;
mpeg_parse->avg_bitrate_time = 0;
mpeg_parse->avg_bitrate_bytes = 0;
mpeg_parse->first_scr = MP_INVALID_SCR;
mpeg_parse->first_scr_pos = 0;
mpeg_parse->last_scr = MP_INVALID_SCR;
mpeg_parse->last_scr_pos = 0;
mpeg_parse->scr_rate = 0;
mpeg_parse->adjust = 0;
mpeg_parse->next_scr = 0;
mpeg_parse->mux_rate = 0;
mpeg_parse->discont_pending = FALSE;
mpeg_parse->scr_pending = FALSE;
}
static void
gst_mpeg_parse_handle_discont (GstMPEGParse * mpeg_parse, GstEvent * event)
{
GstClockTime time;
g_return_if_fail (GST_EVENT_TYPE (event) == GST_EVENT_DISCONTINUOUS);
if (GST_EVENT_DISCONT_NEW_MEDIA (event)) {
gst_mpeg_parse_reset (mpeg_parse);
}
ext/a52dec/gsta52dec.c: Add some debug output. Check that a discont has a valid time associated. Original commit message from CVS: * ext/a52dec/gsta52dec.c: (gst_a52dec_push), (gst_a52dec_handle_event), (gst_a52dec_chain): Add some debug output. Check that a discont has a valid time associated. * ext/alsa/gstalsasink.c: (gst_alsa_sink_check_event), (gst_alsa_sink_loop): Ignore TAG events. A little extra debug for broken timestamps. * ext/dvdnav/dvdnavsrc.c: (dvdnavsrc_init), (dvdnavsrc_loop), (dvdnavsrc_change_state): Ensure we send a discont to engage the link before we send any other events. * ext/dvdread/dvdreadsrc.c: (dvdreadsrc_init), (dvdreadsrc_finalize), (_close), (_open), (_seek_title), (_seek_chapter), (seek_sector), (dvdreadsrc_get), (dvdreadsrc_uri_get_uri), (dvdreadsrc_uri_set_uri): Handle URI of the form dvd://title[,chapter[,angle]]. Currently only dvd://title works in totem because typefinding sends a seek that ends up going back to chapter 1 regardless. * ext/mpeg2dec/gstmpeg2dec.c: * ext/mpeg2dec/gstmpeg2dec.h: Output correct timestamps and handle disconts. * ext/ogg/gstoggdemux.c: (get_relative): Small guard against a null dereference. * ext/pango/gsttextoverlay.c: (gst_textoverlay_finalize), (gst_textoverlay_set_property): Free memory when done. Don't call gst_event_filler_get_duration on EOS events. Use GST_LOG and GST_WARNING instead of g_message and g_warning. * ext/smoothwave/gstsmoothwave.c: (gst_smoothwave_init), (draw_line), (gst_smoothwave_dispose), (gst_sw_sinklink), (gst_sw_srclink), (gst_smoothwave_chain): Draw solid lines, prettier colours. * gst/mpeg2sub/gstmpeg2subt.c: (gst_mpeg2subt_init): Add a default palette that'll work for some movies. * gst/mpegstream/gstdvddemux.c: (gst_dvd_demux_init), (gst_dvd_demux_handle_dvd_event), (gst_dvd_demux_send_discont), (gst_dvd_demux_send_subbuffer), (gst_dvd_demux_reset): * gst/mpegstream/gstdvddemux.h: * gst/mpegstream/gstmpegdemux.c: (gst_mpeg_demux_send_discont), (gst_mpeg_demux_parse_syshead), (gst_mpeg_demux_parse_pes): * gst/mpegstream/gstmpegparse.c: (gst_mpeg_parse_init), (gst_mpeg_parse_handle_discont), (gst_mpeg_parse_parse_packhead): * gst/mpegstream/gstmpegparse.h: Use PTM/NAV events when for timestamp adjustment when connected to dvdnavsrc. Don't use many discont events where one suffices. * gst/playback/gstplaybasebin.c: (group_destroy), (gen_preroll_element), (gst_play_base_bin_add_element): * gst/playback/gstplaybasebin.h: Make sure we remove subtitles from the same bin we put them in. * gst/subparse/gstsubparse.c: (convert_encoding), (parse_subrip), (gst_subparse_buffer_format_autodetect), (gst_subparse_change_state): Fix some memleaks and invalid accesses. * gst/typefind/gsttypefindfunctions.c: (ogganx_type_find), (oggskel_type_find), (cmml_type_find), (plugin_init): Some typefind functions for Annodex v3.0 files * gst/wavparse/gstwavparse.h: GstRiffReadClass is the correct parent class.
2005-01-25 15:34:08 +00:00
if (gst_event_discont_get_value (event, GST_FORMAT_TIME, &time)
&& (GST_CLOCK_TIME_IS_VALID (time))) {
GST_DEBUG_OBJECT (mpeg_parse, "forwarding discontinuity, time: %0.3fs",
(double) time / GST_SECOND);
if (CLASS (mpeg_parse)->send_discont)
CLASS (mpeg_parse)->send_discont (mpeg_parse, time);
} else {
/* Use the next SCR to send a discontinuous event. */
mpeg_parse->discont_pending = TRUE;
mpeg_parse->scr_pending = TRUE;
}
mpeg_parse->packetize->resync = TRUE;
gst_event_unref (event);
}
static void
gst_mpeg_parse_send_data (GstMPEGParse * mpeg_parse, GstData * data,
GstClockTime time)
{
if (GST_IS_EVENT (data)) {
GstEvent *event = GST_EVENT (data);
switch (GST_EVENT_TYPE (event)) {
default:
gst_pad_event_default (mpeg_parse->sinkpad, event);
break;
}
} else {
if (!gst_pad_is_negotiated (mpeg_parse->srcpad)) {
gboolean mpeg2 = GST_MPEG_PACKETIZE_IS_MPEG2 (mpeg_parse->packetize);
GstCaps *caps;
caps = gst_caps_new_simple ("video/mpeg",
"mpegversion", G_TYPE_INT, (mpeg2 ? 2 : 1),
"systemstream", G_TYPE_BOOLEAN, TRUE,
"parsed", G_TYPE_BOOLEAN, TRUE, NULL);
if (!gst_pad_set_explicit_caps (mpeg_parse->srcpad, caps)) {
GST_ELEMENT_ERROR (GST_ELEMENT (mpeg_parse),
CORE, NEGOTIATION, (NULL), ("failed to set caps"));
return;
}
}
GST_BUFFER_TIMESTAMP (data) = time;
GST_DEBUG ("current_scr %" G_GINT64_FORMAT, time);
if (GST_PAD_IS_USABLE (mpeg_parse->srcpad))
gst_pad_push (mpeg_parse->srcpad, GST_DATA (data));
else
gst_data_unref (data);
}
}
static void
gst_mpeg_parse_send_discont (GstMPEGParse * mpeg_parse, GstClockTime time)
{
GstEvent *event;
if (GST_PAD_IS_USABLE (mpeg_parse->srcpad)) {
event = gst_event_new_discontinuous (FALSE, GST_FORMAT_TIME, time, NULL);
gst_pad_push (mpeg_parse->srcpad, GST_DATA (event));
}
}
ext/dvdread/dvdreadsrc.c: Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat... Original commit message from CVS: * ext/dvdread/dvdreadsrc.c: (dvdreadsrc_class_init), (dvdreadsrc_init), (dvdreadsrc_dispose), (dvdreadsrc_set_property), (dvdreadsrc_get_property), (_open), (_seek), (_read), (dvdreadsrc_get), (dvdreadsrc_open_file), (dvdreadsrc_change_state): Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat sizeof(dvd) (several GB) before we see something. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_seek): Actually NULL'ify event after using it. * gst/matroska/ebml-read.c: (gst_ebml_read_use_event), (gst_ebml_read_handle_event), (gst_ebml_read_element_id), (gst_ebml_read_element_length), (gst_ebml_read_element_data), (gst_ebml_read_seek), (gst_ebml_read_skip): Handle events. * gst/mpegstream/gstdvddemux.c: (gst_dvd_demux_base_init), (gst_dvd_demux_init), (gst_dvd_demux_get_audio_stream), (gst_dvd_demux_get_subpicture_stream), (gst_dvd_demux_plugin_init): Fix timing (this will probably break if I seek using menus, but I didn't get there yet). VOBs and normal DVDs should now work. Add a mpeg2-only pad with high rank so this get autoplugged for MPEG-2 movies. * gst/mpegstream/gstmpegdemux.c: (gst_mpeg_demux_base_init), (gst_mpeg_demux_class_init), (gst_mpeg_demux_init), (gst_mpeg_demux_new_output_pad), (gst_mpeg_demux_get_video_stream), (gst_mpeg_demux_get_audio_stream), (gst_mpeg_demux_get_private_stream), (gst_mpeg_demux_parse_packet), (gst_mpeg_demux_parse_pes), (gst_mpeg_demux_plugin_init): Use this as second rank for MPEG-1 and MPEG-2. Still use this for MPEG-1 but use dvddemux for MPEG-2. * gst/mpegstream/gstmpegparse.c: (gst_mpeg_parse_class_init), (gst_mpeg_parse_init), (gst_mpeg_parse_new_pad), (gst_mpeg_parse_parse_packhead): Timing. Only add pad template if it exists. Add sink template from class and not from ourselves. This means we will always use the correct sink template even if it is not the one defined in this file.
2004-10-01 08:42:56 +00:00
static void
gst_mpeg_parse_new_pad (GstElement * element, GstPad * pad)
{
GstMPEGParse *mpeg_parse;
if (GST_PAD_IS_SINK (pad))
return;
mpeg_parse = GST_MPEG_PARSE (element);
/* For each new added pad, send a discont so it knows about the current
* time. This is required because MPEG allows any sort of order of
* packets, including setting base time before defining streams or
* even adding streams halfway a stream. */
if (!mpeg_parse->scr_pending) {
ext/dvdread/dvdreadsrc.c: Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat... Original commit message from CVS: * ext/dvdread/dvdreadsrc.c: (dvdreadsrc_class_init), (dvdreadsrc_init), (dvdreadsrc_dispose), (dvdreadsrc_set_property), (dvdreadsrc_get_property), (_open), (_seek), (_read), (dvdreadsrc_get), (dvdreadsrc_open_file), (dvdreadsrc_change_state): Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat sizeof(dvd) (several GB) before we see something. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_seek): Actually NULL'ify event after using it. * gst/matroska/ebml-read.c: (gst_ebml_read_use_event), (gst_ebml_read_handle_event), (gst_ebml_read_element_id), (gst_ebml_read_element_length), (gst_ebml_read_element_data), (gst_ebml_read_seek), (gst_ebml_read_skip): Handle events. * gst/mpegstream/gstdvddemux.c: (gst_dvd_demux_base_init), (gst_dvd_demux_init), (gst_dvd_demux_get_audio_stream), (gst_dvd_demux_get_subpicture_stream), (gst_dvd_demux_plugin_init): Fix timing (this will probably break if I seek using menus, but I didn't get there yet). VOBs and normal DVDs should now work. Add a mpeg2-only pad with high rank so this get autoplugged for MPEG-2 movies. * gst/mpegstream/gstmpegdemux.c: (gst_mpeg_demux_base_init), (gst_mpeg_demux_class_init), (gst_mpeg_demux_init), (gst_mpeg_demux_new_output_pad), (gst_mpeg_demux_get_video_stream), (gst_mpeg_demux_get_audio_stream), (gst_mpeg_demux_get_private_stream), (gst_mpeg_demux_parse_packet), (gst_mpeg_demux_parse_pes), (gst_mpeg_demux_plugin_init): Use this as second rank for MPEG-1 and MPEG-2. Still use this for MPEG-1 but use dvddemux for MPEG-2. * gst/mpegstream/gstmpegparse.c: (gst_mpeg_parse_class_init), (gst_mpeg_parse_init), (gst_mpeg_parse_new_pad), (gst_mpeg_parse_parse_packhead): Timing. Only add pad template if it exists. Add sink template from class and not from ourselves. This means we will always use the correct sink template even if it is not the one defined in this file.
2004-10-01 08:42:56 +00:00
GstEvent *event = gst_event_new_discontinuous (FALSE,
GST_FORMAT_TIME,
(guint64) MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr +
mpeg_parse->adjust),
GST_FORMAT_UNDEFINED);
gst_pad_push (pad, GST_DATA (event));
}
}
static gboolean
gst_mpeg_parse_parse_packhead (GstMPEGParse * mpeg_parse, GstBuffer * buffer)
{
guint8 *buf;
guint64 prev_scr, scr;
guint32 scr1, scr2;
guint32 new_rate;
buf = GST_BUFFER_DATA (buffer);
buf += 4;
scr1 = GUINT32_FROM_BE (*(guint32 *) buf);
scr2 = GUINT32_FROM_BE (*(guint32 *) (buf + 4));
if (GST_MPEG_PACKETIZE_IS_MPEG2 (mpeg_parse->packetize)) {
guint32 scr_ext;
/* :2=01 ! scr:3 ! marker:1==1 ! scr:15 ! marker:1==1 ! scr:15 */
scr = ((guint64) scr1 & 0x38000000) << 3;
scr |= ((guint64) scr1 & 0x03fff800) << 4;
scr |= ((guint64) scr1 & 0x000003ff) << 5;
scr |= ((guint64) scr2 & 0xf8000000) >> 27;
scr_ext = (scr2 & 0x03fe0000) >> 17;
scr = (scr * 300 + scr_ext % 300) / 300;
GST_LOG_OBJECT (mpeg_parse, "%" G_GINT64_FORMAT " %d, %08x %08x %"
G_GINT64_FORMAT " diff: %" G_GINT64_FORMAT,
scr, scr_ext, scr1, scr2, mpeg_parse->bytes_since_scr,
scr - mpeg_parse->current_scr);
buf += 6;
new_rate = (GUINT32_FROM_BE ((*(guint32 *) buf)) & 0xfffffc00) >> 10;
} else {
scr = ((guint64) scr1 & 0x0e000000) << 5;
scr |= ((guint64) scr1 & 0x00fffe00) << 6;
scr |= ((guint64) scr1 & 0x000000ff) << 7;
scr |= ((guint64) scr2 & 0xfe000000) >> 25;
buf += 5;
/* we do this byte by byte because buf[3] might be outside of buf's
* memory space */
new_rate = ((gint32) buf[0] & 0x7f) << 15;
new_rate |= ((gint32) buf[1]) << 7;
new_rate |= buf[2] >> 1;
}
new_rate *= MP_MUX_RATE_MULT;
prev_scr = mpeg_parse->current_scr;
mpeg_parse->current_scr = scr;
mpeg_parse->scr_pending = FALSE;
if (mpeg_parse->next_scr == MP_INVALID_SCR) {
mpeg_parse->next_scr = mpeg_parse->current_scr;
}
if ((mpeg_parse->first_scr == MP_INVALID_SCR) ||
(mpeg_parse->current_scr < mpeg_parse->first_scr)) {
mpeg_parse->first_scr = mpeg_parse->current_scr;
mpeg_parse->first_scr_pos = gst_bytestream_tell (mpeg_parse->packetize->bs);
}
if ((mpeg_parse->last_scr == MP_INVALID_SCR) ||
(mpeg_parse->current_scr > mpeg_parse->last_scr)) {
mpeg_parse->last_scr = mpeg_parse->current_scr;
mpeg_parse->last_scr_pos = gst_bytestream_tell (mpeg_parse->packetize->bs);
}
GST_LOG_OBJECT (mpeg_parse,
"SCR is %" G_GUINT64_FORMAT
" (%" G_GUINT64_FORMAT ") next: %"
G_GINT64_FORMAT " (%" G_GINT64_FORMAT
") diff: %" G_GINT64_FORMAT " (%"
G_GINT64_FORMAT ")",
mpeg_parse->current_scr,
MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr),
mpeg_parse->next_scr,
MPEGTIME_TO_GSTTIME (mpeg_parse->next_scr),
mpeg_parse->current_scr - mpeg_parse->next_scr,
MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr) -
MPEGTIME_TO_GSTTIME (mpeg_parse->next_scr));
if (ABS ((gint64) mpeg_parse->next_scr - (gint64) (scr)) >
mpeg_parse->max_discont) {
GST_DEBUG ("discontinuity detected; expected: %" G_GUINT64_FORMAT " got: %"
G_GUINT64_FORMAT " adjusted:%" G_GINT64_FORMAT " adjust:%"
G_GINT64_FORMAT, mpeg_parse->next_scr, mpeg_parse->current_scr,
mpeg_parse->current_scr + mpeg_parse->adjust, mpeg_parse->adjust);
ext/dvdread/dvdreadsrc.c: Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat... Original commit message from CVS: * ext/dvdread/dvdreadsrc.c: (dvdreadsrc_class_init), (dvdreadsrc_init), (dvdreadsrc_dispose), (dvdreadsrc_set_property), (dvdreadsrc_get_property), (_open), (_seek), (_read), (dvdreadsrc_get), (dvdreadsrc_open_file), (dvdreadsrc_change_state): Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat sizeof(dvd) (several GB) before we see something. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_seek): Actually NULL'ify event after using it. * gst/matroska/ebml-read.c: (gst_ebml_read_use_event), (gst_ebml_read_handle_event), (gst_ebml_read_element_id), (gst_ebml_read_element_length), (gst_ebml_read_element_data), (gst_ebml_read_seek), (gst_ebml_read_skip): Handle events. * gst/mpegstream/gstdvddemux.c: (gst_dvd_demux_base_init), (gst_dvd_demux_init), (gst_dvd_demux_get_audio_stream), (gst_dvd_demux_get_subpicture_stream), (gst_dvd_demux_plugin_init): Fix timing (this will probably break if I seek using menus, but I didn't get there yet). VOBs and normal DVDs should now work. Add a mpeg2-only pad with high rank so this get autoplugged for MPEG-2 movies. * gst/mpegstream/gstmpegdemux.c: (gst_mpeg_demux_base_init), (gst_mpeg_demux_class_init), (gst_mpeg_demux_init), (gst_mpeg_demux_new_output_pad), (gst_mpeg_demux_get_video_stream), (gst_mpeg_demux_get_audio_stream), (gst_mpeg_demux_get_private_stream), (gst_mpeg_demux_parse_packet), (gst_mpeg_demux_parse_pes), (gst_mpeg_demux_plugin_init): Use this as second rank for MPEG-1 and MPEG-2. Still use this for MPEG-1 but use dvddemux for MPEG-2. * gst/mpegstream/gstmpegparse.c: (gst_mpeg_parse_class_init), (gst_mpeg_parse_init), (gst_mpeg_parse_new_pad), (gst_mpeg_parse_parse_packhead): Timing. Only add pad template if it exists. Add sink template from class and not from ourselves. This means we will always use the correct sink template even if it is not the one defined in this file.
2004-10-01 08:42:56 +00:00
if (mpeg_parse->do_adjust) {
ext/a52dec/gsta52dec.c: Add some debug output. Check that a discont has a valid time associated. Original commit message from CVS: * ext/a52dec/gsta52dec.c: (gst_a52dec_push), (gst_a52dec_handle_event), (gst_a52dec_chain): Add some debug output. Check that a discont has a valid time associated. * ext/alsa/gstalsasink.c: (gst_alsa_sink_check_event), (gst_alsa_sink_loop): Ignore TAG events. A little extra debug for broken timestamps. * ext/dvdnav/dvdnavsrc.c: (dvdnavsrc_init), (dvdnavsrc_loop), (dvdnavsrc_change_state): Ensure we send a discont to engage the link before we send any other events. * ext/dvdread/dvdreadsrc.c: (dvdreadsrc_init), (dvdreadsrc_finalize), (_close), (_open), (_seek_title), (_seek_chapter), (seek_sector), (dvdreadsrc_get), (dvdreadsrc_uri_get_uri), (dvdreadsrc_uri_set_uri): Handle URI of the form dvd://title[,chapter[,angle]]. Currently only dvd://title works in totem because typefinding sends a seek that ends up going back to chapter 1 regardless. * ext/mpeg2dec/gstmpeg2dec.c: * ext/mpeg2dec/gstmpeg2dec.h: Output correct timestamps and handle disconts. * ext/ogg/gstoggdemux.c: (get_relative): Small guard against a null dereference. * ext/pango/gsttextoverlay.c: (gst_textoverlay_finalize), (gst_textoverlay_set_property): Free memory when done. Don't call gst_event_filler_get_duration on EOS events. Use GST_LOG and GST_WARNING instead of g_message and g_warning. * ext/smoothwave/gstsmoothwave.c: (gst_smoothwave_init), (draw_line), (gst_smoothwave_dispose), (gst_sw_sinklink), (gst_sw_srclink), (gst_smoothwave_chain): Draw solid lines, prettier colours. * gst/mpeg2sub/gstmpeg2subt.c: (gst_mpeg2subt_init): Add a default palette that'll work for some movies. * gst/mpegstream/gstdvddemux.c: (gst_dvd_demux_init), (gst_dvd_demux_handle_dvd_event), (gst_dvd_demux_send_discont), (gst_dvd_demux_send_subbuffer), (gst_dvd_demux_reset): * gst/mpegstream/gstdvddemux.h: * gst/mpegstream/gstmpegdemux.c: (gst_mpeg_demux_send_discont), (gst_mpeg_demux_parse_syshead), (gst_mpeg_demux_parse_pes): * gst/mpegstream/gstmpegparse.c: (gst_mpeg_parse_init), (gst_mpeg_parse_handle_discont), (gst_mpeg_parse_parse_packhead): * gst/mpegstream/gstmpegparse.h: Use PTM/NAV events when for timestamp adjustment when connected to dvdnavsrc. Don't use many discont events where one suffices. * gst/playback/gstplaybasebin.c: (group_destroy), (gen_preroll_element), (gst_play_base_bin_add_element): * gst/playback/gstplaybasebin.h: Make sure we remove subtitles from the same bin we put them in. * gst/subparse/gstsubparse.c: (convert_encoding), (parse_subrip), (gst_subparse_buffer_format_autodetect), (gst_subparse_change_state): Fix some memleaks and invalid accesses. * gst/typefind/gsttypefindfunctions.c: (ogganx_type_find), (oggskel_type_find), (cmml_type_find), (plugin_init): Some typefind functions for Annodex v3.0 files * gst/wavparse/gstwavparse.h: GstRiffReadClass is the correct parent class.
2005-01-25 15:34:08 +00:00
if (mpeg_parse->use_adjust) {
mpeg_parse->adjust +=
(gint64) mpeg_parse->next_scr - (gint64) mpeg_parse->current_scr;
GST_DEBUG ("new adjust: %" G_GINT64_FORMAT, mpeg_parse->adjust);
}
ext/dvdread/dvdreadsrc.c: Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat... Original commit message from CVS: * ext/dvdread/dvdreadsrc.c: (dvdreadsrc_class_init), (dvdreadsrc_init), (dvdreadsrc_dispose), (dvdreadsrc_set_property), (dvdreadsrc_get_property), (_open), (_seek), (_read), (dvdreadsrc_get), (dvdreadsrc_open_file), (dvdreadsrc_change_state): Fix. Don't do one big huge loop around the whole DVD, that will cache all data and thus eat sizeof(dvd) (several GB) before we see something. * gst-libs/gst/riff/riff-read.c: (gst_riff_read_seek): Actually NULL'ify event after using it. * gst/matroska/ebml-read.c: (gst_ebml_read_use_event), (gst_ebml_read_handle_event), (gst_ebml_read_element_id), (gst_ebml_read_element_length), (gst_ebml_read_element_data), (gst_ebml_read_seek), (gst_ebml_read_skip): Handle events. * gst/mpegstream/gstdvddemux.c: (gst_dvd_demux_base_init), (gst_dvd_demux_init), (gst_dvd_demux_get_audio_stream), (gst_dvd_demux_get_subpicture_stream), (gst_dvd_demux_plugin_init): Fix timing (this will probably break if I seek using menus, but I didn't get there yet). VOBs and normal DVDs should now work. Add a mpeg2-only pad with high rank so this get autoplugged for MPEG-2 movies. * gst/mpegstream/gstmpegdemux.c: (gst_mpeg_demux_base_init), (gst_mpeg_demux_class_init), (gst_mpeg_demux_init), (gst_mpeg_demux_new_output_pad), (gst_mpeg_demux_get_video_stream), (gst_mpeg_demux_get_audio_stream), (gst_mpeg_demux_get_private_stream), (gst_mpeg_demux_parse_packet), (gst_mpeg_demux_parse_pes), (gst_mpeg_demux_plugin_init): Use this as second rank for MPEG-1 and MPEG-2. Still use this for MPEG-1 but use dvddemux for MPEG-2. * gst/mpegstream/gstmpegparse.c: (gst_mpeg_parse_class_init), (gst_mpeg_parse_init), (gst_mpeg_parse_new_pad), (gst_mpeg_parse_parse_packhead): Timing. Only add pad template if it exists. Add sink template from class and not from ourselves. This means we will always use the correct sink template even if it is not the one defined in this file.
2004-10-01 08:42:56 +00:00
} else {
mpeg_parse->discont_pending = TRUE;
}
}
if (mpeg_parse->index && GST_INDEX_IS_WRITABLE (mpeg_parse->index)) {
/* update index if any */
gst_index_add_association (mpeg_parse->index, mpeg_parse->index_id,
GST_ASSOCIATION_FLAG_KEY_UNIT,
GST_FORMAT_BYTES, GST_BUFFER_OFFSET (buffer),
GST_FORMAT_TIME, MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr), 0);
}
if (mpeg_parse->current_scr > prev_scr) {
mpeg_parse->avg_bitrate_time +=
MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr - prev_scr);
mpeg_parse->avg_bitrate_bytes += mpeg_parse->bytes_since_scr;
}
if (mpeg_parse->mux_rate != new_rate) {
if (GST_MPEG_PACKETIZE_IS_MPEG2 (mpeg_parse->packetize)) {
mpeg_parse->mux_rate = new_rate;
} else if (mpeg_parse->avg_bitrate_bytes > MP_MIN_VALID_BSS) {
mpeg_parse->mux_rate =
GST_SECOND * mpeg_parse->avg_bitrate_bytes /
mpeg_parse->avg_bitrate_time;
}
//gst_mpeg_parse_update_streaminfo (mpeg_parse);
GST_DEBUG ("stream current is %1.3fMbs, calculated over %1.3fkB",
(mpeg_parse->mux_rate * 8) / 1048576.0,
mpeg_parse->bytes_since_scr / 1024.0);
}
if (mpeg_parse->avg_bitrate_bytes > MP_MAX_VALID_BSS) {
mpeg_parse->avg_bitrate_bytes = 0;
mpeg_parse->avg_bitrate_time = 0;
}
mpeg_parse->bytes_since_scr = 0;
if (mpeg_parse->avg_bitrate_bytes) {
GST_DEBUG ("stream avg is %1.3fMbs, calculated over %1.3fkB",
(float) (mpeg_parse->avg_bitrate_bytes) * 8 * GST_SECOND
/ mpeg_parse->avg_bitrate_time / 1048576.0,
mpeg_parse->avg_bitrate_bytes / 1024.0);
}
return TRUE;
}
static void
gst_mpeg_parse_loop (GstElement * element)
{
GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (element);
GstData *data;
guint id;
gboolean mpeg2;
GstClockTime time;
data = gst_mpeg_packetize_read (mpeg_parse->packetize);
if (!data)
return;
id = GST_MPEG_PACKETIZE_ID (mpeg_parse->packetize);
mpeg2 = GST_MPEG_PACKETIZE_IS_MPEG2 (mpeg_parse->packetize);
if (GST_IS_BUFFER (data)) {
GstBuffer *buffer = GST_BUFFER (data);
GST_LOG_OBJECT (mpeg_parse, "have chunk 0x%02X", id);
switch (id) {
case 0xb9:
break;
case 0xba:
if (CLASS (mpeg_parse)->parse_packhead) {
CLASS (mpeg_parse)->parse_packhead (mpeg_parse, buffer);
}
break;
case 0xbb:
if (CLASS (mpeg_parse)->parse_syshead) {
CLASS (mpeg_parse)->parse_syshead (mpeg_parse, buffer);
}
break;
default:
if (mpeg2 && ((id < 0xBD) || (id > 0xFE))) {
g_warning ("******** unknown id 0x%02X", id);
} else {
if (mpeg2) {
if (CLASS (mpeg_parse)->parse_pes) {
CLASS (mpeg_parse)->parse_pes (mpeg_parse, buffer);
}
} else {
if (CLASS (mpeg_parse)->parse_packet) {
CLASS (mpeg_parse)->parse_packet (mpeg_parse, buffer);
}
}
}
}
}
time = MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr);
if (GST_IS_EVENT (data)) {
GstEvent *event = GST_EVENT (data);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_DISCONTINUOUS:
if (CLASS (mpeg_parse)->handle_discont)
CLASS (mpeg_parse)->handle_discont (mpeg_parse, event);
return;
default:
break;
}
if (CLASS (mpeg_parse)->send_data)
CLASS (mpeg_parse)->send_data (mpeg_parse, data, time);
else
gst_event_unref (event);
} else {
guint64 size;
/* we're not sending data as long as no new SCR was found */
if (mpeg_parse->discont_pending) {
if (!mpeg_parse->scr_pending) {
if (mpeg_parse->clock && mpeg_parse->sync) {
gst_element_set_time (GST_ELEMENT (mpeg_parse),
MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr));
}
if (CLASS (mpeg_parse)->send_discont) {
CLASS (mpeg_parse)->send_discont (mpeg_parse,
MPEGTIME_TO_GSTTIME (mpeg_parse->current_scr +
mpeg_parse->adjust));
}
mpeg_parse->discont_pending = FALSE;
} else {
GST_DEBUG ("waiting for SCR");
gst_buffer_unref (GST_BUFFER (data));
return;
}
}
size = GST_BUFFER_SIZE (data);
mpeg_parse->bytes_since_scr += size;
if (!GST_PAD_CAPS (mpeg_parse->sinkpad)) {
gboolean mpeg2 = GST_MPEG_PACKETIZE_IS_MPEG2 (mpeg_parse->packetize);
if (gst_pad_try_set_caps (mpeg_parse->sinkpad,
gst_caps_new_simple ("video/mpeg",
"mpegversion", G_TYPE_INT, (mpeg2 ? 2 : 1),
"systemstream", G_TYPE_BOOLEAN, TRUE,
"parsed", G_TYPE_BOOLEAN, TRUE, NULL)) < 0) {
GST_ELEMENT_ERROR (mpeg_parse, CORE, NEGOTIATION, (NULL), (NULL));
return;
}
}
if (CLASS (mpeg_parse)->send_data)
CLASS (mpeg_parse)->send_data (mpeg_parse, data, time);
if (mpeg_parse->clock && mpeg_parse->sync && !mpeg_parse->discont_pending) {
GST_DEBUG ("syncing mpegparse");
gst_element_wait (GST_ELEMENT (mpeg_parse), time);
}
if (mpeg_parse->current_scr != MP_INVALID_SCR) {
guint64 scr, bss, br;
scr = mpeg_parse->current_scr;
bss = mpeg_parse->bytes_since_scr;
br = mpeg_parse->mux_rate;
if (br) {
if (GST_MPEG_PACKETIZE_IS_MPEG2 (mpeg_parse->packetize)) {
/*
* The mpeg spec says something like this, but that doesn't really work:
*
* mpeg_parse->next_scr = (scr * br + bss * CLOCK_FREQ) / (CLOCK_FREQ + br);
*/
mpeg_parse->next_scr = scr + (bss * CLOCK_FREQ) / br;
} else {
/* we are interpolating the scr here */
mpeg_parse->next_scr = scr + (bss * CLOCK_FREQ) / br;
}
} else {
/* no bitrate known */
mpeg_parse->next_scr = scr;
}
GST_LOG_OBJECT (mpeg_parse, "size: %" G_GINT64_FORMAT
", total since SCR: %" G_GINT64_FORMAT ", br: %" G_GINT64_FORMAT
", next SCR: %" G_GINT64_FORMAT, size, bss, br, mpeg_parse->next_scr);
}
}
}
const GstFormat *
gst_mpeg_parse_get_src_formats (GstPad * pad)
{
static const GstFormat formats[] = {
GST_FORMAT_BYTES,
GST_FORMAT_TIME,
0
};
return formats;
}
/*
* Return the bitrate to the nearest byte/sec
*/
static gboolean
gst_mpeg_parse_get_rate (GstMPEGParse * mpeg_parse, gint64 * rate)
{
GstFormat time_format = GST_FORMAT_TIME;
GstFormat bytes_format = GST_FORMAT_BYTES;
gint64 total_time = 0;
gint64 total_bytes = 0;
/* If upstream knows the total time and the total bytes,
* use those to compute an average byterate
*/
if (gst_pad_query (GST_PAD_PEER (mpeg_parse->sinkpad),
GST_QUERY_TOTAL, &time_format, &total_time)
&&
gst_pad_query (GST_PAD_PEER (mpeg_parse->sinkpad),
GST_QUERY_TOTAL, &bytes_format, &total_bytes)
&& total_time != 0) {
*rate = GST_SECOND * total_bytes / total_time;
return TRUE;
}
if ((mpeg_parse->first_scr != MP_INVALID_SCR) &&
(mpeg_parse->last_scr != MP_INVALID_SCR) &&
(mpeg_parse->last_scr_pos - mpeg_parse->first_scr_pos > MP_MIN_VALID_BSS)
&& (mpeg_parse->last_scr != mpeg_parse->first_scr)) {
*rate =
GST_SECOND * (mpeg_parse->last_scr_pos -
mpeg_parse->first_scr_pos) / MPEGTIME_TO_GSTTIME (mpeg_parse->last_scr -
mpeg_parse->first_scr);
if (*rate != 0) {
/*
* check if we need to update scr_rate
*/
if ((mpeg_parse->scr_rate == 0) ||
(((double) (ABS (mpeg_parse->scr_rate -
*rate)) / mpeg_parse->scr_rate)
>= MP_SCR_RATE_HYST)) {
mpeg_parse->scr_rate = *rate;
return TRUE;
}
}
if (mpeg_parse->scr_rate != 0) {
*rate = mpeg_parse->scr_rate;
return TRUE;
}
}
if (mpeg_parse->avg_bitrate_time != 0 && mpeg_parse->avg_bitrate_bytes != 0) {
*rate =
GST_SECOND * mpeg_parse->avg_bitrate_bytes /
mpeg_parse->avg_bitrate_time;
if (*rate != 0) {
return TRUE;
}
}
if (mpeg_parse->mux_rate != 0) {
*rate = mpeg_parse->mux_rate;
return TRUE;
}
return FALSE;
}
gboolean
gst_mpeg_parse_convert_src (GstPad * pad, GstFormat src_format,
gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
{
gboolean res = TRUE;
GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (gst_pad_get_parent (pad));
gint64 rate;
switch (src_format) {
case GST_FORMAT_BYTES:
switch (*dest_format) {
case GST_FORMAT_DEFAULT:
*dest_format = GST_FORMAT_TIME;
case GST_FORMAT_TIME:
if (!gst_mpeg_parse_get_rate (mpeg_parse, &rate))
res = FALSE;
else {
*dest_value = GST_SECOND * src_value / rate;
}
break;
default:
res = FALSE;
}
break;
case GST_FORMAT_TIME:
switch (*dest_format) {
case GST_FORMAT_DEFAULT:
*dest_format = GST_FORMAT_BYTES;
case GST_FORMAT_BYTES:
if (!gst_mpeg_parse_get_rate (mpeg_parse, &rate))
res = FALSE;
else {
*dest_value = src_value * rate / GST_SECOND;
}
break;
default:
res = FALSE;
}
break;
default:
res = FALSE;
break;
}
return res;
}
const GstQueryType *
gst_mpeg_parse_get_src_query_types (GstPad * pad)
{
static const GstQueryType types[] = {
GST_QUERY_TOTAL,
GST_QUERY_POSITION,
0
};
return types;
}
gboolean
gst_mpeg_parse_handle_src_query (GstPad * pad, GstQueryType type,
GstFormat * format, gint64 * value)
{
gboolean res = TRUE;
GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (gst_pad_get_parent (pad));
GstFormat src_format = GST_FORMAT_UNDEFINED;
gint64 src_value = 0;
switch (type) {
case GST_QUERY_TOTAL:
{
switch (*format) {
case GST_FORMAT_DEFAULT:
*format = GST_FORMAT_TIME;
/* fallthrough */
case GST_FORMAT_TIME:
/*
* Try asking upstream if it knows the time - a DVD might know
*/
src_format = GST_FORMAT_TIME;
if (gst_pad_query (GST_PAD_PEER (mpeg_parse->sinkpad),
GST_QUERY_TOTAL, &src_format, &src_value)) {
res = TRUE;
break;
}
/* Otherwise fallthrough */
default:
src_format = GST_FORMAT_BYTES;
if (!gst_pad_query (GST_PAD_PEER (mpeg_parse->sinkpad),
GST_QUERY_TOTAL, &src_format, &src_value)) {
res = FALSE;
}
break;
}
break;
}
case GST_QUERY_POSITION:
{
switch (*format) {
case GST_FORMAT_DEFAULT:
*format = GST_FORMAT_TIME;
/* fallthrough */
default:
src_format = GST_FORMAT_TIME;
if ((mpeg_parse->current_scr == MP_INVALID_SCR) ||
(mpeg_parse->first_scr == MP_INVALID_SCR))
res = FALSE;
else {
gint64 cur =
(gint64) (mpeg_parse->current_scr) - mpeg_parse->first_scr;
src_value = MPEGTIME_TO_GSTTIME (MAX (0, cur));
}
break;
}
break;
}
default:
res = FALSE;
break;
}
/* bring to requested format */
if (res)
res = gst_pad_convert (pad, src_format, src_value, format, value);
return res;
}
const GstEventMask *
gst_mpeg_parse_get_src_event_masks (GstPad * pad)
{
static const GstEventMask masks[] = {
{GST_EVENT_SEEK, GST_SEEK_METHOD_SET | GST_SEEK_FLAG_FLUSH},
{0,}
};
return masks;
}
static gboolean
index_seek (GstPad * pad, GstEvent * event, guint64 * offset, gint64 * scr)
{
GstIndexEntry *entry;
GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (gst_pad_get_parent (pad));
entry = gst_index_get_assoc_entry (mpeg_parse->index, mpeg_parse->index_id,
GST_INDEX_LOOKUP_BEFORE, 0,
GST_EVENT_SEEK_FORMAT (event), GST_EVENT_SEEK_OFFSET (event));
if (!entry)
return FALSE;
if (gst_index_entry_assoc_map (entry, GST_FORMAT_BYTES, offset)) {
gint64 time;
if (gst_index_entry_assoc_map (entry, GST_FORMAT_TIME, &time)) {
*scr = GSTTIME_TO_MPEGTIME (time);
}
GST_CAT_DEBUG (GST_CAT_SEEK, "%s:%s index %s %" G_GINT64_FORMAT
" -> %" G_GINT64_FORMAT " bytes, scr=%" G_GINT64_FORMAT,
GST_DEBUG_PAD_NAME (pad),
gst_format_get_details (GST_EVENT_SEEK_FORMAT (event))->nick,
GST_EVENT_SEEK_OFFSET (event), *offset, *scr);
return TRUE;
}
return FALSE;
}
static gboolean
normal_seek (GstPad * pad, GstEvent * event, guint64 * offset, gint64 * scr)
{
gboolean res;
GstFormat format;
gint64 time;
/* bring offset to bytes */
format = GST_FORMAT_BYTES;
res = gst_pad_convert (pad,
GST_EVENT_SEEK_FORMAT (event),
GST_EVENT_SEEK_OFFSET (event), &format, offset);
/* bring offset to time */
format = GST_FORMAT_TIME;
res &= gst_pad_convert (pad,
GST_EVENT_SEEK_FORMAT (event),
GST_EVENT_SEEK_OFFSET (event), &format, &time);
/* convert to scr */
*scr = GSTTIME_TO_MPEGTIME (time);
return res;
}
gboolean
gst_mpeg_parse_handle_src_event (GstPad * pad, GstEvent * event)
{
gboolean res = FALSE;
GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (gst_pad_get_parent (pad));
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_SEEK:
{
guint64 desired_offset;
guint64 expected_scr;
/* first to to use the index if we have one */
if (mpeg_parse->index)
res = index_seek (pad, event, &desired_offset, &expected_scr);
/* nothing found, try fuzzy seek */
if (!res)
res = normal_seek (pad, event, &desired_offset, &expected_scr);
if (!res)
break;
GST_DEBUG ("sending seek to %" G_GINT64_FORMAT " expected SCR: %"
G_GUINT64_FORMAT " (%" G_GUINT64_FORMAT ")", desired_offset,
expected_scr, MPEGTIME_TO_GSTTIME (expected_scr));
if (gst_bytestream_seek (mpeg_parse->packetize->bs, desired_offset,
GST_SEEK_METHOD_SET)) {
mpeg_parse->discont_pending = TRUE;
mpeg_parse->scr_pending = TRUE;
mpeg_parse->next_scr = expected_scr;
mpeg_parse->current_scr = MP_INVALID_SCR;
mpeg_parse->adjust = 0;
res = TRUE;
}
break;
}
default:
break;
}
gst_event_unref (event);
return res;
}
static GstStateChangeReturn
gst_mpeg_parse_change_state (GstElement * element, GstStateChange transition)
{
GstMPEGParse *mpeg_parse = GST_MPEG_PARSE (element);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
if (!mpeg_parse->packetize) {
mpeg_parse->packetize =
gst_mpeg_packetize_new (mpeg_parse->sinkpad,
GST_MPEG_PACKETIZE_SYSTEM);
}
/* initialize parser state */
gst_mpeg_parse_reset (mpeg_parse);
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
if (mpeg_parse->packetize) {
gst_mpeg_packetize_destroy (mpeg_parse->packetize);
mpeg_parse->packetize = NULL;
}
//gst_caps_replace (&mpeg_parse->streaminfo, NULL);
break;
default:
break;
}
return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
}
static void
gst_mpeg_parse_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstMPEGParse *mpeg_parse;
mpeg_parse = GST_MPEG_PARSE (object);
switch (prop_id) {
case ARG_SYNC:
g_value_set_boolean (value, mpeg_parse->sync);
break;
case ARG_MAX_DISCONT:
g_value_set_int (value, mpeg_parse->max_discont);
break;
case ARG_DO_ADJUST:
g_value_set_boolean (value, mpeg_parse->do_adjust);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_mpeg_parse_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstMPEGParse *mpeg_parse;
mpeg_parse = GST_MPEG_PARSE (object);
switch (prop_id) {
case ARG_SYNC:
mpeg_parse->sync = g_value_get_boolean (value);
break;
case ARG_MAX_DISCONT:
mpeg_parse->max_discont = g_value_get_int (value);
break;
case ARG_DO_ADJUST:
mpeg_parse->do_adjust = g_value_get_boolean (value);
mpeg_parse->adjust = 0;
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_mpeg_parse_set_index (GstElement * element, GstIndex * index)
{
GstMPEGParse *mpeg_parse;
mpeg_parse = GST_MPEG_PARSE (element);
mpeg_parse->index = index;
gst_index_get_writer_id (index, GST_OBJECT (mpeg_parse->sinkpad),
&mpeg_parse->index_id);
gst_index_add_format (index, mpeg_parse->index_id, scr_format);
}
static GstIndex *
gst_mpeg_parse_get_index (GstElement * element)
{
GstMPEGParse *mpeg_parse;
mpeg_parse = GST_MPEG_PARSE (element);
return mpeg_parse->index;
}
static gboolean
gst_mpeg_parse_release_locks (GstElement * element)
{
GstMPEGParse *mpeg_parse;
mpeg_parse = GST_MPEG_PARSE (element);
if (mpeg_parse->id) {
/* FIXME */
//gst_clock_id_unlock (mpeg_parse->id);
}
return TRUE;
}
gboolean
gst_mpeg_parse_plugin_init (GstPlugin * plugin)
{
scr_format =
gst_format_register ("scr", "The MPEG system clock reference time");
return gst_element_register (plugin, "mpegparse",
GST_RANK_NONE, GST_TYPE_MPEG_PARSE);
}