Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • gstreamer/gst-plugins-good
  • thiblahute/gst-plugins-good
  • slomo/gst-plugins-good
  • sree/gst-plugins-good
  • seungha.yang/gst-plugins-good
  • xclaesse/gst-plugins-good
  • haihao/gst-plugins-good
  • alatiera/gst-plugins-good
  • heftig/gst-plugins-good
  • bilboed/gst-plugins-good
  • ndufresne/gst-plugins-good
  • ystreet/gst-plugins-good
  • hgr/gst-plugins-good
  • nielsdg/gst-plugins-good
  • jh-hsd/gst-plugins-good
  • linussn/gst-plugins-good
  • meh/gst-plugins-good
  • ntrrgc/gst-plugins-good
  • gdesmott/gst-plugins-good
  • arun/gst-plugins-good
  • Yeongjin-Jeong/gst-plugins-good
  • christli/gst-plugins-good
  • drakkan/gst-plugins-good
  • tpm/gst-plugins-good
  • patrickr/gst-plugins-good
  • patricia/gst-plugins-good
  • ahresse/gst-plugins-good
  • mparisdiaz/gst-plugins-good
  • milloni-ct/gst-plugins-good
  • cadubentzen/gst-plugins-good
  • juanpablougarte/gst-plugins-good
  • joykim/gst-plugins-good
  • jonnylamb/gst-plugins-good
  • myrandy1/gst-plugins-good
  • camilo-celis/gst-plugins-good
  • elmarco/gst-plugins-good
  • donghyeok/gst-plugins-good
  • zeenix/gst-plugins-good
  • kode54/gst-plugins-good
  • victortoso/gst-plugins-good
  • nirbheek/gst-plugins-good
  • gkiagia/gst-plugins-good
  • erlend_ne/gst-plugins-good
  • cfoch/gst-plugins-good
  • codinho/gst-plugins-good
  • chturne/gst-plugins-good
  • vivia/gst-plugins-good
  • Bjorkstrom/gst-plugins-good
  • JimmyOhn/gst-plugins-good
  • gunsungithub/gst-plugins-good
  • xhaakon/gst-plugins-good
  • den_erpel/gst-plugins-good
  • mchehab_kernel/gst-plugins-good
  • thaytan/gst-plugins-good
  • pH5/gst-plugins-good
  • julian/gst-plugins-good
  • ebassi/gst-plugins-good
  • dank/gst-plugins-good
  • PanMichal/gst-plugins-good
  • ahamedsajeer.15/gst-plugins-good
  • lord.jacold/gst-plugins-good
  • kevinbing.song/gst-plugins-good
  • wonchul/gst-plugins-good
  • bsu/gst-plugins-good
  • okuoku/gst-plugins-good
  • GstBlub/gst-plugins-good
  • dannys/gst-plugins-good
  • thiagossantos/gst-plugins-good
  • mol/gst-plugins-good
  • Dmt/gst-plugins-good
  • leio/gst-plugins-good
  • dhobsong/gst-plugins-good
  • vjaquez/gst-plugins-good
  • dv1/gst-plugins-good
  • Freyr/gst-plugins-good
  • marcosk/gst-plugins-good
  • sancane/gst-plugins-good
  • jcelaya/gst-plugins-good
  • knut.tidemann/gst-plugins-good
  • staples255/gst-plugins-good
  • dougnazar/gst-plugins-good
  • He_Junyan/gst-plugins-good
  • amrmahdi/gst-plugins-good
  • philippefoubert/gst-plugins-good
  • sbaath/gst-plugins-good
  • springermac/gst-plugins-good
  • skanowitz/gst-plugins-good
  • dabrain34/gst-plugins-good
  • HuQian/gst-plugins-good
  • brechtvr0me/gst-plugins-good
  • cmclar/gst-plugins-good
  • fuweitax/gst-plugins-good
  • Jacquemart/gst-plugins-good
  • ihalip/gst-plugins-good
  • aogun.china/gst-plugins-good
  • goranjn/gst-plugins-good
  • fclaramonte/gst-plugins-good
  • milianw/gst-plugins-good
  • vrplumber/gst-plugins-good
  • cap/gst-plugins-good
  • andrew.voznytsa/gst-plugins-good
  • jfcarp/gst-plugins-good
  • JoakimJ/gst-plugins-good
  • Swap-File/gst-plugins-good
  • danimo/gst-plugins-good
  • alexashley/gst-plugins-good
  • winand.seldeslachts/gst-plugins-good
  • dvzrv/gst-plugins-good
  • pogojotz/gst-plugins-good
  • alexander.lapajne/gst-plugins-good
  • redstar/gst-plugins-good
  • nazar-pc/gst-plugins-good
  • zfigura/gst-plugins-good
  • Buora/gst-plugins-good
  • DuBistKomisch/gst-plugins-good
  • johan-bjareholt/gst-plugins-good
  • ebnerm/gst-plugins-good
  • xiezhaoxuan/gst-plugins-good
  • eberjand/gst-plugins-good
  • paulyc/gst-plugins-good
  • kztslee/gst-plugins-good
  • gordonhart/gst-plugins-good
  • StefanBruens/gst-plugins-good
  • kkangshawn/gst-plugins-good
  • kay0u/gst-plugins-good
  • JaredHu/gst-plugins-good
  • yan3nian/gst-plugins-good
  • kay0u1/gst-plugins-good
  • rambden/gst-plugins-good
  • dpurgin/gst-plugins-good
  • TheMuso/gst-plugins-good
  • Knopp/gst-plugins-good
  • ullysses.a.eoff/gst-plugins-good
  • 4kevinking/gst-plugins-good
  • Hosang/gst-plugins-good
  • marex/gst-plugins-good
  • trollkarlen/gst-plugins-good
  • jedevc/gst-plugins-good
  • appteamlover/gst-plugins-good
  • yishai1999/gst-plugins-good
  • andrey-khamukhin/gst-plugins-good
  • mdzik/gst-plugins-good
  • florent.thiery/gst-plugins-good
  • rickylineow/gst-plugins-good
  • jlaheurte/gst-plugins-good
  • SanchayanMaity/gst-plugins-good
  • ponmadasamy/gst-plugins-good
  • senatoreg/gst-plugins-good
  • id_est/gst-plugins-good
  • Rafostar/gst-plugins-good
  • gstreamer-release-bot/gst-plugins-good
  • bastienr/gst-plugins-good
  • dong9/gst-plugins-good
  • wtfrank/gst-plugins-good
  • saidinesh5/gst-plugins-good
  • mexxik/gst-plugins-good
  • quaresma.jose/gst-plugins-good
  • ChrisDuncan/gst-plugins-good
  • andrzej.p/gst-plugins-good
  • TobiasR/gst-plugins-good
  • Olssdani/gst-plugins-good
  • nacho.resa/gst-plugins-good
  • vicamo/gst-plugins-good
  • ignapk/gst-plugins-good
  • calvaris/gst-plugins-good
  • m.tretter/gst-plugins-good
  • boosth/gst-plugins-good
  • ashley-b/gst-plugins-good
  • i-tsygankov/gst-plugins-good
  • q66/gst-plugins-good
  • nitroxis/gst-plugins-good
  • whoozle/gst-plugins-good
  • dude/gst-plugins-good
  • mildsunrise/gst-plugins-good
  • sid.sethupathi/gst-plugins-good
  • valbok/gst-plugins-good
  • crziter/gst-plugins-good
  • AdvanceSoftware/gst-plugins-good
  • castroadrianitha/gst-plugins-good
  • Nei/gst-plugins-good
  • dwlsalmeida/gst-plugins-good
  • Roopang/gst-plugins-good
  • perfn/gst-plugins-good
  • witaly.iwanow/gst-plugins-good
  • Sidraya/gst-plugins-good
  • ybandou/gst-plugins-good
  • MylesInglis/gst-plugins-good
  • Ded_Zerom/gst-plugins-good
  • aslobodeniuk/gst-plugins-good
  • jfelder/gst-plugins-good
  • Paul_Fee_JCI/gst-plugins-good
  • ruslank/gst-plugins-good
  • brad0/gst-plugins-good
  • Hejsil/gst-plugins-good
  • vivienne/gst-plugins-good
  • pldin601/gst-plugins-good
  • danielknobe/gst-plugins-good
  • aniket_fti/gst-plugins-good
  • zhao-gang/gst-plugins-good
  • hq/gst-plugins-good
  • daniels/gst-plugins-good
  • tobire42/gst-plugins-good
  • ssdeng6812/gst-plugins-good
  • knorth55/gst-plugins-good
  • jinboson/gst-plugins-good
  • xrayzh/gst-plugins-good
  • cnhzcy14/gst-plugins-good
  • yuri.fedoseev/gst-plugins-good
  • Zhipeng/gst-plugins-good
  • vnguyentrong/gst-plugins-good
