mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-09-08 21:28:45 +00:00
Amendment 2 of ISO/IEC 14496-15 (AVC file format) is defining a new structure for fragmented MP4 called "avc3". The principal difference between AVC1 and AVC3 is the location of the codec initialisation data (e.g. SPS, PPS). In AVC1 this data is placed in the initial MOOV box (moov.trak.mdia.minf.stbl.stsd.avc1) but in AVC3 this data goes in the first sample of every fragment (i.e. the first sample in each mdat box). The principal reason for avc3 is to make it easier for client implementations, because it removes the requirement to insert the SPS+PPS in to the decoder pipeline every time there is a representation change. This commit adds support for the "avc3" atom, which is almost identical to the "avc1" atom, except it does not contain any SPS or PPS data. https://bugzilla.gnome.org/show_bug.cgi?id=702004
687 lines
21 KiB
C
687 lines
21 KiB
C
/* GStreamer
|
|
* Copyright (C) <2006> Wim Taymans <wim@fluendo.com>
|
|
*
|
|
* 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., 51 Franklin St, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/*
|
|
* based on http://developer.apple.com/quicktime/icefloe/dispatch026.html
|
|
*/
|
|
#ifdef HAVE_CONFIG_H
|
|
# include "config.h"
|
|
#endif
|
|
|
|
#include <gst/rtp/gstrtpbuffer.h>
|
|
|
|
#include <string.h>
|
|
#include "gstrtpxqtdepay.h"
|
|
|
|
#define MAKE_TLV(a,b) (((a)<<8)|(b))
|
|
|
|
#define TLV_sd MAKE_TLV ('s','d')
|
|
#define TLV_qt MAKE_TLV ('q','t')
|
|
#define TLV_ti MAKE_TLV ('t','i')
|
|
#define TLV_ly MAKE_TLV ('l','y')
|
|
#define TLV_vo MAKE_TLV ('v','o')
|
|
#define TLV_mx MAKE_TLV ('m','x')
|
|
#define TLV_tr MAKE_TLV ('t','r')
|
|
#define TLV_tw MAKE_TLV ('t','w')
|
|
#define TLV_th MAKE_TLV ('t','h')
|
|
#define TLV_la MAKE_TLV ('l','a')
|
|
#define TLV_rt MAKE_TLV ('r','t')
|
|
#define TLV_gm MAKE_TLV ('g','m')
|
|
#define TLV_oc MAKE_TLV ('o','c')
|
|
#define TLV_cr MAKE_TLV ('c','r')
|
|
#define TLV_du MAKE_TLV ('d','u')
|
|
#define TLV_po MAKE_TLV ('p','o')
|
|
|
|
#define QT_UINT32(a) (GST_READ_UINT32_BE(a))
|
|
#define QT_UINT24(a) (GST_READ_UINT32_BE(a) >> 8)
|
|
#define QT_UINT16(a) (GST_READ_UINT16_BE(a))
|
|
#define QT_UINT8(a) (GST_READ_UINT8(a))
|
|
#define QT_FP32(a) ((GST_READ_UINT32_BE(a))/65536.0)
|
|
#define QT_FP16(a) ((GST_READ_UINT16_BE(a))/256.0)
|
|
#define QT_FOURCC(a) (GST_READ_UINT32_LE(a))
|
|
#define QT_UINT64(a) ((((guint64)QT_UINT32(a))<<32)|QT_UINT32(((guint8 *)a)+4))
|
|
|
|
#define FOURCC_avc1 GST_MAKE_FOURCC('a','v','c','1')
|
|
#define FOURCC_avc3 GST_MAKE_FOURCC('a','v','c','3')
|
|
#define FOURCC_avcC GST_MAKE_FOURCC('a','v','c','C')
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (rtpxqtdepay_debug);
|
|
#define GST_CAT_DEFAULT (rtpxqtdepay_debug)
|
|
|
|
/* RtpXQTDepay signals and args */
|
|
enum
|
|
{
|
|
/* FILL ME */
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
enum
|
|
{
|
|
ARG_0,
|
|
};
|
|
|
|
static GstStaticPadTemplate gst_rtp_xqt_depay_src_template =
|
|
GST_STATIC_PAD_TEMPLATE ("src",
|
|
GST_PAD_SRC,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS_ANY);
|
|
|
|
static GstStaticPadTemplate gst_rtp_xqt_depay_sink_template =
|
|
GST_STATIC_PAD_TEMPLATE ("sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("application/x-rtp, "
|
|
"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
|
|
"media = (string) { \"audio\", \"video\" }, clock-rate = (int) [1, MAX], "
|
|
"encoding-name = (string) { \"X-QT\", \"X-QUICKTIME\" }")
|
|
);
|
|
|
|
#define gst_rtp_xqt_depay_parent_class parent_class
|
|
G_DEFINE_TYPE (GstRtpXQTDepay, gst_rtp_xqt_depay, GST_TYPE_RTP_BASE_DEPAYLOAD);
|
|
|
|
static void gst_rtp_xqt_depay_finalize (GObject * object);
|
|
|
|
static gboolean gst_rtp_xqt_depay_setcaps (GstRTPBaseDepayload * depayload,
|
|
GstCaps * caps);
|
|
static GstBuffer *gst_rtp_xqt_depay_process (GstRTPBaseDepayload * depayload,
|
|
GstBuffer * buf);
|
|
|
|
static GstStateChangeReturn gst_rtp_xqt_depay_change_state (GstElement *
|
|
element, GstStateChange transition);
|
|
|
|
|
|
static void
|
|
gst_rtp_xqt_depay_class_init (GstRtpXQTDepayClass * klass)
|
|
{
|
|
GObjectClass *gobject_class;
|
|
GstElementClass *gstelement_class;
|
|
GstRTPBaseDepayloadClass *gstrtpbasedepayload_class;
|
|
|
|
gobject_class = (GObjectClass *) klass;
|
|
gstelement_class = (GstElementClass *) klass;
|
|
gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass;
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
gobject_class->finalize = gst_rtp_xqt_depay_finalize;
|
|
|
|
gstelement_class->change_state = gst_rtp_xqt_depay_change_state;
|
|
|
|
gstrtpbasedepayload_class->set_caps = gst_rtp_xqt_depay_setcaps;
|
|
gstrtpbasedepayload_class->process = gst_rtp_xqt_depay_process;
|
|
|
|
GST_DEBUG_CATEGORY_INIT (rtpxqtdepay_debug, "rtpxqtdepay", 0,
|
|
"QT Media RTP Depayloader");
|
|
|
|
gst_element_class_add_pad_template (gstelement_class,
|
|
gst_static_pad_template_get (&gst_rtp_xqt_depay_src_template));
|
|
gst_element_class_add_pad_template (gstelement_class,
|
|
gst_static_pad_template_get (&gst_rtp_xqt_depay_sink_template));
|
|
|
|
gst_element_class_set_static_metadata (gstelement_class,
|
|
"RTP packet depayloader", "Codec/Depayloader/Network",
|
|
"Extracts Quicktime audio/video from RTP packets",
|
|
"Wim Taymans <wim@fluendo.com>");
|
|
}
|
|
|
|
static void
|
|
gst_rtp_xqt_depay_init (GstRtpXQTDepay * rtpxqtdepay)
|
|
{
|
|
rtpxqtdepay->adapter = gst_adapter_new ();
|
|
}
|
|
|
|
static void
|
|
gst_rtp_xqt_depay_finalize (GObject * object)
|
|
{
|
|
GstRtpXQTDepay *rtpxqtdepay;
|
|
|
|
rtpxqtdepay = GST_RTP_XQT_DEPAY (object);
|
|
|
|
g_object_unref (rtpxqtdepay->adapter);
|
|
rtpxqtdepay->adapter = NULL;
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static gboolean
|
|
gst_rtp_quicktime_parse_sd (GstRtpXQTDepay * rtpxqtdepay, guint8 * data,
|
|
guint data_len)
|
|
{
|
|
gint len;
|
|
guint32 fourcc;
|
|
|
|
if (data_len < 8)
|
|
goto too_short;
|
|
|
|
len = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
|
|
if (len > data_len)
|
|
goto too_short;
|
|
|
|
fourcc = QT_FOURCC (data + 4);
|
|
|
|
GST_DEBUG_OBJECT (rtpxqtdepay, "parsing %" GST_FOURCC_FORMAT,
|
|
GST_FOURCC_ARGS (fourcc));
|
|
|
|
switch (fourcc) {
|
|
case FOURCC_avc1:
|
|
case FOURCC_avc3:
|
|
{
|
|
guint32 chlen;
|
|
|
|
if (len < 0x56)
|
|
goto too_short;
|
|
len -= 0x56;
|
|
data += 0x56;
|
|
|
|
/* find avcC */
|
|
while (len >= 8) {
|
|
chlen = QT_UINT32 (data);
|
|
fourcc = QT_FOURCC (data + 4);
|
|
if (fourcc == FOURCC_avcC) {
|
|
GstBuffer *buf;
|
|
gint size;
|
|
GstCaps *caps;
|
|
|
|
GST_DEBUG_OBJECT (rtpxqtdepay, "found avcC codec_data in sd, %u",
|
|
chlen);
|
|
|
|
/* parse, if found */
|
|
if (chlen < len)
|
|
size = chlen - 8;
|
|
else
|
|
size = len - 8;
|
|
|
|
buf = gst_buffer_new_and_alloc (size);
|
|
gst_buffer_fill (buf, 0, data + 8, size);
|
|
caps = gst_caps_new_simple ("video/x-h264",
|
|
"codec_data", GST_TYPE_BUFFER, buf, NULL);
|
|
gst_buffer_unref (buf);
|
|
gst_pad_set_caps (GST_RTP_BASE_DEPAYLOAD (rtpxqtdepay)->srcpad, caps);
|
|
gst_caps_unref (caps);
|
|
break;
|
|
}
|
|
len -= chlen;
|
|
data += chlen;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return TRUE;
|
|
|
|
/* ERRORS */
|
|
too_short:
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gst_rtp_xqt_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
|
|
{
|
|
GstStructure *structure;
|
|
gint clock_rate = 90000; /* default */
|
|
|
|
structure = gst_caps_get_structure (caps, 0);
|
|
|
|
gst_structure_get_int (structure, "clock-rate", &clock_rate);
|
|
depayload->clock_rate = clock_rate;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static GstBuffer *
|
|
gst_rtp_xqt_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf)
|
|
{
|
|
GstRtpXQTDepay *rtpxqtdepay;
|
|
GstBuffer *outbuf = NULL;
|
|
gboolean m;
|
|
GstRTPBuffer rtp = { NULL };
|
|
|
|
rtpxqtdepay = GST_RTP_XQT_DEPAY (depayload);
|
|
|
|
gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp);
|
|
|
|
if (GST_BUFFER_IS_DISCONT (buf)) {
|
|
/* discont, clear adapter and try to find a new packet start */
|
|
gst_adapter_clear (rtpxqtdepay->adapter);
|
|
rtpxqtdepay->need_resync = TRUE;
|
|
GST_DEBUG_OBJECT (rtpxqtdepay, "we need resync");
|
|
}
|
|
|
|
m = gst_rtp_buffer_get_marker (&rtp);
|
|
GST_LOG_OBJECT (rtpxqtdepay, "marker: %d", m);
|
|
|
|
{
|
|
gint payload_len;
|
|
guint avail;
|
|
guint8 *payload;
|
|
guint8 ver, pck;
|
|
gboolean s, q, l, d;
|
|
|
|
payload_len = gst_rtp_buffer_get_payload_len (&rtp);
|
|
payload = gst_rtp_buffer_get_payload (&rtp);
|
|
|
|
/* 1 2 3
|
|
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | VER |PCK|S|Q|L| RES |D| QuickTime Payload ID |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
if (payload_len <= 4)
|
|
goto wrong_length;
|
|
|
|
ver = (payload[0] & 0xf0) >> 4;
|
|
if (ver > 1)
|
|
goto wrong_version;
|
|
|
|
pck = (payload[0] & 0x0c) >> 2;
|
|
if (pck == 0)
|
|
goto pck_reserved;
|
|
|
|
s = (payload[0] & 0x02) != 0; /* contains sync sample */
|
|
q = (payload[0] & 0x01) != 0; /* has payload description */
|
|
l = (payload[1] & 0x80) != 0; /* has packet specific information description */
|
|
d = (payload[2] & 0x80) != 0; /* don't cache info for payload id */
|
|
/* id used for caching info */
|
|
rtpxqtdepay->current_id = ((payload[2] & 0x7f) << 8) | payload[3];
|
|
|
|
GST_LOG_OBJECT (rtpxqtdepay,
|
|
"VER: %d, PCK: %d, S: %d, Q: %d, L: %d, D: %d, ID: %d", ver, pck, s, q,
|
|
l, d, rtpxqtdepay->current_id);
|
|
|
|
if (rtpxqtdepay->need_resync) {
|
|
/* we need to find the boundary of a new packet after a DISCONT */
|
|
if (pck != 3 || q) {
|
|
/* non-fragmented packet or payload description present, packet starts
|
|
* here. */
|
|
rtpxqtdepay->need_resync = FALSE;
|
|
} else {
|
|
/* fragmented packet without description */
|
|
if (m) {
|
|
/* marker bit set, next packet is start of new one */
|
|
rtpxqtdepay->need_resync = FALSE;
|
|
}
|
|
goto need_resync;
|
|
}
|
|
}
|
|
|
|
payload += 4;
|
|
payload_len -= 4;
|
|
|
|
if (q) {
|
|
gboolean k, f, a, z;
|
|
guint pdlen, pdpadded;
|
|
gint padding;
|
|
/* media_type only used for printing */
|
|
guint32 G_GNUC_UNUSED media_type;
|
|
guint32 timescale;
|
|
|
|
/* 1 2 3
|
|
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* |K|F|A|Z| RES | QuickTime Payload Desc Length |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* . QuickTime Payload Desc Data ... .
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
if (payload_len <= 4)
|
|
goto wrong_length;
|
|
|
|
k = (payload[0] & 0x80) != 0; /* keyframe */
|
|
f = (payload[0] & 0x40) != 0; /* sparse */
|
|
a = (payload[0] & 0x20) != 0; /* start of payload */
|
|
z = (payload[0] & 0x10) != 0; /* end of payload */
|
|
pdlen = (payload[2] << 8) | payload[3];
|
|
|
|
if (pdlen < 12)
|
|
goto wrong_length;
|
|
|
|
/* calc padding */
|
|
pdpadded = pdlen + 3;
|
|
pdpadded -= pdpadded % 4;
|
|
if (payload_len < pdpadded)
|
|
goto wrong_length;
|
|
|
|
padding = pdpadded - pdlen;
|
|
GST_LOG_OBJECT (rtpxqtdepay,
|
|
"K: %d, F: %d, A: %d, Z: %d, len: %d, padding %d", k, f, a, z, pdlen,
|
|
padding);
|
|
|
|
payload += 4;
|
|
payload_len -= 4;
|
|
/* 1 2 3
|
|
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | QuickTime Media Type |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | Timescale |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* . QuickTime TLVs ... .
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
media_type =
|
|
(payload[0] << 24) | (payload[1] << 16) | (payload[2] << 8) |
|
|
payload[3];
|
|
timescale =
|
|
(payload[4] << 24) | (payload[5] << 16) | (payload[6] << 8) |
|
|
payload[7];
|
|
|
|
GST_LOG_OBJECT (rtpxqtdepay, "media_type: %c%c%c%c, timescale %u",
|
|
payload[0], payload[1], payload[2], payload[3], timescale);
|
|
|
|
payload += 8;
|
|
payload_len -= 8;
|
|
pdlen -= 12;
|
|
|
|
/* parse TLV (type-length-value triplets */
|
|
while (pdlen > 3) {
|
|
guint16 tlv_len, tlv_type;
|
|
|
|
/* 1 2 3
|
|
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | QuickTime TLV Length | QuickTime TLV Type |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* . QuickTime TLV Value ... .
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
tlv_len = (payload[0] << 8) | payload[1];
|
|
tlv_type = (payload[2] << 8) | payload[3];
|
|
pdlen -= 4;
|
|
if (tlv_len > pdlen)
|
|
goto wrong_length;
|
|
|
|
GST_LOG_OBJECT (rtpxqtdepay, "TLV '%c%c', len %d", payload[2],
|
|
payload[3], tlv_len);
|
|
|
|
payload += 4;
|
|
payload_len -= 4;
|
|
|
|
switch (tlv_type) {
|
|
case TLV_sd:
|
|
/* Session description */
|
|
if (!gst_rtp_quicktime_parse_sd (rtpxqtdepay, payload, tlv_len))
|
|
goto unknown_format;
|
|
rtpxqtdepay->have_sd = TRUE;
|
|
break;
|
|
case TLV_qt:
|
|
case TLV_ti:
|
|
case TLV_ly:
|
|
case TLV_vo:
|
|
case TLV_mx:
|
|
case TLV_tr:
|
|
case TLV_tw:
|
|
case TLV_th:
|
|
case TLV_la:
|
|
case TLV_rt:
|
|
case TLV_gm:
|
|
case TLV_oc:
|
|
case TLV_cr:
|
|
case TLV_du:
|
|
case TLV_po:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
pdlen -= tlv_len;
|
|
payload += tlv_len;
|
|
payload_len -= tlv_len;
|
|
}
|
|
payload += padding;
|
|
payload_len -= padding;
|
|
}
|
|
|
|
if (l) {
|
|
guint ssilen, ssipadded;
|
|
gint padding;
|
|
|
|
/* 1 2 3
|
|
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | RES | Sample-Specific Info Length |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* . QuickTime TLVs ...
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
if (payload_len <= 4)
|
|
goto wrong_length;
|
|
|
|
ssilen = (payload[2] << 8) | payload[3];
|
|
if (ssilen < 4)
|
|
goto wrong_length;
|
|
|
|
/* calc padding */
|
|
ssipadded = ssilen + 3;
|
|
ssipadded -= ssipadded % 4;
|
|
if (payload_len < ssipadded)
|
|
goto wrong_length;
|
|
|
|
padding = ssipadded - ssilen;
|
|
GST_LOG_OBJECT (rtpxqtdepay, "len: %d, padding %d", ssilen, padding);
|
|
|
|
payload += 4;
|
|
payload_len -= 4;
|
|
ssilen -= 4;
|
|
|
|
/* parse TLV (type-length-value triplets */
|
|
while (ssilen > 3) {
|
|
guint16 tlv_len, tlv_type;
|
|
|
|
/* 1 2 3
|
|
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | QuickTime TLV Length | QuickTime TLV Type |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* . QuickTime TLV Value ... .
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
tlv_len = (payload[0] << 8) | payload[1];
|
|
tlv_type = (payload[2] << 8) | payload[3];
|
|
ssilen -= 4;
|
|
if (tlv_len > ssilen)
|
|
goto wrong_length;
|
|
|
|
GST_LOG_OBJECT (rtpxqtdepay, "TLV '%c%c', len %d", payload[2],
|
|
payload[3], tlv_len);
|
|
|
|
payload += 4;
|
|
payload_len -= 4;
|
|
|
|
switch (tlv_type) {
|
|
case TLV_sd:
|
|
case TLV_qt:
|
|
case TLV_ti:
|
|
case TLV_ly:
|
|
case TLV_vo:
|
|
case TLV_mx:
|
|
case TLV_tr:
|
|
case TLV_tw:
|
|
case TLV_th:
|
|
case TLV_la:
|
|
case TLV_rt:
|
|
case TLV_gm:
|
|
case TLV_oc:
|
|
case TLV_cr:
|
|
case TLV_du:
|
|
case TLV_po:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ssilen -= tlv_len;
|
|
payload += tlv_len;
|
|
payload_len -= tlv_len;
|
|
}
|
|
payload += padding;
|
|
payload_len -= padding;
|
|
}
|
|
|
|
rtpxqtdepay->previous_id = rtpxqtdepay->current_id;
|
|
|
|
switch (pck) {
|
|
case 1:
|
|
{
|
|
/* multiple samples per packet. */
|
|
outbuf = gst_buffer_new_and_alloc (payload_len);
|
|
gst_buffer_fill (outbuf, 0, payload, payload_len);
|
|
|
|
goto done;
|
|
}
|
|
case 2:
|
|
{
|
|
guint slen;
|
|
|
|
/* multiple samples per packet.
|
|
* 1 2 3
|
|
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* |S| Reserved | Sample Length |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | Sample Timestamp |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* . Sample Data ... .
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* |S| Reserved | Sample Length |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* | Sample Timestamp |
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* . Sample Data ... .
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
* . ...... .
|
|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
while (payload_len > 8) {
|
|
s = (payload[0] & 0x80) != 0; /* contains sync sample */
|
|
slen = (payload[2] << 8) | payload[3];
|
|
/* timestamp =
|
|
* (payload[4] << 24) | (payload[5] << 16) | (payload[6] << 8) |
|
|
* payload[7];
|
|
*/
|
|
|
|
payload += 8;
|
|
payload_len -= 8;
|
|
|
|
if (slen > payload_len)
|
|
slen = payload_len;
|
|
|
|
outbuf = gst_buffer_new_and_alloc (slen);
|
|
gst_buffer_fill (outbuf, 0, payload, slen);
|
|
if (!s)
|
|
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT);
|
|
|
|
gst_rtp_base_depayload_push (depayload, outbuf);
|
|
|
|
/* aligned on 32 bit boundary */
|
|
slen = GST_ROUND_UP_4 (slen);
|
|
|
|
payload += slen;
|
|
payload_len -= slen;
|
|
}
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
/* one sample per packet, use adapter to combine based on marker bit. */
|
|
outbuf = gst_buffer_new_and_alloc (payload_len);
|
|
gst_buffer_fill (outbuf, 0, payload, payload_len);
|
|
|
|
gst_adapter_push (rtpxqtdepay->adapter, outbuf);
|
|
outbuf = NULL;
|
|
|
|
if (!m)
|
|
goto done;
|
|
|
|
avail = gst_adapter_available (rtpxqtdepay->adapter);
|
|
outbuf = gst_adapter_take_buffer (rtpxqtdepay->adapter, avail);
|
|
|
|
GST_DEBUG_OBJECT (rtpxqtdepay,
|
|
"gst_rtp_xqt_depay_chain: pushing buffer of size %u", avail);
|
|
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
|
|
done:
|
|
gst_rtp_buffer_unmap (&rtp);
|
|
return outbuf;
|
|
|
|
need_resync:
|
|
{
|
|
GST_DEBUG_OBJECT (rtpxqtdepay, "waiting for marker");
|
|
goto done;
|
|
}
|
|
wrong_version:
|
|
{
|
|
GST_ELEMENT_WARNING (rtpxqtdepay, STREAM, DECODE,
|
|
("Unknown payload version."), (NULL));
|
|
goto done;
|
|
}
|
|
pck_reserved:
|
|
{
|
|
GST_ELEMENT_WARNING (rtpxqtdepay, STREAM, DECODE,
|
|
("PCK reserved 0."), (NULL));
|
|
goto done;
|
|
}
|
|
wrong_length:
|
|
{
|
|
GST_ELEMENT_WARNING (rtpxqtdepay, STREAM, DECODE,
|
|
("Wrong payload length."), (NULL));
|
|
goto done;
|
|
}
|
|
unknown_format:
|
|
{
|
|
GST_ELEMENT_WARNING (rtpxqtdepay, STREAM, DECODE,
|
|
("Unknown payload format."), (NULL));
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
static GstStateChangeReturn
|
|
gst_rtp_xqt_depay_change_state (GstElement * element, GstStateChange transition)
|
|
{
|
|
GstRtpXQTDepay *rtpxqtdepay;
|
|
GstStateChangeReturn ret;
|
|
|
|
rtpxqtdepay = GST_RTP_XQT_DEPAY (element);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_READY_TO_PAUSED:
|
|
gst_adapter_clear (rtpxqtdepay->adapter);
|
|
rtpxqtdepay->previous_id = -1;
|
|
rtpxqtdepay->current_id = -1;
|
|
rtpxqtdepay->need_resync = TRUE;
|
|
rtpxqtdepay->have_sd = FALSE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
|
|
|
|
switch (transition) {
|
|
case GST_STATE_CHANGE_PAUSED_TO_READY:
|
|
gst_adapter_clear (rtpxqtdepay->adapter);
|
|
default:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|