[Tizen][MM][Webrtc] Enable H/W accelerated H.264 support for WebRTC on Tizen
[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 #include "content/common/gpu/media/tizen/tizen_video_decode_accelerator.h"
5
6 #include <gst/gst.h>
7 #include <gst/app/gstappsink.h>
8 #include <gst/app/gstappsrc.h>
9 #include <gst/interfaces/xoverlay.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 using media::VideoFrame;
20
21 namespace {
22
23 struct GstElementDeleter {
24   void operator()(GstElement* ptr) const {
25     DCHECK(ptr != NULL);
26     gst_object_unref(ptr);
27   }
28 };
29
30 } // namespace
31
32 namespace content {
33
34 enum {
35   INPUT_BUFFER_SIZE = 319488,   // bytes. 1 sec for H.264 HD video.
36 };
37
38 // Generating Unique Key from given width and height.
39 int32 ConvertWidthAndHeightToKey(int width, int height) {
40   return ((width << 16) | height);
41 }
42
43 media::VideoDecodeAccelerator* CreateTizenVideoDecodeAccelerator() {
44   return new TizenVideoDecodeAccelerator();
45 }
46
47 struct TizenVideoDecodeAccelerator::BitstreamBufferRef {
48   BitstreamBufferRef(
49       base::WeakPtr<media::VideoDecodeAccelerator::Client> client,
50       const scoped_refptr<base::MessageLoopProxy>& client_message_loop_proxy,
51       base::SharedMemory* shm,
52       size_t size,
53       int32 input_id)
54       : client_(client),
55         client_message_loop_proxy_(client_message_loop_proxy),
56         shm_(shm),
57         size_(size),
58         bytes_used_(0),
59         input_id_(input_id),
60         gst_buffer_(NULL) {}
61
62   ~BitstreamBufferRef() {
63     if (input_id_ >= 0) {
64       client_message_loop_proxy_->PostTask(
65           FROM_HERE,
66           base::Bind(
67               &media::VideoDecodeAccelerator::Client::NotifyEndOfBitstreamBuffer,
68               client_,
69               input_id_));
70     }
71   }
72
73   static void Destruct(gpointer data) {
74     DCHECK(data != NULL);
75     BitstreamBufferRef* pRef = static_cast<BitstreamBufferRef*>(data);
76     delete pRef;
77   }
78
79   base::WeakPtr<media::VideoDecodeAccelerator::Client> client_;
80   scoped_refptr<base::MessageLoopProxy> client_message_loop_proxy_;
81   scoped_ptr<base::SharedMemory> shm_;
82   size_t size_;
83   off_t bytes_used_;
84   int32 input_id_;
85   GstBuffer* gst_buffer_;
86 };
87
88 struct TizenVideoDecodeAccelerator::Impl {
89   Impl()
90   : can_feed_(true),
91     is_destroying_(false),
92     pipeline_(NULL),
93     sink_(NULL),
94     appsrc_(NULL),
95     io_message_loop_proxy_(base::MessageLoopProxy::current()),
96     gst_thread_("TizenDecoderThreadGst") {}
97
98   static GstBusSyncReply OnBusMessage(
99       GstBus *bus, GstMessage *msg, gpointer data) {
100     switch (GST_MESSAGE_TYPE(msg)) {
101       case GST_MESSAGE_EOS:
102         break;
103       case GST_MESSAGE_ERROR: {
104         gchar *debug = NULL;
105         GError *error = NULL;
106         gst_message_parse_error(msg, &error, &debug);
107         g_free(debug);
108         LOG(ERROR) << __FUNCTION__
109                    << "Error Message Received in Gst Decoder Bus: "
110                    << error->message;
111         g_error_free(error);
112         break;
113       }
114       default:
115         break;
116     }
117     return GST_BUS_PASS;
118   }
119
120   static void StartFeed(GstAppSrc *source, guint size, gpointer app) {
121     content::TizenVideoDecodeAccelerator::Impl* impl =
122         static_cast<content::TizenVideoDecodeAccelerator::Impl*>(app);
123     impl->can_feed_ = true;
124   }
125
126   static void StopFeed(GstAppSrc *source, gpointer app) {
127     content::TizenVideoDecodeAccelerator::Impl* impl =
128         static_cast<content::TizenVideoDecodeAccelerator::Impl*>(app);
129     impl->can_feed_ = false;
130   }
131
132   volatile bool can_feed_;
133   volatile bool is_destroying_;
134   GstElement* pipeline_;
135   GstElement* sink_;
136   GstElement* appsrc_;
137   scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
138   scoped_ptr<base::WeakPtrFactory<Client> > io_client_weak_factory_;
139   base::Thread gst_thread_;
140 };
141
142 TizenVideoDecodeAccelerator::TizenVideoDecodeAccelerator()
143     : impl_(NULL) {
144 }
145
146 TizenVideoDecodeAccelerator::~TizenVideoDecodeAccelerator() {
147 }
148
149 bool TizenVideoDecodeAccelerator::Initialize(
150     media::VideoCodecProfile profile,
151     Client* client) {
152   GError* error = NULL;
153   GstCaps* video_caps = NULL;
154   GstElement* gst_decoder = NULL;
155   GstBus* gst_bus = NULL;
156   scoped_ptr<GstElement, GstElementDeleter> gst_pipeline;
157   static GstAppSrcCallbacks appsrc_callbacks =
158       {&Impl::StartFeed, &Impl::StopFeed, NULL};
159   CHECK(impl_ == NULL );
160   impl_ = new Impl();
161   impl_->io_client_weak_factory_.reset(
162       new base::WeakPtrFactory<Client>(client));
163
164   switch (profile) {
165     case media::H264PROFILE_BASELINE:
166       DVLOG(1) << "Initialize(): profile -> H264PROFILE_BASELINE";
167       break;
168     case media::H264PROFILE_MAIN:
169       DVLOG(1) << "Initialize(): profile -> H264PROFILE_MAIN";
170       break;
171     default:
172       LOG(ERROR) << "Initialize(): unsupported profile=" << profile;
173       return false;
174   };
175
176   if (!gst_is_initialized() && !gst_init_check(NULL, NULL, &error)) {
177     LOG(ERROR) << __FUNCTION__ << "cannot initialize gstreamer.";
178     g_error_free(error);
179     return false;
180   }
181
182   // pipeline
183   gst_pipeline.reset(gst_pipeline_new("h264_decode"));
184   if (!gst_pipeline) {
185     LOG(ERROR) << __FUNCTION__ << "cannot initialize gst pipeline.";
186     return false;
187   }
188   // Add a message handler
189   if (!(gst_bus = gst_pipeline_get_bus(GST_PIPELINE(gst_pipeline.get())))) {
190     return false;
191   }
192   gst_bus_set_sync_handler(gst_bus, Impl::OnBusMessage, impl_);
193   gst_object_unref(gst_bus);
194
195   // appsrc
196   if (!(impl_->appsrc_ = gst_element_factory_make("appsrc", "src"))) {
197     LOG(ERROR) << __FUNCTION__ << "cannot initialize gst appsrc.";
198     return false;
199   }
200   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->appsrc_)) {
201     gst_object_unref(impl_->appsrc_);
202     impl_->appsrc_ = NULL;
203     return false;
204   }
205   gst_app_src_set_max_bytes(GST_APP_SRC(impl_->appsrc_), INPUT_BUFFER_SIZE);
206   gst_app_src_set_callbacks(GST_APP_SRC(impl_->appsrc_), &appsrc_callbacks,
207                             static_cast<gpointer>(impl_), NULL);
208   g_object_set(G_OBJECT(impl_->appsrc_),
209                "is-live", TRUE,
210                "block", FALSE,
211                "min-percent", 80, // if buffer below 80%, need-data emits.
212                "stream-type", GST_APP_STREAM_TYPE_STREAM,
213                NULL);
214   if (!(video_caps = gst_caps_from_string("video/x-h264,framerate=30/1"))) {
215     return false;
216   }
217   g_object_set(G_OBJECT(impl_->appsrc_), "caps", video_caps, NULL);
218   gst_caps_unref(video_caps);
219
220 #if defined(OS_TIZEN)
221   DVLOG(1) << "######################################";
222   DVLOG(1) << "      USING omx_h264dec DECODER " << (unsigned int)this;
223   DVLOG(1) << "######################################";
224   // decoder
225   if (!(gst_decoder = gst_element_factory_make("omx_h264dec", "decoder"))) {
226     LOG(ERROR) << __FUNCTION__ << " cannot create omx_h264dec.";
227     return false;
228   }
229   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_decoder)) {
230     gst_object_unref(gst_decoder);
231     return false;
232   }
233
234   // sink
235   if (!(impl_->sink_ = gst_element_factory_make("xvimagesink", "xvimagesink"))) {
236     LOG(ERROR) << __FUNCTION__ << " cannot create xvimagesink.";
237     return false;
238   }
239   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->sink_)) {
240     gst_object_unref(impl_->sink_);
241     impl_->sink_ = NULL;
242     return false;
243   }
244   g_object_set(impl_->sink_, "rotate", 0, NULL);
245
246   // linking
247   if (!gst_element_link(impl_->appsrc_, gst_decoder)) {
248     LOG(ERROR) << __FUNCTION__ << " Source and Decoder could not be linked";
249     return false;
250   }
251   if (!gst_element_link(gst_decoder, impl_->sink_)) {
252     LOG(ERROR) << __FUNCTION__ << " Decoder and Sink could not be linked";
253     return false;
254   }
255
256 #else
257   DVLOG(1) << "######################################";
258   DVLOG(1) << "      USING ffdec_h264 DECODER";
259   DVLOG(1) << "######################################";
260   GstElement* gst_colorspace = NULL;
261
262   // decoder
263   if (!(gst_decoder = gst_element_factory_make("ffdec_h264", "H264-decoder"))) {
264     LOG(ERROR) << __FUNCTION__ << " cannot create ffdec_h264.";
265     return false;
266   }
267   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_decoder)) {
268     gst_object_unref(gst_decoder);
269     return false;
270   }
271
272   // colorspace
273   if (!(gst_colorspace = gst_element_factory_make("ffmpegcolorspace", "cs"))) {
274     LOG(ERROR) << __FUNCTION__ << " cannot create ffmpegcolorspace.";
275     return false;
276   }
277   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_colorspace)) {
278     gst_object_unref(gst_colorspace);
279     return false;
280   }
281
282   if (!(impl_->sink_ = gst_element_factory_make("autovideosink", "sink"))) {
283     LOG(ERROR) << __FUNCTION__ << " cannot create autovideosink.";
284     return false;
285   }
286   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->sink_)) {
287     gst_object_unref(impl_->sink_);
288     impl_->sink_ = NULL;
289     return false;
290   }
291
292   if(!gst_element_link_many(impl_->appsrc_, gst_decoder, gst_colorspace,
293                             impl_->sink_, NULL)) {
294     LOG(ERROR) << __FUNCTION__ << " Some element could not be linked";
295     return false;
296   }
297 #endif
298   if (!impl_->gst_thread_.Start()) {
299     LOG(ERROR) << __FUNCTION__ << " gst_thread_ failed to start";
300     return false;
301   }
302
303   impl_->gst_thread_.message_loop()->PostTask(
304       FROM_HERE,
305       base::Bind(&TizenVideoDecodeAccelerator::StartDecoder,
306       base::Unretained(this)));
307
308   // To enable the gst pipeline graph, set environment variable.
309   // $ export GST_DEBUG_DUMP_DOT_DIR=/opt/usr
310   GST_DEBUG_BIN_TO_DOT_FILE(
311       GST_BIN(gst_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, "decoder_graph.dot");
312
313   impl_->pipeline_ = gst_pipeline.release();
314   return true;
315 }
316
317 void TizenVideoDecodeAccelerator::Decode(
318     const media::BitstreamBuffer& bitstream_buffer) {
319   scoped_ptr<BitstreamBufferRef> buffer_ref;
320   scoped_ptr<base::SharedMemory> shm(
321       new base::SharedMemory(bitstream_buffer.handle(), true));
322
323   if (!shm->Map(bitstream_buffer.size())) {
324     LOG(ERROR) << __FUNCTION__ << " could not map bitstream_buffer";
325     NotifyError(media::VideoDecodeAccelerator::UNREADABLE_INPUT);
326     return;
327   }
328
329   buffer_ref.reset(new BitstreamBufferRef(
330       impl_->io_client_weak_factory_->GetWeakPtr(),
331       base::MessageLoopProxy::current(),
332       shm.release(),
333       bitstream_buffer.size(),
334       bitstream_buffer.id()));
335
336   if (!buffer_ref) {
337     return;
338   }
339
340   if (impl_->can_feed_ && !impl_->is_destroying_) {
341     impl_->gst_thread_.message_loop()->PostTask(
342         FROM_HERE,
343         base::Bind(&TizenVideoDecodeAccelerator::OnDecode,
344                    base::Unretained(this),
345                    base::Passed(&buffer_ref)));
346   }
347   else {
348     DVLOG(2) << __FUNCTION__
349              << " Frame drop on decoder:"
350              << " INPUT Q is FULL";
351   }
352 }
353
354 void TizenVideoDecodeAccelerator::AssignPictureBuffers(
355     const std::vector<media::PictureBuffer>& buffers) {
356   NOTIMPLEMENTED();
357 }
358
359 void TizenVideoDecodeAccelerator::ReusePictureBuffer(
360     int32 picture_buffer_id) {
361   NOTIMPLEMENTED();
362 }
363
364 void TizenVideoDecodeAccelerator::Flush() {
365   NOTIMPLEMENTED();
366 }
367
368 void TizenVideoDecodeAccelerator::Reset() {
369   NOTIMPLEMENTED();
370 }
371
372 void TizenVideoDecodeAccelerator::Destroy() {
373   if (impl_) {
374     if (impl_->gst_thread_.IsRunning()) {
375       impl_->gst_thread_.Stop();
376     }
377     gst_app_src_end_of_stream(GST_APP_SRC(impl_->appsrc_));
378     impl_->is_destroying_ = true;
379
380     if (impl_->pipeline_) {
381       gst_element_set_state(impl_->pipeline_, GST_STATE_NULL);
382       gst_object_unref(GST_OBJECT(impl_->pipeline_));
383     }
384     delete impl_;
385   }
386   delete this;
387 }
388
389 bool TizenVideoDecodeAccelerator::CanDecodeOnIOThread(){
390   return false;
391 }
392
393 void TizenVideoDecodeAccelerator::StartDecoder() {
394   gst_element_set_state(impl_->pipeline_, GST_STATE_PLAYING);
395 };
396
397 void TizenVideoDecodeAccelerator::OnDecode(
398     scoped_ptr<BitstreamBufferRef> buffer_ref) {
399   if (!buffer_ref) {
400     return;
401   }
402
403   if (!(buffer_ref->gst_buffer_ = gst_buffer_new())) {
404     return;
405   }
406
407   GST_BUFFER_MALLOCDATA(buffer_ref->gst_buffer_) =
408       reinterpret_cast<guint8*>(buffer_ref.get());
409   GST_BUFFER_FREE_FUNC(buffer_ref->gst_buffer_) = BitstreamBufferRef::Destruct;
410   GST_BUFFER_SIZE(buffer_ref->gst_buffer_) = buffer_ref->size_;
411   GST_BUFFER_DATA(buffer_ref->gst_buffer_) =
412       static_cast<guint8*>(buffer_ref->shm_->memory());
413
414   if (GST_FLOW_OK !=
415           gst_app_src_push_buffer(GST_APP_SRC(impl_->appsrc_),
416                                   buffer_ref->gst_buffer_)) {
417     LOG(ERROR) << __FUNCTION__ << " fail to push buffer into decoder pipeline";
418     return;
419   }
420
421   // lifecycle of buffer_ref will be handled by gstreamer.
422   buffer_ref.release();
423 }
424
425 void TizenVideoDecodeAccelerator::NotifyError(
426     media::VideoDecodeAccelerator::Error error) {
427   if (impl_->io_client_weak_factory_->GetWeakPtr()) {
428     impl_->io_client_weak_factory_->GetWeakPtr()->NotifyError(error);
429   }
430 }
431
432 }  // namespace content