mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-12 18:35:35 +00:00
ogg: Implement Ogg VP8 mapping
This commit is contained in:
parent
0a4eaa7f60
commit
5fc1309703
4 changed files with 157 additions and 24 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue