ogg: Implement Ogg VP8 mapping

This commit is contained in:
Sebastian Dröge 2010-05-05 13:59:57 +02:00
parent 0a4eaa7f60
commit 5fc1309703
4 changed files with 157 additions and 24 deletions

View file

@ -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,

View file

@ -41,6 +41,7 @@
#include <gst/gst.h>
#include <gst/base/gstcollectpads.h>
#include <gst/tag/tag.h>
#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

View file

@ -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",

View file

@ -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;