153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright (C) 2020 Asymptotic <sanchayan@asymptotic.io> 553a5a1b3Sopenharmony_ci 653a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 753a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as 853a5a1b3Sopenharmony_ci published by the Free Software Foundation; either version 2.1 of the 953a5a1b3Sopenharmony_ci License, or (at your option) any later version. 1053a5a1b3Sopenharmony_ci 1153a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1253a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1353a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1453a5a1b3Sopenharmony_ci General Public License for more details. 1553a5a1b3Sopenharmony_ci 1653a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public 1753a5a1b3Sopenharmony_ci License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1853a5a1b3Sopenharmony_ci***/ 1953a5a1b3Sopenharmony_ci 2053a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2153a5a1b3Sopenharmony_ci#include <config.h> 2253a5a1b3Sopenharmony_ci#endif 2353a5a1b3Sopenharmony_ci 2453a5a1b3Sopenharmony_ci#include <arpa/inet.h> 2553a5a1b3Sopenharmony_ci#include <stdint.h> 2653a5a1b3Sopenharmony_ci 2753a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 2853a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 2953a5a1b3Sopenharmony_ci#include <pulsecore/once.h> 3053a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 3153a5a1b3Sopenharmony_ci#include <pulse/sample.h> 3253a5a1b3Sopenharmony_ci#include <pulse/timeval.h> 3353a5a1b3Sopenharmony_ci#include <pulse/util.h> 3453a5a1b3Sopenharmony_ci 3553a5a1b3Sopenharmony_ci#include "a2dp-codecs.h" 3653a5a1b3Sopenharmony_ci#include "a2dp-codec-api.h" 3753a5a1b3Sopenharmony_ci#include "a2dp-codec-gst.h" 3853a5a1b3Sopenharmony_ci 3953a5a1b3Sopenharmony_ci/* Called from the GStreamer streaming thread */ 4053a5a1b3Sopenharmony_cistatic void app_sink_eos(GstAppSink *appsink, gpointer userdata) { 4153a5a1b3Sopenharmony_ci pa_log_debug("Sink got EOS"); 4253a5a1b3Sopenharmony_ci} 4353a5a1b3Sopenharmony_ci 4453a5a1b3Sopenharmony_cistatic void gst_deinit_common(struct gst_info *info) { 4553a5a1b3Sopenharmony_ci if (!info) 4653a5a1b3Sopenharmony_ci return; 4753a5a1b3Sopenharmony_ci if (info->app_sink) 4853a5a1b3Sopenharmony_ci gst_object_unref(info->app_sink); 4953a5a1b3Sopenharmony_ci if (info->bin) 5053a5a1b3Sopenharmony_ci gst_object_unref(info->bin); 5153a5a1b3Sopenharmony_ci} 5253a5a1b3Sopenharmony_ci 5353a5a1b3Sopenharmony_cibool gst_init_common(struct gst_info *info) { 5453a5a1b3Sopenharmony_ci GstElement *bin = NULL; 5553a5a1b3Sopenharmony_ci GstElement *appsink = NULL; 5653a5a1b3Sopenharmony_ci GstAppSinkCallbacks callbacks = { 0, }; 5753a5a1b3Sopenharmony_ci 5853a5a1b3Sopenharmony_ci appsink = gst_element_factory_make("appsink", "app_sink"); 5953a5a1b3Sopenharmony_ci if (!appsink) { 6053a5a1b3Sopenharmony_ci pa_log_error("Could not create appsink element"); 6153a5a1b3Sopenharmony_ci goto fail; 6253a5a1b3Sopenharmony_ci } 6353a5a1b3Sopenharmony_ci g_object_set(appsink, "sync", FALSE, "async", FALSE, "enable-last-sample", FALSE, NULL); 6453a5a1b3Sopenharmony_ci 6553a5a1b3Sopenharmony_ci callbacks.eos = app_sink_eos; 6653a5a1b3Sopenharmony_ci gst_app_sink_set_callbacks(GST_APP_SINK(appsink), &callbacks, info, NULL); 6753a5a1b3Sopenharmony_ci 6853a5a1b3Sopenharmony_ci bin = gst_bin_new(NULL); 6953a5a1b3Sopenharmony_ci pa_assert(bin); 7053a5a1b3Sopenharmony_ci 7153a5a1b3Sopenharmony_ci info->app_sink = appsink; 7253a5a1b3Sopenharmony_ci info->bin = bin; 7353a5a1b3Sopenharmony_ci 7453a5a1b3Sopenharmony_ci return true; 7553a5a1b3Sopenharmony_ci 7653a5a1b3Sopenharmony_cifail: 7753a5a1b3Sopenharmony_ci if (appsink) 7853a5a1b3Sopenharmony_ci gst_object_unref(appsink); 7953a5a1b3Sopenharmony_ci 8053a5a1b3Sopenharmony_ci return false; 8153a5a1b3Sopenharmony_ci} 8253a5a1b3Sopenharmony_ci 8353a5a1b3Sopenharmony_cistatic GstCaps *gst_create_caps_from_sample_spec(const pa_sample_spec *ss) { 8453a5a1b3Sopenharmony_ci gchar *sample_format; 8553a5a1b3Sopenharmony_ci GstCaps *caps; 8653a5a1b3Sopenharmony_ci uint64_t channel_mask; 8753a5a1b3Sopenharmony_ci 8853a5a1b3Sopenharmony_ci switch (ss->format) { 8953a5a1b3Sopenharmony_ci case PA_SAMPLE_S16LE: 9053a5a1b3Sopenharmony_ci sample_format = "S16LE"; 9153a5a1b3Sopenharmony_ci break; 9253a5a1b3Sopenharmony_ci case PA_SAMPLE_S24LE: 9353a5a1b3Sopenharmony_ci sample_format = "S24LE"; 9453a5a1b3Sopenharmony_ci break; 9553a5a1b3Sopenharmony_ci case PA_SAMPLE_S32LE: 9653a5a1b3Sopenharmony_ci sample_format = "S32LE"; 9753a5a1b3Sopenharmony_ci break; 9853a5a1b3Sopenharmony_ci case PA_SAMPLE_FLOAT32LE: 9953a5a1b3Sopenharmony_ci sample_format = "F32LE"; 10053a5a1b3Sopenharmony_ci break; 10153a5a1b3Sopenharmony_ci default: 10253a5a1b3Sopenharmony_ci pa_assert_not_reached(); 10353a5a1b3Sopenharmony_ci break; 10453a5a1b3Sopenharmony_ci } 10553a5a1b3Sopenharmony_ci 10653a5a1b3Sopenharmony_ci switch (ss->channels) { 10753a5a1b3Sopenharmony_ci case 1: 10853a5a1b3Sopenharmony_ci channel_mask = 0x1; 10953a5a1b3Sopenharmony_ci break; 11053a5a1b3Sopenharmony_ci case 2: 11153a5a1b3Sopenharmony_ci channel_mask = 0x3; 11253a5a1b3Sopenharmony_ci break; 11353a5a1b3Sopenharmony_ci default: 11453a5a1b3Sopenharmony_ci pa_assert_not_reached(); 11553a5a1b3Sopenharmony_ci break; 11653a5a1b3Sopenharmony_ci } 11753a5a1b3Sopenharmony_ci 11853a5a1b3Sopenharmony_ci caps = gst_caps_new_simple("audio/x-raw", 11953a5a1b3Sopenharmony_ci "format", G_TYPE_STRING, sample_format, 12053a5a1b3Sopenharmony_ci "rate", G_TYPE_INT, (int) ss->rate, 12153a5a1b3Sopenharmony_ci "channels", G_TYPE_INT, (int) ss->channels, 12253a5a1b3Sopenharmony_ci "channel-mask", GST_TYPE_BITMASK, channel_mask, 12353a5a1b3Sopenharmony_ci "layout", G_TYPE_STRING, "interleaved", 12453a5a1b3Sopenharmony_ci NULL); 12553a5a1b3Sopenharmony_ci 12653a5a1b3Sopenharmony_ci pa_assert(caps); 12753a5a1b3Sopenharmony_ci return caps; 12853a5a1b3Sopenharmony_ci} 12953a5a1b3Sopenharmony_ci 13053a5a1b3Sopenharmony_cibool gst_codec_init(struct gst_info *info, bool for_encoding, GstElement *transcoder) { 13153a5a1b3Sopenharmony_ci GstPad *pad; 13253a5a1b3Sopenharmony_ci GstCaps *caps; 13353a5a1b3Sopenharmony_ci GstEvent *event; 13453a5a1b3Sopenharmony_ci GstSegment segment; 13553a5a1b3Sopenharmony_ci GstEvent *stream_start; 13653a5a1b3Sopenharmony_ci guint group_id; 13753a5a1b3Sopenharmony_ci 13853a5a1b3Sopenharmony_ci pa_assert(transcoder); 13953a5a1b3Sopenharmony_ci 14053a5a1b3Sopenharmony_ci info->seq_num = 0; 14153a5a1b3Sopenharmony_ci 14253a5a1b3Sopenharmony_ci if (!gst_init_common(info)) 14353a5a1b3Sopenharmony_ci goto common_fail; 14453a5a1b3Sopenharmony_ci 14553a5a1b3Sopenharmony_ci gst_bin_add_many(GST_BIN(info->bin), transcoder, info->app_sink, NULL); 14653a5a1b3Sopenharmony_ci 14753a5a1b3Sopenharmony_ci if (!gst_element_link_many(transcoder, info->app_sink, NULL)) { 14853a5a1b3Sopenharmony_ci pa_log_error("Failed to link codec elements into pipeline"); 14953a5a1b3Sopenharmony_ci goto pipeline_fail; 15053a5a1b3Sopenharmony_ci } 15153a5a1b3Sopenharmony_ci 15253a5a1b3Sopenharmony_ci pad = gst_element_get_static_pad(transcoder, "sink"); 15353a5a1b3Sopenharmony_ci pa_assert_se(gst_element_add_pad(info->bin, gst_ghost_pad_new("sink", pad))); 15453a5a1b3Sopenharmony_ci /** 15553a5a1b3Sopenharmony_ci * Only the sink pad is needed to push buffers. Cache it since 15653a5a1b3Sopenharmony_ci * gst_element_get_static_pad is relatively expensive and verbose 15753a5a1b3Sopenharmony_ci * on higher log levels. 15853a5a1b3Sopenharmony_ci */ 15953a5a1b3Sopenharmony_ci info->pad_sink = pad; 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_ci if (gst_element_set_state(info->bin, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { 16253a5a1b3Sopenharmony_ci pa_log_error("Could not start pipeline"); 16353a5a1b3Sopenharmony_ci goto pipeline_fail; 16453a5a1b3Sopenharmony_ci } 16553a5a1b3Sopenharmony_ci 16653a5a1b3Sopenharmony_ci /* First, send stream-start sticky event */ 16753a5a1b3Sopenharmony_ci group_id = gst_util_group_id_next(); 16853a5a1b3Sopenharmony_ci stream_start = gst_event_new_stream_start("gst-codec-pa"); 16953a5a1b3Sopenharmony_ci gst_event_set_group_id(stream_start, group_id); 17053a5a1b3Sopenharmony_ci gst_pad_send_event(info->pad_sink, stream_start); 17153a5a1b3Sopenharmony_ci 17253a5a1b3Sopenharmony_ci /* Retrieve the pad that handles the PCM format between PA and GStreamer */ 17353a5a1b3Sopenharmony_ci if (for_encoding) 17453a5a1b3Sopenharmony_ci pad = gst_element_get_static_pad(transcoder, "sink"); 17553a5a1b3Sopenharmony_ci else 17653a5a1b3Sopenharmony_ci pad = gst_element_get_static_pad(transcoder, "src"); 17753a5a1b3Sopenharmony_ci 17853a5a1b3Sopenharmony_ci /* Second, send caps sticky event */ 17953a5a1b3Sopenharmony_ci caps = gst_create_caps_from_sample_spec(info->ss); 18053a5a1b3Sopenharmony_ci pa_assert_se(gst_pad_set_caps(pad, caps)); 18153a5a1b3Sopenharmony_ci gst_caps_unref(caps); 18253a5a1b3Sopenharmony_ci gst_object_unref(GST_OBJECT(pad)); 18353a5a1b3Sopenharmony_ci 18453a5a1b3Sopenharmony_ci /* Third, send segment sticky event */ 18553a5a1b3Sopenharmony_ci gst_segment_init(&segment, GST_FORMAT_TIME); 18653a5a1b3Sopenharmony_ci event = gst_event_new_segment(&segment); 18753a5a1b3Sopenharmony_ci gst_pad_send_event(info->pad_sink, event); 18853a5a1b3Sopenharmony_ci 18953a5a1b3Sopenharmony_ci pa_log_info("GStreamer pipeline initialisation succeeded"); 19053a5a1b3Sopenharmony_ci 19153a5a1b3Sopenharmony_ci return true; 19253a5a1b3Sopenharmony_ci 19353a5a1b3Sopenharmony_cipipeline_fail: 19453a5a1b3Sopenharmony_ci gst_deinit_common(info); 19553a5a1b3Sopenharmony_ci 19653a5a1b3Sopenharmony_ci pa_log_error("GStreamer pipeline initialisation failed"); 19753a5a1b3Sopenharmony_ci 19853a5a1b3Sopenharmony_ci return false; 19953a5a1b3Sopenharmony_ci 20053a5a1b3Sopenharmony_cicommon_fail: 20153a5a1b3Sopenharmony_ci /* If common initialization fails the bin has not yet had its ownership 20253a5a1b3Sopenharmony_ci * transferred to the pipeline yet. 20353a5a1b3Sopenharmony_ci */ 20453a5a1b3Sopenharmony_ci gst_object_unref(transcoder); 20553a5a1b3Sopenharmony_ci 20653a5a1b3Sopenharmony_ci pa_log_error("GStreamer pipeline creation failed"); 20753a5a1b3Sopenharmony_ci 20853a5a1b3Sopenharmony_ci return false; 20953a5a1b3Sopenharmony_ci} 21053a5a1b3Sopenharmony_ci 21153a5a1b3Sopenharmony_cisize_t gst_transcode_buffer(void *codec_info, uint32_t timestamp, const uint8_t *input_buffer, size_t input_size, uint8_t *output_buffer, size_t output_size, size_t *processed) { 21253a5a1b3Sopenharmony_ci struct gst_info *info = (struct gst_info *) codec_info; 21353a5a1b3Sopenharmony_ci gsize transcoded; 21453a5a1b3Sopenharmony_ci GstBuffer *in_buf; 21553a5a1b3Sopenharmony_ci GstFlowReturn ret; 21653a5a1b3Sopenharmony_ci size_t written = 0; 21753a5a1b3Sopenharmony_ci GstSample *sample; 21853a5a1b3Sopenharmony_ci 21953a5a1b3Sopenharmony_ci pa_assert(info->pad_sink); 22053a5a1b3Sopenharmony_ci 22153a5a1b3Sopenharmony_ci in_buf = gst_buffer_new_wrapped_full(GST_MEMORY_FLAG_READONLY, 22253a5a1b3Sopenharmony_ci (gpointer)input_buffer, input_size, 0, input_size, NULL, NULL); 22353a5a1b3Sopenharmony_ci pa_assert(in_buf); 22453a5a1b3Sopenharmony_ci /* Acquire an extra reference to validate refcount afterwards */ 22553a5a1b3Sopenharmony_ci gst_mini_object_ref(GST_MINI_OBJECT_CAST(in_buf)); 22653a5a1b3Sopenharmony_ci pa_assert(GST_MINI_OBJECT_REFCOUNT_VALUE(in_buf) == 2); 22753a5a1b3Sopenharmony_ci 22853a5a1b3Sopenharmony_ci if (timestamp == -1) 22953a5a1b3Sopenharmony_ci GST_BUFFER_TIMESTAMP(in_buf) = GST_CLOCK_TIME_NONE; 23053a5a1b3Sopenharmony_ci else { 23153a5a1b3Sopenharmony_ci // Timestamp is monotonically increasing with samplerate/packets-per-second; 23253a5a1b3Sopenharmony_ci // convert it to a timestamp in nanoseconds: 23353a5a1b3Sopenharmony_ci GST_BUFFER_TIMESTAMP(in_buf) = timestamp * PA_USEC_PER_SEC / info->ss->rate; 23453a5a1b3Sopenharmony_ci } 23553a5a1b3Sopenharmony_ci 23653a5a1b3Sopenharmony_ci ret = gst_pad_chain(info->pad_sink, in_buf); 23753a5a1b3Sopenharmony_ci /** 23853a5a1b3Sopenharmony_ci * Ensure we're the only one holding a reference to this buffer after gst_pad_chain, 23953a5a1b3Sopenharmony_ci * which internally holds a pointer reference to input_buffer. The caller provides 24053a5a1b3Sopenharmony_ci * no guarantee to the validity of this pointer after returning from this function. 24153a5a1b3Sopenharmony_ci */ 24253a5a1b3Sopenharmony_ci pa_assert(GST_MINI_OBJECT_REFCOUNT_VALUE(in_buf) == 1); 24353a5a1b3Sopenharmony_ci gst_mini_object_unref(GST_MINI_OBJECT_CAST(in_buf)); 24453a5a1b3Sopenharmony_ci 24553a5a1b3Sopenharmony_ci if (ret != GST_FLOW_OK) { 24653a5a1b3Sopenharmony_ci pa_log_error("failed to push buffer for transcoding %d", ret); 24753a5a1b3Sopenharmony_ci goto fail; 24853a5a1b3Sopenharmony_ci } 24953a5a1b3Sopenharmony_ci 25053a5a1b3Sopenharmony_ci while ((sample = gst_app_sink_try_pull_sample(GST_APP_SINK(info->app_sink), 0))) { 25153a5a1b3Sopenharmony_ci in_buf = gst_sample_get_buffer(sample); 25253a5a1b3Sopenharmony_ci 25353a5a1b3Sopenharmony_ci transcoded = gst_buffer_get_size(in_buf); 25453a5a1b3Sopenharmony_ci written += transcoded; 25553a5a1b3Sopenharmony_ci pa_assert(written <= output_size); 25653a5a1b3Sopenharmony_ci 25753a5a1b3Sopenharmony_ci GstMapInfo map_info; 25853a5a1b3Sopenharmony_ci pa_assert_se(gst_buffer_map(in_buf, &map_info, GST_MAP_READ)); 25953a5a1b3Sopenharmony_ci memcpy(output_buffer, map_info.data, transcoded); 26053a5a1b3Sopenharmony_ci gst_buffer_unmap(in_buf, &map_info); 26153a5a1b3Sopenharmony_ci gst_sample_unref(sample); 26253a5a1b3Sopenharmony_ci } 26353a5a1b3Sopenharmony_ci 26453a5a1b3Sopenharmony_ci *processed = input_size; 26553a5a1b3Sopenharmony_ci 26653a5a1b3Sopenharmony_ci return written; 26753a5a1b3Sopenharmony_ci 26853a5a1b3Sopenharmony_cifail: 26953a5a1b3Sopenharmony_ci *processed = 0; 27053a5a1b3Sopenharmony_ci 27153a5a1b3Sopenharmony_ci return written; 27253a5a1b3Sopenharmony_ci} 27353a5a1b3Sopenharmony_ci 27453a5a1b3Sopenharmony_civoid gst_codec_deinit(void *codec_info) { 27553a5a1b3Sopenharmony_ci struct gst_info *info = (struct gst_info *) codec_info; 27653a5a1b3Sopenharmony_ci 27753a5a1b3Sopenharmony_ci if (info->bin) { 27853a5a1b3Sopenharmony_ci gst_element_set_state(info->bin, GST_STATE_NULL); 27953a5a1b3Sopenharmony_ci gst_object_unref(info->bin); 28053a5a1b3Sopenharmony_ci } 28153a5a1b3Sopenharmony_ci 28253a5a1b3Sopenharmony_ci if (info->pad_sink) 28353a5a1b3Sopenharmony_ci gst_object_unref(GST_OBJECT(info->pad_sink)); 28453a5a1b3Sopenharmony_ci 28553a5a1b3Sopenharmony_ci pa_xfree(info); 28653a5a1b3Sopenharmony_ci} 287