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-bad
  • thiblahute/gst-plugins-bad
  • slomo/gst-plugins-bad
  • sree/gst-plugins-bad
  • seungha.yang/gst-plugins-bad
  • xclaesse/gst-plugins-bad
  • haihao/gst-plugins-bad
  • Russel/gst-plugins-bad
  • heftig/gst-plugins-bad
  • joshuadoe/gst-plugins-bad
  • bilboed/gst-plugins-bad
  • ndufresne/gst-plugins-bad
  • ystreet/gst-plugins-bad
  • hgr/gst-plugins-bad
  • nielsdg/gst-plugins-bad
  • Brad/gst-plugins-bad
  • alatiera/gst-plugins-bad
  • SiewHoon/gst-plugins-bad
  • joykim/gst-plugins-bad
  • edholland/gst-plugins-bad
  • jh-hsd/gst-plugins-bad
  • lpendresen/gst-plugins-bad
  • daniels/gst-plugins-bad
  • nirbheek/gst-plugins-bad
  • tchakabam/gst-plugins-bad
  • wangfei/gst-plugins-bad
  • mangix/gst-plugins-bad
  • gdesmott/gst-plugins-bad
  • jonakn/gst-plugins-bad
  • Yeongjin-Jeong/gst-plugins-bad
  • harshadkhedkar/gst-plugins-bad
  • vjaquez/gst-plugins-bad
  • tpm/gst-plugins-bad
  • drakkan/gst-plugins-bad
  • milloni-ct/gst-plugins-bad
  • wonchul/gst-plugins-bad
  • philn/gst-plugins-bad
  • patricia/gst-plugins-bad
  • alexashley/gst-plugins-bad
  • gkiagia/gst-plugins-bad
  • dong9/gst-plugins-bad
  • michaelgruner/gst-plugins-bad
  • ikreymer/gst-plugins-bad
  • donghyeok/gst-plugins-bad
  • ullysses.a.eoff/gst-plugins-bad
  • CarlFK/gst-plugins-bad
  • ding/gst-plugins-bad
  • meh/gst-plugins-bad
  • victortoso/gst-plugins-bad
  • valbok/gst-plugins-bad
  • joel-holdsworth/gst-plugins-bad
  • jdm/gst-plugins-bad
  • thaytan/gst-plugins-bad
  • yangcha/gst-plugins-bad
  • ThomasSchlien/gst-plugins-bad
  • boxerab/gst-plugins-bad
  • den_erpel/gst-plugins-bad
  • alyyousuf7/gst-plugins-bad
  • andrey-konovalov/gst-plugins-bad
  • mvlad/gst-plugins-bad
  • JimmyOhn/gst-plugins-bad
  • chturne/gst-plugins-bad
  • yskkmi/gst-plugins-bad
  • vivia/gst-plugins-bad
  • billylindeman/gst-plugins-bad
  • nacho.garglez/gst-plugins-bad
  • MaZderMind/gst-plugins-bad
  • david.lee/gst-plugins-bad
  • JaredHu/gst-plugins-bad
  • ulfo/gst-plugins-bad
  • fuweitax/gst-plugins-bad
  • calvaris/gst-plugins-bad
  • codinho/gst-plugins-bad
  • lord.jacold/gst-plugins-bad
  • adn770/gst-plugins-bad
  • ayaka/gst-plugins-bad
  • kevinbing.song/gst-plugins-bad
  • billconan/gst-plugins-bad
  • psreport/gst-plugins-bad
  • welaq/gst-plugins-bad
  • dank/gst-plugins-bad
  • raytiley/gst-plugins-bad
  • myungjoo/gst-plugins-bad
  • fabio-d/gst-plugins-bad
  • marcosk/gst-plugins-bad
  • nh2/gst-plugins-bad
  • creiter/gst-plugins-bad
  • mnauw/gst-plugins-bad
  • dv1/gst-plugins-bad
  • santoscadenas/gst-plugins-bad
  • dabrain34/gst-plugins-bad
  • taylor.patton/gst-plugins-bad
  • edersondisouza/gst-plugins-bad
  • GNUDimarik/gst-plugins-bad
  • Aduskett/gst-plugins-bad
  • aleksandrm8/gst-plugins-bad
  • OleksandrKvl/gst-plugins-bad
  • autintim/gst-plugins-bad
  • charles/gst-plugins-bad
  • floobleflam/gst-plugins-bad
  • lantw/gst-plugins-bad
  • bellet/gst-plugins-bad
  • marxin.liska/gst-plugins-bad
  • therimar/gst-plugins-bad
  • xhaakon/gst-plugins-bad
  • zeidbekli48/gst-plugins-bad
  • okajun/gst-plugins-bad
  • szve/gst-plugins-bad
  • dougnazar/gst-plugins-bad
  • nacho.resa/gst-plugins-bad
  • pfy/gst-plugins-bad
  • coldtom/gst-plugins-bad
  • ankurdeepjaiswal/gst-plugins-bad
  • fdavulcu/gst-plugins-bad
  • lemenkov/gst-plugins-bad
  • Pyrrhvs/gst-plugins-bad
  • wangyan42164/gst-plugins-bad
  • tezcatli/gst-plugins-bad
  • ssaito/gst-plugins-bad
  • slabajo/gst-plugins-bad
  • hq/gst-plugins-bad
  • peat-psuwit/gst-plugins-bad
  • mol/gst-plugins-bad
  • luisbg/gst-plugins-bad
  • zubzub/gst-plugins-bad
  • fabiangreffrath/gst-plugins-bad
  • davidph/gst-plugins-bad
  • pH5/gst-plugins-bad
  • jmaibaum/gst-plugins-bad
  • aleb/gst-plugins-bad
  • jcelaya/gst-plugins-bad
  • StefanBruens/gst-plugins-bad
  • abranson/gst-plugins-bad
  • cap/gst-plugins-bad
  • ihalip/gst-plugins-bad
  • vedangpatel1/gst-plugins-bad
  • ali1234/gst-plugins-bad
  • jgilje/gst-plugins-bad
  • He_Junyan/gst-plugins-bad
  • aguedes/gst-plugins-bad
  • francisv/gst-plugins-bad
  • t/gst-plugins-bad
  • hwilkes/gst-plugins-bad
  • thiagossantos/gst-plugins-bad
  • linussn/gst-plugins-bad
  • Hosang/gst-plugins-bad
  • rob_gries/gst-plugins-bad
  • o0Ignition0o/gst-plugins-bad
  • ntrrgc/gst-plugins-bad
  • neithanmo/gst-plugins-bad
  • andrew.voznytsa/gst-plugins-bad
  • sf2020/gst-plugins-bad
  • danisla/gst-plugins-bad
  • juzza_uk/gst-plugins-bad
  • fraxinas/gst-plugins-bad
  • Arhno/gst-plugins-bad
  • leio/gst-plugins-bad
  • guillerodriguez/gst-plugins-bad
  • krivoguzovVlad/gst-plugins-bad
  • ludvigr/gst-plugins-bad
  • nazar-pc/gst-plugins-bad
  • Buora/gst-plugins-bad
  • heinrich.kruger/gst-plugins-bad
  • motownread/gst-plugins-bad
  • jwestman/gst-plugins-bad
  • paulyc/gst-plugins-bad
  • balte/gst-plugins-bad
  • jurijs.satcs/gst-plugins-bad
  • rgonzalez/gst-plugins-bad
  • antonovitch/gst-plugins-bad
  • jan.vermaete/gst-plugins-bad
  • XuGuangxin/gst-plugins-bad
  • 123vivekr/gst-plugins-bad
  • decembersoul/gst-plugins-bad
  • mparisdiaz/gst-plugins-bad
  • worldofpeace/gst-plugins-bad
  • achris/gst-plugins-bad
  • nanonyme/gst-plugins-bad
  • arun/gst-plugins-bad
  • DuBistKomisch/gst-plugins-bad
  • lblasc/gst-plugins-bad
  • ahoenig/gst-plugins-bad
  • Mats/gst-plugins-bad
  • Andruxin52rus/gst-plugins-bad
  • cketti/gst-plugins-bad
  • MM_Star/gst-plugins-bad
  • Chikushu/gst-plugins-bad
  • daniel.qtec/gst-plugins-bad
  • 4kevinking/gst-plugins-bad
  • fritzprix/gst-plugins-bad
  • dhobsong/gst-plugins-bad
  • rambden/gst-plugins-bad
  • razvanphp/gst-plugins-bad
  • foreverneilyoung/gst-plugins-bad
  • wtaymans/gst-plugins-bad
  • ezequielgarcia/gst-plugins-bad
  • trollkarlen/gst-plugins-bad
  • jedevc/gst-plugins-bad
  • jusizela/gst-plugins-bad
  • karim.davoodi/gst-plugins-bad
  • quaresma.jose/gst-plugins-bad
  • felixonmars/gst-plugins-bad
  • louisharris/gst-plugins-bad
  • lmurillo/gst-plugins-bad
  • trilene/gst-plugins-bad
  • jlaheurte/gst-plugins-bad
  • jmatthew/gst-plugins-bad
  • wilkinsw/gst-plugins-bad
  • yychao/gst-plugins-bad
  • eckhart.koppen/gst-plugins-bad
  • AdamW/gst-plugins-bad
  • SanchayanMaity/gst-plugins-bad
  • twischer/gst-plugins-bad
  • marian/gst-plugins-bad
  • linkmauve/gst-plugins-bad
  • gvanmeter/gst-plugins-bad
  • julian/gst-plugins-bad
  • raghu447/gst-plugins-bad
  • TobiasR/gst-plugins-bad
  • MoizAhmedd/gst-plugins-bad
  • gstreamer-release-bot/gst-plugins-bad
  • artectrex/gst-plugins-bad
  • Rafostar/gst-plugins-bad
  • mindriot88/gst-plugins-bad
  • mexxik/gst-plugins-bad
  • robin.carlisle/gst-plugins-bad
  • MarijnS95/gst-plugins-bad
  • igor.v.kovalenko/gst-plugins-bad
  • chriswiggins/gst-plugins-bad
  • johast/gst-plugins-bad
  • fauxsoup1/gst-plugins-bad
  • ekwange/gst-plugins-bad
  • raju.babannavar/gst-plugins-bad
  • eater/gst-plugins-bad
  • ds/gst-plugins-bad
  • avantgardnerio/gst-plugins-bad
  • marsupial/gst-plugins-bad
  • ignacy.ruksza/gst-plugins-bad
  • t-8ch/gst-plugins-bad
  • trey.hutcheson/gst-plugins-bad
  • ssanders1449/gst-plugins-bad
  • stazio/gst-plugins-bad
  • obbardc/gst-plugins-bad
  • dwlsalmeida/gst-plugins-bad
  • benjamin.gaignard1/gst-plugins-bad
  • AntoninRousset/gst-plugins-bad
  • AdhBash816/gst-plugins-bad
  • jjanku/gst-plugins-bad
  • liuyinhangx/gst-plugins-bad
  • AdvanceSoftware/gst-plugins-bad
  • jn/gst-plugins-bad
  • wantlamy/gst-plugins-bad
  • hjanuschka/gst-plugins-bad
  • Nei/gst-plugins-bad
  • arojas/gst-plugins-bad
  • zhao-gang/gst-plugins-bad
  • sid.sethupathi/gst-plugins-bad
  • mkba/gst-plugins-bad
  • YakoYakoYokuYoku/gst-plugins-bad
  • CartoonFan/gst-plugins-bad
  • rsiv/gst-plugins-bad
  • martinetd/gst-plugins-bad
  • reynaldo/gst-plugins-bad
  • kay0u1/gst-plugins-bad
  • thongthai/gst-plugins-bad
  • spartazhc/gst-plugins-bad
  • leeys888/gst-plugins-bad
  • ferruck/gst-plugins-bad
  • benjamin545/gst-plugins-bad
  • budziq/gst-plugins-bad
  • madsbuvi/gst-plugins-bad
  • agx/gst-plugins-bad
  • amotzte/gst-plugins-bad
  • devarsht/gst-plugins-bad
  • kimtinh/gst-plugins-bad
  • lshuying/gst-plugins-bad
  • rsnk96/gst-plugins-bad
  • mortimergoro/gst-plugins-bad
  • cfoch/gst-plugins-bad
  • jfelder/gst-plugins-bad
  • kathirvel621/gst-plugins-bad
  • bradh/gst-plugins-bad
  • middelschultele/gst-plugins-bad
  • pocock/gst-plugins-bad
  • marex/gst-plugins-bad
  • vivienne/gst-plugins-bad
  • pldin601/gst-plugins-bad
  • heirecka/gst-plugins-bad
  • Casey-Bateman/gst-plugins-bad
  • adrianf0/gst-plugins-bad
  • ovchinnikov.dmitrii/gst-plugins-bad
  • DimStar77/gst-plugins-bad
  • intelfx/gst-plugins-bad
  • derschueddi/gst-plugins-bad
  • driss.el.bouhali/gst-plugins-bad
  • jinsl00000/gst-plugins-bad
  • Wenlin/gst-plugins-bad
  • carra8674/gst-plugins-bad
  • ssdeng6812/gst-plugins-bad
  • glepag1/gst-plugins-bad
  • cnhzcy14/gst-plugins-bad
  • HuQian/gst-plugins-bad
  • Zhipeng/gst-plugins-bad
  • Harikrishnan/gst-plugins-bad
  • benjaminGraef/gst-plugins-bad
  • vnguyentrong/gst-plugins-bad
