| /* |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 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 |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| /* |
| * Copyright (c) 2013-2015, Freescale Semiconductor, Inc. |
| * Copyright 2017 NXP |
| */ |
| |
| |
| |
| /* |
| * Module Name: aiurdemux.c |
| * |
| * Description: Implementation of unified parser gstreamer plugin |
| * |
| * Portability: This code is written for Linux OS and Gstreamer |
| */ |
| |
| /* |
| * Changelog: |
| * |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "aiurdemux.h" |
| |
| |
| GST_DEBUG_CATEGORY (aiurdemux_debug); |
| |
| typedef struct{ |
| |
| |
| guint32 type; |
| guint32 subtype; |
| const gchar* codec_str; |
| const gchar* mime; |
| }AiurdemuxCodecStruct; |
| |
| |
| static AiurdemuxCodecStruct aiurdemux_videocodec_tab[] ={ |
| {VIDEO_H263, 0, "H.263", "video/x-h263, variant=(string)itu"}, |
| {VIDEO_H264, 0, "H.264/AVC", "video/x-h264, parsed = (boolean)true, alignment=(string)au"}, |
| {VIDEO_MPEG2, 0, "MPEG2", "video/mpeg, systemstream = (boolean)false, parsed = (boolean)true, mpegversion=(int)2"}, |
| {VIDEO_MPEG4, 0, "MPEG4", "video/mpeg, systemstream = (boolean)false, parsed = (boolean)true, mpegversion=(int)4"}, |
| {VIDEO_JPEG, 0, "JPEG", "image/jpeg"}, |
| {VIDEO_MJPG, VIDEO_MJPEG_FORMAT_A, "Motion JPEG format A", "image/jpeg"}, |
| {VIDEO_MJPG, VIDEO_MJPEG_FORMAT_B, "Motion JPEG format B", "image/jpeg"}, |
| {VIDEO_MJPG, VIDEO_MJPEG_2000, "Motion JPEG 2000", "image/x-j2c"}, |
| {VIDEO_MJPG, 0, "Motion JPEG format unknow", "image/jpeg"}, |
| {VIDEO_DIVX, VIDEO_DIVX3, "Divx3", "video/x-divx, divxversion=(int)3"}, |
| {VIDEO_DIVX, VIDEO_DIVX4, "Divx4", "video/x-divx, divxversion=(int)4"}, |
| {VIDEO_DIVX, VIDEO_DIVX5_6, "Divx", "video/x-divx, divxversion=(int)5"}, |
| {VIDEO_DIVX, 0, "Divx", "video/x-divx, divxversion=(int)5"}, |
| {VIDEO_XVID, 0, "Xvid", "video/x-xvid"}, |
| {VIDEO_WMV, VIDEO_WMV7, "WMV7", "video/x-wmv, wmvversion=(int)1, format=(string)WMV1"}, |
| {VIDEO_WMV, VIDEO_WMV8, "WMV8", "video/x-wmv, wmvversion=(int)2, format=(string)WMV2"}, |
| {VIDEO_WMV, VIDEO_WMV9, "WMV9", "video/x-wmv, wmvversion=(int)3, format=(string)WMV3"}, |
| {VIDEO_WMV, VIDEO_WVC1, "VC1", "video/x-wmv, wmvversion=(int)3, format=(string)WVC1"}, |
| {VIDEO_WMV, 0, NULL, NULL}, |
| {VIDEO_REAL, 0, "RealVideo", "video/x-pn-realvideo"}, |
| {VIDEO_SORENSON_H263, 0, "Sorenson H.263", "video/x-flash-video, flvversion=(int)1"}, |
| {VIDEO_FLV_SCREEN, 0, "Flash Screen", "video/x-flash-screen"}, |
| {VIDEO_ON2_VP, VIDEO_VP6A, "VP6 Alpha", "video/x-vp6-alpha"}, |
| {VIDEO_ON2_VP, VIDEO_VP6, "VP6 Flash", "video/x-vp6-flash"}, |
| {VIDEO_ON2_VP, VIDEO_VP8, "VP8", "video/x-vp8"}, |
| {VIDEO_ON2_VP, VIDEO_VP9, "VP9", "video/x-vp9"}, |
| {VIDEO_ON2_VP, 0, NULL, NULL}, |
| {VIDEO_HEVC, 0, "H.265/HEVC", "video/x-h265, parsed = (boolean)true, alignment=(string)au"}, |
| {VIDEO_AVS, 0, "AVS", "video/x-cavs"}, |
| }; |
| |
| static GstStaticPadTemplate gst_aiurdemux_videosrc_template = |
| GST_STATIC_PAD_TEMPLATE ("video_%u", |
| GST_PAD_SRC, |
| GST_PAD_SOMETIMES, |
| GST_STATIC_CAPS_ANY); |
| |
| static GstStaticPadTemplate gst_aiurdemux_audiosrc_template = |
| GST_STATIC_PAD_TEMPLATE ("audio_%u", |
| GST_PAD_SRC, |
| GST_PAD_SOMETIMES, |
| GST_STATIC_CAPS_ANY); |
| |
| static GstStaticPadTemplate gst_aiurdemux_subsrc_template = |
| GST_STATIC_PAD_TEMPLATE ("subtitle_%u", |
| GST_PAD_SRC, |
| GST_PAD_SOMETIMES, |
| GST_STATIC_CAPS_ANY); |
| |
| static GstsutilsOptionEntry g_aiurdemux_option_table[] = { |
| {PROP_PROGRAM_NUMBER, "program-number", |
| "program-number", |
| "Program number to demux for (-1 to ignore), valid only when multiprogram-enabled=false", |
| G_TYPE_INT, |
| G_STRUCT_OFFSET (AiurDemuxOption, program_number), |
| "-1", "-1", G_MAXINT_STR}, |
| {PROP_MULTIPROGRAM_ENABLED, "multiprogram-enabled", "multiprogram enabled", |
| "enable/disable multiprogram", |
| G_TYPE_BOOLEAN, G_STRUCT_OFFSET (AiurDemuxOption, multiprogram_enabled), |
| "false"}, |
| {PROP_PROGRAM_MASK, "program-mask", "program mask", |
| "set program track bit mask (valid for first 32 programs, 0 for enable all)", |
| G_TYPE_UINT, |
| G_STRUCT_OFFSET (AiurDemuxOption, program_mask), |
| "0x0", "0", "0xffffffff"}, |
| {PROP_INTERLEAVE_QUEUE_SIZE, "interleave-queue-size", "interleave queue size", |
| "set length of interleave queue in bytes for file read mode only", |
| G_TYPE_UINT, |
| G_STRUCT_OFFSET (AiurDemuxOption, interleave_queue_size), |
| "10240000", "0", G_MAXUINT_STR}, |
| {PROP_STREAMING_LATENCY, "streaming_latency", "latency for streaming", |
| "set the latency in ms seconds for streaming mode", |
| G_TYPE_UINT, |
| G_STRUCT_OFFSET (AiurDemuxOption, streaming_latency), |
| "400", "0", G_MAXUINT_STR}, |
| {PROP_INDEX_ENABLED, "enable-index", "enable index file for clips", |
| "enable/disable index file for clips", |
| G_TYPE_BOOLEAN, |
| G_STRUCT_OFFSET (AiurDemuxOption, index_enabled), |
| "false"}, |
| {PROP_DISABLE_VORBIS_CODEC_DATA, "disable_vorbis_codec_data", "do not send vorbis codec data", |
| "whether to parse vorbis codec data to three buffers to send", |
| G_TYPE_BOOLEAN, |
| G_STRUCT_OFFSET (AiurDemuxOption, disable_vorbis_codec_data), |
| "true"}, |
| {PROP_LOW_LATENCY_TOLERANCE, "low_latency_tolerance", "low latency tolarance", |
| "-1: disable low latency function, 0-streaming_latency: tolarance in ms seconds for time diff", |
| G_TYPE_INT, |
| G_STRUCT_OFFSET (AiurDemuxOption, low_latency_tolerance), |
| "-1", "-1", G_MAXINT_STR}, |
| {-1, NULL, NULL, NULL, 0, 0, NULL} /* terminator */ |
| }; |
| |
| typedef struct |
| { |
| gint core_tag; |
| gint format; |
| const gchar *gst_tag_name; |
| const gchar *print_string; |
| } AiurDemuxTagEntry; |
| static AiurDemuxTagEntry g_user_data_entry[] = { |
| {USER_DATA_TITLE, USER_DATA_FORMAT_UTF8, GST_TAG_TITLE, |
| "Title : %s\n"}, |
| {USER_DATA_LANGUAGE, USER_DATA_FORMAT_UTF8, GST_TAG_LANGUAGE_CODE, |
| "Langurage : %s\n"}, |
| {USER_DATA_GENRE, USER_DATA_FORMAT_UTF8, GST_TAG_GENRE, |
| "Genre : %s\n"}, |
| {USER_DATA_ARTIST, USER_DATA_FORMAT_UTF8, GST_TAG_ARTIST, |
| "Artist : %s\n"}, |
| {USER_DATA_COPYRIGHT, USER_DATA_FORMAT_UTF8, GST_TAG_COPYRIGHT, |
| "Copy Right : %s\n"}, |
| {USER_DATA_COMMENTS, USER_DATA_FORMAT_UTF8, GST_TAG_COMMENT, |
| "Comments : %s\n"}, |
| {USER_DATA_CREATION_DATE, USER_DATA_FORMAT_UTF8, GST_TAG_DATE, |
| "Creation Date : %s\n"}, |
| {USER_DATA_ALBUM, USER_DATA_FORMAT_UTF8, GST_TAG_ALBUM, |
| "Album : %s\n"}, |
| {USER_DATA_VCODECNAME, USER_DATA_FORMAT_UTF8, GST_TAG_VIDEO_CODEC, |
| "Video Codec Name : %s\n"}, |
| {USER_DATA_ACODECNAME, USER_DATA_FORMAT_UTF8, GST_TAG_AUDIO_CODEC, |
| "Audio Codec Name : %s\n"}, |
| {USER_DATA_ARTWORK, USER_DATA_FORMAT_JPEG, GST_TAG_IMAGE, |
| "Found Artwork : foamrt %d , %d bytes\n"}, |
| {USER_DATA_COMPOSER, USER_DATA_FORMAT_UTF8, GST_TAG_COMPOSER, |
| "Composer : %s\n"}, |
| //{USER_DATA_DIRECTOR, USER_DATA_FORMAT_UTF8, ?, "Director : %s\n"}, /* tag is not defined */ |
| //{USER_DATA_INFORMATION, USER_DATA_FORMAT_UTF8, ?, "Information : %s\n"}, /* tag is not defined */ |
| //{USER_DATA_CREATOR, USER_DATA_FORMAT_UTF8, ?, "Creator : %s\n"}, /* tag is not defined */ |
| //{USER_DATA_PRODUCER, USER_DATA_FORMAT_UTF8, ?, "Producer : %s\n"}, /* tag is not defined */ |
| {USER_DATA_PERFORMER, USER_DATA_FORMAT_UTF8, GST_TAG_PERFORMER, |
| "Performer : %s\n"}, |
| //{USER_DATA_REQUIREMENTS, USER_DATA_FORMAT_UTF8, ?, "Requirements : %s\n"}, /* tag is not defined */ |
| //{USER_DATA_SONGWRITER, USER_DATA_FORMAT_UTF8, ?, "Song Writer : %s\n"}, /* tag is not defined */ |
| //{USER_DATA_MOVIEWRITER, USER_DATA_FORMAT_UTF8, ?, "Movie Writer : %s\n"}, /* tag is not defined */ |
| {USER_DATA_TOOL, USER_DATA_FORMAT_UTF8, GST_TAG_APPLICATION_NAME, |
| "Writing Application : %s\n"}, |
| {USER_DATA_DESCRIPTION, USER_DATA_FORMAT_UTF8, GST_TAG_DESCRIPTION, |
| "Description : %s\n"}, |
| {USER_DATA_TRACKNUMBER, USER_DATA_FORMAT_UTF8, GST_TAG_TRACK_NUMBER, |
| "Track Number : %s\n"}, |
| {USER_DATA_TOTALTRACKNUMBER, USER_DATA_FORMAT_UTF8, GST_TAG_TRACK_COUNT, |
| "Track Count : %s\n"}, |
| {USER_DATA_LOCATION, USER_DATA_FORMAT_UTF8, -1, |
| "Location : %s\n"}, |
| {USER_DATA_KEYWORDS, USER_DATA_FORMAT_UTF8, GST_TAG_KEYWORDS, |
| "Keywords : %s\n"}, |
| {USER_DATA_ALBUMARTIST, USER_DATA_FORMAT_UTF8, GST_TAG_ALBUM_ARTIST, |
| "Album Artist : %s\n"}, |
| {USER_DATA_DISCNUMBER, USER_DATA_FORMAT_UTF8, GST_TAG_ALBUM_VOLUME_NUMBER, |
| "Disc Number : %s\n"}, |
| {USER_DATA_RATING, USER_DATA_FORMAT_UTF8, GST_TAG_USER_RATING, |
| "User Rating : %s\n"}, |
| |
| }; |
| |
| |
| #define gst_aiurdemux_parent_class parent_class |
| G_DEFINE_TYPE (GstAiurDemux, gst_aiurdemux, GST_TYPE_ELEMENT); |
| |
| |
| static void gst_aiurdemux_finalize (GObject * object); |
| static GstStateChangeReturn gst_aiurdemux_change_state (GstElement * element, |
| GstStateChange transition); |
| |
| static GstFlowReturn gst_aiurdemux_chain (GstPad * sinkpad, GstObject * parent, |
| GstBuffer * inbuf); |
| |
| static gboolean gst_aiurdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent, |
| GstEvent * event); |
| static gboolean gst_aiurdemux_handle_src_event (GstPad * sinkpad, GstObject * parent, |
| GstEvent * event); |
| static gboolean |
| gst_aiurdemux_handle_src_query (GstPad * pad, GstObject * parent, GstQuery * query); |
| static void |
| gst_aiurdemux_push_tags (GstAiurDemux * demux, AiurDemuxStream * stream); |
| static void |
| gst_aiurdemux_push_event (GstAiurDemux * demux, GstEvent * event); |
| |
| |
| static GstPadTemplate *gst_aiurdemux_sink_pad_template (void); |
| |
| static gboolean aiurdemux_sink_activate (GstPad * sinkpad,GstObject * parent); |
| |
| static gboolean aiurdemux_sink_activate_mode (GstPad * sinkpad, |
| GstObject * parent, GstPadMode mode, gboolean active); |
| static gboolean aiurdemux_sink_activate_pull (GstPad * sinkpad, GstObject * parent, |
| gboolean active); |
| static gboolean aiurdemux_sink_activate_push (GstPad * sinkpad, GstObject * parent, |
| gboolean active); |
| gpointer aiurdemux_loop_push (gpointer * data); |
| |
| static void aiurdemux_pull_task (GstPad * pad); |
| static void aiurdemux_push_task (GstAiurDemux * demux); |
| |
| static gboolean gst_aiurdemux_setcaps (GstPad * pad, GstObject * parent, GstCaps * caps); |
| |
| static GstFlowReturn aiurdemux_loop_state_probe (GstAiurDemux * demux); |
| static GstFlowReturn aiurdemux_loop_state_init (GstAiurDemux * demux); |
| static GstFlowReturn aiurdemux_loop_state_header (GstAiurDemux * demux); |
| static GstFlowReturn aiurdemux_loop_state_movie (GstAiurDemux * demux); |
| |
| |
| static gboolean aiurdemux_set_readmode (GstAiurDemux * demux); |
| |
| static gboolean |
| aiurdemux_parse_programs (GstAiurDemux * demux); |
| static gboolean |
| aiurdemux_parse_pmt (GstAiurDemux * demux); |
| |
| gboolean |
| aiurdemux_parse_program_info (GstAiurDemux * demux); |
| |
| static void |
| aiurdemux_select_programs (GstAiurDemux * demux); |
| |
| |
| |
| |
| static GstTagList * aiurdemux_add_user_tags (GstAiurDemux * demux); |
| |
| static int aiurdemux_parse_streams (GstAiurDemux * demux); |
| static void aiurdemux_parse_video (GstAiurDemux * demux, AiurDemuxStream * stream, |
| gint track_index); |
| static void aiurdemux_parse_audio (GstAiurDemux * demux, AiurDemuxStream * stream, |
| gint track_index); |
| static void aiurdemux_parse_text (GstAiurDemux * demux, AiurDemuxStream * stream, |
| gint track_index); |
| static void aiurdemux_check_interleave_stream_eos (GstAiurDemux * demux); |
| static void |
| _gst_buffer_copy_into_mem (GstBuffer * dest, gsize offset, const guint8 * src, |
| gsize size); |
| |
| static GstFlowReturn aiurdemux_read_buffer(GstAiurDemux * demux, uint32* track_idx, |
| AiurDemuxStream** stream_out); |
| static GstFlowReturn aiurdemux_parse_vorbis_codec_data(GstAiurDemux * demux, AiurDemuxStream* stream); |
| |
| static gint aiurdemux_choose_next_stream (GstAiurDemux * demux); |
| |
| static AiurDemuxStream * aiurdemux_trackidx_to_stream (GstAiurDemux * demux, guint32 stream_idx); |
| static void |
| aiurdemux_check_start_offset (GstAiurDemux * demux, AiurDemuxStream * stream); |
| static void |
| aiurdemux_adjust_timestamp (GstAiurDemux * demux, AiurDemuxStream * stream, |
| GstBuffer * buffer); |
| static void |
| aiurdemux_update_stream_position (GstAiurDemux * demux, |
| AiurDemuxStream * stream, GstBuffer * buffer); |
| static void |
| aiurdemux_send_stream_newsegment (GstAiurDemux * demux, |
| AiurDemuxStream * stream); |
| static GstFlowReturn |
| aiurdemux_send_stream_eos (GstAiurDemux * demux, AiurDemuxStream * stream); |
| static GstFlowReturn |
| aiurdemux_send_stream_eos_all (GstAiurDemux * demux); |
| |
| static GstFlowReturn aiurdemux_push_pad_buffer (GstAiurDemux * demux, AiurDemuxStream * stream, |
| GstBuffer * buffer); |
| static GstFlowReturn |
| aiurdemux_combine_flows (GstAiurDemux * demux, AiurDemuxStream * stream, |
| GstFlowReturn ret); |
| |
| static void aiurdemux_reset_stream (GstAiurDemux * demux, AiurDemuxStream * stream); |
| |
| |
| static gboolean |
| gst_aiurdemux_convert_seek (GstPad * pad, GstFormat * format, |
| GstSeekType cur_type, gint64 * cur, GstSeekType stop_type, gint64 * stop); |
| |
| static gboolean |
| gst_aiurdemux_perform_seek (GstAiurDemux * demux, GstSegment * segment, |
| gint accurate); |
| static gboolean |
| aiurdemux_do_push_seek (GstAiurDemux * demux, GstPad * pad, |
| GstEvent * event); |
| static gboolean |
| aiurdemux_do_seek (GstAiurDemux * demux, GstPad * pad, GstEvent * event); |
| |
| static gboolean |
| gst_aiurdemux_get_duration (GstAiurDemux * demux, gint64 * duration); |
| |
| static void |
| aiurdemux_send_pending_events (GstAiurDemux * demux); |
| |
| |
| static void aiurdemux_release_resource (GstAiurDemux * demux); |
| static GstFlowReturn gst_aiurdemux_close_core (GstAiurDemux * demux); |
| |
| |
| |
| |
| |
| #define AIUR_MEDIATYPE2STR(media) \ |
| (((media)==MEDIA_VIDEO)?"video":(((media)==MEDIA_AUDIO)?"audio":"subtitle")) |
| |
| #define AIUR_CORETS_2_GSTTS(ts) (((ts)==PARSER_UNKNOWN_TIME_STAMP)? GST_CLOCK_TIME_NONE : (ts*1000)) |
| #define AIUR_GSTTS_2_CORETS(ts) ((ts)/1000) |
| |
| #define AIUR_COREDURATION_2_GSTDURATION(ts) (((ts)==PARSER_UNKNOWN_DURATION)? 0 : (ts*1000)) |
| |
| #define AIUR_RESET_SAMPLE_STAT(stat)\ |
| do {\ |
| (stat).start = GST_CLOCK_TIME_NONE;\ |
| (stat).duration = 0;\ |
| (stat).flag = 0;\ |
| }while(0) |
| |
| #define AIUR_UPDATE_SAMPLE_STAT(stat,timestamp, dura, sflag)\ |
| do {\ |
| if (((stat).start==GST_CLOCK_TIME_NONE) && \ |
| ((timestamp)!=GST_CLOCK_TIME_NONE))\ |
| (stat).start = (timestamp);\ |
| (stat).duration += dura;\ |
| (stat).flag |= sflag;\ |
| }while(0) |
| |
| #define MARK_STREAM_EOS(demux, stream)\ |
| do {\ |
| (stream)->valid = FALSE;\ |
| (stream)->pending_eos = TRUE;\ |
| (demux)->pending_event = TRUE;\ |
| }while(0) |
| |
| |
| |
| |
| #define AIURDEMUX_FUNCTION_IMPLEMENTATION 1 |
| |
| static void gst_aiurdemux_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| GstAiurDemux *self = GST_AIURDEMUX (object); |
| if (gstsutils_options_set_option (g_aiurdemux_option_table, |
| (gchar *) & self->option, prop_id, value) == FALSE) { |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| } |
| |
| } |
| static void gst_aiurdemux_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec) |
| { |
| GstAiurDemux *self = GST_AIURDEMUX (object); |
| if (gstsutils_options_get_option (g_aiurdemux_option_table, |
| (gchar *) & self->option, prop_id, value) == FALSE) { |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| } |
| |
| } |
| |
| |
| static void gst_aiurdemux_class_init (GstAiurDemuxClass * klass) |
| { |
| GObjectClass *gobject_class; |
| GstElementClass *gstelement_class; |
| |
| gobject_class = (GObjectClass *) klass; |
| gstelement_class = (GstElementClass *) klass; |
| |
| parent_class = g_type_class_peek_parent (klass); |
| |
| gobject_class->finalize = gst_aiurdemux_finalize; |
| gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_aiurdemux_set_property); |
| gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_aiurdemux_get_property); |
| |
| gstelement_class->change_state = GST_DEBUG_FUNCPTR (gst_aiurdemux_change_state); |
| |
| gstsutils_options_install_properties_by_options (g_aiurdemux_option_table, |
| gobject_class); |
| |
| gst_element_class_add_pad_template (gstelement_class, |
| gst_aiurdemux_sink_pad_template ()); |
| gst_element_class_add_pad_template (gstelement_class, |
| gst_static_pad_template_get (&gst_aiurdemux_videosrc_template)); |
| gst_element_class_add_pad_template (gstelement_class, |
| gst_static_pad_template_get (&gst_aiurdemux_audiosrc_template)); |
| gst_element_class_add_pad_template (gstelement_class, |
| gst_static_pad_template_get (&gst_aiurdemux_subsrc_template)); |
| |
| gst_element_class_set_static_metadata (gstelement_class, "Aiur universal demuxer", |
| "Codec/Demuxer", |
| "demux container file to video, audio, and subtitle", |
| "FreeScale Multimedia Team <shamm@freescale.com>"); |
| |
| GST_DEBUG_CATEGORY_INIT (aiurdemux_debug, "aiurdemux", 0, "aiurdemux plugin"); |
| |
| } |
| |
| |
| static void gst_aiurdemux_init (GstAiurDemux * demux) |
| { |
| |
| demux->sinkpad = |
| gst_pad_new_from_template (gst_aiurdemux_sink_pad_template (), "sink"); |
| |
| gst_pad_set_activate_function (demux->sinkpad, aiurdemux_sink_activate); |
| gst_pad_set_activatemode_function (demux->sinkpad, |
| aiurdemux_sink_activate_mode); |
| |
| gst_pad_set_chain_function (demux->sinkpad, |
| GST_DEBUG_FUNCPTR (gst_aiurdemux_chain)); |
| gst_pad_set_event_function (demux->sinkpad, |
| GST_DEBUG_FUNCPTR (gst_aiurdemux_handle_sink_event)); |
| gst_element_add_pad (GST_ELEMENT_CAST (demux), demux->sinkpad); |
| |
| gstsutils_options_load_default (g_aiurdemux_option_table, |
| (gchar *) & demux->option); |
| gstsutils_options_load_from_keyfile (g_aiurdemux_option_table, |
| (gchar *) & demux->option, (gchar *)FSL_GST_CONF_DEFAULT_FILENAME, |
| (gchar *)"aiurdemux"); |
| |
| demux->state = AIURDEMUX_STATE_PROBE; |
| demux->pullbased = FALSE; |
| demux->core_interface = NULL; |
| demux->core_handle = NULL; |
| demux->thread = NULL; |
| |
| demux->stream_cache = gst_aiur_stream_cache_new (AIUR_STREAM_CACHE_SIZE, |
| AIUR_STREAM_CACHE_SIZE_MAX, demux); |
| |
| g_mutex_init (&demux->runmutex); |
| g_mutex_init (&demux->seekmutex); |
| demux->play_mode = AIUR_PLAY_MODE_NORMAL; |
| |
| GST_LOG_OBJECT(demux,"gst_aiurdemux_init"); |
| gst_segment_init (&demux->segment, GST_FORMAT_TIME); |
| } |
| |
| static void gst_aiurdemux_finalize (GObject * object) |
| { |
| |
| GstAiurDemux *demux = GST_AIURDEMUX (object); |
| GST_LOG_OBJECT(demux,"gst_aiurdemux_finalize"); |
| |
| if (demux->stream_cache) { |
| gst_mini_object_unref (GST_MINI_OBJECT_CAST (demux->stream_cache)); |
| g_free(demux->stream_cache); |
| demux->stream_cache = NULL; |
| } |
| |
| g_mutex_clear (&demux->runmutex); |
| g_mutex_clear (&demux->seekmutex); |
| G_OBJECT_CLASS (parent_class)->finalize (object); |
| |
| } |
| |
| static GstStateChangeReturn gst_aiurdemux_change_state (GstElement * element, |
| GstStateChange transition) |
| { |
| GstAiurDemux *demux = GST_AIURDEMUX (element); |
| GstStateChangeReturn result = GST_STATE_CHANGE_FAILURE; |
| |
| switch (transition) { |
| case GST_STATE_CHANGE_READY_TO_PAUSED: |
| GST_DEBUG_OBJECT(demux,"change_state READY_TO_PAUSED"); |
| |
| |
| demux->clock_offset = GST_CLOCK_TIME_NONE; |
| demux->media_offset = 0; |
| demux->avg_diff = 0; |
| demux->start_time = GST_CLOCK_TIME_NONE; |
| demux->tag_list = gst_tag_list_new_empty (); |
| aiurcontent_new(&demux->content_info); |
| break; |
| default: |
| GST_LOG_OBJECT(demux,"change_state transition=%x",transition); |
| break; |
| } |
| |
| result = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
| |
| switch (transition) { |
| case GST_STATE_CHANGE_PAUSED_TO_READY: |
| { |
| GST_DEBUG_OBJECT(demux,"change_state PAUSED_TO_READY"); |
| demux->state = AIURDEMUX_STATE_PROBE; |
| demux->pullbased = FALSE; |
| |
| if (demux->tag_list) |
| gst_tag_list_unref (demux->tag_list); |
| |
| aiurdemux_release_resource (demux); |
| |
| gst_aiurdemux_close_core (demux); |
| aiurcontent_release(demux->content_info); |
| demux->content_info = NULL; |
| |
| gst_segment_init (&demux->segment, GST_FORMAT_TIME); |
| |
| demux->play_mode = AIUR_PLAY_MODE_NORMAL; |
| demux->valid_mask = 0; |
| demux->n_streams = 0; |
| demux->n_video_streams = 0; |
| demux->n_audio_streams = 0; |
| demux->n_sub_streams = 0; |
| demux->sub_read_cnt = 0; |
| demux->sub_read_ready = 0; |
| |
| break; |
| } |
| |
| default: |
| break; |
| } |
| |
| return result; |
| |
| } |
| |
| static GstFlowReturn gst_aiurdemux_chain (GstPad * sinkpad, GstObject * parent, |
| GstBuffer * inbuf) |
| { |
| GstAiurDemux *demux; |
| |
| demux = GST_AIURDEMUX (parent); |
| if (inbuf) { |
| gst_aiur_stream_cache_add_buffer (demux->stream_cache, inbuf); |
| } |
| |
| return GST_FLOW_OK; |
| |
| } |
| static gboolean gst_aiurdemux_handle_sink_event(GstPad * sinkpad, GstObject * parent, |
| GstEvent * event) |
| { |
| GstAiurDemux *demux = GST_AIURDEMUX (parent); |
| gboolean res = TRUE; |
| |
| |
| switch (GST_EVENT_TYPE (event)) { |
| case GST_EVENT_SEGMENT: |
| { |
| const GstSegment* segment; |
| |
| gst_event_parse_segment(event,&segment); |
| |
| /* we only expect a BYTE segment, e.g. following a seek */ |
| if (segment->format == GST_FORMAT_BYTES) { |
| if (demux->pullbased == FALSE) { |
| gst_aiur_stream_cache_set_segment (demux->stream_cache, segment->start, segment->stop); |
| } |
| } else if (segment->format == GST_FORMAT_TIME) { |
| GST_DEBUG_OBJECT (demux, "handling new segment from%lld", segment->start); |
| goto exit; |
| } else { |
| GST_DEBUG_OBJECT (demux, "unsupported segment format, ignoring"); |
| goto exit; |
| } |
| |
| GST_DEBUG_OBJECT (demux, |
| "Pushing newseg rate %g, " |
| "format %d, start %" |
| GST_TIME_FORMAT ", " "stop %" GST_TIME_FORMAT, |
| segment->rate, GST_FORMAT_TIME, |
| GST_TIME_ARGS (segment->start), GST_TIME_ARGS (segment->stop)); |
| |
| if (segment->stop < segment->start) { |
| gst_event_unref (event); |
| return FALSE; |
| } |
| |
| /* clear leftover in current segment, if any */ |
| exit: |
| gst_event_unref (event); |
| res = TRUE; |
| goto drop; |
| break; |
| } |
| |
| case GST_EVENT_FLUSH_START: |
| case GST_EVENT_FLUSH_STOP: |
| { |
| gint i; |
| |
| /* reset flow return, e.g. following seek */ |
| for (i = 0; i < demux->n_streams; i++) { |
| demux->streams[i]->last_ret = GST_FLOW_OK; |
| } |
| |
| gst_event_unref (event); |
| res = TRUE; |
| goto drop; |
| break; |
| } |
| case GST_EVENT_EOS: |
| /* If we are in push mode, and get an EOS before we've seen any streams, |
| * then error out - we have nowhere to send the EOS */ |
| if (demux->pullbased) { |
| gint i; |
| gboolean has_valid_stream = FALSE; |
| for (i = 0; i < demux->n_streams; i++) { |
| if (demux->streams[i]->pad != NULL) { |
| has_valid_stream = TRUE; |
| break; |
| } |
| } |
| if (!has_valid_stream){ |
| ;// gst_aiurdemux_post_no_playable_stream_error (demux); |
| } |
| } else { |
| gst_aiur_stream_cache_seteos (demux->stream_cache, TRUE); |
| gst_event_unref (event); |
| goto drop; |
| } |
| break; |
| case GST_EVENT_CAPS: |
| { |
| GstCaps *caps = NULL; |
| gst_event_parse_caps (event, &caps); |
| res = gst_aiurdemux_setcaps(sinkpad, parent, caps); |
| gst_event_unref (event); |
| goto drop; |
| break; |
| } |
| default: |
| GST_LOG_OBJECT(demux,"gst_aiurdemux_handle_sink_event event=%x",GST_EVENT_TYPE (event)); |
| break; |
| } |
| |
| res = gst_pad_event_default (demux->sinkpad, parent, event); |
| |
| drop: |
| return res; |
| |
| } |
| static gboolean gst_aiurdemux_handle_src_event (GstPad * pad, GstObject * parent, GstEvent * event) |
| { |
| gboolean res = TRUE; |
| GstAiurDemux *demux = GST_AIURDEMUX (parent); |
| switch (GST_EVENT_TYPE (event)) { |
| case GST_EVENT_SEEK: |
| if (!demux->seekable || !aiurcontent_is_seelable(demux->content_info)) { |
| goto not_support; |
| } |
| |
| if ((demux->state == AIURDEMUX_STATE_MOVIE) && demux->n_streams) { |
| if (demux->pullbased) { |
| res = aiurdemux_do_seek (demux, pad, event); |
| } else { |
| res = aiurdemux_do_push_seek (demux, pad, event); |
| } |
| } else { |
| GST_DEBUG_OBJECT (demux, |
| "ignoring seek in push mode in current state"); |
| res = FALSE; |
| } |
| gst_event_unref (event); |
| break; |
| |
| case GST_EVENT_QOS: |
| case GST_EVENT_NAVIGATION: |
| res = FALSE; |
| gst_event_unref (event); |
| break; |
| default: |
| GST_LOG_OBJECT(demux,"gst_aiurdemux_handle_src_event event=%x",GST_EVENT_TYPE (event)); |
| res = gst_pad_event_default (pad,parent, event); |
| break; |
| } |
| |
| return res; |
| |
| /* ERRORS */ |
| not_support: |
| { |
| GST_WARNING ("Unsupport source event %s. ", GST_EVENT_TYPE_NAME (event)); |
| gst_event_unref (event); |
| return FALSE; |
| } |
| } |
| static gboolean |
| gst_aiurdemux_handle_src_query (GstPad * pad, GstObject * parent, GstQuery * query) |
| { |
| gboolean res = FALSE; |
| GstAiurDemux *demux = GST_AIURDEMUX (parent); |
| |
| GST_LOG_OBJECT (pad, "%s query", GST_QUERY_TYPE_NAME (query)); |
| |
| switch (GST_QUERY_TYPE (query)) { |
| |
| case GST_QUERY_DURATION:{ |
| GstFormat fmt; |
| |
| gst_query_parse_duration (query, &fmt, NULL); |
| if (fmt == GST_FORMAT_TIME) { |
| gint64 duration = -1; |
| |
| gst_aiurdemux_get_duration (demux, &duration); |
| if (duration > 0) { |
| gst_query_set_duration (query, GST_FORMAT_TIME, duration); |
| res = TRUE; |
| } |
| } |
| break; |
| } |
| case GST_QUERY_SEEKING:{ |
| GstFormat fmt; |
| gboolean seekable = FALSE; |
| |
| gst_query_parse_seeking (query, &fmt, NULL, NULL, NULL); |
| if (fmt == GST_FORMAT_TIME) { |
| gint64 duration = -1; |
| |
| gst_aiurdemux_get_duration (demux, &duration); |
| |
| if (demux->seekable && aiurcontent_is_seelable(demux->content_info)) { |
| seekable = TRUE; |
| } |
| |
| gst_query_set_seeking (query, GST_FORMAT_TIME, seekable, 0, duration); |
| res = TRUE; |
| } |
| break; |
| } |
| case GST_QUERY_POSITION: |
| if (GST_CLOCK_TIME_IS_VALID (demux->segment.position)) { |
| gst_query_set_position (query, GST_FORMAT_TIME, |
| demux->segment.position); |
| res = TRUE; |
| } |
| break; |
| case GST_QUERY_SEGMENT: |
| { |
| GstFormat format; |
| gint64 start, stop = GST_CLOCK_TIME_NONE; |
| |
| format = demux->segment.format; |
| |
| start = |
| gst_segment_to_stream_time (&demux->segment, format, |
| demux->segment.start); |
| |
| if (format == GST_FORMAT_TIME) { |
| gint64 duration = GST_CLOCK_TIME_NONE; |
| |
| gst_aiurdemux_get_duration (demux, &duration); |
| if (duration > 0) { |
| stop = duration; |
| } |
| } |
| |
| gst_query_set_segment (query, demux->segment.rate, format, start, stop); |
| res = TRUE; |
| break; |
| } |
| |
| default: |
| GST_LOG_OBJECT(demux,"gst_aiurdemux_handle_src_query event=%x",GST_QUERY_TYPE (query)); |
| res = gst_pad_query_default (pad, parent, query); |
| break; |
| } |
| |
| return res; |
| } |
| static void |
| gst_aiurdemux_push_tags (GstAiurDemux * demux, AiurDemuxStream * stream) |
| { |
| if (G_LIKELY (stream->pad)) { |
| GST_DEBUG_OBJECT (demux, "Checking pad %s:%s for tags", |
| GST_DEBUG_PAD_NAME (stream->pad)); |
| |
| if (G_UNLIKELY (stream->pending_tags)) { |
| gst_pad_push_event (stream->pad, |
| gst_event_new_tag (stream->pending_tags)); |
| |
| stream->pending_tags = NULL; |
| } |
| |
| if (G_UNLIKELY (stream->send_global_tags && demux->tag_list)) { |
| GST_DEBUG_OBJECT (demux, "Sending global tags %" GST_PTR_FORMAT, |
| demux->tag_list); |
| gst_pad_push_event (stream->pad, |
| gst_event_new_tag (gst_tag_list_ref (demux->tag_list))); |
| stream->send_global_tags = FALSE; |
| } |
| |
| } |
| } |
| |
| /* push event on all source pads; takes ownership of the event */ |
| static void |
| gst_aiurdemux_push_event (GstAiurDemux * demux, GstEvent * event) |
| { |
| gint n; |
| //gboolean pushed_sucessfully = FALSE; |
| |
| for (n = 0; n < demux->n_streams; n++) { |
| GstPad *pad = demux->streams[n]->pad; |
| |
| if (pad) { |
| if (gst_pad_push_event (pad, gst_event_ref (event))) { |
| ;//pushed_sucessfully = TRUE; |
| } |
| } |
| |
| } |
| |
| |
| gst_event_unref (event); |
| } |
| |
| static GstPadTemplate * |
| gst_aiurdemux_sink_pad_template (void) |
| { |
| static GstPadTemplate *templ = NULL; |
| |
| if (!templ) { |
| GstCaps *caps = aiur_core_get_caps (); |
| |
| if (caps) { |
| templ = gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS, caps); |
| gst_caps_unref (caps); |
| } |
| } |
| return templ; |
| } |
| |
| static gboolean aiurdemux_sink_activate (GstPad * sinkpad,GstObject * parent) |
| { |
| GstQuery *query; |
| gboolean pull_mode; |
| |
| query = gst_query_new_scheduling (); |
| |
| if (!gst_pad_peer_query (sinkpad, query)) { |
| gst_query_unref (query); |
| goto activate_push; |
| } |
| |
| pull_mode = gst_query_has_scheduling_mode_with_flags (query, |
| GST_PAD_MODE_PULL, GST_SCHEDULING_FLAG_SEEKABLE); |
| gst_query_unref (query); |
| |
| if (!pull_mode) |
| goto activate_push; |
| |
| GST_LOG_OBJECT (sinkpad, "activating pull"); |
| return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PULL, TRUE); |
| |
| activate_push: |
| GST_LOG_OBJECT (sinkpad, "activating push"); |
| return gst_pad_activate_mode (sinkpad, GST_PAD_MODE_PUSH, TRUE); |
| |
| } |
| |
| static gboolean aiurdemux_sink_activate_mode (GstPad * sinkpad, |
| GstObject * parent, GstPadMode mode, gboolean active) |
| { |
| gboolean res = FALSE; |
| |
| switch (mode) { |
| case GST_PAD_MODE_PUSH: |
| return aiurdemux_sink_activate_push(sinkpad, parent, active); |
| case GST_PAD_MODE_PULL: |
| return aiurdemux_sink_activate_pull(sinkpad, parent, active); |
| default: |
| break; |
| } |
| return res; |
| } |
| |
| static gboolean aiurdemux_sink_activate_pull (GstPad * sinkpad, GstObject * parent, gboolean active) |
| { |
| GstAiurDemux *demux = GST_AIURDEMUX (parent); |
| |
| if (active) { |
| demux->pullbased = TRUE; |
| return gst_pad_start_task (sinkpad, |
| (GstTaskFunction) aiurdemux_pull_task, sinkpad, NULL); |
| } else { |
| return gst_pad_stop_task (sinkpad); |
| } |
| } |
| |
| gpointer aiurdemux_loop_push (gpointer * data) |
| { |
| GstAiurDemux *demux = (GstAiurDemux *) data; |
| |
| g_mutex_lock (&demux->runmutex); |
| |
| while (demux->loop_push) { |
| aiurdemux_push_task (demux); |
| } |
| |
| g_mutex_unlock (&demux->runmutex); |
| |
| return NULL; |
| } |
| |
| static gboolean aiurdemux_sink_activate_push (GstPad * sinkpad, GstObject * parent, gboolean active) |
| { |
| GstAiurDemux *demux = GST_AIURDEMUX (parent); |
| demux->pullbased = FALSE; |
| |
| |
| if (active) { |
| demux->loop_push = TRUE; |
| demux->thread = g_thread_new ("aiur_push",(GThreadFunc) aiurdemux_loop_push, (gpointer) demux); |
| |
| return TRUE; |
| |
| } else { |
| demux->loop_push = FALSE; |
| gst_aiur_stream_cache_close (demux->stream_cache); |
| /* make sure task is closed */ |
| g_mutex_lock (&demux->runmutex); |
| g_mutex_unlock (&demux->runmutex); |
| |
| if (demux->thread) { |
| g_thread_unref(demux->thread); |
| demux->thread = NULL; |
| } |
| |
| return gst_pad_stop_task (sinkpad); |
| } |
| } |
| |
| static void aiurdemux_pull_task (GstPad * pad) |
| { |
| GstAiurDemux *demux; |
| GstFlowReturn ret = GST_FLOW_OK; |
| |
| demux = GST_AIURDEMUX (gst_pad_get_parent (pad)); |
| |
| switch (demux->state) { |
| case AIURDEMUX_STATE_PROBE: |
| ret = aiurdemux_loop_state_probe (demux); |
| break; |
| case AIURDEMUX_STATE_INITIAL: |
| ret = aiurdemux_loop_state_init (demux); |
| break; |
| case AIURDEMUX_STATE_HEADER: |
| ret = aiurdemux_loop_state_header (demux); |
| break; |
| case AIURDEMUX_STATE_MOVIE: |
| ret = aiurdemux_loop_state_movie (demux); |
| break; |
| default: |
| /* ouch */ |
| goto invalid_state; |
| } |
| |
| /* if something went wrong, pause */ |
| if (ret != GST_FLOW_OK) |
| goto pause; |
| |
| done: |
| gst_object_unref (demux); |
| return; |
| |
| invalid_state: |
| aiurdemux_send_stream_eos_all (demux); |
| |
| pause: |
| { |
| const gchar *reason = gst_flow_get_name (ret); |
| |
| GST_WARNING ("pausing task, reason %s \r\n", reason); |
| |
| gst_pad_pause_task (pad); |
| |
| if (ret < GST_FLOW_EOS) { |
| GST_ELEMENT_ERROR (demux, STREAM, FAILED, |
| (NULL), ("streaming stopped, reason %s, state %d", |
| reason, demux->state)); |
| } |
| |
| goto done; |
| } |
| } |
| |
| static void aiurdemux_push_task (GstAiurDemux * demux) |
| { |
| GstFlowReturn ret = GST_FLOW_OK; |
| |
| switch (demux->state) { |
| case AIURDEMUX_STATE_PROBE: |
| ret = aiurdemux_loop_state_probe (demux); |
| break; |
| case AIURDEMUX_STATE_INITIAL: |
| ret = aiurdemux_loop_state_init (demux); |
| break; |
| case AIURDEMUX_STATE_HEADER: |
| ret = aiurdemux_loop_state_header (demux); |
| break; |
| case AIURDEMUX_STATE_MOVIE: |
| ret = aiurdemux_loop_state_movie (demux); |
| break; |
| default: |
| /* ouch */ |
| goto invalid_state; |
| } |
| |
| /* if something went wrong, pause */ |
| if (ret != GST_FLOW_OK) |
| goto pause; |
| |
| done: |
| return; |
| |
| invalid_state: |
| aiurdemux_send_stream_eos_all (demux); |
| |
| pause: |
| { |
| const gchar *reason = gst_flow_get_name (ret); |
| |
| GST_LOG_OBJECT (demux, "pausing task, reason %s \r\n", reason); |
| |
| demux->loop_push = FALSE; |
| |
| if (ret < GST_FLOW_EOS) { |
| GST_ELEMENT_ERROR (demux, STREAM, FAILED, |
| (NULL), ("streaming stopped, reason %s, state %d", |
| reason, demux->state)); |
| } |
| |
| goto done; |
| } |
| } |
| |
| static gboolean gst_aiurdemux_setcaps(GstPad * pad, GstObject * parent, GstCaps * caps) |
| { |
| GstAiurDemux *demux = GST_AIURDEMUX (parent); |
| |
| if (!demux->pullbased) |
| gst_aiur_stream_cache_attach_pad (demux->stream_cache, pad); |
| |
| GST_DEBUG_OBJECT(demux,"gst_aiurdemux_setcaps=%s",gst_caps_to_string(caps)); |
| |
| if(demux->core_interface == NULL){ |
| demux->core_interface = aiur_core_create_interface_from_caps (caps); |
| }else{ |
| return TRUE; |
| } |
| |
| if ((demux->core_interface) && (demux->core_interface->name) |
| && (demux->core_interface->name)) { |
| gst_tag_list_add (demux->tag_list, GST_TAG_MERGE_REPLACE, |
| GST_TAG_CONTAINER_FORMAT, (demux->core_interface->name), NULL); |
| GST_INFO_OBJECT (demux, "Container: %s", demux->core_interface->name); |
| } |
| |
| if (demux->core_interface) { |
| demux->state = AIURDEMUX_STATE_INITIAL; |
| |
| return TRUE; |
| } else{ |
| return FALSE; |
| } |
| } |
| |
| static GstFlowReturn |
| aiurdemux_loop_state_probe (GstAiurDemux * demux) |
| { |
| GstBuffer *buffer = NULL; |
| GstFlowReturn ret = GST_FLOW_OK; |
| GstCaps * caps; |
| |
| if (demux->pullbased) { |
| gst_pad_pull_range (demux->sinkpad, (guint64) 0, |
| AIURDEMUX_INIT_BLOCK_SIZE, &buffer); |
| gst_buffer_unref (buffer); |
| |
| caps = gst_pad_peer_query_caps (demux->sinkpad, NULL); |
| GST_LOG_OBJECT(demux,"state_probe CAPS=%s",gst_caps_to_string(caps)); |
| gst_aiurdemux_setcaps(demux->sinkpad,(GstObject*) demux, caps); |
| gst_caps_unref (caps); |
| } |
| |
| return ret; |
| } |
| |
| |
| static GstFlowReturn |
| aiurdemux_loop_state_init (GstAiurDemux * demux) |
| { |
| GstFlowReturn ret = GST_FLOW_ERROR; |
| int32 parser_result = PARSER_SUCCESS; |
| FslParserHandle core_handle=NULL; |
| gboolean isLive = FALSE; |
| AiurCoreInterface *IParser = demux->core_interface; |
| FslFileStream *file_cbks; |
| ParserMemoryOps *mem_cbks; |
| ParserOutputBufferOps *buf_cbks; |
| uint32 flag; |
| |
| if (IParser == NULL) |
| return GST_FLOW_OK; |
| |
| file_cbks = g_new0 (FslFileStream, 1); |
| mem_cbks = g_new0 (ParserMemoryOps, 1); |
| buf_cbks = g_new0 (ParserOutputBufferOps, 1); |
| |
| if ((!file_cbks) || (!mem_cbks) || (!buf_cbks)) |
| goto fail; |
| |
| if(!demux->content_info) |
| goto fail; |
| if(demux->pullbased) |
| aiurcontent_get_pullfile_callback(demux->content_info,file_cbks); |
| |
| else |
| aiurcontent_get_pushfile_callback(demux->content_info,file_cbks); |
| |
| |
| aiurcontent_get_memory_callback(demux->content_info,mem_cbks); |
| |
| |
| aiurcontent_get_buffer_callback(demux->content_info,buf_cbks); |
| |
| |
| aiurcontent_init(demux->content_info,demux->sinkpad,demux->stream_cache); |
| |
| |
| isLive = aiurcontent_is_live(demux->content_info); |
| |
| if(IParser->createParser2){ |
| flag = FLAG_H264_NO_CONVERT; |
| if(isLive){ |
| flag |= FILE_FLAG_NON_SEEKABLE; |
| flag |= FILE_FLAG_READ_IN_SEQUENCE; |
| } |
| parser_result = IParser->createParser2(flag ,file_cbks, mem_cbks, |
| buf_cbks, (void *)(demux->content_info), &core_handle); |
| }else{ |
| parser_result = IParser->createParser((bool)isLive,file_cbks, mem_cbks, |
| buf_cbks, (void *)(demux->content_info), &core_handle); |
| } |
| |
| if (parser_result != PARSER_SUCCESS) { |
| GST_ERROR_OBJECT(demux,"Failed to create fsl parser! ret=%d",parser_result); |
| goto fail; |
| } |
| |
| |
| demux->core_handle = core_handle; |
| demux->state = AIURDEMUX_STATE_HEADER; |
| ret = GST_FLOW_OK; |
| |
| g_free (file_cbks); |
| g_free (mem_cbks); |
| g_free (buf_cbks); |
| GST_LOG_OBJECT(demux,"aiurdemux_loop_state_init SUCCESS"); |
| |
| return ret; |
| fail: |
| GST_LOG_OBJECT(demux,"aiurdemux_loop_state_init FAIL"); |
| if (file_cbks) { |
| g_free (file_cbks); |
| } |
| if (mem_cbks) { |
| g_free (mem_cbks); |
| } |
| if (buf_cbks) { |
| g_free (buf_cbks); |
| } |
| if (core_handle) { |
| IParser->deleteParser(core_handle); |
| } |
| return ret; |
| } |
| |
| static GstFlowReturn aiurdemux_loop_state_header (GstAiurDemux * demux) |
| { |
| GstFlowReturn ret = GST_FLOW_ERROR; |
| int32 parser_result = PARSER_SUCCESS; |
| AiurCoreInterface *IParser = demux->core_interface; |
| FslParserHandle handle = demux->core_handle; |
| uint64 start_time_us = 0; |
| int32 i = 0; |
| uint64 duration = 0; |
| gboolean need_init_index = TRUE; |
| gchar * index_file = NULL; |
| |
| do{ |
| if(IParser == NULL || handle == NULL) |
| break; |
| |
| index_file = aiurcontent_get_index_file(demux->content_info); |
| |
| if ((demux->option.index_enabled) && index_file) { |
| AiurIndexTable *idxtable = |
| aiurdemux_import_idx_table (index_file); |
| if (idxtable) { |
| |
| if ((idxtable->coreid) && (IParser->coreid) |
| && (strlen (IParser->coreid) == idxtable->coreid_len) |
| && (!memcmp (idxtable->coreid, IParser->coreid, idxtable->coreid_len))) { |
| |
| if ((IParser->initializeIndex) |
| && (IParser->importIndex) |
| && (idxtable->info.size)) { |
| |
| parser_result = IParser->importIndex(handle, idxtable->idx, |
| idxtable->info.size); |
| if (parser_result == PARSER_SUCCESS) { |
| GST_INFO ("Index table %s[size %d] imported.", |
| index_file, idxtable->info.size); |
| need_init_index = FALSE; |
| } |
| } |
| } |
| aiurdemux_destroy_idx_table (idxtable); |
| } |
| |
| } |
| |
| if (need_init_index && IParser->initializeIndex != NULL) { |
| parser_result = IParser->initializeIndex(handle); |
| } |
| |
| parser_result = IParser->isSeekable(handle, &demux->seekable); |
| if(parser_result != PARSER_SUCCESS) |
| break; |
| |
| demux->movie_duration = GST_CLOCK_TIME_NONE; |
| parser_result = IParser->getMovieDuration(handle,&duration); |
| demux->movie_duration = AIUR_CORETS_2_GSTTS (duration); |
| if(parser_result != PARSER_SUCCESS) |
| break; |
| |
| if(aiurdemux_set_readmode(demux) == FALSE) |
| break; |
| |
| parser_result = IParser->getNumTracks(handle,&demux->track_count); |
| if(parser_result != PARSER_SUCCESS) |
| break; |
| |
| //get progreme number and parse programes |
| if(IParser->getNumPrograms) |
| IParser->getNumPrograms(handle, &demux->program_num); |
| |
| if(demux->program_num > 0){ |
| demux->programs = g_new0 (AiurDemuxProgram*, demux->program_num); |
| aiurdemux_parse_programs (demux); |
| } |
| |
| demux->tag_list = aiurdemux_add_user_tags (demux); |
| |
| //parse stream and tracks |
| parser_result = aiurdemux_parse_streams (demux); |
| |
| gst_element_no_more_pads (GST_ELEMENT_CAST (demux)); |
| |
| for (i = 0; i < demux->n_streams; i++) { |
| IParser->seek(handle,i,&start_time_us,SEEK_FLAG_NO_LATER); |
| } |
| |
| if (parser_result == PARSER_SUCCESS && demux->n_streams > 0) { |
| GST_LOG_OBJECT(demux,"aiurdemux_loop_state_header next MOVIE"); |
| demux->state = AIURDEMUX_STATE_MOVIE; |
| ret = GST_FLOW_OK; |
| } |
| GST_LOG_OBJECT(demux,"aiurdemux_loop_state_header SUCCESS"); |
| }while(0); |
| |
| if(ret != GST_FLOW_OK){ |
| aiurdemux_release_resource (demux); |
| GST_LOG_OBJECT(demux,"aiurdemux_loop_state_header FAILED"); |
| } |
| |
| return ret; |
| } |
| |
| static GstFlowReturn aiurdemux_loop_state_movie (GstAiurDemux * demux) |
| { |
| |
| GstFlowReturn ret = GST_FLOW_OK; |
| AiurDemuxStream *stream = NULL; |
| GstBuffer *gstbuf = NULL; |
| uint32 track_idx = 0; |
| AiurCoreInterface *IParser = demux->core_interface; |
| |
| GST_LOG_OBJECT(demux,"aiurdemux_loop_state_movie BEGIN"); |
| |
| |
| if (demux->pending_event) { |
| aiurdemux_send_pending_events (demux); |
| demux->pending_event = FALSE; |
| if (demux->valid_mask == 0) { |
| return GST_FLOW_EOS; |
| } |
| } |
| |
| //select a track to read |
| track_idx = aiurdemux_choose_next_stream(demux); |
| |
| //check long interleave for file mode |
| if(demux->read_mode == PARSER_READ_MODE_FILE_BASED){ |
| aiurdemux_check_interleave_stream_eos(demux); |
| stream = aiurdemux_trackidx_to_stream (demux, track_idx); |
| |
| //push buffer from interleave queue |
| if ((stream->buf_queue) |
| && (!g_queue_is_empty (stream->buf_queue))) { |
| gstbuf = g_queue_pop_head (stream->buf_queue); |
| stream->buf_queue_size -= gst_buffer_get_size(gstbuf);//GST_BUFFER_SIZE (gstbuf); |
| ret = aiurdemux_push_pad_buffer (demux, stream, gstbuf); |
| goto bail; |
| } |
| } |
| |
| //read stream buffer |
| ret = aiurdemux_read_buffer(demux,&track_idx,&stream); |
| |
| GST_LOG_OBJECT(demux,"aiurdemux_loop_state_movie read buffer ret=%d",ret); |
| if(ret != GST_FLOW_OK) |
| goto beach; |
| |
| //update time |
| if (!stream || !stream->buffer) |
| goto bail; |
| GST_LOG_OBJECT (demux, "CHECK track_idx=%d,usStartTime=%lld,sampleFlags=%x",track_idx,stream->sample_stat.start,stream->sample_stat.flag); |
| |
| if((demux->seekable == FALSE) |
| && !aiurcontent_is_seelable(demux->content_info) |
| && !aiurcontent_is_random_access(demux->content_info)) |
| aiurdemux_check_start_offset(demux, stream); |
| |
| aiurdemux_adjust_timestamp (demux, stream, stream->buffer); |
| |
| //send new segment |
| if (stream->new_segment) { |
| GST_BUFFER_FLAG_UNSET (stream->buffer, GST_BUFFER_FLAG_DELTA_UNIT); |
| GST_BUFFER_FLAG_SET (stream->buffer, GST_BUFFER_FLAG_DISCONT); |
| aiurdemux_send_stream_newsegment (demux, stream); |
| } |
| |
| gst_aiurdemux_push_tags (demux, stream); |
| |
| //for vorbis codec data |
| if(demux->option.disable_vorbis_codec_data && |
| (stream->type == MEDIA_AUDIO) |
| && (stream->codec_type == AUDIO_VORBIS) |
| && (stream->send_codec_data == FALSE) |
| && (stream->codec_data.length)){ |
| aiurdemux_parse_vorbis_codec_data(demux, stream); |
| stream->send_codec_data = TRUE; |
| } |
| //push buffer to pad |
| if (demux->interleave_queue_size) { |
| stream->buf_queue_size += gst_buffer_get_size(stream->buffer); |
| if (stream->buf_queue_size > stream->buf_queue_size_max) { |
| stream->buf_queue_size_max = stream->buf_queue_size; |
| } |
| |
| g_queue_push_tail (stream->buf_queue, stream->buffer); |
| stream->buffer = NULL; |
| } else { |
| ret = aiurdemux_push_pad_buffer (demux, stream, stream->buffer); |
| stream->buffer = NULL; |
| } |
| |
| AIUR_RESET_SAMPLE_STAT (stream->sample_stat); |
| |
| ret = aiurdemux_combine_flows (demux, stream, ret); |
| |
| GST_LOG_OBJECT (demux, "STATE MOVIE END ret=%d",ret); |
| |
| bail: |
| return ret; |
| |
| |
| beach: |
| if (stream) { |
| if (stream->buffer) { |
| gst_buffer_unref (stream->buffer); |
| stream->buffer = NULL; |
| } |
| |
| gst_adapter_clear (stream->adapter); |
| |
| AIUR_RESET_SAMPLE_STAT (stream->sample_stat); |
| } |
| return ret; |
| } |
| |
| static void |
| aiurdemux_print_track_info (AiurDemuxStream * stream) |
| { |
| if (NULL == stream) |
| return; |
| if ((stream->pad) && (stream->caps)) { |
| gchar *mime = gst_caps_to_string (stream->caps); |
| gchar *padname = gst_pad_get_name (stream->pad); |
| g_print("------------------------\n"); |
| g_print (" Track %02d [%s] Enabled\n", stream->track_idx, |
| padname ? padname : ""); |
| |
| g_print ("\tDuration: %" GST_TIME_FORMAT "\n", |
| GST_TIME_ARGS (stream->track_duration)); |
| g_print ("\tLanguage: %s\n", stream->lang); |
| if (mime) { |
| g_print (" Mime:\n\t%s \r\n", mime); |
| g_free (mime); |
| } |
| if (padname) { |
| g_free (padname); |
| } |
| } else { |
| g_print (" Track %02d [%s]: Disabled\n", stream->track_idx, |
| AIUR_MEDIATYPE2STR (stream->type)); |
| g_print ("\tCodec: %ld, SubCodec: %ld\n", |
| stream->codec_type, stream->codec_sub_type); |
| } |
| g_print("------------------------\n"); |
| } |
| |
| |
| static gboolean aiurdemux_set_readmode (GstAiurDemux * demux) |
| { |
| AiurCoreInterface *IParser = demux->core_interface; |
| FslParserHandle handle = demux->core_handle; |
| |
| gchar *format = NULL; |
| demux->isMPEG = FALSE; |
| |
| int32 parser_result; |
| uint32 readmode; |
| |
| gboolean ret = gst_tag_list_get_string (demux->tag_list, GST_TAG_CONTAINER_FORMAT, &format); |
| if(ret && format && strncmp(format,"MPEG",4) == 0){ |
| demux->isMPEG = TRUE; |
| } |
| if(format ){ |
| g_free(format); |
| format = NULL; |
| } |
| |
| //use track mode for local file and file mode for streaming file. |
| if(aiurcontent_is_random_access(demux->content_info) && !demux->isMPEG){ |
| readmode = PARSER_READ_MODE_TRACK_BASED; |
| }else{ |
| readmode = PARSER_READ_MODE_FILE_BASED; |
| } |
| |
| do{ |
| parser_result = IParser->setReadMode(handle, readmode); |
| |
| if (parser_result != PARSER_SUCCESS) { |
| |
| //some parser does not have track mode API, so use file mode. |
| readmode = PARSER_READ_MODE_FILE_BASED; |
| parser_result = IParser->setReadMode(handle, readmode); |
| if (parser_result != PARSER_SUCCESS) |
| break; |
| } |
| |
| //check readmode API |
| if ((readmode == PARSER_READ_MODE_TRACK_BASED) && (IParser->getNextSample == NULL)) |
| break; |
| |
| if((readmode == PARSER_READ_MODE_FILE_BASED) && (IParser->getFileNextSample == NULL)) |
| break; |
| |
| demux->read_mode = readmode; |
| |
| if (readmode == PARSER_READ_MODE_FILE_BASED && |
| !(demux->isMPEG && aiurcontent_is_live(demux->content_info))){ |
| demux->interleave_queue_size = demux->option.interleave_queue_size; |
| GST_DEBUG_OBJECT(demux,"read mode = file mode"); |
| } else { |
| demux->interleave_queue_size = 0; |
| GST_DEBUG_OBJECT(demux,"read mode = track mode"); |
| } |
| |
| return TRUE; |
| |
| }while(0); |
| |
| return FALSE; |
| } |
| static gboolean |
| aiurdemux_parse_programs (GstAiurDemux * demux) |
| { |
| gboolean ret = FALSE; |
| if ((aiurdemux_parse_pmt (demux)) |
| || (aiurdemux_parse_program_info (demux))) { |
| aiurdemux_select_programs (demux); |
| ret = TRUE; |
| } |
| return ret; |
| } |
| static gboolean |
| aiurdemux_parse_pmt (GstAiurDemux * demux) |
| { |
| gboolean ret = FALSE; |
| int32 core_ret = PARSER_SUCCESS; |
| //AiurDemuxClipInfo *clip_info = &demux->clip_info; |
| AiurCoreInterface *IParser = demux->core_interface; |
| FslParserHandle handle = demux->core_handle; |
| UserDataID id = USER_DATA_PMT; |
| UserDataFormat format = USER_DATA_FORMAT_PMT_INFO; |
| PMTInfoList *pmt = NULL; |
| uint32 userDataSize = 0; |
| |
| core_ret = IParser->getMetaData(handle,id, &format, (uint8 **) & pmt, &userDataSize); |
| if ((core_ret == PARSER_SUCCESS) && (pmt != NULL) |
| && (userDataSize > 0)) { |
| int n; |
| for (n = 0; |
| ((n < demux->program_num) && (n < pmt->m_dwProgramNum)); n++) { |
| AiurDemuxProgram *program; |
| PMTInfo *pmtinfo = &pmt->m_ptPMTInfo[n]; |
| program = g_try_malloc (sizeof (AiurDemuxProgram) + |
| sizeof (AiurDemuxProgramTrack) * pmtinfo->m_dwTrackNum); |
| if (program) { |
| int m; |
| program->program_number = pmtinfo->m_dwChannel; |
| program->enabled = FALSE; |
| program->pid = pmtinfo->m_dwPID; |
| program->track_num = pmtinfo->m_dwTrackNum; |
| for (m = 0; m < program->track_num; m++) { |
| program->tracks[m].id = pmtinfo->m_ptTrackInfo[m].m_dwTrackNo; |
| program->tracks[m].pid = pmtinfo->m_ptTrackInfo[m].m_dwPID; |
| memcpy (program->tracks[m].lang, |
| pmtinfo->m_ptTrackInfo[m].m_byLan, 3); |
| } |
| demux->programs[n] = program; |
| } |
| } |
| ret = TRUE; |
| } |
| |
| return ret; |
| } |
| gboolean |
| aiurdemux_parse_program_info (GstAiurDemux * demux) |
| { |
| gboolean ret = FALSE; |
| int32 core_ret = PARSER_SUCCESS; |
| //AiurDemuxClipInfo *clip_info = &demux->clip_info; |
| AiurCoreInterface *IParser = demux->core_interface; |
| FslParserHandle handle = demux->core_handle; |
| UserDataID id = USER_DATA_PROGRAMINFO; |
| UserDataFormat format = USER_DATA_FORMAT_PROGRAM_INFO; |
| ProgramInfoMenu *pinfo = NULL; |
| uint32 userDataSize = 0; |
| int n; |
| |
| core_ret = IParser->getMetaData(handle, id, &format, (uint8 **) & pinfo, &userDataSize); |
| if ((core_ret != PARSER_SUCCESS) || (userDataSize == 0)) { |
| pinfo = NULL; |
| } |
| |
| for (n = 0; n < demux->program_num; n++) { |
| AiurDemuxProgram *program; |
| uint32 track_num, *tracklist; |
| core_ret = IParser->getProgramTracks(handle, n, |
| &track_num, &tracklist); |
| if (core_ret == PARSER_SUCCESS) { |
| program = |
| g_try_malloc (sizeof (AiurDemuxProgram) + |
| sizeof (AiurDemuxProgramTrack) * track_num); |
| if (program) { |
| int m; |
| program->enabled = FALSE; |
| if ((pinfo) && (n < pinfo->m_dwProgramNum)) { |
| program->program_number = pinfo->m_atProgramInfo[n].m_dwChannel; |
| program->pid = pinfo->m_atProgramInfo[n].m_dwPID; |
| } else { |
| program->program_number = -1; |
| program->pid = -1; |
| } |
| program->track_num = track_num; |
| for (m = 0; m < track_num; m++) { |
| program->tracks[m].pid = -1; |
| program->tracks[m].id = tracklist[m]; |
| program->tracks[m].lang[0] = '\0'; |
| } |
| demux->programs[n] = program; |
| } |
| } |
| } |
| ret = TRUE; |
| |
| return ret; |
| } |
| static void |
| aiurdemux_select_programs (GstAiurDemux * demux) |
| { |
| int n; |
| for (n = 0; n < demux->program_num; n++) { |
| AiurDemuxProgram *program; |
| program = demux->programs[n]; |
| if (program) { |
| if (demux->option.multiprogram_enabled) { |
| if (((demux->option.program_mask == 0)) |
| || ((1 << n) & demux->option.program_mask)) { |
| program->enabled = TRUE; |
| } |
| } else { |
| if (demux->option.program_number >= 0) { |
| if (demux->option.program_number == program->program_number) { |
| program->enabled = TRUE; |
| break; |
| } |
| } else { |
| program->enabled = TRUE; |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| |
| |
| |
| static GstTagList * |
| aiurdemux_add_user_tags (GstAiurDemux * demux) |
| { |
| int32 core_ret = 0; |
| AiurCoreInterface *IParser = demux->core_interface; |
| FslParserHandle handle = demux->core_handle; |
| GstTagList *list = demux->tag_list; |
| |
| int i; |
| |
| if (list == NULL) |
| return list; |
| |
| if (IParser->getMetaData) { |
| UserDataID id; |
| UserDataFormat format; |
| |
| for (i = 0; i < G_N_ELEMENTS (g_user_data_entry); i++) { |
| uint8 *userData = NULL; |
| uint32 userDataSize = 0; |
| |
| id = g_user_data_entry[i].core_tag; |
| format = g_user_data_entry[i].format; |
| core_ret = IParser->getMetaData(handle,id, &format, &userData, &userDataSize); |
| if ((core_ret == PARSER_SUCCESS) && |
| (userData != NULL) && (userDataSize > 0)) { |
| if (USER_DATA_FORMAT_UTF8 == format) { |
| GString *string = g_string_new_len ((const gchar *)userData, userDataSize); |
| if (string) { |
| /* FIXME : create GDate object for GST_TAG_DATA */ |
| if (USER_DATA_CREATION_DATE == id) { |
| guint y, m = 1, d = 1; |
| gint ret; |
| ret = sscanf (string->str, "%u-%u-%u", &y, &m, &d); |
| if (ret >= 1 && y > 1500 && y < 3000) { |
| GDate *date; |
| date = g_date_new_dmy (d, m, y); |
| gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, g_user_data_entry[i].gst_tag_name, |
| date, NULL); |
| GST_INFO_OBJECT (demux, g_user_data_entry[i].print_string, string->str); |
| g_date_free (date); |
| } |
| g_string_free (string, TRUE); |
| continue; |
| }else if(USER_DATA_LOCATION == id){ |
| gdouble latitude; |
| gdouble longitude; |
| guint longitude_pos = 0; |
| guint8* latitude_ptr = g_strndup ((gchar *) userData, 8); |
| guint8* longitude_ptr = g_strndup ((gchar *) userData+8, 9); |
| if ((sscanf (latitude_ptr, "%lf", &latitude) == 1) |
| && (sscanf (longitude_ptr, "%lf", &longitude) == 1)) { |
| gst_tag_list_add (list, GST_TAG_MERGE_APPEND, |
| GST_TAG_GEO_LOCATION_LATITUDE, latitude, NULL); |
| gst_tag_list_add (list, GST_TAG_MERGE_APPEND, |
| GST_TAG_GEO_LOCATION_LONGITUDE, longitude, NULL); |
| GST_INFO_OBJECT (demux, "LATITUDE=%lf,LONGITUDE=%lf", latitude,longitude); |
| } |
| g_free (latitude_ptr); |
| g_free (longitude_ptr); |
| g_string_free (string, TRUE); |
| continue; |
| }else if(USER_DATA_TRACKNUMBER == id || USER_DATA_TOTALTRACKNUMBER == id || USER_DATA_DISCNUMBER == id ){ |
| guint32 value; |
| value = atoi(string->str); |
| gst_tag_list_add (list, GST_TAG_MERGE_APPEND, |
| g_user_data_entry[i].gst_tag_name, value, NULL); |
| GST_INFO_OBJECT (demux, g_user_data_entry[i].print_string, string->str); |
| g_string_free (string, TRUE); |
| continue; |
| }else if (USER_DATA_RATING ==id) { |
| double value; |
| value = atof(string->str); |
| if (value <= 5.0) |
| value = value*20; |
| gst_tag_list_add (list, GST_TAG_MERGE_APPEND, |
| g_user_data_entry[i].gst_tag_name, (guint32)value, NULL); |
| GST_INFO_OBJECT (demux, g_user_data_entry[i].print_string, string->str); |
| g_string_free (string, TRUE); |
| continue; |
| } |
| gst_tag_list_add (list, GST_TAG_MERGE_APPEND, |
| g_user_data_entry[i].gst_tag_name, string->str, NULL); |
| |
| GST_INFO_OBJECT (demux, g_user_data_entry[i].print_string, string->str); |
| |
| g_string_free (string, TRUE); |
| } |
| } else if ((USER_DATA_FORMAT_JPEG == format) || |
| (USER_DATA_FORMAT_PNG == format) || |
| (USER_DATA_FORMAT_BMP == format) || |
| (USER_DATA_FORMAT_GIF == format)) { |
| GstSample *sample = gst_tag_image_data_to_image_sample (userData, |
| userDataSize, GST_TAG_IMAGE_TYPE_UNDEFINED); |
| if (sample) { |
| |
| gst_tag_list_add (list, GST_TAG_MERGE_APPEND, |
| g_user_data_entry[i].gst_tag_name, sample, NULL); |
| |
| GST_INFO_OBJECT (demux, g_user_data_entry[i].print_string, format,userDataSize); |
| |
| gst_sample_unref (sample); |
| } |
| } |
| } |
| } |
| } else if (IParser->getUserData) { |
| |
| for (i = 0; i < G_N_ELEMENTS (g_user_data_entry); i++) { |
| uint16 *userData = NULL; |
| uint32 userDataSize = 0; |
| |
| core_ret = IParser->getUserData(handle,g_user_data_entry[i].core_tag, &userData, &userDataSize); |
| if (core_ret == PARSER_SUCCESS) { |
| if ((userData) && (userDataSize)) { |
| gsize in, out; |
| gchar *value_utf8; |
| value_utf8 = |
| g_convert ((const char *) userData, userDataSize * 2, "UTF-8", |
| "UTF-16LE", &in, &out, NULL); |
| if (value_utf8) { |
| gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, |
| g_user_data_entry[i].gst_tag_name, value_utf8, NULL); |
| |
| GST_INFO_OBJECT (demux, g_user_data_entry[i].print_string, value_utf8); |
| |
| g_free (value_utf8); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| if (gst_tag_list_is_empty (list)) { |
| gst_tag_list_unref (list); |
| list = NULL; |
| } |
| |
| return list; |
| } |
| |
| static int aiurdemux_parse_streams (GstAiurDemux * demux) |
| { |
| AiurCoreInterface *IParser = demux->core_interface; |
| FslParserHandle handle = demux->core_handle; |
| int ret = PARSER_SUCCESS; |
| uint32 i = 0; |
| AiurDemuxStream *stream = NULL; |
| |
| |
| demux->n_streams = 0; |
| demux->n_video_streams = 0; |
| demux->n_audio_streams = 0; |
| demux->n_sub_streams = 0; |
| demux->sub_read_cnt = 0; |
| demux->sub_read_ready = 0; |
| |
| memset(demux->streams, 0,GST_AIURDEMUX_MAX_STREAMS*sizeof(AiurDemuxStream *)); |
| |
| for(i = 0; i < demux->track_count; i++){ |
| uint64 duration = 0; |
| stream = g_new0 (AiurDemuxStream, 1); |
| |
| if(stream == NULL){ |
| ret = PARSER_INSUFFICIENT_MEMORY; |
| break; |
| } |
| |
| memset(stream, 0, sizeof(AiurDemuxStream)); |
| stream->pid = -1; |
| stream->track_idx = i; |
| stream->partial_sample = FALSE; |
| |
| ret = IParser->getTrackType(handle, i, &stream->type, |
| &stream->codec_type, &stream->codec_sub_type); |
| |
| if(ret != PARSER_SUCCESS) |
| break; |
| |
| ret = IParser->getTrackDuration(handle, i, &duration); |
| |
| if(ret != PARSER_SUCCESS) |
| break; |
| stream->track_duration = AIUR_CORETS_2_GSTTS(duration); |
| |
| if(IParser->getLanguage){ |
| ret = IParser->getLanguage(handle, i, (uint8 *)stream->lang); |
| if(ret != PARSER_SUCCESS){ |
| stream->lang[0] = '\0'; |
| } |
| } |
| |
| ret = IParser->getBitRate(handle, i, &stream->bitrate); |
| if (ret != PARSER_SUCCESS) { |
| stream->bitrate = 0; |
| } |
| |
| ret = IParser->getDecoderSpecificInfo(handle, i, |
| &stream->codec_data.codec_data, &stream->codec_data.length); |
| |
| ret = PARSER_SUCCESS; |
| |
| |
| int m, n; |
| gboolean track_enable = TRUE; |
| for (m = 0; m < demux->program_num; m++) { |
| if (demux->programs[m]) { |
| for (n = 0; n < demux->programs[m]->track_num; n++) { |
| if (demux->programs[m]->tracks[n].id == stream->track_idx |
| && demux->programs[m]->enabled == FALSE) { |
| track_enable = FALSE; |
| break; |
| } |
| } |
| } |
| if (track_enable == FALSE) |
| break; |
| } |
| |
| if (track_enable == FALSE) |
| continue; |
| |
| |
| switch (stream->type) { |
| case MEDIA_VIDEO: |
| aiurdemux_parse_video (demux, stream, i); |
| break; |
| case MEDIA_AUDIO: |
| aiurdemux_parse_audio (demux, stream, i); |
| break; |
| case MEDIA_TEXT: |
| aiurdemux_parse_text (demux, stream, i); |
| break; |
| default: |
| break; |
| } |
| |
| if ((stream->send_codec_data) && (stream->codec_data.length) |
| && (stream->codec_data.length < AIURDEMUX_CODEC_DATA_MAX_LEN)) { |
| GstBuffer *gstbuf; |
| gstbuf = gst_buffer_new_and_alloc (stream->codec_data.length); |
| gst_buffer_fill(gstbuf,0,(guint8 *)stream->codec_data.codec_data,stream->codec_data.length); |
| |
| GST_DEBUG_OBJECT(demux,"set codec data for caps len=%d",stream->codec_data.length); |
| if(stream->caps){ |
| gst_caps_set_simple (stream->caps, "codec_data", |
| GST_TYPE_BUFFER, gstbuf, NULL); |
| } |
| gst_buffer_unref (gstbuf); |
| } |
| |
| aiurdemux_print_track_info (stream); |
| |
| if (stream->pad) { |
| //gboolean padRet = TRUE; |
| gchar *stream_id; |
| GST_PAD_ELEMENT_PRIVATE (stream->pad) = stream; |
| gst_pad_use_fixed_caps (stream->pad); |
| gst_pad_set_event_function (stream->pad, gst_aiurdemux_handle_src_event); |
| gst_pad_set_query_function (stream->pad, gst_aiurdemux_handle_src_query); |
| gst_pad_set_active (stream->pad, TRUE); |
| |
| |
| ret = IParser->enableTrack(handle, stream->track_idx, TRUE); |
| if(ret != PARSER_SUCCESS) |
| break; |
| |
| stream_id = |
| gst_pad_create_stream_id_printf (stream->pad, |
| GST_ELEMENT_CAST (demux), "%u", stream->track_idx); |
| gst_pad_push_event (stream->pad, gst_event_new_stream_start (stream_id)); |
| g_free (stream_id); |
| |
| gst_pad_set_caps (stream->pad, stream->caps); |
| |
| GST_INFO_OBJECT (demux, "adding pad %s %p to demux %p, caps string=%s", |
| GST_OBJECT_NAME (stream->pad), stream->pad, demux,gst_caps_to_string(stream->caps)); |
| gst_element_add_pad (GST_ELEMENT_CAST (demux), stream->pad); |
| |
| // global tags go on each pad anyway |
| stream->send_global_tags = TRUE; |
| |
| stream->mask = (1 << demux->n_streams); |
| stream->adapter = gst_adapter_new (); |
| |
| aiurdemux_reset_stream (demux, stream); |
| if (demux->interleave_queue_size) { |
| stream->buf_queue = g_queue_new (); |
| } |
| |
| demux->streams[demux->n_streams] = stream; |
| demux->n_streams++; |
| stream->discont = TRUE; |
| }else{ |
| IParser->enableTrack(handle, i, FALSE); |
| |
| if (stream) { |
| if (stream->caps) { |
| gst_caps_unref (stream->caps); |
| } |
| if (stream->pending_tags) { |
| gst_tag_list_unref (stream->pending_tags); |
| } |
| g_free (stream); |
| } |
| } |
| } |
| |
| if(ret != PARSER_SUCCESS){ |
| |
| for (; i < demux->track_count; i++) { |
| IParser->enableTrack(handle, i, FALSE); |
| } |
| } |
| GST_LOG_OBJECT(demux,"aiurdemux_parse_streams ret=%d",ret); |
| |
| return ret; |
| } |
| static void aiurdemux_parse_video (GstAiurDemux * demux, AiurDemuxStream * stream, |
| gint track_index) |
| { |
| gchar *mime = NULL, *codec = NULL; |
| gchar *padname; |
| |
| int32 parser_ret = PARSER_SUCCESS; |
| AiurCoreInterface *IParser = demux->core_interface; |
| FslParserHandle handle = demux->core_handle; |
| AiurdemuxCodecStruct * codec_struct = NULL; |
| int i; |
| |
| |
| memset(&stream->info.video, 0, sizeof(AiurDemuxVideoInfo)); |
| |
| parser_ret = IParser->getVideoFrameWidth(handle,track_index,&stream->info.video.width); |
| |
| if(parser_ret != PARSER_SUCCESS) |
| goto bail; |
| |
| parser_ret = IParser->getVideoFrameHeight(handle,track_index,&stream->info.video.height); |
| |
| if(parser_ret != PARSER_SUCCESS) |
| goto bail; |
| |
| parser_ret = IParser->getVideoFrameRate(handle,track_index,&stream->info.video.fps_n, |
| &stream->info.video.fps_d); |
| |
| if(parser_ret != PARSER_SUCCESS) |
| goto bail; |
| |
| if ((stream->info.video.fps_n == 0) || (stream->info.video.fps_d == 0) |
| || (stream->info.video.fps_n /stream->info.video.fps_d) > 250) { |
| stream->info.video.fps_n = AIURDEMUX_FRAME_N_DEFAULT; |
| stream->info.video.fps_d = AIURDEMUX_FRAME_D_DEFAULT; |
| } |
| |
| if ((stream->info.video.width == 0) || (stream->info.video.height == 0)) { |
| stream->info.video.width = AIURDEMUX_VIDEO_WIDTH_DEFAULT; |
| stream->info.video.height = AIURDEMUX_VIDEO_HEIGHT_DEFAULT; |
| } |
| |
| stream->send_codec_data = TRUE; |
| |
| for(i = 0; i < sizeof(aiurdemux_videocodec_tab)/sizeof(AiurdemuxCodecStruct); i++){ |
| codec_struct = &aiurdemux_videocodec_tab[i]; |
| if (stream->codec_type == codec_struct->type){ |
| if((codec_struct->subtype > 0) && (stream->codec_sub_type == codec_struct->subtype)){ |
| mime = (gchar *)codec_struct->mime; |
| codec = (gchar *)codec_struct->codec_str; |
| break; |
| }else if(codec_struct->subtype == 0){ |
| mime = (gchar *)codec_struct->mime; |
| codec = (gchar *)codec_struct->codec_str; |
| break; |
| } |
| } |
| } |
| |
| if(mime == NULL) |
| goto bail; |
| |
| if(stream->codec_type == VIDEO_H264){ |
| if(stream->codec_data.length > 0 && stream->codec_data.codec_data != NULL){ |
| mime = g_strdup_printf |
| ("%s, stream-format=(string)avc, width=(int)%ld, height=(int)%ld, framerate=(fraction)%ld/%ld", |
| mime, stream->info.video.width, stream->info.video.height, |
| stream->info.video.fps_n, stream->info.video.fps_d); |
| stream->send_codec_data = TRUE; |
| }else{ |
| mime = |
| g_strdup_printf |
| ("%s, stream-format=(string)byte-stream, width=(int)%ld, height=(int)%ld, framerate=(fraction)%ld/%ld", |
| mime, stream->info.video.width, stream->info.video.height, |
| stream->info.video.fps_n, stream->info.video.fps_d); |
| stream->send_codec_data = FALSE; |
| } |
| }else if (stream->codec_type == VIDEO_HEVC){ |
| if(stream->codec_data.length > 0 && stream->codec_data.codec_data != NULL){ |
| mime = g_strdup_printf |
| ("%s, stream-format=(string)hev1, width=(int)%ld, height=(int)%ld, framerate=(fraction)%ld/%ld", |
| mime, stream->info.video.width, stream->info.video.height, |
| stream->info.video.fps_n, stream->info.video.fps_d); |
| stream->send_codec_data = TRUE; |
| }else{ |
| mime = |
| g_strdup_printf |
| ("%s, stream-format=(string)byte-stream, width=(int)%ld, height=(int)%ld, framerate=(fraction)%ld/%ld", |
| mime, stream->info.video.width, stream->info.video.height, |
| stream->info.video.fps_n, stream->info.video.fps_d); |
| stream->send_codec_data = FALSE; |
| } |
| }else{ |
| mime = |
| g_strdup_printf |
| ("%s, width=(int)%ld, height=(int)%ld, framerate=(fraction)%ld/%ld", |
| mime, stream->info.video.width, stream->info.video.height, |
| stream->info.video.fps_n, stream->info.video.fps_d); |
| } |
| stream->caps = gst_caps_from_string (mime); |
| g_free (mime); |
| |
| if (stream->pid < 0) { |
| stream->pid = demux->n_video_streams; |
| } |
| |
| padname = g_strdup_printf ("video_%u", stream->pid); |
| |
| GST_INFO ("Create video pad %s \r\n", padname); |
| |
| stream->pad = |
| gst_pad_new_from_static_template (&gst_aiurdemux_videosrc_template, |
| padname); |
| g_free (padname); |
| |
| demux->n_video_streams++; |
| |
| stream->pending_tags = gst_tag_list_new (GST_TAG_CODEC, codec, NULL); |
| |
| if (stream->lang[0] != '\0') { |
| gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, |
| GST_TAG_LANGUAGE_CODE, stream->lang, NULL); |
| |
| } |
| |
| if (stream->bitrate) { |
| gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, |
| GST_TAG_BITRATE, stream->bitrate, NULL); |
| } |
| |
| if (demux->tag_list) { |
| |
| gst_tag_list_add (demux->tag_list, GST_TAG_MERGE_REPLACE, GST_TAG_VIDEO_CODEC, |
| codec, NULL); |
| } |
| |
| return; |
| |
| |
| bail: |
| GST_WARNING ("Unknown Video code-type=%d, sub-type=%d", stream->codec_type, stream->codec_sub_type); |
| return; |
| } |
| |
| static void aiurdemux_parse_audio (GstAiurDemux * demux, AiurDemuxStream * stream, |
| gint track_index) |
| { |
| gchar *mime = NULL; |
| const gchar *codec = NULL; |
| const gchar *codec_mime = NULL; |
| const gchar *stream_type = NULL; |
| gchar *padname; |
| |
| int32 parser_ret = PARSER_SUCCESS; |
| AiurCoreInterface *IParser = demux->core_interface; |
| FslParserHandle handle = demux->core_handle; |
| |
| |
| memset(&stream->info.audio, 0, sizeof(AiurDemuxAudioInfo)); |
| |
| parser_ret = IParser->getAudioNumChannels(handle,track_index,&stream->info.audio.n_channels); |
| |
| if(parser_ret != PARSER_SUCCESS) |
| goto bail; |
| |
| parser_ret = IParser->getAudioSampleRate(handle,track_index,&stream->info.audio.rate); |
| |
| if(parser_ret != PARSER_SUCCESS) |
| goto bail; |
| |
| if(IParser->getAudioBitsPerSample) |
| parser_ret = IParser->getAudioBitsPerSample(handle,track_index,&stream->info.audio.sample_width); |
| |
| if(parser_ret != PARSER_SUCCESS) |
| goto bail; |
| |
| if (stream->info.audio.n_channels == 0) { |
| stream->info.audio.n_channels = AIURDEMUX_AUDIO_CHANNEL_DEFAULT; |
| } |
| if (stream->info.audio.rate == 0) { |
| stream->info.audio.rate = AIURDEMUX_AUDIO_SAMPLERATE_DEFAULT; |
| } |
| if (stream->info.audio.sample_width == 0) { |
| stream->info.audio.sample_width = AIURDEMUX_AUDIO_SAMPLEWIDTH_DEFAULT; |
| } |
| |
| stream->send_codec_data = TRUE; |
| |
| switch (stream->codec_type) { |
| case AUDIO_AAC: |
| codec_mime = "audio/mpeg, mpegversion=(int)4"; |
| codec = "AAC"; |
| stream->send_codec_data= TRUE; |
| switch (stream->codec_sub_type) { |
| case AUDIO_AAC_ADTS: |
| stream_type = "adts"; |
| break; |
| case AUDIO_AAC_ADIF: |
| stream_type = "adif"; |
| break; |
| case AUDIO_AAC_RAW: |
| stream_type = "raw"; |
| break; |
| case AUDIO_ER_BSAC: |
| codec_mime = "audio/x-bsac"; |
| break; |
| default: |
| stream_type = NULL; |
| break; |
| } |
| if (stream_type) { |
| mime = |
| g_strdup_printf |
| ("%s, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld, stream-format=%s", |
| codec_mime, stream->info.audio.n_channels, |
| stream->info.audio.rate, stream->bitrate, stream_type); |
| } else { |
| mime = |
| g_strdup_printf |
| ("%s, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld", |
| codec_mime, stream->info.audio.n_channels, |
| stream->info.audio.rate, stream->bitrate); |
| } |
| break; |
| case AUDIO_MPEG2_AAC: |
| codec_mime = "audio/mpeg, mpegversion=(int)2"; |
| codec = "AAC"; |
| stream->send_codec_data = TRUE; |
| mime = |
| g_strdup_printf |
| ("%s, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld", codec_mime, |
| stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate); |
| break; |
| case AUDIO_MP3: |
| codec_mime = |
| "audio/mpeg, mpegversion=(int)1"; |
| codec = "MP3"; |
| mime = |
| g_strdup_printf |
| ("%s, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld", codec_mime, |
| stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate); |
| break; |
| case AUDIO_AC3: |
| codec_mime = "audio/x-ac3"; |
| codec = "AC3"; |
| mime = |
| g_strdup_printf |
| ("%s, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld, framed=(boolean)true", |
| codec_mime, stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate); |
| break; |
| case AUDIO_WMA: |
| parser_ret = IParser->getAudioBlockAlign(handle,track_index,&stream->info.audio.block_align); |
| |
| if(parser_ret != PARSER_SUCCESS) |
| goto bail; |
| switch (stream->codec_sub_type) { |
| case AUDIO_WMA1: |
| codec_mime = "audio/x-wma, wmaversion=(int)1"; |
| codec = "WMA7"; |
| break; |
| case AUDIO_WMA2: |
| codec_mime = "audio/x-wma, wmaversion=(int)2"; |
| codec = "WMA8"; |
| break; |
| case AUDIO_WMA3: |
| codec_mime = "audio/x-wma, wmaversion=(int)3"; |
| codec = "WMA9"; |
| break; |
| case AUDIO_WMALL: |
| codec_mime = "audio/x-wma, wmaversion=(int)4"; |
| codec = "WMA9 Lossless"; |
| break; |
| default: |
| goto bail; |
| break; |
| } |
| stream->send_codec_data = TRUE; |
| mime = |
| g_strdup_printf |
| ("%s, channels=(int)%ld, rate=(int)%ld, block_align=(int)%ld, depth=(int)%ld, bitrate=(int)%ld", |
| codec_mime, stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->info.audio.block_align, stream->info.audio.sample_width, |
| stream->bitrate); |
| break; |
| case AUDIO_WMS: |
| parser_ret = IParser->getAudioBlockAlign(handle,track_index,&stream->info.audio.block_align); |
| |
| if(parser_ret != PARSER_SUCCESS) |
| goto bail; |
| stream->send_codec_data = TRUE; |
| codec_mime = "audio/x-wms"; |
| codec = "WMA Voice"; |
| mime = |
| g_strdup_printf |
| ("%s, channels=(int)%ld, rate=(int)%ld, block_align=(int)%ld, depth=(int)%ld, bitrate=(int)%ld", |
| codec_mime, stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->info.audio.block_align, stream->info.audio.sample_width, |
| stream->bitrate); |
| break; |
| |
| case AUDIO_APE: |
| codec_mime = "audio/x-ffmpeg-parsed-ape"; |
| codec = "APE monkey's Audio"; |
| mime = |
| g_strdup_printf |
| ("%s, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld, framed=(boolean)true, depth=(int)%ld", |
| codec_mime, stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate, stream->info.audio.sample_width); |
| break; |
| |
| case AUDIO_PCM: |
| { |
| int width, depth, endian; |
| gboolean sign = TRUE; |
| switch (stream->codec_sub_type) { |
| case AUDIO_PCM_U8: |
| width = depth = 8; |
| endian = G_BYTE_ORDER; |
| sign = FALSE; |
| codec_mime = "format=(string)U8"; |
| break; |
| case AUDIO_PCM_S16LE: |
| width = depth = 16; |
| endian = G_LITTLE_ENDIAN; |
| codec_mime = "format=(string)S16LE"; |
| break; |
| case AUDIO_PCM_S24LE: |
| width = depth = 24; |
| endian = G_LITTLE_ENDIAN; |
| codec_mime = "format=(string)S24LE"; |
| break; |
| case AUDIO_PCM_S32LE: |
| width = depth = 32; |
| endian = G_LITTLE_ENDIAN; |
| codec_mime = "format=(string)S32LE"; |
| break; |
| case AUDIO_PCM_S16BE: |
| width = depth = 16; |
| endian = G_BIG_ENDIAN; |
| codec_mime = "format=(string)S16BE"; |
| break; |
| case AUDIO_PCM_S24BE: |
| width = depth = 24; |
| endian = G_BIG_ENDIAN; |
| codec_mime = "format=(string)S24BE"; |
| break; |
| case AUDIO_PCM_S32BE: |
| width = depth = 32; |
| endian = G_BIG_ENDIAN; |
| codec_mime = "format=(string)S32BE"; |
| break; |
| default: |
| goto bail; |
| break; |
| } |
| codec = "PCM"; |
| mime = |
| g_strdup_printf |
| ("audio/x-raw, %s,channels=(int)%ld, layout=(string)interleaved, rate=(int)%ld,bitrate=(int)%ld",codec_mime, |
| stream->info.audio.n_channels, stream->info.audio.rate,stream->bitrate); |
| } |
| break; |
| case AUDIO_REAL: |
| switch (stream->codec_sub_type) { |
| case REAL_AUDIO_RAAC: |
| mime = |
| g_strdup_printf |
| ("audio/mpeg, mpegversion=(int)4, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld", |
| stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate); |
| codec = "AAC"; |
| break; |
| case REAL_AUDIO_SIPR: |
| mime = |
| g_strdup_printf |
| ("audio/x-sipro, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld", |
| stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate); |
| codec = "SIPRO"; |
| break; |
| case REAL_AUDIO_ATRC: |
| /* mime = |
| g_strdup_printf |
| ("audio/x-vnd.sony.atrac3, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld", |
| stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate); |
| codec = "ATRC";*/ |
| break; |
| default: |
| { |
| uint32 frame_bit; |
| parser_ret = IParser->getAudioBitsPerFrame(handle,track_index,&frame_bit); |
| if(parser_ret != PARSER_SUCCESS) |
| goto bail; |
| mime = |
| g_strdup_printf |
| ("audio/x-pn-realaudio, channels=(int)%ld, rate=(int)%ld, frame_bit=(int)%ld", |
| stream->info.audio.n_channels, stream->info.audio.rate, |
| frame_bit); |
| codec = "RealAudio"; |
| stream->send_codec_data = TRUE; |
| } |
| break; |
| } |
| |
| break; |
| case AUDIO_VORBIS: |
| codec = "VORBIS"; |
| mime = |
| g_strdup_printf |
| ("audio/x-vorbis, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld, framed=(boolean)true", |
| stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate); |
| if(demux->option.disable_vorbis_codec_data){ |
| stream->send_codec_data = FALSE; |
| }else{ |
| stream->send_codec_data = TRUE; |
| } |
| break; |
| case AUDIO_FLAC: |
| codec = "FLAC"; |
| mime = |
| g_strdup_printf |
| ("audio/x-flac, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld", |
| stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate); |
| break; |
| case AUDIO_DTS: |
| codec = "DTS"; |
| mime = |
| g_strdup_printf |
| ("audio/x-dts, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld", |
| stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate); |
| break; |
| case AUIDO_SPEEX: |
| codec = "SPEEX"; |
| mime = |
| g_strdup_printf |
| ("audio/x-speex, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld", |
| stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate); |
| break; |
| case AUDIO_AMR: |
| switch (stream->codec_sub_type) { |
| case AUDIO_AMR_NB: |
| codec_mime = "audio/AMR"; |
| codec = "AMR-NB"; |
| if (stream->info.audio.rate != 8000) { |
| GST_WARNING |
| ("amr-nb should using 8k rate, this maybe a core parser BUG!"); |
| stream->info.audio.rate = 8000; |
| } |
| break; |
| case AUDIO_AMR_WB: |
| codec_mime = "audio/AMR-WB"; |
| codec = "AMR-WB"; |
| break; |
| default: |
| goto bail; |
| break; |
| } |
| stream->send_codec_data = TRUE; |
| //only support one channel amr |
| stream->info.audio.n_channels = 1; |
| mime = |
| g_strdup_printf |
| ("%s, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld", |
| codec_mime, stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->info.audio.sample_width, stream->bitrate); |
| break; |
| case AUDIO_EC3: |
| codec = "Dobly Digital Plus (E-AC3)"; |
| mime = |
| g_strdup_printf |
| ("audio/eac3, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld", |
| stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate); |
| break; |
| case AUDIO_OPUS: |
| codec = "OPUS"; |
| mime = |
| g_strdup_printf |
| ("audio/x-opus, channel-mapping-family=(int)0, channels=(int)%ld, rate=(int)%ld, bitrate=(int)%ld", |
| stream->info.audio.n_channels, stream->info.audio.rate, |
| stream->bitrate); |
| break; |
| default: |
| break; |
| } |
| |
| if(mime == NULL) |
| goto bail; |
| |
| stream->caps = gst_caps_from_string (mime); |
| g_free (mime); |
| |
| if (stream->pid < 0) { |
| stream->pid = demux->n_audio_streams; |
| } |
| |
| padname = g_strdup_printf ("audio_%u", stream->pid); |
| |
| GST_INFO ("Create audio pad %s", padname); |
| |
| stream->pad = |
| gst_pad_new_from_static_template (&gst_aiurdemux_audiosrc_template, |
| padname); |
| g_free (padname); |
| |
| demux->n_audio_streams++; |
| |
| stream->pending_tags = gst_tag_list_new (GST_TAG_CODEC, codec, NULL); |
| |
| if (stream->lang[0] != '\0') { |
| gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, |
| GST_TAG_LANGUAGE_CODE, stream->lang, NULL); |
| } |
| |
| if (stream->bitrate) { |
| gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, |
| GST_TAG_BITRATE, stream->bitrate, NULL); |
| } |
| |
| if (demux->tag_list) { |
| gst_tag_list_add (demux->tag_list, |
| GST_TAG_MERGE_REPLACE, GST_TAG_AUDIO_CODEC, codec, NULL); |
| } |
| |
| return; |
| |
| bail: |
| GST_WARNING ("Unknown Audio code-type=%d, sub-type=%d", |
| stream->codec_type, stream->codec_sub_type); |
| |
| return; |
| } |
| |
| static void aiurdemux_parse_text (GstAiurDemux * demux, AiurDemuxStream * stream, |
| gint track_index) |
| { |
| gchar *mime = NULL; |
| const gchar *codec_mime = NULL; |
| gchar *padname; |
| |
| int32 parser_ret = PARSER_SUCCESS; |
| AiurCoreInterface *IParser = demux->core_interface; |
| FslParserHandle handle = demux->core_handle; |
| |
| |
| parser_ret = IParser->getTextTrackWidth(handle,track_index,&stream->info.subtitle.width); |
| |
| if(parser_ret != PARSER_SUCCESS) |
| goto bail; |
| |
| parser_ret = IParser->getTextTrackHeight(handle,track_index,&stream->info.subtitle.height); |
| |
| if(parser_ret != PARSER_SUCCESS) |
| goto bail; |
| |
| stream->send_codec_data = TRUE; |
| |
| switch (stream->codec_type) { |
| #if 0 |
| case TXT_DIVX_FEATURE_SUBTITLE: |
| codec_mime = "subpicture/x-xsub"; |
| mime = |
| g_strdup_printf ("%s, width=(int)%ld, height=(int)%ld", codec_mime, |
| stream->info.subtitle.width, stream->info.subtitle.height); |
| break; |
| |
| case TXT_QT_TEXT: |
| codec_mime = "application/x-subtitle-qttext"; |
| mime = g_strdup_printf ("application/x-subtitle-qttext"); |
| break; |
| #endif |
| case TXT_3GP_STREAMING_TEXT: |
| case TXT_SUBTITLE_TEXT: |
| codec_mime = "text/x-raw"; |
| mime = g_strdup_printf ("text/x-raw, format=(string)pango-markup"); |
| break; |
| |
| case TXT_SUBTITLE_SSA: |
| codec_mime = "application/x-ssa"; |
| mime = g_strdup_printf ("application/x-ssa"); |
| break; |
| |
| case TXT_SUBTITLE_ASS: |
| codec_mime = "application/x-ass"; |
| mime = g_strdup_printf ("application/x-ass"); |
| break; |
| #if 0 |
| case TXT_DIVX_MENU_SUBTITLE: |
| case TXT_TYPE_UNKNOWN: |
| GST_WARNING ("Unknown Text code-type=%d, sub-type=%d", |
| stream->codec_type, stream->codec_sub_type); |
| codec_mime = "application/x-subtitle-unknown"; |
| mime = g_strdup_printf ("application/x-subtitle-unknown"); |
| break; |
| #endif |
| default: |
| GST_WARNING ("Unsupported Text code-type=%d, sub-type=%d", |
| stream->codec_type, stream->codec_sub_type); |
| goto bail; |
| } |
| |
| stream->caps = gst_caps_from_string (mime); |
| g_free (mime); |
| |
| if (stream->pid < 0) { |
| stream->pid = demux->n_sub_streams; |
| } |
| |
| padname = g_strdup_printf ("subtitle_%u", stream->pid); |
| |
| GST_INFO ("Create text pad %s", padname); |
| |
| stream->pad = |
| gst_pad_new_from_static_template (&gst_aiurdemux_subsrc_template, |
| padname); |
| g_free (padname); |
| |
| stream->pending_tags = gst_tag_list_new (GST_TAG_CODEC, codec_mime, NULL); |
| if (stream->lang[0] != '\0') { |
| gst_tag_list_add (stream->pending_tags, GST_TAG_MERGE_REPLACE, |
| GST_TAG_LANGUAGE_CODE, stream->lang, NULL); |
| } |
| |
| if (demux->tag_list) { |
| gst_tag_list_add (demux->tag_list, |
| GST_TAG_MERGE_REPLACE, GST_TAG_SUBTITLE_CODEC, codec_mime, NULL); |
| } |
| |
| demux->n_sub_streams++; |
| |
| bail: |
| return; |
| } |
| static void aiurdemux_check_interleave_stream_eos (GstAiurDemux * demux) |
| { |
| int n; |
| gint track_num = 0; |
| gint64 min_time = -1; |
| AiurDemuxStream *stream; |
| AiurDemuxStream *min_stream = NULL; |
| gboolean bQueueFull = FALSE; |
| if(demux->n_streams <= 1) |
| return; |
| for (n = 0; n < demux->n_streams; n++) { |
| stream = demux->streams[n]; |
| if (!stream->valid) { |
| continue; |
| } |
| if ((demux->interleave_queue_size) |
| && (stream->buf_queue_size > demux->interleave_queue_size)) { |
| GST_LOG_OBJECT(demux,"bQueueFull "); |
| bQueueFull = TRUE; |
| } |
| if(min_time < 0){ |
| min_time = stream->last_stop; |
| track_num = stream->track_idx; |
| min_stream = stream; |
| }else if (min_time > 0 && stream->last_stop < min_time ) { |
| min_time = stream->last_stop; |
| track_num = stream->track_idx; |
| min_stream = stream; |
| } |
| } |
| if(bQueueFull && (min_stream->buf_queue) && (g_queue_is_empty (min_stream->buf_queue))){ |
| MARK_STREAM_EOS (demux, min_stream); |
| GST_ERROR_OBJECT(demux,"file mode interleave detect !!! send EOS to track%d",track_num); |
| } |
| return; |
| |
| } |
| |
| static void |
| _gst_buffer_copy_into_mem (GstBuffer * dest, gsize offset, const guint8 * src, |
| gsize size) |
| { |
| gsize bsize; |
| |
| g_return_if_fail (gst_buffer_is_writable (dest)); |
| |
| bsize = gst_buffer_get_size (dest); |
| g_return_if_fail (bsize >= offset + size); |
| |
| gst_buffer_fill (dest, offset, src, size); |
| } |
| |
| static GstFlowReturn aiurdemux_read_buffer (GstAiurDemux * demux, uint32* track_idx, AiurDemuxStream** stream_out) |
| { |
| GstFlowReturn ret = GST_FLOW_OK; |
| int32 parser_ret = PARSER_ERR_UNKNOWN; |
| GstBuffer *gstbuf; |
| uint8 *buffer; |
| uint32 buffer_size; |
| uint64 usStartTime; |
| uint64 usDuration; |
| uint32 direction; |
| |
| uint32 sampleFlags = 0; |
| AiurCoreInterface *IParser = demux->core_interface; |
| FslParserHandle handle = demux->core_handle; |
| AiurDemuxStream *stream = NULL; |
| |
| |
| do{ |
| |
| gstbuf = NULL; |
| buffer = NULL; |
| buffer_size = 0; |
| usStartTime = 0; |
| usDuration = 0; |
| sampleFlags = 0; |
| |
| if(demux->play_mode != AIUR_PLAY_MODE_NORMAL){ |
| if (demux->play_mode == AIUR_PLAY_MODE_TRICK_FORWARD) { |
| direction = FLAG_FORWARD; |
| } else { |
| direction = FLAG_BACKWARD; |
| } |
| } |
| |
| if (demux->read_mode == PARSER_READ_MODE_FILE_BASED) { |
| |
| if (demux->play_mode == AIUR_PLAY_MODE_NORMAL) { |
| |
| parser_ret = IParser->getFileNextSample(handle,track_idx,&buffer,(void *) (&gstbuf), |
| &buffer_size,&usStartTime, &usDuration, &sampleFlags); |
| |
| }else { |
| |
| parser_ret = IParser->getFileNextSyncSample(handle, direction, track_idx, &buffer, |
| (void *) (&gstbuf), &buffer_size, &usStartTime, &usDuration, &sampleFlags); |
| } |
| |
| }else{ |
| |
| if (demux->play_mode == AIUR_PLAY_MODE_NORMAL) { |
| parser_ret = IParser->getNextSample(handle,(uint32)(*track_idx),&buffer,(void *) (&gstbuf), |
| &buffer_size,&usStartTime, &usDuration, &sampleFlags); |
| |
| }else{ |
| parser_ret = IParser->getNextSyncSample(handle, direction, (uint32)(*track_idx), &buffer, |
| (void *) (&gstbuf), &buffer_size, &usStartTime, &usDuration, &sampleFlags); |
| |
| } |
| } |
| |
| GST_LOG_OBJECT(demux,"aiurdemux_read_buffer ret=%d,track=%d,size=%d,time=%lld,duration=%lld,flag=%x", |
| parser_ret,*track_idx,buffer_size,usStartTime,usDuration,sampleFlags); |
| stream = aiurdemux_trackidx_to_stream (demux, *track_idx); |
| |
| if((parser_ret == PARSER_EOS) || (PARSER_BOS == parser_ret) |
| || (PARSER_READ_ERROR == parser_ret)){ |
| if (demux->read_mode == PARSER_READ_MODE_FILE_BASED) { |
| aiurdemux_send_stream_eos_all (demux); |
| ret = GST_FLOW_EOS; |
| goto beach; |
| }else{ |
| aiurdemux_send_stream_eos (demux, stream); |
| if (demux->valid_mask == 0) { |
| ret = GST_FLOW_EOS; |
| goto beach; |
| } |
| if(PARSER_READ_ERROR == parser_ret) |
| goto beach; |
| } |
| } else if (PARSER_ERR_INVALID_MEDIA == parser_ret || PARSER_SEEK_ERROR == parser_ret) { |
| GST_WARNING ("Movie parser interrupt, track_idx %d, error = %d", |
| *track_idx, parser_ret); |
| if (stream) { |
| aiurdemux_send_stream_eos (demux, stream); |
| if (demux->valid_mask == 0) { |
| ret = GST_FLOW_EOS; |
| } |
| } else { |
| aiurdemux_send_stream_eos_all (demux); |
| ret = GST_FLOW_EOS; |
| } |
| goto beach; |
| } else if (PARSER_NOT_READY == parser_ret && stream->type == MEDIA_TEXT) { |
| GST_WARNING ("read track not ready, track_idx %d", *track_idx); |
| int n; |
| gint64 min_time = G_MAXINT64; |
| for (n = 0; n < demux->n_streams; n++) { |
| if (!demux->streams[n]->valid || demux->streams[n]->type == MEDIA_TEXT) { |
| continue; |
| } |
| |
| if (GST_CLOCK_TIME_IS_VALID(demux->streams[n]->last_stop) && |
| GST_CLOCK_TIME_IS_VALID(demux->streams[n]->last_start) && |
| demux->streams[n]->last_stop < min_time) { |
| min_time = demux->streams[n]->last_stop; |
| } |
| } |
| |
| GST_INFO ("min_time=%lld\n", min_time); |
| |
| if (GST_CLOCK_TIME_IS_VALID(stream->time_position) && |
| min_time != G_MAXINT64 && |
| stream->time_position + GST_SECOND <= min_time) { |
| if (stream->new_segment) { |
| aiurdemux_send_stream_newsegment (demux, stream); |
| } |
| |
| GstEvent *gap = gst_event_new_gap (stream->time_position, GST_SECOND); |
| stream->last_start = stream->time_position; |
| stream->last_stop = stream->time_position + GST_SECOND; |
| |
| gst_pad_push_event (stream->pad, gap); |
| GST_INFO ("TEXT GAP event sent %d, time_position=%lld, " |
| "last_start=%lld, last_stop=%lld\n", *track_idx, |
| stream->time_position, stream->last_start, stream->last_stop); |
| |
| stream->time_position = stream->last_stop; |
| } |
| |
| goto readend; |
| } else if (PARSER_SUCCESS != parser_ret) { |
| GST_ERROR ("Movie parser failed, error = %d", parser_ret); |
| aiurdemux_send_stream_eos_all (demux); |
| ret = GST_FLOW_ERROR; |
| goto beach; |
| } |
| |
| if(gstbuf && gst_buffer_get_size(gstbuf) != buffer_size){ |
| gst_buffer_set_size(gstbuf,buffer_size); |
| } |
| |
| if ((sampleFlags & FLAG_SAMPLE_CODEC_DATA) && (stream->send_codec_data) && (buffer_size)) { |
| GstCaps *caps; |
| |
| caps = gst_caps_copy(stream->caps); |
| if(caps){ |
| gst_caps_set_simple (caps, "codec_data", GST_TYPE_BUFFER, gstbuf, NULL); |
| } |
| |
| gst_buffer_unref (gstbuf); |
| |
| gst_caps_unref(stream->caps); |
| stream->caps = caps; |
| |
| gst_pad_set_caps (stream->pad, stream->caps); |
| |
| return ret; |
| } |
| |
| |
| AIUR_UPDATE_SAMPLE_STAT (stream->sample_stat, |
| AIUR_CORETS_2_GSTTS (usStartTime), |
| AIUR_COREDURATION_2_GSTDURATION (usDuration), sampleFlags); |
| if(!(sampleFlags & FLAG_SAMPLE_NOT_FINISHED) && stream->partial_sample == FALSE){ |
| stream->buffer = gstbuf; |
| GST_LOG_OBJECT(demux,"get stream buffer 1"); |
| }else{ |
| |
| if(buffer_size > 0){ |
| stream->adapter_buffer_size += gst_buffer_get_size(gstbuf); |
| gst_adapter_push (stream->adapter, gstbuf); |
| gstbuf = NULL; |
| GST_LOG_OBJECT(demux,"push to adapter 2 "); |
| } |
| |
| if (sampleFlags & FLAG_SAMPLE_NOT_FINISHED) { |
| stream->partial_sample = TRUE; |
| }else{ |
| gint sample_size = 0; |
| stream->partial_sample = FALSE; |
| sample_size = gst_adapter_available (stream->adapter); |
| if(sample_size > 0){ |
| stream->buffer = gst_adapter_take_buffer (stream->adapter, stream->adapter_buffer_size); |
| |
| stream->adapter_buffer_size = 0; |
| gst_adapter_clear (stream->adapter); |
| GST_LOG_OBJECT(demux,"get stream buffer 2"); |
| } |
| |
| } |
| |
| } |
| |
| readend: |
| ; |
| }while(sampleFlags & FLAG_SAMPLE_NOT_FINISHED); |
| if(stream->buffer){ |
| if (!(stream->sample_stat.flag & FLAG_SYNC_SAMPLE)) { |
| GST_BUFFER_FLAG_SET (stream->buffer, GST_BUFFER_FLAG_DELTA_UNIT); |
| } |
| |
| if (demux->play_mode != AIUR_PLAY_MODE_NORMAL && stream->buffer) { |
| GST_BUFFER_FLAG_SET (stream->buffer, GST_BUFFER_FLAG_DISCONT); |
| } |
| } |
| |
| if (stream->sample_stat.start == stream->last_start |
| && !(stream->type == MEDIA_VIDEO && stream->codec_type == VIDEO_ON2_VP)) { |
| stream->sample_stat.start = GST_CLOCK_TIME_NONE; |
| } |
| |
| *stream_out = stream; |
| return ret; |
| beach: |
| if (stream) { |
| if (stream->buffer) { |
| gst_buffer_unref (stream->buffer); |
| stream->buffer = NULL; |
| } |
| |
| gst_adapter_clear (stream->adapter); |
| |
| stream->sample_stat.duration = 0; |
| stream->sample_stat.start = GST_CLOCK_TIME_NONE; |
| stream->sample_stat.flag = 0; |
| |
| } |
| return ret; |
| |
| } |
| static GstFlowReturn aiurdemux_parse_vorbis_codec_data(GstAiurDemux * demux, AiurDemuxStream* stream) |
| { |
| int offset = 0; |
| int32 header1 = 0; |
| int32 header2 = 0; |
| int32 header3 = 0; |
| int32 len1 = 0; |
| int32 len2 = 0; |
| int32 len3 = 0; |
| uint8 * inbuffer; |
| uint8 * vorbisPtr; |
| if(demux == NULL || stream == NULL) |
| return GST_FLOW_ERROR; |
| if(stream->codec_data.length == 0 || stream->codec_data.codec_data == NULL) |
| return GST_FLOW_ERROR; |
| |
| inbuffer = stream->codec_data.codec_data; |
| while(offset + 6 < stream->codec_data.length){ |
| vorbisPtr = inbuffer+offset+1; |
| if(inbuffer[offset] == 0x01){ |
| if(!memcmp(vorbisPtr,"vorbis",6)){ |
| header1 = offset; |
| offset += 6; |
| } |
| }else if(inbuffer[offset] == 0x03){ |
| if(!memcmp(vorbisPtr,"vorbis",6)){ |
| header2 = offset; |
| offset += 6; |
| } |
| }else if(inbuffer[offset] == 0x05){ |
| if(!memcmp(vorbisPtr,"vorbis",6)){ |
| header3 = offset; |
| break; |
| } |
| } |
| offset ++; |
| } |
| len1 = header2 - header1; |
| len2 = header3 - header2; |
|