diff --git a/common b/common index 605cd9a65e..a39eb835fb 160000 --- a/common +++ b/common @@ -1 +1 @@ -Subproject commit 605cd9a65ed61505f24b840d3fe8e252be72b151 +Subproject commit a39eb835fb3be2a4c5a6a89b5ca5cc064e79b2e2 diff --git a/configure.ac b/configure.ac index 44dc48a805..dda50d4c9c 100644 --- a/configure.ac +++ b/configure.ac @@ -1963,6 +1963,7 @@ gst-libs/gst/Makefile gst-libs/gst/basecamerabinsrc/Makefile gst-libs/gst/interfaces/Makefile gst-libs/gst/signalprocessor/Makefile +gst-libs/gst/codecparsers/Makefile gst-libs/gst/video/Makefile sys/Makefile sys/dshowdecwrapper/Makefile @@ -2058,10 +2059,13 @@ ext/zbar/Makefile po/Makefile.in docs/Makefile docs/plugins/Makefile +docs/libs/Makefile docs/version.entities pkgconfig/Makefile pkgconfig/gstreamer-plugins-bad.pc pkgconfig/gstreamer-plugins-bad-uninstalled.pc +pkgconfig/gstreamer-codecparsers.pc +pkgconfig/gstreamer-codecparsers-uninstalled.pc tools/Makefile m4/Makefile win32/common/config.h diff --git a/docs/Makefile.am b/docs/Makefile.am index 7af47d43f1..a99c90f362 100644 --- a/docs/Makefile.am +++ b/docs/Makefile.am @@ -10,8 +10,8 @@ else GTK_DOC_DIRS = endif -SUBDIRS = $(GTK_DOC_DIRS) -DIST_SUBDIRS = plugins +SUBDIRS = libs $(GTK_DOC_DIRS) +DIST_SUBDIRS = libs plugins EXTRA_DIST = \ random/ChangeLog-0.8 \ diff --git a/docs/libs/Makefile.am b/docs/libs/Makefile.am new file mode 100644 index 0000000000..e93bbda17b --- /dev/null +++ b/docs/libs/Makefile.am @@ -0,0 +1,102 @@ +## Process this file with automake to produce Makefile.in + +# FIXME: fix the docs then remove this variable +DOCS_ARE_INCOMPLETE_PLEASE_FIXME=yespleasedo + +# The name of the module, e.g. 'glib'. +#DOC_MODULE=gst-plugins-libs-@GST_MAJORMINOR@ +DOC_MODULE=gst-plugins-bad-libs + +# for upload-doc.mak +DOC=gst-plugins-bad-libs +FORMATS=html +html: html-build.stamp +include $(top_srcdir)/common/upload-doc.mak + +# generated basefiles +#basefiles = \ +## $(DOC_MODULE).types \ +# $(DOC_MODULE)-sections.txt \ +# $(DOC_MODULE)-docs.sgml + +# ugly hack to make -unused.sgml work +#unused-build.stamp: +# BUILDDIR=`pwd` && \ +# cd $(srcdir)/tmpl && \ +# ln -sf gstreamer-libs-unused.sgml \ +# $$BUILDDIR/tmpl/gstreamer-libs-@GST_MAJORMINOR@-unused.sgml +# touch unused-build.stamp + +# these rules are added to create parallel docs using GST_MAJORMINOR +#$(basefiles): gstreamer-libs-@GST_MAJORMINOR@%: gstreamer-libs% +# cp $< $@ + +#CLEANFILES = $(basefiles) + +# The top-level SGML file. Change it if you want. +DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.sgml + +# The directory containing the source code. Relative to $(top_srcdir). +# gtk-doc will search all .c & .h files beneath here for inline comments +# documenting functions and macros. +DOC_SOURCE_DIR=$(top_srcdir)/gst-libs/gst +DOC_BUILD_DIR=$(top_builddir)/gst-libs/gst + +# Extra options to supply to gtkdoc-scan. +SCAN_OPTIONS=--deprecated-guards="GST_DISABLE_DEPRECATED" + +# FIXME : +# there's something wrong with gstreamer-sections.txt not being in the dist +# maybe it doesn't resolve; we're adding it below for now +#EXTRA_DIST = gstreamer.types.in gstreamer.hierarchy $(DOC_MODULE)-sections.txt gstreamer-sections.txt $(DOC_MAIN_SGML_FILE) + +# Extra options to supply to gtkdoc-mkdb. +MKDB_OPTIONS=--sgml-mode --output-format=xml + +# Extra options to supply to gtkdoc-fixref. +FIXXREF_OPTIONS=--extra-dir=$(GLIB_PREFIX)/share/gtk-doc/html \ + --extra-dir=$(GST_PREFIX)/share/gtk-doc/html + +# Used for dependencies. +HFILE_GLOB=$(DOC_SOURCE_DIR)/*/*.h +CFILE_GLOB=$(DOC_SOURCE_DIR)/*/*.c + +# this is a wingo addition +# thomasvs: another nice wingo addition would be an explanation on why +# this is useful ;) + +SCANOBJ_DEPS = \ + $(top_builddir)/gst-libs/gst/codecparsers/libgstcodecparsers-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/basecamerabinsrc/libgstbasecamerabinsrc-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/interfaces/libgstphotography-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/signalprocessor/libgstsignalprocessor-@GST_MAJORMINOR@.la \ + $(top_builddir)/gst-libs/gst/video/libgstbasevideo-@GST_MAJORMINOR@.la + + +# Header files to ignore when scanning. +IGNORE_HFILES = + +# Images to copy into HTML directory. +HTML_IMAGES = + +# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE). +content_files = compiling.sgml + +# Other files to distribute. +extra_files = + +# CFLAGS and LDFLAGS for compiling scan program. Only needed if your app/lib +# contains GtkObjects/GObjects and you want to document signals and properties. +GTKDOC_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) +GTKDOC_LIBS = $(SCANOBJ_DEPS) $(GST_BASE_LIBS) $(GST_BAD_LIBS) + +GTKDOC_CC=$(LIBTOOL) --tag=CC --mode=compile $(CC) +GTKDOC_LD=$(LIBTOOL) --tag=CC --mode=link $(CC) + +# If you need to override some of the declarations, place them in this file +# and uncomment this line. +#DOC_OVERRIDES = $(DOC_MODULE)-overrides.txt +DOC_OVERRIDES = + +include $(top_srcdir)/common/gtk-doc.mak + diff --git a/docs/libs/compiling.sgml b/docs/libs/compiling.sgml new file mode 100644 index 0000000000..949209a068 --- /dev/null +++ b/docs/libs/compiling.sgml @@ -0,0 +1,48 @@ + + +%version-entities; +]> + + +Compiling +3 +GStreamer-Bad Library + + + +Compiling against the bad plugins libraries + +How to compile against the bad plugins libraries + + + + +Compiling against the bad plugins libraries + + +To compile against these libraries, you need to tell the compiler where to +find the header files and libraries. This is done with the +pkg-config utility. + + +The following interactive shell session demonstrates how +pkg-config is used: + +$ pkg-config --cflags gstreamer-plugins-bad-&GST_MAJORMINOR; +-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -pthread -I/usr/include/gstreamer-&GST_MAJORMINOR; -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/libxml2 +$ pkg-config --libs gstreamer-plugins-bad-&GST_MAJORMINOR; +-Wl,--export-dynamic -pthread -lgstreamer-&GST_MAJORMINOR; -lgobject-2.0 -lgmodule-2.0 -ldl -lgthread-2.0 -lxml2 -lpthread -lz -lm -lglib-2.0 + + + + +Note that, because of the number of libraries provided in this package, +the pkg-config information does not add -l flags itself +to choose the libraries to link to. You must add these yourself to select +which of the libraries you want to use. + + + + diff --git a/docs/libs/gst-plugins-bad-libs-docs.sgml b/docs/libs/gst-plugins-bad-libs-docs.sgml new file mode 100644 index 0000000000..f137ca3c2f --- /dev/null +++ b/docs/libs/gst-plugins-bad-libs-docs.sgml @@ -0,0 +1,60 @@ + + +%version-entities; +]> + + + GStreamer Bad Plugins &GST_MAJORMINOR; Library Reference Manual + + for GStreamer Bad Library &GST_MAJORMINOR; (&GST_VERSION;) + http://gstreamer.freedesktop.org/data/doc/gstreamer/head/gst-plugins-bad-libs/html/. + + + + + GStreamer Bad Plugins Libraries + + This manual describes the libraries provided by the GStreamer Bad Plugins + package. + + + + + Bitstream parsing Library + + This library should be linked to by getting cflags and libs from + gstreamer-plugins-bad-&GST_MAJORMINOR;.pc and adding + -lgscodeparsers-&GST_MAJORMINOR; to the library flags. + + + + + + + Base video element classes + + + + + + + + + Object Hierarchy + + + + + Index + + + + Index of deprecated API + + + + + + diff --git a/docs/libs/gst-plugins-bad-libs-overrides.txt b/docs/libs/gst-plugins-bad-libs-overrides.txt new file mode 100644 index 0000000000..e69de29bb2 diff --git a/docs/libs/gst-plugins-bad-libs-sections.txt b/docs/libs/gst-plugins-bad-libs-sections.txt new file mode 100644 index 0000000000..73cc720197 --- /dev/null +++ b/docs/libs/gst-plugins-bad-libs-sections.txt @@ -0,0 +1,361 @@ +# codecparsers +
+gsth264parser +h264parser +gst/codecparsers/gsth264parser.h +GST_H264_MAX_SPS_COUNT +GST_H264_MAX_PPS_COUNT +GST_H264_IS_P_SLICE +GST_H264_IS_B_SLICE +GST_H264_IS_I_SLICE +GST_H264_IS_SP_SLICE +GST_H264_IS_SI_SLICE +GstH264NalUnitType +GstH264ParserResult +GstH264SEIPayloadType +GstH264SEIPicStructType +GstH264SliceType +GstH264NalParser +GstH264NalUnit +GstH264SPS +GstH264PPS +GstH264HRDParams +GstH264VUIParams +GstH264DecRefPicMarking +GstH264RefPicMarking +GstH264PredWeightTable +GstH264SliceHdr +GstH264ClockTimestamp +GstH264PicTiming +GstH264BufferingPeriod +GstH264SEIMessage +gst_h264_parser_identify_nalu +gst_h264_parser_identify_nalu_avc +gst_h264_parser_parse_nal +gst_h264_parser_parse_slice_hdr +gst_h264_parser_parse_sps +gst_h264_parser_parse_pps +gst_h264_parser_parse_sei +gst_h264_nal_parser_new +gst_h264_nal_parser_free +gst_h264_parse_sps +gst_h264_parse_pps + + +
+ +
+gstmpegvideoparser +mpegvideoparser +gst/codecparsers/gstmpegvideoparser.h +GstMpegVideoPacketTypeCode +GstMpegVideoPacketExtensionCode +GstMpegVideoLevel +GstMpegVideoProfile +GstMpegVideoPictureType +GstMpegVideoPictureStructure +GstMpegVideoSequenceHdr +GstMpegVideoSequenceExt +GstMpegVideoPictureHdr +GstMpegVideoGop +GstMpegVideoPictureExt +GstMpegVideoQuantMatrixExt +GstMpegVideoTypeOffsetSize +gst_mpeg_video_parse +gst_mpeg_video_parse_sequence_header +gst_mpeg_video_parse_picture_header +gst_mpeg_video_parse_picture_extension +gst_mpeg_video_parse_gop +gst_mpeg_video_parse_sequence_extension +gst_mpeg_video_parse_quant_matrix_extension + + +
+ +
+gstphotography +GST_PHOTOGRAPHY_AUTOFOCUS_DONE +GST_PHOTOGRAPHY_SHAKE_RISK +GST_PHOTOGRAPHY_PROP_WB_MODE +GST_PHOTOGRAPHY_PROP_COLOUR_TONE +GST_PHOTOGRAPHY_PROP_SCENE_MODE +GST_PHOTOGRAPHY_PROP_FLASH_MODE +GST_PHOTOGRAPHY_PROP_NOISE_REDUCTION +GST_PHOTOGRAPHY_PROP_FOCUS_STATUS +GST_PHOTOGRAPHY_PROP_CAPABILITIES +GST_PHOTOGRAPHY_PROP_SHAKE_RISK +GST_PHOTOGRAPHY_PROP_EV_COMP +GST_PHOTOGRAPHY_PROP_ISO_SPEED +GST_PHOTOGRAPHY_PROP_APERTURE +GST_PHOTOGRAPHY_PROP_EXPOSURE +GST_PHOTOGRAPHY_PROP_IMAGE_CAPTURE_SUPPORTED_CAPS +GST_PHOTOGRAPHY_PROP_IMAGE_PREVIEW_SUPPORTED_CAPS +GST_PHOTOGRAPHY_PROP_FLICKER_MODE +GST_PHOTOGRAPHY_PROP_FOCUS_MODE +GST_PHOTOGRAPHY_PROP_ZOOM +GstPhotographyNoiseReduction +GstWhiteBalanceMode +GstColourToneMode +GstSceneMode +GstFlashMode +GstFocusStatus +GstPhotoCaps +GstPhotoShakeRisk +GstFlickerReductionMode +GstFocusMode +GstPhotoCapturePrepared +get_ev_compensation +get_iso_speed +get_aperture +get_exposure +get_white_balance_mode +get_colour_tone_mode +get_scene_mode +get_flash_mode +get_zoom +get_flicker_mode +get_focus_mode +set_ev_compensation +set_iso_speed +set_aperture +set_exposure +set_white_balance_mode +set_colour_tone_mode +set_scene_mode +set_flash_mode +set_zoom +set_flicker_mode +set_focus_mode +get_capabilities +prepare_for_capture +set_autofocus +set_config +get_config +get_noise_reduction +set_noise_reduction +gst_photography_get_ev_compensation +gst_photography_get_iso_speed +gst_photography_get_aperture +gst_photography_get_exposure +gst_photography_get_white_balance_mode +gst_photography_get_colour_tone_mode +gst_photography_get_scene_mode +gst_photography_get_flash_mode +gst_photography_get_noise_reduction +gst_photography_get_zoom +gst_photography_get_flicker_mode +gst_photography_get_focus_mode +gst_photography_set_ev_compensation +gst_photography_set_iso_speed +gst_photography_set_aperture +gst_photography_set_exposure +gst_photography_set_white_balance_mode +gst_photography_set_colour_tone_mode +gst_photography_set_scene_mode +gst_photography_set_flash_mode +gst_photography_set_noise_reduction +gst_photography_set_zoom +gst_photography_set_flicker_mode +gst_photography_set_focus_mode +gst_photography_get_capabilities +gst_photography_prepare_for_capture +gst_photography_set_autofocus +gst_photography_set_config +gst_photography_get_config +GstPhotography + +GST_PHOTOGRAPHY +GST_IS_PHOTOGRAPHY +GST_TYPE_PHOTOGRAPHY +gst_photography_get_type +GST_PHOTOGRAPHY_GET_IFACE +
+ +
+gstbasecamerasrc +GstBaseCameraSrc +GST_BASE_CAMERA_SRC_CAST +GST_BASE_CAMERA_SRC_VIEWFINDER_PAD_NAME +GST_BASE_CAMERA_SRC_IMAGE_PAD_NAME +GST_BASE_CAMERA_SRC_VIDEO_PAD_NAME +GST_BASE_CAMERA_SRC_PREVIEW_MESSAGE_NAME +GstBaseCameraSrc +GstBaseCameraSrcClass +MIN_ZOOM +MAX_ZOOM +ZOOM_1X +gst_base_camera_src_get_photography +gst_base_camera_src_get_color_balance +gst_base_camera_src_set_mode +gst_base_camera_src_setup_zoom +gst_base_camera_src_setup_preview +gst_base_camera_src_finish_capture +gst_base_camera_src_post_preview + +GST_BASE_CAMERA_SRC +GST_IS_BASE_CAMERA_SRC +GST_TYPE_BASE_CAMERA_SRC +gst_base_camera_src_get_type +GST_BASE_CAMERA_SRC_CLASS +GST_IS_BASE_CAMERA_SRC_CLASS +GST_BASE_CAMERA_SRC_GET_CLASS +
+ +
+gstbasevideoencoder +GstBaseVideoEncoder +GST_BASE_VIDEO_ENCODER_SINK_NAME +GST_BASE_VIDEO_ENCODER_SRC_NAME +GST_BASE_VIDEO_ENCODER_FLOW_DROPPED +GstBaseVideoEncoder +GstBaseVideoEncoderClass +gst_base_video_encoder_get_state +gst_base_video_encoder_get_oldest_frame +gst_base_video_encoder_finish_frame +gst_base_video_encoder_set_latency +gst_base_video_encoder_set_latency_fields + +GST_BASE_VIDEO_ENCODER +GST_IS_BASE_VIDEO_ENCODER +GST_TYPE_BASE_VIDEO_ENCODER +gst_base_video_encoder_get_type +GST_BASE_VIDEO_ENCODER_CLASS +GST_IS_BASE_VIDEO_ENCODER_CLASS +GST_BASE_VIDEO_ENCODER_GET_CLASS +
+ +
+gstbasevideodecoder +GstBaseVideoDecoder +GST_BASE_VIDEO_DECODER_SINK_NAME +GST_BASE_VIDEO_DECODER_SRC_NAME +GST_BASE_VIDEO_DECODER_FLOW_NEED_DATA +GST_BASE_VIDEO_DECODER_FLOW_DROPPED +GST_BASE_AUDIO_DECODER_ERROR +GstBaseVideoDecoder +GstBaseVideoDecoderClass +gst_base_video_decoder_class_set_capture_pattern +gst_base_video_decoder_get_frame +gst_base_video_decoder_get_oldest_frame +gst_base_video_decoder_add_to_frame +gst_base_video_decoder_lost_sync +gst_base_video_decoder_have_frame +gst_base_video_decoder_set_sync_point +gst_base_video_decoder_set_src_caps +gst_base_video_decoder_alloc_src_buffer +gst_base_video_decoder_alloc_src_frame +gst_base_video_decoder_get_state +gst_base_video_decoder_get_max_decode_time +gst_base_video_decoder_finish_frame + +GST_BASE_VIDEO_DECODER +GST_IS_BASE_VIDEO_DECODER +GST_TYPE_BASE_VIDEO_DECODER +gst_base_video_decoder_get_type +GST_BASE_VIDEO_DECODER_CLASS +GST_IS_BASE_VIDEO_DECODER_CLASS +GST_BASE_VIDEO_DECODER_GET_CLASS +
+ +
+gstbasevideocodec +GstBaseVideoCodec +GST_BASE_VIDEO_CODEC_SINK_NAME +GST_BASE_VIDEO_CODEC_SRC_NAME +GST_BASE_VIDEO_CODEC_SRC_PAD +GST_BASE_VIDEO_CODEC_SINK_PAD +GST_BASE_VIDEO_CODEC_FLOW_NEED_DATA +GST_BASE_VIDEO_CODEC_STREAM_LOCK +GST_BASE_VIDEO_CODEC_STREAM_UNLOCK +GstVideoState +GstVideoFrame +GstBaseVideoCodec +GstBaseVideoCodecClass +gst_base_video_codec_new_frame +gst_base_video_codec_free_frame + +GST_BASE_VIDEO_CODEC +GST_IS_BASE_VIDEO_CODEC +GST_TYPE_BASE_VIDEO_CODEC +gst_base_video_codec_get_type +GST_BASE_VIDEO_CODEC_CLASS +GST_IS_BASE_VIDEO_CODEC_CLASS +GST_BASE_VIDEO_CODEC_GET_CLASS +
+ +
+gstsignalprocessor +GstSignalProcessor +GstSignalProcessorClassFlags +GST_SIGNAL_PROCESSOR_CLASS_CAN_PROCESS_IN_PLACE +GST_SIGNAL_PROCESSOR_CLASS_SET_CAN_PROCESS_IN_PLACE +GstSignalProcessorState +GST_SIGNAL_PROCESSOR_IS_INITIALIZED +GST_SIGNAL_PROCESSOR_IS_RUNNING +GstSignalProcessorGroup +GstSignalProcessor +GstSignalProcessorClass +gst_signal_processor_class_add_pad_template + +GST_SIGNAL_PROCESSOR +GST_IS_SIGNAL_PROCESSOR +GST_TYPE_SIGNAL_PROCESSOR +gst_signal_processor_get_type +GST_SIGNAL_PROCESSOR_CLASS +GST_IS_SIGNAL_PROCESSOR_CLASS +GST_SIGNAL_PROCESSOR_GET_CLASS +
+ + +
+photography-enumtypes +gst_photography_noise_reduction_get_type +GST_TYPE_PHOTOGRAPHY_NOISE_REDUCTION +gst_white_balance_mode_get_type +GST_TYPE_WHITE_BALANCE_MODE +gst_colour_tone_mode_get_type +GST_TYPE_COLOUR_TONE_MODE +gst_scene_mode_get_type +GST_TYPE_SCENE_MODE +gst_flash_mode_get_type +GST_TYPE_FLASH_MODE +gst_focus_status_get_type +GST_TYPE_FOCUS_STATUS +gst_photo_caps_get_type +GST_TYPE_PHOTO_CAPS +gst_photo_shake_risk_get_type +GST_TYPE_PHOTO_SHAKE_RISK +gst_flicker_reduction_mode_get_type +GST_TYPE_FLICKER_REDUCTION_MODE +gst_focus_mode_get_type +GST_TYPE_FOCUS_MODE +
+ +
+gstcamerabin-enum +DEFAULT_WIDTH +DEFAULT_HEIGHT +DEFAULT_CAPTURE_WIDTH +DEFAULT_CAPTURE_HEIGHT +DEFAULT_FPS_N +DEFAULT_FPS_D +DEFAULT_ZOOM +GstCameraBinMode +GST_TYPE_CAMERABIN_MODE +gst_camerabin_mode_get_type +
+ +
+gstcamerabinpreview +gst_camerabin_create_preview_pipeline +gst_camerabin_destroy_preview_pipeline +gst_camerabin_preview_pipeline_post +gst_camerabin_preview_set_caps +
+ +
+gstbasevideoutils +gst_base_video_rawvideo_convert +gst_base_video_encoded_video_convert +gst_video_state_get_timestamp +
diff --git a/docs/libs/gst-plugins-bad-libs.types b/docs/libs/gst-plugins-bad-libs.types new file mode 100644 index 0000000000..493215718a --- /dev/null +++ b/docs/libs/gst-plugins-bad-libs.types @@ -0,0 +1,4 @@ +#include + +#include +#include diff --git a/ext/libmms/gstmms.c b/ext/libmms/gstmms.c index 23a5226ea3..cf7a091454 100644 --- a/ext/libmms/gstmms.c +++ b/ext/libmms/gstmms.c @@ -273,7 +273,7 @@ gst_mms_prepare_seek_segment (GstBaseSrc * src, GstEvent * event, static gboolean gst_mms_do_seek (GstBaseSrc * src, GstSegment * segment) { - mms_off_t start; + gint64 start; GstMMS *mmssrc = GST_MMS (src); if (segment->format == GST_FORMAT_TIME) { diff --git a/ext/opencv/MotionCells.cpp b/ext/opencv/MotionCells.cpp index 2b81b305dd..5223bc7f73 100644 --- a/ext/opencv/MotionCells.cpp +++ b/ext/opencv/MotionCells.cpp @@ -95,6 +95,8 @@ MotionCells::MotionCells () m_saveerrorcode = 0; m_alpha = 0.5; m_beta = 0.5; + m_useAlpha = false; + m_isVisible = false; } @@ -168,8 +170,6 @@ MotionCells::performDetectionMotionCells (IplImage * p_frame, m_pbwImage = cvCreateImage (m_frameSize, IPL_DEPTH_8U, 1); cvPyrDown (m_pprevFrame, m_pprevDown); cvCvtColor (m_pprevDown, m_pprevgreyImage, CV_RGB2GRAY); - if (m_pprevFrame) - cvReleaseImage (&m_pprevFrame); cvPyrDown (m_pcurFrame, m_pcurDown); cvCvtColor (m_pcurDown, m_pcurgreyImage, CV_RGB2GRAY); m_pdifferenceImage = cvCloneImage (m_pcurgreyImage); @@ -268,6 +268,8 @@ MotionCells::performDetectionMotionCells (IplImage * p_frame, cvReleaseImage (&transparencyimg); } + if (m_pprevFrame) + cvReleaseImage (&m_pprevFrame); m_pprevFrame = cvCloneImage (m_pcurFrame); m_framecnt = 0; if (m_pcurFrame) diff --git a/ext/soundtouch/gstpitch.cc b/ext/soundtouch/gstpitch.cc index 46f73194d5..6b5b95f151 100644 --- a/ext/soundtouch/gstpitch.cc +++ b/ext/soundtouch/gstpitch.cc @@ -55,6 +55,7 @@ struct _GstPitchPrivate enum { ARG_0, + ARG_OUT_RATE, ARG_RATE, ARG_TEMPO, ARG_PITCH @@ -147,6 +148,11 @@ gst_pitch_class_init (GstPitchClass * klass) "Audio stream rate", 0.1, 10.0, 1.0, (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property (gobject_class, ARG_OUT_RATE, + g_param_spec_float ("output-rate", "Output Rate", + "Output rate on downstream segment events", 0.1, 10.0, 1.0, + (GParamFlags) (G_PARAM_READWRITE | GST_PARAM_CONTROLLABLE | G_PARAM_STATIC_STRINGS))); + g_type_class_add_private (gobject_class, sizeof (GstPitchPrivate)); } @@ -186,12 +192,14 @@ gst_pitch_init (GstPitch * pitch, GstPitchClass * pitch_class) pitch->tempo = 1.0; pitch->rate = 1.0; + pitch->out_seg_rate = 1.0; + pitch->seg_arate = 1.0; pitch->pitch = 1.0; pitch->next_buffer_time = 0; pitch->next_buffer_offset = 0; pitch->priv->st->setRate (pitch->rate); - pitch->priv->st->setTempo (pitch->tempo); + pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate); pitch->priv->st->setPitch (pitch->pitch); pitch->priv->stream_time_ratio = 1.0; @@ -232,18 +240,22 @@ gst_pitch_set_property (GObject * object, guint prop_id, switch (prop_id) { case ARG_TEMPO: pitch->tempo = g_value_get_float (value); - pitch->priv->stream_time_ratio = pitch->tempo * pitch->rate; - pitch->priv->st->setTempo (pitch->tempo); + pitch->priv->stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate; + pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate); GST_OBJECT_UNLOCK (pitch); gst_pitch_update_duration (pitch); break; case ARG_RATE: pitch->rate = g_value_get_float (value); - pitch->priv->stream_time_ratio = pitch->tempo * pitch->rate; + pitch->priv->stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate; pitch->priv->st->setRate (pitch->rate); GST_OBJECT_UNLOCK (pitch); gst_pitch_update_duration (pitch); break; + case ARG_OUT_RATE: + /* Has no effect until the next input segment */ + pitch->out_seg_rate = g_value_get_float (value); + GST_OBJECT_UNLOCK (pitch); case ARG_PITCH: pitch->pitch = g_value_get_float (value); pitch->priv->st->setPitch (pitch->pitch); @@ -270,6 +282,9 @@ gst_pitch_get_property (GObject * object, guint prop_id, case ARG_RATE: g_value_set_float (value, pitch->rate); break; + case ARG_OUT_RATE: + g_value_set_float (value, pitch->out_seg_rate); + break; case ARG_PITCH: g_value_set_float (value, pitch->pitch); break; @@ -695,16 +710,17 @@ gst_pitch_process_segment (GstPitch * pitch, GstEvent ** event) gint64 start_value, stop_value, base; gint64 next_offset = 0, next_time = 0; gboolean update = FALSE; - gdouble rate; + gdouble rate, out_seg_rate, arate, our_arate; gfloat stream_time_ratio; g_return_val_if_fail (event, FALSE); GST_OBJECT_LOCK (pitch); stream_time_ratio = pitch->priv->stream_time_ratio; + out_seg_rate = pitch->out_seg_rate; GST_OBJECT_UNLOCK (pitch); - gst_event_parse_new_segment (*event, &update, &rate, &format, &start_value, + gst_event_parse_new_segment_full (*event, &update, &rate, &arate, &format, &start_value, &stop_value, &base); if (format != GST_FORMAT_TIME && format != GST_FORMAT_DEFAULT) { @@ -713,21 +729,36 @@ gst_pitch_process_segment (GstPitch * pitch, GstEvent ** event) "open ended NEWSEGMENT in TIME format."); gst_event_unref (*event); *event = - gst_event_new_new_segment (update, rate, GST_FORMAT_TIME, 0, -1, 0); + gst_event_new_new_segment_full (update, rate, arate, GST_FORMAT_TIME, 0, -1, 0); start_value = 0; stop_value = -1; base = 0; } + /* Figure out how much of the incoming 'rate' we'll apply ourselves */ + our_arate = rate / out_seg_rate; + /* update the output rate variables */ + rate = out_seg_rate; + arate *= our_arate; + GST_LOG_OBJECT (pitch->sinkpad, "segment %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT " (%d)", start_value, stop_value, format); + stream_time_ratio = pitch->tempo * pitch->rate * pitch->seg_arate; + if (stream_time_ratio == 0) { GST_LOG_OBJECT (pitch->sinkpad, "stream_time_ratio is zero"); return FALSE; } + /* Update the playback rate */ + GST_OBJECT_LOCK (pitch); + pitch->seg_arate = our_arate; + pitch->priv->stream_time_ratio = stream_time_ratio; + pitch->priv->st->setTempo (pitch->tempo * pitch->seg_arate); + GST_OBJECT_UNLOCK (pitch); + start_value = (gint64) (start_value / stream_time_ratio); if (stop_value != -1) stop_value = (gint64) (stop_value / stream_time_ratio); @@ -752,8 +783,8 @@ gst_pitch_process_segment (GstPitch * pitch, GstEvent ** event) pitch->next_buffer_offset = next_offset; gst_event_unref (*event); - *event = gst_event_new_new_segment (update, rate, format, start_value, - stop_value, base); + *event = gst_event_new_new_segment_full (update, rate, arate, format, + start_value, stop_value, base); return TRUE; } @@ -772,6 +803,8 @@ gst_pitch_sink_event (GstPad * pad, GstEvent * event) case GST_EVENT_FLUSH_STOP: gst_pitch_flush_buffer (pitch, FALSE); pitch->priv->st->clear (); + pitch->next_buffer_offset = 0; + pitch->next_buffer_time = 0; pitch->min_latency = pitch->max_latency = 0; break; case GST_EVENT_EOS: diff --git a/ext/soundtouch/gstpitch.hh b/ext/soundtouch/gstpitch.hh index 32cb04dd13..ad1f7f6d1f 100644 --- a/ext/soundtouch/gstpitch.hh +++ b/ext/soundtouch/gstpitch.hh @@ -58,11 +58,18 @@ struct _GstPitch * > 1 makes the stream shorter */ + gfloat out_seg_rate; /* change output segment rate + * Affects playback when input + * segments have rate != out_rate + */ + gfloat pitch; /* change pitch * change the pitch without affecting the * duration, stream length doesn't change */ + gfloat seg_arate; /* Rate to apply from input segment */ + /* values extracted from caps */ gint samplerate; /* samplerate */ gint channels; /* number of audio channels */ diff --git a/ext/vp8/gstvp8enc.c b/ext/vp8/gstvp8enc.c index f0d282f220..e832975644 100644 --- a/ext/vp8/gstvp8enc.c +++ b/ext/vp8/gstvp8enc.c @@ -563,7 +563,7 @@ gst_vp8_enc_set_property (GObject * object, guint prop_id, gst_vp8_enc->static_threshold = g_value_get_int (value); break; case PROP_DROP_FRAME: - gst_vp8_enc->drop_frame = g_value_get_boolean (value); + gst_vp8_enc->drop_frame = g_value_get_int (value); break; case PROP_RESIZE_ALLOWED: gst_vp8_enc->resize_allowed = g_value_get_boolean (value); @@ -652,7 +652,7 @@ gst_vp8_enc_get_property (GObject * object, guint prop_id, GValue * value, g_value_set_int (value, gst_vp8_enc->static_threshold); break; case PROP_DROP_FRAME: - g_value_set_boolean (value, gst_vp8_enc->drop_frame); + g_value_set_int (value, gst_vp8_enc->drop_frame); break; case PROP_RESIZE_ALLOWED: g_value_set_boolean (value, gst_vp8_enc->resize_allowed); diff --git a/gst-libs/gst/Makefile.am b/gst-libs/gst/Makefile.am index ac83c681fe..130956ee4c 100644 --- a/gst-libs/gst/Makefile.am +++ b/gst-libs/gst/Makefile.am @@ -1,6 +1,6 @@ -SUBDIRS = interfaces signalprocessor video basecamerabinsrc +SUBDIRS = interfaces signalprocessor video basecamerabinsrc codecparsers noinst_HEADERS = gst-i18n-plugin.h gettext.h -DIST_SUBDIRS = interfaces signalprocessor video basecamerabinsrc +DIST_SUBDIRS = interfaces signalprocessor video basecamerabinsrc codecparsers diff --git a/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c b/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c index 6b854197dd..b916d7f6b3 100644 --- a/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c +++ b/gst-libs/gst/basecamerabinsrc/gstcamerabinpreview.c @@ -125,9 +125,9 @@ gst_camerabin_create_preview_pipeline (GstElement * element, { GstCameraBinPreviewPipelineData *data; GstElement *csp; - GstElement *csp2; GstElement *vscale; gboolean added = FALSE; + gboolean linkfail = FALSE; GstBus *bus; GstAppSinkCallbacks callbacks = { 0, }; @@ -138,29 +138,48 @@ gst_camerabin_create_preview_pipeline (GstElement * element, data->capsfilter = gst_element_factory_make ("capsfilter", "preview-capsfilter"); data->appsink = gst_element_factory_make ("appsink", "preview-appsink"); - csp = gst_element_factory_make ("ffmpegcolorspace", "preview-csp0"); - csp2 = gst_element_factory_make ("ffmpegcolorspace", "preview-csp1"); + csp = gst_element_factory_make ("ffmpegcolorspace", "preview-csp"); vscale = gst_element_factory_make ("videoscale", "preview-vscale"); - if (!data->appsrc || !data->capsfilter || !data->appsink || !csp || - !csp2 || !vscale) { + if (!data->appsrc || !data->capsfilter || !data->appsink || !csp || !vscale) { goto error; } + g_object_set (data->appsrc, "emit-signals", FALSE, NULL); + g_object_set (data->appsink, "sync", FALSE, NULL); + gst_bin_add_many (GST_BIN (data->pipeline), data->appsrc, data->capsfilter, - data->appsink, csp, csp2, vscale, NULL); + data->appsink, csp, vscale, NULL); if (filter) gst_bin_add (GST_BIN (data->pipeline), gst_object_ref (filter)); added = TRUE; if (filter) { - if (!gst_element_link_many (data->appsrc, filter, csp, vscale, csp2, - data->capsfilter, data->appsink, NULL)) - goto error; + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (data->appsrc, "src", + filter, NULL, GST_PAD_LINK_CHECK_NOTHING)); + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (filter, NULL, + vscale, "sink", GST_PAD_LINK_CHECK_CAPS)); } else { - if (!gst_element_link_many (data->appsrc, csp, vscale, csp2, - data->capsfilter, data->appsink, NULL)) - goto error; + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (data->appsrc, "src", + vscale, "sink", GST_PAD_LINK_CHECK_NOTHING)); + } + + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (vscale, "src", csp, + "sink", GST_PAD_LINK_CHECK_NOTHING)); + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (csp, "src", + data->capsfilter, "sink", GST_PAD_LINK_CHECK_NOTHING)); + linkfail |= + GST_PAD_LINK_FAILED (gst_element_link_pads_full (data->capsfilter, "src", + data->appsink, "sink", GST_PAD_LINK_CHECK_NOTHING)); + + if (linkfail) { + GST_WARNING ("Failed to link preview pipeline elements"); + goto error; } callbacks.new_buffer = gst_camerabin_preview_pipeline_new_buffer; @@ -188,8 +207,6 @@ error: if (!added) { if (csp) gst_object_unref (csp); - if (csp2) - gst_object_unref (csp2); if (vscale) gst_object_unref (vscale); if (data->appsrc) diff --git a/gst-libs/gst/codecparsers/Makefile.am b/gst-libs/gst/codecparsers/Makefile.am new file mode 100644 index 0000000000..7fa44f1585 --- /dev/null +++ b/gst-libs/gst/codecparsers/Makefile.am @@ -0,0 +1,30 @@ +lib_LTLIBRARIES = libgstcodecparsers-@GST_MAJORMINOR@.la + +libgstcodecparsers_@GST_MAJORMINOR@_la_SOURCES = \ + gstmpegvideoparser.c gsth264parser.c + +libgstcodecparsers_@GST_MAJORMINOR@includedir = \ + $(includedir)/gstreamer-@GST_MAJORMINOR@/gst/codecparsers + +libgstcodecparsers_@GST_MAJORMINOR@include_HEADERS = \ + gstmpegvideoparser.h gsth264parser.h + +libgstcodecparsers_@GST_MAJORMINOR@_la_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_CFLAGS) +libgstcodecparsers_@GST_MAJORMINOR@_la_LIBADD = $(GST_BASE_LIBS) $(GST_LIBS) +libgstcodecparsers_@GST_MAJORMINOR@_la_LDFLAGS = $(GST_LIB_LDFLAGS) $(GST_ALL_LDFLAGS) $(GST_LT_LDFLAGS) + +Android.mk: $(BUILT_SOURCES) Makefile.am + androgenizer -:PROJECT libgstcodecparsers -:STATIC libgstcodecparsers-@GST_MAJORMINOR@ \ + -:TAGS eng debug \ + -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \ + -:SOURCES $(libgstcodecparsers_@GST_MAJORMINOR@_la_SOURCES) \ + $(built_sources) \ + -:CFLAGS $(DEFS) $(libgstcodecparsers_@GST_MAJORMINOR@_la_CFLAGS) \ + -:LDFLAGS $(libgstcodecparsers_@GST_MAJORMINOR@_la_LDFLAGS) \ + $(libgstcodecparsers@GST_MAJORMINOR@_la_LIBADD) \ + -ldl \ + -:HEADER_TARGET gstreamer-@GST_MAJORMINOR@/gst/codecparsers \ + -:HEADERS $(libgstcodecparsersinclude_HEADERS) \ + $(built_headers) \ + -:PASSTHROUGH LOCAL_ARM_MODE:=arm \ + > $@ diff --git a/gst-libs/gst/codecparsers/gsth264parser.c b/gst-libs/gst/codecparsers/gsth264parser.c new file mode 100644 index 0000000000..869aa6122b --- /dev/null +++ b/gst-libs/gst/codecparsers/gsth264parser.c @@ -0,0 +1,1837 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * Some bits C-c,C-v'ed and s/4/3 from h264parse and videoparsers/h264parse.c: + * Copyright (C) <2010> Mark Nauwelaerts + * Copyright (C) <2010> Collabora Multimedia + * Copyright (C) <2010> Nokia Corporation + * + * (C) 2005 Michal Benes + * (C) 2008 Wim Taymans + * + * 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. + */ + +/** + * SECTION:gsth264parser + * @short_description: Convenience library for h264 video + * bitstream parsing. + * + * It offers you basic parsing in AVC mode or not. Tp identify Nals in a bitstream and + * parse its basic headers, you should call: + * + * + * gst_h264_parser_identify_nalu to identify the following nalu in not AVC bitstreams + * + * + * gst_h264_parser_identify_nalu_avc to identify the following nalu in AVC bitstreams + * + * + * + * Then, depending on the #GstH264NalUnitType of the newly parsed #GstH264NalUnit, you should + * call the differents functions to parse the struct. + * + * Note: You should always call gst_h264_parser_parse_nal if you don't actually need + * #GstH264NalUnitType to be parsed for your personnal use. This, to guarantee that the + * #GstH264NalParser is always up to date. + * + * For more details about the structures, look at the ISO specifications. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gsth264parser.h" + +#include +#include +#include + +GST_DEBUG_CATEGORY (h264_parser_debug); +#define GST_CAT_DEFAULT h264_parser_debug + +/**** Default scaling_lists according to Table 7-2 *****/ +const guint8 default_4x4_intra[16] = { + 6, 13, 13, 20, 20, 20, 28, 28, 28, 28, 32, 32, + 32, 37, 37, 42 +}; + +const guint8 default_4x4_inter[16] = { + 10, 14, 14, 20, 20, 20, 24, 24, 24, 24, 27, 27, + 27, 30, 30, 34 +}; + +const guint8 default_8x8_intra[64] = { + 6, 10, 10, 13, 11, 13, 16, 16, 16, 16, 18, 18, + 18, 18, 18, 23, 23, 23, 23, 23, 23, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, + 27, 27, 27, 27, 27, 29, 29, 29, 29, 29, 29, 29, 31, 31, 31, 31, 31, 31, 33, + 33, 33, 33, 33, 36, 36, 36, 36, 38, 38, 38, 40, 40, 42 +}; + +const guint8 default_8x8_inter[64] = { + 9, 13, 13, 15, 13, 15, 17, 17, 17, 17, 19, 19, + 19, 19, 19, 21, 21, 21, 21, 21, 21, 22, 22, 22, 22, 22, 22, 22, 24, 24, 24, + 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 27, 27, 27, 27, 27, 27, 28, + 28, 28, 28, 28, 30, 30, 30, 30, 32, 32, 32, 33, 33, 35 +}; + +const guint8 zigzag_8x8[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +const guint8 zigzag_4x4[16] = { + 0, 1, 4, 8, + 5, 2, 3, 6, + 9, 12, 13, 10, + 7, 11, 14, 15, +}; + +/* Compute Ceil(Log2(v)) */ +/* Derived from branchless code for integer log2(v) from: + */ +static guint +ceil_log2 (guint32 v) +{ + guint r, shift; + + v--; + r = (v > 0xFFFF) << 4; + v >>= r; + shift = (v > 0xFF) << 3; + v >>= shift; + r |= shift; + shift = (v > 0xF) << 2; + v >>= shift; + r |= shift; + shift = (v > 0x3) << 1; + v >>= shift; + r |= shift; + r |= (v >> 1); + return r + 1; +} + +/****** Nal parser ******/ + +typedef struct +{ + const guint8 *data; + guint size; + + guint byte; /* Byte position */ + guint bits_in_cache; /* bitpos in the cache of next bit */ + guint8 first_byte; + guint64 cache; /* cached bytes */ +} NalReader; + +static void +nal_reader_init (NalReader * nr, const guint8 * data, guint size) +{ + nr->data = data; + nr->size = size; + + nr->byte = 0; + nr->bits_in_cache = 0; + /* fill with something other than 0 to detect emulation prevention bytes */ + nr->first_byte = 0xff; + nr->cache = 0xff; +} + +static gboolean +nal_reader_read (NalReader * nr, guint nbits) +{ + if (G_UNLIKELY (nr->byte * 8 + (nbits - nr->bits_in_cache) > nr->size * 8)) { + GST_DEBUG ("Can not read %u bits, bits in cache %u, Byte * 8 %u, size in " + "bits %u", nbits, nr->bits_in_cache, nr->byte * 8, nr->size * 8); + return FALSE; + } + + while (nr->bits_in_cache < nbits) { + guint8 byte; + gboolean check_three_byte; + + check_three_byte = TRUE; + next_byte: + if (G_UNLIKELY (nr->byte >= nr->size)) + return FALSE; + + byte = nr->data[nr->byte++]; + + /* check if the byte is a emulation_prevention_three_byte */ + if (check_three_byte && byte == 0x03 && nr->first_byte == 0x00 && + ((nr->cache & 0xff) == 0)) { + /* next byte goes unconditionally to the cache, even if it's 0x03 */ + check_three_byte = FALSE; + goto next_byte; + } + nr->cache = (nr->cache << 8) | nr->first_byte; + nr->first_byte = byte; + nr->bits_in_cache += 8; + } + + return TRUE; +} + +static inline gboolean +nal_reader_skip (NalReader * nr, guint nbits) +{ + g_return_val_if_fail (nr != NULL, FALSE); + + if (G_UNLIKELY (!nal_reader_read (nr, nbits))) + return FALSE; + + nr->bits_in_cache -= nbits; + + return TRUE; +} + +static inline gboolean +nal_reader_skip_to_byte (NalReader * nr) +{ + g_return_val_if_fail (nr != NULL, FALSE); + + if (nr->bits_in_cache == 0) { + if (G_LIKELY ((nr->size - nr->byte) > 0)) + nr->byte++; + else + return FALSE; + } + + nr->bits_in_cache = 0; + + return TRUE; +} + +static inline guint +nal_reader_get_pos (const NalReader * nr) +{ + return nr->byte * 8 - nr->bits_in_cache; +} + +static inline guint +nal_reader_get_remaining (const NalReader * nr) +{ + return (nr->size - nr->byte) * 8 + nr->bits_in_cache; +} + +#define GST_NAL_READER_READ_BITS(bits) \ +static gboolean \ +nal_reader_get_bits_uint##bits (NalReader *nr, guint##bits *val, guint nbits) \ +{ \ + guint shift; \ + \ + g_return_val_if_fail (nr != NULL, FALSE); \ + g_return_val_if_fail (val != NULL, FALSE); \ + g_return_val_if_fail (nbits <= bits, FALSE); \ + \ + if (!nal_reader_read (nr, nbits)) \ + return FALSE; \ + \ + /* bring the required bits down and truncate */ \ + shift = nr->bits_in_cache - nbits; \ + *val = nr->first_byte >> shift; \ + \ + *val |= nr->cache << (8 - shift); \ + /* mask out required bits */ \ + if (nbits < bits) \ + *val &= ((guint##bits)1 << nbits) - 1; \ + \ + nr->bits_in_cache = shift; \ + \ + return TRUE; \ +} \ + +GST_NAL_READER_READ_BITS (8); +GST_NAL_READER_READ_BITS (16); +GST_NAL_READER_READ_BITS (32); + +#define GST_NAL_READER_PEAK_BITS(bits) \ +static gboolean \ +nal_reader_peek_bits_uint##bits (const NalReader *nr, guint##bits *val, guint nbits) \ +{ \ + NalReader tmp; \ + \ + g_return_val_if_fail (nr != NULL, FALSE); \ + tmp = *nr; \ + return nal_reader_get_bits_uint##bits (&tmp, val, nbits); \ +} + +GST_NAL_READER_PEAK_BITS (8); + +static gboolean +nal_reader_get_ue (NalReader * nr, guint32 * val) +{ + guint i = 0; + guint8 bit; + guint32 value; + + if (G_UNLIKELY (!nal_reader_get_bits_uint8 (nr, &bit, 1))) { + + return FALSE; + } + + while (bit == 0) { + i++; + if G_UNLIKELY + ((!nal_reader_get_bits_uint8 (nr, &bit, 1))) + return FALSE; + } + + g_return_val_if_fail (i <= 32, FALSE); + + if (G_UNLIKELY (!nal_reader_get_bits_uint32 (nr, &value, i))) + return FALSE; + + *val = (1 << i) - 1 + value; + + return TRUE; +} + +static gboolean +nal_reader_get_se (NalReader * nr, gint32 * val) +{ + guint32 value; + + if (G_UNLIKELY (!nal_reader_get_ue (nr, &value))) + return FALSE; + + if (value % 2) + *val = (value / 2) + 1; + else + *val = -(value / 2); + + return TRUE; +} + +#define CHECK_ALLOWED(val, min, max) { \ + if (val < min || val > max) { \ + GST_WARNING ("value not in allowed range. value: %d, range %d-%d", \ + val, min, max); \ + goto error; \ + } \ +} + +#define READ_UINT8(nr, val, nbits) { \ + if (!nal_reader_get_bits_uint8 (nr, &val, nbits)) { \ + GST_WARNING ("failed to read uint8, nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define READ_UINT16(nr, val, nbits) { \ + if (!nal_reader_get_bits_uint16 (nr, &val, nbits)) { \ + GST_WARNING ("failed to read uint16, nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define READ_UINT32(nr, val, nbits) { \ + if (!nal_reader_get_bits_uint32 (nr, &val, nbits)) { \ + GST_WARNING ("failed to read uint32, nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define READ_UINT64(nr, val, nbits) { \ + if (!nal_reader_get_bits_uint64 (nr, &val, nbits)) { \ + GST_WARNING ("failed to read uint32, nbits: %d", nbits); \ + goto error; \ + } \ +} + +#define READ_UE(nr, val) { \ + if (!nal_reader_get_ue (nr, &val)) { \ + GST_WARNING ("failed to read UE"); \ + goto error; \ + } \ +} + +#define READ_UE_ALLOWED(nr, val, min, max) { \ + guint32 tmp; \ + READ_UE (nr, tmp); \ + CHECK_ALLOWED (tmp, min, max); \ + val = tmp; \ +} + +#define READ_SE(nr, val) { \ + if (!nal_reader_get_se (nr, &val)) { \ + GST_WARNING ("failed to read SE"); \ + goto error; \ + } \ +} + +#define READ_SE_ALLOWED(nr, val, min, max) { \ + gint32 tmp; \ + READ_SE (nr, tmp); \ + CHECK_ALLOWED (tmp, min, max); \ + val = tmp; \ +} + +/*********** end of nal parser ***************/ + +/***** Utils ****/ +#define EXTENDED_SAR 255 + +static GstH264SPS * +gst_h264_parser_get_sps (GstH264NalParser * nalparser, guint8 sps_id) +{ + GstH264SPS *sps; + + sps = &nalparser->sps[sps_id]; + + if (sps->valid) + return sps; + + return NULL; +} + +static GstH264PPS * +gst_h264_parser_get_pps (GstH264NalParser * nalparser, guint8 pps_id) +{ + GstH264PPS *pps; + + pps = &nalparser->pps[pps_id]; + + if (pps->valid) + return pps; + + return NULL; +} + +static inline void +set_nalu_datas (GstH264NalUnit * nalu) +{ + guint8 *data = nalu->data + nalu->offset; + + nalu->type = (data[0] & 0x1f); + nalu->ref_idc = (data[0] & 0x60) >> 5; + nalu->idr_pic_flag = (nalu->type == 5 ? 1 : 0); + + GST_DEBUG ("Nal type %u, ref_idc %u", nalu->type, nalu->ref_idc); +} + +static inline gint +scan_for_start_codes (const guint8 * data, guint size) +{ + GstByteReader br; + gst_byte_reader_init (&br, data, size); + + /* NALU not empty, so we can at least expect 1 (even 2) bytes following sc */ + return gst_byte_reader_masked_scan_uint32 (&br, 0xffffff00, 0x00000100, + 0, size); +} + +static gboolean +gst_h264_parser_more_data (NalReader * nr) +{ + guint remaining; + + remaining = nal_reader_get_remaining (nr); + if (remaining == 0) + return FALSE; + + if (remaining <= 8) { + guint8 rbsp_stop_one_bit; + + if (!nal_reader_peek_bits_uint8 (nr, &rbsp_stop_one_bit, 1)) + return FALSE; + + if (rbsp_stop_one_bit == 1) { + guint8 zero_bits; + + if (remaining == 1) + return FALSE; + + if (!nal_reader_peek_bits_uint8 (nr, &zero_bits, remaining)) + return FALSE; + + if ((zero_bits - (1 << (remaining - 1))) == 0) + return FALSE; + } + } + + return TRUE; +} + +/****** Parsing functions *****/ + +static gboolean +gst_h264_parse_hrd_parameters (GstH264HRDParams * hrd, NalReader * nr) +{ + guint sched_sel_idx; + + GST_DEBUG ("parsing \"HRD Parameters\""); + + READ_UE_ALLOWED (nr, hrd->cpb_cnt_minus1, 0, 31); + READ_UINT8 (nr, hrd->bit_rate_scale, 4); + READ_UINT8 (nr, hrd->cpb_size_scale, 4); + + for (sched_sel_idx = 0; sched_sel_idx <= hrd->cpb_cnt_minus1; sched_sel_idx++) { + READ_UE (nr, hrd->bit_rate_value_minus1[sched_sel_idx]); + READ_UE (nr, hrd->cpb_size_value_minus1[sched_sel_idx]); + } + + READ_UINT8 (nr, hrd->initial_cpb_removal_delay_length_minus1, 5); + READ_UINT8 (nr, hrd->cpb_removal_delay_length_minus1, 5); + READ_UINT8 (nr, hrd->dpb_output_delay_length_minus1, 5); + READ_UINT8 (nr, hrd->time_offset_length, 5); + + return TRUE; + +error: + GST_WARNING ("error parsing \"HRD Parameters\""); + return FALSE; +} + +static gboolean +gst_h264_parse_vui_parameters (GstH264SPS * sps, NalReader * nr) +{ + GstH264VUIParams *vui = &sps->vui_parameters; + + GST_DEBUG ("parsing \"VUI Parameters\""); + + /* set default values for fields that might not be present in the bitstream + and have valid defaults */ + vui->aspect_ratio_idc = 0; + vui->video_format = 5; + vui->video_full_range_flag = 0; + vui->colour_primaries = 2; + vui->transfer_characteristics = 2; + vui->matrix_coefficients = 2; + vui->chroma_sample_loc_type_top_field = 0; + vui->chroma_sample_loc_type_bottom_field = 0; + vui->low_delay_hrd_flag = 0; + + READ_UINT8 (nr, vui->aspect_ratio_info_present_flag, 1); + if (vui->aspect_ratio_info_present_flag) { + READ_UINT8 (nr, vui->aspect_ratio_idc, 8); + if (vui->aspect_ratio_idc == EXTENDED_SAR) { + READ_UINT16 (nr, vui->sar_width, 16); + READ_UINT16 (nr, vui->sar_height, 16); + } + } + + READ_UINT8 (nr, vui->overscan_info_present_flag, 1); + if (vui->overscan_info_present_flag) + READ_UINT8 (nr, vui->overscan_appropriate_flag, 1); + + READ_UINT8 (nr, vui->video_signal_type_present_flag, 1); + if (vui->video_signal_type_present_flag) { + + READ_UINT8 (nr, vui->video_format, 3); + READ_UINT8 (nr, vui->video_full_range_flag, 1); + READ_UINT8 (nr, vui->colour_description_present_flag, 1); + if (vui->colour_description_present_flag) { + READ_UINT8 (nr, vui->colour_primaries, 8); + READ_UINT8 (nr, vui->transfer_characteristics, 8); + READ_UINT8 (nr, vui->matrix_coefficients, 8); + } + } + + READ_UINT8 (nr, vui->chroma_loc_info_present_flag, 1); + if (vui->chroma_loc_info_present_flag) { + READ_UE_ALLOWED (nr, vui->chroma_sample_loc_type_top_field, 0, 5); + READ_UE_ALLOWED (nr, vui->chroma_sample_loc_type_bottom_field, 0, 5); + } + + READ_UINT8 (nr, vui->timing_info_present_flag, 1); + if (vui->timing_info_present_flag) { + READ_UINT32 (nr, vui->num_units_in_tick, 32); + if (vui->num_units_in_tick == 0) + GST_WARNING ("num_units_in_tick = 0 detected in stream " + "(incompliant to H.264 E.2.1)."); + + READ_UINT32 (nr, vui->time_scale, 32); + if (vui->time_scale == 0) + GST_WARNING ("time_scale = 0 detected in stream " + "(incompliant to H.264 E.2.1)."); + + READ_UINT8 (nr, vui->fixed_frame_rate_flag, 1); + } + + READ_UINT8 (nr, vui->nal_hrd_parameters_present_flag, 1); + if (vui->nal_hrd_parameters_present_flag) { + if (!gst_h264_parse_hrd_parameters (&vui->nal_hrd_parameters, nr)) + goto error; + } + + READ_UINT8 (nr, vui->vcl_hrd_parameters_present_flag, 1); + if (vui->vcl_hrd_parameters_present_flag) { + if (!gst_h264_parse_hrd_parameters (&vui->vcl_hrd_parameters, nr)) + goto error; + } + + if (vui->nal_hrd_parameters_present_flag || + vui->vcl_hrd_parameters_present_flag) + READ_UINT8 (nr, vui->low_delay_hrd_flag, 1); + + READ_UINT8 (nr, vui->pic_struct_present_flag, 1); + READ_UINT8 (nr, vui->bitstream_restriction_flag, 1); + if (vui->bitstream_restriction_flag) { + READ_UINT8 (nr, vui->motion_vectors_over_pic_boundaries_flag, 1); + READ_UE (nr, vui->max_bytes_per_pic_denom); + READ_UE_ALLOWED (nr, vui->max_bits_per_mb_denom, 0, 16); + READ_UE_ALLOWED (nr, vui->log2_max_mv_length_horizontal, 0, 16); + READ_UE_ALLOWED (nr, vui->log2_max_mv_length_vertical, 0, 16); + READ_UE_ALLOWED (nr, vui->log2_max_mv_length_vertical, 0, 16); + READ_UE (nr, vui->num_reorder_frames); + READ_UE (nr, vui->max_dec_frame_buffering); + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"VUI Parameters\""); + return FALSE; +} + +static gboolean +gst_h264_parser_parse_scaling_list (NalReader * nr, + guint8 scaling_lists_4x4[6][16], guint8 scaling_lists_8x8[6][64], + const guint8 fallback_4x4_inter[16], const guint8 fallback_4x4_intra[16], + const guint8 fallback_8x8_inter[64], const guint8 fallback_8x8_intra[64], + guint8 n_lists) +{ + guint i; + + GST_DEBUG ("parsing scaling lists"); + + for (i = 0; i < 12; i++) { + gboolean use_default = FALSE; + + if (i < n_lists) { + guint8 scaling_list_present_flag; + + READ_UINT8 (nr, scaling_list_present_flag, 1); + if (scaling_list_present_flag) { + guint8 *scaling_list; + const guint8 *scan; + guint size; + guint j; + guint8 last_scale, next_scale; + + if (i < 6) { + scaling_list = scaling_lists_4x4[i]; + scan = zigzag_4x4; + size = 16; + } else { + scaling_list = scaling_lists_8x8[i - 6]; + scan = zigzag_8x8; + size = 64; + } + + last_scale = 8; + next_scale = 8; + for (j = 0; j < size; j++) { + if (next_scale != 0) { + gint32 delta_scale; + + READ_SE (nr, delta_scale); + next_scale = (last_scale + delta_scale) & 0xff; + } + if (j == 0 && next_scale == 0) { + use_default = TRUE; + break; + } + last_scale = scaling_list[scan[j]] = + (next_scale == 0) ? last_scale : next_scale; + } + } else + use_default = TRUE; + } else + use_default = TRUE; + + if (use_default) { + switch (i) { + case 0: + memcpy (scaling_lists_4x4[0], fallback_4x4_intra, 16); + break; + case 1: + memcpy (scaling_lists_4x4[1], scaling_lists_4x4[0], 16); + break; + case 2: + memcpy (scaling_lists_4x4[2], scaling_lists_4x4[1], 16); + break; + case 3: + memcpy (scaling_lists_4x4[3], fallback_4x4_inter, 16); + break; + case 4: + memcpy (scaling_lists_4x4[4], scaling_lists_4x4[3], 16); + break; + case 5: + memcpy (scaling_lists_4x4[5], scaling_lists_4x4[4], 16); + break; + case 6: + memcpy (scaling_lists_8x8[0], fallback_8x8_intra, 64); + break; + case 7: + memcpy (scaling_lists_8x8[1], fallback_8x8_inter, 64); + break; + case 8: + memcpy (scaling_lists_8x8[2], scaling_lists_8x8[0], 64); + break; + case 9: + memcpy (scaling_lists_8x8[3], scaling_lists_8x8[1], 64); + break; + case 10: + memcpy (scaling_lists_8x8[4], scaling_lists_8x8[2], 64); + break; + case 11: + memcpy (scaling_lists_8x8[5], scaling_lists_8x8[3], 64); + break; + + default: + break; + } + } + } + + return TRUE; + +error: + GST_WARNING ("error parsing scaling lists"); + return FALSE; +} + +static gboolean +slice_parse_ref_pic_list_reordering (GstH264SliceHdr * slice, NalReader * nr) +{ + GST_DEBUG ("parsing \"Reference picture list reordering\""); + + if (!GST_H264_IS_I_SLICE (slice) && !GST_H264_IS_SI_SLICE (slice)) { + guint8 ref_pic_list_reordering_flag_l0; + guint32 reordering_of_pic_nums_idc; + + READ_UINT8 (nr, ref_pic_list_reordering_flag_l0, 1); + if (ref_pic_list_reordering_flag_l0) + do { + READ_UE (nr, reordering_of_pic_nums_idc); + if (reordering_of_pic_nums_idc == 0 || reordering_of_pic_nums_idc == 1) { + guint32 abs_diff_pic_num_minus1 G_GNUC_UNUSED; + + READ_UE_ALLOWED (nr, abs_diff_pic_num_minus1, 0, + slice->max_pic_num - 1); + } else if (reordering_of_pic_nums_idc == 2) { + guint32 long_term_pic_num; + + READ_UE (nr, long_term_pic_num); + } + } while (reordering_of_pic_nums_idc != 3); + } + + if (GST_H264_IS_B_SLICE (slice)) { + guint8 ref_pic_list_reordering_flag_l1; + guint32 reordering_of_pic_nums_idc; + + READ_UINT8 (nr, ref_pic_list_reordering_flag_l1, 1); + if (ref_pic_list_reordering_flag_l1) + do { + READ_UE (nr, reordering_of_pic_nums_idc); + if (reordering_of_pic_nums_idc == 0 || reordering_of_pic_nums_idc == 1) { + guint32 abs_diff_num_minus1; + + READ_UE (nr, abs_diff_num_minus1); + } else if (reordering_of_pic_nums_idc == 2) { + guint32 long_term_pic_num; + + READ_UE (nr, long_term_pic_num); + } + } while (reordering_of_pic_nums_idc != 3); + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"Reference picture list reordering\""); + return FALSE; +} + +static gboolean +gst_h264_slice_parse_dec_ref_pic_marking (GstH264SliceHdr * slice, + GstH264NalUnit * nalu, NalReader * nr) +{ + GstH264DecRefPicMarking *dec_ref_pic_m; + + GST_DEBUG ("parsing \"Decoded reference picture marking\""); + + dec_ref_pic_m = &slice->dec_ref_pic_marking; + + if (nalu->idr_pic_flag) { + READ_UINT8 (nr, dec_ref_pic_m->no_output_of_prior_pics_flag, 1); + READ_UINT8 (nr, dec_ref_pic_m->long_term_reference_flag, 1); + } else { + READ_UINT8 (nr, dec_ref_pic_m->adaptive_ref_pic_marking_mode_flag, 1); + if (dec_ref_pic_m->adaptive_ref_pic_marking_mode_flag) { + guint32 mem_mgmt_ctrl_op; + GstH264RefPicMarking *refpicmarking; + + dec_ref_pic_m->n_ref_pic_marking = 0; + while (1) { + refpicmarking = + &dec_ref_pic_m->ref_pic_marking[dec_ref_pic_m->n_ref_pic_marking]; + + READ_UE (nr, mem_mgmt_ctrl_op); + if (mem_mgmt_ctrl_op == 0) + break; + + refpicmarking->memory_management_control_operation = mem_mgmt_ctrl_op; + + if (mem_mgmt_ctrl_op == 1 || mem_mgmt_ctrl_op == 3) + READ_UE (nr, refpicmarking->difference_of_pic_nums_minus1); + + if (mem_mgmt_ctrl_op == 2) + READ_UE (nr, refpicmarking->long_term_pic_num); + + if (mem_mgmt_ctrl_op == 3 || mem_mgmt_ctrl_op == 6) + READ_UE (nr, refpicmarking->long_term_frame_idx); + + if (mem_mgmt_ctrl_op == 4) + READ_UE (nr, refpicmarking->max_long_term_frame_idx_plus1); + + dec_ref_pic_m->n_ref_pic_marking++; + } + } + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"Decoded reference picture marking\""); + return FALSE; +} + +static gboolean +gst_h264_slice_parse_pred_weight_table (GstH264SliceHdr * slice, + NalReader * nr, guint8 chroma_array_type) +{ + GstH264PredWeightTable *p; + gint16 default_luma_weight, default_chroma_weight; + gint i; + + GST_DEBUG ("parsing \"Prediction weight table\""); + + p = &slice->pred_weight_table; + + READ_UE_ALLOWED (nr, p->luma_log2_weight_denom, 0, 7); + /* set default values */ + default_luma_weight = 1 << p->luma_log2_weight_denom; + for (i = 0; i < G_N_ELEMENTS (p->luma_weight_l0); i++) + p->luma_weight_l0[i] = default_luma_weight; + memset (p->luma_offset_l0, 0, sizeof (p->luma_offset_l0)); + if (GST_H264_IS_B_SLICE (slice)) { + for (i = 0; i < G_N_ELEMENTS (p->luma_weight_l1); i++) + p->luma_weight_l1[i] = default_luma_weight; + memset (p->luma_offset_l1, 0, sizeof (p->luma_offset_l1)); + } + + if (chroma_array_type != 0) { + READ_UE_ALLOWED (nr, p->chroma_log2_weight_denom, 0, 7); + /* set default values */ + default_chroma_weight = 1 << p->chroma_log2_weight_denom; + for (i = 0; i < G_N_ELEMENTS (p->chroma_weight_l0); i++) { + p->chroma_weight_l0[i][0] = default_chroma_weight; + p->chroma_weight_l0[i][1] = default_chroma_weight; + } + memset (p->chroma_offset_l0, 0, sizeof (p->chroma_offset_l0)); + if (GST_H264_IS_B_SLICE (slice)) { + for (i = 0; i < G_N_ELEMENTS (p->chroma_weight_l1); i++) { + p->chroma_weight_l1[i][0] = default_chroma_weight; + p->chroma_weight_l1[i][1] = default_chroma_weight; + } + memset (p->chroma_offset_l1, 0, sizeof (p->chroma_offset_l1)); + } + } + + for (i = 0; i <= slice->num_ref_idx_l0_active_minus1; i++) { + guint8 luma_weight_l0_flag; + + READ_UINT8 (nr, luma_weight_l0_flag, 1); + if (luma_weight_l0_flag) { + READ_SE_ALLOWED (nr, p->luma_weight_l0[i], -128, 127); + READ_SE_ALLOWED (nr, p->luma_offset_l0[i], -128, 127); + } + if (chroma_array_type != 0) { + guint8 chroma_weight_l0_flag; + gint j; + + READ_UINT8 (nr, chroma_weight_l0_flag, 1); + if (chroma_weight_l0_flag) { + for (j = 0; j < 2; j++) { + READ_SE_ALLOWED (nr, p->chroma_weight_l0[i][j], -128, 127); + READ_SE_ALLOWED (nr, p->chroma_offset_l0[i][j], -128, 127); + } + } + } + } + + if (GST_H264_IS_B_SLICE (slice)) { + for (i = 0; i <= slice->num_ref_idx_l1_active_minus1; i++) { + guint8 luma_weight_l1_flag; + + READ_UINT8 (nr, luma_weight_l1_flag, 1); + if (luma_weight_l1_flag) { + READ_SE_ALLOWED (nr, p->luma_weight_l1[i], -128, 127); + READ_SE_ALLOWED (nr, p->luma_offset_l1[i], -128, 127); + } + if (chroma_array_type != 0) { + guint8 chroma_weight_l1_flag; + gint j; + + READ_UINT8 (nr, chroma_weight_l1_flag, 1); + if (chroma_weight_l1_flag) { + for (j = 0; j < 2; j++) { + READ_SE_ALLOWED (nr, p->chroma_weight_l1[i][j], -128, 127); + READ_SE_ALLOWED (nr, p->chroma_offset_l1[i][j], -128, 127); + } + } + } + } + } + + return TRUE; + +error: + GST_WARNING ("error parsing \"Prediction weight table\""); + return FALSE; +} + +static gboolean +gst_h264_parser_parse_buffering_period (GstH264NalParser * nalparser, + GstH264BufferingPeriod * per, NalReader * nr) +{ + GstH264SPS *sps; + guint8 sps_id; + + GST_DEBUG ("parsing \"Buffering period\""); + + READ_UE_ALLOWED (nr, sps_id, 0, GST_H264_MAX_SPS_COUNT); + sps = gst_h264_parser_get_sps (nalparser, sps_id); + if (!sps) { + GST_WARNING ("couldn't find associated sequence parameter set with id: %d", + sps_id); + return GST_H264_PARSER_BROKEN_LINK; + } + per->sps = sps; + + if (sps->vui_parameters_present_flag) { + GstH264VUIParams *vui = &sps->vui_parameters; + + if (vui->nal_hrd_parameters_present_flag) { + GstH264HRDParams *hrd = &vui->nal_hrd_parameters; + guint8 sched_sel_idx; + + for (sched_sel_idx = 0; sched_sel_idx <= hrd->cpb_cnt_minus1; + sched_sel_idx++) { + READ_UINT8 (nr, per->nal_initial_cpb_removal_delay[sched_sel_idx], 5); + READ_UINT8 (nr, + per->nal_initial_cpb_removal_delay_offset[sched_sel_idx], 5); + } + } + + if (vui->vcl_hrd_parameters_present_flag) { + GstH264HRDParams *hrd = &vui->vcl_hrd_parameters; + guint8 sched_sel_idx; + + for (sched_sel_idx = 0; sched_sel_idx <= hrd->cpb_cnt_minus1; + sched_sel_idx++) { + READ_UINT8 (nr, per->vcl_initial_cpb_removal_delay[sched_sel_idx], 5); + READ_UINT8 (nr, + per->vcl_initial_cpb_removal_delay_offset[sched_sel_idx], 5); + } + } + } + + return GST_H264_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Buffering period\""); + return GST_H264_PARSER_ERROR; +} + +static gboolean +gst_h264_parse_clock_timestamp (GstH264ClockTimestamp * tim, + GstH264VUIParams * vui, NalReader * nr) +{ + guint8 full_timestamp_flag; + guint8 time_offset_length; + + GST_DEBUG ("parsing \"Clock timestamp\""); + + /* defalt values */ + tim->time_offset = 0; + + READ_UINT8 (nr, tim->ct_type, 2); + READ_UINT8 (nr, tim->nuit_field_based_flag, 1); + READ_UINT8 (nr, tim->counting_type, 5); + READ_UINT8 (nr, full_timestamp_flag, 1); + READ_UINT8 (nr, tim->discontinuity_flag, 1); + READ_UINT8 (nr, tim->cnt_dropped_flag, 1); + READ_UINT8 (nr, tim->n_frames, 8); + + if (full_timestamp_flag) { + tim->seconds_flag = TRUE; + READ_UINT8 (nr, tim->seconds_value, 6); + + tim->minutes_flag = TRUE; + READ_UINT8 (nr, tim->minutes_value, 6); + + tim->hours_flag = TRUE; + READ_UINT8 (nr, tim->hours_value, 5); + } else { + READ_UINT8 (nr, tim->seconds_flag, 1); + if (tim->seconds_flag) { + READ_UINT8 (nr, tim->seconds_value, 6); + READ_UINT8 (nr, tim->minutes_flag, 1); + if (tim->minutes_flag) { + READ_UINT8 (nr, tim->minutes_value, 6); + READ_UINT8 (nr, tim->hours_flag, 1); + if (tim->hours_flag) + READ_UINT8 (nr, tim->hours_value, 5); + } + } + } + + time_offset_length = 0; + if (vui->nal_hrd_parameters_present_flag) + time_offset_length = vui->nal_hrd_parameters.time_offset_length; + else if (vui->vcl_hrd_parameters_present_flag) + time_offset_length = vui->vcl_hrd_parameters.time_offset_length; + + if (time_offset_length > 0) + READ_UINT32 (nr, tim->time_offset, time_offset_length); + +error: + GST_WARNING ("error parsing \"Clock timestamp\""); + return FALSE; +} + +static gboolean +gst_h264_parser_parse_pic_timing (GstH264NalParser * nalparser, + GstH264PicTiming * tim, NalReader * nr) +{ + GST_DEBUG ("parsing \"Picture timing\""); + if (!nalparser->last_sps || !nalparser->last_sps->valid) { + GST_WARNING ("didn't get the associated sequence paramater set for the " + "current access unit"); + goto error; + } + + /* default values */ + memset (tim->clock_timestamp_flag, 0, 3); + + if (nalparser->last_sps->vui_parameters_present_flag) { + GstH264VUIParams *vui = &nalparser->last_sps->vui_parameters; + + if (vui->nal_hrd_parameters_present_flag) { + READ_UINT32 (nr, tim->cpb_removal_delay, + vui->nal_hrd_parameters.cpb_removal_delay_length_minus1 + 1); + READ_UINT32 (nr, tim->dpb_output_delay, + vui->nal_hrd_parameters.dpb_output_delay_length_minus1 + 1); + } else if (vui->nal_hrd_parameters_present_flag) { + READ_UINT32 (nr, tim->cpb_removal_delay, + vui->vcl_hrd_parameters.cpb_removal_delay_length_minus1 + 1); + READ_UINT32 (nr, tim->dpb_output_delay, + vui->vcl_hrd_parameters.dpb_output_delay_length_minus1 + 1); + } + + if (vui->pic_struct_present_flag) { + const guint8 num_clock_ts_table[9] = { + 1, 1, 1, 2, 2, 3, 3, 2, 3 + }; + guint8 num_clock_num_ts; + guint i; + + READ_UINT8 (nr, tim->pic_struct, 4); + CHECK_ALLOWED (tim->pic_struct, 0, 8); + + num_clock_num_ts = num_clock_ts_table[tim->pic_struct]; + for (i = 0; i < num_clock_num_ts; i++) { + READ_UINT8 (nr, tim->clock_timestamp_flag[i], 1); + if (tim->clock_timestamp_flag[i]) { + if (!gst_h264_parse_clock_timestamp (&tim->clock_timestamp[i], vui, + nr)) + goto error; + } + } + } + } + + return GST_H264_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Picture timing\""); + return GST_H264_PARSER_ERROR; +} + +/******** API *************/ + +/** + * gst_h264_nal_parser_new: + * + * Creates a nez #GstH264NalParser + * + * Returns: a new #GstH264NalParser + */ +GstH264NalParser * +gst_h264_nal_parser_new (void) +{ + GstH264NalParser *nalparser; + + nalparser = g_malloc0 (sizeof (GstH264NalParser)); + GST_DEBUG_CATEGORY_INIT (h264_parser_debug, "codecparsers_h264", 0, + "h264 parser library"); + + return nalparser; +} + +/** + * gst_h264_parser_identify_nalu: + * @nalparser: a #GstH264NalParser + * @data: The data to parse + * @offset: the offset from which to parse @data + * @size: the size of @data + * @nalu: The #GstH264NalUnit where to store parsed nal headers + * + * Parses the buffer and set @nalu from the next nalu data from @data + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parser_identify_nalu (GstH264NalParser * nalparser, + const guint8 * data, guint offset, gsize size, GstH264NalUnit * nalu) +{ + gint off1, off2; + + if (size - offset < 4) { + GST_DEBUG ("Can't parse, buffer has too small size %u, offset %u", size, + offset); + return GST_H264_PARSER_ERROR; + } + + off1 = scan_for_start_codes (data + offset, size - offset); + + if (off1 < 0) { + GST_DEBUG ("No start code prefix in this buffer"); + return GST_H264_PARSER_NO_NAL; + } + + if (offset + off1 == size - 1) { + GST_DEBUG ("Missing data to identify nal unit"); + + return GST_H264_PARSER_ERROR; + } + + nalu->valid = TRUE; + nalu->sc_offset = offset + off1; + /* sc might have 2 or 3 0-bytes */ + if (nalu->sc_offset > 0 && data[nalu->sc_offset - 1] == 00) + nalu->sc_offset--; + + nalu->offset = offset + off1 + 3; + nalu->data = (guint8 *) data; + set_nalu_datas (nalu); + + if (nalu->type == GST_H264_NAL_SEQ_END || + nalu->type == GST_H264_NAL_STREAM_END) { + GST_DEBUG ("end-of-seq or end-of-stream nal found"); + nalu->size = 0; + return GST_H264_PARSER_OK; + } + + off2 = scan_for_start_codes (data + nalu->offset, size - nalu->offset); + if (off2 < 0) { + GST_DEBUG ("Nal start %d, No end found", nalu->offset); + + return GST_H264_PARSER_NO_NAL_END; + } + + if (off2 > 0 && data[nalu->offset + off2 - 1] == 00) + off2--; + + nalu->size = off2; + if (nalu->size < 2) + return GST_H264_PARSER_BROKEN_DATA; + + GST_DEBUG ("Complete nal found. Off: %d, Size: %d", nalu->offset, nalu->size); + return GST_H264_PARSER_OK; +} + +/** + * gst_h264_parser_identify_nalu_avc: + * @data: The data to parse, must be the beging of the Nal unit + * @size: the size of @data + * @nal_length_size: the size in bytes of the AVC nal length prefix. + * @nalu: The #GstH264NalUnit where to store parsed nal headers + * + * Parses the data and sets @nalu from @data. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parser_identify_nalu_avc (GstH264NalParser * nalparser, + const guint8 * data, guint offset, gsize size, guint8 nal_length_size, + GstH264NalUnit * nalu) +{ + GstBitReader br; + + size = size - offset; + gst_bit_reader_init (&br, data + offset, size); + + gst_bit_reader_get_bits_uint32 (&br, &nalu->size, nal_length_size * 8); + nalu->sc_offset = offset; + nalu->offset = offset + nal_length_size; + + if (size < nalu->size + nal_length_size) { + nalu->size = 0; + + return GST_H264_PARSER_NO_NAL_END; + } + + nalu->data = (guint8 *) data; + + set_nalu_datas (nalu); + + if (nalu->size < 2) + return GST_H264_PARSER_BROKEN_DATA; + + nalu->valid = TRUE; + + return GST_H264_PARSER_OK; +} + +GstH264ParserResult +gst_h264_parser_parse_nal (GstH264NalParser * nalparser, GstH264NalUnit * nalu) +{ + GstH264SPS sps; + GstH264PPS pps; + + switch (nalu->type) { + case GST_H264_NAL_SPS: + return gst_h264_parser_parse_sps (nalparser, nalu, &sps, FALSE); + break; + case GST_H264_NAL_PPS: + return gst_h264_parser_parse_pps (nalparser, nalu, &pps); + } + + return GST_H264_PARSER_OK; +} + +/** + * gst_h264_parser_parse_sps: + * @nalparser: a #GstH264NalParser + * @nalu: The #GST_H264_NAL_SPS #GstH264NalUnit you want to parse + * @slice: The #GstH264SPS to set. + * @parse_vui_params: Whether to parse the vui_params or not + * + * Parses the @data, and sets the @sps. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parser_parse_sps (GstH264NalParser * nalparser, GstH264NalUnit * nalu, + GstH264SPS * sps, gboolean parse_vui_params) +{ + GstH264ParserResult res = gst_h264_parse_sps (nalu, sps, parse_vui_params); + + if (res == GST_H264_PARSER_OK) { + GST_DEBUG ("adding sequence parameter set with id: %d to array", sps->id); + + nalparser->sps[sps->id] = *sps; + nalparser->last_sps = &nalparser->sps[sps->id]; + } + + + + return res; +} + +/** + * gst_h264_parse_sps: + * @nalu: The #GST_H264_NAL_SPS #GstH264NalUnit you want to parse + * @slice: The #GstH264SPS to set. + * @parse_vui_params: Whether to parse the vui_params or not + * + * Parses the @data, and sets the @sps. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parse_sps (GstH264NalUnit * nalu, GstH264SPS * sps, + gboolean parse_vui_params) +{ + NalReader nr; + gint width, height; + guint8 frame_cropping_flag; + guint subwc[] = { 1, 2, 2, 1 }; + guint subhc[] = { 1, 2, 1, 1 }; + GstH264VUIParams *vui = NULL; + + GST_DEBUG ("parsing SPS"); + nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size - 1); + + /* set default values for fields that might not be present in the bitstream + and have valid defaults */ + sps->chroma_format_idc = 1; + sps->separate_colour_plane_flag = 0; + sps->bit_depth_luma_minus8 = 0; + sps->bit_depth_chroma_minus8 = 0; + memset (sps->scaling_lists_4x4, 16, 96); + memset (sps->scaling_lists_8x8, 16, 384); + sps->mb_adaptive_frame_field_flag = 0; + sps->frame_crop_left_offset = 0; + sps->frame_crop_right_offset = 0; + sps->frame_crop_top_offset = 0; + sps->frame_crop_bottom_offset = 0; + + READ_UINT8 (&nr, sps->profile_idc, 8); + READ_UINT8 (&nr, sps->constraint_set0_flag, 1); + READ_UINT8 (&nr, sps->constraint_set1_flag, 1); + READ_UINT8 (&nr, sps->constraint_set2_flag, 1); + READ_UINT8 (&nr, sps->constraint_set3_flag, 1); + + /* skip reserved_zero_4bits */ + if (!nal_reader_skip (&nr, 4)) + goto error; + + READ_UINT8 (&nr, sps->level_idc, 8); + + READ_UE_ALLOWED (&nr, sps->id, 0, GST_H264_MAX_SPS_COUNT); + + if (sps->profile_idc == 100 || sps->profile_idc == 110 || + sps->profile_idc == 122 || sps->profile_idc == 244 || + sps->profile_idc == 44 || sps->profile_idc == 83 || + sps->profile_idc == 86) { + READ_UE_ALLOWED (&nr, sps->chroma_format_idc, 0, 3); + if (sps->chroma_format_idc == 3) + READ_UINT8 (&nr, sps->separate_colour_plane_flag, 1); + + READ_UE_ALLOWED (&nr, sps->bit_depth_luma_minus8, 0, 6); + READ_UE_ALLOWED (&nr, sps->bit_depth_chroma_minus8, 0, 6); + READ_UINT8 (&nr, sps->qpprime_y_zero_transform_bypass_flag, 1); + + READ_UINT8 (&nr, sps->scaling_matrix_present_flag, 1); + if (sps->scaling_matrix_present_flag) { + guint8 n_lists; + + n_lists = (sps->chroma_format_idc != 3) ? 8 : 12; + if (!gst_h264_parser_parse_scaling_list (&nr, + sps->scaling_lists_4x4, sps->scaling_lists_8x8, + default_4x4_inter, default_4x4_intra, + default_8x8_inter, default_8x8_intra, n_lists)) + goto error; + } + } + + READ_UE_ALLOWED (&nr, sps->log2_max_frame_num_minus4, 0, 12); + + sps->max_frame_num = 1 << (sps->log2_max_frame_num_minus4 + 4); + + READ_UE_ALLOWED (&nr, sps->pic_order_cnt_type, 0, 2); + if (sps->pic_order_cnt_type == 0) { + READ_UE_ALLOWED (&nr, sps->log2_max_pic_order_cnt_lsb_minus4, 0, 12); + } else if (sps->pic_order_cnt_type == 1) { + guint i; + + READ_UINT8 (&nr, sps->delta_pic_order_always_zero_flag, 1); + READ_SE (&nr, sps->offset_for_non_ref_pic); + READ_SE (&nr, sps->offset_for_top_to_bottom_field); + READ_UE_ALLOWED (&nr, sps->num_ref_frames_in_pic_order_cnt_cycle, 0, 255); + + for (i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++) + READ_SE (&nr, sps->offset_for_ref_frame[i]); + } + + READ_UE (&nr, sps->num_ref_frames); + READ_UINT8 (&nr, sps->gaps_in_frame_num_value_allowed_flag, 1); + READ_UE (&nr, sps->pic_width_in_mbs_minus1); + READ_UE (&nr, sps->pic_height_in_map_units_minus1); + READ_UINT8 (&nr, sps->frame_mbs_only_flag, 1); + + if (!sps->frame_mbs_only_flag) + READ_UINT8 (&nr, sps->mb_adaptive_frame_field_flag, 1); + + READ_UINT8 (&nr, sps->direct_8x8_inference_flag, 1); + READ_UINT8 (&nr, frame_cropping_flag, 1); + if (frame_cropping_flag) { + READ_UE (&nr, sps->frame_crop_left_offset); + READ_UE (&nr, sps->frame_crop_right_offset); + READ_UE (&nr, sps->frame_crop_top_offset); + READ_UE (&nr, sps->frame_crop_bottom_offset); + } + + READ_UINT8 (&nr, sps->vui_parameters_present_flag, 1); + if (sps->vui_parameters_present_flag && parse_vui_params) { + if (!gst_h264_parse_vui_parameters (sps, &nr)) + goto error; + vui = &sps->vui_parameters; + } + + /* calculate ChromaArrayType */ + if (sps->separate_colour_plane_flag) + sps->chroma_array_type = 0; + else + sps->chroma_array_type = sps->chroma_format_idc; + + /* Calculate width and height */ + width = (sps->pic_width_in_mbs_minus1 + 1); + width *= 16; + height = (sps->pic_height_in_map_units_minus1 + 1); + height *= 16 * (2 - sps->frame_mbs_only_flag); + GST_LOG ("initial width=%d, height=%d", width, height); + + width -= (sps->frame_crop_left_offset + sps->frame_crop_right_offset) + * subwc[sps->chroma_format_idc]; + height -= (sps->frame_crop_top_offset + sps->frame_crop_bottom_offset + * subhc[sps->chroma_format_idc] * (2 - sps->frame_mbs_only_flag)); + if (width < 0 || height < 0) { + GST_WARNING ("invalid width/height in SPS"); + return FALSE; + } + GST_LOG ("final width=%u, height=%u", width, height); + sps->width = width; + sps->height = height; + + /* derive framerate */ + /* FIXME verify / also handle other cases */ + GST_LOG ("Framerate: %u %u %u %u", parse_vui_params, + vui->fixed_frame_rate_flag, sps->frame_mbs_only_flag, + vui->pic_struct_present_flag); + + if (parse_vui_params && vui->fixed_frame_rate_flag && + sps->frame_mbs_only_flag && !vui->pic_struct_present_flag) { + sps->fps_num = vui->time_scale; + sps->fps_den = vui->num_units_in_tick; + /* picture is a frame = 2 fields */ + sps->fps_den *= 2; + GST_LOG ("framerate %d/%d", sps->fps_num, sps->fps_den); + } + + sps->valid = TRUE; + + return GST_H264_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Sequence parameter set\""); + + return GST_H264_PARSER_ERROR; +} + +/** + * gst_h264_parse_pps: + * @nalparser: a #GstH264NalParser + * @data: the data to parse + * @size: the size of @data + * @nalu: The #GST_H264_NAL_PPS #GstH264NalUnit you want to parse + * @slice: The #GstH264PPS to set. + * + * Parses the @data, and sets the @pps. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parse_pps (GstH264NalParser * nalparser, GstH264NalUnit * nalu, + GstH264PPS * pps) +{ + NalReader nr; + GstH264SPS *sps; + gint sps_id; + guint8 pic_scaling_matrix_present_flag; + gint qp_bd_offset; + + GST_DEBUG ("parsing PPS"); + + nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size - 1); + + READ_UE_ALLOWED (&nr, pps->id, 0, GST_H264_MAX_PPS_COUNT); + READ_UE_ALLOWED (&nr, sps_id, 0, GST_H264_MAX_SPS_COUNT); + + sps = gst_h264_parser_get_sps (nalparser, sps_id); + if (!sps) { + GST_WARNING ("couldn't find associated sequence parameter set with id: %d", + sps_id); + return GST_H264_PARSER_BROKEN_LINK; + } + pps->sequence = sps; + qp_bd_offset = 6 * (sps->bit_depth_luma_minus8 + + sps->separate_colour_plane_flag); + + /* set default values for fields that might not be present in the bitstream + and have valid defaults */ + pps->slice_group_id = NULL; + pps->transform_8x8_mode_flag = 0; + memcpy (&pps->scaling_lists_4x4, &sps->scaling_lists_4x4, 96); + memcpy (&pps->scaling_lists_8x8, &sps->scaling_lists_8x8, 384); + + READ_UINT8 (&nr, pps->entropy_coding_mode_flag, 1); + READ_UINT8 (&nr, pps->pic_order_present_flag, 1); + READ_UE_ALLOWED (&nr, pps->num_slice_groups_minus1, 0, 7); + if (pps->num_slice_groups_minus1 > 0) { + READ_UE_ALLOWED (&nr, pps->slice_group_map_type, 0, 6); + + if (pps->slice_group_map_type == 0) { + gint i; + + for (i = 0; i <= pps->num_slice_groups_minus1; i++) + READ_UE (&nr, pps->run_length_minus1[i]); + } else if (pps->slice_group_map_type == 2) { + gint i; + + for (i = 0; i <= pps->num_slice_groups_minus1; i++) { + READ_UE (&nr, pps->top_left[i]); + READ_UE (&nr, pps->bottom_right[i]); + } + } else if (pps->slice_group_map_type >= 3 && pps->slice_group_map_type <= 5) { + READ_UINT8 (&nr, pps->slice_group_change_direction_flag, 1); + READ_UE (&nr, pps->slice_group_change_rate_minus1); + } else if (pps->slice_group_map_type == 6) { + gint bits; + gint i; + + READ_UE (&nr, pps->pic_size_in_map_units_minus1); + bits = g_bit_storage (pps->num_slice_groups_minus1); + + pps->slice_group_id = + g_new (guint8, pps->pic_size_in_map_units_minus1 + 1); + for (i = 0; i <= pps->pic_size_in_map_units_minus1; i++) + READ_UINT8 (&nr, pps->slice_group_id[i], bits); + } + } + + READ_UE_ALLOWED (&nr, pps->num_ref_idx_l0_active_minus1, 0, 31); + READ_UE_ALLOWED (&nr, pps->num_ref_idx_l1_active_minus1, 0, 31); + READ_UINT8 (&nr, pps->weighted_pred_flag, 1); + READ_UINT8 (&nr, pps->weighted_bipred_idc, 2); + READ_SE_ALLOWED (&nr, pps->pic_init_qp_minus26, -(26 + qp_bd_offset), 25); + READ_SE_ALLOWED (&nr, pps->pic_init_qs_minus26, -26, 25); + READ_SE_ALLOWED (&nr, pps->chroma_qp_index_offset, -12, 12); + pps->second_chroma_qp_index_offset = pps->chroma_qp_index_offset; + READ_UINT8 (&nr, pps->deblocking_filter_control_present_flag, 1); + READ_UINT8 (&nr, pps->constrained_intra_pred_flag, 1); + READ_UINT8 (&nr, pps->redundant_pic_cnt_present_flag, 1); + + if (!gst_h264_parser_more_data (&nr)) + goto done; + + READ_UINT8 (&nr, pps->transform_8x8_mode_flag, 1); + + READ_UINT8 (&nr, pic_scaling_matrix_present_flag, 1); + if (pic_scaling_matrix_present_flag) { + guint8 n_lists; + + n_lists = 6 + ((sps->chroma_format_idc != 3) ? 2 : 6) * + pps->transform_8x8_mode_flag; + + if (sps->scaling_matrix_present_flag) { + if (!gst_h264_parser_parse_scaling_list (&nr, + pps->scaling_lists_4x4, pps->scaling_lists_8x8, + sps->scaling_lists_4x4[0], sps->scaling_lists_4x4[3], + sps->scaling_lists_8x8[0], sps->scaling_lists_8x8[3], n_lists)) + goto error; + } else { + if (!gst_h264_parser_parse_scaling_list (&nr, + pps->scaling_lists_4x4, pps->scaling_lists_8x8, + default_4x4_inter, default_4x4_intra, + default_8x8_inter, default_8x8_intra, n_lists)) + goto error; + } + } + + READ_SE_ALLOWED (&nr, pps->second_chroma_qp_index_offset, -12, 12); + +done: + pps->valid = TRUE; + return GST_H264_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Picture parameter set\""); + return GST_H264_PARSER_ERROR; +} + +/** + * gst_h264_parser_parse_pps: + * @nalparser: a #GstH264NalParser + * @data: the data to parse + * @size: the size of @data + * @nalu: The #GST_H264_NAL_PPS #GstH264NalUnit you want to parse + * @slice: The #GstH264PPS to set. + * + * Parses the @data, and sets the @pps. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parser_parse_pps (GstH264NalParser * nalparser, + GstH264NalUnit * nalu, GstH264PPS * pps) +{ + GstH264ParserResult res = gst_h264_parse_pps (nalparser, nalu, pps); + + if (res == GST_H264_PARSER_OK) { + GST_DEBUG ("adding picture parameter set with id: %d to array", pps->id); + + nalparser->pps[pps->id] = *pps; + nalparser->last_pps = &nalparser->pps[pps->id]; + } + + return res; +} + +/** + * gst_h264_parser_parse_slice_hdr: + * @nalu: The #GST_H264_NAL_SLICE #GstH264NalUnit you want to parse + * @slice: The #GstH264SliceHdr to set. + * @parse_pred_weight_table: Whether to parse the pred_weight_table or not + * @parse_dec_ref_pic_marking: Whether to parse the dec_ref_pic_marking or not + * + * Parses the @data, and sets the @slice. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parser_parse_slice_hdr (GstH264NalParser * nalparser, + GstH264NalUnit * nalu, GstH264SliceHdr * slice, + gboolean parse_pred_weight_table, gboolean parse_dec_ref_pic_marking) +{ + NalReader nr; + gint pps_id; + GstH264PPS *pps; + GstH264SPS *sps; + + if (!nalu->size) { + GST_DEBUG ("Invalid Nal Unit"); + return GST_H264_PARSER_ERROR; + } + + + nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size - 1); + + READ_UE (&nr, slice->first_mb_in_slice); + READ_UE (&nr, slice->type); + + GST_DEBUG ("parsing \"Slice header\", slice type %u", slice->type); + + READ_UE_ALLOWED (&nr, pps_id, 0, GST_H264_MAX_PPS_COUNT); + pps = gst_h264_parser_get_pps (nalparser, pps_id); + + if (!pps) { + GST_WARNING ("couldn't find associated picture parameter set with id: %d", + pps_id); + + return GST_H264_PARSER_BROKEN_LINK; + } + + slice->pps = pps; + sps = pps->sequence; + if (!sps) { + GST_WARNING ("couldn't find associated sequence parameter set with id: %d", + pps->id); + return GST_H264_PARSER_BROKEN_LINK; + } + + /* set default values for fields that might not be present in the bitstream + and have valid defaults */ + slice->field_pic_flag = 0; + slice->bottom_field_flag = 0; + slice->delta_pic_order_cnt_bottom = 0; + slice->delta_pic_order_cnt[0] = 0; + slice->delta_pic_order_cnt[1] = 0; + slice->redundant_pic_cnt = 0; + slice->num_ref_idx_l0_active_minus1 = pps->num_ref_idx_l0_active_minus1; + slice->num_ref_idx_l1_active_minus1 = pps->num_ref_idx_l1_active_minus1; + slice->disable_deblocking_filter_idc = 0; + slice->slice_alpha_c0_offset_div2 = 0; + + if (sps->separate_colour_plane_flag) + READ_UINT8 (&nr, slice->colour_plane_id, 2); + + READ_UINT16 (&nr, slice->frame_num, sps->log2_max_frame_num_minus4 + 4); + + if (!sps->frame_mbs_only_flag) { + READ_UINT8 (&nr, slice->field_pic_flag, 1); + if (slice->field_pic_flag) + READ_UINT8 (&nr, slice->bottom_field_flag, 1); + } + + /* calculate MaxPicNum */ + if (slice->field_pic_flag) + slice->max_pic_num = sps->max_frame_num; + else + slice->max_pic_num = 2 * sps->max_frame_num; + + if (nalu->type == 5) + READ_UE_ALLOWED (&nr, slice->idr_pic_id, 0, G_MAXUINT16); + + if (sps->pic_order_cnt_type == 0) { + READ_UINT16 (&nr, slice->pic_order_cnt_lsb, + sps->log2_max_pic_order_cnt_lsb_minus4 + 4); + + if (pps->pic_order_present_flag && !slice->field_pic_flag) + READ_SE (&nr, slice->delta_pic_order_cnt_bottom); + } + + if (sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag) { + READ_SE (&nr, slice->delta_pic_order_cnt[0]); + if (pps->pic_order_present_flag && !slice->field_pic_flag) + READ_SE (&nr, slice->delta_pic_order_cnt[1]); + } + + if (pps->redundant_pic_cnt_present_flag) + READ_UE_ALLOWED (&nr, slice->redundant_pic_cnt, 0, G_MAXINT8); + + if (GST_H264_IS_B_SLICE (slice)) + READ_UINT8 (&nr, slice->direct_spatial_mv_pred_flag, 1); + + if (GST_H264_IS_P_SLICE (slice) || GST_H264_IS_SP_SLICE (slice) || + GST_H264_IS_B_SLICE (slice)) { + guint8 num_ref_idx_active_override_flag; + + READ_UINT8 (&nr, num_ref_idx_active_override_flag, 1); + if (num_ref_idx_active_override_flag) { + READ_UE_ALLOWED (&nr, slice->num_ref_idx_l0_active_minus1, 0, 31); + + if (GST_H264_IS_B_SLICE (slice)) + READ_UE_ALLOWED (&nr, slice->num_ref_idx_l1_active_minus1, 0, 31); + } + } + + if (!slice_parse_ref_pic_list_reordering (slice, &nr)) + goto error; + + if ((pps->weighted_pred_flag && (GST_H264_IS_P_SLICE (slice) + || GST_H264_IS_SP_SLICE (slice))) + || (pps->weighted_bipred_idc == 1 && GST_H264_IS_B_SLICE (slice))) { + if (!gst_h264_slice_parse_pred_weight_table (slice, &nr, + sps->chroma_array_type)) + goto error; + } + + if (nalu->ref_idc != 0) { + if (!gst_h264_slice_parse_dec_ref_pic_marking (slice, nalu, &nr)) + goto error; + } + + if (pps->entropy_coding_mode_flag && !GST_H264_IS_I_SLICE (slice) && + !GST_H264_IS_SI_SLICE (slice)) + READ_UE_ALLOWED (&nr, slice->cabac_init_idc, 0, 2); + + READ_SE_ALLOWED (&nr, slice->slice_qp_delta, -87, 77); + + if (GST_H264_IS_SP_SLICE (slice) || GST_H264_IS_SI_SLICE (slice)) { + guint8 sp_for_switch_flag; + + if (GST_H264_IS_SP_SLICE (slice)) + READ_UINT8 (&nr, sp_for_switch_flag, 1); + READ_SE_ALLOWED (&nr, slice->slice_qs_delta, -51, 51); + } + + if (pps->deblocking_filter_control_present_flag) { + READ_UE_ALLOWED (&nr, slice->disable_deblocking_filter_idc, 0, 2); + if (slice->disable_deblocking_filter_idc != 1) { + READ_SE_ALLOWED (&nr, slice->slice_alpha_c0_offset_div2, -6, 6); + READ_SE_ALLOWED (&nr, slice->slice_beta_offset_div2, -6, 6); + } + } + + if (pps->num_slice_groups_minus1 > 0 && + pps->slice_group_map_type >= 3 && pps->slice_group_map_type <= 5) { + /* Ceil(Log2(PicSizeInMapUnits / SliceGroupChangeRate + 1)) [7-33] */ + guint32 PicWidthInMbs = sps->pic_width_in_mbs_minus1 + 1; + guint32 PicHeightInMapUnits = sps->pic_height_in_map_units_minus1 + 1; + guint32 PicSizeInMapUnits = PicWidthInMbs * PicHeightInMapUnits; + guint32 SliceGroupChangeRate = pps->slice_group_change_rate_minus1 + 1; + const guint n = ceil_log2 (PicSizeInMapUnits / SliceGroupChangeRate + 1); + READ_UINT16 (&nr, slice->slice_group_change_cycle, n); + } + + slice->header_size = nal_reader_get_pos (&nr); + + return GST_H264_PARSER_OK; + +error: + GST_WARNING ("error parsing \"Slice header\""); + return GST_H264_PARSER_ERROR; +} + +/** + * gst_h264_parser_parse_sei: + * @nalparser: a #GstH264NalParser + * @nalu: The #GST_H264_NAL_SEI #GstH264NalUnit you want to parse + * @slice: The #GstH264SEIMessage to set. + * + * Parses the @data, and sets the @pps. + * + * Returns: a #GstH264ParserResult + */ +GstH264ParserResult +gst_h264_parser_parse_sei (GstH264NalParser * nalparser, GstH264NalUnit * nalu, + GstH264SEIMessage * sei) +{ + NalReader nr; + + guint32 payloadSize; + guint8 payload_type_byte, payload_size_byte; + guint remaining, payload_size; + gboolean res; + + GST_DEBUG ("parsing \"Sei message\""); + + nal_reader_init (&nr, nalu->data + nalu->offset + 1, nalu->size - 1); + + sei->payloadType = 0; + do { + READ_UINT8 (&nr, payload_type_byte, 8); + sei->payloadType += payload_type_byte; + } while (payload_type_byte == 0xff); + + payloadSize = 0; + do { + READ_UINT8 (&nr, payload_size_byte, 8); + payloadSize += payload_size_byte; + } + while (payload_size_byte == 0xff); + + remaining = nal_reader_get_remaining (&nr) * 8; + payload_size = payloadSize < remaining ? payloadSize : remaining; + + GST_DEBUG ("SEI message received: payloadType %u, payloadSize = %u bytes", + sei->payloadType, payload_size); + + if (sei->payloadType == GST_H264_SEI_BUF_PERIOD) { + /* Set the nal reader size properly */ + nr.size = payload_size; + res = gst_h264_parser_parse_buffering_period (nalparser, + &sei->buffering_period, &nr); + } else if (sei->payloadType == GST_H264_SEI_PIC_TIMING) { + /* Set the nal reader size properly */ + nr.size = payload_size; + res = gst_h264_parser_parse_pic_timing (nalparser, &sei->pic_timing, &nr); + } else + res = GST_H264_PARSER_OK; + + return res; + +error: + GST_WARNING ("error parsing \"Sei message\""); + return GST_H264_PARSER_ERROR; +} diff --git a/gst-libs/gst/codecparsers/gsth264parser.h b/gst-libs/gst/codecparsers/gsth264parser.h new file mode 100644 index 0000000000..84577f686b --- /dev/null +++ b/gst-libs/gst/codecparsers/gsth264parser.h @@ -0,0 +1,682 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * Some bits C-c,C-v'ed and s/4/3 from h264parse and videoparsers/h264parse.c: + * Copyright (C) <2010> Mark Nauwelaerts + * Copyright (C) <2010> Collabora Multimedia + * Copyright (C) <2010> Nokia Corporation + * + * (C) 2005 Michal Benes + * (C) 2008 Wim Taymans + * + * 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_H264_PARSER_H__ +#define __GST_H264_PARSER_H__ + +#include + +G_BEGIN_DECLS + +#define GST_H264_MAX_SPS_COUNT 32 +#define GST_H264_MAX_PPS_COUNT 256 + +#define GST_H264_IS_P_SLICE(slice) (((slice)->type % 5) == GST_H264_P_SLICE) +#define GST_H264_IS_B_SLICE(slice) (((slice)->type % 5) == GST_H264_B_SLICE) +#define GST_H264_IS_I_SLICE(slice) (((slice)->type % 5) == GST_H264_I_SLICE) +#define GST_H264_IS_SP_SLICE(slice) (((slice)->type % 5) == GST_H264_SP_SLICE) +#define GST_H264_IS_SI_SLICE(slice) (((slice)->type % 5) == GST_H264_SI_SLICE) + +/** + * GstH264NalUnitType: + * @GST_H264_NAL_UNKNOWN: Unkonw nal type + * @GST_H264_NAL_SLICE: Slice nal + * @GST_H264_NAL_SLICE_DPA: DPA slice nal + * @GST_H264_NAL_SLICE_DPB: DPB slice nal + * @GST_H264_NAL_SLICE_DPC: DPC slice nal + * @GST_H264_NAL_SLICE_IDR: DPR slice nal + * @GST_H264_NAL_SEI: Supplemental enhancement information nal unit + * @GST_H264_NAL_SPS: Sequence parameter set nal unit + * @GST_H264_NAL_PPS: Picture parameter set nal unit + * @GST_H264_NAL_AU_DELIMITER: Access unit delimiter nal unit + * @GST_H264_NAL_SEQ_END: End of sequence nal unit + * @GST_H264_NAL_STREAM_END: End of stream nal unit + * @GST_H264_NAL_FILLER_DATA: Filler data na lunit + * + * Indicates the type of H264 Nal Units + */ +typedef enum +{ + GST_H264_NAL_UNKNOWN = 0, + GST_H264_NAL_SLICE = 1, + GST_H264_NAL_SLICE_DPA = 2, + GST_H264_NAL_SLICE_DPB = 3, + GST_H264_NAL_SLICE_DPC = 4, + GST_H264_NAL_SLICE_IDR = 5, + GST_H264_NAL_SEI = 6, + GST_H264_NAL_SPS = 7, + GST_H264_NAL_PPS = 8, + GST_H264_NAL_AU_DELIMITER = 9, + GST_H264_NAL_SEQ_END = 10, + GST_H264_NAL_STREAM_END = 11, + GST_H264_NAL_FILLER_DATA = 12 +} GstH264NalUnitType; + +/** + * GstH264ParserResult: + * @GST_H264_PARSER_OK: The parsing succeded + * @GST_H264_PARSER_BROKEN_DATA: The data we parsed where broken + * @GST_H264_PARSER_BROKEN_LINK: The link to a needed struct for the parsing couldn't be found + * @GST_H264_PARSER_ERROR: An error accured when parsing + * @GST_H264_PARSER_NO_NAL: No nal found during the parsing + * @GST_H264_PARSER_NO_NAL_END: Start of the nal found, not the end. + * + * Information about how the parsing of a H264 elements went. + */ +typedef enum +{ + GST_H264_PARSER_OK, + GST_H264_PARSER_BROKEN_DATA, + GST_H264_PARSER_BROKEN_LINK, + GST_H264_PARSER_ERROR, + GST_H264_PARSER_NO_NAL, + GST_H264_PARSER_NO_NAL_END +} GstH264ParserResult; + +/** + * GstH264SEIPayloadType: + * @GST_H264_SEI_BUF_PERIOD: The Sei Message contains a buffering period message + * @GST_H264_SEI_PIC_TIMING: The Sei Message contains a picture timing message + * ... + * + * The type of the SEI message information + */ +typedef enum +{ + GST_H264_SEI_BUF_PERIOD = 0, + GST_H264_SEI_PIC_TIMING = 1 + /* and more... */ +} GstH264SEIPayloadType; + +/** + * GstH264SEIPicStructType: + * @GST_H264_SEI_PIC_STRUCT_FRAME: Picture is a frame + * @GST_H264_SEI_PIC_STRUCT_TOP_FIELD: Top field of frame + * @GST_H264_SEI_PIC_STRUCT_BOTTOM_FIELD: Botom field of frame + * @GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM: Top bottom field of frame + * @GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP: bottom top field of frame + * @GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP: top bottom top field of frame + * @GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: bottom top bottom field of frame + * @GST_H264_SEI_PIC_STRUCT_FRAME_DOUBLING: indicates that the frame should + * be displayed two times consecutively + * @GST_H264_SEI_PIC_STRUCT_FRAME_TRIPLING: indicates that the frame should be + * displayed three times consecutively + * + * SEI pic_struct type + */ +typedef enum +{ + GST_H264_SEI_PIC_STRUCT_FRAME = 0, + GST_H264_SEI_PIC_STRUCT_TOP_FIELD = 1, + GST_H264_SEI_PIC_STRUCT_BOTTOM_FIELD = 2, + GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM = 3, + GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP = 4, + GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP = 5, + GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM = 6, + GST_H264_SEI_PIC_STRUCT_FRAME_DOUBLING = 7, + GST_H264_SEI_PIC_STRUCT_FRAME_TRIPLING = 8 +} GstH264SEIPicStructType; + +/** + * GstH264SliceType: + * + * Type of Picture slice + */ + +typedef enum +{ + GST_H264_P_SLICE = 0, + GST_H264_B_SLICE = 1, + GST_H264_I_SLICE = 2, + GST_H264_SP_SLICE = 3, + GST_H264_SI_SLICE = 4, + GST_H264_S_P_SLICE = 5, + GST_H264_S_B_SLICE = 6, + GST_H264_S_I_SLICE = 7, + GST_H264_S_SP_SLICE = 8, + GST_H264_S_SI_SLICE = 9 +} GstH264SliceType; + +typedef struct _GstH264NalParser GstH264NalParser; + +typedef struct _GstH264NalUnit GstH264NalUnit; + +typedef struct _GstH264SPS GstH264SPS; +typedef struct _GstH264PPS GstH264PPS; +typedef struct _GstH264HRDParams GstH264HRDParams; +typedef struct _GstH264VUIParams GstH264VUIParams; + +typedef struct _GstH264DecRefPicMarking GstH264DecRefPicMarking; +typedef struct _GstH264RefPicMarking GstH264RefPicMarking; +typedef struct _GstH264PredWeightTable GstH264PredWeightTable; +typedef struct _GstH264SliceHdr GstH264SliceHdr; + +typedef struct _GstH264ClockTimestamp GstH264ClockTimestamp; +typedef struct _GstH264PicTiming GstH264PicTiming; +typedef struct _GstH264BufferingPeriod GstH264BufferingPeriod; +typedef struct _GstH264SEIMessage GstH264SEIMessage; + +/** + * GstH264NalUnit: + * @ref_idc: not equal to 0 specifies that the content of the NAL unit contains a sequence + * parameter set, a sequence * parameter set extension, a subset sequence parameter set, a + * picture parameter set, a slice of a reference picture, a slice data partition of a + * reference picture, or a prefix NAL unit preceding a slice of a reference picture. + * @type: A #GstH264NalUnitType + * @idr_pic_flag: calculated idr_pic_flag + * @size: The size of the nal unit starting from @offset + * @offset: The offset of the actual start of the nal unit + * @sc_offset:The offset of the start code of the nal unit + * @valid: If the nal unit is valid, which mean it has + * already been parsed + * @data: The data from which the Nalu has been parsed + * + * Structure defining the Nal unit headers + */ +struct _GstH264NalUnit +{ + guint16 ref_idc; + guint16 type; + + /* calculated values */ + guint8 idr_pic_flag; + guint size; + guint offset; + guint sc_offset; + gboolean valid; + + guint8 *data; +}; + +/** + * GstH264HRDParams: + * @cpb_cnt_minus1: plus 1 specifies the number of alternative + * CPB specifications in the bitstream + * @bit_rate_scale: specifies the maximum input bit rate of the + * SchedSelIdx-th CPB + * @cpb_size_scale: specifies the CPB size of the SchedSelIdx-th CPB + * @guint32 bit_rate_value_minus1: specifies the maximum input bit rate for the + * SchedSelIdx-th CPB + * @cpb_size_value_minus1: is used together with cpb_size_scale to specify the + * SchedSelIdx-th CPB size + * @cbr_flag: Specifies if running in itermediate bitrate mode or constant + * @initial_cpb_removal_delay_length_minus1: specifies the length in bits of + * the cpb_removal_delay syntax element + * @cpb_removal_delay_length_minus1: specifies the length in bits of the + * dpb_output_delay syntax element + * @dpb_output_delay_length_minus1: >0 specifies the length in bits of the time_offset syntax element. + * =0 specifies that the time_offset syntax element is not present + * @time_offset_length: Length of the time offset + * + * Defines the HRD parameters + */ +struct _GstH264HRDParams +{ + guint8 cpb_cnt_minus1; + guint8 bit_rate_scale; + guint8 cpb_size_scale; + + guint32 bit_rate_value_minus1[32]; + guint32 cpb_size_value_minus1[32]; + guint8 cbr_flag[32]; + + guint8 initial_cpb_removal_delay_length_minus1; + guint8 cpb_removal_delay_length_minus1; + guint8 dpb_output_delay_length_minus1; + guint8 time_offset_length; +}; + +/** + * GstH264VUIParams: + * @aspect_ratio_info_present_flag: %TRUE specifies that aspect_ratio_idc is present. + * %FALSE specifies that aspect_ratio_idc is not present + * @aspect_ratio_idc specifies the value of the sample aspect ratio of the luma samples + * @sar_width indicates the horizontal size of the sample aspect ratio + * @sar_height indicates the vertical size of the sample aspect ratio + * @overscan_info_present_flag: %TRUE overscan_appropriate_flag is present %FALSE otherwize + * @overscan_appropriate_flag: %TRUE indicates that the cropped decoded pictures + * output are suitable for display using overscan. %FALSE the cropped decoded pictures + * output contain visually important information + * @video_signal_type_present_flag: %TRUE specifies that video_format, video_full_range_flag and + * colour_description_present_flag are present. + * @video_format: indicates the representation of the picture + * @video_full_range_flag: indicates the black level and range of the luma and chroma signals + * @colour_description_present_flag: %TRUE specifies that colour_primaries, + * transfer_characteristics and matrix_coefficients are present + * @colour_primaries: indicates the chromaticity coordinates of the source primaries + * @transfer_characteristics: indicates the opto-electronic transfer characteristic + * @matrix_coefficients: describes the matrix coefficients used in deriving luma and chroma signals + * @chroma_loc_info_present_flag: %TRUE specifies that chroma_sample_loc_type_top_field and + * chroma_sample_loc_type_bottom_field are present, %FALSE otherwize + * @chroma_sample_loc_type_top_field: specify the location of chroma for top field + * @chroma_sample_loc_type_bottom_field specify the location of chroma for bottom field + * @timing_info_present_flag: %TRUE specifies that num_units_in_tick, + * time_scale and fixed_frame_rate_flag are present in the bitstream + * @num_units_in_tick: is the number of time units of a clock operating at the frequency time_scale Hz + * time_scale: is the number of time units that pass in one second + * @fixed_frame_rate_flag: %TRUE indicates that the temporal distance between the HRD output times + * of any two consecutive pictures in output order is constrained as specified in the spec, %FALSE + * otherwize. + * @nal_hrd_parameters_present_flag: %TRUE if nal hrd parameters present in the bitstream + * @vcl_hrd_parameters_present_flag: %TRUE if nal vlc hrd parameters present in the bitstream + * @low_delay_hrd_flag: specifies the HRD operational mode + * @pic_struct_present_flag: %TRUE specifies that picture timing SEI messages are present or not + * @bitstream_restriction_flag: %TRUE specifies that the following coded video sequence bitstream restriction + * parameters are present + * @motion_vectors_over_pic_boundaries_flag: %FALSE indicates that no sample outside the + * picture boundaries and no sample at a fractional sample position, %TRUE indicates that one or more + * samples outside picture boundaries may be used in inter prediction + * @max_bytes_per_pic_denom: indicates a number of bytes not exceeded by the sum of the sizes of + * the VCL NAL units associated with any coded picture in the coded video sequence. + * @max_bits_per_mb_denom: indicates the maximum number of coded bits of macroblock_layer() + * @log2_max_mv_length_horizontal: indicate the maximum absolute value of a decoded horizontal + * motion vector component + * @log2_max_mv_length_vertical: indicate the maximum absolute value of a decoded vertical + * motion vector component + * @num_reorder_frames: indicates the maximum number of frames, complementary field pairs, + * or non-paired fields that precede any frame, + * @max_dec_frame_buffering: specifies the required size of the HRD decoded picture buffer in + * units of frame buffers. + * + * The structure representing the VUI parameters. + */ +struct _GstH264VUIParams +{ + guint8 aspect_ratio_info_present_flag; + guint8 aspect_ratio_idc; + /* if aspect_ratio_idc == 255 */ + guint16 sar_width; + guint16 sar_height; + + guint8 overscan_info_present_flag; + /* if overscan_info_present_flag */ + guint8 overscan_appropriate_flag; + + guint8 video_signal_type_present_flag; + guint8 video_format; + guint8 video_full_range_flag; + guint8 colour_description_present_flag; + guint8 colour_primaries; + guint8 transfer_characteristics; + guint8 matrix_coefficients; + + guint8 chroma_loc_info_present_flag; + guint8 chroma_sample_loc_type_top_field; + guint8 chroma_sample_loc_type_bottom_field; + + guint8 timing_info_present_flag; + /* if timing_info_present_flag */ + guint32 num_units_in_tick; + guint32 time_scale; + guint8 fixed_frame_rate_flag; + + guint8 nal_hrd_parameters_present_flag; + /* if nal_hrd_parameters_present_flag */ + GstH264HRDParams nal_hrd_parameters; + + guint8 vcl_hrd_parameters_present_flag; + /* if nal_hrd_parameters_present_flag */ + GstH264HRDParams vcl_hrd_parameters; + + guint8 low_delay_hrd_flag; + guint8 pic_struct_present_flag; + + guint8 bitstream_restriction_flag; + /* if bitstream_restriction_flag */ + guint8 motion_vectors_over_pic_boundaries_flag; + guint32 max_bytes_per_pic_denom; + guint32 max_bits_per_mb_denom; + guint32 log2_max_mv_length_horizontal; + guint32 log2_max_mv_length_vertical; + guint32 num_reorder_frames; + guint32 max_dec_frame_buffering; +}; + +/** + * GstH264SPS: + * @id: The ID of the sequence parameter set + * @profile_idc: indicate the profile to which the coded video sequence conforms + * + * + */ +struct _GstH264SPS +{ + gint id; + + guint8 profile_idc; + guint8 constraint_set0_flag; + guint8 constraint_set1_flag; + guint8 constraint_set2_flag; + guint8 constraint_set3_flag; + guint8 level_idc; + + guint8 chroma_format_idc; + guint8 separate_colour_plane_flag; + guint8 bit_depth_luma_minus8; + guint8 bit_depth_chroma_minus8; + guint8 qpprime_y_zero_transform_bypass_flag; + + guint8 scaling_matrix_present_flag; + guint8 scaling_lists_4x4[6][16]; + guint8 scaling_lists_8x8[6][64]; + + guint8 log2_max_frame_num_minus4; + guint8 pic_order_cnt_type; + + /* if pic_order_cnt_type == 0 */ + guint8 log2_max_pic_order_cnt_lsb_minus4; + + /* else if pic_order_cnt_type == 1 */ + guint8 delta_pic_order_always_zero_flag; + gint32 offset_for_non_ref_pic; + gint32 offset_for_top_to_bottom_field; + guint8 num_ref_frames_in_pic_order_cnt_cycle; + gint32 offset_for_ref_frame[255]; + + guint32 num_ref_frames; + guint8 gaps_in_frame_num_value_allowed_flag; + guint32 pic_width_in_mbs_minus1; + guint32 pic_height_in_map_units_minus1; + guint8 frame_mbs_only_flag; + + guint8 mb_adaptive_frame_field_flag; + + guint8 direct_8x8_inference_flag; + + guint8 frame_cropping_flag; + + /* if frame_cropping_flag */ + guint32 frame_crop_left_offset; + guint32 frame_crop_right_offset; + guint32 frame_crop_top_offset; + guint32 frame_crop_bottom_offset; + + guint8 vui_parameters_present_flag; + /* if vui_parameters_present_flag */ + GstH264VUIParams vui_parameters; + + /* calculated values */ + guint8 chroma_array_type; + guint32 max_frame_num; + gint width, height; + gint fps_num, fps_den; + gboolean valid; +}; + +struct _GstH264PPS +{ + gint id; + + GstH264SPS *sequence; + + guint8 entropy_coding_mode_flag; + guint8 pic_order_present_flag; + + guint32 num_slice_groups_minus1; + + /* if num_slice_groups_minus1 > 0 */ + guint8 slice_group_map_type; + /* and if slice_group_map_type == 0 */ + guint32 run_length_minus1[8]; + /* or if slice_group_map_type == 2 */ + guint32 top_left[8]; + guint32 bottom_right[8]; + /* or if slice_group_map_type == (3, 4, 5) */ + guint8 slice_group_change_direction_flag; + guint32 slice_group_change_rate_minus1; + /* or if slice_group_map_type == 6 */ + guint32 pic_size_in_map_units_minus1; + guint8 *slice_group_id; + + guint8 num_ref_idx_l0_active_minus1; + guint8 num_ref_idx_l1_active_minus1; + guint8 weighted_pred_flag; + guint8 weighted_bipred_idc; + gint8 pic_init_qp_minus26; + gint8 pic_init_qs_minus26; + gint8 chroma_qp_index_offset; + guint8 deblocking_filter_control_present_flag; + guint8 constrained_intra_pred_flag; + guint8 redundant_pic_cnt_present_flag; + + guint8 transform_8x8_mode_flag; + + guint8 scaling_lists_4x4[6][16]; + guint8 scaling_lists_8x8[6][64]; + + guint8 second_chroma_qp_index_offset; + + gboolean valid; +}; + +struct _GstH264PredWeightTable +{ + guint8 luma_log2_weight_denom; + guint8 chroma_log2_weight_denom; + + gint16 luma_weight_l0[32]; + gint8 luma_offset_l0[32]; + + /* if seq->ChromaArrayType != 0 */ + gint16 chroma_weight_l0[32][2]; + gint8 chroma_offset_l0[32][2]; + + /* if slice->slice_type % 5 == 1 */ + gint16 luma_weight_l1[32]; + gint8 luma_offset_l1[32]; + + /* and if seq->ChromaArrayType != 0 */ + gint16 chroma_weight_l1[32][2]; + gint8 chroma_offset_l1[32][2]; +}; + +struct _GstH264RefPicMarking +{ + guint8 memory_management_control_operation; + + guint32 difference_of_pic_nums_minus1; + guint32 long_term_pic_num; + guint32 long_term_frame_idx; + guint32 max_long_term_frame_idx_plus1; +}; + +struct _GstH264DecRefPicMarking +{ + /* if slice->nal_unit.IdrPicFlag */ + guint8 no_output_of_prior_pics_flag; + guint8 long_term_reference_flag; + + guint8 adaptive_ref_pic_marking_mode_flag; + GstH264RefPicMarking ref_pic_marking[10]; + guint8 n_ref_pic_marking; +}; + + +struct _GstH264SliceHdr +{ + guint32 first_mb_in_slice; + guint32 type; + GstH264PPS *pps; + + /* if seq->separate_colour_plane_flag */ + guint8 colour_plane_id; + + guint16 frame_num; + + guint8 field_pic_flag; + guint8 bottom_field_flag; + + /* if nal_unit.type == 5 */ + guint16 idr_pic_id; + + /* if seq->pic_order_cnt_type == 0 */ + guint16 pic_order_cnt_lsb; + /* if seq->pic_order_present_flag && !field_pic_flag */ + gint32 delta_pic_order_cnt_bottom; + + gint32 delta_pic_order_cnt[2]; + guint8 redundant_pic_cnt; + + /* if slice_type == B_SLICE */ + guint8 direct_spatial_mv_pred_flag; + + guint8 num_ref_idx_l0_active_minus1; + guint8 num_ref_idx_l1_active_minus1; + + GstH264PredWeightTable pred_weight_table; + /* if nal_unit.ref_idc != 0 */ + GstH264DecRefPicMarking dec_ref_pic_marking; + + guint8 cabac_init_idc; + gint8 slice_qp_delta; + gint8 slice_qs_delta; + + guint8 disable_deblocking_filter_idc; + gint8 slice_alpha_c0_offset_div2; + gint8 slice_beta_offset_div2; + + guint16 slice_group_change_cycle; + + /* calculated values */ + guint32 max_pic_num; + gboolean valid; + + /* Size of the slice_header() in bits */ + guint header_size; +}; + + +struct _GstH264ClockTimestamp +{ + guint8 ct_type; + guint8 nuit_field_based_flag; + guint8 counting_type; + guint8 discontinuity_flag; + guint8 cnt_dropped_flag; + guint8 n_frames; + + guint8 seconds_flag; + guint8 seconds_value; + + guint8 minutes_flag; + guint8 minutes_value; + + guint8 hours_flag; + guint8 hours_value; + + guint32 time_offset; +}; + +struct _GstH264PicTiming +{ + guint32 cpb_removal_delay; + guint32 dpb_output_delay; + + guint8 pic_struct_present_flag; + /* if pic_struct_present_flag */ + guint8 pic_struct; + + guint8 clock_timestamp_flag[3]; + GstH264ClockTimestamp clock_timestamp[3]; +}; + +struct _GstH264BufferingPeriod +{ + GstH264SPS *sps; + + /* seq->vui_parameters->nal_hrd_parameters_present_flag */ + guint8 nal_initial_cpb_removal_delay[32]; + guint8 nal_initial_cpb_removal_delay_offset[32]; + + /* seq->vui_parameters->vcl_hrd_parameters_present_flag */ + guint8 vcl_initial_cpb_removal_delay[32]; + guint8 vcl_initial_cpb_removal_delay_offset[32]; +}; + +struct _GstH264SEIMessage +{ + GstH264SEIPayloadType payloadType; + + union { + GstH264BufferingPeriod buffering_period; + GstH264PicTiming pic_timing; + /* ... could implement more */ + }; +}; + +/** + * GstH264NalParser: + * + * H264 NAL Parser (opaque structure). + */ +struct _GstH264NalParser +{ + /*< private >*/ + GstH264SPS sps[GST_H264_MAX_SPS_COUNT]; + GstH264PPS pps[GST_H264_MAX_PPS_COUNT]; + GstH264SPS *last_sps; + GstH264PPS *last_pps; +}; + +GstH264NalParser *gst_h264_nal_parser_new (void); + +GstH264ParserResult gst_h264_parser_identify_nalu (GstH264NalParser *nalparser, + const guint8 *data, guint offset, + gsize size, GstH264NalUnit *nalu); + +GstH264ParserResult gst_h264_parser_identify_nalu_avc (GstH264NalParser *nalparser, const guint8 *data, + guint offset, gsize size, guint8 nal_length_size, + GstH264NalUnit *nalu); + +GstH264ParserResult gst_h264_parser_parse_nal (GstH264NalParser *nalparser, + GstH264NalUnit *nalu); + +GstH264ParserResult gst_h264_parser_parse_slice_hdr (GstH264NalParser *nalparser, GstH264NalUnit *nalu, + GstH264SliceHdr *slice, gboolean parse_pred_weight_table, + gboolean parse_dec_ref_pic_marking); + +GstH264ParserResult gst_h264_parser_parse_sps (GstH264NalParser *nalparser, GstH264NalUnit *nalu, + GstH264SPS *sps, gboolean parse_vui_params); + +GstH264ParserResult gst_h264_parser_parse_pps (GstH264NalParser *nalparser, + GstH264NalUnit *nalu, GstH264PPS *pps); + +GstH264ParserResult gst_h264_parser_parse_sei (GstH264NalParser *nalparser, + GstH264NalUnit *nalu, GstH264SEIMessage *sei); + +void gst_h264_nal_parser_free (GstH264NalParser *nalparser); + +GstH264ParserResult gst_h264_parse_sps (GstH264NalUnit *nalu, + GstH264SPS *sps, gboolean parse_vui_params); + +GstH264ParserResult gst_h264_parse_pps (GstH264NalParser *nalparser, + GstH264NalUnit *nalu, GstH264PPS *pps); + +G_END_DECLS +#endif diff --git a/gst-libs/gst/codecparsers/gstmpegvideoparser.c b/gst-libs/gst/codecparsers/gstmpegvideoparser.c new file mode 100644 index 0000000000..44df76ad5e --- /dev/null +++ b/gst-libs/gst/codecparsers/gstmpegvideoparser.c @@ -0,0 +1,755 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * From bad/sys/vdpau/mpeg/mpegutil.c: + * Copyright (C) <2007> Jan Schmidt + * Copyright (C) <2009> Carl-Anton Ingmarsson + * + * 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. + */ + +/** + * SECTION:gstmpegvideoparser + * @short_description: Convenience library for mpeg1 and 2 video + * bitstream parsing. + * + * + * + * Provides useful functions for mpeg videos bitstream parsing. + * + * + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "gstmpegvideoparser.h" + +#include +#include +#include + +#define MARKER_BIT 0x1 + +#define GET_BITS(b, num, bits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint32(b, bits, num)) \ + goto failed; \ + GST_TRACE ("parsed %d bits: %d", num, *(bits)); \ +} G_STMT_END + +#define READ_UINT8(br, val, nbits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint8 (br, &val, nbits)) { \ + GST_WARNING ("failed to read uint8, nbits: %d", nbits); \ + goto failed; \ + } \ +} G_STMT_END + +#define READ_UINT16(br, val, nbits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint16 (br, &val, nbits)) { \ + GST_WARNING ("failed to read uint16, nbits: %d", nbits); \ + goto failed; \ + } \ +} G_STMT_END + +#define READ_UINT32(br, val, nbits) G_STMT_START { \ + if (!gst_bit_reader_get_bits_uint32 (br, &val, nbits)) { \ + GST_WARNING ("failed to read uint32, nbits: %d", nbits); \ + goto failed; \ + } \ +} G_STMT_END + +/* default intra quant matrix, in zig-zag order */ +const guint8 default_intra_quantizer_matrix[64] = { + 8, + 16, 16, + 19, 16, 19, + 22, 22, 22, 22, + 22, 22, 26, 24, 26, + 27, 27, 27, 26, 26, 26, + 26, 27, 27, 27, 29, 29, 29, + 34, 34, 34, 29, 29, 29, 27, 27, + 29, 29, 32, 32, 34, 34, 37, + 38, 37, 35, 35, 34, 35, + 38, 38, 40, 40, 40, + 48, 48, 46, 46, + 56, 56, 58, + 69, 69, + 83 +}; + +const guint8 mpeg_zigzag_8x8[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63 +}; + +GST_DEBUG_CATEGORY (mpegvideo_parser_debug); +#define GST_CAT_DEFAULT mpegvideo_parser_debug + +static gboolean initialized = FALSE; + +static inline gboolean +find_start_code (GstBitReader * b) +{ + guint32 bits; + + /* 0 bits until byte aligned */ + while (b->bit != 0) { + GET_BITS (b, 1, &bits); + } + + /* 0 bytes until startcode */ + while (gst_bit_reader_peek_bits_uint32 (b, &bits, 32)) { + if (bits >> 8 == 0x1) { + return TRUE; + } else { + gst_bit_reader_skip (b, 8); + } + } + + return FALSE; + +failed: + return FALSE; +} + +/* Set the Pixel Aspect Ratio in our hdr from a DAR code in the data */ +static void +set_par_from_dar (GstMpegVideoSequenceHdr * seqhdr, guint8 asr_code) +{ + /* Pixel_width = DAR_width * display_vertical_size */ + /* Pixel_height = DAR_height * display_horizontal_size */ + switch (asr_code) { + case 0x02: /* 3:4 DAR = 4:3 pixels */ + seqhdr->par_w = 4 * seqhdr->height; + seqhdr->par_h = 3 * seqhdr->width; + break; + case 0x03: /* 9:16 DAR */ + seqhdr->par_w = 16 * seqhdr->height; + seqhdr->par_h = 9 * seqhdr->width; + break; + case 0x04: /* 1:2.21 DAR */ + seqhdr->par_w = 221 * seqhdr->height; + seqhdr->par_h = 100 * seqhdr->width; + break; + case 0x01: /* Square pixels */ + seqhdr->par_w = seqhdr->par_h = 1; + break; + default: + GST_DEBUG ("unknown/invalid aspect_ratio_information %d", asr_code); + break; + } +} + +static void +set_fps_from_code (GstMpegVideoSequenceHdr * seqhdr, guint8 fps_code) +{ + const gint framerates[][2] = { + {30, 1}, {24000, 1001}, {24, 1}, {25, 1}, + {30000, 1001}, {30, 1}, {50, 1}, {60000, 1001}, + {60, 1}, {30, 1} + }; + + if (fps_code && fps_code < 10) { + seqhdr->fps_n = framerates[fps_code][0]; + seqhdr->fps_d = framerates[fps_code][1]; + } else { + GST_DEBUG ("unknown/invalid frame_rate_code %d", fps_code); + /* Force a valid framerate */ + /* FIXME or should this be kept unknown ?? */ + seqhdr->fps_n = 30000; + seqhdr->fps_d = 1001; + } +} + +static gboolean +gst_mpeg_video_parse_sequence (GstMpegVideoSequenceHdr * seqhdr, + GstBitReader * br) +{ + guint8 bits; + guint8 load_intra_flag, load_non_intra_flag; + + /* Setting the height/width codes */ + READ_UINT16 (br, seqhdr->width, 12); + READ_UINT16 (br, seqhdr->height, 12); + + READ_UINT8 (br, seqhdr->aspect_ratio_info, 4); + set_par_from_dar (seqhdr, seqhdr->aspect_ratio_info); + + READ_UINT8 (br, seqhdr->frame_rate_code, 4); + set_fps_from_code (seqhdr, seqhdr->frame_rate_code); + + READ_UINT32 (br, seqhdr->bitrate_value, 18); + if (seqhdr->bitrate_value == 0x3ffff) { + /* VBR stream */ + seqhdr->bitrate = 0; + } else { + /* Value in header is in units of 400 bps */ + seqhdr->bitrate *= 400; + } + + READ_UINT8 (br, bits, 1); + if (bits != MARKER_BIT) + goto failed; + + /* VBV buffer size */ + READ_UINT16 (br, seqhdr->vbv_buffer_size_value, 10); + + /* constrained_parameters_flag */ + READ_UINT8 (br, seqhdr->constrained_parameters_flag, 1); + + /* load_intra_quantiser_matrix */ + READ_UINT8 (br, load_intra_flag, 1); + if (load_intra_flag) { + gint i; + for (i = 0; i < 64; i++) + READ_UINT8 (br, seqhdr->intra_quantizer_matrix[mpeg_zigzag_8x8[i]], 8); + } else + memcpy (seqhdr->intra_quantizer_matrix, default_intra_quantizer_matrix, 64); + + /* non intra quantizer matrix */ + READ_UINT8 (br, load_non_intra_flag, 1); + if (load_non_intra_flag) { + gint i; + for (i = 0; i < 64; i++) + READ_UINT8 (br, seqhdr->non_intra_quantizer_matrix[mpeg_zigzag_8x8[i]], + 8); + } else + memset (seqhdr->non_intra_quantizer_matrix, 16, 64); + + /* dump some info */ + GST_LOG ("width x height: %d x %d", seqhdr->width, seqhdr->height); + GST_LOG ("fps: %d/%d", seqhdr->fps_n, seqhdr->fps_d); + GST_LOG ("par: %d/%d", seqhdr->par_w, seqhdr->par_h); + GST_LOG ("bitrate: %d", seqhdr->bitrate); + + return TRUE; + + /* ERRORS */ +failed: + { + GST_WARNING ("Failed to parse sequence header"); + /* clear out stuff */ + memset (seqhdr, 0, sizeof (*seqhdr)); + return FALSE; + } +} + +static inline guint +scan_for_start_codes (const GstByteReader * reader, guint offset, guint size) +{ + const guint8 *data; + guint32 state; + guint i; + + g_return_val_if_fail (size > 0, -1); + g_return_val_if_fail ((guint64) offset + size <= reader->size - reader->byte, + -1); + + /* we can't find the pattern with less than 4 bytes */ + if (G_UNLIKELY (size < 4)) + return -1; + + data = reader->data + reader->byte + offset; + + /* set the state to something that does not match */ + state = 0xffffffff; + + /* now find data */ + for (i = 0; i < size; i++) { + /* throw away one byte and move in the next byte */ + state = ((state << 8) | data[i]); + if (G_UNLIKELY ((state & 0xffffff00) == 0x00000100)) { + /* we have a match but we need to have skipped at + * least 4 bytes to fill the state. */ + if (G_LIKELY (i >= 3)) + return offset + i - 3; + } + + /* TODO: reimplement making 010001 not detected as a sc + * Accelerate search for start code + * if (data[i] > 1) { + * while (i < (size - 4) && data[i] > 1) { + * if (data[i + 3] > 1) + * i += 4; + * else + * i += 1; + * } + * state = 0x00000100; + *} + */ + } + + /* nothing found */ + return -1; +} + +/****** API *******/ + +/** + * gst_mpeg_video_parse: + * @data: The datas from which to parse + * @size: The size of @data + * @offset: The offset from which to start the parsing + * + * Parses @data, and detects the different packets types, offset, + * and size, starting from @offset + * + * Returns: a #GList of #GstMpegVideoTypeOffsetSize + */ +GList * +gst_mpeg_video_parse (guint8 * data, gsize size, guint offset) +{ + gint off, rsize; + GstByteReader br; + GList *ret = NULL; + size = size - offset; + + if (!initialized) { + GST_DEBUG_CATEGORY_INIT (mpegvideo_parser_debug, "codecparsers_mpegvideo", + 0, "Mpegvideo parser library"); + initialized = TRUE; + } + + if (size <= 0) { + GST_DEBUG ("Can't parse from offset %d, buffer is to small", offset); + return NULL; + } + + gst_byte_reader_init (&br, &data[offset], size); + + off = scan_for_start_codes (&br, 0, size); + + if (off < 0) { + GST_DEBUG ("No start code prefix in this buffer"); + return NULL; + } + + while (off >= 0 && off + 3 < size) { + GstMpegVideoTypeOffsetSize *codoffsize; + + gst_byte_reader_skip (&br, off + 3); + + codoffsize = g_malloc (sizeof (GstMpegVideoTypeOffsetSize)); + gst_byte_reader_get_uint8 (&br, &codoffsize->type); + + codoffsize->offset = gst_byte_reader_get_pos (&br) + offset; + + rsize = gst_byte_reader_get_remaining (&br); + if (rsize <= 0) + break; + + off = scan_for_start_codes (&br, 0, rsize); + + codoffsize->size = off; + + ret = g_list_prepend (ret, codoffsize); + codoffsize = ret->data; + } + + return g_list_reverse (ret); +} + +/** + * gst_mpeg_video_parse_sequence_header: + * @seqhdr: The #GstMpegVideoSequenceHdr to set + * @data: The datas from which to parse the seqhdr + * @size: The size of @data + * @offset: The offset in byte from which to start parsing @data + * + * Sets the @seqhdr Mpeg Video Sequence Header structure members from @data + * + * Returns: %TRUE if the seqhdr could be parsed correctly, %FALSE otherwize. + */ +gboolean +gst_mpeg_video_parse_sequence_header (GstMpegVideoSequenceHdr * seqhdr, + guint8 * data, gsize size, guint offset) +{ + GstBitReader br; + + size = size - offset; + + if (size - offset < 4) + return FALSE; + + gst_bit_reader_init (&br, &data[offset], size); + + return gst_mpeg_video_parse_sequence (seqhdr, &br); +} + +/** + * gst_mpeg_video_parse_sequence_extension: + * @seqhdr: The #GstMpegVideoSequenceExt to set + * @data: The datas from which to parse the seqext + * @size: The size of @data + * @offset: The offset in byte from which to start parsing @data + * + * Sets the @seqext Mpeg Video Sequence Extension structure members from @data + * + * Returns: %TRUE if the seqext could be parsed correctly, %FALSE otherwize. + */ +gboolean +gst_mpeg_video_parse_sequence_extension (GstMpegVideoSequenceExt * seqext, + guint8 * data, gsize size, guint offset) +{ + GstBitReader br; + + size = size - offset; + + if (size < 6) { + GST_DEBUG ("not enough bytes to parse the extension"); + return FALSE; + } + + gst_bit_reader_init (&br, &data[offset], size); + + if (gst_bit_reader_get_bits_uint8_unchecked (&br, 4) != + GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE) { + GST_DEBUG ("Not parsing a sequence extension"); + return FALSE; + } + + /* skip profile and level escape bit */ + gst_bit_reader_skip_unchecked (&br, 1); + + seqext->profile = gst_bit_reader_get_bits_uint8_unchecked (&br, 3); + seqext->level = gst_bit_reader_get_bits_uint8_unchecked (&br, 4); + + /* progressive */ + seqext->progressive = gst_bit_reader_get_bits_uint8_unchecked (&br, 1); + + /* chroma format */ + seqext->chroma_format = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); + + /* resolution extension */ + seqext->horiz_size_ext = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); + seqext->vert_size_ext = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); + + seqext->bitrate_ext = gst_bit_reader_get_bits_uint16_unchecked (&br, 12); + + /* skip marker bits */ + gst_bit_reader_skip_unchecked (&br, 1); + + seqext->vbv_buffer_size_extension = + gst_bit_reader_get_bits_uint8_unchecked (&br, 8); + seqext->low_delay = gst_bit_reader_get_bits_uint8_unchecked (&br, 1); + + /* framerate extension */ + seqext->fps_n_ext = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); + seqext->fps_d_ext = gst_bit_reader_get_bits_uint8_unchecked (&br, 2); + + return TRUE; +} + +/** + * gst_mpeg_video_parse_quant_matrix_extension: + * @ext: The #GstMpegVideoQuantMatrixExt to set + * @data: The datas from which to parse @quant + * @size: The size of @data + * @offset: The offset in byte from which to start the parsing + * + * Sets the @quant Mpeg Video Quant Matrix Extension structure members from + * @data + * + * Returns: %TRUE if the quant matrix extension could be parsed correctly, + * %FALSE otherwize. + */ +gboolean +gst_mpeg_video_parse_quant_matrix_extension (GstMpegVideoQuantMatrixExt * quant, + guint8 * data, gsize size, guint offset) +{ + guint8 i; + GstBitReader br; + + size = size - offset; + + if (size < 1) { + GST_DEBUG ("not enough bytes to parse the extension"); + return FALSE; + } + + gst_bit_reader_init (&br, &data[offset], size); + + if (gst_bit_reader_get_bits_uint8_unchecked (&br, 4) != + GST_MPEG_VIDEO_PACKET_EXT_QUANT_MATRIX) { + GST_DEBUG ("Not parsing a quant matrix extension"); + return FALSE; + } + + READ_UINT8 (&br, quant->load_intra_quantiser_matrix, 1); + if (quant->load_intra_quantiser_matrix) { + for (i = 0; i < 64; i++) { + READ_UINT8 (&br, quant->intra_quantiser_matrix[mpeg_zigzag_8x8[i]], 8); + } + } + + READ_UINT8 (&br, quant->load_non_intra_quantiser_matrix, 1); + if (quant->load_non_intra_quantiser_matrix) { + for (i = 0; i < 64; i++) { + READ_UINT8 (&br, quant->non_intra_quantiser_matrix[mpeg_zigzag_8x8[i]], + 8); + } + } + + READ_UINT8 (&br, quant->load_chroma_intra_quantiser_matrix, 1); + if (quant->load_non_intra_quantiser_matrix) { + for (i = 0; i < 64; i++) { + READ_UINT8 (&br, quant->chroma_intra_quantiser_matrix[mpeg_zigzag_8x8[i]], + 8); + } + } + + READ_UINT8 (&br, quant->load_chroma_non_intra_quantiser_matrix, 1); + if (quant->load_chroma_non_intra_quantiser_matrix) { + for (i = 0; i < 64; i++) { + READ_UINT8 (&br, + quant->chroma_non_intra_quantiser_matrix[mpeg_zigzag_8x8[i]], 8); + } + } + + return TRUE; + +failed: + GST_WARNING ("error parsing \"Quant Matrix Extension\""); + return FALSE; +} + +/** + * gst_mpeg_video_parse_picture_extension: + * @ext: The #GstMpegVideoPictureExt to set + * @data: The datas from which to parse the ext + * @size: The size of @data + * @offset: The offset in byte from which to start the parsing + * + * Sets the @ext Mpeg Video Picture Extension structure members from @data + * + * Returns: %TRUE if the picture extension could be parsed correctly, + * %FALSE otherwize. + */ +gboolean +gst_mpeg_video_parse_picture_extension (GstMpegVideoPictureExt * ext, + guint8 * data, gsize size, guint offset) +{ + GstBitReader br; + + size = size - offset; + + if (size < 4) + return FALSE; + + gst_bit_reader_init (&br, &data[offset], size); + + if (gst_bit_reader_get_bits_uint8_unchecked (&br, 4) != + GST_MPEG_VIDEO_PACKET_EXT_PICTURE) { + GST_DEBUG ("Not parsing a picture extension"); + return FALSE; + } + + /* f_code */ + READ_UINT8 (&br, ext->f_code[0][0], 4); + READ_UINT8 (&br, ext->f_code[0][1], 4); + READ_UINT8 (&br, ext->f_code[1][0], 4); + READ_UINT8 (&br, ext->f_code[1][1], 4); + + /* intra DC precision */ + READ_UINT8 (&br, ext->intra_dc_precision, 2); + + /* picture structure */ + READ_UINT8 (&br, ext->picture_structure, 2); + + /* top field first */ + READ_UINT8 (&br, ext->top_field_first, 1); + + /* frame pred frame dct */ + READ_UINT8 (&br, ext->frame_pred_frame_dct, 1); + + /* concealment motion vectors */ + READ_UINT8 (&br, ext->concealment_motion_vectors, 1); + + /* q scale type */ + READ_UINT8 (&br, ext->q_scale_type, 1); + + /* intra vlc format */ + READ_UINT8 (&br, ext->intra_vlc_format, 1); + + /* alternate scan */ + READ_UINT8 (&br, ext->alternate_scan, 1); + + /* repeat first field */ + READ_UINT8 (&br, ext->repeat_first_field, 1); + + /* chroma_420_type */ + READ_UINT8 (&br, ext->chroma_420_type, 1); + + /* progressive_frame */ + READ_UINT8 (&br, ext->progressive_frame, 1); + + /* composite display */ + READ_UINT8 (&br, ext->composite_display, 1); + + if (ext->composite_display) { + + /* v axis */ + READ_UINT8 (&br, ext->v_axis, 1); + + /* field sequence */ + READ_UINT8 (&br, ext->field_sequence, 3); + + /* sub carrier */ + READ_UINT8 (&br, ext->sub_carrier, 1); + + /* burst amplitude */ + READ_UINT8 (&br, ext->burst_amplitude, 7); + + /* sub_carrier phase */ + READ_UINT8 (&br, ext->sub_carrier_phase, 8); + } + + return TRUE; + +failed: + GST_WARNING ("error parsing \"Picture Coding Extension\""); + return FALSE; + +} + +/** + * gst_mpeg_video_parse_picture_header: + * @hdr: The #GstMpegVideoPictureHdr to set + * @data: The datas from which to parse the hdr + * @size: The size of @data + * @offset: The offset in byte from which to start the parsing + * + * Sets the @hdr Mpeg Video Picture Header structure members from @data + * + * Returns: %TRUE if the picture sequence could be parsed correctly, %FALSE otherwize. + */ +gboolean +gst_mpeg_video_parse_picture_header (GstMpegVideoPictureHdr * hdr, + guint8 * data, gsize size, guint offset) +{ + GstBitReader br; + + size = size - offset; + + if (size < 4) + return FALSE; + + gst_bit_reader_init (&br, &data[offset], size); + + /* temperal sequence number */ + if (!gst_bit_reader_get_bits_uint16 (&br, &hdr->tsn, 10)) + return FALSE; + + /* frame type */ + if (!gst_bit_reader_get_bits_uint8 (&br, (guint8 *) & hdr->pic_type, 3)) + return FALSE; + + if (hdr->pic_type == 0 || hdr->pic_type > 4) + return FALSE; /* Corrupted picture packet */ + + /* skype VBV delay */ + if (!gst_bit_reader_skip (&br, 8)) + return FALSE; + + if (hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_P + || hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_B) { + + READ_UINT8 (&br, hdr->full_pel_forward_vector, 1); + + READ_UINT8 (&br, hdr->f_code[0][0], 3); + hdr->f_code[0][1] = hdr->f_code[0][0]; + } else { + hdr->full_pel_forward_vector = 0; + hdr->f_code[0][0] = hdr->f_code[0][1] = 0; + } + + if (hdr->pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_B) { + READ_UINT8 (&br, hdr->full_pel_backward_vector, 1); + + READ_UINT8 (&br, hdr->f_code[1][0], 3); + hdr->f_code[1][1] = hdr->f_code[1][0]; + } else { + hdr->full_pel_backward_vector = 0; + hdr->f_code[1][0] = hdr->f_code[1][1] = 0; + } + + return TRUE; + +failed: + { + GST_WARNING ("Failed to parse sequence extension"); + return FALSE; + } +} + +/** + * gst_mpeg_video_parse_gop: + * @gop: The #GstMpegVideoGop to set + * @data: The datas from which to parse the gop + * @size: The size of @data + * @offset: The offset in byte from which to start the parsing + * + * + * Sets the @gop Mpeg Video Group of Picture structure members from @data + * + * Returns: %TRUE if the gop could be parsed correctly, %FALSE otherwize. + */ +gboolean +gst_mpeg_video_parse_gop (GstMpegVideoGop * gop, guint8 * data, + gsize size, guint offset) +{ + GstBitReader br; + + size = size - offset; + + if (size < 4) + return FALSE; + + gst_bit_reader_init (&br, &data[offset], size); + + READ_UINT8 (&br, gop->drop_frame_flag, 1); + + READ_UINT8 (&br, gop->hour, 5); + + READ_UINT8 (&br, gop->minute, 6); + + /* skip unused bit */ + if (!gst_bit_reader_skip (&br, 1)) + return FALSE; + + READ_UINT8 (&br, gop->second, 6); + + READ_UINT8 (&br, gop->frame, 6); + + READ_UINT8 (&br, gop->closed_gop, 1); + + READ_UINT8 (&br, gop->broken_gop, 1); + + return TRUE; + +failed: + GST_WARNING ("error parsing \"GOP\""); + return FALSE; +} diff --git a/gst-libs/gst/codecparsers/gstmpegvideoparser.h b/gst-libs/gst/codecparsers/gstmpegvideoparser.h new file mode 100644 index 0000000000..a42792b81b --- /dev/null +++ b/gst-libs/gst/codecparsers/gstmpegvideoparser.h @@ -0,0 +1,368 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * From bad/sys/vdpau/mpeg/mpegutil.c: + * Copyright (C) <2007> Jan Schmidt + * Copyright (C) <2009> Carl-Anton Ingmarsson + * + * 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_MPEG_VIDEO_UTILS_H__ +#define __GST_MPEG_VIDEO_UTILS_H__ + +#include + +G_BEGIN_DECLS + +/** + * GstMpegVideoPacketTypeCode: + * @GST_MPEG_VIDEO_PACKET_PICTURE: Picture packet starting code + * @GST_MPEG_VIDEO_PACKET_SLICE_MIN: Picture packet starting code + * @GST_MPEG_VIDEO_PACKET_SLICE_MAX: Slice max packet starting code + * @GST_MPEG_VIDEO_PACKET_USER_DATA: User data packet starting code + * @GST_MPEG_VIDEO_PACKET_SEQUENCE : Sequence packet starting code + * @GST_MPEG_VIDEO_PACKET_EXTENSION: Extension packet starting code + * @GST_MPEG_VIDEO_PACKET_SEQUENCE_END: Sequence end packet code + * @GST_MPEG_VIDEO_PACKET_GOP: Group of Picture packet starting code + * @GST_MPEG_VIDEO_PACKET_NONE: None packet code + * + * Indicates the type of MPEG packet + */ +typedef enum { + GST_MPEG_VIDEO_PACKET_PICTURE = 0x00, + GST_MPEG_VIDEO_PACKET_SLICE_MIN = 0x01, + GST_MPEG_VIDEO_PACKET_SLICE_MAX = 0xaf, + GST_MPEG_VIDEO_PACKET_USER_DATA = 0xb2, + GST_MPEG_VIDEO_PACKET_SEQUENCE = 0xb3, + GST_MPEG_VIDEO_PACKET_EXTENSION = 0xb5, + GST_MPEG_VIDEO_PACKET_SEQUENCE_END = 0xb7, + GST_MPEG_VIDEO_PACKET_GOP = 0xb8, + GST_MPEG_VIDEO_PACKET_NONE = 0xff +} GstMpegVideoPacketTypeCode; + +/** + * GstMpegVideoPacketExtensionCode: + * @GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE: Sequence extension code + * @GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_DISPLAY: Display extension code + * @GST_MPEG_VIDEO_PACKET_EXT_QUANT_MATRIX: Quantizer extension code + * @GST_MPEG_VIDEO_PACKET_EXT_GOP: Group Of Picture extension code + * + * Indicates what type of packets are in this + * block, some are mutually * exclusive though - ie, sequence packs are + * accumulated separately. GOP & Picture may occur together or separately + */ +typedef enum { + GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE = 0x01, + GST_MPEG_VIDEO_PACKET_EXT_SEQUENCE_DISPLAY = 0x02, + GST_MPEG_VIDEO_PACKET_EXT_QUANT_MATRIX = 0x03, + GST_MPEG_VIDEO_PACKET_EXT_GOP = 0x04, + GST_MPEG_VIDEO_PACKET_EXT_PICTURE = 0x08 +} GstMpegVideoPacketExtensionCode; + +/** + * GstMpegVideoLevel: + * @GST_MPEG_VIDEO_LEVEL_LOW: Level Low + * @GST_MPEG_VIDEO_LEVEL_MAIN: Level Main + * @GST_MPEG_VIDEO_LEVEL_HIGH_1440: Level High 1440 + * @GST_MPEG_VIDEO_LEVEL_HIGH: Level High + * + * Indicates the level in use + **/ +typedef enum { + GST_MPEG_VIDEO_LEVEL_HIGH = 0x04, + GST_MPEG_VIDEO_LEVEL_HIGH_1440 = 0x06, + GST_MPEG_VIDEO_LEVEL_MAIN = 0x08, + GST_MPEG_VIDEO_LEVEL_LOW = 0x0a +} GstMpegVideoLevel; + +/** + * GstMpegVideoProfile: + * @GST_MPEG_VIDEO_PROFILE_422, + * @GST_MPEG_VIDEO_PROFILE_HIGH, + * @GST_MPEG_VIDEO_PROFILE_SPATIALLY_SCALABLE, + * @GST_MPEG_VIDEO_PROFILE_SNR_SCALABLE, + * @GST_MPEG_VIDEO_PROFILE_MAIN, + * @GST_MPEG_VIDEO_PROFILE_SIMPLE, + * + * Indicates the profile type in use + **/ +typedef enum { + GST_MPEG_VIDEO_PROFILE_422 = 0x00, + GST_MPEG_VIDEO_PROFILE_HIGH = 0x01, + GST_MPEG_VIDEO_PROFILE_SPATIALLY_SCALABLE = 0x02, + GST_MPEG_VIDEO_PROFILE_SNR_SCALABLE = 0x03, + GST_MPEG_VIDEO_PROFILE_MAIN = 0x04, + GST_MPEG_VIDEO_PROFILE_SIMPLE = 0x05 +} GstMpegVideoProfile; + +/** + * GstMpegVideoPictureType: + * @GST_MPEG_VIDEO_PICTURE_TYPE_I: Type I + * @GST_MPEG_VIDEO_PICTURE_TYPE_P: Type P + * @GST_MPEG_VIDEO_PICTURE_TYPE_B: Type B + * @GST_MPEG_VIDEO_PICTURE_TYPE_D: Type D + * + * Indicates the type of picture + */ +typedef enum { + GST_MPEG_VIDEO_PICTURE_TYPE_I = 0x01, + GST_MPEG_VIDEO_PICTURE_TYPE_P = 0x02, + GST_MPEG_VIDEO_PICTURE_TYPE_B = 0x03, + GST_MPEG_VIDEO_PICTURE_TYPE_D = 0x04 +} GstMpegVideoPictureType; + +/** + * GstMpegVideoPictureStructure: + * @GST_MPEG_VIDEO_PICTURE_STRUCTURE_TOP_FIELD: Top field + * @GST_MPEG_VIDEO_PICTURE_STRUCTURE_BOTTOM_FIELD: Bottom field + * @GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME: Frame + * + * Indicates the structure of picture + */ +typedef enum { + GST_MPEG_VIDEO_PICTURE_STRUCTURE_TOP_FIELD = 0x01, + GST_MPEG_VIDEO_PICTURE_STRUCTURE_BOTTOM_FIELD = 0x02, + GST_MPEG_VIDEO_PICTURE_STRUCTURE_FRAME = 0x03 +} GstMpegVideoPictureStructure; + +typedef struct _GstMpegVideoSequenceHdr GstMpegVideoSequenceHdr; +typedef struct _GstMpegVideoSequenceExt GstMpegVideoSequenceExt; +typedef struct _GstMpegVideoPictureHdr GstMpegVideoPictureHdr; +typedef struct _GstMpegVideoGop GstMpegVideoGop; +typedef struct _GstMpegVideoPictureExt GstMpegVideoPictureExt; +typedef struct _GstMpegVideoQuantMatrixExt GstMpegVideoQuantMatrixExt; +typedef struct _GstMpegVideoTypeOffsetSize GstMpegVideoTypeOffsetSize; + +/** + * GstMpegVideoSequenceHdr: + * @width: Width of each frame + * @height: Height of each frame + * @par_w: Calculated Pixel Aspect Ratio width + * @par_h: Pixel Aspect Ratio height + * @fps_n: Calculated Framrate nominator + * @fps_d: Calculated Framerate denominator + * @bitrate_value: Value of the bitrate as is in the stream (400bps unit) + * @bitrate: the real bitrate of the Mpeg video stream in bits per second, 0 if VBR stream + * @constrained_parameters_flag: %TRUE if this stream uses contrained parameters. + * @intra_quantizer_matrix: intra-quantization table + * @non_intra_quantizer_matrix: non-intra quantization table + * + * The Mpeg2 Video Sequence Header structure. + */ +struct _GstMpegVideoSequenceHdr +{ + guint16 width, height; + guint8 aspect_ratio_info; + guint8 frame_rate_code; + guint32 bitrate_value; + guint16 vbv_buffer_size_value; + + guint8 constrained_parameters_flag; + + guint8 intra_quantizer_matrix[64]; + guint8 non_intra_quantizer_matrix[64]; + + /* Calculated values */ + guint par_w, par_h; + guint fps_n, fps_d; + guint bitrate; +}; + +/** + * GstMpegVideoSequenceExt: + * @profile: mpeg2 decoder profil + * @level: mpeg2 decoder level + * @progressive: %TRUE if the frames are progressive %FALSE otherwize + * @chroma_format: indicates the chrominance format + * @horiz_size_ext: Horizontal size + * @vert_size_ext: Vertical size + * @bitrate_ext: The bitrate + * @vbv_buffer_size_extension: Vbv vuffer size + * @low_delay: %TRUE if the sequence doesn't contain any B-pitcture, %FALSE + * otherwize + * @fps_n_ext: Framerate nominator code + * @fps_d_ext: Framerate denominator code + * + * The Mpeg2 Video Sequence Extension structure. + **/ +struct _GstMpegVideoSequenceExt +{ + /* mpeg2 decoder profile */ + guint8 profile; + /* mpeg2 decoder level */ + guint8 level; + + guint8 progressive; + guint8 chroma_format; + + guint8 horiz_size_ext, vert_size_ext; + + guint16 bitrate_ext; + guint8 vbv_buffer_size_extension; + guint8 low_delay; + guint8 fps_n_ext, fps_d_ext; + +}; + +/** + * GstMpegVideoQuantMatrixExt: + * @load_intra_quantiser_matrix + * @intra_quantiser_matrix + * @load_non_intra_quantiser_matrix + * @non_intra_quantiser_matrix: + * @load_chroma_intra_quantiser_matrix + * @chroma_intra_quantiser_matrix + * @load_chroma_non_intra_quantiser_matrix + * @chroma_non_intra_quantiser_matrix + * + * The Quant Matrix Extension structure + */ +struct _GstMpegVideoQuantMatrixExt +{ + guint8 load_intra_quantiser_matrix; + guint8 intra_quantiser_matrix[64]; + guint8 load_non_intra_quantiser_matrix; + guint8 non_intra_quantiser_matrix[64]; + guint8 load_chroma_intra_quantiser_matrix; + guint8 chroma_intra_quantiser_matrix[64]; + guint8 load_chroma_non_intra_quantiser_matrix; + guint8 chroma_non_intra_quantiser_matrix[64]; +}; + +/** + * GstMpegVideoPictureHdr: + * @tsn: Temporal Sequence Number + * @pic_type: Type of the frame + * @full_pel_forward_vector: the full pel forward flag of + * the frame: 0 or 1. + * @full_pel_backward_vector: the full pel backward flag + * of the frame: 0 or 1. + * @f_code: F code + * + * The Mpeg2 Video Picture Header structure. + */ +struct _GstMpegVideoPictureHdr +{ + guint16 tsn; + guint8 pic_type; + + guint8 full_pel_forward_vector, full_pel_backward_vector; + + guint8 f_code[2][2]; +}; + +/** + * GstMpegVideoPictureExt: + * @intra_dc_precision: Intra DC precision + * @picture_structure: Structure of the picture + * @top_field_first: Top field first + * @frame_pred_frame_dct: Frame + * @concealment_motion_vectors: Concealment Motion Vectors + * @q_scale_type: Q Scale Type + * @intra_vlc_format: Intra Vlc Format + * @alternate_scan: Alternate Scan + * @repeat_first_field: Repeat First Field + * @chroma_420_type: Chroma 420 Type + * @progressive_frame: %TRUE if the frame is progressive %FALSE otherwize + * + * The Mpeg2 Video Picture Extension structure. + */ +struct _GstMpegVideoPictureExt +{ + guint8 f_code[2][2]; + + guint8 intra_dc_precision; + guint8 picture_structure; + guint8 top_field_first; + guint8 frame_pred_frame_dct; + guint8 concealment_motion_vectors; + guint8 q_scale_type; + guint8 intra_vlc_format; + guint8 alternate_scan; + guint8 repeat_first_field; + guint8 chroma_420_type; + guint8 progressive_frame; + guint8 composite_display; + guint8 v_axis; + guint8 field_sequence; + guint8 sub_carrier; + guint8 burst_amplitude; + guint8 sub_carrier_phase; +}; + +/** + * GstMpegVideoGop: + * @drop_frame_flag: Drop Frame Flag + * @hour: Hour (0-23) + * @minute: Minute (O-59) + * @second: Second (0-59) + * @frame: Frame (0-59) + * @closed_gop: Closed Gop + * @broken_gop: Broken Gop + * + * The Mpeg Video Group of Picture structure. + */ +struct _GstMpegVideoGop +{ + guint8 drop_frame_flag; + + guint8 hour, minute, second, frame; + + guint8 closed_gop; + guint8 broken_gop; +}; + +/** + * GstMpegVideoTypeOffsetSize: + * @type: the type of the packet that start at @offset + * @offset: the offset of the packet start in bytes, it is the exact, start of the packet, no sync code included + * @size: The size in bytes of the packet or -1 if the end wasn't found. It is the exact size of the packet, no sync code included + * + * A structure that contains the type of a packet, its offset and its size + */ +struct _GstMpegVideoTypeOffsetSize +{ + guint8 type; + guint offset; + gint size; +}; + +GList * gst_mpeg_video_parse (guint8 * data, gsize size, guint offset); + +gboolean gst_mpeg_video_parse_sequence_header (GstMpegVideoSequenceHdr * params, + guint8 * data, gsize size, guint offset); + +gboolean gst_mpeg_video_parse_picture_header (GstMpegVideoPictureHdr* hdr, + guint8 * data, gsize size, guint offset); + +gboolean gst_mpeg_video_parse_picture_extension (GstMpegVideoPictureExt *ext, + guint8 * data, gsize size, guint offset); + +gboolean gst_mpeg_video_parse_gop (GstMpegVideoGop * gop, + guint8 * data, gsize size, guint offset); + +gboolean gst_mpeg_video_parse_sequence_extension (GstMpegVideoSequenceExt * seqext, + guint8 * data, gsize size, guint offset); + +gboolean gst_mpeg_video_parse_quant_matrix_extension (GstMpegVideoQuantMatrixExt * quant, + guint8 * data, gsize size, guint offset); + +G_END_DECLS + +#endif diff --git a/gst-libs/gst/play/.gitignore b/gst-libs/gst/play/.gitignore deleted file mode 100644 index b7f5a7eb66..0000000000 --- a/gst-libs/gst/play/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -play-enumtypes.[ch] -play-marshal.[ch] -play-marshal.list diff --git a/gst-libs/gst/play/play.h b/gst-libs/gst/play/play.h deleted file mode 100644 index 6cbedce379..0000000000 --- a/gst-libs/gst/play/play.h +++ /dev/null @@ -1,104 +0,0 @@ -/* GStreamer - * Copyright (C) 2003 Julien Moutte - * - * 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_PLAY_H__ -#define __GST_PLAY_H__ - -#include -#include -#include - -G_BEGIN_DECLS - -/* GError stuff */ - -#define GST_PLAY_ERROR gst_play_error_quark () -/* GObject stuff */ - -#define GST_TYPE_PLAY (gst_play_get_type()) -#define GST_PLAY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PLAY, GstPlay)) -#define GST_PLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PLAY, GstPlayClass)) -#define GST_IS_PLAY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PLAY)) -#define GST_IS_PLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLAY)) -#define GST_PLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PLAY, GstPlayClass)) - -typedef enum -{ - GST_PLAY_SINK_TYPE_AUDIO, - GST_PLAY_SINK_TYPE_VIDEO, - GST_PLAY_SINK_TYPE_ANY, -} GstPlaySinkType; - -typedef struct _GstPlay GstPlay; -typedef struct _GstPlayClass GstPlayClass; -typedef struct _GstPlayPrivate GstPlayPrivate; - -struct _GstPlay -{ - GstPipeline pipeline; - - GstPlayPrivate *priv; - - gpointer _gst_reserved[GST_PADDING]; -}; - -struct _GstPlayClass -{ - GstPipelineClass parent_class; - - void (*time_tick) (GstPlay *play, gint64 time_nanos); - void (*stream_length) (GstPlay *play, gint64 length_nanos); - void (*have_video_size) (GstPlay *play, gint width, gint height); - - gpointer _gst_reserved[GST_PADDING]; -}; - -GType gst_play_get_type (void); -GstPlay * gst_play_new (GError **error); - -gboolean gst_play_set_data_src (GstPlay *play, - GstElement *data_src); -gboolean gst_play_set_video_sink (GstPlay *play, - GstElement *video_sink); -gboolean gst_play_set_audio_sink (GstPlay *play, - GstElement *audio_sink); - -gboolean gst_play_set_visualization (GstPlay *play, - GstElement *element); -gboolean gst_play_connect_visualization (GstPlay *play, - gboolean connect); - -gboolean gst_play_set_location (GstPlay *play, - const char *location); -char * gst_play_get_location (GstPlay *play); - -gboolean gst_play_seek_to_time (GstPlay *play, - gint64 time_nanos); - -GstElement * gst_play_get_sink_element (GstPlay *play, - GstElement *element, - GstPlaySinkType sink_type); -GList * gst_play_get_all_by_interface (GstPlay *play, - GType interface); - -gdouble gst_play_get_framerate (GstPlay *play); - -G_END_DECLS - -#endif /* __GST_PLAY_H__ */ diff --git a/gst-libs/gst/play/play.vcproj b/gst-libs/gst/play/play.vcproj deleted file mode 100644 index d55cb3a579..0000000000 --- a/gst-libs/gst/play/play.vcproj +++ /dev/null @@ -1,144 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gst-libs/gst/video/gstbasevideoencoder.c b/gst-libs/gst/video/gstbasevideoencoder.c index e1c2c77a62..b04cf7710b 100644 --- a/gst-libs/gst/video/gstbasevideoencoder.c +++ b/gst-libs/gst/video/gstbasevideoencoder.c @@ -432,6 +432,7 @@ done: GST_LOG_OBJECT (base_video_encoder, "Returning caps %" GST_PTR_FORMAT, fcaps); + g_object_unref (base_video_encoder); return fcaps; } @@ -1024,6 +1025,10 @@ gst_base_video_encoder_set_latency_fields (GstBaseVideoEncoder * { gint64 latency; + /* 0 numerator is used for "don't know" */ + if (GST_BASE_VIDEO_CODEC (base_video_encoder)->state.fps_n == 0) + return; + latency = gst_util_uint64_scale (n_fields, GST_BASE_VIDEO_CODEC (base_video_encoder)->state.fps_d * GST_SECOND, 2 * GST_BASE_VIDEO_CODEC (base_video_encoder)->state.fps_n); diff --git a/gst-plugins-bad.spec.in b/gst-plugins-bad.spec.in index c1c81f198f..adbd33df61 100644 --- a/gst-plugins-bad.spec.in +++ b/gst-plugins-bad.spec.in @@ -204,6 +204,7 @@ make ERROR_CFLAGS='' ERROR_CXXFLAGS='' %{_libdir}/libgstbasevideo-%{majorminor}.so.* %{_libdir}/libgstphotography-%{majorminor}.so.* %{_libdir}/libgstsignalprocessor-%{majorminor}.so.* +%{_libdir}/libgstcodecparsers-%{majorminor}.so.* # Plugins without external dependencies %{_libdir}/gstreamer-%{majorminor}/libgstadpcmdec.so %{_libdir}/gstreamer-%{majorminor}/libgstadpcmenc.so @@ -329,8 +330,10 @@ make ERROR_CFLAGS='' ERROR_CXXFLAGS='' %{_libdir}/libgstbasevideo-%{majorminor}.so %{_libdir}/libgstphotography-%{majorminor}.so %{_libdir}/libgstsignalprocessor-%{majorminor}.so +%{_libdir}/libgstcodecparsers-%{majorminor}.so %{_libdir}/libgstbasecamerabinsrc-%{majorminor}.so %{_includedir}/gstreamer-%{majorminor}/gst/interfaces/photography* +%{_includedir}/gstreamer-%{majorminor}/gst/codecparsers %{_includedir}/gstreamer-%{majorminor}/gst/signalprocessor %{_includedir}/gstreamer-%{majorminor}/gst/video %{_includedir}/gstreamer-%{majorminor}/gst/basecamerabinsrc/gstbasecamerasrc.h diff --git a/gst/camerabin2/gstcamerabin2.c b/gst/camerabin2/gstcamerabin2.c index f40ef051be..1caa164048 100644 --- a/gst/camerabin2/gstcamerabin2.c +++ b/gst/camerabin2/gstcamerabin2.c @@ -273,6 +273,8 @@ gst_cam_flags_get_type (void) {C_FLAGS (GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION), "Do not use viewfinder conversion " "elements", "no-viewfinder-conversion"}, + {C_FLAGS (GST_CAM_FLAG_NO_IMAGE_CONVERSION), "Do not use image conversion " + "elements", "no-image-conversion"}, {0, NULL, NULL} }; static volatile GType id = 0; @@ -1132,7 +1134,7 @@ gst_camera_bin_link_encodebin (GstCameraBin2 * camera, GstElement * encodebin, return GST_PAD_LINK_REFUSED; } - ret = gst_pad_link (srcpad, sinkpad); + ret = gst_pad_link_full (srcpad, sinkpad, GST_PAD_LINK_CHECK_CAPS); gst_object_unref (sinkpad); gst_object_unref (srcpad); @@ -1368,7 +1370,8 @@ gst_camera_bin_create_elements (GstCameraBin2 * camera) } g_object_set (camera->viewfinderbin_queue, "leaky", 2, "silent", TRUE, - NULL); + "max-size-time", (guint64) 0, "max-size-bytes", (guint) 0, + "max-size-buffers", (guint) 1, NULL); gst_bin_add_many (GST_BIN_CAST (camera), gst_object_ref (camera->video_encodebin), @@ -1422,8 +1425,14 @@ gst_camera_bin_create_elements (GstCameraBin2 * camera) encbin_flags |= (1 << 1); g_object_set (camera->video_encodebin, "flags", encbin_flags, NULL); + /* image encodebin has only video branch so disable its conversion elements + * appropriately */ + if (camera->flags & GST_CAM_FLAG_NO_IMAGE_CONVERSION) + g_object_set (camera->image_encodebin, "flags", (1 << 1), NULL); + g_object_set (camera->viewfinderbin, "disable-converters", - camera->flags & GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION, NULL); + camera->flags & GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION ? TRUE : FALSE, + NULL); if (camera->video_profile_switch) { GST_DEBUG_OBJECT (camera, "Switching encodebin's profile"); diff --git a/gst/camerabin2/gstcamerabin2.h b/gst/camerabin2/gstcamerabin2.h index b2cf61f6ce..46113d0372 100644 --- a/gst/camerabin2/gstcamerabin2.h +++ b/gst/camerabin2/gstcamerabin2.h @@ -38,7 +38,10 @@ typedef enum /* matches GstEncFlags GST_ENC_FLAG_NO_VIDEO_CONVERSION in encodebin */ GST_CAM_FLAG_NO_VIDEO_CONVERSION = (1 << 1), /* maps to 'disable-converters' property in viewfinderbin */ - GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION = (1 << 2) + GST_CAM_FLAG_NO_VIEWFINDER_CONVERSION = (1 << 2), + /* maps to GstEncFlags GST_ENC_FLAG_NO_VIDEO_CONVERSION in the image bin's + * encodebin */ + GST_CAM_FLAG_NO_IMAGE_CONVERSION = (1 << 3) } GstCamFlags; diff --git a/gst/dtmf/gstdtmfcommon.h b/gst/dtmf/gstdtmfcommon.h index aff881b987..82617d72fd 100644 --- a/gst/dtmf/gstdtmfcommon.h +++ b/gst/dtmf/gstdtmfcommon.h @@ -9,9 +9,9 @@ #define MAX_VOLUME 36 #define MIN_EVENT 0 -#define MAX_EVENT 16 +#define MAX_EVENT 15 #define MIN_EVENT_STRING "0" -#define MAX_EVENT_STRING "16" +#define MAX_EVENT_STRING "15" #ifndef M_PI #define M_PI 3.14159265358979323846 /* pi */ diff --git a/gst/dtmf/gstdtmfsrc.c b/gst/dtmf/gstdtmfsrc.c index 79525b5729..7492b151e7 100644 --- a/gst/dtmf/gstdtmfsrc.c +++ b/gst/dtmf/gstdtmfsrc.c @@ -65,7 +65,7 @@ * * number * G_TYPE_INT - * 0-16 + * 0-15 * The event number. * * @@ -323,6 +323,7 @@ gst_dtmf_src_handle_dtmf_event (GstDTMFSrc * dtmfsrc, gint event_type; gboolean start; gint method; + GstClockTime last_stop; if (!gst_structure_get_int (event_structure, "type", &event_type) || !gst_structure_get_boolean (event_structure, "start", &start) || @@ -335,6 +336,14 @@ gst_dtmf_src_handle_dtmf_event (GstDTMFSrc * dtmfsrc, } } + + GST_OBJECT_LOCK (dtmfsrc); + if (gst_structure_get_clock_time (event_structure, "last-stop", &last_stop)) + dtmfsrc->last_stop = last_stop; + else + dtmfsrc->last_stop = GST_CLOCK_TIME_NONE; + GST_OBJECT_UNLOCK (dtmfsrc); + if (start) { gint event_number; gint event_volume; @@ -447,19 +456,37 @@ gst_dtmf_src_get_property (GObject * object, guint prop_id, GValue * value, static void gst_dtmf_prepare_timestamps (GstDTMFSrc * dtmfsrc) { - GstClock *clock; + GstClockTime last_stop; + GstClockTime timestamp; - clock = gst_element_get_clock (GST_ELEMENT (dtmfsrc)); - if (clock != NULL) { - dtmfsrc->timestamp = gst_clock_get_time (clock) - - gst_element_get_base_time (GST_ELEMENT (dtmfsrc)); - gst_object_unref (clock); + GST_OBJECT_LOCK (dtmfsrc); + last_stop = dtmfsrc->last_stop; + GST_OBJECT_UNLOCK (dtmfsrc); + + if (GST_CLOCK_TIME_IS_VALID (last_stop)) { + timestamp = last_stop; } else { - gchar *dtmf_name = gst_element_get_name (dtmfsrc); - GST_ERROR_OBJECT (dtmfsrc, "No clock set for element %s", dtmf_name); - dtmfsrc->timestamp = GST_CLOCK_TIME_NONE; - g_free (dtmf_name); + GstClock *clock; + + /* If there is no valid start time, lets use now as the start time */ + + clock = gst_element_get_clock (GST_ELEMENT (dtmfsrc)); + if (clock != NULL) { + timestamp = gst_clock_get_time (clock) + - gst_element_get_base_time (GST_ELEMENT (dtmfsrc)); + gst_object_unref (clock); + } else { + gchar *dtmf_name = gst_element_get_name (dtmfsrc); + GST_ERROR_OBJECT (dtmfsrc, "No clock set for element %s", dtmf_name); + dtmfsrc->timestamp = GST_CLOCK_TIME_NONE; + g_free (dtmf_name); + return; + } } + + /* Make sure the timestamp always goes forward */ + if (timestamp > dtmfsrc->timestamp) + dtmfsrc->timestamp = timestamp; } static void @@ -584,6 +611,12 @@ gst_dtmf_src_create_next_tone_packet (GstDTMFSrc * dtmfsrc, /* timestamp and duration of GstBuffer */ GST_BUFFER_DURATION (buf) = dtmfsrc->interval * GST_MSECOND; GST_BUFFER_TIMESTAMP (buf) = dtmfsrc->timestamp; + + GST_LOG_OBJECT (dtmfsrc, "Creating new buffer with event %u duration " + " gst: %" GST_TIME_FORMAT " at %" GST_TIME_FORMAT, + event->event_number, GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf))); + dtmfsrc->timestamp += GST_BUFFER_DURATION (buf); /* Set caps on the buffer before pushing it */ @@ -823,6 +856,7 @@ gst_dtmf_src_change_state (GstElement * element, GstStateChange transition) g_slice_free (GstDTMFSrcEvent, event); event = g_async_queue_try_pop (dtmfsrc->event_queue); } + dtmfsrc->timestamp = 0; no_preroll = TRUE; break; default: diff --git a/gst/dtmf/gstdtmfsrc.h b/gst/dtmf/gstdtmfsrc.h index aa5d35a611..cda5840a5f 100644 --- a/gst/dtmf/gstdtmfsrc.h +++ b/gst/dtmf/gstdtmfsrc.h @@ -81,6 +81,8 @@ struct _GstDTMFSrc gboolean paused; GstClockID clockid; + GstClockTime last_stop; + gint sample_rate; }; diff --git a/gst/dtmf/gstrtpdtmfsrc.c b/gst/dtmf/gstrtpdtmfsrc.c index c7e1c1fb2a..8f07cafc4a 100644 --- a/gst/dtmf/gstrtpdtmfsrc.c +++ b/gst/dtmf/gstrtpdtmfsrc.c @@ -63,7 +63,7 @@ * * number * G_TYPE_INT - * 0-16 + * 0-15 * The event number. * * @@ -122,9 +122,7 @@ #include "gstrtpdtmfsrc.h" #define GST_RTP_DTMF_TYPE_EVENT 1 -#define DEFAULT_PACKET_INTERVAL 50 /* ms */ -#define MIN_PACKET_INTERVAL 10 /* ms */ -#define MAX_PACKET_INTERVAL 50 /* ms */ +#define DEFAULT_PTIME 40 /* ms */ #define DEFAULT_SSRC -1 #define DEFAULT_PT 96 #define DEFAULT_TIMESTAMP_OFFSET -1 @@ -155,7 +153,6 @@ enum PROP_CLOCK_RATE, PROP_TIMESTAMP, PROP_SEQNUM, - PROP_INTERVAL, PROP_REDUNDANCY }; @@ -264,11 +261,6 @@ gst_rtp_dtmf_src_class_init (GstRTPDTMFSrcClass * klass) g_param_spec_uint ("pt", "payload type", "The payload type of the packets", 0, 0x80, DEFAULT_PT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); - g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_INTERVAL, - g_param_spec_uint ("interval", "Interval between rtp packets", - "Interval in ms between two rtp packets", MIN_PACKET_INTERVAL, - MAX_PACKET_INTERVAL, DEFAULT_PACKET_INTERVAL, - G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_REDUNDANCY, g_param_spec_uint ("packet-redundancy", "Packet Redundancy", "Number of packets to send to indicate start and stop dtmf events", @@ -309,7 +301,7 @@ gst_rtp_dtmf_src_init (GstRTPDTMFSrc * object, GstRTPDTMFSrcClass * g_class) object->ts_offset = DEFAULT_TIMESTAMP_OFFSET; object->pt = DEFAULT_PT; object->clock_rate = DEFAULT_CLOCK_RATE; - object->interval = DEFAULT_PACKET_INTERVAL; + object->ptime = DEFAULT_PTIME; object->packet_redundancy = DEFAULT_PACKET_REDUNDANCY; object->event_queue = @@ -342,6 +334,7 @@ gst_rtp_dtmf_src_handle_dtmf_event (GstRTPDTMFSrc * dtmfsrc, gint event_type; gboolean start; gint method; + GstClockTime last_stop; if (!gst_structure_get_int (event_structure, "type", &event_type) || !gst_structure_get_boolean (event_structure, "start", &start) || @@ -354,6 +347,13 @@ gst_rtp_dtmf_src_handle_dtmf_event (GstRTPDTMFSrc * dtmfsrc, } } + GST_OBJECT_LOCK (dtmfsrc); + if (gst_structure_get_clock_time (event_structure, "last-stop", &last_stop)) + dtmfsrc->last_stop = last_stop; + else + dtmfsrc->last_stop = GST_CLOCK_TIME_NONE; + GST_OBJECT_UNLOCK (dtmfsrc); + if (start) { gint event_number; gint event_volume; @@ -448,9 +448,6 @@ gst_rtp_dtmf_src_set_property (GObject * object, guint prop_id, dtmfsrc->pt = g_value_get_uint (value); dtmfsrc->dirty = TRUE; break; - case PROP_INTERVAL: - dtmfsrc->interval = g_value_get_uint (value); - break; case PROP_REDUNDANCY: dtmfsrc->packet_redundancy = g_value_get_uint (value); break; @@ -490,9 +487,6 @@ gst_rtp_dtmf_src_get_property (GObject * object, guint prop_id, GValue * value, case PROP_SEQNUM: g_value_set_uint (value, dtmfsrc->seqnum); break; - case PROP_INTERVAL: - g_value_set_uint (value, dtmfsrc->interval); - break; case PROP_REDUNDANCY: g_value_set_uint (value, dtmfsrc->packet_redundancy); break; @@ -502,29 +496,40 @@ gst_rtp_dtmf_src_get_property (GObject * object, guint prop_id, GValue * value, } } -static void +static gboolean gst_rtp_dtmf_prepare_timestamps (GstRTPDTMFSrc * dtmfsrc) { - GstClock *clock; + GstClockTime last_stop; - clock = gst_element_get_clock (GST_ELEMENT (dtmfsrc)); - if (clock != NULL) { - dtmfsrc->timestamp = gst_clock_get_time (clock) - + (MIN_INTER_DIGIT_INTERVAL * GST_MSECOND) - - gst_element_get_base_time (GST_ELEMENT (dtmfsrc)); - dtmfsrc->start_timestamp = dtmfsrc->timestamp; - gst_object_unref (clock); + GST_OBJECT_LOCK (dtmfsrc); + last_stop = dtmfsrc->last_stop; + GST_OBJECT_UNLOCK (dtmfsrc); + + if (GST_CLOCK_TIME_IS_VALID (last_stop)) { + dtmfsrc->start_timestamp = last_stop; } else { - gchar *dtmf_name = gst_element_get_name (dtmfsrc); - GST_ERROR_OBJECT (dtmfsrc, "No clock set for element %s", dtmf_name); - dtmfsrc->timestamp = GST_CLOCK_TIME_NONE; - g_free (dtmf_name); + GstClock *clock = gst_element_get_clock (GST_ELEMENT (dtmfsrc)); + + if (clock == NULL) + return FALSE; + + dtmfsrc->start_timestamp = gst_clock_get_time (clock) + - gst_element_get_base_time (GST_ELEMENT (dtmfsrc)); + gst_object_unref (clock); } + /* If the last stop was in the past, then lets add the buffers together */ + if (dtmfsrc->start_timestamp < dtmfsrc->timestamp) + dtmfsrc->start_timestamp = dtmfsrc->timestamp; + + dtmfsrc->timestamp = dtmfsrc->start_timestamp; + dtmfsrc->rtp_timestamp = dtmfsrc->ts_base + gst_util_uint64_scale_int (gst_segment_to_running_time (&GST_BASE_SRC (dtmfsrc)->segment, GST_FORMAT_TIME, dtmfsrc->timestamp), dtmfsrc->clock_rate, GST_SECOND); + + return TRUE; } @@ -539,7 +544,7 @@ gst_rtp_dtmf_src_add_start_event (GstRTPDTMFSrc * dtmfsrc, gint event_number, event->payload = g_slice_new0 (GstRTPDTMFPayload); event->payload->event = CLAMP (event_number, MIN_EVENT, MAX_EVENT); event->payload->volume = CLAMP (event_volume, MIN_VOLUME, MAX_VOLUME); - event->payload->duration = dtmfsrc->interval * dtmfsrc->clock_rate / 1000; + event->payload->duration = dtmfsrc->ptime * dtmfsrc->clock_rate / 1000; g_async_queue_push (dtmfsrc->event_queue, event); } @@ -586,30 +591,40 @@ gst_rtp_dtmf_prepare_buffer_data (GstRTPDTMFSrc * dtmfsrc, GstBuffer * buf) if (dtmfsrc->redundancy_count > 1) GST_BUFFER_DURATION (buf) = 0; else - GST_BUFFER_DURATION (buf) = dtmfsrc->interval * GST_MSECOND; + GST_BUFFER_DURATION (buf) = dtmfsrc->ptime * GST_MSECOND; GST_BUFFER_TIMESTAMP (buf) = dtmfsrc->timestamp; - dtmfsrc->timestamp += GST_BUFFER_DURATION (buf); payload = (GstRTPDTMFPayload *) gst_rtp_buffer_get_payload (buf); /* copy payload and convert to network-byte order */ g_memmove (payload, dtmfsrc->payload, sizeof (GstRTPDTMFPayload)); - /* Force the packet duration to a certain minumum - * if its the end of the event - */ - if (payload->e && - payload->duration < MIN_PULSE_DURATION * dtmfsrc->clock_rate / 1000) - payload->duration = MIN_PULSE_DURATION * dtmfsrc->clock_rate / 1000; payload->duration = g_htons (payload->duration); + if (dtmfsrc->redundancy_count <= 1 && dtmfsrc->last_packet) { + GstClockTime inter_digit_interval = MIN_INTER_DIGIT_INTERVAL; + + if (inter_digit_interval % dtmfsrc->ptime != 0) + inter_digit_interval += dtmfsrc->ptime - + (MIN_INTER_DIGIT_INTERVAL % dtmfsrc->ptime); + + GST_BUFFER_DURATION (buf) += inter_digit_interval * GST_MSECOND; + } + + GST_LOG_OBJECT (dtmfsrc, "Creating new buffer with event %u duration " + " gst: %" GST_TIME_FORMAT " at %" GST_TIME_FORMAT "(rtp ts:%u dur:%u)", + dtmfsrc->payload->event, GST_TIME_ARGS (GST_BUFFER_DURATION (buf)), + GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), dtmfsrc->rtp_timestamp, + dtmfsrc->payload->duration); /* duration of DTMF payloadfor the NEXT packet */ /* not updated for redundant packets */ - if (dtmfsrc->redundancy_count == 0) - dtmfsrc->payload->duration += - dtmfsrc->interval * dtmfsrc->clock_rate / 1000; + if (dtmfsrc->redundancy_count <= 1) + dtmfsrc->payload->duration += dtmfsrc->ptime * dtmfsrc->clock_rate / 1000; + + if (GST_CLOCK_TIME_IS_VALID (dtmfsrc->timestamp)) + dtmfsrc->timestamp += GST_BUFFER_DURATION (buf); } @@ -660,7 +675,8 @@ gst_rtp_dtmf_src_create (GstBaseSrc * basesrc, guint64 offset, dtmfsrc->last_packet = FALSE; /* Set the redundancy on the first packet */ dtmfsrc->redundancy_count = dtmfsrc->packet_redundancy; - gst_rtp_dtmf_prepare_timestamps (dtmfsrc); + if (!gst_rtp_dtmf_prepare_timestamps (dtmfsrc)) + goto no_clock; dtmfsrc->payload = event->payload; event->payload = NULL; @@ -727,6 +743,8 @@ gst_rtp_dtmf_src_create (GstBaseSrc * basesrc, guint64 offset, GST_DEBUG_OBJECT (dtmfsrc, "Processed events, now lets wait on the clock"); clock = gst_element_get_clock (GST_ELEMENT (basesrc)); + if (!clock) + goto no_clock; clockid = gst_clock_new_single_shot_id (clock, dtmfsrc->timestamp + gst_element_get_base_time (GST_ELEMENT (dtmfsrc))); gst_object_unref (clock); @@ -793,6 +811,12 @@ paused: } else { return GST_FLOW_WRONG_STATE; } + +no_clock: + GST_ELEMENT_ERROR (dtmfsrc, STREAM, MUX, ("No available clock"), + ("No available clock")); + gst_pad_pause_task (GST_BASE_SRC_PAD (dtmfsrc)); + return GST_FLOW_ERROR; } @@ -913,6 +937,23 @@ gst_rtp_dtmf_src_negotiate (GstBaseSrc * basesrc) GST_LOG_OBJECT (dtmfsrc, "using internal seqnum-base %u", dtmfsrc->seqnum_base); } + + if (gst_structure_has_field_typed (s, "ptime", G_TYPE_UINT)) { + value = gst_structure_get_value (s, "ptime"); + dtmfsrc->ptime = g_value_get_uint (value); + GST_LOG_OBJECT (dtmfsrc, "using peer ptime %u", dtmfsrc->ptime); + } else if (gst_structure_has_field_typed (s, "maxptime", G_TYPE_UINT)) { + value = gst_structure_get_value (s, "maxptime"); + dtmfsrc->ptime = g_value_get_uint (value); + GST_LOG_OBJECT (dtmfsrc, "using peer maxptime as ptime %u", + dtmfsrc->ptime); + } else { + /* FIXME, fixate_nearest_uint would be even better */ + gst_structure_set (s, "ptime", G_TYPE_UINT, dtmfsrc->ptime, NULL); + GST_LOG_OBJECT (dtmfsrc, "using internal ptime %u", dtmfsrc->ptime); + } + + GST_DEBUG_OBJECT (dtmfsrc, "with peer caps: %" GST_PTR_FORMAT, srccaps); } @@ -945,6 +986,7 @@ gst_rtp_dtmf_src_ready_to_paused (GstRTPDTMFSrc * dtmfsrc) else dtmfsrc->ts_base = dtmfsrc->ts_offset; + dtmfsrc->timestamp = 0; } static GstStateChangeReturn diff --git a/gst/dtmf/gstrtpdtmfsrc.h b/gst/dtmf/gstrtpdtmfsrc.h index d04c6ecb7c..9be9df69cb 100644 --- a/gst/dtmf/gstrtpdtmfsrc.h +++ b/gst/dtmf/gstrtpdtmfsrc.h @@ -90,10 +90,12 @@ struct _GstRTPDTMFSrc guint pt; guint ssrc; guint current_ssrc; - guint16 interval; + guint16 ptime; guint16 packet_redundancy; guint32 clock_rate; + GstClockTime last_stop; + gboolean dirty; guint16 redundancy_count; }; diff --git a/gst/hls/gsthlsdemux.c b/gst/hls/gsthlsdemux.c index 1e984b9dc9..143f36387d 100644 --- a/gst/hls/gsthlsdemux.c +++ b/gst/hls/gsthlsdemux.c @@ -100,16 +100,15 @@ static gboolean gst_hls_demux_fetcher_sink_event (GstPad * pad, GstEvent * event); static void gst_hls_demux_loop (GstHLSDemux * demux); static void gst_hls_demux_stop (GstHLSDemux * demux); -static void gst_hls_demux_stop_fetcher (GstHLSDemux * demux, +static void gst_hls_demux_stop_fetcher_locked (GstHLSDemux * demux, gboolean cancelled); +static void gst_hls_demux_stop_update (GstHLSDemux * demux); static gboolean gst_hls_demux_start_update (GstHLSDemux * demux); static gboolean gst_hls_demux_cache_fragments (GstHLSDemux * demux); static gboolean gst_hls_demux_schedule (GstHLSDemux * demux); static gboolean gst_hls_demux_switch_playlist (GstHLSDemux * demux); -static gboolean gst_hls_demux_get_next_fragment (GstHLSDemux * demux, - gboolean retry); -static gboolean gst_hls_demux_update_playlist (GstHLSDemux * demux, - gboolean retry); +static gboolean gst_hls_demux_get_next_fragment (GstHLSDemux * demux); +static gboolean gst_hls_demux_update_playlist (GstHLSDemux * demux); static void gst_hls_demux_reset (GstHLSDemux * demux, gboolean dispose); static gboolean gst_hls_demux_set_location (GstHLSDemux * demux, const gchar * uri); @@ -155,10 +154,7 @@ gst_hls_demux_dispose (GObject * obj) g_cond_free (demux->thread_cond); g_mutex_free (demux->thread_lock); - if (GST_TASK_STATE (demux->task) != GST_TASK_STOPPED) { - gst_task_stop (demux->task); - gst_task_join (demux->task); - } + gst_task_join (demux->task); gst_object_unref (demux->task); g_static_rec_mutex_free (&demux->task_lock); @@ -290,9 +286,17 @@ gst_hls_demux_change_state (GstElement * element, GstStateChange transition) GstHLSDemux *demux = GST_HLS_DEMUX (element); switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: + case GST_STATE_CHANGE_READY_TO_PAUSED: gst_hls_demux_reset (demux, FALSE); break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + /* Start the streaming loop in paused only if we already received + the main playlist. It might have been stopped if we were in PAUSED + state and we filled our queue with enough cached fragments + */ + if (gst_m3u8_client_get_uri (demux->client)[0] != '\0') + gst_hls_demux_start_update (demux); + break; default: break; } @@ -300,9 +304,13 @@ gst_hls_demux_change_state (GstElement * element, GstStateChange transition) ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + gst_hls_demux_stop_update (demux); + break; case GST_STATE_CHANGE_PAUSED_TO_READY: demux->cancelled = TRUE; - g_cond_signal (demux->fetcher_cond); + gst_hls_demux_stop (demux); + gst_hls_demux_reset (demux, FALSE); break; default: break; @@ -348,6 +356,7 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) " stop: %" GST_TIME_FORMAT, rate, GST_TIME_ARGS (start), GST_TIME_ARGS (stop)); + GST_M3U8_CLIENT_LOCK (demux->client); file = GST_M3U8_MEDIA_FILE (demux->client->current->files->data); current_sequence = file->sequence; current_pos = 0; @@ -363,6 +372,7 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) } current_pos += file->duration; } + GST_M3U8_CLIENT_UNLOCK (demux->client); if (walk == NULL) { GST_WARNING_OBJECT (demux, "Could not find seeked fragment"); @@ -374,9 +384,13 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) gst_pad_push_event (demux->srcpad, gst_event_new_flush_start ()); } - gst_hls_demux_stop_fetcher (demux, TRUE); + demux->cancelled = TRUE; + gst_task_pause (demux->task); + g_mutex_lock (demux->fetcher_lock); + gst_hls_demux_stop_fetcher_locked (demux, TRUE); + g_mutex_unlock (demux->fetcher_lock); + gst_hls_demux_stop_update (demux); gst_task_pause (demux->task); - g_cond_signal (demux->thread_cond); /* wait for streaming to finish */ g_static_rec_mutex_lock (&demux->task_lock); @@ -386,17 +400,23 @@ gst_hls_demux_src_event (GstPad * pad, GstEvent * event) GstBuffer *buf = g_queue_pop_head (demux->queue); gst_buffer_unref (buf); } + g_queue_clear (demux->queue); + gst_adapter_clear (demux->download); + GST_M3U8_CLIENT_LOCK (demux->client); GST_DEBUG_OBJECT (demux, "seeking to sequence %d", current_sequence); demux->client->sequence = current_sequence; demux->position = start; demux->need_segment = TRUE; + GST_M3U8_CLIENT_UNLOCK (demux->client); + if (flags & GST_SEEK_FLAG_FLUSH) { GST_DEBUG_OBJECT (demux, "sending flush stop"); gst_pad_push_event (demux->srcpad, gst_event_new_flush_stop ()); } + demux->cancelled = FALSE; gst_task_start (demux->task); g_static_rec_mutex_unlock (&demux->task_lock); @@ -507,7 +527,7 @@ gst_hls_demux_src_query (GstPad * pad, GstQuery * query) if (hlsdemux->client) { /* FIXME: Do we answer with the variant playlist, with the current * playlist or the the uri of the least downlowaded fragment? */ - gst_query_set_uri (query, hlsdemux->client->current->uri); + gst_query_set_uri (query, gst_m3u8_client_get_uri (hlsdemux->client)); ret = TRUE; } break; @@ -552,8 +572,11 @@ gst_hls_demux_fetcher_sink_event (GstPad * pad, GstEvent * event) case GST_EVENT_EOS:{ GST_DEBUG_OBJECT (demux, "Got EOS on the fetcher pad"); /* signal we have fetched the URI */ - if (!demux->cancelled) - g_cond_signal (demux->fetcher_cond); + if (!demux->cancelled) { + g_mutex_lock (demux->fetcher_lock); + g_cond_broadcast (demux->fetcher_cond); + g_mutex_unlock (demux->fetcher_lock); + } } default: break; @@ -593,8 +616,6 @@ gst_hls_demux_fetcher_chain (GstPad * pad, GstBuffer * buf) goto done; } - GST_LOG_OBJECT (demux, "The uri fetcher received a new buffer of size %u", - GST_BUFFER_SIZE (buf)); gst_adapter_push (demux->download, buf); done: @@ -604,7 +625,7 @@ done: } static void -gst_hls_demux_stop_fetcher (GstHLSDemux * demux, gboolean cancelled) +gst_hls_demux_stop_fetcher_locked (GstHLSDemux * demux, gboolean cancelled) { GstPad *pad; @@ -630,20 +651,22 @@ gst_hls_demux_stop_fetcher (GstHLSDemux * demux, gboolean cancelled) demux->fetcher = NULL; /* if we stopped it to cancell a download, free the cached buffer */ - if (cancelled && !gst_adapter_available (demux->download)) { + if (cancelled && gst_adapter_available (demux->download)) { gst_adapter_clear (demux->download); - /* signal the fetcher thread that the download has finished/cancelled */ - g_cond_signal (demux->fetcher_cond); } + /* signal the fetcher thread that the download has finished/cancelled */ + if (cancelled) + g_cond_broadcast (demux->fetcher_cond); } static void gst_hls_demux_stop (GstHLSDemux * demux) { - gst_hls_demux_stop_fetcher (demux, TRUE); - if (GST_TASK_STATE (demux->task) != GST_TASK_STOPPED) - gst_task_stop (demux->task); - g_cond_signal (demux->thread_cond); + g_mutex_lock (demux->fetcher_lock); + gst_hls_demux_stop_fetcher_locked (demux, TRUE); + g_mutex_unlock (demux->fetcher_lock); + gst_task_join (demux->task); + gst_hls_demux_stop_update (demux); } static void @@ -653,6 +676,18 @@ switch_pads (GstHLSDemux * demux, GstCaps * newcaps) GST_DEBUG ("Switching pads (oldpad:%p)", oldpad); + /* FIXME: This is a workaround for a bug in playsink. + * If we're switching from an audio-only or video-only fragment + * to an audio-video segment, the new sink doesn't know about + * the current running time and audio/video will go out of sync. + * + * This should be fixed in playsink by distributing the + * current running time to newly created sinks and is + * fixed in 0.11 with the new segments. + */ + if (demux->srcpad) + gst_pad_push_event (demux->srcpad, gst_event_new_flush_stop ()); + /* First create and activate new pad */ demux->srcpad = gst_pad_new_from_static_template (&srctemplate, NULL); gst_pad_set_event_function (demux->srcpad, @@ -690,8 +725,9 @@ gst_hls_demux_loop (GstHLSDemux * demux) if (!gst_hls_demux_cache_fragments (demux)) goto cache_error; - /* we can start now the updates thread */ - gst_hls_demux_start_update (demux); + /* we can start now the updates thread (only if on playing) */ + if (GST_STATE (demux) == GST_STATE_PLAYING) + gst_hls_demux_start_update (demux); GST_INFO_OBJECT (demux, "First fragments cached successfully"); } @@ -699,7 +735,7 @@ gst_hls_demux_loop (GstHLSDemux * demux) if (demux->end_of_playlist) goto end_of_playlist; - goto empty_queue; + goto pause_task; } buf = g_queue_pop_head (demux->queue); @@ -740,9 +776,12 @@ end_of_playlist: cache_error: { - GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, - ("Could not cache the first fragments"), (NULL)); - gst_hls_demux_stop (demux); + gst_task_pause (demux->task); + if (!demux->cancelled) { + GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, + ("Could not cache the first fragments"), (NULL)); + gst_hls_demux_stop (demux); + } return; } @@ -754,7 +793,7 @@ error: return; } -empty_queue: +pause_task: { gst_task_pause (demux->task); return; @@ -769,7 +808,11 @@ gst_hls_demux_fetcher_bus_handler (GstBus * bus, if (GST_MESSAGE_TYPE (message) == GST_MESSAGE_ERROR) { demux->fetcher_error = TRUE; - g_cond_signal (demux->fetcher_cond); + if (!demux->cancelled) { + g_mutex_lock (demux->fetcher_lock); + g_cond_broadcast (demux->fetcher_cond); + g_mutex_unlock (demux->fetcher_lock); + } } gst_message_unref (message); @@ -777,7 +820,7 @@ gst_hls_demux_fetcher_bus_handler (GstBus * bus, } static gboolean -gst_hls_demux_make_fetcher (GstHLSDemux * demux, const gchar * uri) +gst_hls_demux_make_fetcher_locked (GstHLSDemux * demux, const gchar * uri) { GstPad *pad; @@ -824,8 +867,10 @@ gst_hls_demux_reset (GstHLSDemux * demux, gboolean dispose) gst_adapter_clear (demux->download); - if (demux->client) + if (demux->client) { gst_m3u8_client_free (demux->client); + demux->client = NULL; + } if (!dispose) { demux->client = gst_m3u8_client_new (""); @@ -861,6 +906,7 @@ gst_hls_demux_update_thread (GstHLSDemux * demux) * switch to a different bitrate */ g_mutex_lock (demux->thread_lock); + GST_DEBUG_OBJECT (demux, "Started updates thread"); while (TRUE) { /* block until the next scheduled update or the signal to quit this thread */ if (g_cond_timed_wait (demux->thread_cond, demux->thread_lock, @@ -870,9 +916,17 @@ gst_hls_demux_update_thread (GstHLSDemux * demux) /* update the playlist for live sources */ if (gst_m3u8_client_is_live (demux->client)) { - if (!gst_hls_demux_update_playlist (demux, TRUE)) { - GST_ERROR_OBJECT (demux, "Could not update the playlist"); - goto quit; + if (!gst_hls_demux_update_playlist (demux)) { + demux->client->update_failed_count++; + if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) { + GST_WARNING_OBJECT (demux, "Could not update the playlist"); + gst_hls_demux_schedule (demux); + continue; + } else { + GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, + ("Could not update the playlist"), (NULL)); + goto quit; + } } } @@ -891,31 +945,62 @@ gst_hls_demux_update_thread (GstHLSDemux * demux) } /* fetch the next fragment */ - if (!gst_hls_demux_get_next_fragment (demux, TRUE)) { - if (!demux->end_of_playlist && !demux->cancelled) - GST_ERROR_OBJECT (demux, "Could not fetch the next fragment"); - goto quit; - } + if (g_queue_is_empty (demux->queue)) { + if (!gst_hls_demux_get_next_fragment (demux)) { + if (!demux->end_of_playlist && !demux->cancelled) { + demux->client->update_failed_count++; + if (demux->client->update_failed_count < DEFAULT_FAILED_COUNT) { + GST_WARNING_OBJECT (demux, "Could not fetch the next fragment"); + continue; + } else { + GST_ELEMENT_ERROR (demux, RESOURCE, NOT_FOUND, + ("Could not fetch the next fragment"), (NULL)); + goto quit; + } + } + } else { + demux->client->update_failed_count = 0; + } - /* try to switch to another bitrate if needed */ - gst_hls_demux_switch_playlist (demux); + /* try to switch to another bitrate if needed */ + gst_hls_demux_switch_playlist (demux); + } } quit: { + GST_DEBUG_OBJECT (demux, "Stopped updates thread"); + demux->updates_thread = NULL; g_mutex_unlock (demux->thread_lock); return TRUE; } } + +static void +gst_hls_demux_stop_update (GstHLSDemux * demux) +{ + GST_DEBUG_OBJECT (demux, "Stopping updates thread"); + while (demux->updates_thread) { + g_mutex_lock (demux->thread_lock); + g_cond_signal (demux->thread_cond); + g_mutex_unlock (demux->thread_lock); + } +} + static gboolean gst_hls_demux_start_update (GstHLSDemux * demux) { GError *error; /* creates a new thread for the updates */ - demux->updates_thread = g_thread_create ( - (GThreadFunc) gst_hls_demux_update_thread, demux, TRUE, &error); + g_mutex_lock (demux->thread_lock); + if (demux->updates_thread == NULL) { + GST_DEBUG_OBJECT (demux, "Starting updates thread"); + demux->updates_thread = g_thread_create ( + (GThreadFunc) gst_hls_demux_update_thread, demux, FALSE, &error); + } + g_mutex_unlock (demux->thread_lock); return (error != NULL); } @@ -924,23 +1009,16 @@ gst_hls_demux_cache_fragments (GstHLSDemux * demux) { gint i; - /* Start parsing the main playlist */ - gst_m3u8_client_set_current (demux->client, demux->client->main); - - if (gst_m3u8_client_is_live (demux->client)) { - if (!gst_hls_demux_update_playlist (demux, FALSE)) { - GST_ERROR_OBJECT (demux, "Could not fetch the main playlist %s", - demux->client->main->uri); - return FALSE; - } - } - /* If this playlist is a variant playlist, select the first one * and update it */ if (gst_m3u8_client_has_variant_playlist (demux->client)) { - GstM3U8 *child = demux->client->main->current_variant->data; + GstM3U8 *child = NULL; + + GST_M3U8_CLIENT_LOCK (demux->client); + child = demux->client->main->current_variant->data; + GST_M3U8_CLIENT_UNLOCK (demux->client); gst_m3u8_client_set_current (demux->client, child); - if (!gst_hls_demux_update_playlist (demux, FALSE)) { + if (!gst_hls_demux_update_playlist (demux)) { GST_ERROR_OBJECT (demux, "Could not fetch the child playlist %s", child->uri); return FALSE; @@ -950,11 +1028,22 @@ gst_hls_demux_cache_fragments (GstHLSDemux * demux) /* If it's a live source, set the sequence number to the end of the list * and substract the 'fragmets_cache' to start from the last fragment*/ if (gst_m3u8_client_is_live (demux->client)) { + GST_M3U8_CLIENT_LOCK (demux->client); demux->client->sequence += g_list_length (demux->client->current->files); if (demux->client->sequence >= demux->fragments_cache) demux->client->sequence -= demux->fragments_cache; else demux->client->sequence = 0; + GST_M3U8_CLIENT_UNLOCK (demux->client); + } else { + GstClockTime duration = gst_m3u8_client_get_duration (demux->client); + + GST_DEBUG_OBJECT (demux, "Sending duration message : %" GST_TIME_FORMAT, + GST_TIME_ARGS (duration)); + if (duration != GST_CLOCK_TIME_NONE) + gst_element_post_message (GST_ELEMENT (demux), + gst_message_new_duration (GST_OBJECT (demux), + GST_FORMAT_TIME, duration)); } /* Cache the first fragments */ @@ -964,8 +1053,9 @@ gst_hls_demux_cache_fragments (GstHLSDemux * demux) 100 * i / demux->fragments_cache)); g_get_current_time (&demux->next_update); g_time_val_add (&demux->next_update, - demux->client->current->targetduration * 1000000); - if (!gst_hls_demux_get_next_fragment (demux, FALSE)) { + gst_m3u8_client_get_target_duration (demux->client) + / GST_SECOND * G_USEC_PER_SEC); + if (!gst_hls_demux_get_next_fragment (demux)) { if (!demux->cancelled) GST_ERROR_OBJECT (demux, "Error caching the first fragments"); return FALSE; @@ -992,7 +1082,13 @@ gst_hls_demux_fetch_location (GstHLSDemux * demux, const gchar * uri) g_mutex_lock (demux->fetcher_lock); - if (!gst_hls_demux_make_fetcher (demux, uri)) { + while (demux->fetcher) + g_cond_wait (demux->fetcher_cond, demux->fetcher_lock); + + if (demux->cancelled) + goto quit; + + if (!gst_hls_demux_make_fetcher_locked (demux, uri)) { goto uri_error; } @@ -1004,9 +1100,9 @@ gst_hls_demux_fetch_location (GstHLSDemux * demux, const gchar * uri) GST_DEBUG_OBJECT (demux, "Waiting to fetch the URI"); g_cond_wait (demux->fetcher_cond, demux->fetcher_lock); - gst_hls_demux_stop_fetcher (demux, FALSE); + gst_hls_demux_stop_fetcher_locked (demux, FALSE); - if (gst_adapter_available (demux->download)) { + if (!demux->fetcher_error && gst_adapter_available (demux->download)) { GST_INFO_OBJECT (demux, "URI fetched successfully"); bret = TRUE; } @@ -1031,6 +1127,8 @@ state_change_error: quit: { + /* Unlock any other fetcher that might be waiting */ + g_cond_broadcast (demux->fetcher_cond); g_mutex_unlock (demux->fetcher_lock); return bret; } @@ -1051,15 +1149,15 @@ gst_hls_src_buf_to_utf8_playlist (gchar * data, guint size) } static gboolean -gst_hls_demux_update_playlist (GstHLSDemux * demux, gboolean retry) +gst_hls_demux_update_playlist (GstHLSDemux * demux) { const guint8 *data; gchar *playlist; guint avail; + const gchar *uri = gst_m3u8_client_get_current_uri (demux->client); - GST_INFO_OBJECT (demux, "Updating the playlist %s", - demux->client->current->uri); - if (!gst_hls_demux_fetch_location (demux, demux->client->current->uri)) + GST_INFO_OBJECT (demux, "Updating the playlist %s", uri); + if (!gst_hls_demux_fetch_location (demux, uri)) return FALSE; avail = gst_adapter_available (demux->download); @@ -1079,26 +1177,36 @@ gst_hls_demux_change_playlist (GstHLSDemux * demux, gboolean is_fast) { GList *list; GstStructure *s; + gint new_bandwidth; + GST_M3U8_CLIENT_LOCK (demux->client); if (is_fast) list = g_list_next (demux->client->main->current_variant); else list = g_list_previous (demux->client->main->current_variant); /* Don't do anything else if the playlist is the same */ - if (!list || list->data == demux->client->current) + if (!list || list->data == demux->client->current) { + GST_M3U8_CLIENT_UNLOCK (demux->client); return TRUE; + } demux->client->main->current_variant = list; + GST_M3U8_CLIENT_UNLOCK (demux->client); gst_m3u8_client_set_current (demux->client, list->data); - gst_hls_demux_update_playlist (demux, TRUE); + + GST_M3U8_CLIENT_LOCK (demux->client); + new_bandwidth = demux->client->current->bandwidth; + GST_M3U8_CLIENT_UNLOCK (demux->client); + + gst_hls_demux_update_playlist (demux); GST_INFO_OBJECT (demux, "Client is %s, switching to bitrate %d", - is_fast ? "fast" : "slow", demux->client->current->bandwidth); + is_fast ? "fast" : "slow", new_bandwidth); s = gst_structure_new ("playlist", - "uri", G_TYPE_STRING, demux->client->current->uri, - "bitrate", G_TYPE_INT, demux->client->current->bandwidth, NULL); + "uri", G_TYPE_STRING, gst_m3u8_client_get_current_uri (demux->client), + "bitrate", G_TYPE_INT, new_bandwidth, NULL); gst_element_post_message (GST_ELEMENT_CAST (demux), gst_message_new_element (GST_OBJECT_CAST (demux), s)); @@ -1129,7 +1237,8 @@ gst_hls_demux_schedule (GstHLSDemux * demux) /* schedule the next update using the target duration field of the * playlist */ g_time_val_add (&demux->next_update, - demux->client->current->targetduration * update_factor * 1000000); + gst_m3u8_client_get_target_duration (demux->client) + / GST_SECOND * G_USEC_PER_SEC * update_factor); GST_DEBUG_OBJECT (demux, "Next update scheduled at %s", g_time_val_to_iso8601 (&demux->next_update)); @@ -1143,28 +1252,38 @@ gst_hls_demux_switch_playlist (GstHLSDemux * demux) gint64 diff, limit; g_get_current_time (&now); - if (!demux->client->main->lists) + GST_M3U8_CLIENT_LOCK (demux->client); + if (!demux->client->main->lists) { + GST_M3U8_CLIENT_UNLOCK (demux->client); return TRUE; + } + GST_M3U8_CLIENT_UNLOCK (demux->client); /* compare the time when the fragment was downloaded with the time when it was * scheduled */ diff = (GST_TIMEVAL_TO_TIME (demux->next_update) - GST_TIMEVAL_TO_TIME (now)); - limit = demux->client->current->targetduration * GST_SECOND * - demux->bitrate_switch_tol; + limit = gst_m3u8_client_get_target_duration (demux->client) + * demux->bitrate_switch_tol; GST_DEBUG ("diff:%s%" GST_TIME_FORMAT ", limit:%" GST_TIME_FORMAT, diff < 0 ? "-" : " ", GST_TIME_ARGS (ABS (diff)), GST_TIME_ARGS (limit)); /* if we are on time switch to a higher bitrate */ if (diff > limit) { - gst_hls_demux_change_playlist (demux, TRUE); + while (diff > limit) { + gst_hls_demux_change_playlist (demux, TRUE); + diff -= limit; + } + demux->accumulated_delay = 0; } else if (diff < 0) { /* if the client is too slow wait until it has accumulated a certain delay to * switch to a lower bitrate */ demux->accumulated_delay -= diff; if (demux->accumulated_delay >= limit) { - gst_hls_demux_change_playlist (demux, FALSE); - } else if (demux->accumulated_delay < 0) { + while (demux->accumulated_delay >= limit) { + gst_hls_demux_change_playlist (demux, FALSE); + demux->accumulated_delay -= limit; + } demux->accumulated_delay = 0; } } @@ -1172,7 +1291,7 @@ gst_hls_demux_switch_playlist (GstHLSDemux * demux) } static gboolean -gst_hls_demux_get_next_fragment (GstHLSDemux * demux, gboolean retry) +gst_hls_demux_get_next_fragment (GstHLSDemux * demux) { GstBuffer *buf; guint avail; @@ -1191,8 +1310,15 @@ gst_hls_demux_get_next_fragment (GstHLSDemux * demux, gboolean retry) GST_INFO_OBJECT (demux, "Fetching next fragment %s", next_fragment_uri); - if (!gst_hls_demux_fetch_location (demux, next_fragment_uri)) + if (!gst_hls_demux_fetch_location (demux, next_fragment_uri)) { + /* FIXME: The gst_m3u8_get_next_fragment increments the sequence number + but another thread might call get_next_fragment and this decrement + will not redownload the failed fragment, but might duplicate the + download of a succeeded fragment + */ + g_atomic_int_add (&demux->client->sequence, -1); return FALSE; + } avail = gst_adapter_available (demux->download); buf = gst_adapter_take_buffer (demux->download, avail); diff --git a/gst/hls/m3u8.c b/gst/hls/m3u8.c index 26548101f3..c407d72d83 100644 --- a/gst/hls/m3u8.c +++ b/gst/hls/m3u8.c @@ -225,11 +225,13 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated) title = NULL; data += 7; while (TRUE) { - end = g_utf8_strchr (data, -1, '\n'); /* FIXME: support \r\n */ + end = g_utf8_strchr (data, -1, '\n'); if (end) *end = '\0'; if (data[0] != '#') { + gchar *r; + if (duration < 0 && list == NULL) { GST_LOG ("%s: got line without EXTINF or EXTSTREAMINF, dropping", data); goto next_line; @@ -250,8 +252,13 @@ gst_m3u8_update (GstM3U8 * self, gchar * data, gboolean * updated) *slash = '\0'; data = g_strdup_printf ("%s/%s", self->uri, data); *slash = '/'; - } else + } else { data = g_strdup (data); + } + + r = g_utf8_strchr (data, -1, '\r'); + if (r) + *r = '\0'; if (list != NULL) { if (g_list_find_custom (self->lists, data, @@ -382,6 +389,7 @@ gst_m3u8_client_new (const gchar * uri) client->current = NULL; client->sequence = -1; client->update_failed_count = 0; + client->lock = g_mutex_new (); gst_m3u8_set_uri (client->main, g_strdup (uri)); return client; @@ -393,6 +401,7 @@ gst_m3u8_client_free (GstM3U8Client * self) g_return_if_fail (self != NULL); gst_m3u8_free (self->main); + g_mutex_free (self->lock); g_free (self); } @@ -401,10 +410,12 @@ gst_m3u8_client_set_current (GstM3U8Client * self, GstM3U8 * m3u8) { g_return_if_fail (self != NULL); + GST_M3U8_CLIENT_LOCK (self); if (m3u8 != self->current) { self->current = m3u8; self->update_failed_count = 0; } + GST_M3U8_CLIENT_UNLOCK (self); } gboolean @@ -412,17 +423,19 @@ gst_m3u8_client_update (GstM3U8Client * self, gchar * data) { GstM3U8 *m3u8; gboolean updated = FALSE; + gboolean ret = FALSE; g_return_val_if_fail (self != NULL, FALSE); + GST_M3U8_CLIENT_LOCK (self); m3u8 = self->current ? self->current : self->main; if (!gst_m3u8_update (m3u8, data, &updated)) - return FALSE; + goto out; if (!updated) { self->update_failed_count++; - return FALSE; + goto out; } /* select the first playlist, for now */ @@ -440,7 +453,10 @@ gst_m3u8_client_update (GstM3U8Client * self, gchar * data) GST_DEBUG ("Setting first sequence at %d", self->sequence); } - return TRUE; + ret = TRUE; +out: + GST_M3U8_CLIENT_UNLOCK (self); + return ret; } static gboolean @@ -465,11 +481,14 @@ gst_m3u8_client_get_next_fragment (GstM3U8Client * client, g_return_val_if_fail (client->current != NULL, FALSE); g_return_val_if_fail (discontinuity != NULL, FALSE); + GST_M3U8_CLIENT_LOCK (client); GST_DEBUG ("Looking for fragment %d", client->sequence); l = g_list_find_custom (client->current->files, client, (GCompareFunc) _find_next); - if (l == NULL) + if (l == NULL) { + GST_M3U8_CLIENT_UNLOCK (client); return FALSE; + } file = GST_M3U8_MEDIA_FILE (l->data); @@ -487,6 +506,7 @@ gst_m3u8_client_get_next_fragment (GstM3U8Client * client, } *timestamp *= GST_SECOND; + GST_M3U8_CLIENT_UNLOCK (client); return TRUE; } @@ -503,37 +523,82 @@ gst_m3u8_client_get_duration (GstM3U8Client * client) g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE); + GST_M3U8_CLIENT_LOCK (client); /* We can only get the duration for on-demand streams */ - if (!client->current->endlist) + if (!client->current->endlist) { + GST_M3U8_CLIENT_UNLOCK (client); return GST_CLOCK_TIME_NONE; + } g_list_foreach (client->current->files, (GFunc) _sum_duration, &duration); + GST_M3U8_CLIENT_UNLOCK (client); + return duration * GST_SECOND; +} + +GstClockTime +gst_m3u8_client_get_target_duration (GstM3U8Client * client) +{ + GstClockTime duration = 0; + + g_return_val_if_fail (client != NULL, GST_CLOCK_TIME_NONE); + + GST_M3U8_CLIENT_LOCK (client); + duration = client->current->targetduration; + GST_M3U8_CLIENT_UNLOCK (client); return duration * GST_SECOND; } const gchar * gst_m3u8_client_get_uri (GstM3U8Client * client) { + const gchar *uri; + g_return_val_if_fail (client != NULL, NULL); - return client->main->uri; + GST_M3U8_CLIENT_LOCK (client); + uri = client->main->uri; + GST_M3U8_CLIENT_UNLOCK (client); + return uri; +} + +const gchar * +gst_m3u8_client_get_current_uri (GstM3U8Client * client) +{ + const gchar *uri; + + g_return_val_if_fail (client != NULL, NULL); + + GST_M3U8_CLIENT_LOCK (client); + uri = client->current->uri; + GST_M3U8_CLIENT_UNLOCK (client); + return uri; } gboolean gst_m3u8_client_has_variant_playlist (GstM3U8Client * client) { + gboolean ret; + g_return_val_if_fail (client != NULL, FALSE); - return client->main->lists != NULL; + GST_M3U8_CLIENT_LOCK (client); + ret = (client->main->lists != NULL); + GST_M3U8_CLIENT_UNLOCK (client); + return ret; } gboolean gst_m3u8_client_is_live (GstM3U8Client * client) { + gboolean ret; + g_return_val_if_fail (client != NULL, FALSE); + GST_M3U8_CLIENT_LOCK (client); if (!client->current || client->current->endlist) - return FALSE; - - return TRUE; + ret = FALSE; + else + ret = TRUE; + GST_M3U8_CLIENT_UNLOCK (client); + return ret; } diff --git a/gst/hls/m3u8.h b/gst/hls/m3u8.h index aefe8667d1..8c83990f87 100644 --- a/gst/hls/m3u8.h +++ b/gst/hls/m3u8.h @@ -32,6 +32,9 @@ typedef struct _GstM3U8Client GstM3U8Client; #define GST_M3U8(m) ((GstM3U8*)m) #define GST_M3U8_MEDIA_FILE(f) ((GstM3U8MediaFile*)f) +#define GST_M3U8_CLIENT_LOCK(c) g_mutex_lock (c->lock); +#define GST_M3U8_CLIENT_UNLOCK(c) g_mutex_unlock (c->lock); + struct _GstM3U8 { gchar *uri; @@ -70,6 +73,7 @@ struct _GstM3U8Client GstM3U8 *current; guint update_failed_count; gint sequence; /* the next sequence for this client */ + GMutex *lock; }; @@ -81,7 +85,9 @@ gboolean gst_m3u8_client_get_next_fragment (GstM3U8Client * client, gboolean * discontinuity, const gchar ** uri, GstClockTime * duration, GstClockTime * timestamp); GstClockTime gst_m3u8_client_get_duration (GstM3U8Client * client); +GstClockTime gst_m3u8_client_get_target_duration (GstM3U8Client * client); const gchar *gst_m3u8_client_get_uri(GstM3U8Client * client); +const gchar *gst_m3u8_client_get_current_uri(GstM3U8Client * client); gboolean gst_m3u8_client_has_variant_playlist(GstM3U8Client * client); gboolean gst_m3u8_client_is_live(GstM3U8Client * client); diff --git a/gst/mpegtsdemux/mpegtsbase.c b/gst/mpegtsdemux/mpegtsbase.c index 1749ba2ccc..5542abf029 100644 --- a/gst/mpegtsdemux/mpegtsbase.c +++ b/gst/mpegtsdemux/mpegtsbase.c @@ -1154,6 +1154,18 @@ gst_mpegts_base_handle_eos (MpegTSBase * base) return TRUE; } +static inline void +mpegts_base_flush (MpegTSBase * base) +{ + MpegTSBaseClass *klass = GST_MPEGTS_BASE_GET_CLASS (base); + + /* Call implementation */ + if (G_UNLIKELY (klass->flush == NULL)) + GST_WARNING_OBJECT (base, "Class doesn't have a 'flush' implementation !"); + else + klass->flush (base); +} + static gboolean mpegts_base_sink_event (GstPad * pad, GstEvent * event) { @@ -1191,6 +1203,7 @@ mpegts_base_sink_event (GstPad * pad, GstEvent * event) break; case GST_EVENT_FLUSH_START: mpegts_packetizer_flush (base->packetizer); + mpegts_base_flush (base); res = GST_MPEGTS_BASE_GET_CLASS (base)->push_event (base, event); gst_event_unref (event); break; @@ -1442,7 +1455,7 @@ mpegts_base_handle_seek_event (MpegTSBase * base, GstPad * pad, if (base->mode == BASE_MODE_PUSHING) { GST_ERROR ("seeking in push mode not supported"); - goto done; + goto push_mode; } /* stop streaming, either by flushing or by pausing the task */ @@ -1493,7 +1506,7 @@ mpegts_base_handle_seek_event (MpegTSBase * base, GstPad * pad, //else done: gst_pad_start_task (base->sinkpad, (GstTaskFunction) mpegts_base_loop, base); - +push_mode: GST_PAD_STREAM_UNLOCK (base->sinkpad); return ret == GST_FLOW_OK; } diff --git a/gst/mpegtsdemux/mpegtsbase.h b/gst/mpegtsdemux/mpegtsbase.h index 1513898ec5..cce4e0fd72 100644 --- a/gst/mpegtsdemux/mpegtsbase.h +++ b/gst/mpegtsdemux/mpegtsbase.h @@ -162,6 +162,9 @@ struct _MpegTSBaseClass { /* seek is called to wait for seeking */ GstFlowReturn (*seek) (MpegTSBase * base, GstEvent * event, guint16 pid); + /* flush all streams */ + void (*flush) (MpegTSBase * base); + /* signals */ void (*pat_info) (GstStructure *pat); void (*pmt_info) (GstStructure *pmt); diff --git a/gst/mpegtsdemux/tsdemux.c b/gst/mpegtsdemux/tsdemux.c index f7dc5cc6b1..3a327a5cd9 100644 --- a/gst/mpegtsdemux/tsdemux.c +++ b/gst/mpegtsdemux/tsdemux.c @@ -188,6 +188,7 @@ static void gst_ts_demux_reset (MpegTSBase * base); static GstFlowReturn gst_ts_demux_push (MpegTSBase * base, MpegTSPacketizerPacket * packet, MpegTSPacketizerSection * section); +static void gst_ts_demux_flush (MpegTSBase * base); static void gst_ts_demux_stream_added (MpegTSBase * base, MpegTSBaseStream * stream, MpegTSBaseProgram * program); @@ -282,6 +283,7 @@ gst_ts_demux_class_init (GstTSDemuxClass * klass) ts_class->stream_removed = gst_ts_demux_stream_removed; ts_class->find_timestamps = GST_DEBUG_FUNCPTR (find_timestamps); ts_class->seek = GST_DEBUG_FUNCPTR (gst_ts_demux_do_seek); + ts_class->flush = GST_DEBUG_FUNCPTR (gst_ts_demux_flush); } static void @@ -882,9 +884,7 @@ gst_ts_demux_srcpad_event (GstPad * pad, GstEvent * event) switch (GST_EVENT_TYPE (event)) { case GST_EVENT_SEEK: res = mpegts_base_handle_seek_event ((MpegTSBase *) demux, pad, event); - if (res) - demux->need_newsegment = TRUE; - else + if (!res) GST_WARNING ("seeking failed"); gst_event_unref (event); break; @@ -2278,6 +2278,15 @@ gst_ts_demux_handle_packet (GstTSDemux * demux, TSDemuxStream * stream, return res; } +static void +gst_ts_demux_flush (MpegTSBase * base) +{ + GstTSDemux *demux = GST_TS_DEMUX_CAST (base); + + demux->need_newsegment = TRUE; + gst_ts_demux_flush_streams (demux); +} + static GstFlowReturn gst_ts_demux_push (MpegTSBase * base, MpegTSPacketizerPacket * packet, MpegTSPacketizerSection * section) diff --git a/gst/rtpmux/gstrtpdtmfmux.c b/gst/rtpmux/gstrtpdtmfmux.c index f62c6263db..97ffacd2f0 100644 --- a/gst/rtpmux/gstrtpdtmfmux.c +++ b/gst/rtpmux/gstrtpdtmfmux.c @@ -64,6 +64,8 @@ static GstStateChangeReturn gst_rtp_dtmf_mux_change_state (GstElement * element, static gboolean gst_rtp_dtmf_mux_accept_buffer_locked (GstRTPMux * rtp_mux, GstRTPMuxPadPrivate * padpriv, GstBuffer * buffer); +static gboolean gst_rtp_dtmf_mux_src_event (GstRTPMux * rtp_mux, + GstEvent * event); GST_BOILERPLATE (GstRTPDTMFMux, gst_rtp_dtmf_mux, GstRTPMux, GST_TYPE_RTP_MUX); @@ -100,6 +102,7 @@ gst_rtp_dtmf_mux_class_init (GstRTPDTMFMuxClass * klass) gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_rtp_dtmf_mux_change_state); gstrtpmux_class->accept_buffer_locked = gst_rtp_dtmf_mux_accept_buffer_locked; + gstrtpmux_class->src_event = gst_rtp_dtmf_mux_src_event; } static gboolean @@ -173,6 +176,28 @@ gst_rtp_dtmf_mux_request_new_pad (GstElement * element, GstPadTemplate * templ, return pad; } +static gboolean +gst_rtp_dtmf_mux_src_event (GstRTPMux * rtp_mux, GstEvent * event) +{ + if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_UPSTREAM) { + const GstStructure *s = gst_event_get_structure (event); + + if (s && gst_structure_has_name (s, "dtmf-event")) { + GST_OBJECT_LOCK (rtp_mux); + if (GST_CLOCK_TIME_IS_VALID (rtp_mux->last_stop)) { + event = (GstEvent *) + gst_mini_object_make_writable (GST_MINI_OBJECT_CAST (event)); + s = gst_event_get_structure (event); + gst_structure_set ((GstStructure *) s, + "last-stop", G_TYPE_UINT64, rtp_mux->last_stop, NULL); + } + GST_OBJECT_UNLOCK (rtp_mux); + } + } + + return GST_RTP_MUX_CLASS (parent_class)->src_event (rtp_mux, event); +} + static GstStateChangeReturn gst_rtp_dtmf_mux_change_state (GstElement * element, GstStateChange transition) diff --git a/gst/rtpmux/gstrtpmux.c b/gst/rtpmux/gstrtpmux.c index 57929ec608..f86fd5d6bc 100644 --- a/gst/rtpmux/gstrtpmux.c +++ b/gst/rtpmux/gstrtpmux.c @@ -107,6 +107,9 @@ static void gst_rtp_mux_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); static void gst_rtp_mux_dispose (GObject * object); +static gboolean gst_rtp_mux_src_event_real (GstRTPMux *rtp_mux, + GstEvent * event); + GST_BOILERPLATE (GstRTPMux, gst_rtp_mux, GstElement, GST_TYPE_ELEMENT); static void @@ -137,6 +140,8 @@ gst_rtp_mux_class_init (GstRTPMuxClass * klass) gobject_class->set_property = gst_rtp_mux_set_property; gobject_class->dispose = gst_rtp_mux_dispose; + klass->src_event = gst_rtp_mux_src_event_real; + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_TIMESTAMP_OFFSET, g_param_spec_int ("timestamp-offset", "Timestamp Offset", @@ -183,16 +188,30 @@ restart: static gboolean gst_rtp_mux_src_event (GstPad * pad, GstEvent * event) { - GstElement *rtp_mux; + GstRTPMux *rtp_mux; + GstRTPMuxClass *klass; + gboolean ret = FALSE; + + rtp_mux = (GstRTPMux *) gst_pad_get_parent_element (pad); + g_return_val_if_fail (rtp_mux != NULL, FALSE); + klass = GST_RTP_MUX_GET_CLASS (rtp_mux); + + ret = klass->src_event (rtp_mux, event); + + gst_object_unref (rtp_mux); + + return ret; +} + +static gboolean +gst_rtp_mux_src_event_real (GstRTPMux *rtp_mux, GstEvent * event) +{ GstIterator *iter; GstPad *sinkpad; gboolean result = FALSE; gboolean done = FALSE; - rtp_mux = gst_pad_get_parent_element (pad); - g_return_val_if_fail (rtp_mux != NULL, FALSE); - - iter = gst_element_iterate_sink_pads (rtp_mux); + iter = gst_element_iterate_sink_pads (GST_ELEMENT (rtp_mux)); while (!done) { switch (gst_iterator_next (iter, (gpointer) & sinkpad)) { @@ -213,7 +232,6 @@ gst_rtp_mux_src_event (GstPad * pad, GstEvent * event) } } gst_iterator_free (iter); - gst_object_unref (rtp_mux); gst_event_unref (event); return result; @@ -236,6 +254,7 @@ gst_rtp_mux_init (GstRTPMux * object, GstRTPMuxClass * g_class) object->seqnum_offset = DEFAULT_SEQNUM_OFFSET; object->segment_pending = TRUE; + object->last_stop = GST_CLOCK_TIME_NONE; } static void @@ -394,6 +413,20 @@ gst_rtp_mux_chain_list (GstPad * pad, GstBufferList * bufferlist) break; gst_buffer_list_iterator_take (it, rtpbuf); + + do { + if (GST_BUFFER_DURATION_IS_VALID (rtpbuf) && + GST_BUFFER_TIMESTAMP_IS_VALID (rtpbuf)) + rtp_mux->last_stop = GST_BUFFER_TIMESTAMP (rtpbuf) + + GST_BUFFER_DURATION (rtpbuf); + else + rtp_mux->last_stop = GST_CLOCK_TIME_NONE; + + gst_buffer_list_iterator_take (it, rtpbuf); + + } while ((rtpbuf = gst_buffer_list_iterator_next (it)) != NULL); + + } gst_buffer_list_iterator_free (it); @@ -456,15 +489,25 @@ gst_rtp_mux_chain (GstPad * pad, GstBuffer * buffer) drop = !process_buffer_locked (rtp_mux, padpriv, buffer); - if (!drop && rtp_mux->segment_pending) { - /* - * We set the start at 0, because we re-timestamps to the running time - */ - newseg_event = gst_event_new_new_segment_full (FALSE, 1.0, 1.0, - GST_FORMAT_TIME, 0, -1, 0); + if (!drop) { + if (rtp_mux->segment_pending) { + /* + * We set the start at 0, because we re-timestamps to the running time + */ + newseg_event = gst_event_new_new_segment_full (FALSE, 1.0, 1.0, + GST_FORMAT_TIME, 0, -1, 0); - rtp_mux->segment_pending = FALSE; + rtp_mux->segment_pending = FALSE; + } + + if (GST_BUFFER_DURATION_IS_VALID (buffer) && + GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) + rtp_mux->last_stop = GST_BUFFER_TIMESTAMP (buffer) + + GST_BUFFER_DURATION (buffer); + else + rtp_mux->last_stop = GST_CLOCK_TIME_NONE; } + GST_OBJECT_UNLOCK (rtp_mux); if (newseg_event) @@ -709,6 +752,7 @@ gst_rtp_mux_sink_event (GstPad * pad, GstEvent * event) GstRTPMuxPadPrivate *padpriv; GST_OBJECT_LOCK (mux); + mux->last_stop = GST_CLOCK_TIME_NONE; mux->segment_pending = TRUE; padpriv = gst_pad_get_element_private (pad); if (padpriv) @@ -801,6 +845,8 @@ gst_rtp_mux_ready_to_paused (GstRTPMux * rtp_mux) else rtp_mux->ts_base = rtp_mux->ts_offset; + rtp_mux->last_stop = GST_CLOCK_TIME_NONE; + GST_DEBUG_OBJECT (rtp_mux, "set clock-base to %u", rtp_mux->ts_base); GST_OBJECT_UNLOCK (rtp_mux); diff --git a/gst/rtpmux/gstrtpmux.h b/gst/rtpmux/gstrtpmux.h index 96513836d5..7bfea60c0b 100644 --- a/gst/rtpmux/gstrtpmux.h +++ b/gst/rtpmux/gstrtpmux.h @@ -31,7 +31,7 @@ G_BEGIN_DECLS #define GST_TYPE_RTP_MUX (gst_rtp_mux_get_type()) #define GST_RTP_MUX(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MUX, GstRTPMux)) -#define GST_RTP_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MUX, GstRTPMux)) +#define GST_RTP_MUX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MUX, GstRTPMuxClass)) #define GST_RTP_MUX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_RTP_MUX, GstRTPMuxClass)) #define GST_IS_RTP_MUX(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MUX)) #define GST_IS_RTP_MUX_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MUX)) @@ -74,6 +74,8 @@ struct _GstRTPMux guint current_ssrc; gboolean segment_pending; + + GstClockTime last_stop; }; struct _GstRTPMuxClass @@ -82,6 +84,8 @@ struct _GstRTPMuxClass gboolean (*accept_buffer_locked) (GstRTPMux *rtp_mux, GstRTPMuxPadPrivate * padpriv, GstBuffer * buffer); + + gboolean (*src_event) (GstRTPMux *rtp_mux, GstEvent *event); }; diff --git a/gst/videoparsers/Makefile.am b/gst/videoparsers/Makefile.am index 6cebef5330..811d5f860a 100644 --- a/gst/videoparsers/Makefile.am +++ b/gst/videoparsers/Makefile.am @@ -2,20 +2,21 @@ plugin_LTLIBRARIES = libgstvideoparsersbad.la libgstvideoparsersbad_la_SOURCES = plugin.c \ h263parse.c gsth263parse.c \ - gsth264parse.c h264parse.c \ gstdiracparse.c dirac_parse.c \ - gstmpegvideoparse.c mpegvideoparse.c + gsth264parse.c gstmpegvideoparse.c + libgstvideoparsersbad_la_CFLAGS = \ + $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) $(GST_CFLAGS) -libgstvideoparsersbad_la_LIBADD = \ +libgstvideoparsersbad_la_LIBADD = $(GST_PLUGINS_BASE_LIBS) \ + $(top_builddir)/gst-libs/gst/codecparsers/libgstcodecparsers-$(GST_MAJORMINOR).la \ $(GST_BASE_LIBS) $(GST_LIBS) libgstvideoparsersbad_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstvideoparsersbad_la_LIBTOOLFLAGS = --tag=disable-static noinst_HEADERS = gsth263parse.h h263parse.h \ - gsth264parse.h h264parse.h \ gstdiracparse.h dirac_parse.h \ - gstmpegvideoparse.h mpegvideoparse.h + gsth264parse.h gstmpegvideoparse.h Android.mk: Makefile.am $(BUILT_SOURCES) androgenizer \ diff --git a/gst/videoparsers/gsth264parse.c b/gst/videoparsers/gsth264parse.c index 089b5a7878..3821f0031f 100644 --- a/gst/videoparsers/gsth264parse.c +++ b/gst/videoparsers/gsth264parse.c @@ -1,7 +1,10 @@ /* GStreamer H.264 Parser - * Copyright (C) <2010> Mark Nauwelaerts - * Copyright (C) <2010> Collabora Multimedia + * Copyright (C) <2010> Collabora ltd * Copyright (C) <2010> Nokia Corporation + * Copyright (C) <2011> Intel Corporation + * + * Copyright (C) <2010> Mark Nauwelaerts + * Copyright (C) <2011> Thibault Saunier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -99,7 +102,7 @@ gst_h264_parse_base_init (gpointer g_class) gst_static_pad_template_get (&sinktemplate)); gst_element_class_set_details_simple (gstelement_class, "H.264 parser", - "Codec/Parser/Video", + "Codec/Parser/Converter/Video", "Parses H.264 streams", "Mark Nauwelaerts "); @@ -163,9 +166,14 @@ gst_h264_parse_finalize (GObject * object) static void gst_h264_parse_reset_frame (GstH264Parse * h264parse) { + GST_DEBUG_OBJECT (h264parse, "reset frame"); + /* done parsing; reset state */ - h264parse->last_nal_pos = 0; - h264parse->next_sc_pos = 0; + h264parse->nalu.valid = FALSE; + h264parse->nalu.offset = 0; + h264parse->nalu.size = 0; + h264parse->current_off = 0; + h264parse->picture_start = FALSE; h264parse->update_caps = FALSE; h264parse->idr_pos = -1; @@ -202,7 +210,13 @@ gst_h264_parse_start (GstBaseParse * parse) GST_DEBUG_OBJECT (parse, "start"); gst_h264_parse_reset (h264parse); - gst_h264_params_create (&h264parse->params, GST_ELEMENT (h264parse)); + h264parse->nalparser = gst_h264_nal_parser_new (); + + h264parse->dts = GST_CLOCK_TIME_NONE; + h264parse->ts_trn_nb = GST_CLOCK_TIME_NONE; + h264parse->sei_pic_struct_pres_flag = FALSE; + h264parse->sei_pic_struct = 0; + h264parse->field_pic_flag = 0; gst_base_parse_set_min_frame_size (parse, 6); @@ -212,13 +226,19 @@ gst_h264_parse_start (GstBaseParse * parse) static gboolean gst_h264_parse_stop (GstBaseParse * parse) { + guint i; GstH264Parse *h264parse = GST_H264_PARSE (parse); GST_DEBUG_OBJECT (parse, "stop"); gst_h264_parse_reset (h264parse); - gst_h264_params_free (h264parse->params); - h264parse->params = NULL; + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) + gst_buffer_replace (&h264parse->sps_nals[i], NULL); + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) + gst_buffer_replace (&h264parse->pps_nals[i], NULL); + + g_free (h264parse->nalparser); + h264parse->nalparser = NULL; return TRUE; } @@ -318,6 +338,8 @@ gst_h264_parse_wrap_nal (GstH264Parse * h264parse, guint format, guint8 * data, GstBuffer *buf; const guint nl = h264parse->nal_length_size; + GST_DEBUG ("Nal length %d %d", size, h264parse->nal_length_size); + buf = gst_buffer_new_and_alloc (size + nl + 4); if (format == GST_H264_PARSE_FORMAT_AVC) { GST_WRITE_UINT32_BE (GST_BUFFER_DATA (buf), size << (32 - 8 * nl)); @@ -332,65 +354,158 @@ gst_h264_parse_wrap_nal (GstH264Parse * h264parse, guint format, guint8 * data, return buf; } +static void +gst_h264_parser_store_nal (GstH264Parse * h264parse, guint id, + GstH264NalUnitType naltype, GstH264NalUnit * nalu) +{ + GstBuffer *buf, **store; + guint size = nalu->size, store_size; + + if (naltype == GST_H264_NAL_SPS) { + store_size = GST_H264_MAX_SPS_COUNT; + store = h264parse->sps_nals; + GST_DEBUG ("Storing sps %u", id); + } else if (naltype == GST_H264_NAL_PPS) { + store_size = GST_H264_MAX_PPS_COUNT; + store = h264parse->pps_nals; + GST_DEBUG ("Storing pps %u", id); + } else + return; + + if (id >= store_size) { + GST_DEBUG_OBJECT (h264parse, "unable to store nal, id out-of-range %d", id); + return; + } + + buf = gst_buffer_new_and_alloc (size); + memcpy (GST_BUFFER_DATA (buf), nalu->data + nalu->offset, size); + + if (store[id]) + gst_buffer_unref (store[id]); + + store[id] = buf; +} + /* SPS/PPS/IDR considered key, all others DELTA; * so downstream waiting for keyframe can pick up at SPS/PPS/IDR */ #define NAL_TYPE_IS_KEY(nt) (((nt) == 5) || ((nt) == 7) || ((nt) == 8)) /* caller guarantees 2 bytes of nal payload */ static void -gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data, - gint sc_pos, gint nal_pos, guint nal_size) +gst_h264_parse_process_nal (GstH264Parse * h264parse, GstH264NalUnit * nalu) { guint nal_type; + GstH264SliceHdr slice; + GstH264PPS pps; + GstH264SPS sps; + GstH264SEIMessage sei; + + gboolean slcparsed = FALSE; + GstH264NalParser *nalparser = h264parse->nalparser; - g_return_if_fail (nal_pos - sc_pos > 0 && nal_pos - sc_pos <= 4); /* nothing to do for broken input */ - if (G_UNLIKELY (nal_size < 2)) { - GST_DEBUG_OBJECT (h264parse, "not processing nal size %u", nal_size); + if (G_UNLIKELY (nalu->size < 2)) { + GST_DEBUG_OBJECT (h264parse, "not processing nal size %u", nalu->size); return; } - /* lower layer collects params */ - gst_h264_params_parse_nal (h264parse->params, data + nal_pos, nal_size); - /* we have a peek as well */ - nal_type = data[nal_pos] & 0x1f; + nal_type = nalu->type; h264parse->keyframe |= NAL_TYPE_IS_KEY (nal_type); + GST_DEBUG_OBJECT (h264parse, "processing nal of type %u, size %u", + nal_type, nalu->size); + switch (nal_type) { - case NAL_SPS: - case NAL_PPS: + case GST_H264_NAL_SPS: + gst_h264_parser_parse_sps (nalparser, nalu, &sps, TRUE); + + GST_DEBUG_OBJECT (h264parse, "triggering src caps check"); + h264parse->update_caps = TRUE; + /* found in stream, no need to forcibly push at start */ + h264parse->push_codec = FALSE; + + gst_h264_parser_store_nal (h264parse, sps.id, nal_type, nalu); + break; + case GST_H264_NAL_PPS: + gst_h264_parser_parse_pps (nalparser, nalu, &pps); /* parameters might have changed, force caps check */ GST_DEBUG_OBJECT (h264parse, "triggering src caps check"); h264parse->update_caps = TRUE; /* found in stream, no need to forcibly push at start */ h264parse->push_codec = FALSE; + + gst_h264_parser_store_nal (h264parse, pps.id, nal_type, nalu); break; - case NAL_SLICE: - case NAL_SLICE_DPA: - case NAL_SLICE_DPB: - case NAL_SLICE_DPC: + case GST_H264_NAL_SEI: + gst_h264_parser_parse_sei (nalparser, nalu, &sei); + switch (sei.payloadType) { + case GST_H264_SEI_PIC_TIMING: + h264parse->sei_pic_struct_pres_flag = + sei.pic_timing.pic_struct_present_flag; + h264parse->sei_cpb_removal_delay = sei.pic_timing.cpb_removal_delay; + if (h264parse->sei_pic_struct_pres_flag) + h264parse->sei_pic_struct = sei.pic_timing.pic_struct; + break; + case GST_H264_SEI_BUF_PERIOD: + if (h264parse->ts_trn_nb == GST_CLOCK_TIME_NONE || + h264parse->dts == GST_CLOCK_TIME_NONE) + h264parse->ts_trn_nb = 0; + else + h264parse->ts_trn_nb = h264parse->dts; + + GST_LOG_OBJECT (h264parse, + "new buffering period; ts_trn_nb updated: %" GST_TIME_FORMAT, + GST_TIME_ARGS (h264parse->ts_trn_nb)); + break; + } + break; + + case GST_H264_NAL_SLICE: + case GST_H264_NAL_SLICE_DPA: + case GST_H264_NAL_SLICE_DPB: + case GST_H264_NAL_SLICE_DPC: + slcparsed = TRUE; + if (gst_h264_parser_parse_slice_hdr (nalparser, nalu, + &slice, FALSE, FALSE) == GST_H264_PARSER_ERROR) + return; + /* real frame data */ - h264parse->frame_start |= (h264parse->params->first_mb_in_slice == 0); + h264parse->frame_start |= (slice.first_mb_in_slice == 0); /* if we need to sneak codec NALs into the stream, * this is a good place, so fake it as IDR * (which should be at start anyway) */ + GST_DEBUG ("Frame start: %i first_mb_in_slice %i", h264parse->frame_start, + slice.first_mb_in_slice); if (G_LIKELY (!h264parse->push_codec)) break; /* fall-through */ - case NAL_SLICE_IDR: + case GST_H264_NAL_SLICE_IDR: + if (!slcparsed) { + if (gst_h264_parser_parse_slice_hdr (nalparser, nalu, + &slice, FALSE, FALSE) == GST_H264_PARSER_ERROR) + return; + GST_DEBUG ("Frame start: %i first_mb_in_slice %i", + h264parse->frame_start, slice.first_mb_in_slice); + } /* real frame data */ - h264parse->frame_start |= (h264parse->params->first_mb_in_slice == 0); + h264parse->frame_start |= (slice.first_mb_in_slice == 0); + /* mark where config needs to go if interval expired */ /* mind replacement buffer if applicable */ if (h264parse->format == GST_H264_PARSE_FORMAT_AVC) h264parse->idr_pos = gst_adapter_available (h264parse->frame_out); else - h264parse->idr_pos = sc_pos; + h264parse->idr_pos = nalu->offset - 4; GST_DEBUG_OBJECT (h264parse, "marking IDR in frame at offset %d", h264parse->idr_pos); + + GST_DEBUG ("first MB: %u, slice type: %u", slice.first_mb_in_slice, + slice.type); break; + default: + gst_h264_parser_parse_nal (nalparser, nalu); } /* if AVC output needed, collect properly prefixed nal in adapter, @@ -400,7 +515,7 @@ gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data, GST_LOG_OBJECT (h264parse, "collecting NAL in AVC frame"); buf = gst_h264_parse_wrap_nal (h264parse, h264parse->format, - data + nal_pos, nal_size); + nalu->data + nalu->offset, nalu->size); gst_adapter_push (h264parse->frame_out, buf); } } @@ -408,21 +523,31 @@ gst_h264_parse_process_nal (GstH264Parse * h264parse, guint8 * data, /* caller guarantees at least 2 bytes of nal payload for each nal * returns TRUE if next_nal indicates that nal terminates an AU */ static inline gboolean -gst_h264_parse_collect_nal (GstH264Parse * h264parse, guint8 * nal, - guint8 * next_nal) +gst_h264_parse_collect_nal (GstH264Parse * h264parse, const guint8 * data, + guint size, GstH264NalUnit * nalu) { - gint nal_type; gboolean complete; + GstH264ParserResult parse_res; + GstH264NalUnitType nal_type = nalu->type; + GstH264NalUnit nnalu; - if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) + GST_DEBUG ("Parsing collecte nal"); + parse_res = gst_h264_parser_identify_nalu (h264parse->nalparser, data, + nalu->offset + nalu->size, size, &nnalu); + + if (parse_res == GST_H264_PARSER_ERROR) + return FALSE; + + if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) { return TRUE; + } /* determine if AU complete */ - nal_type = nal[0] & 0x1f; GST_LOG_OBJECT (h264parse, "nal type: %d", nal_type); /* coded slice NAL starts a picture, * i.e. other types become aggregated in front of it */ - h264parse->picture_start |= (nal_type == 1 || nal_type == 2 || nal_type == 5); + h264parse->picture_start |= (nal_type == GST_H264_NAL_SLICE || + nal_type == GST_H264_NAL_SLICE_DPA || nal_type == GST_H264_NAL_SLICE_IDR); /* consider a coded slices (IDR or not) to start a picture, * (so ending the previous one) if first_mb_in_slice == 0 @@ -431,35 +556,23 @@ gst_h264_parse_collect_nal (GstH264Parse * h264parse, guint8 * nal, * but in practice it works in sane cases, needs not much parsing, * and also works with broken frame_num in NAL * (where spec-wise would fail) */ - nal_type = next_nal[0] & 0x1f; + nal_type = nnalu.type; + complete = h264parse->picture_start && (nal_type >= GST_H264_NAL_SEI && + nal_type <= GST_H264_NAL_AU_DELIMITER); + GST_LOG_OBJECT (h264parse, "next nal type: %d", nal_type); - complete = h264parse->picture_start && (nal_type >= 6 && nal_type <= 9); complete |= h264parse->picture_start && - (nal_type == 1 || nal_type == 2 || nal_type == 5) && + (nal_type == GST_H264_NAL_SLICE || + nal_type == GST_H264_NAL_SLICE_DPA || + nal_type == GST_H264_NAL_SLICE_IDR) && /* first_mb_in_slice == 0 considered start of frame */ - (next_nal[1] & 0x80); + (nnalu.data[nnalu.offset + 1] & 0x80); GST_LOG_OBJECT (h264parse, "au complete: %d", complete); return complete; } -/* finds next startcode == 00 00 01, along with a subsequent byte */ -static guint -gst_h264_parse_find_sc (GstBuffer * buffer, guint skip) -{ - GstByteReader br; - guint sc_pos = -1; - - gst_byte_reader_init_from_buffer (&br, buffer); - - /* NALU not empty, so we can at least expect 1 (even 2) bytes following sc */ - sc_pos = gst_byte_reader_masked_scan_uint32 (&br, 0xffffff00, 0x00000100, - skip, gst_byte_reader_get_remaining (&br) - skip); - - return sc_pos; -} - /* FIXME move into baseparse, or anything equivalent; * see https://bugzilla.gnome.org/show_bug.cgi?id=650093 */ #define GST_BASE_PARSE_FRAME_FLAG_PARSING 0x10000 @@ -470,10 +583,11 @@ gst_h264_parse_check_valid_frame (GstBaseParse * parse, { GstH264Parse *h264parse = GST_H264_PARSE (parse); GstBuffer *buffer = frame->buffer; - gint sc_pos, nal_pos, next_sc_pos, next_nal_pos; guint8 *data; - guint size; + guint size, current_off = 0; gboolean drain; + GstH264NalParser *nalparser = h264parse->nalparser; + GstH264NalUnit nalu = h264parse->nalu; /* expect at least 3 bytes startcode == sc, and 2 bytes NALU payload */ if (G_UNLIKELY (GST_BUFFER_SIZE (buffer) < 5)) @@ -495,105 +609,108 @@ gst_h264_parse_check_valid_frame (GstBaseParse * parse, data = GST_BUFFER_DATA (buffer); size = GST_BUFFER_SIZE (buffer); - GST_LOG_OBJECT (h264parse, "last_nal_pos: %d, last_scan_pos %d", - h264parse->last_nal_pos, h264parse->next_sc_pos); - - nal_pos = h264parse->last_nal_pos; - next_sc_pos = h264parse->next_sc_pos; - - if (!next_sc_pos) { - sc_pos = gst_h264_parse_find_sc (buffer, 0); - - if (sc_pos == -1) { - /* SC not found, need more data */ - sc_pos = GST_BUFFER_SIZE (buffer) - 3; - /* avoid going < 0 later on */ - nal_pos = next_sc_pos = sc_pos; - goto more; - } - - nal_pos = sc_pos + 3; - next_sc_pos = nal_pos; - /* sc might have 2 or 3 0-bytes */ - if (sc_pos > 0 && data[sc_pos - 1] == 00) - sc_pos--; - GST_LOG_OBJECT (h264parse, "found sc at offset %d", sc_pos); - } else { - /* previous checks already arrange sc at start */ - sc_pos = 0; - } - drain = GST_BASE_PARSE_DRAINING (parse); + current_off = h264parse->current_off; + + GST_DEBUG ("Last parse position %u", current_off); while (TRUE) { - gint prev_sc_pos; + switch (gst_h264_parser_identify_nalu (nalparser, data, current_off, + size, &nalu)) { + case GST_H264_PARSER_OK: + GST_DEBUG ("Complete nal found. %u Off: %u, Size: %u", + current_off, nalu.offset, nalu.size); - next_sc_pos = gst_h264_parse_find_sc (buffer, next_sc_pos); - if (next_sc_pos == -1) { - GST_LOG_OBJECT (h264parse, "no next sc"); - if (drain) { - /* FLUSH/EOS, it's okay if we can't find the next frame */ - next_sc_pos = size; - next_nal_pos = size; - } else { - next_sc_pos = size - 3; - goto more; - } - } else { - next_nal_pos = next_sc_pos + 3; - if (data[next_sc_pos - 1] == 00) - next_sc_pos--; - GST_LOG_OBJECT (h264parse, "found next sc at offset %d", next_sc_pos); - /* need at least 1 more byte of next NAL */ - if (!drain && (next_nal_pos == size - 1)) - goto more; - } - - /* determine nal's sc position */ - prev_sc_pos = nal_pos - 3; - g_assert (prev_sc_pos >= 0); - if (prev_sc_pos > 0 && data[prev_sc_pos - 1] == 0) - prev_sc_pos--; - - /* already consume and gather info from NAL */ - if (G_UNLIKELY (next_sc_pos - nal_pos < 2)) { - GST_WARNING_OBJECT (h264parse, "input stream is corrupt; " - "it contains a NAL unit of length %d", next_sc_pos - nal_pos); - /* broken nal at start -> arrange to skip it, - * otherwise have it terminate current au - * (and so it will be skippd on next frame round) */ - if (prev_sc_pos == sc_pos) { - *skipsize = sc_pos + 2; - return FALSE; - } else { - next_sc_pos = prev_sc_pos; + current_off = nalu.offset + nalu.size; + GST_DEBUG ("CURENT OFF. %u, %u", current_off, nalu.offset + nalu.size); + if (!h264parse->nalu.size && !h264parse->nalu.valid) + h264parse->nalu = nalu; break; - } - } - gst_h264_parse_process_nal (h264parse, data, prev_sc_pos, nal_pos, - next_sc_pos - nal_pos); - if (next_nal_pos >= size - 1 || - gst_h264_parse_collect_nal (h264parse, data + nal_pos, - data + next_nal_pos)) - break; + case GST_H264_PARSER_BROKEN_LINK: + return FALSE; + case GST_H264_PARSER_ERROR: + current_off = size - 3; + goto parsing_error; + case GST_H264_PARSER_NO_NAL: + current_off = size - 3; + goto more; + case GST_H264_PARSER_BROKEN_DATA: + GST_WARNING_OBJECT (h264parse, "input stream is corrupt; " + "it contains a NAL unit of length %d", nalu.size); - /* move along */ - next_sc_pos = nal_pos = next_nal_pos; + /* broken nal at start -> arrange to skip it, + * otherwise have it terminate current au + * (and so it will be skipped on next frame round) */ + if (nalu.sc_offset == h264parse->nalu.sc_offset) { + *skipsize = nalu.offset; + + GST_DEBUG ("Skiping broken nal"); + return FALSE; + } else { + nalu.size = 0; + + goto end; + } + case GST_H264_PARSER_NO_NAL_END: + GST_DEBUG ("Not a complete nal found at offset %u", nalu.offset); + + current_off = nalu.sc_offset; + /* We keep the reference to this nal so we start over the parsing + * here */ + if (!h264parse->nalu.size && !h264parse->nalu.valid) + h264parse->nalu = nalu; + + if (drain) { + GST_DEBUG ("Drainning NAL %u %u %u", size, h264parse->nalu.offset, + h264parse->nalu.size); + /* Can't parse the nalu */ + if (size - h264parse->nalu.offset < 2) { + *skipsize = nalu.offset; + return FALSE; + } + + /* We parse it anyway */ + nalu.size = size - nalu.offset; + break; + } + goto more; + } + + current_off = nalu.offset + nalu.size; + + GST_DEBUG ("%p Complete nal found. Off: %u, Size: %u", data, nalu.offset, + nalu.size); + + gst_h264_parse_process_nal (h264parse, &nalu); + if (gst_h264_parse_collect_nal (h264parse, data, size, &nalu) || drain) + break; } - *skipsize = sc_pos; - *framesize = next_sc_pos - sc_pos; +end: + /* FIXME this shouldnt be needed */ + if (h264parse->nalu.sc_offset > 0 && data[h264parse->nalu.sc_offset - 1] == 0) + h264parse->nalu.sc_offset--; + + *skipsize = h264parse->nalu.sc_offset; + *framesize = nalu.offset + nalu.size - h264parse->nalu.sc_offset; /* CHECKME */ + h264parse->current_off = current_off; return TRUE; +parsing_error: + GST_DEBUG ("Error parsing Nal Unit"); more: + /* ask for best next available */ *framesize = G_MAXUINT; + if (!h264parse->nalu.size) { + /* skip up to initial startcode */ + *skipsize = h264parse->nalu.sc_offset; + } else { + *skipsize = 0; + } - /* skip up to initial startcode */ - *skipsize = sc_pos; - /* resume scanning here next time */ - h264parse->last_nal_pos = nal_pos - sc_pos; - h264parse->next_sc_pos = next_sc_pos - sc_pos; + /* Restart parsing from here next time */ + h264parse->current_off = current_off; return FALSE; } @@ -610,8 +727,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) /* only nal payload in stored nals */ - for (i = 0; i < MAX_SPS_COUNT; i++) { - if ((nal = h264parse->params->sps_nals[i])) { + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if ((nal = h264parse->sps_nals[i])) { num_sps++; /* size bytes also count */ sps_size += GST_BUFFER_SIZE (nal) + 2; @@ -623,8 +740,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) } } } - for (i = 0; i < MAX_PPS_COUNT; i++) { - if ((nal = h264parse->params->pps_nals[i])) { + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if ((nal = h264parse->pps_nals[i])) { num_pps++; /* size bytes also count */ pps_size += GST_BUFFER_SIZE (nal) + 2; @@ -648,8 +765,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) data[5] = 0xe0 | num_sps; /* number of SPSs */ data += 6; - for (i = 0; i < MAX_SPS_COUNT; i++) { - if ((nal = h264parse->params->sps_nals[i])) { + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if ((nal = h264parse->sps_nals[i])) { GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (nal)); memcpy (data + 2, GST_BUFFER_DATA (nal), GST_BUFFER_SIZE (nal)); data += 2 + GST_BUFFER_SIZE (nal); @@ -658,8 +775,8 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) data[0] = num_pps; data++; - for (i = 0; i < MAX_PPS_COUNT; i++) { - if ((nal = h264parse->params->pps_nals[i])) { + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if ((nal = h264parse->pps_nals[i])) { GST_WRITE_UINT16_BE (data, GST_BUFFER_SIZE (nal)); memcpy (data + 2, GST_BUFFER_DATA (nal), GST_BUFFER_SIZE (nal)); data += 2 + GST_BUFFER_SIZE (nal); @@ -672,7 +789,7 @@ gst_h264_parse_make_codec_data (GstH264Parse * h264parse) static void gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps) { - GstH264ParamsSPS *sps; + GstH264SPS *sps; GstCaps *sink_caps; gboolean modified = FALSE; GstBuffer *buf = NULL; @@ -695,7 +812,7 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps) else sink_caps = gst_caps_new_simple ("video/x-h264", NULL); - sps = h264parse->params->sps; + sps = h264parse->nalparser->last_sps; GST_DEBUG_OBJECT (h264parse, "sps: %p", sps); /* only codec-data for nice-and-clean au aligned packetized avc format */ @@ -764,6 +881,134 @@ gst_h264_parse_update_src_caps (GstH264Parse * h264parse, GstCaps * caps) gst_buffer_unref (buf); } +static void +gst_h264_parse_get_timestamp (GstH264Parse * h264parse, + GstClockTime * out_ts, GstClockTime * out_dur, gboolean frame) +{ + GstH264SPS *sps = h264parse->nalparser->last_sps; + GstClockTime upstream; + gint duration = 1; + + g_return_if_fail (out_dur != NULL); + g_return_if_fail (out_ts != NULL); + + upstream = *out_ts; + + if (!frame) { + GST_LOG_OBJECT (h264parse, "no frame data -> 0 duration"); + *out_dur = 0; + goto exit; + } else { + *out_ts = upstream; + } + + if (!sps) { + GST_DEBUG_OBJECT (h264parse, "referred SPS invalid"); + goto exit; + } else if (!sps->vui_parameters.timing_info_present_flag) { + GST_DEBUG_OBJECT (h264parse, + "unable to compute timestamp: timing info not present"); + goto exit; + } else if (sps->vui_parameters.time_scale == 0) { + GST_DEBUG_OBJECT (h264parse, + "unable to compute timestamp: time_scale = 0 " + "(this is forbidden in spec; bitstream probably contains error)"); + goto exit; + } + + if (h264parse->sei_pic_struct_pres_flag && + h264parse->sei_pic_struct != (guint8) - 1) { + /* Note that when h264parse->sei_pic_struct == -1 (unspecified), there + * are ways to infer its value. This is related to computing the + * TopFieldOrderCnt and BottomFieldOrderCnt, which looks + * complicated and thus not implemented for the time being. Yet + * the value we have here is correct for many applications + */ + switch (h264parse->sei_pic_struct) { + case GST_H264_SEI_PIC_STRUCT_TOP_FIELD: + case GST_H264_SEI_PIC_STRUCT_BOTTOM_FIELD: + duration = 1; + break; + case GST_H264_SEI_PIC_STRUCT_FRAME: + case GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM: + case GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP: + duration = 2; + break; + case GST_H264_SEI_PIC_STRUCT_TOP_BOTTOM_TOP: + case GST_H264_SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: + duration = 3; + break; + case GST_H264_SEI_PIC_STRUCT_FRAME_DOUBLING: + duration = 4; + break; + case GST_H264_SEI_PIC_STRUCT_FRAME_TRIPLING: + duration = 6; + break; + default: + GST_DEBUG_OBJECT (h264parse, + "h264parse->sei_pic_struct of unknown value %d. Not parsed", + h264parse->sei_pic_struct); + break; + } + } else { + duration = h264parse->field_pic_flag ? 1 : 2; + } + + GST_LOG_OBJECT (h264parse, "frame tick duration %d", duration); + + /* + * h264parse.264 C.1.2 Timing of coded picture removal (equivalent to DTS): + * Tr,n(0) = initial_cpb_removal_delay[ SchedSelIdx ] / 90000 + * Tr,n(n) = Tr,n(nb) + Tc * cpb_removal_delay(n) + * where + * Tc = num_units_in_tick / time_scale + */ + + if (h264parse->ts_trn_nb != GST_CLOCK_TIME_NONE) { + GST_LOG_OBJECT (h264parse, "buffering based ts"); + /* buffering period is present */ + if (upstream != GST_CLOCK_TIME_NONE) { + /* If upstream timestamp is valid, we respect it and adjust current + * reference point */ + h264parse->ts_trn_nb = upstream - + (GstClockTime) gst_util_uint64_scale_int + (h264parse->sei_cpb_removal_delay * GST_SECOND, + sps->vui_parameters.num_units_in_tick, + sps->vui_parameters.time_scale); + } else { + /* If no upstream timestamp is given, we write in new timestamp */ + upstream = h264parse->dts = h264parse->ts_trn_nb + + (GstClockTime) gst_util_uint64_scale_int + (h264parse->sei_cpb_removal_delay * GST_SECOND, + sps->vui_parameters.num_units_in_tick, + sps->vui_parameters.time_scale); + } + } else { + GstClockTime dur; + + GST_LOG_OBJECT (h264parse, "duration based ts"); + /* naive method: no removal delay specified + * track upstream timestamp and provide best guess frame duration */ + dur = gst_util_uint64_scale_int (duration * GST_SECOND, + sps->vui_parameters.num_units_in_tick, sps->vui_parameters.time_scale); + /* sanity check */ + if (dur < GST_MSECOND) { + GST_DEBUG_OBJECT (h264parse, "discarding dur %" GST_TIME_FORMAT, + GST_TIME_ARGS (dur)); + } else { + *out_dur = dur; + } + } + +exit: + if (GST_CLOCK_TIME_IS_VALID (upstream)) + *out_ts = h264parse->dts = upstream; + + if (GST_CLOCK_TIME_IS_VALID (*out_dur) && + GST_CLOCK_TIME_IS_VALID (h264parse->dts)) + h264parse->dts += *out_dur; +} + static GstFlowReturn gst_h264_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) { @@ -776,7 +1021,7 @@ gst_h264_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) gst_h264_parse_update_src_caps (h264parse, NULL); - gst_h264_params_get_timestamp (h264parse->params, + gst_h264_parse_get_timestamp (h264parse, &GST_BUFFER_TIMESTAMP (buffer), &GST_BUFFER_DURATION (buffer), h264parse->frame_start); @@ -864,16 +1109,16 @@ gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) if (h264parse->align == GST_H264_PARSE_ALIGN_NAL) { /* send separate config NAL buffers */ GST_DEBUG_OBJECT (h264parse, "- sending SPS/PPS"); - for (i = 0; i < MAX_SPS_COUNT; i++) { - if ((codec_nal = h264parse->params->sps_nals[i])) { + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if ((codec_nal = h264parse->sps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "sending SPS nal"); gst_h264_parse_push_codec_buffer (h264parse, codec_nal, timestamp); h264parse->last_report = new_ts; } } - for (i = 0; i < MAX_PPS_COUNT; i++) { - if ((codec_nal = h264parse->params->pps_nals[i])) { + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if ((codec_nal = h264parse->pps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "sending PPS nal"); gst_h264_parse_push_codec_buffer (h264parse, codec_nal, timestamp); @@ -890,8 +1135,8 @@ gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) gst_byte_writer_put_data (&bw, GST_BUFFER_DATA (buffer), h264parse->idr_pos); GST_DEBUG_OBJECT (h264parse, "- inserting SPS/PPS"); - for (i = 0; i < MAX_SPS_COUNT; i++) { - if ((codec_nal = h264parse->params->sps_nals[i])) { + for (i = 0; i < GST_H264_MAX_SPS_COUNT; i++) { + if ((codec_nal = h264parse->sps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "inserting SPS nal"); gst_byte_writer_put_uint32_be (&bw, bs ? 1 : GST_BUFFER_SIZE (codec_nal)); @@ -900,8 +1145,8 @@ gst_h264_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame) h264parse->last_report = new_ts; } } - for (i = 0; i < MAX_PPS_COUNT; i++) { - if ((codec_nal = h264parse->params->pps_nals[i])) { + for (i = 0; i < GST_H264_MAX_PPS_COUNT; i++) { + if ((codec_nal = h264parse->pps_nals[i])) { GST_DEBUG_OBJECT (h264parse, "inserting PPS nal"); gst_byte_writer_put_uint32_be (&bw, bs ? 1 : GST_BUFFER_SIZE (codec_nal)); @@ -937,7 +1182,9 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps) GstStructure *str; const GValue *value; GstBuffer *codec_data = NULL; - guint size, format, align; + guint size, format, align, off; + GstH264NalUnit nalu; + GstH264ParserResult parseres; h264parse = GST_H264_PARSE (parse); @@ -959,7 +1206,7 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps) if (format != GST_H264_PARSE_FORMAT_BYTE && (value = gst_structure_get_value (str, "codec_data"))) { guint8 *data; - guint num_sps, num_pps, profile, len; + guint num_sps, num_pps, profile; gint i; GST_DEBUG_OBJECT (h264parse, "have packetized h264"); @@ -992,28 +1239,29 @@ gst_h264_parse_set_caps (GstBaseParse * parse, GstCaps * caps) GST_DEBUG_OBJECT (h264parse, "nal length %u", h264parse->nal_length_size); num_sps = data[5] & 0x1f; - data += 6; - size -= 6; + off = 6; for (i = 0; i < num_sps; i++) { - len = GST_READ_UINT16_BE (data); - if (size < len + 2 || len < 2) + parseres = gst_h264_parser_identify_nalu_avc (h264parse->nalparser, + data, off, size - off, 2, &nalu); + if (parseres != GST_H264_PARSER_OK) goto avcc_too_small; - /* digest for later reference */ - gst_h264_parse_process_nal (h264parse, data, 0, 2, len); - data += len + 2; - size -= len + 2; + + gst_h264_parse_process_nal (h264parse, &nalu); + off = nalu.offset + nalu.size; } + num_pps = data[0]; data++; size++; for (i = 0; i < num_pps; i++) { - len = GST_READ_UINT16_BE (data); - if (size < len + 2 || len < 2) + parseres = gst_h264_parser_identify_nalu_avc (h264parse->nalparser, + data, off, size - off, 2, &nalu); + if (parseres != GST_H264_PARSER_OK) { goto avcc_too_small; - /* digest for later reference */ - gst_h264_parse_process_nal (h264parse, data, 0, 2, len); - data += len + 2; - size -= len + 2; + } + + gst_h264_parse_process_nal (h264parse, &nalu); + off = nalu.offset + nalu.size; } h264parse->codec_data = gst_buffer_ref (codec_data); @@ -1089,10 +1337,12 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer) GstBuffer *sub; GstFlowReturn ret = GST_FLOW_OK; guint32 len; + GstH264NalUnit nalu; const guint nl = h264parse->nal_length_size; GST_LOG_OBJECT (h264parse, "processing packet buffer of size %d", GST_BUFFER_SIZE (buffer)); + gst_byte_reader_init_from_buffer (&br, buffer); while (ret == GST_FLOW_OK && gst_byte_reader_get_remaining (&br)) { GST_DEBUG_OBJECT (h264parse, "AVC nal offset %d", @@ -1114,9 +1364,10 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer) break; default: goto not_negotiated; - break; } + GST_DEBUG_OBJECT (h264parse, "AVC nal size %d", len); + if (gst_byte_reader_get_remaining (&br) < len) goto parse_failed; if (h264parse->split_packetized) { @@ -1133,9 +1384,10 @@ gst_h264_parse_chain (GstPad * pad, GstBuffer * buffer) /* NOTE: so if it is really configured to do so, * pre_push can/will still insert codec-data at intervals, * which is not really pure pass-through, but anyway ... */ - gst_h264_parse_process_nal (h264parse, + gst_h264_parser_identify_nalu (h264parse->nalparser, GST_BUFFER_DATA (buffer), gst_byte_reader_get_pos (&br) - nl, - gst_byte_reader_get_pos (&br), len); + GST_BUFFER_SIZE (buffer), &nalu); + gst_h264_parse_process_nal (h264parse, &nalu); gst_byte_reader_skip_unchecked (&br, len); } } diff --git a/gst/videoparsers/gsth264parse.h b/gst/videoparsers/gsth264parse.h index 1aa1323211..e013a3faca 100644 --- a/gst/videoparsers/gsth264parse.h +++ b/gst/videoparsers/gsth264parse.h @@ -1,7 +1,10 @@ /* GStreamer H.264 Parser - * Copyright (C) <2010> Mark Nauwelaerts - * Copyright (C) <2010> Collabora Multimedia + * Copyright (C) <2010> Collabora ltd * Copyright (C) <2010> Nokia Corporation + * Copyright (C) <2011> Intel Corporation + * + * Copyright (C) <2010> Mark Nauwelaerts + * Copyright (C) <2011> Thibault Saunier * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -24,8 +27,7 @@ #include #include - -#include "h264parse.h" +#include G_BEGIN_DECLS @@ -61,16 +63,34 @@ struct _GstH264Parse gboolean packetized; /* state */ - GstH264Params *params; + GstH264NalParser *nalparser; + GstH264NalUnit nalu; guint align; guint format; + guint current_off; GstClockTime last_report; gboolean push_codec; + /* collected SPS and PPS NALUs */ + GstBuffer *sps_nals[GST_H264_MAX_SPS_COUNT]; + GstBuffer *pps_nals[GST_H264_MAX_PPS_COUNT]; + + /* Infos we need to keep track of */ + guint32 sei_cpb_removal_delay; + guint8 sei_pic_struct; + guint8 sei_pic_struct_pres_flag; + guint field_pic_flag; + + /* cached timestamps */ + /* (trying to) track upstream dts and interpolate */ + GstClockTime dts; + /* dts at start of last buffering period */ + GstClockTime ts_trn_nb; + /* frame parsing */ - guint last_nal_pos; - guint next_sc_pos; + /*guint last_nal_pos;*/ + /*guint next_sc_pos;*/ gint idr_pos; gboolean update_caps; GstAdapter *frame_out; diff --git a/gst/videoparsers/gstmpegvideoparse.c b/gst/videoparsers/gstmpegvideoparse.c index 7ebbdf36a4..12d98d5d77 100644 --- a/gst/videoparsers/gstmpegvideoparse.c +++ b/gst/videoparsers/gstmpegvideoparse.c @@ -1,8 +1,10 @@ /* GStreamer * Copyright (C) <2007> Jan Schmidt * Copyright (C) <2011> Mark Nauwelaerts - * Copyright (C) <2011> Collabora Multimedia + * Copyright (C) <2011> Thibault Saunier + * Copyright (C) <2011> Collabora ltd * Copyright (C) <2011> Nokia Corporation + * Copyright (C) <2011> Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -168,6 +170,7 @@ gst_mpegv_parse_class_init (GstMpegvParseClass * klass) static void gst_mpegv_parse_init (GstMpegvParse * parse, GstMpegvParseClass * g_class) { + parse->mpeg_version = 0; } static void @@ -175,7 +178,7 @@ gst_mpegv_parse_reset_frame (GstMpegvParse * mpvparse) { /* done parsing; reset state */ mpvparse->last_sc = -1; - mpvparse->seq_offset = -1; + mpvparse->seq_offset = G_MAXUINT; mpvparse->pic_offset = -1; } @@ -187,7 +190,9 @@ gst_mpegv_parse_reset (GstMpegvParse * mpvparse) mpvparse->update_caps = TRUE; gst_buffer_replace (&mpvparse->config, NULL); - memset (&mpvparse->params, 0, sizeof (mpvparse->params)); + memset (&mpvparse->sequencehdr, 0, sizeof (mpvparse->sequencehdr)); + memset (&mpvparse->sequenceext, 0, sizeof (mpvparse->sequenceext)); + memset (&mpvparse->pichdr, 0, sizeof (mpvparse->pichdr)); } static gboolean @@ -217,23 +222,45 @@ gst_mpegv_parse_stop (GstBaseParse * parse) } static gboolean -gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, const guint8 * data, - gsize size) +gst_mpegv_parse_process_config (GstMpegvParse * mpvparse, GstBuffer * buf, + guint size) { + GList *tmp; + guint8 *data = GST_BUFFER_DATA (buf); + data = data + mpvparse->seq_offset; + /* only do stuff if something new */ if (mpvparse->config && size == GST_BUFFER_SIZE (mpvparse->config) && memcmp (GST_BUFFER_DATA (mpvparse->config), data, size) == 0) return TRUE; - if (!gst_mpeg_video_params_parse_config (&mpvparse->params, data, size)) { - GST_DEBUG_OBJECT (mpvparse, "failed to parse config data (size %" - G_GSSIZE_FORMAT ")", size); + if (!gst_mpeg_video_parse_sequence_header (&mpvparse->sequencehdr, data, + GST_BUFFER_SIZE (buf) - mpvparse->seq_offset, 0)) { + GST_DEBUG_OBJECT (mpvparse, + "failed to parse config data (size %" G_GSSIZE_FORMAT ") at offset %d", + size, mpvparse->seq_offset); return FALSE; } GST_LOG_OBJECT (mpvparse, "accepting parsed config size %" G_GSSIZE_FORMAT, size); + /* Set mpeg version, and parse sequence extension */ + if (mpvparse->mpeg_version <= 0) { + GstMpegVideoTypeOffsetSize *tpoffsz; + + mpvparse->mpeg_version = 1; + for (tmp = mpvparse->typeoffsize; tmp; tmp = tmp->next) { + tpoffsz = tmp->data; + + if (tpoffsz->type == GST_MPEG_VIDEO_PACKET_EXTENSION) { + mpvparse->mpeg_version = 2; + gst_mpeg_video_parse_sequence_extension (&mpvparse->sequenceext, + GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), tpoffsz->offset); + } + } + } + /* parsing ok, so accept it as new config */ if (mpvparse->config != NULL) gst_buffer_unref (mpvparse->config); @@ -309,22 +336,18 @@ picture_type_name (guint8 pct) /* for off == 0 initial code; returns TRUE if code starts a frame, * otherwise returns TRUE if code terminates preceding frame */ static gboolean -gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse, GstBuffer * buf, gint off) +gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse, + GstBuffer * buf, guint off, guint8 code) { - gboolean ret = FALSE, do_seq = TRUE; - guint8 *data; - guint code; + gboolean ret = FALSE, packet = TRUE; g_return_val_if_fail (buf && GST_BUFFER_SIZE (buf) >= 4, FALSE); - data = GST_BUFFER_DATA (buf); - code = data[off + 3]; - GST_LOG_OBJECT (mpvparse, "process startcode %x (%s)", code, picture_start_code_name (code)); switch (code) { - case MPEG_PACKET_PICTURE: + case GST_MPEG_VIDEO_PACKET_PICTURE: GST_LOG_OBJECT (mpvparse, "startcode is PICTURE"); /* picture is aggregated with preceding sequence/gop, if any. * so, picture start code only ends if already a previous one */ @@ -332,103 +355,64 @@ gst_mpegv_parse_process_sc (GstMpegvParse * mpvparse, GstBuffer * buf, gint off) mpvparse->pic_offset = off; else ret = TRUE; - if (!off) + if (off == 4) ret = TRUE; break; - case MPEG_PACKET_SEQUENCE: + case GST_MPEG_VIDEO_PACKET_SEQUENCE: GST_LOG_OBJECT (mpvparse, "startcode is SEQUENCE"); - if (off == 0) + if (off < mpvparse->seq_offset) mpvparse->seq_offset = off; ret = TRUE; break; - case MPEG_PACKET_GOP: + case GST_MPEG_VIDEO_PACKET_GOP: GST_LOG_OBJECT (mpvparse, "startcode is GOP"); - if (mpvparse->seq_offset >= 0) + if (mpvparse->seq_offset < G_MAXUINT) ret = mpvparse->gop_split; else ret = TRUE; break; default: - do_seq = FALSE; + packet = FALSE; break; } - /* process config data */ - if (G_UNLIKELY (mpvparse->seq_offset >= 0 && off && do_seq)) { - g_assert (mpvparse->seq_offset == 0); - gst_mpegv_parse_process_config (mpvparse, GST_BUFFER_DATA (buf), off); - /* avoid accepting again for a PICTURE sc following a GOP sc */ - mpvparse->seq_offset = -1; + if (mpvparse->seq_offset != G_MAXUINT && off != mpvparse->seq_offset && + packet) { + gst_mpegv_parse_process_config (mpvparse, buf, off - mpvparse->seq_offset); + mpvparse->seq_offset = G_MAXUINT; } /* extract some picture info if there is any in the frame being terminated */ - if (G_UNLIKELY (ret && off)) { - if (G_LIKELY (mpvparse->pic_offset >= 0 && mpvparse->pic_offset < off)) { - if (G_LIKELY (GST_BUFFER_SIZE (buf) >= mpvparse->pic_offset + 6)) { - gint pct = (data[mpvparse->pic_offset + 5] >> 3) & 0x7; - - GST_LOG_OBJECT (mpvparse, "picture_coding_type %d (%s)", pct, - picture_type_name (pct)); - mpvparse->intra_frame = (pct == MPEG_PICTURE_TYPE_I); - } else { - GST_WARNING_OBJECT (mpvparse, "no data following PICTURE startcode"); - mpvparse->intra_frame = FALSE; - } - } else { - /* frame without picture must be some config, consider as keyframe */ - mpvparse->intra_frame = TRUE; - } - GST_LOG_OBJECT (mpvparse, "ending frame of size %d, is intra %d", off, - mpvparse->intra_frame); + if (ret && mpvparse->pic_offset >= 0 && mpvparse->pic_offset < off) { + if (gst_mpeg_video_parse_picture_header (&mpvparse->pichdr, + GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), mpvparse->pic_offset)) + GST_LOG_OBJECT (mpvparse, "picture_coding_type %d (%s), ending" + "frame of size %d", mpvparse->pichdr.pic_type, + picture_type_name (mpvparse->pichdr.pic_type), off - 4); + else + GST_LOG_OBJECT (mpvparse, "Couldn't parse picture at offset %d", + mpvparse->pic_offset); } return ret; } -static inline guint -scan_for_start_codes (const GstByteReader * reader, guint offset, guint size) +static inline gint +get_frame_size (GstMpegvParse * mpvparse, GstBuffer * buf, GList * l_codoffsz) { - const guint8 *data; - guint32 state; - guint i; + GList *tmp; + GstMpegVideoTypeOffsetSize *codoffsz; - g_return_val_if_fail (size > 0, -1); - g_return_val_if_fail ((guint64) offset + size <= reader->size - reader->byte, - -1); + for (tmp = l_codoffsz; tmp; tmp = tmp->next) { + codoffsz = tmp->data; - /* we can't find the pattern with less than 4 bytes */ - if (G_UNLIKELY (size < 4)) - return -1; + GST_LOG_OBJECT (mpvparse, "next start code at %d", codoffsz->offset); - data = reader->data + reader->byte + offset; - - /* set the state to something that does not match */ - state = 0xffffffff; - - /* now find data */ - for (i = 0; i < size; i++) { - /* throw away one byte and move in the next byte */ - state = ((state << 8) | data[i]); - if (G_UNLIKELY ((state & 0xffffff00) == 0x00000100)) { - /* we have a match but we need to have skipped at - * least 4 bytes to fill the state. */ - if (G_LIKELY (i >= 3)) - return offset + i - 3; - } - - /* Accelerate search for start code */ - if (data[i] > 1) { - while (i < (size - 4) && data[i] > 1) { - if (data[i + 3] > 1) - i += 4; - else - i += 1; - } - state = 0x00000100; - } + if (gst_mpegv_parse_process_sc (mpvparse, buf, codoffsz->offset, + codoffsz->type)) + return codoffsz->offset - 4; } - /* nothing found */ return -1; } @@ -436,21 +420,10 @@ scan_for_start_codes (const GstByteReader * reader, guint offset, guint size) * see https://bugzilla.gnome.org/show_bug.cgi?id=650093 */ #define GST_BASE_PARSE_FRAME_FLAG_PARSING 0x10000 -static gboolean -gst_mpegv_parse_check_valid_frame (GstBaseParse * parse, - GstBaseParseFrame * frame, guint * framesize, gint * skipsize) +static inline void +update_frame_parsing_status (GstMpegvParse * mpvparse, + GstBaseParseFrame * frame) { - GstMpegvParse *mpvparse = GST_MPEGVIDEO_PARSE (parse); - GstBuffer *buf = frame->buffer; - GstByteReader reader = GST_BYTE_READER_INIT_FROM_BUFFER (buf); - gint off = 0; - gboolean ret; - -retry: - /* at least start code and subsequent byte */ - if (G_UNLIKELY (GST_BUFFER_SIZE (buf) - off < 5)) - return FALSE; - /* avoid stale cached parsing state */ if (!(frame->flags & GST_BASE_PARSE_FRAME_FLAG_PARSING)) { GST_LOG_OBJECT (mpvparse, "parsing new frame"); @@ -459,72 +432,75 @@ retry: } else { GST_LOG_OBJECT (mpvparse, "resuming frame parsing"); } +} - /* if already found a previous start code, e.g. start of frame, go for next */ - if (mpvparse->last_sc >= 0) { + +static gboolean +gst_mpegv_parse_check_valid_frame (GstBaseParse * parse, + GstBaseParseFrame * frame, guint * framesize, gint * skipsize) +{ + GstMpegvParse *mpvparse = GST_MPEGVIDEO_PARSE (parse); + GstBuffer *buf = frame->buffer; + gboolean ret = FALSE; + GList *tmp; + gint off = 0, fsize = -1; + + update_frame_parsing_status (mpvparse, frame); + + if (mpvparse->last_sc >= 0) off = mpvparse->last_sc; - goto next; + + mpvparse->typeoffsize = + gst_mpeg_video_parse (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf), off); + + /* No sc found */ + if (mpvparse->typeoffsize == NULL) + goto end; + + /* Already found the start code looking for the end */ + if (mpvparse->last_sc >= 0) { + *skipsize = 0; + fsize = get_frame_size (mpvparse, buf, mpvparse->typeoffsize); + + goto end; } - off = scan_for_start_codes (&reader, off, GST_BUFFER_SIZE (buf) - off); + for (tmp = mpvparse->typeoffsize; tmp; tmp = g_list_next (tmp)) { + GstMpegVideoTypeOffsetSize *codoffsz = tmp->data; - GST_LOG_OBJECT (mpvparse, "possible sync at buffer offset %d", off); + GST_LOG_OBJECT (mpvparse, "next start code at %d", codoffsz->offset); - /* didn't find anything that looks like a sync word, skip */ - if (G_UNLIKELY (off < 0)) { - *skipsize = GST_BUFFER_SIZE (buf) - 3; - return FALSE; - } + if (codoffsz->size < 0) + break; - /* possible frame header, but not at offset 0? skip bytes before sync */ - if (G_UNLIKELY (off > 0)) { - *skipsize = off; - return FALSE; - } + ret = gst_mpegv_parse_process_sc (mpvparse, buf, codoffsz->offset, + codoffsz->type); - /* note: initial start code is assumed at offset 0 by subsequent code */ - - /* examine start code, see if it looks like an initial start code */ - if (gst_mpegv_parse_process_sc (mpvparse, buf, 0)) { - /* found sc */ - mpvparse->last_sc = 0; - } else { - off++; - goto retry; - } - -next: - /* start is fine as of now */ - *skipsize = 0; - /* position a bit further than last sc */ - off++; - /* so now we have start code at start of data; locate next start code */ - off = scan_for_start_codes (&reader, off, GST_BUFFER_SIZE (buf) - off); - - GST_LOG_OBJECT (mpvparse, "next start code at %d", off); - if (off < 0) { - /* if draining, take all */ - if (GST_BASE_PARSE_DRAINING (parse)) { - off = GST_BUFFER_SIZE (buf); - ret = TRUE; - } else { - /* resume scan where we left it */ - mpvparse->last_sc = GST_BUFFER_SIZE (buf) - 4; - /* request best next available */ - *framesize = G_MAXUINT; - return FALSE; + if (ret) { + *skipsize = 0; + fsize = get_frame_size (mpvparse, buf, tmp->next); + break; } - } else { - /* decide whether this startcode ends a frame */ - ret = gst_mpegv_parse_process_sc (mpvparse, buf, off); } - if (ret) { - *framesize = off; +end: + if (fsize > 0) { + *framesize = fsize; + ret = TRUE; + } else if (GST_BASE_PARSE_DRAINING (parse)) { + *framesize = GST_BUFFER_SIZE (buf); + ret = TRUE; } else { - goto next; + /* resume scan where we left it */ + mpvparse->last_sc = GST_BUFFER_SIZE (buf); + /* request best next available */ + *framesize = G_MAXUINT; + ret = FALSE; } + g_list_free_full (mpvparse->typeoffsize, (GDestroyNotify) g_free); + mpvparse->typeoffsize = NULL; + return ret; } @@ -550,22 +526,23 @@ gst_mpegv_parse_update_src_caps (GstMpegvParse * mpvparse) * config data, so we should at least know about version. * If not, it means it has been requested not to drop data, and * upstream and/or app must know what they are doing ... */ - if (G_LIKELY (mpvparse->params.mpeg_version)) + + if (G_LIKELY (mpvparse->mpeg_version)) gst_caps_set_simple (caps, - "mpegversion", G_TYPE_INT, mpvparse->params.mpeg_version, NULL); + "mpegversion", G_TYPE_INT, mpvparse->mpeg_version, NULL); gst_caps_set_simple (caps, "systemstream", G_TYPE_BOOLEAN, FALSE, "parsed", G_TYPE_BOOLEAN, TRUE, NULL); - if (mpvparse->params.width > 0 && mpvparse->params.height > 0) { - gst_caps_set_simple (caps, "width", G_TYPE_INT, mpvparse->params.width, - "height", G_TYPE_INT, mpvparse->params.height, NULL); + if (mpvparse->sequencehdr.width > 0 && mpvparse->sequencehdr.height > 0) { + gst_caps_set_simple (caps, "width", G_TYPE_INT, mpvparse->sequencehdr.width, + "height", G_TYPE_INT, mpvparse->sequencehdr.height, NULL); } /* perhaps we have a framerate */ - if (mpvparse->params.fps_n > 0 && mpvparse->params.fps_d > 0) { - gint fps_num = mpvparse->params.fps_n; - gint fps_den = mpvparse->params.fps_d; + if (mpvparse->sequencehdr.fps_n > 0 && mpvparse->sequencehdr.fps_d > 0) { + gint fps_num = mpvparse->sequencehdr.fps_n; + gint fps_den = mpvparse->sequencehdr.fps_d; GstClockTime latency = gst_util_uint64_scale (GST_SECOND, fps_den, fps_num); gst_caps_set_simple (caps, "framerate", @@ -576,9 +553,9 @@ gst_mpegv_parse_update_src_caps (GstMpegvParse * mpvparse) } /* or pixel-aspect-ratio */ - if (mpvparse->params.par_w && mpvparse->params.par_h > 0) { + if (mpvparse->sequencehdr.par_w && mpvparse->sequencehdr.par_h > 0) { gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION, - mpvparse->params.par_w, mpvparse->params.par_h, NULL); + mpvparse->sequencehdr.par_w, mpvparse->sequencehdr.par_h, NULL); } if (mpvparse->config != NULL) { @@ -586,9 +563,9 @@ gst_mpegv_parse_update_src_caps (GstMpegvParse * mpvparse) GST_TYPE_BUFFER, mpvparse->config, NULL); } - if (mpvparse->params.mpeg_version == 2) { - const guint profile_c = mpvparse->params.profile; - const guint level_c = mpvparse->params.level; + if (mpvparse->mpeg_version == 2) { + const guint profile_c = mpvparse->sequenceext.profile; + const guint level_c = mpvparse->sequenceext.level; const gchar *profile = NULL, *level = NULL; /* * Profile indication - 1 => High, 2 => Spatially Scalable, @@ -657,7 +634,7 @@ gst_mpegv_parse_parse_frame (GstBaseParse * parse, GstBaseParseFrame * frame) gst_mpegv_parse_update_src_caps (mpvparse); - if (G_UNLIKELY (mpvparse->intra_frame)) + if (G_UNLIKELY (mpvparse->pichdr.pic_type == GST_MPEG_VIDEO_PICTURE_TYPE_I)) GST_BUFFER_FLAG_UNSET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); else GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_DELTA_UNIT); @@ -694,8 +671,7 @@ gst_mpegv_parse_set_caps (GstBaseParse * parse, GstCaps * caps) /* best possible parse attempt, * src caps are based on sink caps so it will end up in there * whether sucessful or not */ - gst_mpegv_parse_process_config (mpvparse, GST_BUFFER_DATA (buf), - GST_BUFFER_SIZE (buf)); + gst_mpegv_parse_process_config (mpvparse, buf, GST_BUFFER_SIZE (buf)); } /* let's not interfere and accept regardless of config parsing success */ diff --git a/gst/videoparsers/gstmpegvideoparse.h b/gst/videoparsers/gstmpegvideoparse.h index a3706a41a4..6837a316e4 100644 --- a/gst/videoparsers/gstmpegvideoparse.h +++ b/gst/videoparsers/gstmpegvideoparse.h @@ -1,8 +1,10 @@ /* GStreamer * Copyright (C) <2007> Jan Schmidt * Copyright (C) <2011> Mark Nauwelaerts - * Copyright (C) <2011> Collabora Multimedia + * Copyright (C) <2011> Thibault Saunier + * Copyright (C) <2011> Collabora ltd * Copyright (C) <2011> Nokia Corporation + * Copyright (C) <2011> Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -26,7 +28,7 @@ #include #include -#include "mpegvideoparse.h" +#include G_BEGIN_DECLS @@ -49,15 +51,18 @@ struct _GstMpegvParse { GstBaseParse element; /* parse state */ + GList *typeoffsize; gint last_sc; gint seq_offset; gint pic_offset; - gboolean intra_frame; gboolean update_caps; GstBuffer *config; guint8 profile; - MPEGVParams params; + guint mpeg_version; + GstMpegVideoSequenceHdr sequencehdr; + GstMpegVideoSequenceExt sequenceext; + GstMpegVideoPictureHdr pichdr; /* properties */ gboolean drop; diff --git a/gst/videoparsers/h264parse.c b/gst/videoparsers/h264parse.c deleted file mode 100644 index a556d449cc..0000000000 --- a/gst/videoparsers/h264parse.c +++ /dev/null @@ -1,1046 +0,0 @@ -/* GStreamer H.264 Parser - * Copyright (C) <2010> Mark Nauwelaerts - * Copyright (C) <2010> Collabora Multimedia - * Copyright (C) <2010> Nokia Corporation - * - * Some bits C-c,C-v'ed and s/4/3 from h264parse: - * (C) 2005 Michal Benes - * (C) 2008 Wim Taymans - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "h264parse.h" - -#include - -GST_DEBUG_CATEGORY_EXTERN (h264_parse_debug); -#define GST_CAT_DEFAULT h264_parse_debug - -/* simple bitstream parser, automatically skips over - * emulation_prevention_three_bytes. */ -typedef struct -{ - const guint8 *orig_data; - const guint8 *data; - const guint8 *end; - /* bitpos in the cache of next bit */ - gint head; - /* cached bytes */ - guint64 cache; -} GstNalBs; - -static void -gst_nal_bs_init (GstNalBs * bs, const guint8 * data, guint size) -{ - bs->orig_data = data; - bs->data = data; - bs->end = data + size; - bs->head = 0; - /* fill with something other than 0 to detect emulation prevention bytes */ - bs->cache = 0xffffffff; -} - -static inline void -gst_nal_bs_get_data (GstNalBs * bs, const guint8 ** data, guint * size) -{ - *data = bs->orig_data; - *size = bs->end - bs->orig_data; -} - -static guint32 -gst_nal_bs_read (GstNalBs * bs, guint n) -{ - guint32 res = 0; - gint shift; - - if (n == 0) - return res; - - /* fill up the cache if we need to */ - while (bs->head < n) { - guint8 byte; - gboolean check_three_byte; - - check_three_byte = TRUE; - next_byte: - if (bs->data >= bs->end) { - /* we're at the end, can't produce more than head number of bits */ - n = bs->head; - break; - } - /* get the byte, this can be an emulation_prevention_three_byte that we need - * to ignore. */ - byte = *bs->data++; - if (check_three_byte && byte == 0x03 && ((bs->cache & 0xffff) == 0)) { - /* next byte goes unconditionally to the cache, even if it's 0x03 */ - check_three_byte = FALSE; - goto next_byte; - } - /* shift bytes in cache, moving the head bits of the cache left */ - bs->cache = (bs->cache << 8) | byte; - bs->head += 8; - } - - /* bring the required bits down and truncate */ - if ((shift = bs->head - n) > 0) - res = bs->cache >> shift; - else - res = bs->cache; - - /* mask out required bits */ - if (n < 32) - res &= (1 << n) - 1; - - bs->head = shift; - - return res; -} - -static gboolean -gst_nal_bs_eos (GstNalBs * bs) -{ - return (bs->data >= bs->end) && (bs->head == 0); -} - -/* read unsigned Exp-Golomb code */ -static gint -gst_nal_bs_read_ue (GstNalBs * bs) -{ - gint i = 0; - - while (gst_nal_bs_read (bs, 1) == 0 && !gst_nal_bs_eos (bs) && i < 32) - i++; - - return ((1 << i) - 1 + gst_nal_bs_read (bs, i)); -} - -/* read signed Exp-Golomb code */ -static gint -gst_nal_bs_read_se (GstNalBs * bs) -{ - gint i = 0; - - i = gst_nal_bs_read_ue (bs); - /* (-1)^(i+1) Ceil (i / 2) */ - i = (i + 1) / 2 * (i & 1 ? 1 : -1); - - return i; -} - -/* end parser helper */ - -static void -gst_h264_params_store_nal (GstH264Params * params, GstBuffer ** store, - gint store_size, gint id, GstNalBs * bs) -{ - const guint8 *data; - GstBuffer *buf; - guint size; - - if (id >= store_size) { - GST_DEBUG_OBJECT (params->el, - "unable to store nal, id out-of-range %d", id); - return; - } - - gst_nal_bs_get_data (bs, &data, &size); - buf = gst_buffer_new_and_alloc (size); - memcpy (GST_BUFFER_DATA (buf), data, size); - - if (store[id]) - gst_buffer_unref (store[id]); - - store[id] = buf; -} - -static GstH264ParamsSPS * -gst_h264_params_get_sps (GstH264Params * params, guint8 sps_id, gboolean set) -{ - GstH264ParamsSPS *sps; - - g_return_val_if_fail (params != NULL, NULL); - - if (G_UNLIKELY (sps_id >= MAX_SPS_COUNT)) { - GST_WARNING_OBJECT (params->el, - "requested sps_id=%04x out of range", sps_id); - return NULL; - } - - sps = ¶ms->sps_buffers[sps_id]; - if (set) { - if (sps->valid) { - params->sps = sps; - } else { - GST_WARNING_OBJECT (params->el, "invalid sps not selected"); - params->sps = NULL; - sps = NULL; - } - } - - return sps; -} - -static GstH264ParamsPPS * -gst_h264_params_get_pps (GstH264Params * params, guint8 pps_id, gboolean set) -{ - GstH264ParamsPPS *pps; - - g_return_val_if_fail (params != NULL, NULL); - - pps = ¶ms->pps_buffers[pps_id]; - if (set) { - if (pps->valid) { - params->pps = pps; - } else { - GST_WARNING_OBJECT (params->el, "invalid pps not selected"); - params->pps = NULL; - pps = NULL; - } - } - - return pps; -} - -static gboolean -gst_h264_params_decode_sps_vui_hrd (GstH264Params * params, - GstH264ParamsSPS * sps, GstNalBs * bs) -{ - gint sched_sel_idx; - - sps->cpb_cnt_minus1 = gst_nal_bs_read_ue (bs); - if (sps->cpb_cnt_minus1 > 31U) { - GST_WARNING_OBJECT (params->el, "cpb_cnt_minus1 = %d out of range", - sps->cpb_cnt_minus1); - return FALSE; - } - - /* bit_rate_scale */ - gst_nal_bs_read (bs, 4); - /* cpb_size_scale */ - gst_nal_bs_read (bs, 4); - - for (sched_sel_idx = 0; sched_sel_idx <= sps->cpb_cnt_minus1; sched_sel_idx++) { - /* bit_rate_value_minus1 */ - gst_nal_bs_read_ue (bs); - /* cpb_size_value_minus1 */ - gst_nal_bs_read_ue (bs); - /* cbr_flag */ - gst_nal_bs_read (bs, 1); - } - - sps->initial_cpb_removal_delay_length_minus1 = gst_nal_bs_read (bs, 5); - sps->cpb_removal_delay_length_minus1 = gst_nal_bs_read (bs, 5); - sps->dpb_output_delay_length_minus1 = gst_nal_bs_read (bs, 5); - sps->time_offset_length_minus1 = gst_nal_bs_read (bs, 5); - - return TRUE; -} - -static gboolean -gst_h264_params_decode_sps_vui (GstH264Params * params, GstH264ParamsSPS * sps, - GstNalBs * bs) -{ - if (G_UNLIKELY (!sps)) - return FALSE; - - /* aspect_ratio_info_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* aspect_ratio_idc */ - if (gst_nal_bs_read (bs, 8) == 255) { - /* Extended_SAR */ - /* sar_width */ - gst_nal_bs_read (bs, 16); - /* sar_height */ - gst_nal_bs_read (bs, 16); - } - } - - /* overscan_info_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* overscan_appropriate_flag */ - gst_nal_bs_read (bs, 1); - } - - /* video_signal_type_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* video_format */ - gst_nal_bs_read (bs, 3); - /* video_full_range_flag */ - gst_nal_bs_read (bs, 1); - - /* colour_description_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* colour_primaries */ - gst_nal_bs_read (bs, 8); - /* transfer_characteristics */ - gst_nal_bs_read (bs, 8); - /* matrix_coefficients */ - gst_nal_bs_read (bs, 8); - } - } - - /* chroma_loc_info_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* chroma_sample_loc_type_top_field */ - gst_nal_bs_read_ue (bs); - /* chroma_sample_loc_type_bottom_field */ - gst_nal_bs_read_ue (bs); - } - - sps->timing_info_present_flag = gst_nal_bs_read (bs, 1); - if (sps->timing_info_present_flag) { - guint32 num_units_in_tick = gst_nal_bs_read (bs, 32); - guint32 time_scale = gst_nal_bs_read (bs, 32); - - /* If any of these parameters = 0, discard all timing_info */ - if (time_scale == 0) { - GST_WARNING_OBJECT (params->el, - "time_scale = 0 detected in stream (incompliant to H.264 E.2.1)." - " Discarding related info."); - } else if (num_units_in_tick == 0) { - GST_WARNING_OBJECT (params->el, - "num_units_in_tick = 0 detected in stream (incompliant to H.264 E.2.1)." - " Discarding related info."); - } else { - sps->num_units_in_tick = num_units_in_tick; - sps->time_scale = time_scale; - sps->fixed_frame_rate_flag = gst_nal_bs_read (bs, 1); - GST_LOG_OBJECT (params->el, "timing info: dur=%d/%d fixed=%d", - num_units_in_tick, time_scale, sps->fixed_frame_rate_flag); - } - } - - sps->nal_hrd_parameters_present_flag = gst_nal_bs_read (bs, 1); - if (sps->nal_hrd_parameters_present_flag) { - gst_h264_params_decode_sps_vui_hrd (params, sps, bs); - } - sps->vcl_hrd_parameters_present_flag = gst_nal_bs_read (bs, 1); - if (sps->vcl_hrd_parameters_present_flag) { - gst_h264_params_decode_sps_vui_hrd (params, sps, bs); - } - if (sps->nal_hrd_parameters_present_flag - || sps->vcl_hrd_parameters_present_flag) { - gst_nal_bs_read (bs, 1); /* low_delay_hrd_flag */ - } - - sps->pic_struct_present_flag = gst_nal_bs_read (bs, 1); - - /* derive framerate */ - /* FIXME verify / also handle other cases */ - if (sps->fixed_frame_rate_flag && sps->frame_mbs_only_flag && - !sps->pic_struct_present_flag) { - sps->fps_num = sps->time_scale; - sps->fps_den = sps->num_units_in_tick; - /* picture is a frame = 2 fields */ - sps->fps_den *= 2; - GST_LOG_OBJECT (params->el, "framerate %d/%d", sps->fps_num, sps->fps_den); - } - - return TRUE; -} - -static gboolean -gst_h264_params_decode_sps (GstH264Params * params, GstNalBs * bs) -{ - guint8 profile_idc, level_idc; - guint8 sps_id; - GstH264ParamsSPS *sps = NULL; - guint subwc[] = { 1, 2, 2, 1 }; - guint subhc[] = { 1, 2, 1, 1 }; - guint chroma; - guint fc_top, fc_bottom, fc_left, fc_right; - gint width, height; - - profile_idc = gst_nal_bs_read (bs, 8); - /* constraint_set0_flag */ - gst_nal_bs_read (bs, 1); - /* constraint_set1_flag */ - gst_nal_bs_read (bs, 1); - /* constraint_set1_flag */ - gst_nal_bs_read (bs, 1); - /* constraint_set1_flag */ - gst_nal_bs_read (bs, 1); - /* reserved */ - gst_nal_bs_read (bs, 4); - level_idc = gst_nal_bs_read (bs, 8); - - sps_id = gst_nal_bs_read_ue (bs); - sps = gst_h264_params_get_sps (params, sps_id, FALSE); - if (G_UNLIKELY (sps == NULL)) - return FALSE; - - gst_h264_params_store_nal (params, params->sps_nals, MAX_SPS_COUNT, sps_id, - bs); - - /* could be redefined mid stream, arrange for clear state */ - memset (sps, 0, sizeof (*sps)); - - GST_LOG_OBJECT (params->el, "sps id %d", sps_id); - sps->valid = TRUE; - /* validate and force activate this one if it is the first SPS we see */ - if (params->sps == NULL) - params->sps = sps; - - sps->profile_idc = profile_idc; - sps->level_idc = level_idc; - - if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 - || profile_idc == 244 || profile_idc == 44 || - profile_idc == 83 || profile_idc == 86) { - gint scp_flag = 0; - - /* chroma_format_idc */ - if ((chroma = gst_nal_bs_read_ue (bs)) == 3) { - /* separate_colour_plane_flag */ - sps->scp_flag = gst_nal_bs_read (bs, 1); - } - /* bit_depth_luma_minus8 */ - gst_nal_bs_read_ue (bs); - /* bit_depth_chroma_minus8 */ - gst_nal_bs_read_ue (bs); - /* qpprime_y_zero_transform_bypass_flag */ - gst_nal_bs_read (bs, 1); - /* seq_scaling_matrix_present_flag */ - if (gst_nal_bs_read (bs, 1)) { - gint i, j, m, d; - - m = (chroma != 3) ? 8 : 12; - for (i = 0; i < m; i++) { - /* seq_scaling_list_present_flag[i] */ - d = gst_nal_bs_read (bs, 1); - if (d) { - gint lastScale = 8, nextScale = 8, deltaScale; - - j = (i < 6) ? 16 : 64; - for (; j > 0; j--) { - if (nextScale != 0) { - deltaScale = gst_nal_bs_read_se (bs); - nextScale = (lastScale + deltaScale + 256) % 256; - } - if (nextScale != 0) - lastScale = nextScale; - } - } - } - } - if (scp_flag) - chroma = 0; - } else { - /* inferred value */ - chroma = 1; - } - - /* between 0 and 12 */ - sps->log2_max_frame_num_minus4 = gst_nal_bs_read_ue (bs); - if (sps->log2_max_frame_num_minus4 > 12) { - GST_WARNING_OBJECT (params->el, - "log2_max_frame_num_minus4 = %d out of range" " [0,12]", - sps->log2_max_frame_num_minus4); - return FALSE; - } - - sps->pic_order_cnt_type = gst_nal_bs_read_ue (bs); - if (sps->pic_order_cnt_type == 0) { - sps->log2_max_pic_order_cnt_lsb_minus4 = gst_nal_bs_read_ue (bs); - } else if (sps->pic_order_cnt_type == 1) { - gint d; - - /* delta_pic_order_always_zero_flag */ - gst_nal_bs_read (bs, 1); - /* offset_for_non_ref_pic */ - gst_nal_bs_read_ue (bs); - /* offset_for_top_to_bottom_field */ - gst_nal_bs_read_ue (bs); - /* num_ref_frames_in_pic_order_cnt_cycle */ - d = gst_nal_bs_read_ue (bs); - for (; d > 0; d--) { - /* offset_for_ref_frame[i] */ - gst_nal_bs_read_ue (bs); - } - } - - /* max_num_ref_frames */ - gst_nal_bs_read_ue (bs); - /* gaps_in_frame_num_value_allowed_flag */ - gst_nal_bs_read (bs, 1); - /* pic_width_in_mbs_minus1 */ - width = gst_nal_bs_read_ue (bs); - /* pic_height_in_map_units_minus1 */ - height = gst_nal_bs_read_ue (bs); - - sps->frame_mbs_only_flag = gst_nal_bs_read (bs, 1); - if (!sps->frame_mbs_only_flag) { - /* mb_adaptive_frame_field_flag */ - gst_nal_bs_read (bs, 1); - } - - width++; - width *= 16; - height++; - height *= 16 * (2 - sps->frame_mbs_only_flag); - - /* direct_8x8_inference_flag */ - gst_nal_bs_read (bs, 1); - /* frame_cropping_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* frame_crop_left_offset */ - fc_left = gst_nal_bs_read_ue (bs); - /* frame_crop_right_offset */ - fc_right = gst_nal_bs_read_ue (bs); - /* frame_crop_top_offset */ - fc_top = gst_nal_bs_read_ue (bs); - /* frame_crop_bottom_offset */ - fc_bottom = gst_nal_bs_read_ue (bs); - } else { - fc_left = fc_right = fc_top = fc_bottom = 0; - } - - GST_LOG_OBJECT (params->el, "decoding SPS: profile_idc = %d, " - "level_idc = %d, sps_id = %d, pic_order_cnt_type = %d, " - "frame_mbs_only_flag = %d", - sps->profile_idc, sps->level_idc, sps_id, sps->pic_order_cnt_type, - sps->frame_mbs_only_flag); - - /* calculate width and height */ - GST_LOG_OBJECT (params->el, "initial width=%d, height=%d", width, height); - GST_LOG_OBJECT (params->el, "crop (%d,%d)(%d,%d)", - fc_left, fc_top, fc_right, fc_bottom); - if (chroma > 3) { - GST_LOG_OBJECT (params->el, "chroma=%d in SPS is out of range", chroma); - return FALSE; - } - width -= (fc_left + fc_right) * subwc[chroma]; - height -= - (fc_top + fc_bottom) * subhc[chroma] * (2 - sps->frame_mbs_only_flag); - if (width < 0 || height < 0) { - GST_WARNING_OBJECT (params->el, "invalid width/height in SPS"); - return FALSE; - } - GST_LOG_OBJECT (params->el, "final width=%u, height=%u", width, height); - sps->width = width; - sps->height = height; - - sps->vui_parameters_present_flag = gst_nal_bs_read (bs, 1); - if (sps->vui_parameters_present_flag) { - /* discard parsing problem */ - gst_h264_params_decode_sps_vui (params, sps, bs); - } - - return TRUE; -} - -static gboolean -gst_h264_params_decode_pps (GstH264Params * params, GstNalBs * bs) -{ - gint pps_id; - GstH264ParamsPPS *pps = NULL; - - pps_id = gst_nal_bs_read_ue (bs); - if (G_UNLIKELY (pps_id >= MAX_PPS_COUNT)) { - GST_WARNING_OBJECT (params->el, - "requested pps_id=%04x out of range", pps_id); - return FALSE; - } - - - pps = gst_h264_params_get_pps (params, pps_id, FALSE); - if (G_UNLIKELY (pps == NULL)) - return FALSE; - - /* validate and set */ - pps->valid = TRUE; - params->pps = pps; - - gst_h264_params_store_nal (params, params->pps_nals, MAX_PPS_COUNT, pps_id, - bs); - - pps->sps_id = gst_nal_bs_read_ue (bs); - GST_LOG_OBJECT (params->el, "pps %d referencing sps %d", pps_id, pps->sps_id); - - /* activate referenced sps */ - if (!gst_h264_params_get_sps (params, pps->sps_id, TRUE)) - return FALSE; - - /* not parsing the rest for the time being */ - return TRUE; -} - -static gboolean -gst_h264_params_decode_sei_buffering_period (GstH264Params * params, - GstNalBs * bs) -{ -#ifdef EXTRA_PARSE - guint8 sps_id; - gint sched_sel_idx; - GstH264ParamsSPS *sps; - - sps_id = gst_nal_bs_read_ue (bs); - sps = gst_h264_params_get_sps (params, sps_id, TRUE); - if (G_UNLIKELY (sps == NULL)) - return FALSE; - - if (sps->nal_hrd_parameters_present_flag) { - for (sched_sel_idx = 0; sched_sel_idx <= sps->cpb_cnt_minus1; - sched_sel_idx++) { - params->initial_cpb_removal_delay[sched_sel_idx] - = gst_nal_bs_read (bs, - sps->initial_cpb_removal_delay_length_minus1 + 1); - /* initial_cpb_removal_delay_offset */ - gst_nal_bs_read (bs, sps->initial_cpb_removal_delay_length_minus1 + 1); - } - } - - if (sps->vcl_hrd_parameters_present_flag) { - for (sched_sel_idx = 0; sched_sel_idx <= sps->cpb_cnt_minus1; - sched_sel_idx++) { - params->initial_cpb_removal_delay[sched_sel_idx] - = gst_nal_bs_read (bs, - sps->initial_cpb_removal_delay_length_minus1 + 1); - /* initial_cpb_removal_delay_offset */ - gst_nal_bs_read (bs, sps->initial_cpb_removal_delay_length_minus1 + 1); - } - } -#endif - - if (params->ts_trn_nb == GST_CLOCK_TIME_NONE || - params->dts == GST_CLOCK_TIME_NONE) - params->ts_trn_nb = 0; - else - params->ts_trn_nb = params->dts; - - GST_LOG_OBJECT (params->el, - "new buffering period; ts_trn_nb updated: %" GST_TIME_FORMAT, - GST_TIME_ARGS (params->ts_trn_nb)); - - return 0; -} - -static gboolean -gst_h264_params_decode_sei_picture_timing (GstH264Params * params, - GstNalBs * bs) -{ - GstH264ParamsSPS *sps = params->sps; - - if (sps == NULL) { - GST_WARNING_OBJECT (params->el, - "SPS == NULL; delayed decoding of picture timing info not implemented "); - return FALSE; - } - - if (sps->nal_hrd_parameters_present_flag - || sps->vcl_hrd_parameters_present_flag) { - params->sei_cpb_removal_delay = - gst_nal_bs_read (bs, sps->cpb_removal_delay_length_minus1 + 1); - /* sei_dpb_output_delay */ - gst_nal_bs_read (bs, sps->dpb_output_delay_length_minus1 + 1); - } - - if (sps->pic_struct_present_flag) { -#ifdef EXTRA_PARSE - /* pic_struct to NumClockTS lookup table */ - static const guint8 sei_num_clock_ts_table[9] = - { 1, 1, 1, 2, 2, 3, 3, 2, 3 }; - guint i, num_clock_ts; - guint sei_ct_type = 0; -#endif - - params->sei_pic_struct = gst_nal_bs_read (bs, 4); - GST_LOG_OBJECT (params, "pic_struct:%d", params->sei_pic_struct); - if (params->sei_pic_struct > SEI_PIC_STRUCT_FRAME_TRIPLING) - return FALSE; - -#ifdef EXTRA_PARSE - num_clock_ts = sei_num_clock_ts_table[params->sei_pic_struct]; - - for (i = 0; i < num_clock_ts; i++) { - /* clock_timestamp_flag */ - if (gst_nal_bs_read (bs, 1)) { - guint full_timestamp_flag; - - sei_ct_type |= 1 << gst_nal_bs_read (bs, 2); - /* nuit_field_based_flag */ - gst_nal_bs_read (bs, 1); - /* counting_type */ - gst_nal_bs_read (bs, 5); - full_timestamp_flag = gst_nal_bs_read (bs, 1); - /* discontinuity_flag */ - gst_nal_bs_read (bs, 1); - /* cnt_dropped_flag */ - gst_nal_bs_read (bs, 1); - /* n_frames */ - gst_nal_bs_read (bs, 8); - if (full_timestamp_flag) { - /* seconds_value 0..59 */ - gst_nal_bs_read (bs, 6); - /* minutes_value 0..59 */ - gst_nal_bs_read (bs, 6); - /* hours_value 0..23 */ - gst_nal_bs_read (bs, 5); - } else { - /* seconds_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* seconds_value range 0..59 */ - gst_nal_bs_read (bs, 6); - /* minutes_flag */ - if (gst_nal_bs_read (bs, 1)) { - /* minutes_value 0..59 */ - gst_nal_bs_read (bs, 6); - /* hours_flag */ - if (gst_nal_bs_read (bs, 1)) - /* hours_value 0..23 */ - gst_nal_bs_read (bs, 5); - } - } - } - if (sps->time_offset_length_minus1 >= 0) { - /* time_offset */ - gst_nal_bs_read (bs, sps->time_offset_length_minus1 + 1); - } - } - } - - GST_LOG_OBJECT (params, "ct_type:%X", sei_ct_type); -#endif - } - - return TRUE; -} - -static gboolean -gst_h264_params_decode_sei (GstH264Params * params, GstNalBs * bs) -{ - guint8 tmp; - GstH264ParamsSEIPayloadType payloadType = 0; - gint8 payloadSize = 0; - - do { - tmp = gst_nal_bs_read (bs, 8); - payloadType += tmp; - } while (tmp == 255); - do { - tmp = gst_nal_bs_read (bs, 8); - payloadSize += tmp; - } while (tmp == 255); - - GST_LOG_OBJECT (params->el, - "SEI message received: payloadType = %d, payloadSize = %d bytes", - payloadType, payloadSize); - - switch (payloadType) { - case SEI_BUF_PERIOD: - if (!gst_h264_params_decode_sei_buffering_period (params, bs)) - return FALSE; - break; - case SEI_PIC_TIMING: - /* TODO: According to H264 D2.2 Note1, it might be the case that the - * picture timing SEI message is encountered before the corresponding SPS - * is specified. Need to hold down the message and decode it later. */ - if (!gst_h264_params_decode_sei_picture_timing (params, bs)) - return FALSE; - break; - default: - GST_LOG_OBJECT (params->el, - "SEI message of payloadType = %d is received but not parsed", - payloadType); - break; - } - - return TRUE; -} - -static gboolean -gst_h264_params_decode_slice_header (GstH264Params * params, GstNalBs * bs) -{ - GstH264ParamsSPS *sps; - GstH264ParamsPPS *pps; - guint8 pps_id; - - params->first_mb_in_slice = gst_nal_bs_read_ue (bs); - params->slice_type = gst_nal_bs_read_ue (bs); - - pps_id = gst_nal_bs_read_ue (bs); - GST_LOG_OBJECT (params->el, "slice header references pps id %d", pps_id); - pps = gst_h264_params_get_pps (params, pps_id, TRUE); - if (G_UNLIKELY (pps == NULL)) - return FALSE; - sps = gst_h264_params_get_sps (params, pps->sps_id, TRUE); - if (G_UNLIKELY (sps == NULL)) - return FALSE; - - if (sps->scp_flag) { - /* colour_plane_id */ - gst_nal_bs_read (bs, 2); - } - - /* frame num */ - gst_nal_bs_read (bs, sps->log2_max_pic_order_cnt_lsb_minus4 + 4); - - if (!sps->frame_mbs_only_flag) { - params->field_pic_flag = gst_nal_bs_read (bs, 1); - if (params->field_pic_flag) - params->bottom_field_flag = gst_nal_bs_read (bs, 1); - } - - /* not parsing the rest for the time being */ - return TRUE; -} - -/* only payload in @data */ -gboolean -gst_h264_params_parse_nal (GstH264Params * params, guint8 * data, gint size) -{ - GstH264ParamsNalUnitType nal_type; - GstNalBs bs; - gint nal_ref_idc; - gboolean res = TRUE; - - g_return_val_if_fail (params != NULL, FALSE); - g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (size != 0, FALSE); - - nal_type = (data[0] & 0x1f); - nal_ref_idc = (data[0] & 0x60) >> 5; - - GST_LOG_OBJECT (params->el, "NAL type: %d, ref_idc: %d", nal_type, - nal_ref_idc); - - gst_nal_bs_init (&bs, data + 1, size - 1); - /* optimality HACK */ - bs.orig_data = data; - - /* first parse some things needed to get to the frame type */ - switch (nal_type) { - case NAL_SLICE: - case NAL_SLICE_DPA: - case NAL_SLICE_DPB: - case NAL_SLICE_DPC: - case NAL_SLICE_IDR: - { - gint first_mb_in_slice, slice_type; - - gst_h264_params_decode_slice_header (params, &bs); - first_mb_in_slice = params->first_mb_in_slice; - slice_type = params->slice_type; - - GST_LOG_OBJECT (params->el, "first MB: %d, slice type: %d", - first_mb_in_slice, slice_type); - - switch (slice_type) { - case 0: - case 5: - case 3: - case 8: /* SP */ - /* P frames */ - GST_LOG_OBJECT (params->el, "we have a P slice"); - break; - case 1: - case 6: - /* B frames */ - GST_LOG_OBJECT (params->el, "we have a B slice"); - break; - case 2: - case 7: - case 4: - case 9: - /* I frames */ - GST_LOG_OBJECT (params->el, "we have an I slice"); - break; - } - break; - } - case NAL_SEI: - GST_LOG_OBJECT (params->el, "SEI NAL"); - res = gst_h264_params_decode_sei (params, &bs); - break; - case NAL_SPS: - GST_LOG_OBJECT (params->el, "SPS NAL"); - res = gst_h264_params_decode_sps (params, &bs); - break; - case NAL_PPS: - GST_LOG_OBJECT (params->el, "PPS NAL"); - res = gst_h264_params_decode_pps (params, &bs); - break; - case NAL_AU_DELIMITER: - GST_LOG_OBJECT (params->el, "AU delimiter NAL"); - break; - default: - GST_LOG_OBJECT (params->el, "unparsed NAL"); - break; - } - - return res; -} - -void -gst_h264_params_get_timestamp (GstH264Params * params, - GstClockTime * out_ts, GstClockTime * out_dur, gboolean frame) -{ - GstH264ParamsSPS *sps = params->sps; - GstClockTime upstream; - gint duration = 1; - - g_return_if_fail (out_dur != NULL); - g_return_if_fail (out_ts != NULL); - - upstream = *out_ts; - - if (!frame) { - GST_LOG_OBJECT (params->el, "no frame data -> 0 duration"); - *out_dur = 0; - goto exit; - } else { - *out_ts = upstream; - } - - if (!sps) { - GST_DEBUG_OBJECT (params->el, "referred SPS invalid"); - goto exit; - } else if (!sps->timing_info_present_flag) { - GST_DEBUG_OBJECT (params->el, - "unable to compute timestamp: timing info not present"); - goto exit; - } else if (sps->time_scale == 0) { - GST_DEBUG_OBJECT (params->el, - "unable to compute timestamp: time_scale = 0 " - "(this is forbidden in spec; bitstream probably contains error)"); - goto exit; - } - - if (sps->pic_struct_present_flag && params->sei_pic_struct != (guint8) - 1) { - /* Note that when h264parse->sei_pic_struct == -1 (unspecified), there - * are ways to infer its value. This is related to computing the - * TopFieldOrderCnt and BottomFieldOrderCnt, which looks - * complicated and thus not implemented for the time being. Yet - * the value we have here is correct for many applications - */ - switch (params->sei_pic_struct) { - case SEI_PIC_STRUCT_TOP_FIELD: - case SEI_PIC_STRUCT_BOTTOM_FIELD: - duration = 1; - break; - case SEI_PIC_STRUCT_FRAME: - case SEI_PIC_STRUCT_TOP_BOTTOM: - case SEI_PIC_STRUCT_BOTTOM_TOP: - duration = 2; - break; - case SEI_PIC_STRUCT_TOP_BOTTOM_TOP: - case SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM: - duration = 3; - break; - case SEI_PIC_STRUCT_FRAME_DOUBLING: - duration = 4; - break; - case SEI_PIC_STRUCT_FRAME_TRIPLING: - duration = 6; - break; - default: - GST_DEBUG_OBJECT (params, - "h264parse->sei_pic_struct of unknown value %d. Not parsed", - params->sei_pic_struct); - break; - } - } else { - duration = params->field_pic_flag ? 1 : 2; - } - - GST_LOG_OBJECT (params->el, "frame tick duration %d", duration); - - /* - * h264parse.264 C.1.2 Timing of coded picture removal (equivalent to DTS): - * Tr,n(0) = initial_cpb_removal_delay[ SchedSelIdx ] / 90000 - * Tr,n(n) = Tr,n(nb) + Tc * cpb_removal_delay(n) - * where - * Tc = num_units_in_tick / time_scale - */ - - if (params->ts_trn_nb != GST_CLOCK_TIME_NONE) { - GST_LOG_OBJECT (params->el, "buffering based ts"); - /* buffering period is present */ - if (upstream != GST_CLOCK_TIME_NONE) { - /* If upstream timestamp is valid, we respect it and adjust current - * reference point */ - params->ts_trn_nb = upstream - - (GstClockTime) gst_util_uint64_scale_int - (params->sei_cpb_removal_delay * GST_SECOND, - sps->num_units_in_tick, sps->time_scale); - } else { - /* If no upstream timestamp is given, we write in new timestamp */ - upstream = params->dts = params->ts_trn_nb + - (GstClockTime) gst_util_uint64_scale_int - (params->sei_cpb_removal_delay * GST_SECOND, - sps->num_units_in_tick, sps->time_scale); - } - } else { - GstClockTime dur; - - GST_LOG_OBJECT (params->el, "duration based ts"); - /* naive method: no removal delay specified - * track upstream timestamp and provide best guess frame duration */ - dur = gst_util_uint64_scale_int (duration * GST_SECOND, - sps->num_units_in_tick, sps->time_scale); - /* sanity check */ - if (dur < GST_MSECOND) { - GST_DEBUG_OBJECT (params->el, "discarding dur %" GST_TIME_FORMAT, - GST_TIME_ARGS (dur)); - } else { - *out_dur = dur; - } - } - -exit: - if (GST_CLOCK_TIME_IS_VALID (upstream)) - *out_ts = params->dts = upstream; - - if (GST_CLOCK_TIME_IS_VALID (*out_dur) && - GST_CLOCK_TIME_IS_VALID (params->dts)) - params->dts += *out_dur; -} - -void -gst_h264_params_create (GstH264Params ** _params, GstElement * element) -{ - GstH264Params *params; - - g_return_if_fail (_params != NULL); - - params = g_new0 (GstH264Params, 1); - params->el = element; - - params->dts = GST_CLOCK_TIME_NONE; - params->ts_trn_nb = GST_CLOCK_TIME_NONE; - - *_params = params; -} - -void -gst_h264_params_free (GstH264Params * params) -{ - gint i; - - g_return_if_fail (params != NULL); - - for (i = 0; i < MAX_SPS_COUNT; i++) - gst_buffer_replace (¶ms->sps_nals[i], NULL); - for (i = 0; i < MAX_PPS_COUNT; i++) - gst_buffer_replace (¶ms->pps_nals[i], NULL); - - g_free (params); -} diff --git a/gst/videoparsers/h264parse.h b/gst/videoparsers/h264parse.h deleted file mode 100644 index 517c8542b2..0000000000 --- a/gst/videoparsers/h264parse.h +++ /dev/null @@ -1,186 +0,0 @@ -/* GStreamer H.264 Parser - * Copyright (C) <2010> Mark Nauwelaerts - * Copyright (C) <2010> Collabora Multimedia - * Copyright (C) <2010> Nokia Corporation - * - * Some bits C-c,C-v'ed and s/4/3 from h264parse: - * (C) 2005 Michal Benes - * (C) 2008 Wim Taymans - * - * 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_H264_PARAMS_H__ -#define __GST_H264_PARAMS_H__ - -#include - -G_BEGIN_DECLS - -typedef enum -{ - NAL_UNKNOWN = 0, - NAL_SLICE = 1, - NAL_SLICE_DPA = 2, - NAL_SLICE_DPB = 3, - NAL_SLICE_DPC = 4, - NAL_SLICE_IDR = 5, - NAL_SEI = 6, - NAL_SPS = 7, - NAL_PPS = 8, - NAL_AU_DELIMITER = 9, - NAL_SEQ_END = 10, - NAL_STREAM_END = 11, - NAL_FILTER_DATA = 12 -} GstH264ParamsNalUnitType; - -/* SEI type */ -typedef enum -{ - SEI_BUF_PERIOD = 0, - SEI_PIC_TIMING = 1 - /* and more... */ -} GstH264ParamsSEIPayloadType; - -/* SEI pic_struct type */ -typedef enum -{ - SEI_PIC_STRUCT_FRAME = 0, - SEI_PIC_STRUCT_TOP_FIELD = 1, - SEI_PIC_STRUCT_BOTTOM_FIELD = 2, - SEI_PIC_STRUCT_TOP_BOTTOM = 3, - SEI_PIC_STRUCT_BOTTOM_TOP = 4, - SEI_PIC_STRUCT_TOP_BOTTOM_TOP = 5, - SEI_PIC_STRUCT_BOTTOM_TOP_BOTTOM = 6, - SEI_PIC_STRUCT_FRAME_DOUBLING = 7, - SEI_PIC_STRUCT_FRAME_TRIPLING = 8 -} GstH264ParamsSEIPicStructType; - -typedef struct _GstH264Params GstH264Params; -typedef struct _GstH264ParamsSPS GstH264ParamsSPS; -typedef struct _GstH264ParamsPPS GstH264ParamsPPS; - -#define MAX_SPS_COUNT 32 -#define MAX_PPS_COUNT 256 - -/* SPS: sequential parameter sets */ -struct _GstH264ParamsSPS -{ - gboolean valid; - - /* raw values */ - guint8 profile_idc; - guint8 level_idc; - - guint8 sps_id; - - guint8 pic_order_cnt_type; - - guint8 log2_max_frame_num_minus4; - gboolean frame_mbs_only_flag; - guint8 log2_max_pic_order_cnt_lsb_minus4; - - gboolean frame_cropping_flag; - gboolean scp_flag; - - /* VUI parameters */ - gboolean vui_parameters_present_flag; - - gboolean timing_info_present_flag; - guint32 num_units_in_tick; - guint32 time_scale; - gboolean fixed_frame_rate_flag; - - gboolean nal_hrd_parameters_present_flag; - gboolean vcl_hrd_parameters_present_flag; - - /* hrd parameters */ - guint8 cpb_cnt_minus1; - gint initial_cpb_removal_delay_length_minus1; - gint cpb_removal_delay_length_minus1; - gint dpb_output_delay_length_minus1; - gboolean time_offset_length_minus1; - - gboolean pic_struct_present_flag; - - /* ... and probably more ... */ - - /* derived values */ - gint width, height; - gint fps_num, fps_den; -}; - -/* PPS: pic parameter sets */ -struct _GstH264ParamsPPS -{ - gboolean valid; - - /* raw values */ - guint8 pps_id; - guint8 sps_id; -}; - -struct _GstH264Params -{ - /* debug purposes */ - GstElement *el; - - /* SPS: sequential parameter set */ - GstH264ParamsSPS sps_buffers[MAX_SPS_COUNT]; - /* current SPS; most recent one in stream or referenced by PPS */ - GstH264ParamsSPS *sps; - /* PPS: sequential parameter set */ - GstH264ParamsPPS pps_buffers[MAX_PPS_COUNT]; - /* current PPS; most recent one in stream */ - GstH264ParamsPPS *pps; - - /* extracted from slice header or otherwise relevant nal */ - guint8 first_mb_in_slice; - guint8 slice_type; - gboolean field_pic_flag; - gboolean bottom_field_flag; - - /* SEI: supplemental enhancement messages */ -#ifdef EXTRA_PARSE - /* buffering period */ - guint32 initial_cpb_removal_delay[32]; -#endif - /* picture timing */ - guint32 sei_cpb_removal_delay; - guint8 sei_pic_struct; - /* And more... */ - - /* cached timestamps */ - /* (trying to) track upstream dts and interpolate */ - GstClockTime dts; - /* dts at start of last buffering period */ - GstClockTime ts_trn_nb; - - /* collected SPS and PPS NALUs */ - GstBuffer *sps_nals[MAX_SPS_COUNT]; - GstBuffer *pps_nals[MAX_PPS_COUNT]; -}; - -gboolean gst_h264_params_parse_nal (GstH264Params * params, guint8 * nal, gint size); -void gst_h264_params_get_timestamp (GstH264Params * params, - GstClockTime * out_ts, GstClockTime * out_dur, - gboolean frame); -void gst_h264_params_create (GstH264Params ** _params, GstElement * element); -void gst_h264_params_free (GstH264Params * params); - - -G_END_DECLS -#endif diff --git a/gst/videoparsers/mpegvideoparse.c b/gst/videoparsers/mpegvideoparse.c deleted file mode 100644 index e85d77bdbb..0000000000 --- a/gst/videoparsers/mpegvideoparse.c +++ /dev/null @@ -1,274 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Jan Schmidt - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "mpegvideoparse.h" - -#include -#include - -GST_DEBUG_CATEGORY_EXTERN (mpegv_parse_debug); -#define GST_CAT_DEFAULT mpegv_parse_debug - - -#define GET_BITS(b, num, bits) G_STMT_START { \ - if (!gst_bit_reader_get_bits_uint32(b, bits, num)) \ - goto failed; \ - GST_TRACE ("parsed %d bits: %d", num, *(bits)); \ -} G_STMT_END - -#define MARKER_BIT(b) G_STMT_START { \ - guint32 i; \ - GET_BITS(b, 1, &i); \ - if (i != 0x1) \ - goto failed; \ -} G_STMT_END - -static inline gboolean -find_start_code (GstBitReader * b) -{ - guint32 bits; - - /* 0 bits until byte aligned */ - while (b->bit != 0) { - GET_BITS (b, 1, &bits); - } - - /* 0 bytes until startcode */ - while (gst_bit_reader_peek_bits_uint32 (b, &bits, 32)) { - if (bits >> 8 == 0x1) { - return TRUE; - } else { - gst_bit_reader_skip (b, 8); - } - } - - return FALSE; - -failed: - return FALSE; -} - -static gboolean -gst_mpeg_video_params_parse_extension (MPEGVParams * params, GstBitReader * br) -{ - guint32 bits; - - /* double-check */ - GET_BITS (br, 32, &bits); - if (bits != 0x100 + MPEG_PACKET_EXTENSION) - goto failed; - - /* extension_start_code identifier */ - GET_BITS (br, 4, &bits); - - /* profile_and_level_indication */ - GET_BITS (br, 4, &bits); - params->profile = bits; - GET_BITS (br, 4, &bits); - params->level = bits; - - /* progressive_sequence */ - GET_BITS (br, 1, &bits); - params->progressive = bits; - - /* chroma_format */ - GET_BITS (br, 2, &bits); - - /* horizontal_size_extension */ - GET_BITS (br, 2, &bits); - params->width += (bits << 12); - /* vertical_size_extension */ - GET_BITS (br, 2, &bits); - params->height += (bits << 12); - - /* bit_rate_extension */ - GET_BITS (br, 12, &bits); - if (params->bitrate) - params->bitrate += (bits << 18) * 400; - /* marker_bit */ - MARKER_BIT (br); - /* vbv_buffer_size_extension */ - GET_BITS (br, 8, &bits); - /* low_delay */ - GET_BITS (br, 1, &bits); - - /* frame_rate_extension_n */ - GET_BITS (br, 2, &bits); - params->fps_n *= bits + 1; - /* frame_rate_extension_d */ - GET_BITS (br, 5, &bits); - params->fps_d *= bits + 1; - - return TRUE; - - /* ERRORS */ -failed: - { - GST_WARNING ("Failed to parse sequence extension"); - return FALSE; - } -} - -/* Set the Pixel Aspect Ratio in our hdr from a DAR code in the data */ -static void -set_par_from_dar (MPEGVParams * params, guint8 asr_code) -{ - /* Pixel_width = DAR_width * display_vertical_size */ - /* Pixel_height = DAR_height * display_horizontal_size */ - switch (asr_code) { - case 0x02: /* 3:4 DAR = 4:3 pixels */ - params->par_w = 4 * params->height; - params->par_h = 3 * params->width; - break; - case 0x03: /* 9:16 DAR */ - params->par_w = 16 * params->height; - params->par_h = 9 * params->width; - break; - case 0x04: /* 1:2.21 DAR */ - params->par_w = 221 * params->height; - params->par_h = 100 * params->width; - break; - case 0x01: /* Square pixels */ - params->par_w = params->par_h = 1; - break; - default: - GST_DEBUG ("unknown/invalid aspect_ratio_information %d", asr_code); - break; - } -} - -static void -set_fps_from_code (MPEGVParams * params, guint8 fps_code) -{ - const gint framerates[][2] = { - {30, 1}, {24000, 1001}, {24, 1}, {25, 1}, - {30000, 1001}, {30, 1}, {50, 1}, {60000, 1001}, - {60, 1}, {30, 1} - }; - - if (fps_code && fps_code < 10) { - params->fps_n = framerates[fps_code][0]; - params->fps_d = framerates[fps_code][1]; - } else { - GST_DEBUG ("unknown/invalid frame_rate_code %d", fps_code); - /* Force a valid framerate */ - /* FIXME or should this be kept unknown ?? */ - params->fps_n = 30000; - params->fps_d = 1001; - } -} - -static gboolean -gst_mpeg_video_params_parse_sequence (MPEGVParams * params, GstBitReader * br) -{ - guint32 bits; - - GET_BITS (br, 32, &bits); - if (bits != 0x100 + MPEG_PACKET_SEQUENCE) - goto failed; - - /* assume MPEG-1 till otherwise discovered */ - params->mpeg_version = 1; - - GET_BITS (br, 12, &bits); - params->width = bits; - GET_BITS (br, 12, &bits); - params->height = bits; - - GET_BITS (br, 4, &bits); - set_par_from_dar (params, bits); - GET_BITS (br, 4, &bits); - set_fps_from_code (params, bits); - - GET_BITS (br, 18, &bits); - if (bits == 0x3ffff) { - /* VBR stream */ - params->bitrate = 0; - } else { - /* Value in header is in units of 400 bps */ - params->bitrate *= 400; - } - - /* skip 1 + VBV buffer size */ - if (!gst_bit_reader_skip (br, 11)) - goto failed; - - /* constrained_parameters_flag */ - GET_BITS (br, 1, &bits); - - /* load_intra_quantiser_matrix */ - GET_BITS (br, 1, &bits); - if (bits) { - if (!gst_bit_reader_skip (br, 8 * 64)) - goto failed; - } - - /* load_non_intra_quantiser_matrix */ - GET_BITS (br, 1, &bits); - if (bits) { - if (!gst_bit_reader_skip (br, 8 * 64)) - goto failed; - } - - /* check for MPEG-2 sequence extension */ - while (find_start_code (br)) { - gst_bit_reader_peek_bits_uint32 (br, &bits, 32); - if (bits == 0x100 + MPEG_PACKET_EXTENSION) { - if (!gst_mpeg_video_params_parse_extension (params, br)) - goto failed; - params->mpeg_version = 2; - } - } - - /* dump some info */ - GST_LOG ("width x height: %d x %d", params->width, params->height); - GST_LOG ("fps: %d/%d", params->fps_n, params->fps_d); - GST_LOG ("par: %d/%d", params->par_w, params->par_h); - GST_LOG ("profile/level: %d/%d", params->profile, params->level); - GST_LOG ("bitrate/progressive: %d/%d", params->bitrate, params->progressive); - - return TRUE; - - /* ERRORS */ -failed: - { - GST_WARNING ("Failed to parse sequence header"); - /* clear out stuff */ - memset (params, 0, sizeof (*params)); - return FALSE; - } -} - -gboolean -gst_mpeg_video_params_parse_config (MPEGVParams * params, const guint8 * data, - guint size) -{ - GstBitReader br; - - if (size < 4) - return FALSE; - - gst_bit_reader_init (&br, data, size); - - return gst_mpeg_video_params_parse_sequence (params, &br); -} diff --git a/gst/videoparsers/mpegvideoparse.h b/gst/videoparsers/mpegvideoparse.h deleted file mode 100644 index f0092b7137..0000000000 --- a/gst/videoparsers/mpegvideoparse.h +++ /dev/null @@ -1,77 +0,0 @@ -/* GStreamer - * Copyright (C) <2007> Jan Schmidt - * - * 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_MPEGVIDEO_PARAMS_H__ -#define __GST_MPEGVIDEO_PARAMS_H__ - -#include - -G_BEGIN_DECLS - -/* Packet ID codes for different packet types we - * care about */ -#define MPEG_PACKET_PICTURE 0x00 -#define MPEG_PACKET_SLICE_MIN 0x01 -#define MPEG_PACKET_SLICE_MAX 0xaf -#define MPEG_PACKET_SEQUENCE 0xb3 -#define MPEG_PACKET_EXTENSION 0xb5 -#define MPEG_PACKET_SEQUENCE_END 0xb7 -#define MPEG_PACKET_GOP 0xb8 -#define MPEG_PACKET_NONE 0xff - -/* Extension codes we care about */ -#define MPEG_PACKET_EXT_SEQUENCE 0x01 -#define MPEG_PACKET_EXT_SEQUENCE_DISPLAY 0x02 -#define MPEG_PACKET_EXT_QUANT_MATRIX 0x03 - -/* Flags indicating what type of packets are in this block, some are mutually - * exclusive though - ie, sequence packs are accumulated separately. GOP & - * Picture may occur together or separately */ -#define MPEG_BLOCK_FLAG_SEQUENCE 0x01 -#define MPEG_BLOCK_FLAG_PICTURE 0x02 -#define MPEG_BLOCK_FLAG_GOP 0x04 - -#define MPEG_PICTURE_TYPE_I 0x01 -#define MPEG_PICTURE_TYPE_P 0x02 -#define MPEG_PICTURE_TYPE_B 0x03 -#define MPEG_PICTURE_TYPE_D 0x04 - -typedef struct _MPEGVParams MPEGVParams; - -struct _MPEGVParams -{ - gint mpeg_version; - - gint profile; - gint level; - - gint width, height; - gint par_w, par_h; - gint fps_n, fps_d; - - gint bitrate; - gboolean progressive; -}; - -GstFlowReturn gst_mpeg_video_params_parse_config (MPEGVParams * params, - const guint8 * data, guint size); - -G_END_DECLS - -#endif diff --git a/pkgconfig/Makefile.am b/pkgconfig/Makefile.am index 38cd20bb71..99e1f1c1d8 100644 --- a/pkgconfig/Makefile.am +++ b/pkgconfig/Makefile.am @@ -1,9 +1,11 @@ ### all of the standard pc files we need to generate pcverfiles = \ - gstreamer-plugins-bad-@GST_MAJORMINOR@.pc + gstreamer-plugins-bad-@GST_MAJORMINOR@.pc \ + gstreamer-codecparsers-@GST_MAJORMINOR@.pc pcverfiles_uninstalled = \ - gstreamer-plugins-bad-@GST_MAJORMINOR@-uninstalled.pc + gstreamer-plugins-bad-@GST_MAJORMINOR@-uninstalled.pc \ + gstreamer-codecparsers-@GST_MAJORMINOR@-uninstalled.pc all-local: $(pcverfiles) $(pcverfiles_uninstalled) @@ -22,7 +24,8 @@ pkgconfig_DATA = $(pcverfiles) CLEANFILES = $(pcverfiles) $(pcverfiles_uninstalled) pcinfiles = \ - gstreamer-plugins-bad.pc.in gstreamer-plugins-bad-uninstalled.pc.in + gstreamer-plugins-bad.pc.in gstreamer-plugins-bad-uninstalled.pc.in \ + gstreamer-codecparsers.pc.in gstreamer-codecparsers-uninstalled.pc.in DISTCLEANFILES = $(pcinfiles:.in=) EXTRA_DIST = $(pcinfiles) diff --git a/pkgconfig/gstreamer-codecparsers-uninstalled.pc.in b/pkgconfig/gstreamer-codecparsers-uninstalled.pc.in new file mode 100644 index 0000000000..5a01ba8d09 --- /dev/null +++ b/pkgconfig/gstreamer-codecparsers-uninstalled.pc.in @@ -0,0 +1,12 @@ +prefix= +exec_prefix= +libdir=${pcfiledir}/../gst-libs/gst/codecparsers +includedir=${pcfiledir}/../gst-libs + +Name: GStreamer codec parsers, Uninstalled +Description: Bitstream parsers for GStreamer elements, uninstalled +Requires: gstreamer-@GST_MAJORMINOR@ gstreamer-base-@GST_MAJORMINOR@ +Version: @VERSION@ +Libs: -L${libdir} ${libdir}/libgstcodecparsers-@GST_MAJORMINOR@.la +Cflags: -I${includedir} + diff --git a/pkgconfig/gstreamer-codecparsers.pc.in b/pkgconfig/gstreamer-codecparsers.pc.in new file mode 100644 index 0000000000..e2fc6d1ad8 --- /dev/null +++ b/pkgconfig/gstreamer-codecparsers.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/gstreamer-@GST_MAJORMINOR@ + +Name: GStreamer codec parsers +Description: Bitstream parsers for GStreamer elements +Requires: gstreamer-@GST_MAJORMINOR@ gstreamer-base-@GST_MAJORMINOR@ +Version: @VERSION@ +Libs: -L${libdir} -lgstcodecparsers-@GST_MAJORMINOR@ +Cflags: -I${includedir} + diff --git a/pkgconfig/gstreamer-plugins-bad.pc.in b/pkgconfig/gstreamer-plugins-bad.pc.in index c75ff59c77..1fad717474 100644 --- a/pkgconfig/gstreamer-plugins-bad.pc.in +++ b/pkgconfig/gstreamer-plugins-bad.pc.in @@ -7,6 +7,7 @@ Name: GStreamer Bad Plugin libraries Description: Currently includes the photography interface library Requires: gstreamer-@GST_MAJORMINOR@ gstreamer-base-@GST_MAJORMINOR@ Version: @VERSION@ -Libs: -L${libdir} -lgstphotography-@GST_MAJORMINOR@ +Libs: -L${libdir} -lgstphotography-@GST_MAJORMINOR@\ + -L${libdir} -lgstcodecparsers-@GST_MAJORMINOR@\ Cflags: -I${includedir} diff --git a/tests/check/Makefile.am b/tests/check/Makefile.am index dca2775b1b..47bd813c98 100644 --- a/tests/check/Makefile.am +++ b/tests/check/Makefile.am @@ -183,6 +183,8 @@ check_PROGRAMS = \ pipelines/colorspace \ $(check_mimic) \ elements/rtpmux \ + libs/mpegvideoparser \ + libs/h264parser \ $(check_schro) \ $(check_vp8) \ elements/viewfinderbin \ @@ -214,6 +216,24 @@ elements_h263parse_LDADD = libparser.la $(LDADD) elements_h264parse_LDADD = libparser.la $(LDADD) +libs_mpegvideoparser_CFLAGS = \ + $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(AM_CFLAGS) + +libs_mpegvideoparser_LDADD = \ + $(top_builddir)/gst-libs/gst/codecparsers/libgstcodecparsers-@GST_MAJORMINOR@.la \ + $(GST_PLUGINS_BAD_LIBS) -lgstcodecparsers-@GST_MAJORMINOR@ \ + $(GST_BASE_LIBS) $(GST_LIBS) $(LDADD) + +libs_h264parser_CFLAGS = \ + $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ + $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(AM_CFLAGS) + +libs_h264parser_LDADD = \ + $(top_builddir)/gst-libs/gst/codecparsers/libgstcodecparsers-@GST_MAJORMINOR@.la \ + $(GST_PLUGINS_BAD_LIBS) -lgstcodecparsers-@GST_MAJORMINOR@ \ + $(GST_BASE_LIBS) $(GST_LIBS) $(LDADD) + elements_voaacenc_CFLAGS = \ $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(AM_CFLAGS) @@ -242,7 +262,6 @@ elements_camerabin_LDADD = \ elements_camerabin_SOURCES = elements/camerabin.c -if BUILD_EXPERIMENTAL elements_camerabin2_CFLAGS = \ $(GST_PLUGINS_BAD_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) \ $(GST_BASE_CFLAGS) $(GST_CFLAGS) $(AM_CFLAGS) -DGST_USE_UNSTABLE_API @@ -253,8 +272,6 @@ elements_camerabin2_LDADD = \ $(GST_PLUGINS_BASE_LIBS) $(GST_BASE_LIBS) $(GST_LIBS) $(LDADD) elements_camerabin2_SOURCES = elements/camerabin2.c -endif - elements_jifmux_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(EXIF_CFLAGS) $(AM_CFLAGS) elements_jifmux_LDADD = $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) $(GST_CHECK_LIBS) $(EXIF_LIBS) $(LDADD) elements_jifmux_SOURCES = elements/jifmux.c diff --git a/tests/check/elements/mpegvideoparse.c b/tests/check/elements/mpegvideoparse.c index 9dac4633d2..9aab78f9f1 100644 --- a/tests/check/elements/mpegvideoparse.c +++ b/tests/check/elements/mpegvideoparse.c @@ -183,8 +183,9 @@ mpeg_video_parse_check_caps (guint version, guint8 * seq, gint size) buf = gst_value_get_buffer (val); fail_unless (buf != NULL); /* codec-data = header - GOP */ - fail_unless (GST_BUFFER_SIZE (buf) == size - 8); - fail_unless (memcmp (GST_BUFFER_DATA (buf), seq, GST_BUFFER_SIZE (buf)) == 0); + assert_equals_int (GST_BUFFER_SIZE (buf), size - 8); + fail_unless (memcmp (GST_BUFFER_DATA (buf), seq + 4, + GST_BUFFER_SIZE (buf)) == 0); gst_caps_unref (caps); } diff --git a/tests/check/libs/h264parser.c b/tests/check/libs/h264parser.c new file mode 100644 index 0000000000..3bc8c49e57 --- /dev/null +++ b/tests/check/libs/h264parser.c @@ -0,0 +1,182 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * 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 +#include + +static guint8 slice_dpa[] = { + 0x00, 0x00, 0x01, 0x02, 0x00, 0x02, 0x01, 0x03, 0x00, + 0x04, 0x00, 0x05, 0x00, 0x06, 0x00, 0x07, 0x00, 0x09, 0x00, 0x0a, 0x00, + 0x0b, 0x00, 0x0c, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x0f, 0x00, 0x10, 0x00, + 0x11, 0x00, 0x12, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, + 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, + 0x1d, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x20, 0x00, 0x21, 0x00, 0x22, 0x00, + 0x23, 0x00, 0x24, 0x00, 0x25, 0x00, 0x26, 0x00, 0x27, 0x00, 0x28, 0x00, + 0x29, 0x00, 0x2a, 0x00, 0x2b, 0x00, 0x2c, 0x00, 0x2d, 0x00, 0x2e, 0x00, + 0x2f, 0x00, 0x30, 0x00, 0x31, 0x00, 0x32, 0x00, 0x33, 0x00, 0x34, 0x00, + 0x35, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, + 0x3b, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x40, 0x00, + 0x41, 0x00, 0x42, 0x00, 0x43, 0x00, 0x44, 0x00, 0x45, 0x00, 0x46, 0x00, + 0x47, 0x00, 0x48, 0x00, 0x49, 0x00, 0x4a, 0x00, 0x4b, 0x00, 0x4c, 0x00, + 0x4d, 0x00, 0x4e, 0x00, 0x4f, 0x00, 0x50, 0x00, 0x51, 0x00, 0x52, 0x00, + 0x53, 0x00, 0x54, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, + 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5e, 0x00, + 0x5f, 0x00, 0x60, 0x00, 0x61, 0x01, 0x04, 0x00, 0xc4, 0x00, 0xa6, 0x00, + 0xc5, 0x00, 0xab, 0x00, 0x82, 0x00, 0xc2, 0x00, 0xd8, 0x00, 0xc6, 0x00, + 0xe4, 0x00, 0xbe, 0x00, 0xb0, 0x00, 0xe6, 0x00, 0xb6, 0x00, 0xb7, 0x00, + 0xb4, 0x00, 0xb5, 0x00, 0x87, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xd9, 0x00, + 0x8c, 0x00, 0xe5, 0x00, 0xbf, 0x00, 0xb1, 0x00, 0xe7, 0x00, 0xbb, 0x00, + 0xa3, 0x00, 0x84, 0x00, 0x85, 0x00, 0xbd, 0x00, 0x96, 0x00, 0xe8, 0x00, + 0x86, 0x00, 0x8e, 0x00, 0x8b, 0x00, 0x9d, 0x00, 0xa9, 0x00, 0x8a, 0x01, + 0x05, 0x00, 0x83, 0x00, 0xf2, 0x00, 0xf3, 0x00, 0x8d, 0x00, 0x97, 0x00, + 0x88, 0x00, 0xde, 0x00, 0xf1, 0x00, 0x9e, 0x00, 0xaa, 0x00, 0xf5, 0x00, + 0xf4, 0x00, 0xf6, 0x00, 0xa2, 0x00, 0xad, 0x00, 0xc9, 0x00, 0xc7, 0x00, + 0xae, 0x00, 0x62, 0x00, 0x63, 0x00, 0x90, 0x00, 0x64, 0x00, 0xcb, 0x00, + 0x65, 0x00, 0xc8, 0x00, 0xca, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xcd, 0x00, + 0xce, 0x00, 0xe9, 0x00, 0x66, 0x00, 0xd3, 0x00, 0xd0, 0x00, 0xd1, 0x00, + 0xaf, 0x00, 0x67, 0x00, 0x91, 0x00, 0xd6, 0x00, 0xd4, 0x00, 0xd5, 0x00, + 0x68, 0x00, 0xeb, 0x00, 0xed, 0x00, 0x89, 0x00, 0x6a, 0x00, 0x69, 0x00, + 0x6b, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x6e, 0x00, 0xa0, 0x00, 0x6f, 0x00, + 0x71, 0x00, 0x70, 0x00, 0x72, 0x00, 0x73, 0x00, 0x75, 0x00, 0x74, 0x00, + 0x76, 0x00, 0x77, 0x00, 0xea, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x79, 0x00, + 0x7b, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0xa1, 0x00, 0x7f, 0x00, 0x7e, 0x00, + 0x80, 0x00, 0x81, 0x00, 0xec, 0x00, 0xee, 0x00, 0xba, 0x01, 0x06, 0x00, + 0xef, 0x00, 0xe1, 0x00, 0xe0, 0x00, 0xdc, 0x01, 0x07, 0x01, 0x08, 0x01, + 0x09, 0x01, 0x0a, 0x01, 0x0b, 0x01, 0x0c, 0x00, 0xdb, 0x00, 0xe2, 0x01, + 0x0d, 0x01, 0x0e, 0x01, 0x0f, 0x01, 0x10, 0x01, 0x11, 0x01, 0x12, 0x00, + 0xdf, 0x01, 0x13, 0x01, 0x14, 0x01, 0x15, 0x01, 0x16, 0x01, 0x17, 0x00, + 0xfd, 0x00, 0xff, 0x01, 0x18, 0x01, 0x19, 0x01, 0x1a, 0x01, 0x1b, 0x01, + 0x1c, 0x01, 0x1d, 0x01, 0x1e, 0x01, 0x1f, 0x01, 0x20, 0x01, 0x21, 0x01, + 0x22, 0x01, 0x23, 0x01, 0x24, 0x01, 0x25, 0x01, 0x26, 0x00, 0xfe, 0x01, + 0x00, 0x01, 0x27, 0x01, 0x28, 0x01, 0x29, 0x01, 0x2a, 0x01, 0x2b, 0x01, + 0x2c, 0x01, 0x2d, 0x01, 0x2e, 0x01, 0x2f, 0x01, 0x30, 0x01, 0x31, 0x00, + 0xe3, 0x00, 0xd7, 0x01, 0x32, 0x00, 0xf8, 0x00, 0xf9, 0x01, 0x33, 0x01, + 0x34, 0x01, 0x35, 0x01, 0x36, 0x01, 0x37, 0x01, 0x38, 0x01, 0x39, 0x01, + 0x3a, 0x01, 0x3b, 0x01, 0x3c, 0x01, 0x3d, 0x01, 0x3e, 0x01, 0x3f, 0x01, + 0x40, 0x01, 0x41, 0x01, 0x42, 0x01, 0x43, 0x01, 0x44, 0x01, 0x45, 0x01, + 0x46, 0x01, 0x47, 0x01, 0x48, 0x01, 0x49, 0x01, 0x4a, 0x01, 0x4b, 0x01, + 0x4c, 0x00, 0x08, 0x05, 0x2e, 0x6e, 0x75, 0x6c, 0x6c, 0x0c, 0x76, 0x69, + 0x73, 0x69, 0x62, 0x6c, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x04, 0x45, + 0x75, 0x72, 0x6f, 0x06, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x0a, 0x62, + 0x75, 0x6c, 0x6c, 0x65, 0x74, 0x6d, 0x61, 0x74, 0x68, 0x06, 0x53, 0x61, + 0x63, 0x75, 0x74, 0x65, 0x06, 0x54, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06, + 0x5a, 0x61, 0x63, 0x75, 0x74, 0x65, 0x06, 0x73, 0x61, 0x63, 0x75, 0x74, + 0x65, 0x06, 0x74, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06, 0x7a, 0x61, 0x63, + 0x75, 0x74, 0x65, 0x07, 0x41, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x07, + 0x61, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x0c, 0x73, 0x63, 0x6f, 0x6d, + 0x6d, 0x61, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x53, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0a, 0x5a, 0x64, + 0x6f, 0x74, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x06, 0x4c, 0x63, 0x61, + 0x72, 0x6f, 0x6e, 0x06, 0x6c, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x0a, 0x7a, + 0x64, 0x6f, 0x74, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x06, 0x52, 0x61, + 0x63, 0x75, 0x74, 0x65, 0x06, 0x41, 0x62, 0x72, 0x65, 0x76, 0x65, 0x06, + 0x4c, 0x61, 0x63, 0x75, 0x74, 0x65, 0x07, 0x45, 0x6f, 0x67, 0x6f, 0x6e, + 0x65, 0x6b, 0x06, 0x45, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06, 0x44, 0x63, + 0x61, 0x72, 0x6f, 0x6e, 0x07, 0x44, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, + 0x06, 0x4e, 0x61, 0x63, 0x75, 0x74, 0x65, 0x06, 0x4e, 0x63, 0x61, 0x72, + 0x6f, 0x6e, 0x0d, 0x4f, 0x68, 0x75, 0x6e, 0x67, 0x61, 0x72, 0x75, 0x6d, + 0x6c, 0x61, 0x75, 0x74, 0x06, 0x52, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x05, + 0x55, 0x72, 0x69, 0x6e, 0x67, 0x09, 0x6e, 0x75, 0x6e, 0x67, 0x61, 0x64, + 0x65, 0x73, 0x68, 0x0d, 0x55, 0x68, 0x75, 0x6e, 0x67, 0x61, 0x72, 0x75, + 0x6d, 0x6c, 0x61, 0x75, 0x74, 0x0c, 0x54, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x06, 0x72, 0x61, 0x63, 0x75, 0x74, + 0x65, 0x06, 0x61, 0x62, 0x72, 0x65, 0x76, 0x65, 0x06, 0x6c, 0x61, 0x63, + 0x75, 0x74, 0x65, 0x07, 0x65, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x06, + 0x65, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x06, 0x64, 0x63, 0x61, 0x72, 0x6f, + 0x6e, 0x07, 0x64, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x06, 0x6e, 0x61, + 0x63, 0x75, 0x74, 0x65, 0x06, 0x6e, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x0d, + 0x6f, 0x68, 0x75, 0x6e, 0x67, 0x61, 0x72, 0x75, 0x6d, 0x6c, 0x61, 0x75, + 0x74, 0x06, 0x72, 0x63, 0x61, 0x72, 0x6f, 0x6e, 0x05, 0x75, 0x72, 0x69, + 0x6e, 0x67, 0x0d, 0x75, 0x68, 0x75, 0x6e, 0x67, 0x61, 0x72, 0x75, 0x6d, + 0x6c, 0x61, 0x75, 0x74, 0x0c, 0x74, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, + 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0a, 0x49, 0x64, 0x6f, 0x74, 0x61, 0x63, + 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x52, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, + 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x72, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x07, 0x49, 0x6f, 0x67, 0x6f, 0x6e, + 0x65, 0x6b, 0x07, 0x41, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x07, 0x45, + 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x0a, 0x45, 0x64, 0x6f, 0x74, 0x61, + 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x47, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x4b, 0x63, 0x6f, 0x6d, 0x6d, + 0x61, 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x07, 0x49, 0x6d, 0x61, 0x63, + 0x72, 0x6f, 0x6e, 0x0c, 0x4c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63, + 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x4e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, + 0x63, 0x63, 0x65, 0x6e, 0x74, 0x07, 0x4f, 0x6d, 0x61, 0x63, 0x72, 0x6f, + 0x6e, 0x07, 0x55, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x07, 0x55, 0x6d, + 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x07, 0x69, 0x6f, 0x67, 0x6f, 0x6e, 0x65, + 0x6b, 0x07, 0x61, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x07, 0x65, 0x6d, + 0x61, 0x63, 0x72, 0x6f, 0x6e, 0x0a, 0x65, 0x64, 0x6f, 0x74, 0x61, 0x63, + 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x67, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, + 0x63, 0x63, 0x65, 0x6e, 0x74, 0x0c, 0x6b, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x61, 0x63, 0x63, 0x65, 0x6e, 0x74, 0x07, 0x69, 0x6d, 0x61, 0x63, 0x72, + 0x6f, 0x6e, 0x0c, 0x6c, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63, 0x63, + 0x65, 0x6e, 0x74, 0x0c, 0x6e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x61, 0x63, + 0x63, 0x65, 0x6e, 0x74, 0x07, 0x6f, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x6e, + 0x07, 0x75, 0x6f, 0x67, 0x6f, 0x6e, 0x65, 0x6b, 0x07, 0x75, 0x6d, 0x61, + 0x63, 0x72, 0x6f, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02 +}; + +GST_START_TEST (test_h264_parse_slice_dpa) +{ + GstH264ParserResult res; + GstH264NalUnit nalu; + + GstH264NalParser *parser = gst_h264_nal_parser_new (); + + res = gst_h264_parser_identify_nalu (parser, slice_dpa, 0, + sizeof (slice_dpa), &nalu); + + assert_equals_int (res, GST_H264_PARSER_OK); + assert_equals_int (nalu.type, GST_H264_NAL_SLICE_DPA); + + g_free (parser); +} + +GST_END_TEST; + +static Suite * +h264parser_suite (void) +{ + Suite *s = suite_create ("H264 Parser library"); + + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_h264_parse_slice_dpa); + + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = h264parser_suite (); + + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +} diff --git a/tests/check/libs/mpegvideoparser.c b/tests/check/libs/mpegvideoparser.c new file mode 100644 index 0000000000..414a45e232 --- /dev/null +++ b/tests/check/libs/mpegvideoparser.c @@ -0,0 +1,208 @@ +/* Gstreamer + * Copyright (C) <2011> Intel Corporation + * Copyright (C) <2011> Collabora Ltd. + * Copyright (C) <2011> Thibault Saunier + * + * 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 +#include + +/* actually seq + gop */ +static guint8 mpeg2_seq[] = { + 0x00, 0x00, 0x01, 0xb3, 0x02, 0x00, 0x18, 0x15, 0xff, 0xff, 0xe0, 0x28, + 0x00, 0x00, 0x01, 0xb3, 0x78, 0x04, 0x38, 0x37, 0xff, 0xff, 0xf0, 0x00, + 0x00, 0x00, 0x01, 0xb5, 0x14, 0x8a, 0x00, 0x11, 0x03, 0x71, + 0x00, 0x00, 0x01, 0xb8, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x03, 0x00, 0x08, 0x00, 0x00 +}; + +static guint8 mis_identified_datas[] = { + 0x00, 0x00, 0x01, 0x1f, 0x4a, 0xf4, 0xd4, 0xd8, 0x08, 0x23, 0xdd, + 0x7c, 0xd3, 0x75, 0x21, 0x43, 0x85, 0x31, 0x43, 0x04, 0x24, 0x30, + 0x18, 0x43, 0xba, 0x1a, 0x50, 0x60, 0xbb, 0x53, 0x56, 0x80, 0x41, + 0xb9, 0xd4, 0x25, 0x42, 0xea, 0x71, 0xb7, 0x49, 0x84, 0x0b, 0x14, + 0x24, 0xc2, 0xaa, 0xba, 0xf9, 0xf7, 0x5b, 0x78, 0xa2, 0xba, 0xd3, + 0xc7, 0x12, 0xee, 0xbe, 0xba, 0xfa, 0xeb, 0xeb, 0xaf, 0xbe, 0x6f, + 0xce, 0x92, 0x05, 0x15, 0x22, 0x44, 0xf4, 0xc9, 0x1b, 0xcd, 0x84, + 0x80, 0x87, 0x35, 0x6c, 0x07, 0x82, 0xaf, 0x3c, 0x3a, 0x89, 0x48, + 0x3a, 0x26, 0x00, 0x64, 0x03, 0x12, 0x60, 0x03, 0xf4, 0x8c, 0x21, + 0x16, 0xbe, 0x3c, 0x7c, 0x18, 0x03, 0x10, 0x0c, 0x80, 0xa0, 0x05, + 0xe1, 0x85, 0x94, 0x90, 0xc4, 0x74, 0x05, 0x72, 0x80, 0x7a, 0x8e, + 0x3e, 0x00, 0x30, + /* The accelerated version of scan_for_start_codes() + * mis-identifies the following as a start code */ + 0x01, 0x00, 0x01, 0x80, 0x68, 0x14, + 0x26, 0xe4, 0x80, 0x98, 0x0a, 0xba, 0x77, 0x01, 0xc2, 0x42, 0x12, + 0xc4, 0x59, 0x2a, 0xbb, 0x49, 0xf2, 0xc5, 0xa8, 0xd9, 0x30, 0x33, + 0x16, 0x50, 0x60, 0x61, 0x41, 0xaa, 0x0d, 0x41, 0x5b, 0x17, 0x77, + 0x76, 0x1a, 0x14, 0x3a, 0x08, 0x19, 0x3d, 0x6c, 0x94, 0x55, 0xd0, + 0x94, 0x5a, 0xeb, 0x61, 0x22, 0xa7, 0xa6, 0x83, 0x47, 0x6d, 0x4d, + 0x84, 0xc4, 0x6f, 0x78, 0xd8, 0x3a, 0xb4, 0x02, 0x0c, 0x36, 0xa6, + 0x0b, 0x18, 0x49, 0xf7, 0xad, 0x00, 0x82, 0x09, 0xba, 0x12, 0xba, + 0x1d, 0x44, 0x94, 0x0a, 0x1b, 0x03, 0xbb, 0xa2, 0x53, 0x02, 0xc0, + 0x41, 0xac, 0x22, + /* the real start code is here */ + 0x00, 0x00, 0x01, 0x20, 0x4a, 0xfd, 0xf5, 0x50 +}; + +static GstMpegVideoPacketTypeCode ordercode[] = { + GST_MPEG_VIDEO_PACKET_SEQUENCE, + GST_MPEG_VIDEO_PACKET_EXTENSION, + GST_MPEG_VIDEO_PACKET_GOP, +}; + +GST_START_TEST (test_mpeg_parse) +{ + gint i; + GList *list, *tmp; + GstMpegVideoTypeOffsetSize *typeoffsz; + + list = gst_mpeg_video_parse (mpeg2_seq, sizeof (mpeg2_seq), 12); + + assert_equals_int (g_list_length (list), 4); + for (tmp = list, i = 0; tmp; tmp = g_list_next (tmp), i++) { + typeoffsz = tmp->data; + if (i == 3) { + fail_unless (GST_MPEG_VIDEO_PACKET_SLICE_MIN <= typeoffsz->type && + typeoffsz->type <= GST_MPEG_VIDEO_PACKET_SLICE_MAX); + fail_unless (typeoffsz->size < 0); + } else + assert_equals_int (ordercode[i], typeoffsz->type); + } + + g_list_free_full (list, (GDestroyNotify) g_free); +} + +GST_END_TEST; + +GST_START_TEST (test_mpeg_parse_sequence_header) +{ + GList *list; + GstMpegVideoTypeOffsetSize *typeoffsz; + GstMpegVideoSequenceHdr seqhdr; + + list = gst_mpeg_video_parse (mpeg2_seq, sizeof (mpeg2_seq), 12); + + typeoffsz = list->data; + fail_unless (typeoffsz->type == GST_MPEG_VIDEO_PACKET_SEQUENCE); + fail_unless (gst_mpeg_video_parse_sequence_header (&seqhdr, mpeg2_seq, + sizeof (mpeg2_seq), typeoffsz->offset)); + assert_equals_int (seqhdr.width, 1920); + assert_equals_int (seqhdr.height, 1080); + assert_equals_int (seqhdr.aspect_ratio_info, 3); + assert_equals_int (seqhdr.par_w, 17280); + assert_equals_int (seqhdr.par_h, 17280); + assert_equals_int (seqhdr.frame_rate_code, 7); + assert_equals_int (seqhdr.fps_n, 60000); + assert_equals_int (seqhdr.fps_d, 1001); + assert_equals_int (seqhdr.bitrate_value, 262143); + assert_equals_int (seqhdr.bitrate, 0); + assert_equals_int (seqhdr.vbv_buffer_size_value, 512); + fail_unless (seqhdr.constrained_parameters_flag == FALSE); + + g_list_free_full (list, (GDestroyNotify) g_free); +} + +GST_END_TEST; + +GST_START_TEST (test_mpeg_parse_sequence_extension) +{ + GList *list; + GstMpegVideoTypeOffsetSize *typeoffsz; + GstMpegVideoSequenceExt seqext; + + list = gst_mpeg_video_parse (mpeg2_seq, sizeof (mpeg2_seq), 12); + + typeoffsz = list->next->data; + fail_unless (typeoffsz->type == GST_MPEG_VIDEO_PACKET_EXTENSION); + fail_unless (gst_mpeg_video_parse_sequence_extension (&seqext, + mpeg2_seq, sizeof (mpeg2_seq), typeoffsz->offset)); + assert_equals_int (seqext.profile, 4); + assert_equals_int (seqext.level, 8); + assert_equals_int (seqext.progressive, 1); + assert_equals_int (seqext.chroma_format, 1); + assert_equals_int (seqext.horiz_size_ext, 0); + assert_equals_int (seqext.vert_size_ext, 0); + assert_equals_int (seqext.vert_size_ext, 0); + assert_equals_int (seqext.bitrate_ext, 8); + assert_equals_int (seqext.vbv_buffer_size_extension, 3); + assert_equals_int (seqext.low_delay, 0); + assert_equals_int (seqext.fps_n_ext, 3); + assert_equals_int (seqext.fps_d_ext, 2); + + g_list_free_full (list, (GDestroyNotify) g_free); +} + +GST_END_TEST; + +GST_START_TEST (test_mis_identified_datas) +{ + GList *list, *tmp; + GstMpegVideoTypeOffsetSize *typeoffsz; + guint8 *data = mis_identified_datas; + + list = gst_mpeg_video_parse (mis_identified_datas, + sizeof (mis_identified_datas), 0); + + assert_equals_int (g_list_length (list), 2); + for (tmp = list; tmp; tmp = g_list_next (tmp)) { + typeoffsz = tmp->data; + + assert_equals_int (data[typeoffsz->offset - 4], 0); + assert_equals_int (data[typeoffsz->offset - 3], 0); + assert_equals_int (data[typeoffsz->offset - 2], 1); + } + + g_list_free_full (list, (GDestroyNotify) g_free); +} + +GST_END_TEST; + +static Suite * +videoparsers_suite (void) +{ + Suite *s = suite_create ("Video Parsers library"); + + TCase *tc_chain = tcase_create ("general"); + + suite_add_tcase (s, tc_chain); + tcase_add_test (tc_chain, test_mpeg_parse); + tcase_add_test (tc_chain, test_mpeg_parse_sequence_header); + tcase_add_test (tc_chain, test_mpeg_parse_sequence_extension); + tcase_add_test (tc_chain, test_mis_identified_datas); + + return s; +} + +int +main (int argc, char **argv) +{ + int nf; + + Suite *s = videoparsers_suite (); + + SRunner *sr = srunner_create (s); + + gst_check_init (&argc, &argv); + + srunner_run_all (sr, CK_NORMAL); + nf = srunner_ntests_failed (sr); + srunner_free (sr); + + return nf; +}