Merge branch 'master' into 0.11

This commit is contained in:
Wim Taymans 2011-10-04 13:29:21 +02:00
commit a7d614b963
8 changed files with 245 additions and 272 deletions

View file

@ -55,7 +55,7 @@ GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC, GST_PAD_SRC,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw-int, " GST_STATIC_CAPS ("audio/x-raw-int, "
"rate = (int) [ 32000, 64000 ], " "rate = (int) { 8000, 12000, 16000, 24000, 48000 }, "
"channels = (int) [ 1, 2 ], " "channels = (int) [ 1, 2 ], "
"endianness = (int) BYTE_ORDER, " "endianness = (int) BYTE_ORDER, "
"signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16") "signed = (boolean) true, " "width = (int) 16, " "depth = (int) 16")
@ -193,6 +193,8 @@ opus_dec_sink_setcaps (GstPad * pad, GstCaps * caps)
GstStructure *s; GstStructure *s;
const GValue *streamheader; const GValue *streamheader;
GST_DEBUG_OBJECT (pad, "Setting sink caps to %" GST_PTR_FORMAT, caps);
s = gst_caps_get_structure (caps, 0); s = gst_caps_get_structure (caps, 0);
if ((streamheader = gst_structure_get_value (s, "streamheader")) && if ((streamheader = gst_structure_get_value (s, "streamheader")) &&
G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) && G_VALUE_HOLDS (streamheader, GST_TYPE_ARRAY) &&
@ -237,6 +239,47 @@ opus_dec_sink_setcaps (GstPad * pad, GstCaps * caps)
} }
} }
if (!gst_structure_get_int (s, "frame-size", &dec->frame_size)) {
GST_WARNING_OBJECT (dec, "Frame size not included in caps");
}
if (!gst_structure_get_int (s, "channels", &dec->n_channels)) {
GST_WARNING_OBJECT (dec, "Number of channels not included in caps");
}
if (!gst_structure_get_int (s, "rate", &dec->sample_rate)) {
GST_WARNING_OBJECT (dec, "Sample rate not included in caps");
}
switch (dec->frame_size) {
case 2:
dec->frame_samples = dec->sample_rate / 400;
break;
case 5:
dec->frame_samples = dec->sample_rate / 200;
break;
case 10:
dec->frame_samples = dec->sample_rate / 100;
break;
case 20:
dec->frame_samples = dec->sample_rate / 50;
break;
case 40:
dec->frame_samples = dec->sample_rate / 25;
break;
case 60:
dec->frame_samples = 3 * dec->sample_rate / 50;
break;
default:
GST_WARNING_OBJECT (dec, "Unsupported frame size: %d", dec->frame_size);
break;
}
dec->frame_duration = gst_util_uint64_scale_int (dec->frame_samples,
GST_SECOND, dec->sample_rate);
GST_INFO_OBJECT (dec,
"Got frame size %d, %d channels, %d Hz, giving %d samples per frame, frame duration %"
GST_TIME_FORMAT, dec->frame_size, dec->n_channels, dec->sample_rate,
dec->frame_samples, GST_TIME_ARGS (dec->frame_duration));
done: done:
gst_object_unref (dec); gst_object_unref (dec);
return ret; return ret;
@ -566,7 +609,7 @@ static GstFlowReturn
opus_dec_chain_parse_header (GstOpusDec * dec, GstBuffer * buf) opus_dec_chain_parse_header (GstOpusDec * dec, GstBuffer * buf)
{ {
GstCaps *caps; GstCaps *caps;
//gint error = OPUS_OK; int err;
#if 0 #if 0
dec->samples_per_frame = opus_packet_get_samples_per_frame ( dec->samples_per_frame = opus_packet_get_samples_per_frame (
@ -578,42 +621,10 @@ opus_dec_chain_parse_header (GstOpusDec * dec, GstBuffer * buf)
goto invalid_header; goto invalid_header;
#endif #endif
#if 0 dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels, &err);
#ifdef HAVE_OPUS_0_7 if (!dec->state || err != OPUS_OK)
dec->mode =
opus_mode_create (dec->sample_rate, dec->header.frame_size, &error);
#else
dec->mode =
opus_mode_create (dec->sample_rate, dec->header.nb_channels,
dec->header.frame_size, &error);
#endif
if (!dec->mode)
goto mode_init_failed;
/* initialize the decoder */
#ifdef HAVE_OPUS_0_11
dec->state =
opus_decoder_create_custom (dec->mode, dec->header.nb_channels, &error);
#else
#ifdef HAVE_OPUS_0_7
dec->state = opus_decoder_create (dec->mode, dec->header.nb_channels, &error);
#else
dec->state = opus_decoder_create (dec->mode);
#endif
#endif
#endif
dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels);
if (!dec->state)
goto init_failed; goto init_failed;
#if 0
#ifdef HAVE_OPUS_0_8
dec->frame_size = dec->header.frame_size;
#else
opus_mode_info (dec->mode, OPUS_GET_FRAME_SIZE, &dec->frame_size);
#endif
#endif
dec->frame_duration = gst_util_uint64_scale_int (dec->frame_size, dec->frame_duration = gst_util_uint64_scale_int (dec->frame_size,
GST_SECOND, dec->sample_rate); GST_SECOND, dec->sample_rate);
@ -711,7 +722,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf,
guint8 *data; guint8 *data;
GstBuffer *outbuf; GstBuffer *outbuf;
gint16 *out_data; gint16 *out_data;
int n; int n, err;
if (timestamp != -1) { if (timestamp != -1) {
dec->segment.last_stop = timestamp; dec->segment.last_stop = timestamp;
@ -721,7 +732,9 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf,
if (dec->state == NULL) { if (dec->state == NULL) {
GstCaps *caps; GstCaps *caps;
dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels); dec->state = opus_decoder_create (dec->sample_rate, dec->n_channels, &err);
if (!dec->state || err != OPUS_OK)
goto creation_failed;
/* set caps */ /* set caps */
caps = gst_caps_new_simple ("audio/x-raw-int", caps = gst_caps_new_simple ("audio/x-raw-int",
@ -772,7 +785,7 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf,
GST_LOG_OBJECT (dec, "decoding frame"); GST_LOG_OBJECT (dec, "decoding frame");
n = opus_decode (dec->state, data, size, out_data, dec->frame_samples, TRUE); n = opus_decode (dec->state, data, size, out_data, dec->frame_samples, 0);
if (n < 0) { if (n < 0) {
GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL)); GST_ELEMENT_ERROR (dec, STREAM, DECODE, ("Decoding error: %d", n), (NULL));
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
@ -805,6 +818,10 @@ opus_dec_chain_parse_data (GstOpusDec * dec, GstBuffer * buf,
GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res)); GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (res));
return res; return res;
creation_failed:
GST_ERROR_OBJECT (dec, "Failed to create Opus decoder: %d", err);
return GST_FLOW_ERROR;
} }
static GstFlowReturn static GstFlowReturn
@ -814,6 +831,10 @@ opus_dec_chain (GstPad * pad, GstBuffer * buf)
GstOpusDec *dec; GstOpusDec *dec;
dec = GST_OPUS_DEC (gst_pad_get_parent (pad)); dec = GST_OPUS_DEC (gst_pad_get_parent (pad));
GST_LOG_OBJECT (pad,
"Got buffer ts %" GST_TIME_FORMAT ", duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (buf)));
if (GST_BUFFER_IS_DISCONT (buf)) { if (GST_BUFFER_IS_DISCONT (buf)) {
dec->discont = TRUE; dec->discont = TRUE;

View file

@ -63,7 +63,7 @@ gst_opus_enc_bandwidth_get_type (void)
{OPUS_BANDWIDTH_WIDEBAND, "Wide band", "wideband"}, {OPUS_BANDWIDTH_WIDEBAND, "Wide band", "wideband"},
{OPUS_BANDWIDTH_SUPERWIDEBAND, "Super wide band", "superwideband"}, {OPUS_BANDWIDTH_SUPERWIDEBAND, "Super wide band", "superwideband"},
{OPUS_BANDWIDTH_FULLBAND, "Full band", "fullband"}, {OPUS_BANDWIDTH_FULLBAND, "Full band", "fullband"},
{OPUS_BANDWIDTH_AUTO, "Auto", "auto"}, {OPUS_AUTO, "Auto", "auto"},
{0, NULL, NULL} {0, NULL, NULL}
}; };
static volatile GType id = 0; static volatile GType id = 0;
@ -286,12 +286,13 @@ gst_opus_enc_sink_setcaps (GstPad * pad, GstCaps * caps)
enc->frame_samples = enc->sample_rate / 50; enc->frame_samples = enc->sample_rate / 50;
break; break;
case 40: case 40:
enc->frame_samples = enc->sample_rate / 20; enc->frame_samples = enc->sample_rate / 25;
break; break;
case 60: case 60:
enc->frame_samples = 3 * enc->sample_rate / 50; enc->frame_samples = 3 * enc->sample_rate / 50;
break; break;
default: default:
GST_WARNING_OBJECT (enc, "Unsupported frame size: %d", enc->frame_size);
return FALSE; return FALSE;
break; break;
} }
@ -664,62 +665,24 @@ gst_opus_enc_create_metadata_buffer (GstOpusEnc * enc)
static gboolean static gboolean
gst_opus_enc_setup (GstOpusEnc * enc) gst_opus_enc_setup (GstOpusEnc * enc)
{ {
//gint error = OPUS_OK; int error = OPUS_OK;
enc->setup = FALSE; enc->setup = FALSE;
#if 0
#ifdef HAVE_OPUS_0_7
enc->mode = opus_mode_create (enc->rate, enc->frame_size, &error);
#else
enc->mode =
opus_mode_create (enc->rate, enc->n_channels, enc->frame_size, &error);
#endif
if (!enc->mode)
goto mode_initialization_failed;
#ifdef HAVE_OPUS_0_11
opus_header_init (&enc->header, enc->mode, enc->frame_size, enc->n_channels);
#else
#ifdef HAVE_OPUS_0_7
opus_header_init (&enc->header, enc->mode, enc->n_channels);
#else
opus_header_init (&enc->header, enc->mode);
#endif
#endif
enc->header.nb_channels = enc->n_channels;
#ifdef HAVE_OPUS_0_8
enc->frame_size = enc->header.frame_size;
#else
opus_mode_info (enc->mode, OPUS_GET_FRAME_SIZE, &enc->frame_size);
#endif
#endif
#if 0
#ifdef HAVE_OPUS_0_11
enc->state = opus_encoder_create_custom (enc->mode, enc->n_channels, &error);
#else
#ifdef HAVE_OPUS_0_7
enc->state = opus_encoder_create (enc->mode, enc->n_channels, &error);
#else
enc->state = opus_encoder_create (enc->mode);
#endif
#endif
#endif
enc->state = opus_encoder_create (enc->sample_rate, enc->n_channels, enc->state = opus_encoder_create (enc->sample_rate, enc->n_channels,
enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP); enc->audio_or_voip ? OPUS_APPLICATION_AUDIO : OPUS_APPLICATION_VOIP,
if (!enc->state) &error);
if (!enc->state || error != OPUS_OK)
goto encoder_creation_failed; goto encoder_creation_failed;
opus_encoder_ctl (enc->state, OPUS_SET_BITRATE (enc->bitrate), 0); opus_encoder_ctl (enc->state, OPUS_SET_BITRATE (enc->bitrate), 0);
opus_encoder_ctl (enc->state, OPUS_SET_BANDWIDTH (enc->bandwidth), 0); opus_encoder_ctl (enc->state, OPUS_SET_BANDWIDTH (enc->bandwidth), 0);
opus_encoder_ctl (enc->state, OPUS_SET_VBR_FLAG (!enc->cbr), 0); opus_encoder_ctl (enc->state, OPUS_SET_VBR (!enc->cbr), 0);
opus_encoder_ctl (enc->state, OPUS_SET_VBR_CONSTRAINT (enc->constrained_vbr), opus_encoder_ctl (enc->state, OPUS_SET_VBR_CONSTRAINT (enc->constrained_vbr),
0); 0);
opus_encoder_ctl (enc->state, OPUS_SET_COMPLEXITY (enc->complexity), 0); opus_encoder_ctl (enc->state, OPUS_SET_COMPLEXITY (enc->complexity), 0);
opus_encoder_ctl (enc->state, OPUS_SET_INBAND_FEC_FLAG (enc->inband_fec), 0); opus_encoder_ctl (enc->state, OPUS_SET_INBAND_FEC (enc->inband_fec), 0);
opus_encoder_ctl (enc->state, OPUS_SET_DTX_FLAG (enc->dtx), 0); opus_encoder_ctl (enc->state, OPUS_SET_DTX (enc->dtx), 0);
opus_encoder_ctl (enc->state, opus_encoder_ctl (enc->state,
OPUS_SET_PACKET_LOSS_PERC (enc->packet_loss_percentage), 0); OPUS_SET_PACKET_LOSS_PERC (enc->packet_loss_percentage), 0);
@ -879,6 +842,12 @@ gst_opus_enc_encode (GstOpusEnc * enc, gboolean flush)
GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize); GST_ERROR_OBJECT (enc, "Encoding failed: %d", outsize);
ret = GST_FLOW_ERROR; ret = GST_FLOW_ERROR;
goto done; goto done;
} else if (outsize != bytes_per_packet) {
GST_WARNING_OBJECT (enc,
"Encoded size %d is different from %d bytes per packet", outsize,
bytes_per_packet);
ret = GST_FLOW_ERROR;
goto done;
} }
GST_BUFFER_TIMESTAMP (outbuf) = enc->start_ts + GST_BUFFER_TIMESTAMP (outbuf) = enc->start_ts +
@ -916,29 +885,10 @@ gst_opus_enc_chain (GstPad * pad, GstBuffer * buf)
if (!enc->setup) if (!enc->setup)
goto not_setup; goto not_setup;
#if 0
if (!enc->header_sent) { if (!enc->header_sent) {
/* Opus streams begin with two headers; the initial header (with
most of the codec setup parameters) which is mandated by the Ogg
bitstream spec. The second header holds any comment fields.
We merely need to make the headers, then pass them to libopus
one at a time; libopus handles the additional Ogg bitstream
constraints */
GstBuffer *buf1, *buf2;
GstCaps *caps; GstCaps *caps;
guchar data[100];
/* create header buffer */
opus_header_to_packet (&enc->header, data, 100);
buf1 = gst_opus_enc_buffer_from_data (enc, data, 100, 0);
/* create comment buffer */
buf2 = gst_opus_enc_create_metadata_buffer (enc);
/* mark and put on caps */
caps = gst_pad_get_caps (enc->srcpad); caps = gst_pad_get_caps (enc->srcpad);
caps = gst_opus_enc_set_header_on_caps (caps, buf1, buf2);
gst_caps_set_simple (caps, gst_caps_set_simple (caps,
"rate", G_TYPE_INT, enc->sample_rate, "rate", G_TYPE_INT, enc->sample_rate,
"channels", G_TYPE_INT, enc->n_channels, "channels", G_TYPE_INT, enc->n_channels,
@ -950,26 +900,8 @@ gst_opus_enc_chain (GstPad * pad, GstBuffer * buf)
enc->sample_rate, enc->n_channels, enc->frame_size); enc->sample_rate, enc->n_channels, enc->frame_size);
gst_pad_set_caps (enc->srcpad, caps); gst_pad_set_caps (enc->srcpad, caps);
gst_buffer_set_caps (buf1, caps);
gst_buffer_set_caps (buf2, caps);
gst_caps_unref (caps);
/* push out buffers */
ret = gst_opus_enc_push_buffer (enc, buf1);
if (ret != GST_FLOW_OK) {
gst_buffer_unref (buf2);
goto done;
}
ret = gst_opus_enc_push_buffer (enc, buf2);
if (ret != GST_FLOW_OK)
goto done;
enc->header_sent = TRUE; enc->header_sent = TRUE;
} }
#endif
GST_DEBUG_OBJECT (enc, "received buffer of %u bytes", GST_BUFFER_SIZE (buf)); GST_DEBUG_OBJECT (enc, "received buffer of %u bytes", GST_BUFFER_SIZE (buf));

View file

@ -131,7 +131,7 @@ gst_camerabin_create_preview_pipeline (GstElement * element,
GstBus *bus; GstBus *bus;
GstAppSinkCallbacks callbacks = { 0, }; GstAppSinkCallbacks callbacks = { 0, };
data = g_new (GstCameraBinPreviewPipelineData, 1); data = g_new0 (GstCameraBinPreviewPipelineData, 1);
data->pipeline = gst_pipeline_new ("preview-pipeline"); data->pipeline = gst_pipeline_new ("preview-pipeline");
data->appsrc = gst_element_factory_make ("appsrc", "preview-appsrc"); data->appsrc = gst_element_factory_make ("appsrc", "preview-appsrc");

View file

@ -2987,10 +2987,10 @@ gst_mpegts_demux_sync_scan (GstMpegTSDemux * demux, const guint8 * in_data,
guint size, guint * flush) guint size, guint * flush)
{ {
guint sync_count = 0; guint sync_count = 0;
const guint8 *end_scan = in_data + size - demux->packetsize;
guint8 *ptr_data = (guint8 *) in_data; guint8 *ptr_data = (guint8 *) in_data;
guint packetsize = guint packetsize =
(demux->packetsize ? demux->packetsize : MPEGTS_NORMAL_TS_PACKETSIZE); (demux->packetsize ? demux->packetsize : MPEGTS_NORMAL_TS_PACKETSIZE);
const guint8 *end_scan = in_data + size - packetsize;
/* Check if the LUT table is big enough */ /* Check if the LUT table is big enough */
if (G_UNLIKELY (demux->sync_lut_len < (size / packetsize))) { if (G_UNLIKELY (demux->sync_lut_len < (size / packetsize))) {
@ -3007,18 +3007,14 @@ gst_mpegts_demux_sync_scan (GstMpegTSDemux * demux, const guint8 * in_data,
guint chance = is_mpegts_sync (ptr_data, end_scan, packetsize); guint chance = is_mpegts_sync (ptr_data, end_scan, packetsize);
if (G_LIKELY (chance > 50)) { if (G_LIKELY (chance > 50)) {
/* skip paketsize bytes and try find next */ /* skip paketsize bytes and try find next */
guint8 *next_sync = ptr_data + packetsize;
if (next_sync < end_scan) {
demux->sync_lut[sync_count] = ptr_data; demux->sync_lut[sync_count] = ptr_data;
sync_count++; sync_count++;
ptr_data += packetsize; ptr_data += packetsize;
} else
goto done;
} else { } else {
ptr_data++; ptr_data++;
} }
} }
done:
if (G_UNLIKELY (!demux->packetsize)) if (G_UNLIKELY (!demux->packetsize))
gst_mpegts_demux_detect_packet_size (demux, sync_count); gst_mpegts_demux_detect_packet_size (demux, sync_count);

View file

@ -1,6 +1,8 @@
/* /*
* gst-rtp-vp8-depay.c - Source for GstRtpVP8Depay * gst-rtp-vp8-depay.c - Source for GstRtpVP8Depay
* Copyright (C) 2011 Sjoerd Simons <sjoerd@luon.net> * Copyright (C) 2011 Sjoerd Simons <sjoerd@luon.net>
* Copyright (C) 2011 Collabora Ltd.
* Contact: Youness Alaoui <youness.alaoui@collabora.co.uk>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -44,7 +46,7 @@ GST_STATIC_PAD_TEMPLATE ("sink",
"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING "," "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ","
"clock-rate = (int) 90000," "clock-rate = (int) 90000,"
"media = (string) \"video\"," "media = (string) \"video\","
"encoding-name = (string) \"VP8-DRAFT-0-3-2\"")); "encoding-name = (string) \"VP8-DRAFT-IETF-01\""));
static void static void
gst_rtp_vp8_depay_init (GstRtpVP8Depay * self, GstRtpVP8DepayClass * klass) gst_rtp_vp8_depay_init (GstRtpVP8Depay * self, GstRtpVP8DepayClass * klass)
@ -129,21 +131,31 @@ gst_rtp_vp8_depay_process (GstBaseRTPDepayload * depay, GstBuffer * buf)
if (G_UNLIKELY (!self->started)) { if (G_UNLIKELY (!self->started)) {
/* Check if this is the start of a VP8 frame, otherwise bail */ /* Check if this is the start of a VP8 frame, otherwise bail */
if ((data[0] & 0x1) == 0) /* S=1 and PartID= 0 */
if ((data[0] & 0x1F) != 0x10)
return NULL; return NULL;
self->started = TRUE; self->started = TRUE;
} }
offset = 1; offset = 1;
if ((data[0] & 0x10) != 0) { /* Check X optional header */
/* Skip Picture identifier bytes */ if ((data[0] & 0x80) != 0) {
for (; (data[offset] & 0x80) != 0; offset++) { offset++;
/* should be at least one more pictureID byte and at least one byte in /* Check I optional header */
* the vp8 payload */ if ((data[1] & 0x80) != 0) {
offset++;
if (G_UNLIKELY (offset + 2 >= size)) if (G_UNLIKELY (offset + 2 >= size))
goto too_small; goto too_small;
/* Check for 16 bits PictureID */
if ((data[2] & 0x80) != 0)
offset++;
} }
/* Check L optional header */
if ((data[1] & 0x40) != 0)
offset++;
/* Check T optional header */
if ((data[1] & 0x20) != 0)
offset++; offset++;
} }

View file

@ -1,6 +1,8 @@
/* /*
* gst-rtp-vp8-pay.c - Source for GstRtpVP8Pay * gst-rtp-vp8-pay.c - Source for GstRtpVP8Pay
* Copyright (C) 2011 Sjoerd Simons <sjoerd@luon.net> * Copyright (C) 2011 Sjoerd Simons <sjoerd@luon.net>
* Copyright (C) 2011 Collabora Ltd.
* Contact: Youness Alaoui <youness.alaoui@collabora.co.uk>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public * modify it under the terms of the GNU Lesser General Public
@ -28,14 +30,11 @@
#include "dboolhuff.h" #include "dboolhuff.h"
#include "gstrtpvp8pay.h" #include "gstrtpvp8pay.h"
#define FI_FRAG_UNFRAGMENTED 0x0
#define FI_FRAG_START 0x1
#define FI_FRAG_MIDDLE 0x2
#define FI_FRAG_END 0x3
GST_DEBUG_CATEGORY_STATIC (gst_rtp_vp8_pay_debug); GST_DEBUG_CATEGORY_STATIC (gst_rtp_vp8_pay_debug);
#define GST_CAT_DEFAULT gst_rtp_vp8_pay_debug #define GST_CAT_DEFAULT gst_rtp_vp8_pay_debug
#define DEFAULT_PICTURE_ID_MODE VP8_PAY_PICTURE_ID_7BITS
GST_BOILERPLATE (GstRtpVP8Pay, gst_rtp_vp8_pay, GstBaseRTPPayload, GST_BOILERPLATE (GstRtpVP8Pay, gst_rtp_vp8_pay, GstBaseRTPPayload,
GST_TYPE_BASE_RTP_PAYLOAD); GST_TYPE_BASE_RTP_PAYLOAD);
@ -45,7 +44,7 @@ GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("application/x-rtp, " GST_STATIC_CAPS ("application/x-rtp, "
"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING "," "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ","
"clock-rate = (int) 90000, encoding-name = (string) \"VP8-DRAFT-0-3-2\"")); "clock-rate = (int) 90000, encoding-name = (string) \"VP8-DRAFT-IETF-01\""));
static GstStaticPadTemplate gst_rtp_vp8_pay_sink_template = static GstStaticPadTemplate gst_rtp_vp8_pay_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_PAD_TEMPLATE ("sink",
@ -56,10 +55,17 @@ GST_STATIC_PAD_TEMPLATE ("sink",
static void static void
gst_rtp_vp8_pay_init (GstRtpVP8Pay * obj, GstRtpVP8PayClass * klass) gst_rtp_vp8_pay_init (GstRtpVP8Pay * obj, GstRtpVP8PayClass * klass)
{ {
/* TODO: Make it configurable */
obj->picture_id_mode = DEFAULT_PICTURE_ID_MODE;
if (obj->picture_id_mode == VP8_PAY_PICTURE_ID_7BITS)
obj->picture_id = g_random_int_range (0, G_MAXUINT8) & 0x7F;
else if (obj->picture_id_mode == VP8_PAY_PICTURE_ID_15BITS)
obj->picture_id = g_random_int_range (0, G_MAXUINT16) & 0x7FFF;
} }
static GstFlowReturn gst_rtp_vp8_pay_handle_buffer (GstBaseRTPPayload * payload, static GstFlowReturn gst_rtp_vp8_pay_handle_buffer (GstBaseRTPPayload * payload,
GstBuffer * buffer); GstBuffer * buffer);
static gboolean gst_rtp_vp8_pay_handle_event (GstPad * pad, GstEvent * event);
static gboolean gst_rtp_vp8_pay_set_caps (GstBaseRTPPayload * payload, static gboolean gst_rtp_vp8_pay_set_caps (GstBaseRTPPayload * payload,
GstCaps * caps); GstCaps * caps);
@ -85,48 +91,20 @@ gst_rtp_vp8_pay_class_init (GstRtpVP8PayClass * gst_rtp_vp8_pay_class)
GST_BASE_RTP_PAYLOAD_CLASS (gst_rtp_vp8_pay_class); GST_BASE_RTP_PAYLOAD_CLASS (gst_rtp_vp8_pay_class);
pay_class->handle_buffer = gst_rtp_vp8_pay_handle_buffer; pay_class->handle_buffer = gst_rtp_vp8_pay_handle_buffer;
pay_class->handle_event = gst_rtp_vp8_pay_handle_event;
pay_class->set_caps = gst_rtp_vp8_pay_set_caps; pay_class->set_caps = gst_rtp_vp8_pay_set_caps;
GST_DEBUG_CATEGORY_INIT (gst_rtp_vp8_pay_debug, "rtpvp8pay", 0, GST_DEBUG_CATEGORY_INIT (gst_rtp_vp8_pay_debug, "rtpvp8pay", 0,
"VP8 Video RTP Payloader"); "VP8 Video RTP Payloader");
} }
static gsize
gst_rtp_vp8_calc_payload_len (GstBaseRTPPayload * payload)
{
return gst_rtp_buffer_calc_payload_len (GST_BASE_RTP_PAYLOAD_MTU (payload) -
1, 0, 0);
}
/* When growing the vp8 header keep gst_rtp_vp8_calc_payload_len in sync */
static GstBuffer *
gst_rtp_vp8_create_header_buffer (gboolean start, gboolean mark, guint fi,
GstBuffer * in)
{
GstBuffer *out;
guint8 *p;
out = gst_rtp_buffer_new_allocate (1, 0, 0);
p = gst_rtp_buffer_get_payload (out);
/* Hardcode I = 0 and N = 0, only set FI and B */
p[0] = (fi & 0x3) << 1 | (start ? 1 : 0);
gst_rtp_buffer_set_marker (out, mark);
GST_BUFFER_DURATION (out) = GST_BUFFER_DURATION (in);
GST_BUFFER_TIMESTAMP (out) = GST_BUFFER_TIMESTAMP (in);
return out;
}
static gboolean static gboolean
gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer) gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer)
{ {
GstBitReader *reader; GstBitReader *reader;
int i; int i;
gboolean keyframe; gboolean keyframe;
guint32 header_size; guint32 partition0_size;
guint8 version; guint8 version;
guint8 tmp8 = 0; guint8 tmp8 = 0;
guint8 *data; guint8 *data;
@ -150,11 +128,11 @@ gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer)
} }
/* keyframe, version and show_frame use 5 bits */ /* keyframe, version and show_frame use 5 bits */
header_size = data[2] << 11 | data[1] << 3 | (data[0] >> 5); partition0_size = data[2] << 11 | data[1] << 3 | (data[0] >> 5);
/* Include the uncompressed data blob in the header */ /* Include the uncompressed data blob in the first partition */
offset = keyframe ? 10 : 3; offset = keyframe ? 10 : 3;
header_size += offset; partition0_size += offset;
if (!gst_bit_reader_skip (reader, 24)) if (!gst_bit_reader_skip (reader, 24))
goto error; goto error;
@ -248,17 +226,16 @@ gst_rtp_vp8_pay_parse_frame (GstRtpVP8Pay * self, GstBuffer * buffer)
partitions = 1 << tmp8; partitions = 1 << tmp8;
/* Check if things are still sensible */ /* Check if things are still sensible */
if (header_size + (partitions - 1) * 3 >= GST_BUFFER_SIZE (buffer)) if (partition0_size + (partitions - 1) * 3 >= GST_BUFFER_SIZE (buffer))
goto error; goto error;
/* partition data is right after the frame header */ /* partition data is right after the mode partition */
data = GST_BUFFER_DATA (buffer) + header_size; data = GST_BUFFER_DATA (buffer) + partition0_size;
/* Set up mapping, count the initial header as a partition to make other /* Set up mapping */
* sections of the code easier */
self->n_partitions = partitions + 1; self->n_partitions = partitions + 1;
self->partition_offset[0] = 0; self->partition_offset[0] = 0;
self->partition_size[0] = header_size + (partitions - 1) * 3; self->partition_size[0] = partition0_size + (partitions - 1) * 3;
self->partition_offset[1] = self->partition_size[0]; self->partition_offset[1] = self->partition_size[0];
for (i = 1; i < partitions; i++) { for (i = 1; i < partitions; i++) {
@ -289,101 +266,108 @@ error:
} }
static guint static guint
gst_rtp_vp8_fit_partitions (GstRtpVP8Pay * self, gint first, gsize available) gst_rtp_vp8_offset_to_partition (GstRtpVP8Pay * self, guint offset)
{ {
guint num = 0;
int i; int i;
g_assert (first < self->n_partitions); for (i = 0; i < self->n_partitions; i++) {
if (offset >= self->partition_offset[i] &&
for (i = first; offset < self->partition_offset[i + 1])
i < self->n_partitions && self->partition_size[i] < available; i++) { return i;
num++;
available -= self->partition_size[i];
} }
return num; return i;
} }
static GstBuffer * static gsize
gst_rtp_vp8_create_sub (GstRtpVP8Pay * self, gst_rtp_vp8_calc_header_len (GstRtpVP8Pay * self)
GstBuffer * buffer, guint current, guint num)
{ {
guint offset = self->partition_offset[current]; switch (self->picture_id_mode) {
guint size = self->partition_offset[current + num] - offset; case VP8_PAY_PICTURE_ID_7BITS:
return 3;
case VP8_PAY_PICTURE_ID_15BITS:
return 4;
case VP8_PAY_NO_PICTURE_ID:
default:
return 1;
}
}
return gst_buffer_create_sub (buffer, offset, size);
static gsize
gst_rtp_vp8_calc_payload_len (GstRtpVP8Pay * self)
{
GstBaseRTPPayload *payload = GST_BASE_RTP_PAYLOAD (self);
return gst_rtp_buffer_calc_payload_len (GST_BASE_RTP_PAYLOAD_MTU (payload) -
gst_rtp_vp8_calc_header_len (self), 0, 0);
}
/* When growing the vp8 header keep gst_rtp_vp8_calc_payload_len in sync */
static GstBuffer *
gst_rtp_vp8_create_header_buffer (GstRtpVP8Pay * self, guint8 partid,
gboolean start, gboolean mark, GstBuffer * in)
{
GstBuffer *out;
guint8 *p;
out = gst_rtp_buffer_new_allocate (gst_rtp_vp8_calc_header_len (self), 0, 0);
p = gst_rtp_buffer_get_payload (out);
/* X=0,R=0,N=0,S=start,PartID=partid */
p[0] = (start << 4) | partid;
if (self->picture_id_mode != VP8_PAY_NO_PICTURE_ID) {
/* Enable X=1 */
p[0] |= 0x80;
/* X: I=1,L=0,T=0,RSVA=0 */
p[1] = 0x80;
if (self->picture_id_mode == VP8_PAY_PICTURE_ID_7BITS) {
/* I: 7 bit picture_id */
p[2] = self->picture_id & 0x7F;
} else {
/* I: 15 bit picture_id */
p[2] = 0x80 | ((self->picture_id & 0x7FFF) >> 8);
p[3] = self->picture_id & 0xFF;
}
}
gst_rtp_buffer_set_marker (out, mark);
GST_BUFFER_DURATION (out) = GST_BUFFER_DURATION (in);
GST_BUFFER_TIMESTAMP (out) = GST_BUFFER_TIMESTAMP (in);
return out;
} }
static guint static guint
gst_rtp_vp8_payload_next (GstRtpVP8Pay * self, gst_rtp_vp8_payload_next (GstRtpVP8Pay * self,
GstBufferListIterator * it, guint first, GstBuffer * buffer) GstBufferListIterator * it, guint offset, GstBuffer * buffer)
{ {
guint num; guint partition;
GstBuffer *header; GstBuffer *header;
GstBuffer *sub; GstBuffer *sub;
gboolean mark; gboolean mark;
gsize available = gst_rtp_vp8_calc_payload_len (GST_BASE_RTP_PAYLOAD (self)); gsize remaining;
gsize available;
g_assert (first < 9); remaining = GST_BUFFER_SIZE (buffer) - offset;
available = gst_rtp_vp8_calc_payload_len (self);
if (available > remaining)
available = remaining;
/* How many partitions can we fit */ partition = gst_rtp_vp8_offset_to_partition (self, offset);
num = gst_rtp_vp8_fit_partitions (self, first, available); g_assert (partition < self->n_partitions);
if (num > 0) { mark = (remaining == available);
mark = (first + num == self->n_partitions);
/* whole set of partitions, payload them and done */ /* whole set of partitions, payload them and done */
header = gst_rtp_vp8_create_header_buffer (first == 0, mark, header = gst_rtp_vp8_create_header_buffer (self, partition,
FI_FRAG_UNFRAGMENTED, buffer); offset == self->partition_offset[partition], mark, buffer);
sub = gst_rtp_vp8_create_sub (self, buffer, first, num);
gst_buffer_list_iterator_add_group (it);
gst_buffer_list_iterator_add (it, header);
gst_buffer_list_iterator_add (it, sub);
} else {
/* Fragmented packets */
guint offset = self->partition_offset[first];
guint left = self->partition_size[first];
gboolean start = (first == 0);
header = gst_rtp_vp8_create_header_buffer (start, FALSE,
FI_FRAG_START, buffer);
sub = gst_buffer_create_sub (buffer, offset, available); sub = gst_buffer_create_sub (buffer, offset, available);
offset += available;
gst_buffer_list_iterator_add_group (it); gst_buffer_list_iterator_add_group (it);
gst_buffer_list_iterator_add (it, header); gst_buffer_list_iterator_add (it, header);
gst_buffer_list_iterator_add (it, sub); gst_buffer_list_iterator_add (it, sub);
left -= available; return GST_BUFFER_SIZE (sub);
for (; left > available; left -= available) {
header = gst_rtp_vp8_create_header_buffer (start, FALSE,
FI_FRAG_MIDDLE, buffer);
sub = gst_buffer_create_sub (buffer, offset, available);
offset += available;
gst_buffer_list_iterator_add_group (it);
gst_buffer_list_iterator_add (it, header);
gst_buffer_list_iterator_add (it, sub);
}
mark = (first + 1 == self->n_partitions);
header = gst_rtp_vp8_create_header_buffer (start, mark,
FI_FRAG_END, buffer);
sub = gst_buffer_create_sub (buffer, offset, left);
gst_buffer_list_iterator_add_group (it);
gst_buffer_list_iterator_add (it, header);
gst_buffer_list_iterator_add (it, sub);
return 1;
}
return num;
} }
@ -394,10 +378,9 @@ gst_rtp_vp8_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer)
GstFlowReturn ret; GstFlowReturn ret;
GstBufferList *list; GstBufferList *list;
GstBufferListIterator *it; GstBufferListIterator *it;
guint current; guint offset;
if (G_UNLIKELY (!gst_rtp_vp8_pay_parse_frame (self, buffer))) { if (G_UNLIKELY (!gst_rtp_vp8_pay_parse_frame (self, buffer))) {
/* FIXME throw flow error */
g_message ("Failed to parse frame"); g_message ("Failed to parse frame");
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
} }
@ -405,24 +388,44 @@ gst_rtp_vp8_pay_handle_buffer (GstBaseRTPPayload * payload, GstBuffer * buffer)
list = gst_buffer_list_new (); list = gst_buffer_list_new ();
it = gst_buffer_list_iterate (list); it = gst_buffer_list_iterate (list);
for (current = 0; current < self->n_partitions;) { for (offset = 0; offset < GST_BUFFER_SIZE (buffer);)
guint n; offset += gst_rtp_vp8_payload_next (self, it, offset, buffer);
n = gst_rtp_vp8_payload_next (self, it, current, buffer);
current += n;
}
ret = gst_basertppayload_push_list (payload, list); ret = gst_basertppayload_push_list (payload, list);
gst_buffer_list_iterator_free (it); gst_buffer_list_iterator_free (it);
/* Incremenent and wrap the picture id if it overflows */
if ((self->picture_id_mode == VP8_PAY_PICTURE_ID_7BITS &&
++self->picture_id >= 0x80) ||
(self->picture_id_mode == VP8_PAY_PICTURE_ID_15BITS &&
++self->picture_id >= 0x8000))
self->picture_id = 0;
return ret; return ret;
} }
static gboolean
gst_rtp_vp8_pay_handle_event (GstPad * pad, GstEvent * event)
{
GstRtpVP8Pay *self = GST_RTP_VP8_PAY (gst_pad_get_parent (pad));
if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_START) {
if (self->picture_id_mode == VP8_PAY_PICTURE_ID_7BITS)
self->picture_id = g_random_int_range (0, G_MAXUINT8) & 0x7F;
else if (self->picture_id_mode == VP8_PAY_PICTURE_ID_15BITS)
self->picture_id = g_random_int_range (0, G_MAXUINT16) & 0x7FFF;
}
gst_object_unref (self);
return FALSE;
}
static gboolean static gboolean
gst_rtp_vp8_pay_set_caps (GstBaseRTPPayload * payload, GstCaps * caps) gst_rtp_vp8_pay_set_caps (GstBaseRTPPayload * payload, GstCaps * caps)
{ {
gst_basertppayload_set_options (payload, "video", TRUE, gst_basertppayload_set_options (payload, "video", TRUE,
"VP8-DRAFT-0-3-2", 90000); "VP8-DRAFT-IETF-01", 90000);
return gst_basertppayload_set_outcaps (payload, NULL); return gst_basertppayload_set_outcaps (payload, NULL);
} }

