[MM]Gstreamer 1.2 Upversion
[platform/framework/web/chromium-efl.git] / tizen_src / impl / content / common / gpu / media / tizen / tizen_video_decode_accelerator.cc
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.
4
5 #include "content/common/gpu/media/tizen/tizen_video_decode_accelerator.h"
6
7 #include <gst/app/gstappsink.h>
8 #include <gst/app/gstappsrc.h>
9 #include <gst/gst.h>
10 #include <gst/video/gstvideosink.h>
11 #include <gst/video/video.h>
12
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"
18
19 #if GST_VERSION_MAJOR == 1
20 #include <gst/video/videooverlay.h>
21 #else
22 #include <gst/interfaces/xoverlay.h>
23 #endif
24
25 using media::VideoFrame;
26
27 namespace {
28
29 struct GstElementDeleter {
30   void operator()(GstElement* ptr) const {
31     DCHECK(ptr != NULL);
32     gst_object_unref(ptr);
33   }
34 };
35
36 // Gstreamer elements and names.
37 const char* kDecoderName = "decoder";
38 #if GST_VERSION_MAJOR == 1
39 const char* kDecoderGstElement = "omxh264dec";
40 #else
41 const char* kDecoderGstElement = "omx_h264dec";
42 #endif
43
44 } // namespace
45
46 namespace content {
47
48 enum {
49   MAX_BITRATE = 2000000,                 // bps.
50   INPUT_BUFFER_SIZE = MAX_BITRATE / 8,   // bytes. 1 sec for H.264 HD video.
51 };
52
53 media::VideoDecodeAccelerator* CreateTizenVideoDecodeAccelerator() {
54   return new TizenVideoDecodeAccelerator();
55 }
56
57 struct TizenVideoDecodeAccelerator::BitstreamBufferRef {
58   BitstreamBufferRef(
59       base::WeakPtr<media::VideoDecodeAccelerator::Client> client,
60       const scoped_refptr<base::MessageLoopProxy>& client_message_loop_proxy,
61       base::SharedMemory* shm,
62       size_t size,
63       int32 input_id)
64       : client_(client),
65         client_message_loop_proxy_(client_message_loop_proxy),
66         shm_(shm),
67         size_(size),
68         bytes_used_(0),
69         input_id_(input_id),
70         gst_buffer_(NULL) {}
71
72   ~BitstreamBufferRef() {
73     if (input_id_ >= 0) {
74       client_message_loop_proxy_->PostTask(
75           FROM_HERE,
76           base::Bind(
77               &media::VideoDecodeAccelerator::Client::NotifyEndOfBitstreamBuffer,
78               client_,
79               input_id_));
80     }
81   }
82
83   static void Destruct(gpointer data) {
84     DCHECK(data != NULL);
85     BitstreamBufferRef* pRef = static_cast<BitstreamBufferRef*>(data);
86     delete pRef;
87   }
88
89   base::WeakPtr<media::VideoDecodeAccelerator::Client> client_;
90   scoped_refptr<base::MessageLoopProxy> client_message_loop_proxy_;
91   scoped_ptr<base::SharedMemory> shm_;
92   size_t size_;
93   off_t bytes_used_;
94   int32 input_id_;
95   GstBuffer* gst_buffer_;
96 };
97
98 struct TizenVideoDecodeAccelerator::Impl {
99   Impl()
100       : can_feed_(true),
101         is_destroying_(false),
102         pipeline_(NULL),
103         sink_(NULL),
104         appsrc_(NULL),
105         io_message_loop_proxy_(base::MessageLoopProxy::current()),
106         gst_thread_("TizenDecoderThreadGst") {}
107
108   static GstBusSyncReply OnBusMessage(
109       GstBus* bus, GstMessage* msg, gpointer data) {
110     switch (GST_MESSAGE_TYPE(msg)) {
111       case GST_MESSAGE_ERROR: {
112         gchar* debug = NULL;
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");
122         g_error_free(error);
123         g_free(debug);
124         break;
125       }
126       default: NOTREACHED();
127     }
128     return GST_BUS_PASS;
129   }
130
131   static void StartFeed(GstAppSrc *source, guint size, gpointer app) {
132     DCHECK(source);
133     content::TizenVideoDecodeAccelerator::Impl* impl =
134         static_cast<content::TizenVideoDecodeAccelerator::Impl*>(app);
135     impl->can_feed_ = true;
136   }
137
138   static void StopFeed(GstAppSrc *source, gpointer app) {
139     DCHECK(source);
140     content::TizenVideoDecodeAccelerator::Impl* impl =
141         static_cast<content::TizenVideoDecodeAccelerator::Impl*>(app);
142     impl->can_feed_ = false;
143   }
144
145   volatile bool can_feed_;
146   volatile bool is_destroying_;
147   GstElement* pipeline_;
148   GstElement* sink_;
149   GstElement* appsrc_;
150   scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
151   scoped_ptr<base::WeakPtrFactory<Client> > io_client_weak_factory_;
152   base::Thread gst_thread_;
153 };
154
155 TizenVideoDecodeAccelerator::TizenVideoDecodeAccelerator()
156     : impl_(NULL) {
157 }
158
159 TizenVideoDecodeAccelerator::~TizenVideoDecodeAccelerator() {
160 }
161
162 bool TizenVideoDecodeAccelerator::Initialize(
163     media::VideoCodecProfile profile,
164     Client* client) {
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);
173   impl_ = new Impl();
174   impl_->io_client_weak_factory_.reset(
175       new base::WeakPtrFactory<Client>(client));
176
177   switch (profile) {
178     case media::H264PROFILE_BASELINE:
179       DVLOG(1) << "Initialize(): profile -> H264PROFILE_BASELINE";
180       break;
181     case media::H264PROFILE_MAIN:
182       DVLOG(1) << "Initialize(): profile -> H264PROFILE_MAIN";
183       break;
184     default:
185       LOG(ERROR) << "Initialize(): unsupported profile=" << profile;
186       return false;
187   };
188
189   if (!gst_is_initialized() && !gst_init_check(NULL, NULL, &error)) {
190     LOG(ERROR) << __FUNCTION__ << "cannot initialize gstreamer.";
191     g_error_free(error);
192     return false;
193   }
194
195   // pipeline initialization.
196   gst_pipeline.reset(gst_pipeline_new("h264_decode"));
197   if (!gst_pipeline) {
198     LOG(ERROR) << __FUNCTION__ << "cannot initialize gst pipeline.";
199     return false;
200   }
201   if (!(gst_bus = gst_pipeline_get_bus(GST_PIPELINE(gst_pipeline.get())))) {
202     return false;
203   }
204 #if GST_VERSION_MAJOR == 1
205   gst_bus_set_sync_handler(gst_bus, Impl::OnBusMessage, impl_, NULL);
206 #else
207   gst_bus_set_sync_handler(gst_bus, Impl::OnBusMessage, impl_);
208 #endif
209   gst_object_unref(gst_bus);
210
211   // appsrc initialization.
212   if (!(impl_->appsrc_ = gst_element_factory_make("appsrc", "src"))) {
213     LOG(ERROR) << __FUNCTION__ << "cannot initialize gst appsrc.";
214     return false;
215   }
216   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->appsrc_)) {
217     gst_object_unref(impl_->appsrc_);
218     impl_->appsrc_ = NULL;
219     return false;
220   }
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_),
225                "is-live", TRUE,
226                "block", FALSE,
227                "min-percent", 80, // if buffer below 80%, need-data emits.
228                "stream-type", GST_APP_STREAM_TYPE_STREAM,
229                NULL);
230   if (!(video_caps = gst_caps_from_string("video/x-h264,framerate=30/1"))) {
231     return false;
232   }
233   g_object_set(G_OBJECT(impl_->appsrc_), "caps", video_caps, NULL);
234   gst_caps_unref(video_caps);
235
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 << ".";
243     return false;
244   }
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);
248     return false;
249   }
250
251   // sink initialization.
252   if (!(impl_->sink_ = gst_element_factory_make("xvimagesink", "xvimagesink"))) {
253     LOG(ERROR) << __FUNCTION__ << " cannot create xvimagesink.";
254     return false;
255   }
256   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->sink_)) {
257     gst_object_unref(impl_->sink_);
258     impl_->sink_ = NULL;
259     return false;
260   }
261   g_object_set(impl_->sink_, "rotate", 0, NULL);
262
263   // linking the elements.
264   if (!gst_element_link(impl_->appsrc_, gst_decoder)) {
265     LOG(ERROR) << __FUNCTION__ << " Source and Decoder could not be linked";
266     return false;
267   }
268   if (!gst_element_link(gst_decoder, impl_->sink_)) {
269     LOG(ERROR) << __FUNCTION__ << " Decoder and Sink could not be linked";
270     return false;
271   }
272
273 #else
274   DVLOG(1) << "######################################";
275   DVLOG(1) << "      USING ffdec_h264 DECODER";
276   DVLOG(1) << "######################################";
277   GstElement* gst_colorspace = NULL;
278
279   // decoder initialization
280   if (!(gst_decoder = gst_element_factory_make("ffdec_h264", "H264-decoder"))) {
281     LOG(ERROR) << __FUNCTION__ << " cannot create ffdec_h264.";
282     return false;
283   }
284   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_decoder)) {
285     gst_object_unref(gst_decoder);
286     return false;
287   }
288
289   // colorspace initialization
290   if (!(gst_colorspace = gst_element_factory_make("ffmpegcolorspace", "cs"))) {
291     LOG(ERROR) << __FUNCTION__ << " cannot create ffmpegcolorspace.";
292     return false;
293   }
294   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_colorspace)) {
295     gst_object_unref(gst_colorspace);
296     return false;
297   }
298
299   if (!(impl_->sink_ = gst_element_factory_make("autovideosink", "sink"))) {
300     LOG(ERROR) << __FUNCTION__ << " cannot create autovideosink.";
301     return false;
302   }
303   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->sink_)) {
304     gst_object_unref(impl_->sink_);
305     impl_->sink_ = NULL;
306     return false;
307   }
308
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";
312     return false;
313   }
314 #endif
315   if (!impl_->gst_thread_.Start()) {
316     LOG(ERROR) << __FUNCTION__ << " gst_thread_ failed to start";
317     return false;
318   }
319
320   impl_->gst_thread_.message_loop()->PostTask(
321       FROM_HERE,
322       base::Bind(&TizenVideoDecodeAccelerator::StartDecoder,
323       base::Unretained(this)));
324
325   GST_DEBUG_BIN_TO_DOT_FILE(
326       GST_BIN(gst_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, "decoder_graph.dot");
327
328   impl_->pipeline_ = gst_pipeline.release();
329   return true;
330 }
331
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));
337
338   if (!shm->Map(bitstream_buffer.size())) {
339     LOG(ERROR) << __FUNCTION__ << " could not map bitstream_buffer";
340     NotifyError(media::VideoDecodeAccelerator::UNREADABLE_INPUT);
341     return;
342   }
343
344   buffer_ref.reset(new BitstreamBufferRef(
345       impl_->io_client_weak_factory_->GetWeakPtr(),
346       base::MessageLoopProxy::current(),
347       shm.release(),
348       bitstream_buffer.size(),
349       bitstream_buffer.id()));
350
351   if (!buffer_ref) {
352     return;
353   }
354
355   if (impl_->can_feed_ && !impl_->is_destroying_) {
356     impl_->gst_thread_.message_loop()->PostTask(
357         FROM_HERE,
358         base::Bind(&TizenVideoDecodeAccelerator::OnDecode,
359                    base::Unretained(this),
360                    base::Passed(&buffer_ref)));
361   } else {
362     DVLOG(2) << __FUNCTION__
363              << " Frame drop on decoder:"
364              << " INPUT Q is FULL";
365   }
366 }
367
368 void TizenVideoDecodeAccelerator::AssignPictureBuffers(
369     const std::vector<media::PictureBuffer>& buffers) {
370   NOTIMPLEMENTED();
371 }
372
373 void TizenVideoDecodeAccelerator::ReusePictureBuffer(
374     int32 picture_buffer_id) {
375   NOTIMPLEMENTED();
376 }
377
378 void TizenVideoDecodeAccelerator::Flush() {
379   NOTIMPLEMENTED();
380 }
381
382 void TizenVideoDecodeAccelerator::Reset() {
383   NOTIMPLEMENTED();
384 }
385
386 void TizenVideoDecodeAccelerator::Destroy() {
387   if (impl_ != NULL) {
388     if (impl_->gst_thread_.IsRunning()) {
389       impl_->gst_thread_.Stop();
390     }
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_));
396     }
397     delete impl_;
398     impl_ = NULL;
399   }
400   delete this;
401 }
402
403 bool TizenVideoDecodeAccelerator::CanDecodeOnIOThread(){
404   return false;
405 }
406
407 void TizenVideoDecodeAccelerator::StartDecoder() {
408   gst_element_set_state(impl_->pipeline_, GST_STATE_PLAYING);
409 };
410
411 void TizenVideoDecodeAccelerator::OnDecode(
412     scoped_ptr<BitstreamBufferRef> buffer_ref) {
413   if (!buffer_ref) {
414     return;
415   }
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()),
420                                   buffer_ref->size_,
421                                   0,
422                                   buffer_ref->size_,
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.!";
427     return;
428   }
429 #else
430   if (!(buffer_ref->gst_buffer_ = gst_buffer_new())) {
431     return;
432   }
433
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());
440 #endif
441   if (GST_FLOW_OK !=
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";
445     return;
446   }
447
448   // lifecycle of buffer_ref will be handled by gstreamer.
449   buffer_ref.release();
450 }
451
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);
456   }
457 }
458
459 }  // namespace content
460