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/common/gpu/media/tizen/tizen_video_decode_accelerator.h"
7 #include <gst/app/gstappsink.h>
8 #include <gst/app/gstappsrc.h>
10 #include <gst/video/gstvideosink.h>
11 #include <gst/video/video.h>
13 #include "base/bind.h"
14 #include "base/memory/shared_memory.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "base/synchronization/waitable_event.h"
17 #include "base/time/time.h"
19 #if GST_VERSION_MAJOR == 1
20 #include <gst/video/videooverlay.h>
22 #include <gst/interfaces/xoverlay.h>
25 using media::VideoFrame;
29 struct GstElementDeleter {
30 void operator()(GstElement* ptr) const {
32 gst_object_unref(ptr);
36 // Gstreamer elements and names.
37 const char* kDecoderName = "decoder";
38 #if GST_VERSION_MAJOR == 1
39 const char* kDecoderGstElement = "omxh264dec";
41 const char* kDecoderGstElement = "omx_h264dec";
49 MAX_BITRATE = 2000000, // bps.
50 INPUT_BUFFER_SIZE = MAX_BITRATE / 8, // bytes. 1 sec for H.264 HD video.
53 media::VideoDecodeAccelerator* CreateTizenVideoDecodeAccelerator() {
54 return new TizenVideoDecodeAccelerator();
57 struct TizenVideoDecodeAccelerator::BitstreamBufferRef {
59 base::WeakPtr<media::VideoDecodeAccelerator::Client> client,
60 const scoped_refptr<base::MessageLoopProxy>& client_message_loop_proxy,
61 base::SharedMemory* shm,
65 client_message_loop_proxy_(client_message_loop_proxy),
72 ~BitstreamBufferRef() {
74 client_message_loop_proxy_->PostTask(
77 &media::VideoDecodeAccelerator::Client::NotifyEndOfBitstreamBuffer,
83 static void Destruct(gpointer data) {
85 BitstreamBufferRef* pRef = static_cast<BitstreamBufferRef*>(data);
89 base::WeakPtr<media::VideoDecodeAccelerator::Client> client_;
90 scoped_refptr<base::MessageLoopProxy> client_message_loop_proxy_;
91 scoped_ptr<base::SharedMemory> shm_;
95 GstBuffer* gst_buffer_;
98 struct TizenVideoDecodeAccelerator::Impl {
101 is_destroying_(false),
105 io_message_loop_proxy_(base::MessageLoopProxy::current()),
106 gst_thread_("TizenDecoderThreadGst") {}
108 static GstBusSyncReply OnBusMessage(
109 GstBus* bus, GstMessage* msg, gpointer data) {
110 switch (GST_MESSAGE_TYPE(msg)) {
111 case GST_MESSAGE_ERROR: {
113 GError* error = NULL;
114 gst_message_parse_error(msg, &error, &debug);
115 LOG(ERROR) << __FUNCTION__
116 << " GSTError happens from bus at "
117 << GST_OBJECT_NAME(msg->src)
118 << ":" << error->message;
119 LOG(ERROR) << __FUNCTION__
120 << " Debugging Info: "
121 << (debug != NULL ? debug : "none");
126 default: NOTREACHED();
131 static void StartFeed(GstAppSrc *source, guint size, gpointer app) {
133 content::TizenVideoDecodeAccelerator::Impl* impl =
134 static_cast<content::TizenVideoDecodeAccelerator::Impl*>(app);
135 impl->can_feed_ = true;
138 static void StopFeed(GstAppSrc *source, gpointer app) {
140 content::TizenVideoDecodeAccelerator::Impl* impl =
141 static_cast<content::TizenVideoDecodeAccelerator::Impl*>(app);
142 impl->can_feed_ = false;
145 volatile bool can_feed_;
146 volatile bool is_destroying_;
147 GstElement* pipeline_;
150 scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
151 scoped_ptr<base::WeakPtrFactory<Client> > io_client_weak_factory_;
152 base::Thread gst_thread_;
155 TizenVideoDecodeAccelerator::TizenVideoDecodeAccelerator()
159 TizenVideoDecodeAccelerator::~TizenVideoDecodeAccelerator() {
162 bool TizenVideoDecodeAccelerator::Initialize(
163 media::VideoCodecProfile profile,
165 GError* error = NULL;
166 GstCaps* video_caps = NULL;
167 GstElement* gst_decoder = NULL;
168 GstBus* gst_bus = NULL;
169 scoped_ptr<GstElement, GstElementDeleter> gst_pipeline;
170 static GstAppSrcCallbacks appsrc_callbacks =
171 {&Impl::StartFeed, &Impl::StopFeed, NULL};
172 CHECK(impl_ == NULL);
174 impl_->io_client_weak_factory_.reset(
175 new base::WeakPtrFactory<Client>(client));
178 case media::H264PROFILE_BASELINE:
179 DVLOG(1) << "Initialize(): profile -> H264PROFILE_BASELINE";
181 case media::H264PROFILE_MAIN:
182 DVLOG(1) << "Initialize(): profile -> H264PROFILE_MAIN";
185 LOG(ERROR) << "Initialize(): unsupported profile=" << profile;
189 if (!gst_is_initialized() && !gst_init_check(NULL, NULL, &error)) {
190 LOG(ERROR) << __FUNCTION__ << "cannot initialize gstreamer.";
195 // pipeline initialization.
196 gst_pipeline.reset(gst_pipeline_new("h264_decode"));
198 LOG(ERROR) << __FUNCTION__ << "cannot initialize gst pipeline.";
201 if (!(gst_bus = gst_pipeline_get_bus(GST_PIPELINE(gst_pipeline.get())))) {
204 #if GST_VERSION_MAJOR == 1
205 gst_bus_set_sync_handler(gst_bus, Impl::OnBusMessage, impl_, NULL);
207 gst_bus_set_sync_handler(gst_bus, Impl::OnBusMessage, impl_);
209 gst_object_unref(gst_bus);
211 // appsrc initialization.
212 if (!(impl_->appsrc_ = gst_element_factory_make("appsrc", "src"))) {
213 LOG(ERROR) << __FUNCTION__ << "cannot initialize gst appsrc.";
216 if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->appsrc_)) {
217 gst_object_unref(impl_->appsrc_);
218 impl_->appsrc_ = NULL;
221 gst_app_src_set_max_bytes(GST_APP_SRC(impl_->appsrc_), INPUT_BUFFER_SIZE);
222 gst_app_src_set_callbacks(GST_APP_SRC(impl_->appsrc_), &appsrc_callbacks,
223 static_cast<gpointer>(impl_), NULL);
224 g_object_set(G_OBJECT(impl_->appsrc_),
227 "min-percent", 80, // if buffer below 80%, need-data emits.
228 "stream-type", GST_APP_STREAM_TYPE_STREAM,
230 if (!(video_caps = gst_caps_from_string("video/x-h264,framerate=30/1"))) {
233 g_object_set(G_OBJECT(impl_->appsrc_), "caps", video_caps, NULL);
234 gst_caps_unref(video_caps);
236 #if defined(OS_TIZEN)
237 DVLOG(1) << "######################################";
238 DVLOG(1) << " USING omx_h264dec DECODER " << (unsigned int)this;
239 DVLOG(1) << "######################################";
240 // decoder initialization.
241 if (!(gst_decoder = gst_element_factory_make(kDecoderGstElement, kDecoderName))) {
242 LOG(ERROR) << " cannot create " << kDecoderGstElement << ".";
245 if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_decoder)) {
246 LOG(ERROR) << " cannot add " << kDecoderGstElement << " to pipeline.";
247 gst_object_unref(gst_decoder);
251 // sink initialization.
252 if (!(impl_->sink_ = gst_element_factory_make("xvimagesink", "xvimagesink"))) {
253 LOG(ERROR) << __FUNCTION__ << " cannot create xvimagesink.";
256 if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->sink_)) {
257 gst_object_unref(impl_->sink_);
261 g_object_set(impl_->sink_, "rotate", 0, NULL);
263 // linking the elements.
264 if (!gst_element_link(impl_->appsrc_, gst_decoder)) {
265 LOG(ERROR) << __FUNCTION__ << " Source and Decoder could not be linked";
268 if (!gst_element_link(gst_decoder, impl_->sink_)) {
269 LOG(ERROR) << __FUNCTION__ << " Decoder and Sink could not be linked";
274 DVLOG(1) << "######################################";
275 DVLOG(1) << " USING ffdec_h264 DECODER";
276 DVLOG(1) << "######################################";
277 GstElement* gst_colorspace = NULL;
279 // decoder initialization
280 if (!(gst_decoder = gst_element_factory_make("ffdec_h264", "H264-decoder"))) {
281 LOG(ERROR) << __FUNCTION__ << " cannot create ffdec_h264.";
284 if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_decoder)) {
285 gst_object_unref(gst_decoder);
289 // colorspace initialization
290 if (!(gst_colorspace = gst_element_factory_make("ffmpegcolorspace", "cs"))) {
291 LOG(ERROR) << __FUNCTION__ << " cannot create ffmpegcolorspace.";
294 if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_colorspace)) {
295 gst_object_unref(gst_colorspace);
299 if (!(impl_->sink_ = gst_element_factory_make("autovideosink", "sink"))) {
300 LOG(ERROR) << __FUNCTION__ << " cannot create autovideosink.";
303 if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->sink_)) {
304 gst_object_unref(impl_->sink_);
309 if(!gst_element_link_many(impl_->appsrc_, gst_decoder, gst_colorspace,
310 impl_->sink_, NULL)) {
311 LOG(ERROR) << __FUNCTION__ << " Some element could not be linked";
315 if (!impl_->gst_thread_.Start()) {
316 LOG(ERROR) << __FUNCTION__ << " gst_thread_ failed to start";
320 impl_->gst_thread_.message_loop()->PostTask(
322 base::Bind(&TizenVideoDecodeAccelerator::StartDecoder,
323 base::Unretained(this)));
325 GST_DEBUG_BIN_TO_DOT_FILE(
326 GST_BIN(gst_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, "decoder_graph.dot");
328 impl_->pipeline_ = gst_pipeline.release();
332 void TizenVideoDecodeAccelerator::Decode(
333 const media::BitstreamBuffer& bitstream_buffer) {
334 scoped_ptr<BitstreamBufferRef> buffer_ref;
335 scoped_ptr<base::SharedMemory> shm(
336 new base::SharedMemory(bitstream_buffer.handle(), true));
338 if (!shm->Map(bitstream_buffer.size())) {
339 LOG(ERROR) << __FUNCTION__ << " could not map bitstream_buffer";
340 NotifyError(media::VideoDecodeAccelerator::UNREADABLE_INPUT);
344 buffer_ref.reset(new BitstreamBufferRef(
345 impl_->io_client_weak_factory_->GetWeakPtr(),
346 base::MessageLoopProxy::current(),
348 bitstream_buffer.size(),
349 bitstream_buffer.id()));
355 if (impl_->can_feed_ && !impl_->is_destroying_) {
356 impl_->gst_thread_.message_loop()->PostTask(
358 base::Bind(&TizenVideoDecodeAccelerator::OnDecode,
359 base::Unretained(this),
360 base::Passed(&buffer_ref)));
362 DVLOG(2) << __FUNCTION__
363 << " Frame drop on decoder:"
364 << " INPUT Q is FULL";
368 void TizenVideoDecodeAccelerator::AssignPictureBuffers(
369 const std::vector<media::PictureBuffer>& buffers) {
373 void TizenVideoDecodeAccelerator::ReusePictureBuffer(
374 int32 picture_buffer_id) {
378 void TizenVideoDecodeAccelerator::Flush() {
382 void TizenVideoDecodeAccelerator::Reset() {
386 void TizenVideoDecodeAccelerator::Destroy() {
388 if (impl_->gst_thread_.IsRunning()) {
389 impl_->gst_thread_.Stop();
391 gst_app_src_end_of_stream(GST_APP_SRC(impl_->appsrc_));
392 impl_->is_destroying_ = true;
393 if (impl_->pipeline_) {
394 gst_element_set_state(impl_->pipeline_, GST_STATE_NULL);
395 gst_object_unref(GST_OBJECT(impl_->pipeline_));
403 bool TizenVideoDecodeAccelerator::CanDecodeOnIOThread(){
407 void TizenVideoDecodeAccelerator::StartDecoder() {
408 gst_element_set_state(impl_->pipeline_, GST_STATE_PLAYING);
411 void TizenVideoDecodeAccelerator::OnDecode(
412 scoped_ptr<BitstreamBufferRef> buffer_ref) {
416 #if GST_VERSION_MAJOR == 1
417 buffer_ref->gst_buffer_ =
418 gst_buffer_new_wrapped_full(GST_MEMORY_FLAG_READONLY,
419 static_cast<guint8*>(buffer_ref->shm_->memory()),
423 reinterpret_cast<guint8*>(buffer_ref.get()),
424 BitstreamBufferRef::Destruct);
425 if (!buffer_ref->gst_buffer_) {
426 LOG(ERROR) << " gst_buffer_new_wrapped_full failed to allocate memory.!";
430 if (!(buffer_ref->gst_buffer_ = gst_buffer_new())) {
434 GST_BUFFER_MALLOCDATA(buffer_ref->gst_buffer_) =
435 reinterpret_cast<guint8*>(buffer_ref.get());
436 GST_BUFFER_FREE_FUNC(buffer_ref->gst_buffer_) = BitstreamBufferRef::Destruct;
437 GST_BUFFER_SIZE(buffer_ref->gst_buffer_) = buffer_ref->size_;
438 GST_BUFFER_DATA(buffer_ref->gst_buffer_) =
439 static_cast<guint8*>(buffer_ref->shm_->memory());
442 gst_app_src_push_buffer(GST_APP_SRC(impl_->appsrc_),
443 buffer_ref->gst_buffer_)) {
444 LOG(ERROR) << __FUNCTION__ << " fail to push buffer into decoder pipeline";
448 // lifecycle of buffer_ref will be handled by gstreamer.
449 buffer_ref.release();
452 void TizenVideoDecodeAccelerator::NotifyError(
453 media::VideoDecodeAccelerator::Error error) {
454 if (impl_->io_client_weak_factory_->GetWeakPtr()) {
455 impl_->io_client_weak_factory_->GetWeakPtr()->NotifyError(error);
459 } // namespace content