View file

@ -26,6 +26,13 @@
G_BEGIN_DECLS typedef struct _GstRtpVP8Pay GstRtpVP8Pay; G_BEGIN_DECLS typedef struct _GstRtpVP8Pay GstRtpVP8Pay;
typedef struct _GstRtpVP8PayClass GstRtpVP8PayClass; typedef struct _GstRtpVP8PayClass GstRtpVP8PayClass;
typedef enum _PictureIDMode PictureIDMode;
enum _PictureIDMode {
VP8_PAY_NO_PICTURE_ID,
VP8_PAY_PICTURE_ID_7BITS,
VP8_PAY_PICTURE_ID_15BITS,
};
struct _GstRtpVP8PayClass struct _GstRtpVP8PayClass
{ {
@ -41,6 +48,8 @@ struct _GstRtpVP8Pay
* folowed by max. 8 data partitions. last offset is the end of the buffer */ * folowed by max. 8 data partitions. last offset is the end of the buffer */
guint partition_offset[10]; guint partition_offset[10];
guint partition_size[9]; guint partition_size[9];
PictureIDMode picture_id_mode;
guint16 picture_id;
}; };
GType gst_rtp_vp8_pay_get_type (void); GType gst_rtp_vp8_pay_get_type (void);

View file

@ -30,15 +30,15 @@
static gboolean static gboolean
plugin_init (GstPlugin * plugin) plugin_init (GstPlugin * plugin)
{ {
gboolean ret; gboolean ret = FALSE;
ret = gst_element_register (plugin, "h263parse", ret |= gst_element_register (plugin, "h263parse",
GST_RANK_PRIMARY + 1, GST_TYPE_H263_PARSE); GST_RANK_PRIMARY + 1, GST_TYPE_H263_PARSE);
ret = gst_element_register (plugin, "h264parse", ret |= gst_element_register (plugin, "h264parse",
GST_RANK_PRIMARY + 1, GST_TYPE_H264_PARSE); GST_RANK_PRIMARY + 1, GST_TYPE_H264_PARSE);
ret = gst_element_register (plugin, "diracparse", ret |= gst_element_register (plugin, "diracparse",
GST_RANK_NONE, GST_TYPE_DIRAC_PARSE); GST_RANK_NONE, GST_TYPE_DIRAC_PARSE);
ret = gst_element_register (plugin, "mpegvideoparse", ret |= gst_element_register (plugin, "mpegvideoparse",
GST_RANK_PRIMARY + 1, GST_TYPE_MPEGVIDEO_PARSE); GST_RANK_PRIMARY + 1, GST_TYPE_MPEGVIDEO_PARSE);
return ret; return ret;