1 // Copyright 2014 Samsung Electronics Inc. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/media/efl/webaudio_decoder_browser_gstreamer.h"
7 #include <gst/app/gstappsink.h>
8 #include <gst/app/gstappsrc.h>
9 #include <gst/audio/audio.h>
12 #include "base/bind.h"
13 #include "base/strings/string_util.h"
14 #include "base/time/time.h"
15 #include "media/base/audio_bus.h"
16 #include "media/base/limits.h"
17 #include "media/base/efl/webaudio_media_codec_info_efl.h"
18 #include "third_party/WebKit/public/platform/WebAudioBus.h"
22 #define CHUNK_SIZE 204800 // (4096*50)
23 #define GST_OBJECT_UNREF(obj) \
25 gst_object_unref(obj); \
29 static int gst_dec_count_ = 32;
30 static int audio_width_ = 16;
32 struct GstElementDeleter {
33 void operator()(GstElement* ptr) const {
34 GST_OBJECT_UNREF(ptr);
38 // GSTDecoder class - declaration
41 GSTDecoder(uint8_t* data, int pcm_output, uint32_t data_size);
43 void InitializeGstDestination(int pcm_output,
44 uint16_t number_of_channels,
46 size_t number_of_frames);
47 void SendGstOutputUsinghandle(int pcm_output, uint8_t* buffer, int buf_size);
48 void MediaFileDecoder();
51 static void OnNewPadAdded(GstElement* bin, GstPad* pad,
53 static void OnNeedData(GstElement* source, guint size, gpointer user_data);
54 static void OnEOS(GstAppSink* sink, gpointer user_data);
55 static GstFlowReturn OnNewPreroll(GstAppSink* sink, gpointer user_data);
56 static GstFlowReturn OnNewBuffer(GstAppSink* sink, gpointer user_data);
57 static GstBusSyncReply OnBusMessage(GstBus* bus, GstMessage* message,
61 GstElement* audioconvert_;
69 }; // GSTDecoder class
71 GSTDecoder::GSTDecoder(uint8_t* data, int pcm_output, uint32_t data_size)
75 enc_length_(data_size),
77 pcm_output_(pcm_output),
79 is_endofstream_(false),
80 is_new_request_(true) {
84 GSTDecoder::~GSTDecoder() {
89 void GSTDecoder::InitializeGstDestination(int pcm_output,
90 uint16_t number_of_channels,
92 size_t number_of_frames) {
93 struct media::WebAudioMediaCodecInfoEfl info = {
94 static_cast<unsigned long>(number_of_channels),
95 static_cast<unsigned long>(sample_rate),
96 static_cast<unsigned long>(number_of_frames)
99 HANDLE_EINTR(write(pcm_output, &info, sizeof(info)));
102 void GSTDecoder::SendGstOutputUsinghandle(int pcm_output, uint8_t* buffer,
104 size_t count = buf_size;
106 int bytes_to_write = (count >= PIPE_BUF) ? PIPE_BUF : count;
107 ssize_t bytes_written =
108 HANDLE_EINTR(write(pcm_output, buffer, bytes_to_write));
109 if (bytes_written == -1)
111 count -= bytes_written;
112 buffer += bytes_written;
117 void GSTDecoder::MediaFileDecoder() {
118 if (!gst_is_initialized()) {
120 if (!gst_init_check(NULL, NULL, &err)) {
121 LOG(ERROR) << "Gst could not be initialized";
127 scoped_ptr<GstElement, GstElementDeleter> pipeline;
128 gchar pipeline_name[16] = {0,};
129 sprintf(pipeline_name, "pipeline_%d", gst_dec_count_);
131 // makes gst-pipeline
132 pipeline.reset(gst_pipeline_new((const gchar*)&pipeline_name));
134 if (!(bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline.get())))) {
135 LOG(ERROR) << "GStreamer bus creation failed";
138 gst_bus_set_sync_handler(
139 bus, (GstBusSyncHandler)OnBusMessage, this, NULL);
142 app_src_ = gst_element_factory_make("appsrc", NULL);
143 if (!gst_bin_add(GST_BIN(pipeline.get()), app_src_)) {
144 GST_OBJECT_UNREF(app_src_);
147 g_signal_connect(app_src_, "need-data", G_CALLBACK(OnNeedData), this);
150 GstElement* app_sink = gst_element_factory_make("appsink", NULL);
151 if (!gst_bin_add(GST_BIN(pipeline.get()), app_sink)) {
152 GST_OBJECT_UNREF(app_sink);
155 GstAppSinkCallbacks callbacks = {OnEOS, OnNewPreroll, OnNewBuffer};
156 gst_app_sink_set_callbacks(GST_APP_SINK(app_sink), &callbacks, this, NULL);
157 g_object_set(G_OBJECT(app_sink), "sync", FALSE, NULL);
160 GstElement* decoder = gst_element_factory_make("decodebin", NULL);
161 if (!gst_bin_add(GST_BIN(pipeline.get()), decoder)) {
162 GST_OBJECT_UNREF(decoder);
165 g_signal_connect(decoder, "pad-added", G_CALLBACK(OnNewPadAdded), this);
167 // audio converter init
168 audioconvert_ = gst_element_factory_make("audioconvert", NULL);
169 if (!gst_bin_add(GST_BIN(pipeline.get()), audioconvert_)) {
170 GST_OBJECT_UNREF(audioconvert_);
175 GstElement* sampler = gst_element_factory_make("audioresample", NULL);
176 if (!gst_bin_add(GST_BIN(pipeline.get()), sampler)) {
177 GST_OBJECT_UNREF(sampler);
182 GstElement* capsfilter = gst_element_factory_make("capsfilter", NULL);
183 if (!gst_bin_add(GST_BIN(pipeline.get()), capsfilter)) {
184 GST_OBJECT_UNREF(capsfilter);
188 GstCaps* caps = gst_caps_new_simple("audio/x-raw",
189 "format", G_TYPE_STRING, "S16LE",
190 "rate", G_TYPE_INT, 44100,
191 "channels", G_TYPE_INT, 2,
192 "layout", G_TYPE_STRING, "interleaved",
195 g_object_set(G_OBJECT(capsfilter), "caps", caps, NULL);
196 gst_caps_unref(caps);
199 if (gst_dec_count_ > 126)
202 if (!gst_element_link(app_src_, decoder)) {
203 LOG(ERROR) << " Something wrong on gst initialization";
207 if (!gst_element_link_many(audioconvert_, sampler,
208 capsfilter, app_sink, NULL)) {
209 LOG(ERROR) << "Some element could not be linked";
213 // actually works decoding
214 gst_element_set_state(pipeline.get(), GST_STATE_PLAYING);
216 //FIXME: Check if its possible to remove usleep() and make any gst
217 //async call so that GST wont block on Browser UI thread.
218 while (is_running_) {
222 // returns resource(s)
223 g_signal_handlers_disconnect_by_func(
224 bus, reinterpret_cast<gpointer>(OnBusMessage), this);
225 gst_bus_set_sync_handler(bus, NULL, NULL, NULL);
226 GST_OBJECT_UNREF(bus);
227 gst_element_set_state(pipeline.get(), GST_STATE_NULL);
230 void GSTDecoder::OnNewPadAdded(GstElement* /*bin*/, GstPad* pad,
232 GSTDecoder* decoder = static_cast<GSTDecoder*>(data);
233 GstPad* sink_pad = gst_element_get_static_pad(decoder->audioconvert_, "sink");
234 if (!GST_PAD_IS_LINKED(sink_pad)) {
235 gst_pad_link(pad, sink_pad);
237 g_object_unref(sink_pad);
240 void GSTDecoder::OnNeedData(GstElement* /*source*/, guint /*size*/,
242 GSTDecoder* decoder = static_cast<GSTDecoder*>(data);
243 if (decoder->is_endofstream_)
246 guint len = CHUNK_SIZE;
247 if ((decoder->enc_offset_ + len ) > decoder->enc_length_)
248 len = decoder->enc_length_ - decoder->enc_offset_;
251 gst_buffer_new_wrapped_full(GST_MEMORY_FLAG_READONLY,
252 decoder->encodeddata_ + decoder->enc_offset_,
253 len, 0, len, NULL, NULL);
255 LOG(ERROR) << "OnNeedData: buffer creation: FAILED";
258 decoder->enc_offset_ += len;
260 GstFlowReturn ret = GST_FLOW_OK;
261 g_signal_emit_by_name(decoder->app_src_, "push-buffer", buffer, &ret);
263 if (ret != GST_FLOW_OK) {
264 LOG(ERROR) << "OnNeedData: push-buffer ret: FAILED";
265 decoder->is_running_ = false;
268 if (decoder->enc_offset_ >= decoder->enc_length_) {
269 decoder->is_endofstream_ = TRUE;
270 g_signal_emit_by_name(decoder->app_src_, "end-of-stream", &ret);
272 gst_buffer_unref(buffer);
275 void GSTDecoder::OnEOS(GstAppSink* sink, gpointer data) {
276 GSTDecoder* decoder = static_cast<GSTDecoder*>(data);
277 if (!decoder->is_endofstream_) {
278 LOG(ERROR) << "not end of stream yet appsrc-side";
281 close(decoder->pcm_output_);
282 decoder->is_running_ = false;
285 GstFlowReturn GSTDecoder::OnNewPreroll(GstAppSink*, gpointer) {
289 GstFlowReturn GSTDecoder::OnNewBuffer(GstAppSink* sink, gpointer data) {
290 GSTDecoder* decoder = static_cast<GSTDecoder*>(data);
291 GstSample* sample = gst_app_sink_pull_sample(sink);
293 return GST_FLOW_ERROR;
295 GstBuffer* buffer = gst_sample_get_buffer(sample);
298 gst_sample_unref(sample);
299 return GST_FLOW_ERROR;
302 if (decoder->is_new_request_) {
303 GstCaps* caps = NULL;
304 GstStructure* str = NULL;
308 caps = gst_sample_get_caps(sample);
309 str = gst_caps_get_structure(caps, 0);
311 gst_sample_unref(sample);
312 return GST_FLOW_ERROR;
314 ret &= gst_structure_get_int(str, "channels", &channel);
315 ret &= gst_structure_get_int(str, "rate", &rate);
317 if (!ret || !channel || !rate) {
318 gst_sample_unref(sample);
319 return GST_FLOW_ERROR;
322 GstClockTime duration =
323 (static_cast<guint64>(gst_buffer_get_size(buffer)) * 8 * GST_SECOND) /
324 (channel * rate * audio_width_);
325 int frames = GST_CLOCK_TIME_TO_FRAMES(duration, rate);
327 decoder->InitializeGstDestination(decoder->pcm_output_, channel,
329 decoder->is_new_request_ = false;
333 gst_buffer_map(buffer, &gst_map, static_cast<GstMapFlags>(GST_MAP_READ));
334 if (gst_map.size > 0) {
335 decoder->SendGstOutputUsinghandle(decoder->pcm_output_,
338 gst_buffer_unmap(buffer, &gst_map);
341 gst_sample_unref(sample);
345 GstBusSyncReply GSTDecoder::OnBusMessage(GstBus* bus,
348 GSTDecoder* decoder = static_cast<GSTDecoder*>(data);
349 switch (GST_MESSAGE_TYPE(message)) {
350 case GST_MESSAGE_ERROR:
352 gst_message_parse_error(message, &error, NULL);
353 LOG(ERROR) << "Error message : " << error->message
354 << " recieved from : "<< GST_MESSAGE_SRC_NAME(message)
355 << ", error code : " << error->code;
358 if (decoder->is_running_) {
359 close(decoder->pcm_output_);
360 decoder->is_running_ = false;
364 DVLOG(1) << "Unhandled GStreamer message type : "
365 << GST_MESSAGE_TYPE_NAME(message);
368 gst_message_unref(message);
372 // WebAudioDecoderGStreamer class
375 WebAudioDecoderGStreamer* WebAudioDecoderGStreamer::GetInstance() {
376 return base::Singleton<WebAudioDecoderGStreamer>::get();
379 WebAudioDecoderGStreamer::WebAudioDecoderGStreamer()
380 : gst_thread_("GstThread") {
383 WebAudioDecoderGStreamer::~WebAudioDecoderGStreamer() {
386 void WebAudioDecoderGStreamer::DecodeUsingGST(
387 base::SharedMemoryHandle foreign_memory_handle,
388 base::FileDescriptor pcm_output,
389 uint32_t data_size) {
391 base::SharedMemory shared_memory(foreign_memory_handle, false);
392 if (!shared_memory.Map(data_size)) {
393 LOG(ERROR) << "Failed to map shared memory for size " << data_size;
397 uint8_t* encoded_data = new uint8_t[data_size];
399 LOG(ERROR) << "Memory allocation failed for size = " << data_size;
404 static_cast<uint8_t*>(shared_memory.memory()),
407 // This will execute until decoding is done
408 GSTDecoder decoder(encoded_data, pcm_output.fd, data_size);
409 decoder.MediaFileDecoder();
412 void WebAudioDecoderGStreamer::EncodedDataReceived(
413 base::SharedMemoryHandle foreign_memory_handle,
414 base::FileDescriptor pcm_output,
415 uint32_t data_size) {
417 if (!gst_thread_.IsRunning() && !gst_thread_.Start()) {
418 LOG(ERROR) << "Starting GStreamer thread failed";
422 gst_thread_.message_loop()->PostTask(FROM_HERE,
423 base::Bind(&WebAudioDecoderGStreamer::DecodeUsingGST,
424 base::Unretained(this), foreign_memory_handle,
425 pcm_output, data_size));
428 } // namespace content