From e00c30657197551ccb52d8ad83fa495b93875240 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Wed, 5 Mar 2014 16:34:42 -0300 Subject: [PATCH] oggmux: create vp8 header data if not provided in caps vp8 stream header shouldn't be assumed to be provided in caps always as this would repeat the same code in all demuxers/encoders. Instead, make oggmux generate them if they are not supplied. https://bugzilla.gnome.org/show_bug.cgi?id=722682 --- ext/ogg/gstoggmux.c | 9 +++ ext/ogg/gstoggstream.c | 165 ++++++++++++++++++++++++++++++++++++++--- ext/ogg/gstoggstream.h | 3 + 3 files changed, 167 insertions(+), 10 deletions(-) diff --git a/ext/ogg/gstoggmux.c b/ext/ogg/gstoggmux.c index 36345a874a..4f12ed70c6 100644 --- a/ext/ogg/gstoggmux.c +++ b/ext/ogg/gstoggmux.c @@ -1008,6 +1008,11 @@ gst_ogg_mux_queue_pads (GstOggMux * ogg_mux, gboolean * popped) /* fallback on the packet */ pad->have_type = gst_ogg_stream_setup_map (&pad->map, &packet); } + if (!pad->have_type) { + /* fallback 2 to try to get the mapping from the caps */ + pad->have_type = + gst_ogg_stream_setup_map_from_caps (&pad->map, caps); + } if (!pad->have_type) { GST_ERROR_OBJECT (data->pad, "mapper didn't recognise input stream " "(pad caps: %" @@ -1097,6 +1102,7 @@ gst_ogg_mux_get_headers (GstOggPadData * pad) GstCaps *caps; const GValue *streamheader; GstPad *thepad; + GstBuffer *header; thepad = pad->collect.pad; @@ -1138,6 +1144,9 @@ gst_ogg_mux_get_headers (GstOggPadData * pad) } else if (gst_structure_has_name (structure, "video/x-dirac")) { res = g_list_append (res, pad->buffer); pad->buffer = NULL; + } else if (pad->have_type + && (header = gst_ogg_stream_get_headers (&pad->map))) { + res = g_list_append (res, header); } else { GST_LOG_OBJECT (thepad, "caps don't have streamheader"); } diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index 045985100b..145fabc187 100644 --- a/ext/ogg/gstoggstream.c +++ b/ext/ogg/gstoggstream.c @@ -40,6 +40,8 @@ typedef struct _GstOggMap GstOggMap; typedef gboolean (*GstOggMapSetupFunc) (GstOggStream * pad, ogg_packet * packet); +typedef gboolean (*GstOggMapSetupFromCapsFunc) (GstOggStream * pad, + const GstCaps * caps); typedef GstClockTime (*GstOggMapToTimeFunc) (GstOggStream * pad, gint64 granulepos); typedef gint64 (*GstOggMapToGranuleFunc) (GstOggStream * pad, @@ -66,6 +68,8 @@ typedef void (*GstOggMapExtractTagsFunc) (GstOggStream * pad, typedef gint64 (*GstOggMapGranuleposToKeyGranuleFunc) (GstOggStream * pad, gint64 granulepos); +typedef GstBuffer *(*GstOggMapGetHeadersFunc) (GstOggStream * pad); + #define SKELETON_FISBONE_MIN_SIZE 52 #define SKELETON_FISHEAD_3_3_MIN_SIZE 112 #define SKELETON_FISHEAD_4_0_MIN_SIZE 80 @@ -77,6 +81,7 @@ struct _GstOggMap int min_packet_size; const gchar *media_type; GstOggMapSetupFunc setup_func; + GstOggMapSetupFromCapsFunc setup_from_caps_func; GstOggMapToGranuleFunc granulepos_to_granule_func; GstOggMapToGranuleposFunc granule_to_granulepos_func; GstOggMapIsGranuleposKeyFrameFunc is_granulepos_key_frame_func; @@ -85,6 +90,7 @@ struct _GstOggMap GstOggMapPacketDurationFunc packet_duration_func; GstOggMapGranuleposToKeyGranuleFunc granulepos_to_key_granule_func; GstOggMapExtractTagsFunc extract_tags_func; + GstOggMapGetHeadersFunc get_headers_func; }; extern const GstOggMap mappers[]; @@ -265,6 +271,15 @@ gst_ogg_stream_get_media_type (GstOggStream * pad) return gst_structure_get_name (structure); } +GstBuffer * +gst_ogg_stream_get_headers (GstOggStream * pad) +{ + if (!mappers[pad->map].get_headers_func) + return NULL; + + return mappers[pad->map].get_headers_func (pad); +} + /* some generic functions */ static gboolean @@ -650,6 +665,50 @@ setup_vp8_mapper (GstOggStream * pad, ogg_packet * packet) return TRUE; } +static gboolean +vp8_fill_header (GstOggStream * pad, const GstCaps * caps, guint8 * data) +{ + gint width, height, par_n, par_d, fps_n, fps_d; + GstStructure *structure = gst_caps_get_structure (caps, 0); + + if (!(gst_structure_get_int (structure, "width", &width) && + gst_structure_get_int (structure, "height", &height) && + gst_structure_get_fraction (structure, "framerate", &fps_n, + &fps_d))) { + GST_DEBUG ("Failed to get width, height or framerate from caps %" + GST_PTR_FORMAT, caps); + return FALSE; + } + if (!gst_structure_get_fraction (structure, "pixel-aspect-ratio", &par_n, + &par_d)) { + par_n = par_d = 1; + } + + memcpy (data, "OVP80\1\1", 8); + GST_WRITE_UINT16_BE (data + 8, width); + GST_WRITE_UINT16_BE (data + 10, height); + GST_WRITE_UINT24_BE (data + 12, par_n); + GST_WRITE_UINT24_BE (data + 15, par_d); + GST_WRITE_UINT32_BE (data + 18, fps_n); + GST_WRITE_UINT32_BE (data + 22, fps_d); + + return TRUE; +} + +static gboolean +setup_vp8_mapper_from_caps (GstOggStream * pad, const GstCaps * caps) +{ + guint8 data[26]; + ogg_packet packet; + + if (!vp8_fill_header (pad, caps, data)) + return FALSE; + + packet.packet = data; + packet.bytes = 26; + return setup_vp8_mapper (pad, &packet); +} + static gboolean is_keyframe_vp8 (GstOggStream * pad, gint64 granulepos) { @@ -742,6 +801,18 @@ extract_tags_vp8 (GstOggStream * pad, ogg_packet * packet) } } +static GstBuffer * +get_headers_vp8 (GstOggStream * pad) +{ + guint8 *data = g_malloc (26); + + if (vp8_fill_header (pad, pad->caps, data)) { + return gst_buffer_new_wrapped (data, 26); + } + g_free (data); + return NULL; +} + /* vorbis */ static gboolean @@ -2141,6 +2212,7 @@ const GstOggMap mappers[] = { "\200theora", 7, 42, "video/x-theora", setup_theora_mapper, + NULL, granulepos_to_granule_theora, granule_to_granulepos_default, is_granulepos_keyframe_theora, @@ -2148,12 +2220,14 @@ const GstOggMap mappers[] = { is_header_theora, packet_duration_constant, NULL, - extract_tags_theora + extract_tags_theora, + NULL }, { "\001vorbis", 7, 22, "audio/x-vorbis", setup_vorbis_mapper, + NULL, granulepos_to_granule_default, granule_to_granulepos_default, is_granulepos_keyframe_true, @@ -2161,12 +2235,14 @@ const GstOggMap mappers[] = { is_header_vorbis, packet_duration_vorbis, NULL, - extract_tags_vorbis + extract_tags_vorbis, + NULL }, { "Speex", 5, 80, "audio/x-speex", setup_speex_mapper, + NULL, granulepos_to_granule_default, granule_to_granulepos_default, is_granulepos_keyframe_true, @@ -2174,7 +2250,8 @@ const GstOggMap mappers[] = { is_header_count, packet_duration_constant, NULL, - extract_tags_count + extract_tags_count, + NULL }, { "PCM ", 8, 0, @@ -2184,9 +2261,11 @@ const GstOggMap mappers[] = { NULL, NULL, NULL, + NULL, is_header_count, NULL, NULL, + NULL, NULL }, { @@ -2197,15 +2276,18 @@ const GstOggMap mappers[] = { NULL, NULL, NULL, + NULL, is_header_count, NULL, NULL, + NULL, NULL }, { "Annodex", 7, 0, "application/x-annodex", setup_fishead_mapper, + NULL, granulepos_to_granule_default, granule_to_granulepos_default, NULL, @@ -2213,6 +2295,7 @@ const GstOggMap mappers[] = { is_header_count, NULL, NULL, + NULL, NULL }, { @@ -2223,15 +2306,18 @@ const GstOggMap mappers[] = { NULL, NULL, NULL, + NULL, is_header_true, NULL, NULL, + NULL, NULL }, { "fLaC", 4, 0, "audio/x-flac", setup_fLaC_mapper, + NULL, granulepos_to_granule_default, granule_to_granulepos_default, is_granulepos_keyframe_true, @@ -2239,12 +2325,14 @@ const GstOggMap mappers[] = { is_header_fLaC, packet_duration_flac, NULL, + NULL, NULL }, { "\177FLAC", 5, 36, "audio/x-flac", setup_flac_mapper, + NULL, granulepos_to_granule_default, granule_to_granulepos_default, is_granulepos_keyframe_true, @@ -2252,7 +2340,8 @@ const GstOggMap mappers[] = { is_header_flac, packet_duration_flac, NULL, - extract_tags_flac + extract_tags_flac, + NULL }, { "AnxData", 7, 0, @@ -2264,12 +2353,15 @@ const GstOggMap mappers[] = { NULL, NULL, NULL, + NULL, + NULL, NULL }, { "CELT ", 8, 0, "audio/x-celt", setup_celt_mapper, + NULL, granulepos_to_granule_default, granule_to_granulepos_default, NULL, @@ -2277,12 +2369,14 @@ const GstOggMap mappers[] = { is_header_count, packet_duration_constant, NULL, - extract_tags_count + extract_tags_count, + NULL }, { "\200kate\0\0\0", 8, 0, "text/x-kate", setup_kate_mapper, + NULL, granulepos_to_granule_default, granule_to_granulepos_default, NULL, @@ -2290,12 +2384,14 @@ const GstOggMap mappers[] = { is_header_count, packet_duration_kate, NULL, - extract_tags_kate + extract_tags_kate, + NULL }, { "BBCD\0", 5, 13, "video/x-dirac", setup_dirac_mapper, + NULL, granulepos_to_granule_dirac, granule_to_granulepos_dirac, is_keyframe_dirac, @@ -2303,12 +2399,14 @@ const GstOggMap mappers[] = { is_header_count, packet_duration_constant, granulepos_to_key_granule_dirac, + NULL, NULL }, { "OVP80\1\1", 7, 4, "video/x-vp8", setup_vp8_mapper, + setup_vp8_mapper_from_caps, granulepos_to_granule_vp8, granule_to_granulepos_vp8, is_keyframe_vp8, @@ -2316,12 +2414,14 @@ const GstOggMap mappers[] = { is_header_vp8, packet_duration_vp8, granulepos_to_key_granule_vp8, - extract_tags_vp8 + extract_tags_vp8, + get_headers_vp8 }, { "OpusHead", 8, 0, "audio/x-opus", setup_opus_mapper, + NULL, granulepos_to_granule_opus, granule_to_granulepos_default, NULL, @@ -2329,12 +2429,14 @@ const GstOggMap mappers[] = { is_header_opus, packet_duration_opus, NULL, - extract_tags_opus + extract_tags_opus, + NULL }, { "\001audio\0\0\0", 9, 53, "application/x-ogm-audio", setup_ogmaudio_mapper, + NULL, granulepos_to_granule_default, granule_to_granulepos_default, is_granulepos_keyframe_true, @@ -2342,12 +2444,14 @@ const GstOggMap mappers[] = { is_header_ogm, packet_duration_ogm, NULL, + NULL, NULL }, { "\001video\0\0\0", 9, 53, "application/x-ogm-video", setup_ogmvideo_mapper, + NULL, granulepos_to_granule_default, granule_to_granulepos_default, NULL, @@ -2355,12 +2459,14 @@ const GstOggMap mappers[] = { is_header_ogm, packet_duration_constant, NULL, + NULL, NULL }, { "\001text\0\0\0", 9, 9, "application/x-ogm-text", setup_ogmtext_mapper, + NULL, granulepos_to_granule_default, granule_to_granulepos_default, is_granulepos_keyframe_true, @@ -2368,12 +2474,14 @@ const GstOggMap mappers[] = { is_header_ogm, packet_duration_ogm, NULL, - extract_tags_ogm + extract_tags_ogm, + NULL }, { "\200daala", 6, 42, "video/x-daala", setup_daala_mapper, + NULL, granulepos_to_granule_daala, granule_to_granulepos_default, is_granulepos_keyframe_daala, @@ -2381,7 +2489,8 @@ const GstOggMap mappers[] = { is_header_daala, packet_duration_constant, NULL, - extract_tags_daala + extract_tags_daala, + NULL }, }; @@ -2419,6 +2528,42 @@ gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet * packet) return FALSE; } +gboolean +gst_ogg_stream_setup_map_from_caps (GstOggStream * pad, const GstCaps * caps) +{ + int i; + gboolean ret; + GstStructure *structure; + + g_return_val_if_fail (caps != NULL, FALSE); + + structure = gst_caps_get_structure (caps, 0); + + for (i = 0; i < G_N_ELEMENTS (mappers); i++) { + if (mappers[i].setup_from_caps_func && + gst_structure_has_name (structure, mappers[i].media_type)) { + + GST_DEBUG ("found mapper for '%s'", mappers[i].id); + + if (mappers[i].setup_from_caps_func) + ret = mappers[i].setup_from_caps_func (pad, caps); + else + continue; + + if (ret) { + GST_DEBUG ("got stream type %" GST_PTR_FORMAT, pad->caps); + pad->map = i; + return TRUE; + } else { + GST_WARNING ("mapper '%s' did not accept caps %" GST_PTR_FORMAT, + mappers[i].media_type, caps); + } + } + } + + return FALSE; +} + gboolean gst_ogg_stream_setup_map_from_caps_headers (GstOggStream * pad, const GstCaps * caps) diff --git a/ext/ogg/gstoggstream.h b/ext/ogg/gstoggstream.h index 7d2c2caaea..a6da79e21b 100644 --- a/ext/ogg/gstoggstream.h +++ b/ext/ogg/gstoggstream.h @@ -116,6 +116,8 @@ struct _GstOggStream gboolean gst_ogg_stream_setup_map (GstOggStream * pad, ogg_packet *packet); gboolean gst_ogg_stream_setup_map_from_caps_headers (GstOggStream * pad, const GstCaps * caps); +gboolean gst_ogg_stream_setup_map_from_caps (GstOggStream * pad, + const GstCaps * caps); GstClockTime gst_ogg_stream_get_end_time_for_granulepos (GstOggStream *pad, gint64 granulepos); GstClockTime gst_ogg_stream_get_start_time_for_granulepos (GstOggStream *pad, @@ -133,6 +135,7 @@ gboolean gst_ogg_stream_packet_is_key_frame (GstOggStream *pad, ogg_packet *pack gint64 gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet *packet); void gst_ogg_stream_extract_tags (GstOggStream * pad, ogg_packet * packet); const char *gst_ogg_stream_get_media_type (GstOggStream * pad); +GstBuffer *gst_ogg_stream_get_headers (GstOggStream *pad); gboolean gst_ogg_map_parse_fisbone (GstOggStream * pad, const guint8 * data, guint size, guint32 * serialno, GstOggSkeleton *type);