diff --git a/ext/ogg/gstoggdemux.c b/ext/ogg/gstoggdemux.c index c29e0e44a9..36b71ebc62 100644 --- a/ext/ogg/gstoggdemux.c +++ b/ext/ogg/gstoggdemux.c @@ -437,7 +437,7 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet, GST_BUFFER_SIZE (buf) = bytes; tags = gst_tag_list_from_vorbiscomment_buffer (buf, - (guint8 *) "\003vorbis", 7, NULL); + (const guint8 *) "\003vorbis", 7, NULL); gst_buffer_unref (buf); buf = NULL; @@ -465,6 +465,39 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet, bytes--; } } + } else if (pad->map.is_vp8) { + /* packet 0 is from the BOS page, packet 1 is the vorbiscomment page */ + if (packet->packetno == 1 && packet->bytes >= 7 + && memcmp (packet->packet, "VP8_TAG", 7) == 0) { + GstTagList *tags; + + buf = gst_buffer_new (); + + GST_BUFFER_DATA (buf) = (guint8 *) packet->packet; + GST_BUFFER_SIZE (buf) = packet->bytes; + + tags = gst_tag_list_from_vorbiscomment_buffer (buf, + (const guint8 *) "VP8_TAG", 7, NULL); + gst_buffer_unref (buf); + buf = NULL; + + if (tags) { + GST_DEBUG_OBJECT (ogg, "tags = %" GST_PTR_FORMAT, tags); + gst_element_found_tags_for_pad (GST_ELEMENT (ogg), GST_PAD_CAST (pad), + tags); + } else { + GST_DEBUG_OBJECT (ogg, "failed to extract tags from vorbis comment"); + } + /* We don't push header packets for VP8 */ + cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK); + goto done; + } else if (packet->b_o_s) { + /* We don't push header packets for VP8 */ + cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK); + goto done; + } + offset = 0; + trim = 0; } else { offset = 0; trim = 0; @@ -524,8 +557,8 @@ gst_ogg_demux_chain_peer (GstOggPad * pad, ogg_packet * packet, pad->current_granule) - out_timestamp; } out_offset_end = - gst_ogg_stream_granule_to_granulepos (&pad->map, pad->current_granule, - pad->keyframe_granule); + gst_ogg_stream_granule_to_granulepos (&pad->map, + pad->current_granule, pad->keyframe_granule); out_offset = gst_ogg_stream_granule_to_time (&pad->map, pad->current_granule); } @@ -629,12 +662,14 @@ empty_packet: cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK); goto done; } + no_timestamp: { GST_DEBUG_OBJECT (ogg, "skipping packet: no valid granule found yet"); cret = gst_ogg_demux_combine_flows (ogg, pad, GST_FLOW_OK); goto done; } + no_buffer: { GST_DEBUG_OBJECT (ogg, @@ -1485,8 +1520,8 @@ error: * is. */ static GstFlowReturn -gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary, - gint64 * offset) +gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, + gint64 boundary, gint64 * offset) { gint64 end_offset = -1; GstFlowReturn ret; @@ -1511,8 +1546,8 @@ gst_ogg_demux_get_next_page (GstOggDemux * ogg, ogg_page * og, gint64 boundary, if (more < 0) { /* skipped n bytes */ ogg->offset -= more; - GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT, more, - ogg->offset); + GST_LOG_OBJECT (ogg, "skipped %ld bytes, offset %" G_GINT64_FORMAT, + more, ogg->offset); } else if (more == 0) { /* we need more data */ if (boundary == 0) @@ -1783,8 +1818,8 @@ do_binary_search (GstOggDemux * ogg, GstOggChain * chain, gint64 begin, best = begin; GST_DEBUG_OBJECT (ogg, - "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT, begin, - end); + "chain offset %" G_GINT64_FORMAT ", end offset %" G_GINT64_FORMAT, + begin, end); GST_DEBUG_OBJECT (ogg, "chain begin time %" GST_TIME_FORMAT ", end time %" GST_TIME_FORMAT, GST_TIME_ARGS (begintime), GST_TIME_ARGS (endtime)); @@ -2048,8 +2083,8 @@ gst_ogg_demux_do_seek (GstOggDemux * ogg, GstSegment * segment, continue; /* convert granule of this pad to the granule of the keyframe */ - pad->keyframe_granule = gst_ogg_stream_granulepos_to_key_granule (&pad->map, - granulepos); + pad->keyframe_granule = + gst_ogg_stream_granulepos_to_key_granule (&pad->map, granulepos); GST_LOG_OBJECT (ogg, "marking stream granule %" G_GINT64_FORMAT, pad->keyframe_granule); @@ -2894,8 +2929,8 @@ gst_ogg_demux_find_chains (GstOggDemux * ogg) /* we still call this function here but with an empty range so that * we can reuse the setup code in this routine. */ ret = - gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, ogg->length, - chain, 0); + gst_ogg_demux_bisect_forward_serialno (ogg, 0, ogg->length, + ogg->length, chain, 0); } if (ret != GST_FLOW_OK) goto done; @@ -3550,8 +3585,9 @@ gst_ogg_print (GstOggDemux * ogg) GstOggChain *chain = g_array_index (ogg->chains, GstOggChain *, i); GST_INFO_OBJECT (ogg, " chain %d (%u streams):", i, chain->streams->len); - GST_INFO_OBJECT (ogg, " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, - chain->offset, chain->end_offset); + GST_INFO_OBJECT (ogg, + " offset: %" G_GINT64_FORMAT " - %" G_GINT64_FORMAT, chain->offset, + chain->end_offset); GST_INFO_OBJECT (ogg, " begin time: %" GST_TIME_FORMAT, GST_TIME_ARGS (chain->begin_time)); GST_INFO_OBJECT (ogg, " total time: %" GST_TIME_FORMAT, diff --git a/ext/ogg/gstoggmux.c b/ext/ogg/gstoggmux.c index bdcc44179b..f46a128c18 100644 --- a/ext/ogg/gstoggmux.c +++ b/ext/ogg/gstoggmux.c @@ -41,6 +41,7 @@ #include #include +#include #include "gstoggmux.h" @@ -100,7 +101,7 @@ static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink_%d", GST_STATIC_CAPS ("video/x-theora; " "audio/x-vorbis; audio/x-flac; audio/x-speex; audio/x-celt; " "application/x-ogm-video; application/x-ogm-audio; video/x-dirac; " - "video/x-smoke; text/x-cmml, encoded = (boolean) TRUE; " + "video/x-smoke; video/x-vp8; text/x-cmml, encoded = (boolean) TRUE; " "subtitle/x-kate; application/x-kate") ); @@ -1037,7 +1038,7 @@ gst_ogg_mux_send_headers (GstOggMux * mux) GST_LOG_OBJECT (mux, "swapped out page with mime type %s", gst_structure_get_name (structure)); - /* quick hack: put Theora and Dirac video pages at the front. + /* quick hack: put Theora, VP8 and Dirac video pages at the front. * Ideally, we would have a settable enum for which Ogg * profile we work with, and order based on that. * (FIXME: if there is more than one video stream, shouldn't we only put @@ -1050,6 +1051,9 @@ gst_ogg_mux_send_headers (GstOggMux * mux) GST_DEBUG_OBJECT (thepad, "putting %s page at the front", "Dirac"); hbufs = g_list_prepend (hbufs, hbuf); pad->always_flush_page = TRUE; + } else if (gst_structure_has_name (structure, "video/x-vp8")) { + GST_DEBUG_OBJECT (thepad, "putting %s page at the front", "VP8"); + hbufs = g_list_prepend (hbufs, hbuf); } else { hbufs = g_list_append (hbufs, hbuf); } @@ -1188,7 +1192,6 @@ gst_ogg_mux_process_best_pad (GstOggMux * ogg_mux, GstOggPadData * best) if (ogg_mux->pulling && best && ogg_mux->pulling != best && ogg_mux->pulling->buffer) { GstOggPadData *pad = ogg_mux->pulling; - GstClockTime last_ts = GST_BUFFER_END_TIME (pad->buffer); /* if the next packet in the current page is going to make the page diff --git a/ext/ogg/gstoggstream.c b/ext/ogg/gstoggstream.c index a4e18912bb..68b3e89d8d 100644 --- a/ext/ogg/gstoggstream.c +++ b/ext/ogg/gstoggstream.c @@ -56,8 +56,6 @@ typedef gboolean (*GstOggMapIsHeaderPacketFunc) (GstOggStream * pad, typedef gint64 (*GstOggMapPacketDurationFunc) (GstOggStream * pad, ogg_packet * packet); - - #define SKELETON_FISBONE_MIN_SIZE 52 #define SKELETON_FISHEAD_3_3_MIN_SIZE 112 @@ -207,9 +205,6 @@ gst_ogg_stream_get_packet_duration (GstOggStream * pad, ogg_packet * packet) return mappers[pad->map].packet_duration_func (pad, packet); } - - - /* some generic functions */ static gboolean @@ -463,6 +458,93 @@ granule_to_granulepos_dirac (GstOggStream * pad, gint64 granule, return -1; } +/* VP8 */ + +static gboolean +setup_vp8_mapper (GstOggStream * pad, ogg_packet * packet) +{ + gint width, height, par_n, par_d, fps_n, fps_d; + + if (packet->bytes < 24) { + GST_DEBUG ("Failed to parse VP8 BOS page"); + return FALSE; + } + + width = GST_READ_UINT16_BE (packet->packet + 6); + height = GST_READ_UINT16_BE (packet->packet + 8); + par_n = GST_READ_UINT24_BE (packet->packet + 10); + par_d = GST_READ_UINT24_BE (packet->packet + 13); + fps_n = GST_READ_UINT32_BE (packet->packet + 16); + fps_d = GST_READ_UINT32_BE (packet->packet + 20); + + pad->is_vp8 = TRUE; + pad->granulerate_n = fps_n; + pad->granulerate_d = fps_d; + pad->n_header_packets = 2; + pad->frame_size = 1; + + pad->caps = gst_caps_new_simple ("video/x-vp8", + "width", G_TYPE_INT, width, + "height", G_TYPE_INT, height, + "pixel-aspect-ratio", GST_TYPE_FRACTION, + par_n, par_d, "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL); + + return TRUE; +} + +static gboolean +is_keyframe_vp8 (GstOggStream * pad, gint64 granulepos) +{ + guint64 gpos = granulepos; + + if (granulepos == -1) + return FALSE; + + /* Get rid of flags */ + gpos >>= 3; + + return ((gpos & 0x07ffffff) == 0); +} + +static gint64 +granulepos_to_granule_vp8 (GstOggStream * pad, gint64 gpos) +{ + guint64 gp = (guint64) gpos; + guint32 pt; + guint32 dist; + + pt = (gp >> 32); + dist = (gp >> 3) & 0x07ffffff; + + GST_DEBUG ("pt %u, dist %u", pt, dist); + + return pt; +} + +static gint64 +granule_to_granulepos_vp8 (GstOggStream * pad, gint64 granule, + gint64 keyframe_granule) +{ + /* FIXME: This requires to look into the content of the packets + * because the simple granule counter doesn't know about invisible + * frames... + */ + return -1; +} + +/* Check if this packet contains an invisible frame or not */ +static gint64 +packet_duration_vp8 (GstOggStream * pad, ogg_packet * packet) +{ + guint32 hdr; + + if (packet->bytes < 3) + return 0; + + hdr = GST_READ_UINT24_LE (packet->packet); + + return (((hdr >> 4) & 1) != 0) ? 1 : 0; +} /* vorbis */ @@ -1452,7 +1534,7 @@ static const GstOggMap mappers[] = { NULL, NULL, NULL, - NULL, + NULL }, { "CELT ", 8, 0, @@ -1484,6 +1566,16 @@ static const GstOggMap mappers[] = { is_header_count, packet_duration_constant }, + { + "VP80\1", 5, 4, + "video/x-vp8", + setup_vp8_mapper, + granulepos_to_granule_vp8, + granule_to_granulepos_vp8, + is_keyframe_vp8, + is_header_count, + packet_duration_vp8 + }, { "\001audio\0\0\0", 9, 53, "application/x-ogm-audio", diff --git a/ext/ogg/gstoggstream.h b/ext/ogg/gstoggstream.h index b70b5b3b25..1622612531 100644 --- a/ext/ogg/gstoggstream.h +++ b/ext/ogg/gstoggstream.h @@ -79,6 +79,8 @@ struct _GstOggStream int last_size; /* theora stuff */ gboolean theora_has_zero_keyoffset; + /* VP8 stuff */ + gboolean is_vp8; /* OGM stuff */ gboolean is_ogm; gboolean is_ogm_text;