[Tizen][WebRTC] Appsink implementation with fimcconvert
[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 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
19 #include "ui/gl/efl_pixmap.h"
20 #else
21 #include "base/process/process.h"
22 #endif
23
24 #if GST_VERSION_MAJOR == 1
25 #include <gst/video/videooverlay.h>
26 #else
27 #include <gst/interfaces/xoverlay.h>
28 #endif
29
30 using media::VideoFrame;
31
32 namespace {
33
34 struct GstElementDeleter {
35   void operator()(GstElement* ptr) const {
36     DCHECK(ptr != NULL);
37     gst_object_unref(ptr);
38   }
39 };
40
41 // Gstreamer elements and names.
42 const char* kDecoderName = "decoder";
43 #if GST_VERSION_MAJOR == 1
44 const char* kDecoderGstElement = "omxh264dec";
45 #else
46 const char* kDecoderGstElement = "omx_h264dec";
47 #endif
48
49 // Generating Unique Key from given width and height.
50 int32 ConvertWidthAndHeightToKey(int width, int height) {
51   return ((width << 16) | height);
52 }
53 } // namespace
54
55 namespace content {
56
57 enum {
58   MAX_BITRATE = 2000000,                 // bps.
59   INPUT_BUFFER_SIZE = MAX_BITRATE / 8,   // bytes. 1 sec for H.264 HD video.
60   ID_LAST = 0x3FFFFFFF,                  // wrap round ID after this
61 };
62
63 media::VideoDecodeAccelerator* CreateTizenVideoDecodeAccelerator() {
64   return new TizenVideoDecodeAccelerator();
65 }
66
67 struct TizenVideoDecodeAccelerator::BitstreamBufferRef {
68   BitstreamBufferRef(
69       base::WeakPtr<media::VideoDecodeAccelerator::Client> client,
70       const scoped_refptr<base::MessageLoopProxy>& client_message_loop_proxy,
71       base::SharedMemory* shm,
72       size_t size,
73       int32 input_id)
74       : client_(client),
75         client_message_loop_proxy_(client_message_loop_proxy),
76         shm_(shm),
77         size_(size),
78         bytes_used_(0),
79         input_id_(input_id),
80         gst_buffer_(NULL) {}
81
82   ~BitstreamBufferRef() {
83     if (input_id_ >= 0) {
84       client_message_loop_proxy_->PostTask(
85           FROM_HERE,
86           base::Bind(
87               &media::VideoDecodeAccelerator::Client::NotifyEndOfBitstreamBuffer,
88               client_,
89               input_id_));
90     }
91   }
92
93   static void Destruct(gpointer data) {
94     DCHECK(data != NULL);
95     BitstreamBufferRef* pRef = static_cast<BitstreamBufferRef*>(data);
96     delete pRef;
97   }
98
99   base::WeakPtr<media::VideoDecodeAccelerator::Client> client_;
100   scoped_refptr<base::MessageLoopProxy> client_message_loop_proxy_;
101   scoped_ptr<base::SharedMemory> shm_;
102   size_t size_;
103   off_t bytes_used_;
104   int32 input_id_;
105   GstBuffer* gst_buffer_;
106 };
107
108 struct TizenVideoDecodeAccelerator::Impl {
109   Impl()
110       : can_feed_(true),
111         is_destroying_(false),
112         pipeline_(NULL),
113         sink_(NULL),
114         appsrc_(NULL),
115         io_message_loop_proxy_(base::MessageLoopProxy::current()),
116         bitstream_buffer_id_(0),
117         gst_thread_("TizenDecoderThreadGst")
118 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
119         ,pixmap_id_(0),
120         gst_width_(0),
121         gst_height_(0),
122         damage_(0),
123         damage_handler_(NULL),
124         is_x_window_handle_set_(false)
125 #else
126         ,caps_width_(0),
127         caps_height_(0)
128 #endif
129 {
130 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
131   xpixmap_buffer_map_.clear();
132 #endif
133 }
134
135 #if !defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
136   static GstFlowReturn OnDecoded(GstAppSink* sink, gpointer app_data);
137   void DeliverVideoFrame(GstBuffer* buffer,
138                          int32 bitstream_buffer_id,
139                          gfx::Rect frame_size);
140   void CreateAppSinkElement();
141   static void OnSinkCapChanged(
142       GstPad* sink_pad, GParamSpec* gparamspec, void* user_data);
143 #endif
144
145   static GstBusSyncReply OnBusMessage(
146       GstBus* bus, GstMessage* msg, gpointer data) {
147     switch (GST_MESSAGE_TYPE(msg)) {
148       case GST_MESSAGE_ERROR: {
149         gchar* debug = NULL;
150         GError* error = NULL;
151         gst_message_parse_error(msg, &error, &debug);
152         LOG(ERROR) << __FUNCTION__
153                    << " GSTError happens from bus at "
154                    << GST_OBJECT_NAME(msg->src)
155                    << ":" << error->message;
156         LOG(ERROR) << __FUNCTION__
157                    << " Debugging Info: "
158                    << (debug != NULL ? debug : "none");
159         g_error_free(error);
160         g_free(debug);
161         break;
162       }
163 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
164       case GST_MESSAGE_ELEMENT: {
165         TizenVideoDecodeAccelerator::Impl* obj_impl =
166             static_cast<TizenVideoDecodeAccelerator::Impl*>(data);
167         if (obj_impl) {
168           if (obj_impl->IsXWindowHandleSet()) {
169 #if GST_VERSION_MAJOR == 1
170             if (gst_is_video_overlay_prepare_window_handle_message(msg)) {
171 #else
172             if (gst_structure_has_name(msg->structure, "prepare-xid")) {
173 #endif
174               obj_impl->OnXWindowIdPrepared(msg);
175               gst_message_unref(msg);
176               return GST_BUS_PASS;
177             }
178           }
179         } else {
180           LOG(ERROR) << __FUNCTION__ << "Accelerator is NULL";
181         }
182         break;
183       }
184 #endif
185       default:
186         break;
187     }
188     return GST_BUS_PASS;
189   }
190
191   static void StartFeed(GstAppSrc *source, guint size, gpointer app) {
192     DCHECK(source);
193     content::TizenVideoDecodeAccelerator::Impl* impl =
194         static_cast<content::TizenVideoDecodeAccelerator::Impl*>(app);
195     impl->can_feed_ = true;
196   }
197
198   static void StopFeed(GstAppSrc *source, gpointer app) {
199     DCHECK(source);
200     content::TizenVideoDecodeAccelerator::Impl* impl =
201         static_cast<content::TizenVideoDecodeAccelerator::Impl*>(app);
202     impl->can_feed_ = false;
203   }
204
205 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
206   bool IsXWindowHandleSet() const {return is_x_window_handle_set_;}
207   void OnXWindowIdPrepared(GstMessage* message);
208   void SetXWindowHandle(bool handle_set);
209   void SetPixmap(const int32& gst_width, const int32& gst_height);
210   void DeregisterDamageHandler();
211   static Eina_Bool OnSurfaceChanged(void* ptr_acc, int type, void* event);
212   static void OnSinkCapChanged(
213       GstPad* sink_pad, GParamSpec* gparamspec, void* user_data);
214 #endif
215
216   volatile bool can_feed_;
217   volatile bool is_destroying_;
218   GstElement* pipeline_;
219   GstElement* sink_;
220   GstElement* appsrc_;
221   scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
222   scoped_ptr<base::WeakPtrFactory<Client> > io_client_weak_factory_;
223   base::Thread gst_thread_;
224   int bitstream_buffer_id_;
225 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
226   scoped_refptr<gfx::EflPixmap> pixmap_surface_;
227   int pixmap_id_;
228   gint gst_width_;
229   gint gst_height_;
230   Ecore_X_Damage damage_;
231   Ecore_Event_Handler* damage_handler_;
232   bool is_x_window_handle_set_;
233   typedef std::map<int16, scoped_refptr<gfx::EflPixmap> > PixmapSurfaceTizenMap;
234   PixmapSurfaceTizenMap xpixmap_buffer_map_;
235 #else
236   int caps_width_;
237   int caps_height_;
238 #endif
239 };
240
241 TizenVideoDecodeAccelerator::TizenVideoDecodeAccelerator()
242     : impl_(NULL) {
243 }
244
245 TizenVideoDecodeAccelerator::~TizenVideoDecodeAccelerator() {
246 }
247
248 bool TizenVideoDecodeAccelerator::Initialize(
249     media::VideoCodecProfile profile,
250     Client* client) {
251   GError* error = NULL;
252   GstCaps* video_caps = NULL;
253   GstElement* gst_decoder = NULL;
254   GstElement* gst_parser = NULL;
255   GstBus* gst_bus = NULL;
256   GstPad* video_sink_pad = NULL;
257 #if !defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
258   GstElement* video_filter_ = NULL;
259   GstElement* gst_converter = NULL;
260 #endif
261   scoped_ptr<GstElement, GstElementDeleter> gst_pipeline;
262   static GstAppSrcCallbacks appsrc_callbacks =
263       {&Impl::StartFeed, &Impl::StopFeed, NULL};
264   CHECK(impl_ == NULL);
265   impl_ = new Impl();
266   impl_->io_client_weak_factory_.reset(
267       new base::WeakPtrFactory<Client>(client));
268
269   switch (profile) {
270     case media::H264PROFILE_BASELINE:
271       DVLOG(1) << "Initialize(): profile -> H264PROFILE_BASELINE";
272       break;
273     case media::H264PROFILE_MAIN:
274       DVLOG(1) << "Initialize(): profile -> H264PROFILE_MAIN";
275       break;
276     default:
277       LOG(ERROR) << "Initialize(): unsupported profile=" << profile;
278       return false;
279   };
280
281   if (!gst_is_initialized() && !gst_init_check(NULL, NULL, &error)) {
282     LOG(ERROR) << __FUNCTION__ << "cannot initialize gstreamer.";
283     g_error_free(error);
284     return false;
285   }
286
287   // pipeline initialization.
288   gst_pipeline.reset(gst_pipeline_new("h264_decode"));
289   if (!gst_pipeline) {
290     LOG(ERROR) << __FUNCTION__ << "cannot initialize gst pipeline.";
291     return false;
292   }
293   if (!(gst_bus = gst_pipeline_get_bus(GST_PIPELINE(gst_pipeline.get())))) {
294     return false;
295   }
296 #if GST_VERSION_MAJOR == 1
297   gst_bus_set_sync_handler(gst_bus, Impl::OnBusMessage, impl_, NULL);
298 #else
299   gst_bus_set_sync_handler(gst_bus, Impl::OnBusMessage, impl_);
300 #endif
301   gst_object_unref(gst_bus);
302
303   // appsrc initialization.
304   if (!(impl_->appsrc_ = gst_element_factory_make("appsrc", "src"))) {
305     LOG(ERROR) << __FUNCTION__ << "cannot initialize gst appsrc.";
306     return false;
307   }
308   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->appsrc_)) {
309     gst_object_unref(impl_->appsrc_);
310     impl_->appsrc_ = NULL;
311     return false;
312   }
313   gst_app_src_set_max_bytes(GST_APP_SRC(impl_->appsrc_), INPUT_BUFFER_SIZE);
314   gst_app_src_set_callbacks(GST_APP_SRC(impl_->appsrc_), &appsrc_callbacks,
315                             static_cast<gpointer>(impl_), NULL);
316   g_object_set(G_OBJECT(impl_->appsrc_),
317                "is-live", TRUE,
318                "block", FALSE,
319                "min-percent", 80, // if buffer below 80%, need-data emits.
320                "stream-type", GST_APP_STREAM_TYPE_STREAM,
321                NULL);
322   if (!(video_caps = gst_caps_from_string("video/x-h264,framerate=30/1"))) {
323     return false;
324   }
325   gst_app_src_set_caps(GST_APP_SRC(impl_->appsrc_), video_caps);
326   gst_caps_unref(video_caps);
327
328 #if defined(OS_TIZEN)
329   DVLOG(1) << "######################################";
330   DVLOG(1) << "      USING omx_h264dec DECODER " << (unsigned int)this;
331   DVLOG(1) << "######################################";
332
333   // parser initialization
334   if (!(gst_parser = gst_element_factory_make("h264parse", "h264parse"))) {
335     LOG(ERROR) << " cannot create h264parse.";
336     return false;
337   }
338   if(!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_parser)) {
339     LOG(ERROR) << " cannot add h264parse into decoder pipeline.";
340     gst_object_unref(gst_parser);
341     return false;
342   }
343
344   // decoder initialization.
345   if (!(gst_decoder = gst_element_factory_make(kDecoderGstElement, kDecoderName))) {
346     LOG(ERROR) << " cannot create " << kDecoderGstElement << ".";
347     return false;
348   }
349   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_decoder)) {
350     LOG(ERROR) << " cannot add " << kDecoderGstElement << " to pipeline.";
351     gst_object_unref(gst_decoder);
352     return false;
353   }
354 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
355   // sink initialization.
356   if (!(impl_->sink_ = gst_element_factory_make("xvimagesink", "xvimagesink"))) {
357     LOG(ERROR) << __FUNCTION__ << " cannot create xvimagesink.";
358     return false;
359   }
360   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->sink_)) {
361     gst_object_unref(impl_->sink_);
362     impl_->sink_ = NULL;
363     return false;
364   }
365   g_object_set(impl_->sink_, "rotate", 0, NULL);
366
367   if (!(video_sink_pad = gst_element_get_static_pad(impl_->sink_, "sink"))) {
368     return false;
369   }
370   g_signal_connect(video_sink_pad, "notify::caps",
371                    G_CALLBACK(impl_->OnSinkCapChanged),
372                    impl_);
373   impl_->SetXWindowHandle(false);
374   gst_object_unref(video_sink_pad);
375
376   // linking the elements.
377   if (!gst_element_link(impl_->appsrc_, gst_parser)) {
378     LOG(ERROR) << " Source and gst_parser could not be linked";
379     return false;
380   }
381
382   if (!gst_element_link(gst_parser, gst_decoder)) {
383     LOG(ERROR) << " gst_parser and Decoder could not be linked";
384     return false;
385   }
386   if (!gst_element_link(gst_decoder, impl_->sink_)) {
387     LOG(ERROR) << __FUNCTION__ << " Decoder and Sink could not be linked";
388     return false;
389   }
390 #else
391   impl_->CreateAppSinkElement();
392   if (!impl_->sink_) {
393     LOG(ERROR) << "Could not create and add appsink element";
394     return false;
395   }
396   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->sink_)) {
397     gst_object_unref(impl_->sink_);
398     impl_->sink_ = NULL;
399     return false;
400   }
401   if (!(gst_converter = gst_element_factory_make("fimcconvert", "cvt"))) {
402     LOG(ERROR) << __FUNCTION__ << " cannot create fimcconvert.";
403     return false;
404   }
405   if(!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_converter)) {
406     LOG(ERROR) << __FUNCTION__ << " cannot add fimcconvert into pipeline.";
407     gst_object_unref(gst_converter);
408     return false;
409   }
410   if (!(video_filter_ = gst_element_factory_make("capsfilter", "VideoFilter"))) {
411     LOG(ERROR) << __FUNCTION__ << " cannot create capsfilter.";
412     return false;
413   }
414   if(!gst_bin_add(GST_BIN(gst_pipeline.get()), video_filter_)) {
415     LOG(ERROR) << __FUNCTION__ << " cannot add videoFilter into pipeline.";
416     gst_object_unref(video_filter_);
417     return false;
418   }
419
420   // FIXME: SONAL
421   // OnSinkCapChanged callback is not coming for Appsink implementation
422   if (!(video_sink_pad =
423           gst_element_get_static_pad(impl_->sink_, "sink"))) {
424     LOG(ERROR) << "Could not create video sink pad";
425     return false;
426   }
427   g_signal_connect(
428       video_sink_pad, "notify::caps",
429       G_CALLBACK(&Impl::OnSinkCapChanged), impl_);
430   gst_object_unref(video_sink_pad);
431   if (!gst_element_link_many(impl_->appsrc_,
432                              gst_parser,
433                              gst_decoder,
434                              gst_converter,
435                              video_filter_,
436                              impl_->sink_,
437                              NULL)) {
438     LOG(ERROR) << __FUNCTION__ << " cannot link some elements in decode pipeline";
439     return false;
440   }
441   g_object_set(G_OBJECT(video_filter_),
442                "caps",
443                gst_caps_new_simple("video/x-raw","format", G_TYPE_STRING, "I420",NULL),
444                NULL);
445 #endif
446 #else
447   DVLOG(1) << "######################################";
448   DVLOG(1) << "      USING ffdec_h264 DECODER";
449   DVLOG(1) << "######################################";
450   GstElement* gst_colorspace = NULL;
451
452   // decoder initialization
453   if (!(gst_decoder = gst_element_factory_make("ffdec_h264", "H264-decoder"))) {
454     LOG(ERROR) << __FUNCTION__ << " cannot create ffdec_h264.";
455     return false;
456   }
457   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_decoder)) {
458     gst_object_unref(gst_decoder);
459     return false;
460   }
461
462   // colorspace initialization
463   if (!(gst_colorspace = gst_element_factory_make("ffmpegcolorspace", "cs"))) {
464     LOG(ERROR) << __FUNCTION__ << " cannot create ffmpegcolorspace.";
465     return false;
466   }
467   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_colorspace)) {
468     gst_object_unref(gst_colorspace);
469     return false;
470   }
471
472   if (!(impl_->sink_ = gst_element_factory_make("autovideosink", "sink"))) {
473     LOG(ERROR) << __FUNCTION__ << " cannot create autovideosink.";
474     return false;
475   }
476   if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->sink_)) {
477     gst_object_unref(impl_->sink_);
478     impl_->sink_ = NULL;
479     return false;
480   }
481
482   if(!gst_element_link_many(impl_->appsrc_, gst_decoder, gst_colorspace,
483                             impl_->sink_, NULL)) {
484     LOG(ERROR) << __FUNCTION__ << " Some element could not be linked";
485     return false;
486   }
487 #endif
488   if (!impl_->gst_thread_.Start()) {
489     LOG(ERROR) << __FUNCTION__ << " gst_thread_ failed to start";
490     return false;
491   }
492
493   impl_->gst_thread_.message_loop()->PostTask(
494       FROM_HERE,
495       base::Bind(&TizenVideoDecodeAccelerator::StartDecoder,
496       base::Unretained(this)));
497
498   GST_DEBUG_BIN_TO_DOT_FILE(
499       GST_BIN(gst_pipeline.get()), GST_DEBUG_GRAPH_SHOW_ALL, "decoder_graph.dot");
500
501   impl_->pipeline_ = gst_pipeline.release();
502   return true;
503 }
504
505 void TizenVideoDecodeAccelerator::Decode(
506     const media::BitstreamBuffer& bitstream_buffer) {
507   scoped_ptr<BitstreamBufferRef> buffer_ref;
508   scoped_ptr<base::SharedMemory> shm(
509       new base::SharedMemory(bitstream_buffer.handle(), true));
510
511   if (!shm->Map(bitstream_buffer.size())) {
512     LOG(ERROR) << __FUNCTION__ << " could not map bitstream_buffer";
513     NotifyError(media::VideoDecodeAccelerator::UNREADABLE_INPUT);
514     return;
515   }
516
517   buffer_ref.reset(new BitstreamBufferRef(
518       impl_->io_client_weak_factory_->GetWeakPtr(),
519       base::MessageLoopProxy::current(),
520       shm.release(),
521       bitstream_buffer.size(),
522       bitstream_buffer.id()));
523
524   if (!buffer_ref) {
525     return;
526   }
527
528   if (impl_->can_feed_ && !impl_->is_destroying_) {
529     impl_->gst_thread_.message_loop()->PostTask(
530         FROM_HERE,
531         base::Bind(&TizenVideoDecodeAccelerator::OnDecode,
532                    base::Unretained(this),
533                    base::Passed(&buffer_ref)));
534   } else {
535     DVLOG(2) << __FUNCTION__
536              << " Frame drop on decoder:"
537              << " INPUT Q is FULL";
538   }
539 }
540
541 void TizenVideoDecodeAccelerator::AssignPictureBuffers(
542     const std::vector<media::PictureBuffer>& buffers) {
543   NOTIMPLEMENTED();
544 }
545
546 void TizenVideoDecodeAccelerator::ReusePictureBuffer(
547     int32 picture_buffer_id) {
548   NOTIMPLEMENTED();
549 }
550
551 void TizenVideoDecodeAccelerator::Flush() {
552   NOTIMPLEMENTED();
553 }
554
555 void TizenVideoDecodeAccelerator::Reset() {
556   NOTIMPLEMENTED();
557 }
558
559 void TizenVideoDecodeAccelerator::Destroy() {
560   if (impl_ != NULL) {
561 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
562     impl_->SetXWindowHandle(false);
563     impl_->DeregisterDamageHandler();
564     impl_->xpixmap_buffer_map_.clear();
565 #endif
566     if (impl_->gst_thread_.IsRunning()) {
567       impl_->gst_thread_.Stop();
568     }
569     gst_app_src_end_of_stream(GST_APP_SRC(impl_->appsrc_));
570     impl_->is_destroying_ = true;
571     if (impl_->pipeline_) {
572       gst_element_set_state(impl_->pipeline_, GST_STATE_NULL);
573       gst_object_unref(GST_OBJECT(impl_->pipeline_));
574     }
575     delete impl_;
576     impl_ = NULL;
577   }
578   delete this;
579 }
580
581 bool TizenVideoDecodeAccelerator::CanDecodeOnIOThread(){
582   return false;
583 }
584
585 void TizenVideoDecodeAccelerator::StartDecoder() {
586   gst_element_set_state(impl_->pipeline_, GST_STATE_PLAYING);
587 };
588
589 #if !defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
590 void TizenVideoDecodeAccelerator::Impl::OnSinkCapChanged(
591       GstPad* sink_pad, GParamSpec* gparamspec, void* user_data) {
592   content::TizenVideoDecodeAccelerator::Impl* impl =
593   static_cast<TizenVideoDecodeAccelerator::Impl*>(user_data);
594   int width = 0, height = 0;
595 #if GST_VERSION_MAJOR == 1
596   GstCaps* caps = gst_pad_get_current_caps(GST_PAD(sink_pad));
597   if (caps) {
598     GstVideoInfo info;
599     gst_video_info_init(&info);
600     if (gst_video_info_from_caps(&info, caps)) {
601       if ((impl->caps_width_ != width) || (impl->caps_height_ != height)) {
602         impl->caps_width_ = info.width;
603         impl->caps_height_ = info.height;
604       }
605     }
606   }
607 #else
608   if (gst_video_get_size(sink_pad, &width, &height)) {
609     if ((impl->caps_width_ != width) || (impl->caps_height_ != height)) {
610       impl->caps_width_ = width;
611       impl->caps_height_ = height;
612     }
613   }
614 #endif
615 }
616
617 GstFlowReturn TizenVideoDecodeAccelerator::Impl::OnDecoded(
618     GstAppSink* sink, gpointer app_data) {
619   GstBuffer* gst_output_buf = NULL;
620   content::TizenVideoDecodeAccelerator::Impl* self =
621       static_cast<TizenVideoDecodeAccelerator::Impl*>(app_data);
622   // FIXME: SONAL
623   // Once OnSinkCapChanged callback startes coming dont find height
624   // and width for all buffers, move this code under if block
625 #if GST_VERSION_MAJOR == 1
626   GstSample* sample = gst_app_sink_pull_sample(GST_APP_SINK(sink));
627   gst_output_buf = gst_sample_get_buffer(sample);
628   GstMapInfo map;
629   if (!gst_buffer_map(gst_output_buf, &map, GST_MAP_READ))
630     LOG (ERROR) << "Decoded Buffer contains invalid or no info!";
631   GstCaps* caps = gst_sample_get_caps(sample);
632 #else
633     gst_output_buf = gst_app_sink_pull_buffer(GST_APP_SINK(sink));
634     GstCaps* caps = gst_buffer_get_caps(GST_BUFFER(gst_output_buf));
635 #endif
636   if (!self->caps_width_ || !self->caps_height_) {
637     if (!caps) {
638       LOG(ERROR) << __FUNCTION__ << "Could not fetch caps from buffer";
639       gst_buffer_unref(gst_output_buf);
640       return GST_FLOW_ERROR;
641     } else {
642       // No need to unref |GstStructure|
643       const GstStructure* str = gst_caps_get_structure(caps, 0);
644       if (!str) {
645         gst_buffer_unref(gst_output_buf);
646         gst_caps_unref(caps);
647         return GST_FLOW_ERROR;
648       }
649       if (!gst_structure_get_int(str, "width", &self->caps_width_) ||
650           !gst_structure_get_int(str, "height", &self->caps_height_)) {
651         LOG(ERROR) << "Buffer information could not be obtained";
652         gst_buffer_unref(gst_output_buf);
653         gst_caps_unref(caps);
654         return GST_FLOW_ERROR;
655       }
656       gst_caps_unref(caps);
657     }
658   }
659
660   if (gst_output_buf) {
661 #if GST_VERSION_MAJOR == 1
662     if (map.data) {
663 #else
664     if (gst_output_buf->data) {
665 #endif
666       gfx::Rect frame_size =
667           gfx::Rect(self->caps_width_, self->caps_height_);
668       self->gst_thread_.message_loop()->PostTask(
669           FROM_HERE,
670           base::Bind(&TizenVideoDecodeAccelerator::Impl::DeliverVideoFrame,
671                       base::Unretained(self),
672                       gst_output_buf,
673                       self->bitstream_buffer_id_,
674                       frame_size));
675       self->bitstream_buffer_id_ = (self->bitstream_buffer_id_ + 1) & ID_LAST;
676     }
677   } else {
678     gst_buffer_unref(gst_output_buf);
679 #if GST_VERSION_MAJOR == 1
680     gst_sample_unref(sample);
681 #endif
682     LOG(ERROR) << __FUNCTION__
683                << " DECODING FRAME FAILED : frame_id"
684                << self->bitstream_buffer_id_;
685   }
686 #if GST_VERSION_MAJOR == 1
687   gst_buffer_unmap(gst_output_buf, &map);
688 #endif
689   return GST_FLOW_OK;
690 }
691
692
693 void TizenVideoDecodeAccelerator::Impl::CreateAppSinkElement() {
694   GstAppSinkCallbacks appsink_callbacks =
695       {NULL, NULL, &OnDecoded, NULL};
696
697   if (!(sink_ = gst_element_factory_make("appsink", "sink"))) {
698     LOG(ERROR) << __FUNCTION__ << "Appsink could not be created";
699     return;
700   }
701   gst_app_sink_set_callbacks(GST_APP_SINK(sink_),
702                              &appsink_callbacks,
703                              static_cast<gpointer>(this),
704                              NULL);
705   gst_app_sink_set_max_buffers(GST_APP_SINK(sink_), 1);
706 }
707
708 void TizenVideoDecodeAccelerator::Impl::DeliverVideoFrame(
709     GstBuffer* buffer,
710     int32 bitstream_buffer_id,
711     gfx::Rect frame_size) {
712   base::SharedMemory shared_memory;
713   base::SharedMemoryHandle shared_memory_handle;
714
715 #if GST_VERSION_MAJOR == 1
716   GstMapInfo map;
717   if (!gst_buffer_map(buffer, &map, GST_MAP_READ)) {
718       LOG (ERROR) << "Encoded Buffer contains invalid or no info.!";
719       return;
720   }
721   uint32 buffer_size = map.size;
722 #else
723   uint32 buffer_size = buffer->size;
724 #endif
725   if (!shared_memory.CreateAndMapAnonymous(buffer_size)) {
726     LOG (ERROR) << "Shared Memory creation failed.";
727   } else {
728     if (!shared_memory.ShareToProcess(base::GetCurrentProcessHandle(),
729                                       &shared_memory_handle)) {
730       LOG(ERROR) << __FUNCTION__ << "Could not get handle of Shared Memory";
731     } else {
732       memcpy(shared_memory.memory(),
733 #if GST_VERSION_MAJOR == 1
734                    map.data,
735 #else
736                   GST_BUFFER_DATA(buffer),
737 #endif
738              buffer_size);
739       io_message_loop_proxy_->PostTask(
740           FROM_HERE,
741           base::Bind(&media::VideoDecodeAccelerator::Client::NotifyDecodeDone,
742                       io_client_weak_factory_->GetWeakPtr(),
743                       shared_memory_handle,
744                       bitstream_buffer_id_,
745                       buffer_size,
746                       frame_size));
747     }
748   }
749 #if GST_VERSION_MAJOR == 1
750   gst_buffer_unmap(buffer, &map);
751 #endif
752   gst_buffer_unref(buffer);
753 }
754 #endif
755
756 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
757 void TizenVideoDecodeAccelerator::Impl::OnXWindowIdPrepared(
758     GstMessage* message) {
759 #if GST_VERSION_MAJOR == 1
760   const GstStructure* structure = gst_message_get_structure(message);
761   gst_structure_get_int(structure, "video-width", &gst_width_);
762   gst_structure_get_int(structure, "video-height", &gst_height_);
763 #else
764   gst_structure_get_int(message->structure, "video-width", &gst_width_);
765   gst_structure_get_int(message->structure, "video-height", &gst_height_);
766 #endif
767   SetPixmap(gst_width_, gst_height_);
768   SetXWindowHandle(true);
769 }
770
771 void TizenVideoDecodeAccelerator::Impl::SetXWindowHandle(
772     bool handle_set) {
773   is_x_window_handle_set_ = handle_set;
774 }
775
776 void TizenVideoDecodeAccelerator::Impl::SetPixmap(
777     const int32& gst_width, const int32& gst_height) {
778   int32 key_wh = ConvertWidthAndHeightToKey(gst_width, gst_height);
779   PixmapSurfaceTizenMap::iterator it = xpixmap_buffer_map_.find(key_wh);
780   if (it != xpixmap_buffer_map_.end()) {
781     pixmap_surface_ = it->second;
782     pixmap_id_ = pixmap_surface_->GetId();
783   } else {
784     pixmap_surface_ =
785         gfx::EflPixmap::Create(gfx::EflPixmapBase::UsageType::SURFACE,
786                                gfx::Size(gst_width, gst_height));
787     if (pixmap_surface_.get() == NULL) {
788       LOG(ERROR) << __FUNCTION__ << "Failed to create pixmap Surface";
789       return;
790     }
791     pixmap_id_ = pixmap_surface_->GetId();
792     xpixmap_buffer_map_[key_wh] = pixmap_surface_;
793   }
794   gst_width_ = gst_width;
795   gst_height_ = gst_height;
796   DeregisterDamageHandler();
797
798   // Register to get notification from ecore for damage updates.
799   damage_ = ecore_x_damage_new(pixmap_id_,
800                                ECORE_X_DAMAGE_REPORT_RAW_RECTANGLES);
801   damage_handler_ = ecore_event_handler_add(ECORE_X_EVENT_DAMAGE_NOTIFY,
802                                             OnSurfaceChanged,
803                                             this);
804 #if GST_VERSION_MAJOR == 1
805   gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(sink_), pixmap_id_);
806 #else
807   gst_x_overlay_set_window_handle(GST_X_OVERLAY(sink_), pixmap_id_);
808 #endif
809 }
810
811 void TizenVideoDecodeAccelerator::Impl::DeregisterDamageHandler() {
812   if (damage_) {
813     ecore_x_damage_free(damage_);
814     damage_ = 0;
815   }
816   if (damage_handler_) {
817     ecore_event_handler_del(damage_handler_);
818     damage_handler_ = NULL;
819   }
820 }
821
822 // Callback received when pixmap surface is changed/damaged
823 Eina_Bool TizenVideoDecodeAccelerator::Impl::OnSurfaceChanged(void* ptr_acc,
824                                                               int type,
825                                                               void* event) {
826   TizenVideoDecodeAccelerator::Impl* self =
827       static_cast<TizenVideoDecodeAccelerator::Impl*>(ptr_acc);
828
829   if (self) {
830     media::Picture picture(self->pixmap_id_,
831                            self->bitstream_buffer_id_,
832                            gfx::Rect(self->gst_width_, self->gst_height_));
833
834     self->io_message_loop_proxy_->PostTask(
835         FROM_HERE,
836         base::Bind(&media::VideoDecodeAccelerator::Client::PictureReady,
837                    self->io_client_weak_factory_->GetWeakPtr(),
838                    picture) );
839     self->bitstream_buffer_id_ = (self->bitstream_buffer_id_ + 1) & ID_LAST;
840   } else {
841     LOG(ERROR) << __FUNCTION__ << "Accelerator is NULL";
842     return ECORE_CALLBACK_CANCEL;
843   }
844   return ECORE_CALLBACK_PASS_ON;
845 }
846
847 void TizenVideoDecodeAccelerator::Impl::OnSinkCapChanged(
848     GstPad* sink_pad, GParamSpec* gparamspec,void* user_data) {
849   TizenVideoDecodeAccelerator::Impl* self =
850       static_cast<TizenVideoDecodeAccelerator::Impl*>(user_data);
851   if (!self) {
852     LOG(ERROR) << __FUNCTION__ << "Accelerator is NULL";
853     return;
854   }
855
856   int width = 0, height = 0;
857 #if GST_VERSION_MAJOR == 1
858   GstCaps* caps = gst_pad_get_current_caps(GST_PAD(sink_pad));
859   if (caps) {
860     GstVideoInfo info;
861     gst_video_info_init(&info);
862
863     if (gst_video_info_from_caps(&info, caps)) {
864       width = info.width;
865       height = info.height;
866       if ((self->gst_width_ != width) || (self->gst_height_ != height)) {
867         self->SetPixmap(width, height);
868       }
869     }
870   }
871 #else
872   if (gst_video_get_size(sink_pad, &width, &height)) {
873     if ((self->gst_width_ != width) || (self->gst_height_ != height)) {
874       self->SetPixmap(width, height);
875     }
876   }
877 #endif
878 }
879 #endif
880
881 void TizenVideoDecodeAccelerator::OnDecode(
882     scoped_ptr<BitstreamBufferRef> buffer_ref) {
883   if (!buffer_ref) {
884     return;
885   }
886 #if GST_VERSION_MAJOR == 1
887   buffer_ref->gst_buffer_ =
888       gst_buffer_new_wrapped_full(GST_MEMORY_FLAG_READONLY,
889                                   static_cast<guint8*>(buffer_ref->shm_->memory()),
890                                   buffer_ref->size_,
891                                   0,
892                                   buffer_ref->size_,
893                                   reinterpret_cast<guint8*>(buffer_ref.get()),
894                                   BitstreamBufferRef::Destruct);
895   if (!buffer_ref->gst_buffer_ || !GST_IS_BUFFER(buffer_ref->gst_buffer_)) {
896     LOG(ERROR) << " gst_buffer_new_wrapped_full failed to allocate memory.!";
897     return;
898   }
899 #else
900   if (!(buffer_ref->gst_buffer_ = gst_buffer_new())) {
901     return;
902   }
903
904   GST_BUFFER_MALLOCDATA(buffer_ref->gst_buffer_) =
905       reinterpret_cast<guint8*>(buffer_ref.get());
906   GST_BUFFER_FREE_FUNC(buffer_ref->gst_buffer_) = BitstreamBufferRef::Destruct;
907   GST_BUFFER_SIZE(buffer_ref->gst_buffer_) = buffer_ref->size_;
908   GST_BUFFER_DATA(buffer_ref->gst_buffer_) =
909       static_cast<guint8*>(buffer_ref->shm_->memory());
910 #endif
911   if (GST_FLOW_OK !=
912           gst_app_src_push_buffer(GST_APP_SRC(impl_->appsrc_),
913                                   buffer_ref->gst_buffer_)) {
914     LOG(ERROR) << __FUNCTION__ << " fail to push buffer into decoder pipeline";
915     return;
916   }
917
918   // lifecycle of buffer_ref will be handled by gstreamer.
919   buffer_ref.release();
920 }
921
922 void TizenVideoDecodeAccelerator::NotifyError(
923     media::VideoDecodeAccelerator::Error error) {
924   if (impl_->io_client_weak_factory_->GetWeakPtr()) {
925     impl_->io_client_weak_factory_->GetWeakPtr()->NotifyError(error);
926   }
927 }
928
929 }  // namespace content
930