mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 08:46:40 +00:00
parent
de5cb168ee
commit
815e06ba55
4 changed files with 866 additions and 0 deletions
|
@ -16,6 +16,7 @@ libgstrtp_la_SOURCES = \
|
|||
gstrtpilbcpay.c \
|
||||
gstrtpmpadepay.c \
|
||||
gstrtpmpapay.c \
|
||||
gstrtpmparobustdepay.c \
|
||||
gstrtpmpvdepay.c \
|
||||
gstrtpmpvpay.c \
|
||||
gstrtppcmadepay.c \
|
||||
|
@ -114,6 +115,7 @@ noinst_HEADERS = \
|
|||
gstrtpgsmdepay.h \
|
||||
gstrtpgsmpay.h \
|
||||
gstrtpmpadepay.h \
|
||||
gstrtpmparobustdepay.h \
|
||||
gstrtpmpapay.h \
|
||||
gstrtpmpvdepay.h \
|
||||
gstrtpmpvpay.h \
|
||||
|
|
|
@ -47,6 +47,7 @@
|
|||
#include "gstrtpamrdepay.h"
|
||||
#include "gstrtpmpapay.h"
|
||||
#include "gstrtpmpadepay.h"
|
||||
#include "gstrtpmparobustdepay.h"
|
||||
#include "gstrtpmpvdepay.h"
|
||||
#include "gstrtpmpvpay.h"
|
||||
#include "gstrtph263pdepay.h"
|
||||
|
@ -166,6 +167,9 @@ plugin_init (GstPlugin * plugin)
|
|||
if (!gst_rtp_mpa_pay_plugin_init (plugin))
|
||||
return FALSE;
|
||||
|
||||
if (!gst_rtp_mpa_robust_depay_plugin_init (plugin))
|
||||
return FALSE;
|
||||
|
||||
if (!gst_rtp_mpv_depay_plugin_init (plugin))
|
||||
return FALSE;
|
||||
|
||||
|
|
782
gst/rtp/gstrtpmparobustdepay.c
Normal file
782
gst/rtp/gstrtpmparobustdepay.c
Normal file
|
@ -0,0 +1,782 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
|
||||
* Copyright (C) <2010> Nokia Corporation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <gst/rtp/gstrtpbuffer.h>
|
||||
|
||||
#include <string.h>
|
||||
#include "gstrtpmparobustdepay.h"
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (rtpmparobustdepay_debug);
|
||||
#define GST_CAT_DEFAULT (rtpmparobustdepay_debug)
|
||||
|
||||
static GstStaticPadTemplate gst_rtp_mpa_robust_depay_src_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("audio/mpeg, " "mpegversion = (int) 1")
|
||||
);
|
||||
|
||||
static GstStaticPadTemplate gst_rtp_mpa_robust_depay_sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS ("application/x-rtp, "
|
||||
"media = (string) \"audio\", "
|
||||
"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
|
||||
"clock-rate = (int) 90000, "
|
||||
"encoding-name = (string) \"MPA-ROBUST\" " "; "
|
||||
/* draft versions appear still in use out there */
|
||||
"application/x-rtp, "
|
||||
"media = (string) \"audio\", "
|
||||
"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
|
||||
"clock-rate = (int) [1, MAX], "
|
||||
"encoding-name = (string) { \"X-MP3-DRAFT-00\", \"X-MP3-DRAFT-01\", "
|
||||
" \"X-MP3-DRAFT-02\", \"X-MP3-DRAFT-03\", \"X-MP3-DRAFT-04\", "
|
||||
" \"X-MP3-DRAFT-05\", \"X-MP3-DRAFT-06\" }")
|
||||
);
|
||||
|
||||
typedef struct _GstADUFrame
|
||||
{
|
||||
guint32 header;
|
||||
gint size;
|
||||
gint side_info;
|
||||
gint data_size;
|
||||
gint layer;
|
||||
gint backpointer;
|
||||
|
||||
GstBuffer *buffer;
|
||||
} GstADUFrame;
|
||||
|
||||
GST_BOILERPLATE (GstRtpMPARobustDepay, gst_rtp_mpa_robust_depay,
|
||||
GstBaseRTPDepayload, GST_TYPE_BASE_RTP_DEPAYLOAD);
|
||||
|
||||
static GstStateChangeReturn gst_rtp_mpa_robust_change_state (GstElement *
|
||||
element, GstStateChange transition);
|
||||
|
||||
static gboolean gst_rtp_mpa_robust_depay_setcaps (GstBaseRTPDepayload *
|
||||
depayload, GstCaps * caps);
|
||||
static GstBuffer *gst_rtp_mpa_robust_depay_process (GstBaseRTPDepayload *
|
||||
depayload, GstBuffer * buf);
|
||||
|
||||
static void
|
||||
gst_rtp_mpa_robust_depay_base_init (gpointer klass)
|
||||
{
|
||||
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
||||
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&gst_rtp_mpa_robust_depay_src_template));
|
||||
gst_element_class_add_pad_template (element_class,
|
||||
gst_static_pad_template_get (&gst_rtp_mpa_robust_depay_sink_template));
|
||||
|
||||
gst_element_class_set_details_simple (element_class,
|
||||
"RTP MPEG audio depayloader", "Codec/Depayloader/Network",
|
||||
"Extracts MPEG audio from RTP packets (RFC 5219)",
|
||||
"Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtp_mpa_robust_depay_finalize (GObject * object)
|
||||
{
|
||||
GstRtpMPARobustDepay *rtpmpadepay;
|
||||
|
||||
rtpmpadepay = (GstRtpMPARobustDepay *) object;
|
||||
|
||||
g_object_unref (rtpmpadepay->adapter);
|
||||
g_queue_free (rtpmpadepay->adu_frames);
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gst_rtp_mpa_robust_depay_class_init (GstRtpMPARobustDepayClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class;
|
||||
GstElementClass *gstelement_class;
|
||||
GstBaseRTPDepayloadClass *gstbasertpdepayload_class;
|
||||
|
||||
gobject_class = (GObjectClass *) klass;
|
||||
gstelement_class = (GstElementClass *) klass;
|
||||
gstbasertpdepayload_class = (GstBaseRTPDepayloadClass *) klass;
|
||||
|
||||
gobject_class->finalize = gst_rtp_mpa_robust_depay_finalize;
|
||||
|
||||
gstelement_class->change_state =
|
||||
GST_DEBUG_FUNCPTR (gst_rtp_mpa_robust_change_state);
|
||||
|
||||
gstbasertpdepayload_class->set_caps = gst_rtp_mpa_robust_depay_setcaps;
|
||||
gstbasertpdepayload_class->process = gst_rtp_mpa_robust_depay_process;
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (rtpmparobustdepay_debug, "rtpmparobustdepay", 0,
|
||||
"Robust MPEG Audio RTP Depayloader");
|
||||
}
|
||||
|
||||
static void
|
||||
gst_rtp_mpa_robust_depay_init (GstRtpMPARobustDepay * rtpmpadepay,
|
||||
GstRtpMPARobustDepayClass * klass)
|
||||
{
|
||||
rtpmpadepay->adapter = gst_adapter_new ();
|
||||
rtpmpadepay->adu_frames = g_queue_new ();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_rtp_mpa_robust_depay_setcaps (GstBaseRTPDepayload * depayload,
|
||||
GstCaps * caps)
|
||||
{
|
||||
GstRtpMPARobustDepay *rtpmpadepay;
|
||||
GstStructure *structure;
|
||||
GstCaps *outcaps;
|
||||
gint clock_rate, draft;
|
||||
gboolean res;
|
||||
const gchar *encoding;
|
||||
|
||||
rtpmpadepay = GST_RTP_MPA_ROBUST_DEPAY (depayload);
|
||||
|
||||
structure = gst_caps_get_structure (caps, 0);
|
||||
|
||||
if (!gst_structure_get_int (structure, "clock-rate", &clock_rate))
|
||||
clock_rate = 90000;
|
||||
depayload->clock_rate = clock_rate;
|
||||
|
||||
rtpmpadepay->has_descriptor = TRUE;
|
||||
if ((encoding = gst_structure_get_string (structure, "encoding-name"))) {
|
||||
if (sscanf (encoding, "X-MP3-DRAFT-%d", &draft) && (draft == 0))
|
||||
rtpmpadepay->has_descriptor = FALSE;
|
||||
}
|
||||
|
||||
outcaps =
|
||||
gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1, NULL);
|
||||
res = gst_pad_set_caps (depayload->srcpad, outcaps);
|
||||
gst_caps_unref (outcaps);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* thanks again go to mp3parse ... */
|
||||
|
||||
static const guint mp3types_bitrates[2][3][16] = {
|
||||
{
|
||||
{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448,},
|
||||
{0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384,},
|
||||
{0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320,}
|
||||
},
|
||||
{
|
||||
{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256,},
|
||||
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,},
|
||||
{0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160,}
|
||||
},
|
||||
};
|
||||
|
||||
static const guint mp3types_freqs[3][3] = { {44100, 48000, 32000},
|
||||
{22050, 24000, 16000},
|
||||
{11025, 12000, 8000}
|
||||
};
|
||||
|
||||
static inline guint
|
||||
mp3_type_frame_length_from_header (GstElement * mp3parse, guint32 header,
|
||||
guint * put_version, guint * put_layer, guint * put_channels,
|
||||
guint * put_bitrate, guint * put_samplerate, guint * put_mode,
|
||||
guint * put_crc)
|
||||
{
|
||||
guint length;
|
||||
gulong mode, samplerate, bitrate, layer, channels, padding, crc;
|
||||
gulong version;
|
||||
gint lsf, mpg25;
|
||||
|
||||
if (header & (1 << 20)) {
|
||||
lsf = (header & (1 << 19)) ? 0 : 1;
|
||||
mpg25 = 0;
|
||||
} else {
|
||||
lsf = 1;
|
||||
mpg25 = 1;
|
||||
}
|
||||
|
||||
version = 1 + lsf + mpg25;
|
||||
|
||||
layer = 4 - ((header >> 17) & 0x3);
|
||||
|
||||
crc = (header >> 16) & 0x1;
|
||||
|
||||
bitrate = (header >> 12) & 0xF;
|
||||
bitrate = mp3types_bitrates[lsf][layer - 1][bitrate] * 1000;
|
||||
/* The caller has ensured we have a valid header, so bitrate can't be
|
||||
zero here. */
|
||||
if (bitrate == 0)
|
||||
return 0;
|
||||
|
||||
samplerate = (header >> 10) & 0x3;
|
||||
samplerate = mp3types_freqs[lsf + mpg25][samplerate];
|
||||
|
||||
padding = (header >> 9) & 0x1;
|
||||
|
||||
mode = (header >> 6) & 0x3;
|
||||
channels = (mode == 3) ? 1 : 2;
|
||||
|
||||
switch (layer) {
|
||||
case 1:
|
||||
length = 4 * ((bitrate * 12) / samplerate + padding);
|
||||
break;
|
||||
case 2:
|
||||
length = (bitrate * 144) / samplerate + padding;
|
||||
break;
|
||||
default:
|
||||
case 3:
|
||||
length = (bitrate * 144) / (samplerate << lsf) + padding;
|
||||
break;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (mp3parse, "Calculated mp3 frame length of %u bytes", length);
|
||||
GST_LOG_OBJECT (mp3parse, "samplerate = %lu, bitrate = %lu, version = %lu, "
|
||||
"layer = %lu, channels = %lu, mode = %lu", samplerate, bitrate, version,
|
||||
layer, channels, mode);
|
||||
|
||||
if (put_version)
|
||||
*put_version = version;
|
||||
if (put_layer)
|
||||
*put_layer = layer;
|
||||
if (put_channels)
|
||||
*put_channels = channels;
|
||||
if (put_bitrate)
|
||||
*put_bitrate = bitrate;
|
||||
if (put_samplerate)
|
||||
*put_samplerate = samplerate;
|
||||
if (put_mode)
|
||||
*put_mode = mode;
|
||||
if (put_crc)
|
||||
*put_crc = crc;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
/* generate empty/silent/dummy frame that mimics @frame,
|
||||
* except for rate, where maximum possible is selected */
|
||||
static GstADUFrame *
|
||||
gst_rtp_mpa_robust_depay_generate_dummy_frame (GstRtpMPARobustDepay *
|
||||
rtpmpadepay, GstADUFrame * frame)
|
||||
{
|
||||
GstADUFrame *dummy;
|
||||
|
||||
dummy = g_slice_dup (GstADUFrame, frame);
|
||||
|
||||
/* go for maximum bitrate */
|
||||
dummy->header = frame->header | (0xf << 12);
|
||||
dummy->size =
|
||||
mp3_type_frame_length_from_header (GST_ELEMENT_CAST (rtpmpadepay),
|
||||
dummy->header, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
dummy->data_size = dummy->size - dummy->side_info;
|
||||
dummy->backpointer = 0;
|
||||
|
||||
dummy->buffer = gst_buffer_new_and_alloc (dummy->size);
|
||||
memset (GST_BUFFER_DATA (dummy->buffer), 0, dummy->size);
|
||||
GST_WRITE_UINT32_BE (GST_BUFFER_DATA (dummy->buffer), dummy->header);
|
||||
GST_BUFFER_TIMESTAMP (dummy->buffer) = GST_BUFFER_TIMESTAMP (frame->buffer);
|
||||
|
||||
return dummy;
|
||||
}
|
||||
|
||||
/* validates and parses @buf, and queues for further transformation if valid,
|
||||
* otherwise discards @buf
|
||||
* Takes ownership of @buf. */
|
||||
static gboolean
|
||||
gst_rtp_mpa_robust_depay_queue_frame (GstRtpMPARobustDepay * rtpmpadepay,
|
||||
GstBuffer * buf)
|
||||
{
|
||||
GstADUFrame *frame = NULL;
|
||||
guint version, layer, channels, size;
|
||||
guint crc;
|
||||
|
||||
g_return_val_if_fail (buf != NULL, FALSE);
|
||||
|
||||
if (GST_BUFFER_SIZE (buf) < 6) {
|
||||
goto corrupt_frame;
|
||||
}
|
||||
|
||||
frame = g_slice_new0 (GstADUFrame);
|
||||
frame->header = GST_READ_UINT32_BE (GST_BUFFER_DATA (buf));
|
||||
|
||||
size = mp3_type_frame_length_from_header (GST_ELEMENT_CAST (rtpmpadepay),
|
||||
frame->header, &version, &layer, &channels, NULL, NULL, NULL, &crc);
|
||||
if (!size)
|
||||
goto corrupt_frame;
|
||||
|
||||
frame->size = size;
|
||||
frame->layer = layer;
|
||||
if (version == 1 && channels == 2)
|
||||
frame->side_info = 32;
|
||||
else if ((version == 1 && channels == 1) || (version >= 2 && channels == 2))
|
||||
frame->side_info = 17;
|
||||
else if (version >= 2 && channels == 1)
|
||||
frame->side_info = 9;
|
||||
else {
|
||||
g_assert_not_reached ();
|
||||
goto corrupt_frame;
|
||||
}
|
||||
|
||||
/* backpointer */
|
||||
if (layer == 3) {
|
||||
frame->backpointer = GST_READ_UINT16_BE (GST_BUFFER_DATA (buf) + 4);
|
||||
frame->backpointer >>= 7;
|
||||
GST_LOG_OBJECT (rtpmpadepay, "backpointer: %d", frame->backpointer);
|
||||
}
|
||||
|
||||
if (crc)
|
||||
frame->side_info += 2;
|
||||
|
||||
GST_LOG_OBJECT (rtpmpadepay, "side info: %d", frame->side_info);
|
||||
frame->data_size = frame->size - 4 - frame->side_info;
|
||||
|
||||
/* some size validation checks */
|
||||
if (4 + frame->side_info > GST_BUFFER_SIZE (buf))
|
||||
goto corrupt_frame;
|
||||
|
||||
/* ADU data would then extend past MP3 frame,
|
||||
* even using past byte reservoir */
|
||||
if (-frame->backpointer + GST_BUFFER_SIZE (buf) > frame->size)
|
||||
goto corrupt_frame;
|
||||
|
||||
/* ok, take buffer and queue */
|
||||
frame->buffer = buf;
|
||||
g_queue_push_tail (rtpmpadepay->adu_frames, frame);
|
||||
|
||||
return TRUE;
|
||||
|
||||
/* ERRORS */
|
||||
corrupt_frame:
|
||||
{
|
||||
GST_DEBUG_OBJECT (rtpmpadepay, "frame is corrupt");
|
||||
gst_buffer_unref (buf);
|
||||
if (frame)
|
||||
g_slice_free (GstADUFrame, frame);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
gst_rtp_mpa_robust_depay_free_frame (GstADUFrame * frame)
|
||||
{
|
||||
if (frame->buffer)
|
||||
gst_buffer_unref (frame->buffer);
|
||||
g_slice_free (GstADUFrame, frame);
|
||||
}
|
||||
|
||||
static inline void
|
||||
gst_rtp_mpa_robust_depay_dequeue_frame (GstRtpMPARobustDepay * rtpmpadepay)
|
||||
{
|
||||
GstADUFrame *head;
|
||||
|
||||
GST_LOG_OBJECT (rtpmpadepay, "dequeueing ADU frame");
|
||||
|
||||
if (rtpmpadepay->adu_frames->head == rtpmpadepay->cur_adu_frame)
|
||||
rtpmpadepay->cur_adu_frame = NULL;
|
||||
|
||||
head = g_queue_pop_head (rtpmpadepay->adu_frames);
|
||||
g_assert (head->buffer);
|
||||
gst_rtp_mpa_robust_depay_free_frame (head);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* returns TRUE if at least one new ADU frame was enqueued for MP3 conversion.
|
||||
* Takes ownership of @buf. */
|
||||
static gboolean
|
||||
gst_rtp_mpa_robust_depay_deinterleave (GstRtpMPARobustDepay * rtpmpadepay,
|
||||
GstBuffer * buf)
|
||||
{
|
||||
gboolean ret = FALSE;
|
||||
guint8 *data;
|
||||
guint val, iindex, icc;
|
||||
|
||||
data = GST_BUFFER_DATA (buf);
|
||||
val = GST_READ_UINT16_BE (data) >> 5;
|
||||
iindex = val >> 3;
|
||||
icc = val & 0x7;
|
||||
|
||||
GST_LOG_OBJECT (rtpmpadepay, "sync: 0x%x, index: %u, cycle count: %u",
|
||||
val, iindex, icc);
|
||||
|
||||
/* basic case; no interleaving ever seen */
|
||||
if (val == 0x7ff && rtpmpadepay->last_icc < 0) {
|
||||
ret = gst_rtp_mpa_robust_depay_queue_frame (rtpmpadepay, buf);
|
||||
} else {
|
||||
if (G_UNLIKELY (rtpmpadepay->last_icc < 0)) {
|
||||
rtpmpadepay->last_icc = icc;
|
||||
rtpmpadepay->last_ii = iindex;
|
||||
}
|
||||
if (icc != rtpmpadepay->last_icc || iindex == rtpmpadepay->last_ii) {
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < 256; ++i) {
|
||||
if (rtpmpadepay->deinter[i] != NULL) {
|
||||
ret |= gst_rtp_mpa_robust_depay_queue_frame (rtpmpadepay,
|
||||
rtpmpadepay->deinter[i]);
|
||||
rtpmpadepay->deinter[i] = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* rewrite buffer sync header */
|
||||
val = GST_READ_UINT16_BE (buf);
|
||||
val = (0x7ff << 5) | val;
|
||||
GST_WRITE_UINT16_BE (buf, val);
|
||||
/* store and keep track of last indices */
|
||||
rtpmpadepay->last_icc = icc;
|
||||
rtpmpadepay->last_ii = iindex;
|
||||
rtpmpadepay->deinter[iindex] = buf;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Head ADU frame corresponds to mp3_frame (i.e. in header in side-info) that
|
||||
* is currently being written
|
||||
* cur_adu_frame refers to ADU frame whose data should be bytewritten next
|
||||
* (possibly starting from offset rather than start 0) (and is typicall tail
|
||||
* at time of last push round).
|
||||
* If at start, position where it should start writing depends on (data) sizes
|
||||
* of previous mp3 frames (corresponding to foregoing ADU frames) kept in size,
|
||||
* and its backpointer */
|
||||
static GstFlowReturn
|
||||
gst_rtp_mpa_robust_depay_push_mp3_frames (GstRtpMPARobustDepay * rtpmpadepay)
|
||||
{
|
||||
GstBuffer *buf;
|
||||
GstADUFrame *frame, *head;
|
||||
gint av;
|
||||
GstFlowReturn ret = GST_FLOW_OK;
|
||||
|
||||
while (1) {
|
||||
|
||||
if (G_UNLIKELY (!rtpmpadepay->cur_adu_frame)) {
|
||||
rtpmpadepay->cur_adu_frame = rtpmpadepay->adu_frames->head;
|
||||
rtpmpadepay->offset = 0;
|
||||
rtpmpadepay->size = 0;
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (!rtpmpadepay->cur_adu_frame))
|
||||
break;
|
||||
|
||||
frame = (GstADUFrame *) rtpmpadepay->cur_adu_frame->data;
|
||||
head = (GstADUFrame *) rtpmpadepay->adu_frames->head->data;
|
||||
|
||||
/* special case: non-layer III are sent straight through */
|
||||
if (G_UNLIKELY (frame->layer != 3)) {
|
||||
GST_DEBUG_OBJECT (rtpmpadepay, "layer %d frame, sending as-is",
|
||||
frame->layer);
|
||||
gst_base_rtp_depayload_push (GST_BASE_RTP_DEPAYLOAD (rtpmpadepay),
|
||||
frame->buffer);
|
||||
frame->buffer = NULL;
|
||||
/* and remove it from any further consideration */
|
||||
g_slice_free (GstADUFrame, frame);
|
||||
g_queue_delete_link (rtpmpadepay->adu_frames, rtpmpadepay->cur_adu_frame);
|
||||
rtpmpadepay->cur_adu_frame = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rtpmpadepay->offset == GST_BUFFER_SIZE (frame->buffer)) {
|
||||
if (g_list_next (rtpmpadepay->cur_adu_frame)) {
|
||||
GST_LOG_OBJECT (rtpmpadepay,
|
||||
"moving to next ADU frame, size %d, side_info %d",
|
||||
frame->size, frame->side_info);
|
||||
rtpmpadepay->size += frame->data_size;
|
||||
rtpmpadepay->cur_adu_frame = g_list_next (rtpmpadepay->cur_adu_frame);
|
||||
frame = (GstADUFrame *) rtpmpadepay->cur_adu_frame->data;
|
||||
rtpmpadepay->offset = 0;
|
||||
/* layer I and II packets have no bitreservoir and must be sent as-is;
|
||||
* so flush any pending frame */
|
||||
if (G_UNLIKELY (frame->layer != 3 && rtpmpadepay->mp3_frame))
|
||||
goto flush;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (!rtpmpadepay->mp3_frame)) {
|
||||
GST_LOG_OBJECT (rtpmpadepay,
|
||||
"setting up new MP3 frame of size %d, side_info %d",
|
||||
head->size, head->side_info);
|
||||
rtpmpadepay->mp3_frame = gst_byte_writer_new_with_size (head->size, TRUE);
|
||||
/* 0-fill possible gaps */
|
||||
gst_byte_writer_fill (rtpmpadepay->mp3_frame, 0, head->size);
|
||||
gst_byte_writer_set_pos (rtpmpadepay->mp3_frame, 0);
|
||||
/* bytewriter corresponds to head frame,
|
||||
* i.e. the header and the side info must match */
|
||||
gst_byte_writer_put_data (rtpmpadepay->mp3_frame,
|
||||
GST_BUFFER_DATA (head->buffer), 4 + head->side_info);
|
||||
}
|
||||
|
||||
buf = frame->buffer;
|
||||
av = gst_byte_writer_get_remaining (rtpmpadepay->mp3_frame);
|
||||
GST_LOG_OBJECT (rtpmpadepay, "current mp3 frame remaining: %d", av);
|
||||
GST_LOG_OBJECT (rtpmpadepay, "accumulated ADU frame data_size: %d",
|
||||
rtpmpadepay->size);
|
||||
|
||||
if (rtpmpadepay->offset) {
|
||||
/* no need to position, simply append */
|
||||
g_assert (GST_BUFFER_SIZE (buf) > rtpmpadepay->offset);
|
||||
av = MIN (av, GST_BUFFER_SIZE (buf) - rtpmpadepay->offset);
|
||||
GST_LOG_OBJECT (rtpmpadepay,
|
||||
"appending %d bytes from ADU frame at offset %d", av,
|
||||
rtpmpadepay->offset);
|
||||
gst_byte_writer_put_data (rtpmpadepay->mp3_frame,
|
||||
GST_BUFFER_DATA (buf) + rtpmpadepay->offset, av);
|
||||
rtpmpadepay->offset += av;
|
||||
} else {
|
||||
gint pos, tpos;
|
||||
|
||||
/* position writing according to ADU frame backpointer */
|
||||
pos = gst_byte_writer_get_pos (rtpmpadepay->mp3_frame);
|
||||
tpos = rtpmpadepay->size - frame->backpointer + 4 + head->side_info;
|
||||
GST_LOG_OBJECT (rtpmpadepay, "current MP3 frame at position %d, "
|
||||
"starting new ADU frame data at offset %d", pos, tpos);
|
||||
if (tpos < pos) {
|
||||
GstADUFrame *dummy;
|
||||
|
||||
/* try to insert as few frames as possible,
|
||||
* so go for a reasonably large dummy frame size */
|
||||
GST_LOG_OBJECT (rtpmpadepay,
|
||||
"overlapping previous data; inserting dummy frame");
|
||||
dummy =
|
||||
gst_rtp_mpa_robust_depay_generate_dummy_frame (rtpmpadepay, frame);
|
||||
g_queue_insert_before (rtpmpadepay->adu_frames,
|
||||
rtpmpadepay->cur_adu_frame, dummy);
|
||||
/* offset is known to be zero, so we can shift current one */
|
||||
rtpmpadepay->cur_adu_frame = rtpmpadepay->cur_adu_frame->prev;
|
||||
/* ... and continue adding that empty one immediately,
|
||||
* and then see if that provided enough extra space */
|
||||
continue;
|
||||
} else if (tpos >= pos + av) {
|
||||
/* ADU frame no longer needs current MP3 frame; move to its end */
|
||||
GST_LOG_OBJECT (rtpmpadepay, "passed current MP3 frame");
|
||||
gst_byte_writer_set_pos (rtpmpadepay->mp3_frame, pos + av);
|
||||
} else {
|
||||
/* position and append */
|
||||
GST_LOG_OBJECT (rtpmpadepay, "adding to current MP3 frame");
|
||||
gst_byte_writer_set_pos (rtpmpadepay->mp3_frame, tpos);
|
||||
av = MIN (av, GST_BUFFER_SIZE (buf) - 4 - frame->side_info);
|
||||
gst_byte_writer_put_data (rtpmpadepay->mp3_frame,
|
||||
GST_BUFFER_DATA (buf) + 4 + frame->side_info, av);
|
||||
rtpmpadepay->offset += av + 4 + frame->side_info;
|
||||
}
|
||||
}
|
||||
|
||||
/* if mp3 frame filled, send on its way */
|
||||
if (gst_byte_writer_get_remaining (rtpmpadepay->mp3_frame) == 0) {
|
||||
flush:
|
||||
buf = gst_byte_writer_free_and_get_buffer (rtpmpadepay->mp3_frame);
|
||||
rtpmpadepay->mp3_frame = NULL;
|
||||
GST_BUFFER_TIMESTAMP (buf) = GST_BUFFER_TIMESTAMP (head->buffer);
|
||||
/* no longer need head ADU frame header and side info */
|
||||
/* NOTE maybe head == current, then size and offset go off a bit,
|
||||
* but current gets reset to NULL, and then also offset and size */
|
||||
rtpmpadepay->size -= head->data_size;
|
||||
gst_rtp_mpa_robust_depay_dequeue_frame (rtpmpadepay);
|
||||
/* send */
|
||||
ret = gst_base_rtp_depayload_push (GST_BASE_RTP_DEPAYLOAD (rtpmpadepay),
|
||||
buf);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* process ADU frame @buf through:
|
||||
* - deinterleaving
|
||||
* - converting to MP3 frames
|
||||
* Takes ownership of @buf.
|
||||
*/
|
||||
static GstFlowReturn
|
||||
gst_rtp_mpa_robust_depay_submit_adu (GstRtpMPARobustDepay * rtpmpadepay,
|
||||
GstBuffer * buf)
|
||||
{
|
||||
if (gst_rtp_mpa_robust_depay_deinterleave (rtpmpadepay, buf))
|
||||
return gst_rtp_mpa_robust_depay_push_mp3_frames (rtpmpadepay);
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static GstBuffer *
|
||||
gst_rtp_mpa_robust_depay_process (GstBaseRTPDepayload * depayload,
|
||||
GstBuffer * buf)
|
||||
{
|
||||
GstRtpMPARobustDepay *rtpmpadepay;
|
||||
gint payload_len, offset;
|
||||
guint8 *payload;
|
||||
gboolean cont, dtype;
|
||||
guint av, size;
|
||||
GstClockTime timestamp;
|
||||
|
||||
rtpmpadepay = GST_RTP_MPA_ROBUST_DEPAY (depayload);
|
||||
|
||||
payload_len = gst_rtp_buffer_get_payload_len (buf);
|
||||
timestamp = GST_BUFFER_TIMESTAMP (buf);
|
||||
|
||||
if (payload_len <= 1)
|
||||
goto short_read;
|
||||
|
||||
payload = gst_rtp_buffer_get_payload (buf);
|
||||
offset = 0;
|
||||
GST_LOG_OBJECT (rtpmpadepay, "payload_len: %d", payload_len);
|
||||
|
||||
/* strip off descriptor
|
||||
*
|
||||
* 0 1
|
||||
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
* |C|T| ADU size |
|
||||
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*
|
||||
* C: if 1, data is continuation
|
||||
* T: if 1, size is 14 bits, otherwise 6 bits
|
||||
* ADU size: size of following packet (not including descriptor)
|
||||
*/
|
||||
while (payload_len) {
|
||||
if (G_LIKELY (rtpmpadepay->has_descriptor)) {
|
||||
cont = !!(payload[offset] & 0x80);
|
||||
dtype = !!(payload[offset] & 0x40);
|
||||
if (dtype) {
|
||||
size = (payload[offset] & 0x3f) << 8 | payload[offset + 1];
|
||||
payload_len--;
|
||||
offset++;
|
||||
} else if (payload_len >= 2) {
|
||||
size = (payload[offset] & 0x3f);
|
||||
payload_len -= 2;
|
||||
offset += 2;
|
||||
} else {
|
||||
goto short_read;
|
||||
}
|
||||
} else {
|
||||
cont = FALSE;
|
||||
dtype = -1;
|
||||
size = payload_len;
|
||||
}
|
||||
|
||||
GST_LOG_OBJECT (rtpmpadepay, "offset %d has cont: %d, dtype: %d, size: %d",
|
||||
offset, cont, dtype, size);
|
||||
|
||||
buf = gst_rtp_buffer_get_payload_subbuffer (buf, offset,
|
||||
MIN (size, payload_len));
|
||||
|
||||
if (cont) {
|
||||
av = gst_adapter_available (rtpmpadepay->adapter);
|
||||
if (G_UNLIKELY (!av)) {
|
||||
GST_DEBUG_OBJECT (rtpmpadepay,
|
||||
"discarding continuation fragment without prior fragment");
|
||||
gst_buffer_unref (buf);
|
||||
} else {
|
||||
av += GST_BUFFER_SIZE (buf);
|
||||
gst_adapter_push (rtpmpadepay->adapter, buf);
|
||||
if (av == size) {
|
||||
timestamp = gst_adapter_prev_timestamp (rtpmpadepay->adapter, NULL);
|
||||
buf = gst_adapter_take_buffer (rtpmpadepay->adapter, size);
|
||||
GST_BUFFER_TIMESTAMP (buf) = timestamp;
|
||||
gst_rtp_mpa_robust_depay_submit_adu (rtpmpadepay, buf);
|
||||
} else if (av > size) {
|
||||
GST_DEBUG_OBJECT (rtpmpadepay,
|
||||
"assembled ADU size %d larger than expected %d; discarding",
|
||||
av, size);
|
||||
gst_adapter_clear (rtpmpadepay->adapter);
|
||||
}
|
||||
}
|
||||
size = payload_len;
|
||||
} else {
|
||||
/* not continuation, first fragment or whole ADU */
|
||||
if (payload_len == size) {
|
||||
/* whole ADU */
|
||||
GST_BUFFER_TIMESTAMP (buf) = timestamp;
|
||||
gst_rtp_mpa_robust_depay_submit_adu (rtpmpadepay, buf);
|
||||
} else if (payload_len < size) {
|
||||
/* first fragment */
|
||||
gst_adapter_push (rtpmpadepay->adapter, buf);
|
||||
size = payload_len;
|
||||
}
|
||||
}
|
||||
|
||||
offset += size;
|
||||
payload_len -= size;
|
||||
|
||||
/* timestamp applies to first payload, no idea for subsequent ones */
|
||||
timestamp = GST_CLOCK_TIME_NONE;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
||||
/* ERRORS */
|
||||
short_read:
|
||||
{
|
||||
GST_ELEMENT_WARNING (rtpmpadepay, STREAM, DECODE,
|
||||
(NULL), ("Packet contains invalid data"));
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static GstStateChangeReturn
|
||||
gst_rtp_mpa_robust_change_state (GstElement * element,
|
||||
GstStateChange transition)
|
||||
{
|
||||
GstStateChangeReturn ret;
|
||||
GstRtpMPARobustDepay *rtpmpadepay;
|
||||
|
||||
rtpmpadepay = GST_RTP_MPA_ROBUST_DEPAY (element);
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
||||
rtpmpadepay->last_ii = -1;
|
||||
rtpmpadepay->last_icc = -1;
|
||||
rtpmpadepay->size = 0;
|
||||
rtpmpadepay->offset = 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
||||
if (ret != GST_STATE_CHANGE_SUCCESS)
|
||||
return ret;
|
||||
|
||||
switch (transition) {
|
||||
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
||||
{
|
||||
gint i;
|
||||
|
||||
gst_adapter_clear (rtpmpadepay->adapter);
|
||||
for (i = 0; i < G_N_ELEMENTS (rtpmpadepay->deinter); i++) {
|
||||
gst_buffer_unref (rtpmpadepay->deinter[i]);
|
||||
rtpmpadepay->deinter[i] = NULL;
|
||||
}
|
||||
rtpmpadepay->cur_adu_frame = NULL;
|
||||
g_queue_foreach (rtpmpadepay->adu_frames,
|
||||
(GFunc) gst_rtp_mpa_robust_depay_free_frame, NULL);
|
||||
g_queue_clear (rtpmpadepay->adu_frames);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gst_rtp_mpa_robust_depay_plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
return gst_element_register (plugin, "rtpmparobustdepay",
|
||||
GST_RANK_MARGINAL, GST_TYPE_RTP_MPA_ROBUST_DEPAY);
|
||||
}
|
78
gst/rtp/gstrtpmparobustdepay.h
Normal file
78
gst/rtp/gstrtpmparobustdepay.h
Normal file
|
@ -0,0 +1,78 @@
|
|||
/* GStreamer
|
||||
* Copyright (C) <2010> Mark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
|
||||
* Copyright (C) <2010> Nokia Corporation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_RTP_MPA_ROBUST_DEPAY_H__
|
||||
#define __GST_RTP_MPA_ROBUST_DEPAY_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/rtp/gstbasertpdepayload.h>
|
||||
#include <gst/base/gstadapter.h>
|
||||
#include <gst/base/gstbytewriter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_RTP_MPA_ROBUST_DEPAY \
|
||||
(gst_rtp_mpa_robust_depay_get_type())
|
||||
#define GST_RTP_MPA_ROBUST_DEPAY(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RTP_MPA_ROBUST_DEPAY,GstRtpMPARobustDepay))
|
||||
#define GST_RTP_MPA_ROBUST_DEPAY_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RTP_MPA_ROBUST_DEPAY,GstRtpMPARobustDepayClass))
|
||||
#define GST_IS_RTP_MPA_ROBUST_DEPAY(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RTP_MPA_ROBUST_DEPAY))
|
||||
#define GST_IS_RTP_MPA_ROBUST_DEPAY_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RTP_MPA_ROBUST_DEPAY))
|
||||
|
||||
typedef struct _GstRtpMPARobustDepay GstRtpMPARobustDepay;
|
||||
typedef struct _GstRtpMPARobustDepayClass GstRtpMPARobustDepayClass;
|
||||
|
||||
struct _GstRtpMPARobustDepay
|
||||
{
|
||||
GstBaseRTPDepayload depayload;
|
||||
|
||||
GstAdapter *adapter;
|
||||
gboolean has_descriptor;
|
||||
|
||||
/* last interleave index */
|
||||
gint last_ii;
|
||||
/* last interleave cycle count */
|
||||
gint last_icc;
|
||||
/* buffers pending deinterleaving */
|
||||
GstBuffer *deinter[256];
|
||||
|
||||
/* ADU buffers pending MP3 transformation */
|
||||
GQueue *adu_frames;
|
||||
GList *cur_adu_frame;
|
||||
gint offset;
|
||||
gint size;
|
||||
GstByteWriter *mp3_frame;
|
||||
};
|
||||
|
||||
struct _GstRtpMPARobustDepayClass
|
||||
{
|
||||
GstBaseRTPDepayloadClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_rtp_mpa_robust_depay_get_type (void);
|
||||
|
||||
gboolean gst_rtp_mpa_robust_depay_plugin_init (GstPlugin * plugin);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_RTP_MPA_ROBUST_DEPAY_H__ */
|
Loading…
Reference in a new issue