306 results
Show changes
Commits on Source (52)
Showing
with 1413 additions and 37 deletions
......@@ -8089,7 +8089,7 @@
"construct": true,
"construct-only": false,
"controllable": false,
"default": "one-sub-device-full (0)",
"default": "default (0)",
"mutable": "null",
"readable": true,
"type": "GstDecklinkProfileId",
......@@ -8262,7 +8262,7 @@
"construct": true,
"construct-only": false,
"controllable": false,
"default": "one-sub-device-full (0)",
"default": "default (0)",
"mutable": "null",
"readable": true,
"type": "GstDecklinkProfileId",
......@@ -8673,30 +8673,35 @@
"GstDecklinkProfileId": {
"kind": "enum",
"values": [
{
"desc": "Default, don't change profile",
"name": "default",
"value": "0"
},
{
"desc": "One sub-device, Full-Duplex",
"name": "one-sub-device-full",
"value": "0"
"value": "1"
},
{
"desc": "One sub-device, Half-Duplex",
"name": "one-sub-device-half",
"value": "1"
"value": "2"
},
{
"desc": "Two sub-devices, Full-Duplex",
"name": "two-sub-devices-full",
"value": "2"
"value": "3"
},
{
"desc": "Two sub-devices, Half-Duplex",
"name": "two-sub-devices-half",
"value": "3"
"value": "4"
},
{
"desc": "Four sub-devices, Half-Duplex",
"name": "four-sub-devices-half",
"value": "4"
"value": "5"
}
]
},
......@@ -220446,6 +220451,11 @@
"direction": "sink",
"presence": "always"
},
"sink_%%u": {
"caps": "ANY",
"direction": "sink",
"presence": "request"
},
"src": {
"caps": "ANY",
"direction": "src",
......@@ -221756,7 +221766,7 @@
"long-name": "JPEG 2000 parser",
"pad-templates": {
"sink": {
"caps": "image/jp2:\nimage/x-jpc:\nimage/x-j2c:\n",
"caps": "image/jp2:\nimage/x-jpc:\n alignment: { (string)frame, (string)stripe }\nimage/x-j2c:\n alignment: { (string)frame, (string)stripe }\n",
"direction": "sink",
"presence": "always"
},
......@@ -619,9 +619,8 @@ gst_dtls_connection_process (GstDtlsConnection * self, gpointer data, gsize len,
g_mutex_lock (&priv->mutex);
GST_TRACE_OBJECT (self, "locked @ process");
g_warn_if_fail (!priv->bio_buffer);
if (self->priv->received_close_notify) {
if (self->priv->received_close_notify
|| self->priv->connection_state == GST_DTLS_CONNECTION_STATE_CLOSED) {
GST_DEBUG_OBJECT (self, "Already received close_notify");
g_mutex_unlock (&priv->mutex);
return GST_FLOW_EOS;
......@@ -637,6 +636,8 @@ gst_dtls_connection_process (GstDtlsConnection * self, gpointer data, gsize len,
return GST_FLOW_ERROR;
}
g_warn_if_fail (!priv->bio_buffer);
priv->bio_buffer = data;
priv->bio_buffer_len = len;
priv->bio_buffer_offset = 0;
......
/* iSAC plugin
*
* Copyright (C) 2020 Collabora Ltd.
* Author: Guillaume Desmottes <guillaume.desmottes@collabora.com>, Collabora Ltd.
*
* 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 Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*/
/**
* plugin-isac:
*
* Since: 1.20
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <gst/gst.h>
#include "gstisacenc.h"
#include "gstisacdec.h"
static gboolean
plugin_init (GstPlugin * plugin)
{
if (!gst_element_register (plugin, "isacenc", GST_RANK_PRIMARY,
GST_TYPE_ISACENC))
return FALSE;
if (!gst_element_register (plugin, "isacdec", GST_RANK_PRIMARY,
GST_TYPE_ISACDEC))
return FALSE;
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
isac,
"iSAC plugin", plugin_init, VERSION, "LGPL", PACKAGE_NAME,
GST_PACKAGE_ORIGIN)
/* iSAC decoder
*
* Copyright (C) 2020 Collabora Ltd.
* Author: Guillaume Desmottes <guillaume.desmottes@collabora.com>, Collabora Ltd.
*
* 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 Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*/
/**
* SECTION:element-isacdec
* @title: isacdec
* @short_description: iSAC audio decoder
*
* Since: 1.20
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstisacdec.h"
#include "gstisacutils.h"
#include <modules/audio_coding/codecs/isac/main/include/isac.h>
GST_DEBUG_CATEGORY_STATIC (isacdec_debug);
#define GST_CAT_DEFAULT isacdec_debug
#define SAMPLE_SIZE 2 /* 16-bits samples */
#define MAX_OUTPUT_SAMPLES 960 /* decoder produces max 960 samples */
#define MAX_OUTPUT_SIZE (SAMPLE_SIZE * MAX_OUTPUT_SAMPLES)
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/isac, "
"rate = (int) { 16000, 32000 }, " "channels = (int) 1")
);
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw, "
"format = (string) " GST_AUDIO_NE (S16) ", "
"rate = (int) { 16000, 32000 }, "
"layout = (string) interleaved, " "channels = (int) 1")
);
struct _GstIsacDec
{
/*< private > */
GstAudioDecoder parent;
ISACStruct *isac;
/* properties */
};
#define gst_isacdec_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstIsacDec, gst_isacdec,
GST_TYPE_AUDIO_DECODER,
GST_DEBUG_CATEGORY_INIT (isacdec_debug, "isacdec", 0,
"debug category for isacdec element"));
static gboolean
gst_isacdec_start (GstAudioDecoder * dec)
{
GstIsacDec *self = GST_ISACDEC (dec);
gint16 ret;
g_assert (!self->isac);
ret = WebRtcIsac_Create (&self->isac);
CHECK_ISAC_RET (ret, Create);
return TRUE;
}
static gboolean
gst_isacdec_stop (GstAudioDecoder * dec)
{
GstIsacDec *self = GST_ISACDEC (dec);
if (self->isac) {
gint16 ret;
ret = WebRtcIsac_Free (self->isac);
CHECK_ISAC_RET (ret, Free);
self->isac = NULL;
}
return TRUE;
}
static gboolean
gst_isacdec_set_format (GstAudioDecoder * dec, GstCaps * input_caps)
{
GstIsacDec *self = GST_ISACDEC (dec);
GstAudioInfo output_format;
gint16 ret;
gboolean result;
GstStructure *s;
gint rate, channels;
GstCaps *output_caps;
GST_DEBUG_OBJECT (self, "input caps: %" GST_PTR_FORMAT, input_caps);
s = gst_caps_get_structure (input_caps, 0);
if (!s)
return FALSE;
if (!gst_structure_get_int (s, "rate", &rate)) {
GST_ERROR_OBJECT (self, "'rate' missing in input caps: %" GST_PTR_FORMAT,
input_caps);
return FALSE;
}
if (!gst_structure_get_int (s, "channels", &channels)) {
GST_ERROR_OBJECT (self,
"'channels' missing in input caps: %" GST_PTR_FORMAT, input_caps);
return FALSE;
}
gst_audio_info_set_format (&output_format, GST_AUDIO_FORMAT_S16LE, rate,
channels, NULL);
output_caps = gst_audio_info_to_caps (&output_format);
GST_DEBUG_OBJECT (self, "output caps: %" GST_PTR_FORMAT, output_caps);
gst_caps_unref (output_caps);
ret = WebRtcIsac_SetDecSampRate (self->isac, rate);
CHECK_ISAC_RET (ret, SetDecSampleRate);
WebRtcIsac_DecoderInit (self->isac);
result = gst_audio_decoder_set_output_format (dec, &output_format);
gst_audio_decoder_set_plc_aware (dec, TRUE);
return result;
}
static GstFlowReturn
gst_isacdec_plc (GstIsacDec * self, GstClockTime duration)
{
GstAudioDecoder *dec = GST_AUDIO_DECODER (self);
guint nb_plc_frames;
GstBuffer *output;
GstMapInfo map_write;
size_t ret;
/* Decoder produces 30 ms PLC frames */
nb_plc_frames = duration / (30 * GST_MSECOND);
GST_DEBUG_OBJECT (self,
"GAP of %" GST_TIME_FORMAT " detected, request PLC for %d frames",
GST_TIME_ARGS (duration), nb_plc_frames);
output =
gst_audio_decoder_allocate_output_buffer (dec,
nb_plc_frames * MAX_OUTPUT_SIZE);
if (!gst_buffer_map (output, &map_write, GST_MAP_WRITE)) {
GST_ERROR_OBJECT (self, "Failed to map output buffer");
gst_buffer_unref (output);
return GST_FLOW_ERROR;
}
ret =
WebRtcIsac_DecodePlc (self->isac, (gint16 *) map_write.data,
nb_plc_frames);
gst_buffer_unmap (output, &map_write);
if (ret < 0) {
/* error */
gint16 code = WebRtcIsac_GetErrorCode (self->isac);
GST_WARNING_OBJECT (self, "Failed to produce PLC: %s (%d)",
isac_error_code_to_str (code), code);
gst_buffer_unref (output);
return GST_FLOW_ERROR;
} else if (ret == 0) {
GST_DEBUG_OBJECT (self, "Decoder didn't produce any PLC frame");
gst_buffer_unref (output);
return GST_FLOW_OK;
}
gst_buffer_set_size (output, ret * SAMPLE_SIZE);
GST_LOG_OBJECT (self, "Produced %" G_GSIZE_FORMAT " PLC samples", ret);
return gst_audio_decoder_finish_frame (dec, output, 1);
}
static GstFlowReturn
gst_isacdec_handle_frame (GstAudioDecoder * dec, GstBuffer * input)
{
GstIsacDec *self = GST_ISACDEC (dec);
GstMapInfo map_read, map_write;
GstBuffer *output;
gint16 ret, speech_type[1];
gsize input_size;
/* Can't drain the decoder */
if (!input)
return GST_FLOW_OK;
if (!gst_buffer_get_size (input)) {
/* Base class detected a gap in the stream, try to do PLC */
return gst_isacdec_plc (self, GST_BUFFER_DURATION (input));
}
if (!gst_buffer_map (input, &map_read, GST_MAP_READ)) {
GST_ELEMENT_ERROR (self, RESOURCE, READ, ("Failed to map input buffer"),
(NULL));
return GST_FLOW_ERROR;
}
input_size = map_read.size;
output = gst_audio_decoder_allocate_output_buffer (dec, MAX_OUTPUT_SIZE);
if (!gst_buffer_map (output, &map_write, GST_MAP_WRITE)) {
GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ("Failed to map output buffer"),
(NULL));
gst_buffer_unref (output);
gst_buffer_unmap (input, &map_read);
return GST_FLOW_ERROR;
}
ret = WebRtcIsac_Decode (self->isac, map_read.data, map_read.size,
(gint16 *) map_write.data, speech_type);
gst_buffer_unmap (input, &map_read);
gst_buffer_unmap (output, &map_write);
if (ret < 0) {
/* error */
gint16 code = WebRtcIsac_GetErrorCode (self->isac);
GST_WARNING_OBJECT (self, "Failed to decode: %s (%d)",
isac_error_code_to_str (code), code);
gst_buffer_unref (output);
/* Give a chance to decode next frames */
return GST_FLOW_OK;
} else if (ret == 0) {
GST_DEBUG_OBJECT (self, "Decoder didn't produce any frame");
gst_buffer_unref (output);
output = NULL;
} else {
gst_buffer_set_size (output, ret * SAMPLE_SIZE);
}
GST_LOG_OBJECT (self, "Decoded %d samples from %" G_GSIZE_FORMAT " bytes",
ret, input_size);
return gst_audio_decoder_finish_frame (dec, output, 1);
}
static void
gst_isacdec_class_init (GstIsacDecClass * klass)
{
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GstAudioDecoderClass *base_class = GST_AUDIO_DECODER_CLASS (klass);
base_class->start = GST_DEBUG_FUNCPTR (gst_isacdec_start);
base_class->stop = GST_DEBUG_FUNCPTR (gst_isacdec_stop);
base_class->set_format = GST_DEBUG_FUNCPTR (gst_isacdec_set_format);
base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_isacdec_handle_frame);
gst_element_class_set_static_metadata (gstelement_class, "iSAC decoder",
"Codec/Decoder/Audio",
"iSAC audio decoder",
"Guillaume Desmottes <guillaume.desmottes@collabora.com>");
gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
gst_element_class_add_static_pad_template (gstelement_class, &src_template);
}
static void
gst_isacdec_init (GstIsacDec * self)
{
self->isac = NULL;
}
/* iSAC decoder
*
* Copyright (C) 2020 Collabora Ltd.
* Author: Guillaume Desmottes <guillaume.desmottes@collabora.com>, Collabora Ltd.
*
* 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 Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*/
#ifndef __GST_ISAC_DEC_H__
#define __GST_ISAC_DEC_H__
#include <gst/audio/audio.h>
G_BEGIN_DECLS
#define GST_TYPE_ISACDEC gst_isacdec_get_type ()
G_DECLARE_FINAL_TYPE(GstIsacDec, gst_isacdec, GST, ISACDEC, GstAudioDecoder)
G_END_DECLS
#endif /* __GST_ISAC_DEC_H__ */
/* iSAC encoder
*
* Copyright (C) 2020 Collabora Ltd.
* Author: Guillaume Desmottes <guillaume.desmottes@collabora.com>, Collabora Ltd.
*
* 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 Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*/
/**
* SECTION:element-isacenc
* @title: isacenc
* @short_description: iSAC audio encoder
*
* Since: 1.20
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "gstisacenc.h"
#include "gstisacutils.h"
#include <modules/audio_coding/codecs/isac/main/include/isac.h>
GST_DEBUG_CATEGORY_STATIC (isacenc_debug);
#define GST_CAT_DEFAULT isacenc_debug
/* Buffer size used in the simpleKenny.c test app from webrtc */
#define OUTPUT_BUFFER_SIZE 1200
#define GST_TYPE_ISACENC_OUTPUT_FRAME_LEN (gst_isacenc_output_frame_len_get_type ())
static GType
gst_isacenc_output_frame_len_get_type (void)
{
static GType qtype = 0;
if (qtype == 0) {
static const GEnumValue values[] = {
{30, "30 ms", "30 ms"},
{60, "60 ms", "60 ms, only usable in wideband mode (16 kHz)"},
{0, NULL, NULL}
};
qtype = g_enum_register_static ("GstIsacEncOutputFrameLen", values);
}
return qtype;
}
enum
{
PROP_0,
PROP_OUTPUT_FRAME_LEN,
PROP_BITRATE,
PROP_MAX_PAYLOAD_SIZE,
PROP_MAX_RATE,
};
#define GST_ISACENC_OUTPUT_FRAME_LEN_DEFAULT (30)
#define GST_ISACENC_BITRATE_DEFAULT (32000)
#define GST_ISACENC_MAX_PAYLOAD_SIZE_DEFAULT (-1)
#define GST_ISACENC_MAX_RATE_DEFAULT (-1)
static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-raw, "
"format = (string) " GST_AUDIO_NE (S16) ", "
"rate = (int) { 16000, 32000 }, "
"layout = (string) interleaved, " "channels = (int) 1")
);
static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/isac, "
"rate = (int) { 16000, 32000 }, " "channels = (int) 1")
);
typedef enum
{
ENCODER_MODE_WIDEBAND, /* 16 kHz */
ENCODER_MODE_SUPER_WIDEBAND, /* 32 kHz */
} EncoderMode;
struct _GstIsacEnc
{
/*< private > */
GstAudioEncoder parent;
ISACStruct *isac;
EncoderMode mode;
gint samples_per_frame; /* number of samples in one input frame */
gsize frame_size; /* size, in bytes, of one input frame */
guint nb_processed_input_frames; /* number of input frames processed by the encoder since the last produced encoded data */
/* properties */
gint output_frame_len;
gint bitrate;
gint max_payload_size;
gint max_rate;
};
#define gst_isacenc_parent_class parent_class
G_DEFINE_TYPE_WITH_CODE (GstIsacEnc, gst_isacenc,
GST_TYPE_AUDIO_ENCODER,
GST_DEBUG_CATEGORY_INIT (isacenc_debug, "isacenc", 0,
"debug category for isacenc element"));
static gboolean
gst_isacenc_start (GstAudioEncoder * enc)
{
GstIsacEnc *self = GST_ISACENC (enc);
gint16 ret;
g_assert (!self->isac);
ret = WebRtcIsac_Create (&self->isac);
CHECK_ISAC_RET (ret, Create);
self->nb_processed_input_frames = 0;
return TRUE;
}
static gboolean
gst_isacenc_stop (GstAudioEncoder * enc)
{
GstIsacEnc *self = GST_ISACENC (enc);
if (self->isac) {
gint16 ret;
ret = WebRtcIsac_Free (self->isac);
CHECK_ISAC_RET (ret, Free);
self->isac = NULL;
}
return TRUE;
}
static gboolean
gst_isacenc_set_format (GstAudioEncoder * enc, GstAudioInfo * info)
{
GstIsacEnc *self = GST_ISACENC (enc);
GstCaps *input_caps, *output_caps;
gint16 ret;
gboolean result;
switch (GST_AUDIO_INFO_RATE (info)) {
case 16000:
self->mode = ENCODER_MODE_WIDEBAND;
break;
case 32000:
self->mode = ENCODER_MODE_SUPER_WIDEBAND;
break;
default:
g_assert_not_reached ();
return FALSE;
}
input_caps = gst_audio_info_to_caps (info);
output_caps = gst_caps_new_simple ("audio/isac",
"channels", G_TYPE_INT, GST_AUDIO_INFO_CHANNELS (info),
"rate", G_TYPE_INT, GST_AUDIO_INFO_RATE (info), NULL);
GST_DEBUG_OBJECT (self, "input caps: %" GST_PTR_FORMAT, input_caps);
GST_DEBUG_OBJECT (self, "output caps: %" GST_PTR_FORMAT, output_caps);
ret = WebRtcIsac_SetEncSampRate (self->isac, GST_AUDIO_INFO_RATE (info));
CHECK_ISAC_RET (ret, SetEncSampleRate);
/* TODO: add support for automatically adjusted bit rate and frame
* length (codingMode = 0). */
ret = WebRtcIsac_EncoderInit (self->isac, 1);
CHECK_ISAC_RET (ret, EncoderInit);
if (self->mode == ENCODER_MODE_SUPER_WIDEBAND && self->output_frame_len != 30) {
GST_ERROR_OBJECT (self,
"Only output-frame-len=30 is supported in super-wideband mode (32 kHz)");
return FALSE;
}
if (self->mode == ENCODER_MODE_WIDEBAND && (self->bitrate < 10000
|| self->bitrate > 32000)) {
GST_ERROR_OBJECT (self,
"bitrate range is 10000 to 32000 bps in wideband mode (16 kHz)");
return FALSE;
} else if (self->mode == ENCODER_MODE_SUPER_WIDEBAND && (self->bitrate < 10000
|| self->bitrate > 56000)) {
GST_ERROR_OBJECT (self,
"bitrate range is 10000 to 56000 bps in super-wideband mode (32 kHz)");
return FALSE;
}
ret = WebRtcIsac_Control (self->isac, self->bitrate, self->output_frame_len);
CHECK_ISAC_RET (ret, Control);
if (self->max_payload_size != GST_ISACENC_MAX_PAYLOAD_SIZE_DEFAULT) {
GST_DEBUG_OBJECT (self, "set max payload size to %d bytes",
self->max_payload_size);
ret = WebRtcIsac_SetMaxPayloadSize (self->isac, self->max_payload_size);
CHECK_ISAC_RET (ret, SetMaxPayloadSize);
}
if (self->max_rate != GST_ISACENC_MAX_RATE_DEFAULT) {
GST_DEBUG_OBJECT (self, "set max rate to %d bits/sec", self->max_rate);
ret = WebRtcIsac_SetMaxRate (self->isac, self->max_rate);
CHECK_ISAC_RET (ret, SetMaxRate);
}
result = gst_audio_encoder_set_output_format (enc, output_caps);
/* input size is 10ms */
self->samples_per_frame = GST_AUDIO_INFO_RATE (info) / 100;
self->frame_size = self->samples_per_frame * GST_AUDIO_INFO_BPS (info);
GST_DEBUG_OBJECT (self, "input frame: %d samples, %" G_GSIZE_FORMAT " bytes",
self->samples_per_frame, self->frame_size);
gst_audio_encoder_set_frame_samples_min (enc, self->samples_per_frame);
gst_audio_encoder_set_frame_samples_max (enc, self->samples_per_frame);
gst_audio_encoder_set_hard_min (enc, TRUE);
gst_caps_unref (input_caps);
gst_caps_unref (output_caps);
return result;
}
static GstFlowReturn
gst_isacenc_handle_frame (GstAudioEncoder * enc, GstBuffer * input)
{
GstIsacEnc *self = GST_ISACENC (enc);
GstMapInfo map_read;
gint16 ret;
GstFlowReturn flow_ret = GST_FLOW_ERROR;
gsize offset = 0;
/* Can't drain the encoder */
if (!input)
return GST_FLOW_OK;
if (!gst_buffer_map (input, &map_read, GST_MAP_READ)) {
GST_ELEMENT_ERROR (self, RESOURCE, READ, ("Failed to map input buffer"),
(NULL));
return GST_FLOW_ERROR;
}
GST_LOG_OBJECT (self, "Received %" G_GSIZE_FORMAT " bytes", map_read.size);
while (offset + self->frame_size <= map_read.size) {
GstBuffer *output;
GstMapInfo map_write;
output = gst_audio_encoder_allocate_output_buffer (enc, OUTPUT_BUFFER_SIZE);
if (!gst_buffer_map (output, &map_write, GST_MAP_WRITE)) {
GST_ELEMENT_ERROR (self, RESOURCE, WRITE, ("Failed to map output buffer"),
(NULL));
gst_buffer_unref (output);
goto out;
}
ret =
WebRtcIsac_Encode (self->isac,
(const gint16 *) (map_read.data + offset), map_write.data);
gst_buffer_unmap (output, &map_write);
self->nb_processed_input_frames++;
offset += self->frame_size;
if (ret == 0) {
/* buffering */
gst_buffer_unref (output);
continue;
} else if (ret < 0) {
/* error */
gint16 code = WebRtcIsac_GetErrorCode (self->isac);
GST_ELEMENT_ERROR (self, LIBRARY, ENCODE, ("Failed to encode frame"),
("Failed to encode: %s (%d)", isac_error_code_to_str (code), code));
gst_buffer_unref (output);
goto out;
} else {
/* encoded */
GST_LOG_OBJECT (self, "Encoded %d input frames to %d bytes",
self->nb_processed_input_frames, ret);
gst_buffer_set_size (output, ret);
flow_ret =
gst_audio_encoder_finish_frame (enc, output,
self->nb_processed_input_frames * self->samples_per_frame);
if (flow_ret != GST_FLOW_OK)
goto out;
self->nb_processed_input_frames = 0;
}
}
flow_ret = GST_FLOW_OK;
out:
gst_buffer_unmap (input, &map_read);
return flow_ret;
}
static void
gst_isacenc_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
GstIsacEnc *self = GST_ISACENC (object);
switch (prop_id) {
case PROP_OUTPUT_FRAME_LEN:
self->output_frame_len = g_value_get_enum (value);
break;
case PROP_BITRATE:
self->bitrate = g_value_get_int (value);
break;
case PROP_MAX_PAYLOAD_SIZE:
self->max_payload_size = g_value_get_int (value);
break;
case PROP_MAX_RATE:
self->max_rate = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_isacenc_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
{
GstIsacEnc *self = GST_ISACENC (object);
switch (prop_id) {
case PROP_OUTPUT_FRAME_LEN:
g_value_set_enum (value, self->output_frame_len);
break;
case PROP_BITRATE:
g_value_set_int (value, self->bitrate);
break;
case PROP_MAX_PAYLOAD_SIZE:
g_value_set_int (value, self->max_payload_size);
break;
case PROP_MAX_RATE:
g_value_set_int (value, self->max_rate);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_isacenc_class_init (GstIsacEncClass * klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
GstAudioEncoderClass *base_class = GST_AUDIO_ENCODER_CLASS (klass);
gobject_class->set_property = gst_isacenc_set_property;
gobject_class->get_property = gst_isacenc_get_property;
base_class->start = GST_DEBUG_FUNCPTR (gst_isacenc_start);
base_class->stop = GST_DEBUG_FUNCPTR (gst_isacenc_stop);
base_class->set_format = GST_DEBUG_FUNCPTR (gst_isacenc_set_format);
base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_isacenc_handle_frame);
g_object_class_install_property (gobject_class, PROP_OUTPUT_FRAME_LEN,
g_param_spec_enum ("output-frame-len", "Output Frame Length",
"Length, in ms, of output frames",
GST_TYPE_ISACENC_OUTPUT_FRAME_LEN,
GST_ISACENC_OUTPUT_FRAME_LEN_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
GST_PARAM_MUTABLE_READY));
g_object_class_install_property (gobject_class, PROP_BITRATE,
g_param_spec_int ("bitrate", "Bitrate",
"Average Bitrate (ABR) in bits/sec",
10000, 56000,
GST_ISACENC_BITRATE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
GST_PARAM_MUTABLE_READY));
g_object_class_install_property (gobject_class, PROP_MAX_PAYLOAD_SIZE,
g_param_spec_int ("max-payload-size", "Max Payload Size",
"Maximum payload size, in bytes. Range is 120 to 400 at 16 kHz "
"and 120 to 600 at 32 kHz (-1 = encoder default)",
-1, 600,
GST_ISACENC_MAX_PAYLOAD_SIZE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
GST_PARAM_MUTABLE_READY));
g_object_class_install_property (gobject_class, PROP_MAX_RATE,
g_param_spec_int ("max-rate", "Max Rate",
"Maximum rate, in bits/sec, which the codec may not exceed for any "
"signal packet. Range is 32000 to 53400 at 16 kHz "
"and 32000 to 160000 at 32 kHz (-1 = encoder default)",
-1, 160000,
GST_ISACENC_MAX_PAYLOAD_SIZE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
GST_PARAM_MUTABLE_READY));
gst_element_class_set_static_metadata (gstelement_class, "iSAC encoder",
"Codec/Encoder/Audio",
"iSAC audio encoder",
"Guillaume Desmottes <guillaume.desmottes@collabora.com>");
gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
gst_element_class_add_static_pad_template (gstelement_class, &src_template);
}
static void
gst_isacenc_init (GstIsacEnc * self)
{
self->output_frame_len = GST_ISACENC_OUTPUT_FRAME_LEN_DEFAULT;
self->bitrate = GST_ISACENC_BITRATE_DEFAULT;
self->max_payload_size = GST_ISACENC_MAX_PAYLOAD_SIZE_DEFAULT;
self->max_rate = GST_ISACENC_MAX_RATE_DEFAULT;
}
/* iSAC encoder
*
* Copyright (C) 2020 Collabora Ltd.
* Author: Guillaume Desmottes <guillaume.desmottes@collabora.com>, Collabora Ltd.
*
* 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 Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*/
#ifndef __GST_ISAC_ENC_H__
#define __GST_ISAC_ENC_H__
#include <gst/audio/audio.h>
G_BEGIN_DECLS
#define GST_TYPE_ISACENC gst_isacenc_get_type ()
G_DECLARE_FINAL_TYPE(GstIsacEnc, gst_isacenc, GST, ISACENC, GstAudioEncoder)
G_END_DECLS
#endif /* __GST_ISAC_ENC_H__ */
/* iSAC plugin utils
*
* Copyright (C) 2020 Collabora Ltd.
* Author: Guillaume Desmottes <guillaume.desmottes@collabora.com>, Collabora Ltd.
*
* 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 Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*/
#include "gstisacutils.h"
#include <modules/audio_coding/codecs/isac/main/source/settings.h>
const gchar *
isac_error_code_to_str (gint code)
{
switch (code) {
case ISAC_MEMORY_ALLOCATION_FAILED:
return "allocation failed";
case ISAC_MODE_MISMATCH:
return "mode mismatch";
case ISAC_DISALLOWED_BOTTLENECK:
return "disallowed bottleneck";
case ISAC_DISALLOWED_FRAME_LENGTH:
return "disallowed frame length";
case ISAC_UNSUPPORTED_SAMPLING_FREQUENCY:
return "unsupported sampling frequency";
case ISAC_RANGE_ERROR_BW_ESTIMATOR:
return "range error bandwitch estimator";
case ISAC_ENCODER_NOT_INITIATED:
return "encoder not initiated";
case ISAC_DISALLOWED_CODING_MODE:
return "disallowed coding mode";
case ISAC_DISALLOWED_FRAME_MODE_ENCODER:
return "disallowed frame mode encoder";
case ISAC_DISALLOWED_BITSTREAM_LENGTH:
return "disallowed bitstream length";
case ISAC_PAYLOAD_LARGER_THAN_LIMIT:
return "payload larger than limit";
case ISAC_DISALLOWED_ENCODER_BANDWIDTH:
return "disallowed encoder bandwith";
case ISAC_DECODER_NOT_INITIATED:
return "decoder not initiated";
case ISAC_EMPTY_PACKET:
return "empty packet";
case ISAC_DISALLOWED_FRAME_MODE_DECODER:
return "disallowed frame mode decoder";
case ISAC_RANGE_ERROR_DECODE_FRAME_LENGTH:
return "range error decode frame length";
case ISAC_RANGE_ERROR_DECODE_BANDWIDTH:
return "range error decode bandwith";
case ISAC_RANGE_ERROR_DECODE_PITCH_GAIN:
return "range error decode pitch gain";
case ISAC_RANGE_ERROR_DECODE_PITCH_LAG:
return "range error decode pitch lag";
case ISAC_RANGE_ERROR_DECODE_LPC:
return "range error decode lpc";
case ISAC_RANGE_ERROR_DECODE_SPECTRUM:
return "range error decode spectrum";
case ISAC_LENGTH_MISMATCH:
return "length mismatch";
case ISAC_RANGE_ERROR_DECODE_BANDWITH:
return "range error decode bandwith";
case ISAC_DISALLOWED_BANDWIDTH_MODE_DECODER:
return "disallowed bandwitch mode decoder";
case ISAC_DISALLOWED_LPC_MODEL:
return "disallowed lpc model";
case ISAC_INCOMPATIBLE_FORMATS:
return "incompatible formats";
}
return "<unknown>";
}
/* iSAC plugin utils
*
* Copyright (C) 2020 Collabora Ltd.
* Author: Guillaume Desmottes <guillaume.desmottes@collabora.com>, Collabora Ltd.
*
* 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 Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*/
#ifndef __GST_ISAC_UTILS_H__
#define __GST_ISAC_UTILS_H__
#include <glib.h>
G_BEGIN_DECLS
const gchar * isac_error_code_to_str (gint code);
#define CHECK_ISAC_RET(ret, function) \
if (ret == -1) {\
gint16 code = WebRtcIsac_GetErrorCode (self->isac);\
GST_WARNING_OBJECT (self, "WebRtcIsac_"#function " call failed: %s (%d)", isac_error_code_to_str (code), code);\
return FALSE;\
}
G_END_DECLS
#endif /* __GST_ISAC_UTILS_H__ */
webrtc_audio_coding_dep = dependency('webrtc-audio-coding-1', required: get_option('isac'))
if webrtc_audio_coding_dep.found()
isac_sources = [
'gstisac.c',
'gstisacenc.c',
'gstisacdec.c',
'gstisacutils.c',
]
gstisac = library('gstisac', isac_sources,
c_args : gst_plugins_bad_args,
include_directories : [configinc],
dependencies : [gstaudio_dep, webrtc_audio_coding_dep],
install : true,
install_dir : plugins_install_dir,
)
pkgconfig.generate(gstisac, install_dir : plugins_pkgconfig_install_dir)
plugins += [gstisac]
endif
......@@ -21,6 +21,7 @@ subdir('gme')
subdir('gsm')
subdir('hls')
subdir('iqa')
subdir('isac')
subdir('kate')
subdir('ladspa')
subdir('libde265')
......
......@@ -66,7 +66,7 @@ if opencv_found
endif
endforeach
else
opencv_dep = dependency('opencv4', version : ['>= 4.0.0', '< 4.5.0'], required : false)
opencv_dep = dependency('opencv4', version : ['>= 4.0.0', '< 4.6.0'], required : false)
opencv_found = opencv_dep.found()
if opencv_found
foreach h : libopencv4_headers
......
......@@ -299,8 +299,7 @@ gst_sctp_dec_change_state (GstElement * element, GstStateChange transition)
ret = GST_STATE_CHANGE_FAILURE;
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
sctpdec_cleanup (self);
gst_flow_combiner_reset (self->flow_combiner);
stop_all_srcpad_tasks (self);
break;
default:
break;
......@@ -309,6 +308,15 @@ gst_sctp_dec_change_state (GstElement * element, GstStateChange transition)
if (ret != GST_STATE_CHANGE_FAILURE)
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
sctpdec_cleanup (self);
gst_flow_combiner_reset (self->flow_combiner);
break;
default:
break;
}
return ret;
}
......@@ -424,7 +432,7 @@ gst_sctp_data_srcpad_loop (GstPad * pad)
GST_OBJECT_UNLOCK (self);
if (G_UNLIKELY (flow_ret == GST_FLOW_FLUSHING
|| flow_ret == GST_FLOW_NOT_LINKED)) {
|| flow_ret == GST_FLOW_NOT_LINKED) || flow_ret == GST_FLOW_EOS) {
GST_DEBUG_OBJECT (pad, "Push failed on packet source pad. Error: %s",
gst_flow_get_name (flow_ret));
} else if (G_UNLIKELY (flow_ret != GST_FLOW_OK)) {
......@@ -705,7 +713,6 @@ sctpdec_cleanup (GstSctpDec * self)
NULL, NULL);
g_signal_handler_disconnect (self->sctp_association,
self->signal_handler_stream_reset);
stop_all_srcpad_tasks (self);
gst_sctp_association_force_close (self->sctp_association);
g_object_unref (self->sctp_association);
self->sctp_association = NULL;
......
......@@ -346,7 +346,7 @@ gst_sctp_enc_change_state (GstElement * element, GstStateChange transition)
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
sctpenc_cleanup (self);
stop_srcpad_task (self->src_pad, self);
self->src_ret = GST_FLOW_FLUSHING;
break;
case GST_STATE_CHANGE_READY_TO_NULL:
......@@ -368,6 +368,7 @@ gst_sctp_enc_change_state (GstElement * element, GstStateChange transition)
case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
break;
case GST_STATE_CHANGE_PAUSED_TO_READY:
sctpenc_cleanup (self);
break;
case GST_STATE_CHANGE_READY_TO_NULL:
break;
......
......@@ -1926,6 +1926,7 @@ ttml_find_child (xmlNodePtr parent, const gchar * name)
return child;
}
#define XML_START_TAG "<?xml"
#define TTML_END_TAG "</tt>"
guint
......@@ -1941,7 +1942,8 @@ ttml_parse (const gchar * input, GstClockTime begin, GstClockTime duration,
guint cellres_x, cellres_y;
TtmlWhitespaceMode doc_whitespace_mode = TTML_WHITESPACE_MODE_DEFAULT;
guint consumed = 0;
gchar *end_tt;
guint start_offset = 0;
gchar *start_xml, *end_tt;
g_return_val_if_fail (parsed != NULL, 0);
......@@ -1952,14 +1954,16 @@ ttml_parse (const gchar * input, GstClockTime begin, GstClockTime duration,
}
GST_CAT_LOG (ttmlparse_debug, "Input:\n%s", input);
end_tt = g_strrstr (input, TTML_END_TAG);
start_xml = g_strstr_len (input, strlen (input), XML_START_TAG);
end_tt = g_strstr_len (input, strlen (input), TTML_END_TAG);
if (!end_tt) {
if (!start_xml || !end_tt) {
GST_CAT_DEBUG (ttmlparse_debug, "Need more data");
return 0;
}
consumed = end_tt - input + strlen (TTML_END_TAG);
start_offset = start_xml - input;
styles_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
(GDestroyNotify) ttml_delete_element);
......@@ -1967,7 +1971,8 @@ ttml_parse (const gchar * input, GstClockTime begin, GstClockTime duration,
(GDestroyNotify) ttml_delete_element);
/* Parse input. */
doc = xmlReadMemory (input, consumed, "any_doc_name", NULL, 0);
doc = xmlReadMemory (start_xml, consumed - start_offset, "any_doc_name",
NULL, 0);
if (!doc) {
GST_CAT_ERROR (ttmlparse_debug, "Failed to parse document.");
return 0;
......
......@@ -637,6 +637,7 @@ frame_redraw_callback (void *data, struct wl_callback *callback, uint32_t time)
g_mutex_lock (&sink->render_lock);
sink->redraw_pending = FALSE;
sink->window->callback = NULL;
g_mutex_unlock (&sink->render_lock);
wl_callback_destroy (callback);
......@@ -660,6 +661,7 @@ render_last_buffer (GstWaylandSink * sink, gboolean redraw)
sink->redraw_pending = TRUE;
callback = wl_surface_frame (surface);
sink->window->callback = callback;
wl_callback_add_listener (callback, &frame_callback_listener, sink);
if (G_UNLIKELY (sink->video_info_changed && !redraw)) {
......
......@@ -157,6 +157,9 @@ gst_wl_window_finalize (GObject * gobject)
{
GstWlWindow *self = GST_WL_WINDOW (gobject);
if (self->callback)
wl_callback_destroy (self->callback);
if (self->wl_shell_surface)
wl_shell_surface_destroy (self->wl_shell_surface);
......@@ -197,6 +200,7 @@ gst_wl_window_new_internal (GstWlDisplay * display, GMutex * render_lock)
window->render_lock = render_lock;
g_cond_init (&window->configure_cond);
window->callback = NULL;
window->area_surface = wl_compositor_create_surface (display->compositor);
window->video_surface = wl_compositor_create_surface (display->compositor);
......
......@@ -55,6 +55,7 @@ struct _GstWlWindow
struct wl_shell_surface *wl_shell_surface;
struct xdg_surface *xdg_surface;
struct xdg_toplevel *xdg_toplevel;
struct wl_callback *callback;
gboolean configured;
GCond configure_cond;
GMutex configure_mutex;
......
......@@ -31,6 +31,8 @@
#include "webrtcdatachannel.h"
#include "sctptransport.h"
#include <gst/rtp/rtp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
......@@ -199,17 +201,6 @@ _have_dtls_elements (GstWebRTCBin * webrtc)
G_DEFINE_TYPE (GstWebRTCBinPad, gst_webrtc_bin_pad, GST_TYPE_GHOST_PAD);
static void
gst_webrtc_bin_pad_set_property (GObject * object, guint prop_id,
const GValue * value, GParamSpec * pspec)
{
switch (prop_id) {
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_webrtc_bin_pad_get_property (GObject * object, guint prop_id,
GValue * value, GParamSpec * pspec)
......@@ -248,7 +239,6 @@ gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
GObjectClass *gobject_class = (GObjectClass *) klass;
gobject_class->get_property = gst_webrtc_bin_pad_get_property;
gobject_class->set_property = gst_webrtc_bin_pad_set_property;
gobject_class->finalize = gst_webrtc_bin_pad_finalize;
g_object_class_install_property (gobject_class,
......@@ -259,6 +249,21 @@ gst_webrtc_bin_pad_class_init (GstWebRTCBinPadClass * klass)
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
}
static void
gst_webrtc_bin_pad_update_ssrc_event (GstWebRTCBinPad * wpad)
{
if (wpad->received_caps) {
WebRTCTransceiver *trans = (WebRTCTransceiver *) wpad->trans;
GstPad *pad = GST_PAD (wpad);
trans->ssrc_event =
gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_STICKY,
gst_structure_new ("GstWebRtcBinUpdateTos", "ssrc", G_TYPE_UINT,
trans->current_ssrc, NULL));
gst_pad_send_event (pad, gst_event_ref (trans->ssrc_event));
}
}
static gboolean
gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
{
......@@ -277,6 +282,15 @@ gst_webrtcbin_sink_event (GstPad * pad, GstObject * parent, GstEvent * event)
GST_DEBUG_OBJECT (parent,
"On %" GST_PTR_FORMAT " checking negotiation? %u, caps %"
GST_PTR_FORMAT, pad, check_negotiation, caps);
if (check_negotiation) {
WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (wpad->trans);
const GstStructure *s;
s = gst_caps_get_structure (caps, 0);
gst_structure_get_uint (s, "ssrc", &trans->current_ssrc);
gst_webrtc_bin_pad_update_ssrc_event (wpad);
}
} else if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
check_negotiation = TRUE;
}
......@@ -699,9 +713,12 @@ _gst_pc_thread (GstWebRTCBin * webrtc)
* tasks */
g_main_loop_run (webrtc->priv->loop);
PC_LOCK (webrtc);
GST_OBJECT_LOCK (webrtc);
g_main_context_unref (webrtc->priv->main_context);
webrtc->priv->main_context = NULL;
GST_OBJECT_UNLOCK (webrtc);
PC_LOCK (webrtc);
g_main_loop_unref (webrtc->priv->loop);
webrtc->priv->loop = NULL;
PC_COND_BROADCAST (webrtc);
......@@ -730,8 +747,11 @@ _start_thread (GstWebRTCBin * webrtc)
static void
_stop_thread (GstWebRTCBin * webrtc)
{
PC_LOCK (webrtc);
GST_OBJECT_LOCK (webrtc);
webrtc->priv->is_closed = TRUE;
GST_OBJECT_UNLOCK (webrtc);
PC_LOCK (webrtc);
g_main_loop_quit (webrtc->priv->loop);
while (webrtc->priv->loop)
PC_COND_WAIT (webrtc);
......@@ -790,16 +810,22 @@ gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
gpointer data, GDestroyNotify notify, GstPromise * promise)
{
GstWebRTCBinTask *op;
GMainContext *ctx;
GSource *source;
g_return_val_if_fail (GST_IS_WEBRTC_BIN (webrtc), FALSE);
GST_OBJECT_LOCK (webrtc);
if (webrtc->priv->is_closed) {
GST_OBJECT_UNLOCK (webrtc);
GST_DEBUG_OBJECT (webrtc, "Peerconnection is closed, aborting execution");
if (notify)
notify (data);
return FALSE;
}
ctx = g_main_context_ref (webrtc->priv->main_context);
GST_OBJECT_UNLOCK (webrtc);
op = g_new0 (GstWebRTCBinTask, 1);
op->webrtc = webrtc;
op->op = func;
......@@ -812,8 +838,9 @@ gst_webrtc_bin_enqueue_task (GstWebRTCBin * webrtc, GstWebRTCBinFunc func,
g_source_set_priority (source, G_PRIORITY_DEFAULT);
g_source_set_callback (source, (GSourceFunc) _execute_op, op,
(GDestroyNotify) _free_op);
g_source_attach (source, webrtc->priv->main_context);
g_source_attach (source, ctx);
g_source_unref (source);
g_main_context_unref (ctx);
return TRUE;
}
......@@ -1639,6 +1666,242 @@ _on_dtls_transport_notify_state (GstWebRTCDTLSTransport * transport,
_update_peer_connection_state (webrtc);
}
static gboolean
match_ssrc (GstWebRTCRTPTransceiver * rtp_trans, gconstpointer data)
{
WebRTCTransceiver *trans = (WebRTCTransceiver *) rtp_trans;
return (trans->current_ssrc == GPOINTER_TO_UINT (data));
}
static gboolean
_on_sending_rtcp (GObject * internal_session, GstBuffer * buffer,
gboolean early, gpointer user_data)
{
GstWebRTCBin *webrtc = user_data;
GstRTCPBuffer rtcp = GST_RTCP_BUFFER_INIT;
GstRTCPPacket packet;
if (!gst_rtcp_buffer_map (buffer, GST_MAP_READ, &rtcp))
goto done;
if (gst_rtcp_buffer_get_first_packet (&rtcp, &packet)) {
if (gst_rtcp_packet_get_type (&packet) == GST_RTCP_TYPE_SR) {
guint32 ssrc;
GstWebRTCRTPTransceiver *rtp_trans;
WebRTCTransceiver *trans;
gst_rtcp_packet_sr_get_sender_info (&packet, &ssrc, NULL, NULL, NULL,
NULL);
rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
match_ssrc);
trans = (WebRTCTransceiver *) rtp_trans;
if (rtp_trans && rtp_trans->sender && trans->ssrc_event) {
GstPad *pad;
gchar *pad_name = NULL;
pad_name =
g_strdup_printf ("send_rtcp_src_%u",
rtp_trans->sender->rtcp_transport->session_id);
pad = gst_element_get_static_pad (webrtc->rtpbin, pad_name);
g_free (pad_name);
if (pad) {
gst_pad_push_event (pad, gst_event_ref (trans->ssrc_event));
gst_object_unref (pad);
}
}
}
}
gst_rtcp_buffer_unmap (&rtcp);
done:
/* False means we don't care about suppression */
return FALSE;
}
static void
gst_webrtc_bin_attach_tos_to_session (GstWebRTCBin * webrtc, guint session_id)
{
GObject *internal_session = NULL;
g_signal_emit_by_name (webrtc->rtpbin, "get-internal-session",
session_id, &internal_session);
if (internal_session) {
g_signal_connect (internal_session, "on-sending-rtcp",
G_CALLBACK (_on_sending_rtcp), webrtc);
g_object_unref (internal_session);
}
}
static GstPadProbeReturn
_nicesink_pad_probe (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
{
GstWebRTCBin *webrtc = user_data;
if (GST_EVENT_TYPE (GST_PAD_PROBE_INFO_EVENT (info))
== GST_EVENT_CUSTOM_DOWNSTREAM_STICKY) {
const GstStructure *s =
gst_event_get_structure (GST_PAD_PROBE_INFO_EVENT (info));
if (gst_structure_has_name (s, "GstWebRtcBinUpdateTos")) {
guint ssrc;
gint priority;
if (gst_structure_get_uint (s, "ssrc", &ssrc)) {
GstWebRTCRTPTransceiver *rtp_trans;
rtp_trans = _find_transceiver (webrtc, GUINT_TO_POINTER (ssrc),
match_ssrc);
if (rtp_trans) {
WebRTCTransceiver *trans = WEBRTC_TRANSCEIVER (rtp_trans);
GstWebRTCICEStream *stream = _find_ice_stream_for_session (webrtc,
trans->stream->session_id);
guint8 dscp = 0;
/* Set DSCP field based on
* https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
*/
switch (rtp_trans->sender->priority) {
case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
dscp = 8; /* CS1 */
break;
case GST_WEBRTC_PRIORITY_TYPE_LOW:
dscp = 0; /* DF */
break;
case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
switch (rtp_trans->kind) {
case GST_WEBRTC_KIND_AUDIO:
dscp = 46; /* EF */
break;
case GST_WEBRTC_KIND_VIDEO:
dscp = 38; /* AF43 *//* TODO: differentiate non-interactive */
break;
case GST_WEBRTC_KIND_UNKNOWN:
dscp = 0;
break;
}
break;
case GST_WEBRTC_PRIORITY_TYPE_HIGH:
switch (rtp_trans->kind) {
case GST_WEBRTC_KIND_AUDIO:
dscp = 46; /* EF */
break;
case GST_WEBRTC_KIND_VIDEO:
dscp = 36; /* AF42 *//* TODO: differentiate non-interactive */
break;
case GST_WEBRTC_KIND_UNKNOWN:
dscp = 0;
break;
}
break;
}
gst_webrtc_ice_set_tos (webrtc->priv->ice, stream, dscp << 2);
}
} else if (gst_structure_get_enum (s, "sctp-priority",
GST_TYPE_WEBRTC_PRIORITY_TYPE, &priority)) {
guint8 dscp = 0;
/* Set DSCP field based on
* https://tools.ietf.org/html/draft-ietf-tsvwg-rtcweb-qos-18#section-5
*/
switch (priority) {
case GST_WEBRTC_PRIORITY_TYPE_VERY_LOW:
dscp = 8; /* CS1 */
break;
case GST_WEBRTC_PRIORITY_TYPE_LOW:
dscp = 0; /* DF */
break;
case GST_WEBRTC_PRIORITY_TYPE_MEDIUM:
dscp = 10; /* AF11 */
break;
case GST_WEBRTC_PRIORITY_TYPE_HIGH:
dscp = 18; /* AF21 */
break;
}
if (webrtc->priv->data_channel_transport)
gst_webrtc_ice_set_tos (webrtc->priv->ice,
webrtc->priv->data_channel_transport->stream, dscp << 2);
}
}
}
return GST_PAD_PROBE_OK;
}
static void gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc);
static void
gst_webrtc_bin_update_sctp_priority (GstWebRTCBin * webrtc)
{
GstWebRTCPriorityType sctp_priority = 0;
guint i;
if (!webrtc->priv->sctp_transport)
return;
for (i = 0; i < webrtc->priv->data_channels->len; i++) {
GstWebRTCDataChannel *channel
= g_ptr_array_index (webrtc->priv->data_channels, i);
sctp_priority = MAX (sctp_priority, channel->priority);
}
/* Default priority is low means DSCP field is left as 0 */
if (sctp_priority == 0)
sctp_priority = GST_WEBRTC_PRIORITY_TYPE_LOW;
/* Nobody asks for DSCP, leave it as-is */
if (sctp_priority == GST_WEBRTC_PRIORITY_TYPE_LOW &&
!webrtc->priv->tos_attached)
return;
/* If one stream has a non-default priority, then everyone else does too */
gst_webrtc_bin_attach_tos (webrtc);
gst_webrtc_sctp_transport_set_priority (webrtc->priv->sctp_transport,
sctp_priority);
}
static void
gst_webrtc_bin_attach_probe_to_ice_sink (GstWebRTCBin * webrtc,
GstWebRTCICETransport * transport)
{
GstPad *pad;
pad = gst_element_get_static_pad (transport->sink, "sink");
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
_nicesink_pad_probe, g_object_ref (webrtc),
(GDestroyNotify) gst_object_unref);
gst_object_unref (pad);
}
static void
gst_webrtc_bin_attach_tos (GstWebRTCBin * webrtc)
{
guint i;
if (webrtc->priv->tos_attached)
return;
webrtc->priv->tos_attached = TRUE;
for (i = 0; i < webrtc->priv->transports->len; i++) {
TransportStream *stream = g_ptr_array_index (webrtc->priv->transports, i);
gst_webrtc_bin_attach_tos_to_session (webrtc, stream->session_id);
gst_webrtc_bin_attach_probe_to_ice_sink (webrtc,
stream->transport->transport);
gst_webrtc_bin_attach_probe_to_ice_sink (webrtc,
stream->rtcp_transport->transport);
}
gst_webrtc_bin_update_sctp_priority (webrtc);
}
static WebRTCTransceiver *
_create_webrtc_transceiver (GstWebRTCBin * webrtc,
GstWebRTCRTPTransceiverDirection direction, guint mline)
......@@ -1657,6 +1920,9 @@ _create_webrtc_transceiver (GstWebRTCBin * webrtc,
/* FIXME: We don't support stopping transceiver yet so they're always not stopped */
rtp_trans->stopped = FALSE;
g_signal_connect_object (sender, "notify::priority",
G_CALLBACK (gst_webrtc_bin_attach_tos), webrtc, G_CONNECT_SWAPPED);
g_ptr_array_add (webrtc->priv->transceivers, trans);
gst_object_unref (sender);
......@@ -1685,6 +1951,8 @@ _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
g_signal_connect (G_OBJECT (transport), "notify::state",
G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
if (webrtc->priv->tos_attached)
gst_webrtc_bin_attach_probe_to_ice_sink (webrtc, transport->transport);
if ((transport = ret->rtcp_transport)) {
g_signal_connect (G_OBJECT (transport->transport),
......@@ -1694,6 +1962,8 @@ _create_transport_channel (GstWebRTCBin * webrtc, guint session_id)
G_CALLBACK (_on_ice_transport_notify_gathering_state), webrtc);
g_signal_connect (G_OBJECT (transport), "notify::state",
G_CALLBACK (_on_dtls_transport_notify_state), webrtc);
if (webrtc->priv->tos_attached)
gst_webrtc_bin_attach_probe_to_ice_sink (webrtc, transport->transport);
}
GST_TRACE_OBJECT (webrtc,
......@@ -1726,6 +1996,8 @@ _get_or_create_rtp_transport_channel (GstWebRTCBin * webrtc, guint session_id)
if (!gst_element_link_pads (GST_ELEMENT (webrtc->rtpbin), pad_name,
GST_ELEMENT (ret->send_bin), "rtcp_sink"))
g_warn_if_reached ();
if (webrtc->priv->tos_attached)
gst_webrtc_bin_attach_tos_to_session (webrtc, ret->session_id);
g_free (pad_name);
}
......@@ -1765,6 +2037,8 @@ _on_data_channel_ready_state (WebRTCDataChannel * channel,
g_ptr_array_add (webrtc->priv->data_channels, channel);
gst_webrtc_bin_update_sctp_priority (webrtc);
g_signal_emit (webrtc, gst_webrtc_bin_signals[ON_DATA_CHANNEL_SIGNAL], 0,
gst_object_ref (channel));
}
......@@ -2015,6 +2289,7 @@ _get_or_create_data_channel_transports (GstWebRTCBin * webrtc, guint session_id)
}
webrtc->priv->sctp_transport = sctp_transport;
gst_webrtc_bin_update_sctp_priority (webrtc);
}
return webrtc->priv->data_channel_transport;
......@@ -2870,6 +3145,47 @@ _media_add_rtx (GstSDPMedia * media, WebRTCTransceiver * trans,
}
}
static GstWebRTCKind
_kind_from_caps (const GstCaps * caps)
{
GstStructure *s;
const gchar *media;
if (gst_caps_get_size (caps) == 0)
return GST_WEBRTC_KIND_UNKNOWN;
s = gst_caps_get_structure (caps, 0);
media = gst_structure_get_string (s, "media");
if (media == NULL)
return GST_WEBRTC_KIND_UNKNOWN;
if (!g_strcmp0 (media, "audio"))
return GST_WEBRTC_KIND_AUDIO;
if (!g_strcmp0 (media, "video"))
return GST_WEBRTC_KIND_VIDEO;
return GST_WEBRTC_KIND_UNKNOWN;
}
static gboolean
_update_transceiver_kind_from_caps (GstWebRTCRTPTransceiver * trans,
const GstCaps * caps)
{
GstWebRTCKind kind = _kind_from_caps (caps);
if (trans->kind == kind)
return TRUE;
if (trans->kind == GST_WEBRTC_KIND_UNKNOWN) {
trans->kind = kind;
return TRUE;
} else {
return FALSE;
}
}
static void
_get_rtx_target_pt_and_ssrc_from_caps (GstCaps * answer_caps, gint * target_pt,
guint * target_ssrc)
......@@ -3180,6 +3496,10 @@ _create_answer_task (GstWebRTCBin * webrtc, const GstStructure * options,
} else {
trans = WEBRTC_TRANSCEIVER (rtp_trans);
}
if (!_update_transceiver_kind_from_caps (rtp_trans, answer_caps))
GST_WARNING_OBJECT (webrtc,
"Trying to change transceiver %d kind from %d to %d",
rtp_trans->mline, rtp_trans->kind, _kind_from_caps (answer_caps));
if (!trans->do_nack) {
answer_caps = gst_caps_make_writable (answer_caps);
......@@ -3799,6 +4119,20 @@ _update_transceiver_from_sdp_media (GstWebRTCBin * webrtc,
rtp_trans->mline = media_idx;
if (!g_strcmp0 (gst_sdp_media_get_media (media), "audio")) {
if (rtp_trans->kind == GST_WEBRTC_KIND_VIDEO)
GST_FIXME_OBJECT (webrtc,
"Updating video transceiver to audio, which isn't fully supported.");
rtp_trans->kind = GST_WEBRTC_KIND_AUDIO;
}
if (!g_strcmp0 (gst_sdp_media_get_media (media), "video")) {
if (rtp_trans->kind == GST_WEBRTC_KIND_AUDIO)
GST_FIXME_OBJECT (webrtc,
"Updating audio transceiver to video, which isn't fully supported.");
rtp_trans->kind = GST_WEBRTC_KIND_VIDEO;
}
for (i = 0; i < gst_sdp_media_attributes_len (media); i++) {
const GstSDPAttribute *attr = gst_sdp_media_get_attribute (media, i);
......@@ -5060,8 +5394,10 @@ gst_webrtc_bin_add_transceiver (GstWebRTCBin * webrtc,
"Created new unassociated transceiver %" GST_PTR_FORMAT, trans);
rtp_trans = GST_WEBRTC_RTP_TRANSCEIVER (trans);
if (caps)
if (caps) {
rtp_trans->codec_preferences = gst_caps_ref (caps);
_update_transceiver_kind_from_caps (rtp_trans, caps);
}
return gst_object_ref (trans);
}
......@@ -5242,6 +5578,7 @@ gst_webrtc_bin_create_data_channel (GstWebRTCBin * webrtc, const gchar * label,
ret = gst_object_ref (ret);
ret->webrtcbin = webrtc;
g_ptr_array_add (webrtc->priv->data_channels, ret);
gst_webrtc_bin_update_sctp_priority (webrtc);
webrtc_data_channel_link_to_sctp (ret, webrtc->priv->sctp_transport);
if (webrtc->priv->sctp_transport &&
webrtc->priv->sctp_transport->association_established
......@@ -5912,6 +6249,12 @@ gst_webrtc_bin_request_new_pad (GstElement * element, GstPadTemplate * templ,
}
pad->trans = gst_object_ref (trans);
if (caps && name && !_update_transceiver_kind_from_caps (trans, caps))
GST_WARNING_OBJECT (webrtc,
"Trying to create pad %s with caps %" GST_PTR_FORMAT
" but transceiver %d already exists with a different"
" media type", name, caps, serial);
pad->block_id = gst_pad_add_probe (GST_PAD (pad), GST_PAD_PROBE_TYPE_BLOCK |
GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST,
(GstPadProbeCallback) sink_pad_block, NULL, NULL);
......
......@@ -141,6 +141,8 @@ struct _GstWebRTCBinPrivate
GstWebRTCSessionDescription *last_generated_answer;
GstStructure *stats;
gboolean tos_attached;
};
typedef void (*GstWebRTCBinFunc) (GstWebRTCBin * webrtc, gpointer data);
......