210 results
Show changes
Commits on Source (22)
Showing
with 990 additions and 129 deletions
......@@ -8868,6 +8868,18 @@
}
},
"properties": {
"audio-level-meta": {
"blurb": "Set GstAudioLevelMeta on buffers",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "false",
"mutable": "null",
"readable": true,
"type": "gboolean",
"writable": true
},
"interval": {
"blurb": "Interval of time between message posts (in nanoseconds)",
"conditionally-available": false,
......@@ -13841,7 +13853,32 @@
"presence": "always"
}
},
"properties": {},
"properties": {
"request-keyframe": {
"blurb": "Request new keyframe when packet loss is detected",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "false",
"mutable": "null",
"readable": true,
"type": "gboolean",
"writable": true
},
"wait-for-keyframe": {
"blurb": "Wait for the next keyframe after packet loss, meaningful only when outputting access units",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "false",
"mutable": "null",
"readable": true,
"type": "gboolean",
"writable": true
}
},
"rank": "secondary"
},
"rtph264pay": {
......@@ -14779,12 +14816,12 @@
"long-name": "RTP Opus packet depayloader",
"pad-templates": {
"sink": {
"caps": "application/x-rtp:\n media: audio\n payload: [ 96, 127 ]\n clock-rate: 48000\n encoding-name: { (string)OPUS, (string)X-GST-OPUS-DRAFT-SPITTKA-00 }\n",
"caps": "application/x-rtp:\n media: audio\n payload: [ 96, 127 ]\n clock-rate: 48000\n encoding-name: { (string)OPUS, (string)X-GST-OPUS-DRAFT-SPITTKA-00, (string)multiopus }\n",
"direction": "sink",
"presence": "always"
},
"src": {
"caps": "audio/x-opus:\nchannel-mapping-family: 0\n",
"caps": "audio/x-opus:\nchannel-mapping-family: [ 0, 1 ]\n",
"direction": "src",
"presence": "always"
}
......@@ -14807,12 +14844,12 @@
"long-name": "RTP Opus payloader",
"pad-templates": {
"sink": {
"caps": "audio/x-opus:\nchannel-mapping-family: 0\n",
"caps": "audio/x-opus:\nchannel-mapping-family: 0\naudio/x-opus:\nchannel-mapping-family: 0\n channels: [ 1, 2 ]\naudio/x-opus:\nchannel-mapping-family: 1\n channels: [ 3, 255 ]\n",
"direction": "sink",
"presence": "always"
},
"src": {
"caps": "application/x-rtp:\n media: audio\n payload: [ 96, 127 ]\n clock-rate: 48000\nencoding-params: 2\n encoding-name: { (string)OPUS, (string)X-GST-OPUS-DRAFT-SPITTKA-00 }\n",
"caps": "application/x-rtp:\n media: audio\n payload: [ 96, 127 ]\n clock-rate: 48000\n encoding-name: { (string)OPUS, (string)X-GST-OPUS-DRAFT-SPITTKA-00, (string)multiopus }\n",
"direction": "src",
"presence": "always"
}
......@@ -15805,6 +15842,18 @@
}
},
"properties": {
"request-keyframe": {
"blurb": "Request new keyframe when packet loss is detected",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "false",
"mutable": "null",
"readable": true,
"type": "gboolean",
"writable": true
},
"wait-for-keyframe": {
"blurb": "Wait for the next keyframe after packet loss",
"conditionally-available": false,
......@@ -17001,6 +17050,36 @@
},
"rank": "none"
},
"rtphdrextrfc6464": {
"RTP-Header-Extension-URI": "urn:ietf:params:rtp-hdrext:ssrc-audio-level",
"author": "Guillaume Desmottes <guillaume.desmottes@collabora.com>",
"description": "Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension",
"hierarchy": [
"GstRTPHeaderExtensionRfc6464",
"GstRTPHeaderExtension",
"GstElement",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"klass": "Network/Extension/RTPHeader",
"long-name": "Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension",
"properties": {
"vad": {
"blurb": "If the vad extension attribute is enabled or not",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "true",
"mutable": "null",
"readable": true,
"type": "gboolean",
"writable": false
}
},
"rank": "marginal"
},
"rtphdrexttwcc": {
"RTP-Header-Extension-URI": "http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
"author": "Matthew Waters <matthew@centricular.com>",
......@@ -25096,7 +25175,7 @@
"elements": {
"videomixer": {
"author": "Wim Taymans <wim@fluendo.com>, Sebastian Dröge <sebastian.droege@collabora.co.uk>",
"description": "Mix multiple video streams",
"description": "Deprecated by compositor. Mix multiple video streams",
"hierarchy": [
"GstVideoMixer2",
"GstElement",
/* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
*
* EffecTV:
* Copyright (C) 2001 FUKUCHI Kentarou
*
* EffecTV is free software. We release this product under the terms of the
* GNU General Public License version 2. The license is included in the file
* COPYING.
*
* This program 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 General Public License for more details.
*/
......@@ -402,6 +402,7 @@ G_BEGIN_DECLS
#define FOURCC_pssh GST_MAKE_FOURCC('p','s','s','h')
#define FOURCC_tenc GST_MAKE_FOURCC('t','e','n','c')
#define FOURCC_cenc GST_MAKE_FOURCC('c','e','n','c')
#define FOURCC_cbcs GST_MAKE_FOURCC('c','b','c','s')
/* Audible AAX encrypted audio */
#define FOURCC_aavd GST_MAKE_FOURCC('a','a','v','d')
......
......@@ -2524,7 +2524,8 @@ gst_qtdemux_stream_clear (QtDemuxStream * stream)
stream->sent_eos = FALSE;
stream->protected = FALSE;
if (stream->protection_scheme_info) {
if (stream->protection_scheme_type == FOURCC_cenc) {
if (stream->protection_scheme_type == FOURCC_cenc
|| stream->protection_scheme_type == FOURCC_cbcs) {
QtDemuxCencSampleSetInfo *info =
(QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
if (info->default_properties)
......@@ -2664,22 +2665,43 @@ qtdemux_parse_ftyp (GstQTDemux * qtdemux, const guint8 * buffer, gint length)
}
static void
qtdemux_update_default_sample_encryption_settings (GstQTDemux * qtdemux,
QtDemuxCencSampleSetInfo * info, guint32 is_encrypted, guint8 iv_size,
const guint8 * kid)
{
qtdemux_update_default_sample_cenc_settings (GstQTDemux * qtdemux,
QtDemuxCencSampleSetInfo * info, guint32 is_encrypted,
guint32 protection_scheme_type, guint8 iv_size, const guint8 * kid,
guint crypt_byte_block, guint skip_byte_block, guint8 constant_iv_size,
const guint8 * constant_iv)
{
const gchar *protection_scheme_type_mime =
protection_scheme_type ==
FOURCC_cbcs ? "application/x-cbcs" : "application/x-cenc";
GstBuffer *kid_buf = gst_buffer_new_allocate (NULL, 16, NULL);
gst_buffer_fill (kid_buf, 0, kid, 16);
if (info->default_properties)
gst_structure_free (info->default_properties);
info->default_properties =
gst_structure_new ("application/x-cenc",
gst_structure_new (protection_scheme_type_mime,
"iv_size", G_TYPE_UINT, iv_size,
"encrypted", G_TYPE_BOOLEAN, (is_encrypted == 1),
"kid", GST_TYPE_BUFFER, kid_buf, NULL);
GST_DEBUG_OBJECT (qtdemux, "default sample properties: "
"is_encrypted=%u, iv_size=%u", is_encrypted, iv_size);
gst_buffer_unref (kid_buf);
if (protection_scheme_type == FOURCC_cbcs) {
if (crypt_byte_block != 0 || skip_byte_block != 0) {
gst_structure_set (info->default_properties, "crypt_byte_block",
G_TYPE_UINT, crypt_byte_block, "skip_byte_block", G_TYPE_UINT,
skip_byte_block, NULL);
}
if (constant_iv != NULL) {
GstBuffer *constant_iv_buf =
gst_buffer_new_allocate (NULL, constant_iv_size, NULL);
gst_buffer_fill (constant_iv_buf, 0, constant_iv, constant_iv_size);
gst_structure_set (info->default_properties, "constant_iv_size",
G_TYPE_UINT, constant_iv_size, "iv", GST_TYPE_BUFFER, constant_iv_buf,
NULL);
gst_buffer_unref (constant_iv_buf);
}
}
}
static gboolean
......@@ -2711,8 +2733,8 @@ qtdemux_update_default_piff_encryption_settings (GstQTDemux * qtdemux,
if (!gst_byte_reader_get_data (br, 16, &kid))
return FALSE;
qtdemux_update_default_sample_encryption_settings (qtdemux, info,
is_encrypted, iv_size, kid);
qtdemux_update_default_sample_cenc_settings (qtdemux, info,
is_encrypted, FOURCC_cenc, iv_size, kid, 0, 0, 0, NULL);
gst_structure_set (info->default_properties, "piff_algorithm_id",
G_TYPE_UINT, algorithm_id, NULL);
return TRUE;
......@@ -3727,9 +3749,10 @@ qtdemux_gst_structure_free (GstStructure * gststructure)
}
}
/* Parses auxiliary information relating to samples protected using Common
* Encryption (cenc); the format of this information is defined in
* ISO/IEC 23001-7. Returns TRUE if successful; FALSE otherwise. */
/* Parses auxiliary information relating to samples protected using
* Common Encryption (cenc and cbcs); the format of this information
* is defined in ISO/IEC 23001-7. Returns TRUE if successful; FALSE
* otherwise. */
static gboolean
qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
GstByteReader * br, guint8 * info_sizes, guint32 sample_count)
......@@ -3787,6 +3810,7 @@ qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
guint8 *data;
guint iv_size;
GstBuffer *buf;
gboolean could_read_iv;
properties = qtdemux_get_cenc_sample_properties (qtdemux, stream, i);
if (properties == NULL) {
......@@ -3798,14 +3822,29 @@ qtdemux_parse_cenc_aux_info (GstQTDemux * qtdemux, QtDemuxStream * stream,
gst_structure_free (properties);
return FALSE;
}
if (!gst_byte_reader_dup_data (br, iv_size, &data)) {
could_read_iv =
iv_size > 0 ? gst_byte_reader_dup_data (br, iv_size, &data) : FALSE;
if (could_read_iv) {
buf = gst_buffer_new_wrapped (data, iv_size);
gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
gst_buffer_unref (buf);
} else if (stream->protection_scheme_type == FOURCC_cbcs) {
const GValue *constant_iv_size_value =
gst_structure_get_value (properties, "constant_iv_size");
const GValue *constant_iv_value =
gst_structure_get_value (properties, "iv");
if (constant_iv_size_value == NULL || constant_iv_value == NULL) {
GST_ERROR_OBJECT (qtdemux, "failed to get constant_iv");
gst_structure_free (properties);
return FALSE;
}
gst_structure_set_value (properties, "iv_size", constant_iv_size_value);
gst_structure_remove_field (properties, "constant_iv_size");
} else if (stream->protection_scheme_type == FOURCC_cenc) {
GST_ERROR_OBJECT (qtdemux, "failed to get IV for sample %u", i);
gst_structure_free (properties);
return FALSE;
}
buf = gst_buffer_new_wrapped (data, iv_size);
gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
gst_buffer_unref (buf);
size = info_sizes[i];
if (size > iv_size) {
if (!gst_byte_reader_get_uint16_be (br, &n_subsamples)
......@@ -3988,7 +4027,8 @@ qtdemux_parse_moof (GstQTDemux * qtdemux, const guint8 * buffer, guint length,
}
if (base_offset > -1 && base_offset > qtdemux->moof_offset)
offset += (guint64) (base_offset - qtdemux->moof_offset);
if (info_type == FOURCC_cenc && info_type_parameter == 0U) {
if ((info_type == FOURCC_cenc || info_type == FOURCC_cbcs)
&& info_type_parameter == 0U) {
GstByteReader br;
if (offset > length) {
GST_DEBUG_OBJECT (qtdemux, "cenc auxiliary info stored out of moof");
......@@ -5726,7 +5766,8 @@ gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
GST_ERROR_OBJECT (qtdemux, "failed to attach aavd metadata to buffer");
}
if (stream->protected && stream->protection_scheme_type == FOURCC_cenc) {
if (stream->protected && (stream->protection_scheme_type == FOURCC_cenc
|| stream->protection_scheme_type == FOURCC_cbcs)) {
GstStructure *crypto_info;
QtDemuxCencSampleSetInfo *info =
(QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
......@@ -5740,8 +5781,19 @@ gst_qtdemux_push_buffer (GstQTDemux * qtdemux, QtDemuxStream * stream,
}
if (info->crypto_info == NULL) {
GST_DEBUG_OBJECT (qtdemux,
"cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
if (stream->protection_scheme_type == FOURCC_cbcs) {
crypto_info = qtdemux_get_cenc_sample_properties (qtdemux, stream, 0);
if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info)) {
GST_ERROR_OBJECT (qtdemux,
"failed to attach cbcs metadata to buffer");
qtdemux_gst_structure_free (crypto_info);
} else {
GST_TRACE_OBJECT (qtdemux, "added cbcs protection metadata");
}
} else {
GST_DEBUG_OBJECT (qtdemux,
"cenc metadata hasn't been parsed yet, pushing buffer as if it wasn't encrypted");
}
} else {
/* The end of the crypto_info array matches our n_samples position,
* so count backward from there */
......@@ -6114,7 +6166,8 @@ gst_qtdemux_do_fragmented_seek (GstQTDemux * qtdemux)
if (stream->protection_scheme_info) {
/* Clear out any old cenc crypto info entries as we'll move to a new moof */
if (stream->protection_scheme_type == FOURCC_cenc) {
if (stream->protection_scheme_type == FOURCC_cenc
|| stream->protection_scheme_type == FOURCC_cbcs) {
QtDemuxCencSampleSetInfo *info =
(QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
if (info->crypto_info) {
......@@ -8314,7 +8367,8 @@ gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
return TRUE;
}
if (stream->protection_scheme_type != FOURCC_cenc) {
if (stream->protection_scheme_type != FOURCC_cenc
&& stream->protection_scheme_type != FOURCC_cbcs) {
GST_ERROR_OBJECT (qtdemux,
"unsupported protection scheme: %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (stream->protection_scheme_type));
......@@ -8322,10 +8376,13 @@ gst_qtdemux_configure_protected_caps (GstQTDemux * qtdemux,
}
s = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
if (!gst_structure_has_name (s, "application/x-cenc")) {
if (!gst_structure_has_name (s, "application/x-cenc")
&& !gst_structure_has_name (s, "application/x-cbcs")) {
gst_structure_set (s,
"original-media-type", G_TYPE_STRING, gst_structure_get_name (s), NULL);
gst_structure_set_name (s, "application/x-cenc");
gst_structure_set_name (s,
stream->protection_scheme_type ==
FOURCC_cbcs ? "application/x-cbcs" : "application/x-cenc");
}
if (qtdemux->protection_system_ids == NULL) {
......@@ -10361,7 +10418,8 @@ qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
sinf = qtdemux_tree_get_child_by_type (container, FOURCC_sinf);
if (G_UNLIKELY (!sinf)) {
if (stream->protection_scheme_type == FOURCC_cenc) {
if (stream->protection_scheme_type == FOURCC_cenc
|| stream->protection_scheme_type == FOURCC_cbcs) {
GST_ERROR_OBJECT (qtdemux, "sinf box does not contain schi box, which is "
"mandatory for Common Encryption");
return FALSE;
......@@ -10400,7 +10458,8 @@ qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
return FALSE;
}
if (stream->protection_scheme_type != FOURCC_cenc &&
stream->protection_scheme_type != FOURCC_piff) {
stream->protection_scheme_type != FOURCC_piff &&
stream->protection_scheme_type != FOURCC_cbcs) {
GST_ERROR_OBJECT (qtdemux,
"Invalid protection_scheme_type: %" GST_FOURCC_FORMAT,
GST_FOURCC_ARGS (stream->protection_scheme_type));
......@@ -10413,10 +10472,15 @@ qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
info = (QtDemuxCencSampleSetInfo *) stream->protection_scheme_info;
if (stream->protection_scheme_type == FOURCC_cenc) {
guint32 is_encrypted;
if (stream->protection_scheme_type == FOURCC_cenc
|| stream->protection_scheme_type == FOURCC_cbcs) {
guint8 is_encrypted;
guint8 iv_size;
guint8 constant_iv_size = 0;
const guint8 *default_kid;
guint8 crypt_byte_block = 0;
guint8 skip_byte_block = 0;
const guint8 *constant_iv = NULL;
tenc = qtdemux_tree_get_child_by_type (schi, FOURCC_tenc);
if (!tenc) {
......@@ -10425,11 +10489,27 @@ qtdemux_parse_protection_scheme_info (GstQTDemux * qtdemux,
return FALSE;
}
tenc_data = (const guint8 *) tenc->data + 12;
is_encrypted = QT_UINT24 (tenc_data);
is_encrypted = QT_UINT8 (tenc_data + 2);
iv_size = QT_UINT8 (tenc_data + 3);
default_kid = (tenc_data + 4);
qtdemux_update_default_sample_encryption_settings (qtdemux, info,
is_encrypted, iv_size, default_kid);
if (stream->protection_scheme_type == FOURCC_cbcs) {
guint8 possible_pattern_info;
if (iv_size == 0) {
constant_iv_size = QT_UINT8 (tenc_data + 20);
if (constant_iv_size != 8 && constant_iv_size != 16) {
GST_ERROR_OBJECT (qtdemux,
"constant IV size should be 8 or 16, not %hhu", constant_iv_size);
return FALSE;
}
constant_iv = (tenc_data + 21);
}
possible_pattern_info = QT_UINT8 (tenc_data + 1);
crypt_byte_block = (possible_pattern_info >> 4) & 0x0f;
skip_byte_block = possible_pattern_info & 0x0f;
}
qtdemux_update_default_sample_cenc_settings (qtdemux, info,
is_encrypted, stream->protection_scheme_type, iv_size, default_kid,
crypt_byte_block, skip_byte_block, constant_iv_size, constant_iv);
} else if (stream->protection_scheme_type == FOURCC_piff) {
GstByteReader br;
static const guint8 piff_track_encryption_uuid[] = {
......
......@@ -98,7 +98,8 @@ enum
PROP_MESSAGE,
PROP_INTERVAL,
PROP_PEAK_TTL,
PROP_PEAK_FALLOFF
PROP_PEAK_FALLOFF,
PROP_AUDIO_LEVEL_META,
};
#define gst_level_parent_class parent_class
......@@ -171,6 +172,17 @@ gst_level_class_init (GstLevelClass * klass)
g_param_spec_double ("peak-falloff", "Peak Falloff",
"Decay rate of decay peak after TTL (in dB/sec)",
0.0, G_MAXDOUBLE, 10.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstLevel:audio-level-meta:
*
* If %TRUE, generate or update GstAudioLevelMeta on output buffers.
*
* Since: 1.20
*/
g_object_class_install_property (gobject_class, PROP_AUDIO_LEVEL_META,
g_param_spec_boolean ("audio-level-meta", "Audio Level Meta",
"Set GstAudioLevelMeta on buffers", FALSE,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
GST_DEBUG_CATEGORY_INIT (level_debug, "level", 0, "Level calculation");
......@@ -262,6 +274,9 @@ gst_level_set_property (GObject * object, guint prop_id,
case PROP_PEAK_FALLOFF:
filter->decay_peak_falloff = g_value_get_double (value);
break;
case PROP_AUDIO_LEVEL_META:
filter->audio_level_meta = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -289,6 +304,9 @@ gst_level_get_property (GObject * object, guint prop_id,
case PROP_PEAK_FALLOFF:
g_value_set_double (value, filter->decay_peak_falloff);
break;
case PROP_AUDIO_LEVEL_META:
g_value_set_boolean (value, filter->audio_level_meta);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -546,6 +564,24 @@ gst_level_message_append_channel (GstMessage * m, gdouble rms, gdouble peak,
g_value_unset (&v);
}
static void
gst_level_rtp_audio_level_meta (GstLevel * self, GstBuffer * buffer,
guint8 level)
{
GstAudioLevelMeta *meta;
/* Update the existing meta, if any, so we can have an upstream element
* filling the voice activity part of the meta. */
meta = gst_buffer_get_audio_level_meta (buffer);
if (meta) {
meta->level = level;
} else {
/* Assume audio does not contain voice, it can be detected by another
* downstream element. */
gst_buffer_add_audio_level_meta (buffer, level, FALSE);
}
}
static GstFlowReturn
gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
{
......@@ -562,6 +598,7 @@ gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
* intervals */
GstClockTimeDiff falloff_time;
gint channels, rate, bps;
gdouble CS_tot = 0; /* Total Cumulative Square on all samples */
filter = GST_LEVEL (trans);
......@@ -598,6 +635,7 @@ gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
if (!GST_BUFFER_FLAG_IS_SET (in, GST_BUFFER_FLAG_GAP)) {
filter->process (in_data + (bps * i), block_int_size, channels, &CS,
&filter->peak[i]);
CS_tot += CS;
GST_LOG_OBJECT (filter,
"[%d]: cumulative squares %lf, over %d samples/%d channels",
i, CS, block_int_size, channels);
......@@ -664,6 +702,13 @@ gst_level_transform_ip (GstBaseTransform * trans, GstBuffer * in)
gst_buffer_unmap (in, &map);
if (filter->audio_level_meta) {
gdouble RMS = sqrt (CS_tot / num_int_samples);
gdouble RMSdB = 20 * log10 (RMS + EPSILON);
gst_level_rtp_audio_level_meta (filter, in, -RMSdB);
}
return GST_FLOW_OK;
}
......
......@@ -67,6 +67,7 @@ struct _GstLevel {
* since last emit */
gint interval_frames; /* after how many frame to sent a message */
GstClockTime message_ts; /* starttime for next message */
gboolean audio_level_meta; /* whether or not generate GstAudioLevelMeta */
/* per-channel arrays for intermediate values */
gdouble *CS; /* normalized Cumulative Square */
......
......@@ -38,6 +38,16 @@ GST_DEBUG_CATEGORY_STATIC (rtph264depay_debug);
* expressed a restriction or preference via caps */
#define DEFAULT_BYTE_STREAM TRUE
#define DEFAULT_ACCESS_UNIT FALSE
#define DEFAULT_WAIT_FOR_KEYFRAME FALSE
#define DEFAULT_REQUEST_KEYFRAME FALSE
enum
{
PROP_0,
PROP_WAIT_FOR_KEYFRAME,
PROP_REQUEST_KEYFRAME,
};
/* 3 zero bytes syncword */
static const guint8 sync_bytes[] = { 0, 0, 0, 1 };
......@@ -99,6 +109,44 @@ static void gst_rtp_h264_depay_push (GstRtpH264Depay * rtph264depay,
GstBuffer * outbuf, gboolean keyframe, GstClockTime timestamp,
gboolean marker);
static void
gst_rtp_h264_depay_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstRtpH264Depay *self = GST_RTP_H264_DEPAY (object);
switch (prop_id) {
case PROP_WAIT_FOR_KEYFRAME:
self->wait_for_keyframe = g_value_get_boolean (value);
break;
case PROP_REQUEST_KEYFRAME:
self->request_keyframe = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_rtp_h264_depay_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstRtpH264Depay *self = GST_RTP_H264_DEPAY (object);
switch (prop_id) {
case PROP_WAIT_FOR_KEYFRAME:
g_value_set_boolean (value, self->wait_for_keyframe);
break;
case PROP_REQUEST_KEYFRAME:
g_value_set_boolean (value, self->request_keyframe);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_rtp_h264_depay_class_init (GstRtpH264DepayClass * klass)
{
......@@ -111,6 +159,36 @@ gst_rtp_h264_depay_class_init (GstRtpH264DepayClass * klass)
gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass;
gobject_class->finalize = gst_rtp_h264_depay_finalize;
gobject_class->set_property = gst_rtp_h264_depay_set_property;
gobject_class->get_property = gst_rtp_h264_depay_get_property;
/**
* GstRtpH264Depay:wait-for-keyframe:
*
* Wait for the next keyframe after packet loss,
* meaningful only when outputting access units
*
* Since: 1.20
*/
g_object_class_install_property (gobject_class, PROP_WAIT_FOR_KEYFRAME,
g_param_spec_boolean ("wait-for-keyframe", "Wait for Keyframe",
"Wait for the next keyframe after packet loss, meaningful only when "
"outputting access units",
DEFAULT_WAIT_FOR_KEYFRAME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstRtpH264Depay:request-keyframe:
*
* Request new keyframe when packet loss is detected
*
* Since: 1.20
*/
g_object_class_install_property (gobject_class, PROP_REQUEST_KEYFRAME,
g_param_spec_boolean ("request-keyframe", "Request Keyframe",
"Request new keyframe when packet loss is detected",
DEFAULT_REQUEST_KEYFRAME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_static_pad_template (gstelement_class,
&gst_rtp_h264_depay_src_template);
......@@ -139,6 +217,8 @@ gst_rtp_h264_depay_init (GstRtpH264Depay * rtph264depay)
(GDestroyNotify) gst_buffer_unref);
rtph264depay->pps = g_ptr_array_new_with_free_func (
(GDestroyNotify) gst_buffer_unref);
rtph264depay->wait_for_keyframe = DEFAULT_WAIT_FOR_KEYFRAME;
rtph264depay->request_keyframe = DEFAULT_REQUEST_KEYFRAME;
}
static void
......@@ -146,6 +226,7 @@ gst_rtp_h264_depay_reset (GstRtpH264Depay * rtph264depay, gboolean hard)
{
gst_adapter_clear (rtph264depay->adapter);
rtph264depay->wait_start = TRUE;
rtph264depay->waiting_for_keyframe = rtph264depay->wait_for_keyframe;
gst_adapter_clear (rtph264depay->picture_adapter);
rtph264depay->picture_start = FALSE;
rtph264depay->last_keyframe = FALSE;
......@@ -952,31 +1033,31 @@ gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal,
if (rtph264depay->merge) {
gboolean start = FALSE, complete = FALSE;
/* marker bit isn't mandatory so in the following code we try to guess
* an AU boundary by detecting a new picture start */
if (!marker) {
/* consider a coded slices (IDR or not) to start a picture,
* (so ending the previous one) if first_mb_in_slice == 0
* (non-0 is part of previous one) */
/* NOTE this is not entirely according to Access Unit specs in 7.4.1.2.4,
* but in practice it works in sane cases, needs not much parsing,
* and also works with broken frame_num in NAL (where spec-wise would fail) */
/* FIXME: this code isn't correct for interlaced content as AUs should be
* constructed with pairs of fields and the guess here will just push out
* AUs with a single field in it */
if (nal_type == 1 || nal_type == 2 || nal_type == 5) {
/* we have a picture start */
start = TRUE;
if (map.data[5] & 0x80) {
/* first_mb_in_slice == 0 completes a picture */
complete = TRUE;
}
} else if (nal_type >= 6 && nal_type <= 9) {
/* SEI, SPS, PPS, AU terminate picture */
/* consider a coded slices (IDR or not) to start a picture,
* (so ending the previous one) if first_mb_in_slice == 0
* (non-0 is part of previous one) */
/* NOTE this is not entirely according to Access Unit specs in 7.4.1.2.4,
* but in practice it works in sane cases, needs not much parsing,
* and also works with broken frame_num in NAL (where spec-wise would fail) */
/* FIXME: this code isn't correct for interlaced content as AUs should be
* constructed with pairs of fields and the guess here will just push out
* AUs with a single field in it */
if (nal_type == 1 || nal_type == 2 || nal_type == 5) {
/* we have a picture start */
start = TRUE;
if (map.data[5] & 0x80) {
/* first_mb_in_slice == 0 completes a picture */
complete = TRUE;
}
GST_DEBUG_OBJECT (depayload, "start %d, complete %d", start, complete);
} else if (nal_type >= 6 && nal_type <= 9) {
/* SEI, SPS, PPS, AU terminate picture */
complete = TRUE;
}
GST_DEBUG_OBJECT (depayload, "start %d, complete %d", start, complete);
/* marker bit isn't mandatory so in the following code we try to guess
* an AU boundary by detecting a new picture start */
if (!marker) {
if (complete && rtph264depay->picture_start)
outbuf = gst_rtp_h264_complete_au (rtph264depay, &out_timestamp,
&out_keyframe);
......@@ -984,6 +1065,9 @@ gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal,
/* add to adapter */
gst_buffer_unmap (nal, &map);
if (!rtph264depay->picture_start && start && out_keyframe)
rtph264depay->waiting_for_keyframe = FALSE;
GST_DEBUG_OBJECT (depayload, "adding NAL to picture adapter");
gst_adapter_push (rtph264depay->picture_adapter, nal);
rtph264depay->last_ts = in_timestamp;
......@@ -1001,8 +1085,15 @@ gst_rtp_h264_depay_handle_nal (GstRtpH264Depay * rtph264depay, GstBuffer * nal,
}
if (outbuf) {
gst_rtp_h264_depay_push (rtph264depay, outbuf, out_keyframe, out_timestamp,
marker);
if (!rtph264depay->waiting_for_keyframe) {
gst_rtp_h264_depay_push (rtph264depay, outbuf, out_keyframe,
out_timestamp, marker);
} else {
GST_LOG_OBJECT (depayload,
"Dropping %" GST_PTR_FORMAT ", we are waiting for a keyframe",
outbuf);
gst_buffer_unref (outbuf);
}
}
return;
......@@ -1056,12 +1147,25 @@ gst_rtp_h264_depay_process (GstRTPBaseDepayload * depayload, GstRTPBuffer * rtp)
rtph264depay = GST_RTP_H264_DEPAY (depayload);
if (!rtph264depay->merge)
rtph264depay->waiting_for_keyframe = FALSE;
/* flush remaining data on discont */
if (GST_BUFFER_IS_DISCONT (rtp->buffer)) {
gst_adapter_clear (rtph264depay->adapter);
rtph264depay->wait_start = TRUE;
rtph264depay->current_fu_type = 0;
rtph264depay->last_fu_seqnum = 0;
if (rtph264depay->merge && rtph264depay->wait_for_keyframe) {
rtph264depay->waiting_for_keyframe = TRUE;
}
if (rtph264depay->request_keyframe)
gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depayload),
gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE,
TRUE, 0));
}
{
......
......@@ -71,6 +71,10 @@ struct _GstRtpH264Depay
/* downstream allocator */
GstAllocator *allocator;
GstAllocationParams params;
gboolean wait_for_keyframe;
gboolean request_keyframe;
gboolean waiting_for_keyframe;
};
struct _GstRtpH264DepayClass
......
......@@ -41,14 +41,14 @@ GST_STATIC_PAD_TEMPLATE ("sink",
"media = (string) \"audio\", "
"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ","
"clock-rate = (int) 48000, "
"encoding-name = (string) { \"OPUS\", \"X-GST-OPUS-DRAFT-SPITTKA-00\" }")
"encoding-name = (string) { \"OPUS\", \"X-GST-OPUS-DRAFT-SPITTKA-00\", \"multiopus\" }")
);
static GstStaticPadTemplate gst_rtp_opus_depay_src_template =
GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-opus, channel-mapping-family = (int) 0")
GST_STATIC_CAPS ("audio/x-opus, channel-mapping-family = (int) [ 0, 1 ]")
);
static GstBuffer *gst_rtp_opus_depay_process (GstRTPBaseDepayload * depayload,
......@@ -96,21 +96,116 @@ gst_rtp_opus_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
GstCaps *srccaps;
GstStructure *s;
gboolean ret;
const gchar *sprop_stereo, *sprop_maxcapturerate;
const gchar *sprop_maxcapturerate;
srccaps =
gst_caps_new_simple ("audio/x-opus", "channel-mapping-family", G_TYPE_INT,
0, NULL);
srccaps = gst_caps_new_empty_simple ("audio/x-opus");
s = gst_caps_get_structure (caps, 0);
if ((sprop_stereo = gst_structure_get_string (s, "sprop-stereo"))) {
if (strcmp (sprop_stereo, "0") == 0)
if (g_str_equal (gst_structure_get_string (s, "encoding-name"), "multiopus")) {
gint channels;
gint stream_count;
gint coupled_count;
const gchar *encoding_params;
const gchar *num_streams;
const gchar *coupled_streams;
const gchar *channel_mapping;
gchar *endptr;
if (!gst_structure_has_field_typed (s, "encoding-params", G_TYPE_STRING) ||
!gst_structure_has_field_typed (s, "num_streams", G_TYPE_STRING) ||
!gst_structure_has_field_typed (s, "coupled_streams", G_TYPE_STRING) ||
!gst_structure_has_field_typed (s, "channel_mapping", G_TYPE_STRING)) {
GST_WARNING_OBJECT (depayload, "Encoding name 'multiopus' requires "
"encoding-params, num_streams, coupled_streams and channel_mapping "
"as string fields in caps.");
goto reject_caps;
}
gst_caps_set_simple (srccaps, "channel-mapping-family", G_TYPE_INT, 1,
NULL);
encoding_params = gst_structure_get_string (s, "encoding-params");
channels = g_ascii_strtoull (encoding_params, &endptr, 10);
if (*endptr != '\0' || channels > 255) {
GST_WARNING_OBJECT (depayload, "Invalid encoding-params value '%s'",
encoding_params);
goto reject_caps;
}
gst_caps_set_simple (srccaps, "channels", G_TYPE_INT, channels, NULL);
num_streams = gst_structure_get_string (s, "num_streams");
stream_count = g_ascii_strtoull (num_streams, &endptr, 10);
if (*endptr != '\0' || stream_count > channels) {
GST_WARNING_OBJECT (depayload, "Invalid num_streams value '%s'",
num_streams);
goto reject_caps;
}
gst_caps_set_simple (srccaps, "stream-count", G_TYPE_INT, stream_count,
NULL);
coupled_streams = gst_structure_get_string (s, "coupled_streams");
coupled_count = g_ascii_strtoull (coupled_streams, &endptr, 10);
if (*endptr != '\0' || coupled_count > stream_count) {
GST_WARNING_OBJECT (depayload, "Invalid coupled_streams value '%s'",
coupled_streams);
goto reject_caps;
}
gst_caps_set_simple (srccaps, "coupled-count", G_TYPE_INT, coupled_count,
NULL);
channel_mapping = gst_structure_get_string (s, "channel_mapping");
{
gchar **split;
gchar **ptr;
GValue mapping = G_VALUE_INIT;
GValue v = G_VALUE_INIT;
split = g_strsplit (channel_mapping, ",", -1);
g_value_init (&mapping, GST_TYPE_ARRAY);
g_value_init (&v, G_TYPE_INT);
for (ptr = split; *ptr; ++ptr) {
gint channel = g_ascii_strtoull (*ptr, &endptr, 10);
if (*endptr != '\0' || channel > channels) {
GST_WARNING_OBJECT (depayload, "Invalid channel_mapping value '%s'",
channel_mapping);
g_value_unset (&mapping);
break;
}
g_value_set_int (&v, channel);
gst_value_array_append_value (&mapping, &v);
}
g_value_unset (&v);
g_strfreev (split);
if (G_IS_VALUE (&mapping)) {
gst_caps_set_value (srccaps, "channel-mapping", &mapping);
g_value_unset (&mapping);
} else {
goto reject_caps;
}
}
} else {
const gchar *sprop_stereo;
gst_caps_set_simple (srccaps, "channel-mapping-family", G_TYPE_INT, 0,
NULL);
if ((sprop_stereo = gst_structure_get_string (s, "sprop-stereo"))) {
if (strcmp (sprop_stereo, "0") == 0)
gst_caps_set_simple (srccaps, "channels", G_TYPE_INT, 1, NULL);
else if (strcmp (sprop_stereo, "1") == 0)
gst_caps_set_simple (srccaps, "channels", G_TYPE_INT, 2, NULL);
else
GST_WARNING_OBJECT (depayload, "Unknown sprop-stereo value '%s'",
sprop_stereo);
} else {
/* sprop-stereo defaults to mono as per RFC 7587. */
gst_caps_set_simple (srccaps, "channels", G_TYPE_INT, 1, NULL);
else if (strcmp (sprop_stereo, "1") == 0)
gst_caps_set_simple (srccaps, "channels", G_TYPE_INT, 2, NULL);
else
GST_WARNING_OBJECT (depayload, "Unknown sprop-stereo value '%s'",
sprop_stereo);
}
}
if ((sprop_maxcapturerate =
......@@ -137,6 +232,11 @@ gst_rtp_opus_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
depayload->clock_rate = 48000;
return ret;
reject_caps:
gst_caps_unref (srccaps);
return FALSE;
}
static GstBuffer *
......
......@@ -19,6 +19,29 @@
* Boston, MA 02110-1301, USA.
*/
/**
* SECTION:element-rtpopuspay
* @title: rtpopuspay
*
* rtpopuspay encapsulates Opus-encoded audio data into RTP packets following
* the payload format described in RFC 7587.
*
* In addition to the RFC, which assumes only mono and stereo payload,
* the element supports multichannel Opus audio streams using a non-standardized
* SDP config and "multiopus" codec developed by Google for libwebrtc. When the
* input data have more than 2 channels, rtpopuspay will add extra fields to
* output caps that can be used to generate SDP in the syntax understood by
* libwebrtc. For example in the case of 5.1 audio:
*
* |[
* a=rtpmap:96 multiopus/48000/6
* a=fmtp:96 num_streams=4;coupled_streams=2;channel_mapping=0,4,1,2,3,5
* ]|
*
* See https://webrtc-review.googlesource.com/c/src/+/129768 for more details on
* multichannel Opus in libwebrtc.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
......@@ -36,10 +59,12 @@ GST_DEBUG_CATEGORY_STATIC (rtpopuspay_debug);
static GstStaticPadTemplate gst_rtp_opus_pay_sink_template =
GST_STATIC_PAD_TEMPLATE ("sink",
GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-opus, channel-mapping-family = (int) 0")
GST_STATIC_CAPS ("audio/x-opus, channel-mapping-family = (int) 0;"
"audio/x-opus, channel-mapping-family = (int) 0, channels = (int) [1, 2];"
"audio/x-opus, channel-mapping-family = (int) 1, channels = (int) [3, 255]")
);
static GstStaticPadTemplate gst_rtp_opus_pay_src_template =
......@@ -50,8 +75,7 @@ GST_STATIC_PAD_TEMPLATE ("src",
"media = (string) \"audio\", "
"payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
"clock-rate = (int) 48000, "
"encoding-params = (string) \"2\", "
"encoding-name = (string) { \"OPUS\", \"X-GST-OPUS-DRAFT-SPITTKA-00\" }")
"encoding-name = (string) { \"OPUS\", \"X-GST-OPUS-DRAFT-SPITTKA-00\", \"multiopus\" }")
);
static gboolean gst_rtp_opus_pay_setcaps (GstRTPBasePayload * payload,
......@@ -101,11 +125,13 @@ gst_rtp_opus_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
{
gboolean res;
GstCaps *src_caps;
GstStructure *s;
GstStructure *s, *outcaps;
const char *encoding_name = "OPUS";
gint channels, rate;
const char *sprop_stereo = NULL;
char *sprop_maxcapturerate = NULL;
gint channels = 2;
gint rate;
gchar *encoding_params;
outcaps = gst_structure_new_empty ("unused");
src_caps = gst_pad_get_allowed_caps (GST_RTP_BASE_PAYLOAD_SRCPAD (payload));
if (src_caps) {
......@@ -130,41 +156,75 @@ gst_rtp_opus_pay_setcaps (GstRTPBasePayload * payload, GstCaps * caps)
s = gst_caps_get_structure (caps, 0);
if (gst_structure_get_int (s, "channels", &channels)) {
if (channels > 2) {
GST_ERROR_OBJECT (payload,
"More than 2 channels with channel-mapping-family=0 is invalid");
return FALSE;
} else if (channels == 2) {
sprop_stereo = "1";
/* Implies channel-mapping-family = 1. */
gint stream_count, coupled_count;
const GValue *channel_mapping_array;
/* libwebrtc only supports "multiopus" when channels > 2. Mono and stereo
* sound must always be payloaded according to RFC 7587. */
encoding_name = "multiopus";
if (gst_structure_get_int (s, "stream-count", &stream_count)) {
char *num_streams = g_strdup_printf ("%d", stream_count);
gst_structure_set (outcaps, "num_streams", G_TYPE_STRING, num_streams,
NULL);
g_free (num_streams);
}
if (gst_structure_get_int (s, "coupled-count", &coupled_count)) {
char *coupled_streams = g_strdup_printf ("%d", coupled_count);
gst_structure_set (outcaps, "coupled_streams", G_TYPE_STRING,
coupled_streams, NULL);
g_free (coupled_streams);
}
channel_mapping_array = gst_structure_get_value (s, "channel-mapping");
if (GST_VALUE_HOLDS_ARRAY (channel_mapping_array)) {
GString *str = g_string_new (NULL);
guint i;
for (i = 0; i < gst_value_array_get_size (channel_mapping_array); ++i) {
if (i != 0) {
g_string_append_c (str, ',');
}
g_string_append_printf (str, "%d",
g_value_get_int (gst_value_array_get_value (channel_mapping_array,
i)));
}
gst_structure_set (outcaps, "channel_mapping", G_TYPE_STRING, str->str,
NULL);
g_string_free (str, TRUE);
}
} else {
sprop_stereo = "0";
gst_structure_set (outcaps, "sprop-stereo", G_TYPE_STRING,
(channels == 2) ? "1" : "0", NULL);
/* RFC 7587 requires the number of channels always be 2. */
channels = 2;
}
}
encoding_params = g_strdup_printf ("%d", channels);
gst_structure_set (outcaps, "encoding-params", G_TYPE_STRING,
encoding_params, NULL);
g_free (encoding_params);
if (gst_structure_get_int (s, "rate", &rate)) {
sprop_maxcapturerate = g_strdup_printf ("%d", rate);
gchar *sprop_maxcapturerate = g_strdup_printf ("%d", rate);
gst_structure_set (outcaps, "sprop-maxcapturerate", G_TYPE_STRING,
sprop_maxcapturerate, NULL);
g_free (sprop_maxcapturerate);
}
gst_rtp_base_payload_set_options (payload, "audio", FALSE,
encoding_name, 48000);
if (sprop_maxcapturerate && sprop_stereo) {
res =
gst_rtp_base_payload_set_outcaps (payload, "sprop-maxcapturerate",
G_TYPE_STRING, sprop_maxcapturerate, "sprop-stereo", G_TYPE_STRING,
sprop_stereo, NULL);
} else if (sprop_maxcapturerate) {
res =
gst_rtp_base_payload_set_outcaps (payload, "sprop-maxcapturerate",
G_TYPE_STRING, sprop_maxcapturerate, NULL);
} else if (sprop_stereo) {
res =
gst_rtp_base_payload_set_outcaps (payload, "sprop-stereo",
G_TYPE_STRING, sprop_stereo, NULL);
} else {
res = gst_rtp_base_payload_set_outcaps (payload, NULL);
}
res = gst_rtp_base_payload_set_outcaps_structure (payload, outcaps);
g_free (sprop_maxcapturerate);
gst_structure_free (outcaps);
return res;
}
......
......@@ -64,11 +64,13 @@ GST_STATIC_PAD_TEMPLATE ("sink",
"encoding-name = (string) { \"VP8\", \"VP8-DRAFT-IETF-01\" }"));
#define DEFAULT_WAIT_FOR_KEYFRAME FALSE
#define DEFAULT_REQUEST_KEYFRAME FALSE
enum
{
PROP_0,
PROP_WAIT_FOR_KEYFRAME
PROP_WAIT_FOR_KEYFRAME,
PROP_REQUEST_KEYFRAME,
};
#define PICTURE_ID_NONE (UINT_MAX)
......@@ -80,6 +82,7 @@ gst_rtp_vp8_depay_init (GstRtpVP8Depay * self)
self->adapter = gst_adapter_new ();
self->started = FALSE;
self->wait_for_keyframe = DEFAULT_WAIT_FOR_KEYFRAME;
self->request_keyframe = DEFAULT_REQUEST_KEYFRAME;
self->last_pushed_was_lost_event = FALSE;
}
......@@ -111,6 +114,19 @@ gst_rtp_vp8_depay_class_init (GstRtpVP8DepayClass * gst_rtp_vp8_depay_class)
DEFAULT_WAIT_FOR_KEYFRAME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstRtpVP8Depay:request-keyframe:
*
* Request new keyframe when packet loss is detected
*
* Since: 1.20
*/
g_object_class_install_property (object_class, PROP_REQUEST_KEYFRAME,
g_param_spec_boolean ("request-keyframe", "Request Keyframe",
"Request new keyframe when packet loss is detected",
DEFAULT_REQUEST_KEYFRAME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
element_class->change_state = gst_rtp_vp8_depay_change_state;
depay_class->process_rtp_packet = gst_rtp_vp8_depay_process;
......@@ -146,6 +162,9 @@ gst_rtp_vp8_depay_set_property (GObject * object, guint prop_id,
case PROP_WAIT_FOR_KEYFRAME:
self->wait_for_keyframe = g_value_get_boolean (value);
break;
case PROP_REQUEST_KEYFRAME:
self->request_keyframe = g_value_get_boolean (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -162,6 +181,9 @@ gst_rtp_vp8_depay_get_property (GObject * object, guint prop_id,
case PROP_WAIT_FOR_KEYFRAME:
g_value_set_boolean (value, self->wait_for_keyframe);
break;
case PROP_REQUEST_KEYFRAME:
g_value_set_boolean (value, self->request_keyframe);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
......@@ -276,6 +298,11 @@ gst_rtp_vp8_depay_process (GstRTPBaseDepayload * depay, GstRTPBuffer * rtp)
if (self->wait_for_keyframe)
self->waiting_for_keyframe = TRUE;
if (self->request_keyframe)
gst_pad_push_event (GST_RTP_BASE_DEPAYLOAD_SINKPAD (depay),
gst_video_event_new_upstream_force_key_unit (GST_CLOCK_TIME_NONE,
TRUE, 0));
}
/* At least one header and one vp8 byte */
......
......@@ -70,6 +70,7 @@ struct _GstRtpVP8Depay
guint last_picture_id;
gboolean wait_for_keyframe;
gboolean request_keyframe;
gboolean last_pushed_was_lost_event;
};
......
/* GStreamer
* Copyright (C) <2018> Havard Graff <havard.graff@gmail.com>
* Copyright (C) <2020-2021> Guillaume Desmottes <guillaume.desmottes@collabora.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
*/
/**
* SECTION:element-rtphdrextrfc6464
* @title: rtphdrextrfc6464
* @short_description: Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension
*
* Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension.
* The extension should be automatically created by payloader and depayloaders,
* if their `auto-header-extension` property is enabled, if the extension
* is part of the RTP caps.
*
* Since: 1.20
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstrtphdrext-rfc6464.h"
#include <gst/audio/audio.h>
#define RFC6464_HDR_EXT_URI GST_RTP_HDREXT_BASE"ssrc-audio-level"
GST_DEBUG_CATEGORY_STATIC (rtphdrrfc6464_twcc_debug);
#define GST_CAT_DEFAULT (rtphdrrfc6464_twcc_debug)
#define DEFAULT_VAD TRUE
enum
{
PROP_0,
PROP_VAD,
};
struct _GstRTPHeaderExtensionRfc6464
{
GstRTPHeaderExtension parent;
gboolean vad;
};
G_DEFINE_TYPE_WITH_CODE (GstRTPHeaderExtensionRfc6464,
gst_rtp_header_extension_rfc6464, GST_TYPE_RTP_HEADER_EXTENSION,
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rtphdrextrfc6464", 0,
"RTP RFC 6464 Header Extensions");
)
static void
gst_rtp_header_extension_rfc6464_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstRTPHeaderExtensionRfc6464 *self =
GST_RTP_HEADER_EXTENSION_RFC6464 (object);
switch (prop_id) {
case PROP_VAD:
g_value_set_boolean (value, self->vad);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static GstRTPHeaderExtensionFlags
gst_rtp_header_extension_rfc6464_get_supported_flags (GstRTPHeaderExtension *
ext)
{
return GST_RTP_HEADER_EXTENSION_ONE_BYTE | GST_RTP_HEADER_EXTENSION_TWO_BYTE;
}
static gsize
gst_rtp_header_extension_rfc6464_get_max_size (GstRTPHeaderExtension * ext,
const GstBuffer * input_meta)
{
return 2;
}
static void
set_vad (GstRTPHeaderExtension * ext, gboolean vad)
{
GstRTPHeaderExtensionRfc6464 *self = GST_RTP_HEADER_EXTENSION_RFC6464 (ext);
if (self->vad == vad)
return;
GST_DEBUG_OBJECT (ext, "vad: %d", vad);
self->vad = vad;
g_object_notify (G_OBJECT (self), "vad");
}
static gboolean
gst_rtp_header_extension_rfc6464_set_attributes_from_caps (GstRTPHeaderExtension
* ext, const GstCaps * caps)
{
gchar *field_name = gst_rtp_header_extension_get_sdp_caps_field_name (ext);
GstStructure *s = gst_caps_get_structure (caps, 0);
const gchar *ext_uri;
const GValue *arr;
if (!field_name)
return FALSE;
if ((ext_uri = gst_structure_get_string (s, field_name))) {
if (g_strcmp0 (ext_uri, gst_rtp_header_extension_get_uri (ext)) != 0) {
/* incompatible extension uri for this instance */
goto error;
}
set_vad (ext, DEFAULT_VAD);
} else if ((arr = gst_structure_get_value (s, field_name))
&& GST_VALUE_HOLDS_ARRAY (arr)
&& gst_value_array_get_size (arr) == 3) {
const GValue *val;
const gchar *vad_attr;
val = gst_value_array_get_value (arr, 1);
if (!G_VALUE_HOLDS_STRING (val))
goto error;
if (g_strcmp0 (g_value_get_string (val),
gst_rtp_header_extension_get_uri (ext)) != 0)
goto error;
val = gst_value_array_get_value (arr, 2);
if (!G_VALUE_HOLDS_STRING (val))
goto error;
vad_attr = g_value_get_string (val);
if (g_str_equal (vad_attr, "vad=on"))
set_vad (ext, TRUE);
else if (g_str_equal (vad_attr, "vad=off"))
set_vad (ext, FALSE);
else {
GST_WARNING_OBJECT (ext, "Invalid attribute: %s", vad_attr);
goto error;
}
} else {
/* unknown caps format */
goto error;
}
g_free (field_name);
return TRUE;
error:
g_free (field_name);
return FALSE;
}
static gboolean
gst_rtp_header_extension_rfc6464_set_caps_from_attributes (GstRTPHeaderExtension
* ext, GstCaps * caps)
{
GstRTPHeaderExtensionRfc6464 *self = GST_RTP_HEADER_EXTENSION_RFC6464 (ext);
gchar *field_name = gst_rtp_header_extension_get_sdp_caps_field_name (ext);
GstStructure *s = gst_caps_get_structure (caps, 0);
GValue arr = G_VALUE_INIT;
GValue val = G_VALUE_INIT;
if (!field_name)
return FALSE;
g_value_init (&arr, GST_TYPE_ARRAY);
g_value_init (&val, G_TYPE_STRING);
/* direction */
g_value_set_string (&val, "");
gst_value_array_append_value (&arr, &val);
/* uri */
g_value_set_string (&val, gst_rtp_header_extension_get_uri (ext));
gst_value_array_append_value (&arr, &val);
/* attributes */
if (self->vad)
g_value_set_string (&val, "vad=on");
else
g_value_set_string (&val, "vad=off");
gst_value_array_append_value (&arr, &val);
gst_structure_set_value (s, field_name, &arr);
GST_DEBUG_OBJECT (self, "%" GST_PTR_FORMAT, caps);
g_value_unset (&val);
g_value_unset (&arr);
g_free (field_name);
return TRUE;
}
static gsize
gst_rtp_header_extension_rfc6464_write (GstRTPHeaderExtension * ext,
const GstBuffer * input_meta, GstRTPHeaderExtensionFlags write_flags,
GstBuffer * output, guint8 * data, gsize size)
{
GstAudioLevelMeta *meta;
g_return_val_if_fail (size >=
gst_rtp_header_extension_rfc6464_get_max_size (ext, NULL), -1);
g_return_val_if_fail (write_flags &
gst_rtp_header_extension_rfc6464_get_supported_flags (ext), -1);
meta = gst_buffer_get_audio_level_meta ((GstBuffer *) input_meta);
if (!meta) {
GST_LOG_OBJECT (ext, "no meta");
return 0;
}
if (meta->level > 127) {
GST_WARNING_OBJECT (ext, "level from meta is higher than 127: %d",
meta->level);
return -1;
}
GST_LOG_OBJECT (ext, "writing ext (level: %d voice: %d)", meta->level,
meta->voice_activity);
if (write_flags & GST_RTP_HEADER_EXTENSION_ONE_BYTE) {
*data = (meta->level & 0x7F) | (meta->voice_activity << 7);
return 1;
} else {
guint16 payload;
payload = ((meta->level & 0x7F) | (meta->voice_activity << 7)) << 8;
GST_WRITE_UINT16_LE (data, payload);
return 2;
}
}
static gboolean
gst_rtp_header_extension_rfc6464_read (GstRTPHeaderExtension * ext,
GstRTPHeaderExtensionFlags read_flags, const guint8 * data, gsize size,
GstBuffer * buffer)
{
guint8 val;
guint8 level;
gboolean voice_activity;
g_return_val_if_fail (read_flags &
gst_rtp_header_extension_rfc6464_get_supported_flags (ext), -1);
if (read_flags & GST_RTP_HEADER_EXTENSION_ONE_BYTE) {
val = data[0];
} else {
val = *((guint16 *) data) >> 8;
}
level = val & 0x7F;
voice_activity = (val & 0x80) >> 7;
GST_LOG_OBJECT (ext, "reading ext (level: %d voice: %d)", level,
voice_activity);
gst_buffer_add_audio_level_meta (buffer, level, voice_activity);
return TRUE;
}
static void
gst_rtp_header_extension_rfc6464_class_init (GstRTPHeaderExtensionRfc6464Class *
klass)
{
GstRTPHeaderExtensionClass *rtp_hdr_class;
GstElementClass *gstelement_class;
GObjectClass *gobject_class;
rtp_hdr_class = GST_RTP_HEADER_EXTENSION_CLASS (klass);
gobject_class = (GObjectClass *) klass;
gstelement_class = GST_ELEMENT_CLASS (klass);
gobject_class->get_property = gst_rtp_header_extension_rfc6464_get_property;
/**
* rtphdrextrfc6464:vad:
*
* If the vad extension attribute is enabled or not, default to %FALSE.
*
* Since: 1.20
*/
g_object_class_install_property (gobject_class, PROP_VAD,
g_param_spec_boolean ("vad", "vad",
"If the vad extension attribute is enabled or not",
DEFAULT_VAD, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
rtp_hdr_class->get_supported_flags =
gst_rtp_header_extension_rfc6464_get_supported_flags;
rtp_hdr_class->get_max_size = gst_rtp_header_extension_rfc6464_get_max_size;
rtp_hdr_class->set_attributes_from_caps =
gst_rtp_header_extension_rfc6464_set_attributes_from_caps;
rtp_hdr_class->set_caps_from_attributes =
gst_rtp_header_extension_rfc6464_set_caps_from_attributes;
rtp_hdr_class->write = gst_rtp_header_extension_rfc6464_write;
rtp_hdr_class->read = gst_rtp_header_extension_rfc6464_read;
gst_element_class_set_static_metadata (gstelement_class,
"Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension",
GST_RTP_HDREXT_ELEMENT_CLASS,
"Client-to-Mixer Audio Level Indication (RFC6464) RTP Header Extension",
"Guillaume Desmottes <guillaume.desmottes@collabora.com>");
gst_rtp_header_extension_class_set_uri (rtp_hdr_class, RFC6464_HDR_EXT_URI);
}
static void
gst_rtp_header_extension_rfc6464_init (GstRTPHeaderExtensionRfc6464 * self)
{
GST_DEBUG_OBJECT (self, "creating element");
self->vad = DEFAULT_VAD;
}
/* GStreamer
* Copyright (C) <2018> Havard Graff <havard.graff@gmail.com>
* Copyright (C) <2020-2021> Guillaume Desmottes <guillaume.desmottes@collabora.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
*/
#ifndef __GST_RTPHDREXT_RFC6464_H__
#define __GST_RTPHDREXT_RFC6464_H__
#include <gst/gst.h>
#include <gst/rtp/gstrtphdrext.h>
G_BEGIN_DECLS
#define GST_TYPE_RTP_HEADER_EXTENSION_RFC6464 (gst_rtp_header_extension_rfc6464_get_type())
G_DECLARE_FINAL_TYPE (GstRTPHeaderExtensionRfc6464, gst_rtp_header_extension_rfc6464, GST, RTP_HEADER_EXTENSION_RFC6464, GstRTPHeaderExtension)
G_END_DECLS
#endif /* __GST_RTPHDREXT_RFC6464_H__ */
......@@ -35,6 +35,7 @@
#include "gstrtpst2022-1-fecdec.h"
#include "gstrtpst2022-1-fecenc.h"
#include "gstrtphdrext-twcc.h"
#include "gstrtphdrext-rfc6464.h"
static gboolean
plugin_init (GstPlugin * plugin)
......@@ -89,6 +90,10 @@ plugin_init (GstPlugin * plugin)
GST_TYPE_RTP_HEADER_EXTENSION_TWCC))
return FALSE;
if (!gst_element_register (plugin, "rtphdrextrfc6464", GST_RANK_MARGINAL,
GST_TYPE_RTP_HEADER_EXTENSION_RFC6464))
return FALSE;
return TRUE;
}
......
......@@ -4,6 +4,7 @@ rtpmanager_sources = [
'gstrtpdtmfmux.c',
'gstrtpjitterbuffer.c',
'gstrtphdrext-twcc.c',
'gstrtphdrext-rfc6464.c',
'gstrtpmux.c',
'gstrtpptdemux.c',
'gstrtprtxqueue.c',
......@@ -26,7 +27,7 @@ gstrtpmanager = library('gstrtpmanager',
rtpmanager_sources,
c_args : gst_plugins_good_args,
include_directories : [configinc, libsinc],
dependencies : [gstbase_dep, gstnet_dep, gstrtp_dep, gio_dep],
dependencies : [gstbase_dep, gstnet_dep, gstrtp_dep, gstaudio_dep, gio_dep],
install : true,
install_dir : plugins_install_dir,
)
......
......@@ -22,6 +22,9 @@
* SECTION:element-videomixer
* @title: videomixer
*
* IMPORTANT: #videomixer is deprecated in favor of #compositor, please do not
* use this element in newly-written code!
*
* Videomixer can accept AYUV, ARGB and BGRA video streams. For each of the requested
* sink pads it will compare the incoming geometry and framerate to define the
* output parameters. Indeed output video frames will have the geometry of the
......@@ -2230,7 +2233,8 @@ gst_videomixer2_class_init (GstVideoMixer2Class * klass)
gst_element_class_set_static_metadata (gstelement_class, "Video mixer 2",
"Filter/Editor/Video/Compositor",
"Mix multiple video streams", "Wim Taymans <wim@fluendo.com>, "
"Deprecated by compositor. Mix multiple video streams",
"Wim Taymans <wim@fluendo.com>, "
"Sebastian Dröge <sebastian.droege@collabora.co.uk>");
/* Register the pad class */
......
......@@ -546,6 +546,7 @@ gst_wavparse_tags_foreach (const GstTagList * tags, const gchar * tag,
0, NULL}
};
gint n;
size_t size;
gchar *str = NULL;
GstByteWriter *bw = data;
for (n = 0; rifftags[n].fcc != 0; n++) {
......@@ -563,9 +564,15 @@ gst_wavparse_tags_foreach (const GstTagList * tags, const gchar * tag,
gst_tag_list_get_string (tags, tag, &str);
}
if (str) {
/* get string length including null termination */
size = strlen (str) + 1;
gst_byte_writer_put_uint32_le (bw, rifftags[n].fcc);
gst_byte_writer_put_uint32_le (bw, GST_ROUND_UP_2 (strlen (str)));
gst_byte_writer_put_string (bw, str);
gst_byte_writer_put_uint32_le (bw, GST_ROUND_UP_2 (size));
gst_byte_writer_put_data (bw, (const guint8 *) str, size);
/* add padding if needed */
if (GST_ROUND_UP_2 (size) > size) {
gst_byte_writer_put_uint8 (bw, 0);
}
g_free (str);
str = NULL;
break;
......
......@@ -1918,7 +1918,8 @@ gst_wavparse_add_src_pad (GstWavParse * wav, GstBuffer * buf)
g_assert (wav->caps != NULL);
s = gst_caps_get_structure (wav->caps, 0);
if (s && gst_structure_has_name (s, "audio/x-raw") && buf != NULL) {
if (s && gst_structure_has_name (s, "audio/x-raw") && buf != NULL
&& (GST_BUFFER_OFFSET (buf) == 0 || !GST_BUFFER_OFFSET_IS_VALID (buf))) {
GstTypeFindProbability prob;
GstCaps *tf_caps;
......@@ -2926,7 +2927,6 @@ gst_wavparse_change_state (GstElement * element, GstStateChange transition)
case GST_STATE_CHANGE_NULL_TO_READY:
break;
case GST_STATE_CHANGE_READY_TO_PAUSED:
gst_wavparse_reset (wav);
break;
case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
break;
......
......@@ -1057,8 +1057,6 @@ gst_v4l2_object_format_get_rank (const struct v4l2_fmtdesc *fmt)
rank = GREY_BASE_RANK;
break;
case V4L2_PIX_FMT_NV12: /* 12 Y/CbCr 4:2:0 */
case V4L2_PIX_FMT_NV12M: /* Same as NV12 */
case V4L2_PIX_FMT_NV12MT: /* NV12 64x32 tile */
case V4L2_PIX_FMT_NV21: /* 12 Y/CrCb 4:2:0 */
case V4L2_PIX_FMT_NV21M: /* Same as NV21 */
......@@ -1082,6 +1080,10 @@ gst_v4l2_object_format_get_rank (const struct v4l2_fmtdesc *fmt)
case V4L2_PIX_FMT_YUV420M:
rank = YUV_BASE_RANK + 7;
break;
case V4L2_PIX_FMT_NV12: /* Y/CbCr 4:2:0, 12 bits per pixel */
case V4L2_PIX_FMT_NV12M: /* Same as NV12 */
rank = YUV_BASE_RANK + 8;
break;
case V4L2_PIX_FMT_YUYV: /* YUY2, 16 bits per pixel */
rank = YUV_BASE_RANK + 10;
break;
......