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"
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>
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 using media::VideoFrame;
23 struct GstElementDeleter {
24 void operator()(GstElement* ptr) const {
26 gst_object_unref(ptr);
35 INPUT_BUFFER_SIZE = 319488, // bytes. 1 sec for H.264 HD video.
38 // Generating Unique Key from given width and height.
39 int32 ConvertWidthAndHeightToKey(int width, int height) {
40 return ((width << 16) | height);
43 media::VideoDecodeAccelerator* CreateTizenVideoDecodeAccelerator() {
44 return new TizenVideoDecodeAccelerator();
47 struct TizenVideoDecodeAccelerator::BitstreamBufferRef {
49 base::WeakPtr<media::VideoDecodeAccelerator::Client> client,
50 const scoped_refptr<base::MessageLoopProxy>& client_message_loop_proxy,
51 base::SharedMemory* shm,
55 client_message_loop_proxy_(client_message_loop_proxy),
62 ~BitstreamBufferRef() {
64 client_message_loop_proxy_->PostTask(
67 &media::VideoDecodeAccelerator::Client::NotifyEndOfBitstreamBuffer,
73 static void Destruct(gpointer data) {
75 BitstreamBufferRef* pRef = static_cast<BitstreamBufferRef*>(data);
79 base::WeakPtr<media::VideoDecodeAccelerator::Client> client_;
80 scoped_refptr<base::MessageLoopProxy> client_message_loop_proxy_;
81 scoped_ptr<base::SharedMemory> shm_;
85 GstBuffer* gst_buffer_;
88 struct TizenVideoDecodeAccelerator::Impl {
91 is_destroying_(false),
95 io_message_loop_proxy_(base::MessageLoopProxy::current()),
96 gst_thread_("TizenDecoderThreadGst") {}
98 static GstBusSyncReply OnBusMessage(
99 GstBus *bus, GstMessage *msg, gpointer data) {
100 switch (GST_MESSAGE_TYPE(msg)) {
101 case GST_MESSAGE_EOS:
103 case GST_MESSAGE_ERROR: {
105 GError *error = NULL;
106 gst_message_parse_error(msg, &error, &debug);
108 LOG(ERROR) << __FUNCTION__
109 << "Error Message Received in Gst Decoder Bus: "
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;
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;
132 volatile bool can_feed_;
133 volatile bool is_destroying_;
134 GstElement* pipeline_;
137 scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_;
138 scoped_ptr<base::WeakPtrFactory<Client> > io_client_weak_factory_;
139 base::Thread gst_thread_;
142 TizenVideoDecodeAccelerator::TizenVideoDecodeAccelerator()
146 TizenVideoDecodeAccelerator::~TizenVideoDecodeAccelerator() {
149 bool TizenVideoDecodeAccelerator::Initialize(
150 media::VideoCodecProfile profile,
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 );
161 impl_->io_client_weak_factory_.reset(
162 new base::WeakPtrFactory<Client>(client));
165 case media::H264PROFILE_BASELINE:
166 DVLOG(1) << "Initialize(): profile -> H264PROFILE_BASELINE";
168 case media::H264PROFILE_MAIN:
169 DVLOG(1) << "Initialize(): profile -> H264PROFILE_MAIN";
172 LOG(ERROR) << "Initialize(): unsupported profile=" << profile;
176 if (!gst_is_initialized() && !gst_init_check(NULL, NULL, &error)) {
177 LOG(ERROR) << __FUNCTION__ << "cannot initialize gstreamer.";
183 gst_pipeline.reset(gst_pipeline_new("h264_decode"));
185 LOG(ERROR) << __FUNCTION__ << "cannot initialize gst pipeline.";
188 // Add a message handler
189 if (!(gst_bus = gst_pipeline_get_bus(GST_PIPELINE(gst_pipeline.get())))) {
192 gst_bus_set_sync_handler(gst_bus, Impl::OnBusMessage, impl_);
193 gst_object_unref(gst_bus);
196 if (!(impl_->appsrc_ = gst_element_factory_make("appsrc", "src"))) {
197 LOG(ERROR) << __FUNCTION__ << "cannot initialize gst appsrc.";
200 if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->appsrc_)) {
201 gst_object_unref(impl_->appsrc_);
202 impl_->appsrc_ = NULL;
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_),
211 "min-percent", 80, // if buffer below 80%, need-data emits.
212 "stream-type", GST_APP_STREAM_TYPE_STREAM,
214 if (!(video_caps = gst_caps_from_string("video/x-h264,framerate=30/1"))) {
217 g_object_set(G_OBJECT(impl_->appsrc_), "caps", video_caps, NULL);
218 gst_caps_unref(video_caps);
220 #if defined(OS_TIZEN)
221 DVLOG(1) << "######################################";
222 DVLOG(1) << " USING omx_h264dec DECODER " << (unsigned int)this;
223 DVLOG(1) << "######################################";
225 if (!(gst_decoder = gst_element_factory_make("omx_h264dec", "decoder"))) {
226 LOG(ERROR) << __FUNCTION__ << " cannot create omx_h264dec.";
229 if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_decoder)) {
230 gst_object_unref(gst_decoder);
235 if (!(impl_->sink_ = gst_element_factory_make("xvimagesink", "xvimagesink"))) {
236 LOG(ERROR) << __FUNCTION__ << " cannot create xvimagesink.";
239 if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->sink_)) {
240 gst_object_unref(impl_->sink_);
244 g_object_set(impl_->sink_, "rotate", 0, NULL);
247 if (!gst_element_link(impl_->appsrc_, gst_decoder)) {
248 LOG(ERROR) << __FUNCTION__ << " Source and Decoder could not be linked";
251 if (!gst_element_link(gst_decoder, impl_->sink_)) {
252 LOG(ERROR) << __FUNCTION__ << " Decoder and Sink could not be linked";
257 DVLOG(1) << "######################################";
258 DVLOG(1) << " USING ffdec_h264 DECODER";
259 DVLOG(1) << "######################################";
260 GstElement* gst_colorspace = NULL;
263 if (!(gst_decoder = gst_element_factory_make("ffdec_h264", "H264-decoder"))) {
264 LOG(ERROR) << __FUNCTION__ << " cannot create ffdec_h264.";
267 if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_decoder)) {
268 gst_object_unref(gst_decoder);
273 if (!(gst_colorspace = gst_element_factory_make("ffmpegcolorspace", "cs"))) {
274 LOG(ERROR) << __FUNCTION__ << " cannot create ffmpegcolorspace.";
277 if (!gst_bin_add(GST_BIN(gst_pipeline.get()), gst_colorspace)) {
278 gst_object_unref(gst_colorspace);
282 if (!(impl_->sink_ = gst_element_factory_make("autovideosink", "sink"))) {
283 LOG(ERROR) << __FUNCTION__ << " cannot create autovideosink.";
286 if (!gst_bin_add(GST_BIN(gst_pipeline.get()), impl_->sink_)) {
287 gst_object_unref(impl_->sink_);
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";
298 if (!impl_->gst_thread_.Start()) {
299 LOG(ERROR) << __FUNCTION__ << " gst_thread_ failed to start";
303 impl_->gst_thread_.message_loop()->PostTask(
305 base::Bind(&TizenVideoDecodeAccelerator::StartDecoder,
306 base::Unretained(this)));
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");
313 impl_->pipeline_ = gst_pipeline.release();
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));
323 if (!shm->Map(bitstream_buffer.size())) {
324 LOG(ERROR) << __FUNCTION__ << " could not map bitstream_buffer";
325 NotifyError(media::VideoDecodeAccelerator::UNREADABLE_INPUT);
329 buffer_ref.reset(new BitstreamBufferRef(
330 impl_->io_client_weak_factory_->GetWeakPtr(),
331 base::MessageLoopProxy::current(),
333 bitstream_buffer.size(),
334 bitstream_buffer.id()));
340 if (impl_->can_feed_ && !impl_->is_destroying_) {
341 impl_->gst_thread_.message_loop()->PostTask(
343 base::Bind(&TizenVideoDecodeAccelerator::OnDecode,
344 base::Unretained(this),
345 base::Passed(&buffer_ref)));
348 DVLOG(2) << __FUNCTION__
349 << " Frame drop on decoder:"
350 << " INPUT Q is FULL";
354 void TizenVideoDecodeAccelerator::AssignPictureBuffers(
355 const std::vector<media::PictureBuffer>& buffers) {
359 void TizenVideoDecodeAccelerator::ReusePictureBuffer(
360 int32 picture_buffer_id) {
364 void TizenVideoDecodeAccelerator::Flush() {
368 void TizenVideoDecodeAccelerator::Reset() {
372 void TizenVideoDecodeAccelerator::Destroy() {
374 if (impl_->gst_thread_.IsRunning()) {
375 impl_->gst_thread_.Stop();
377 gst_app_src_end_of_stream(GST_APP_SRC(impl_->appsrc_));
378 impl_->is_destroying_ = true;
380 if (impl_->pipeline_) {
381 gst_element_set_state(impl_->pipeline_, GST_STATE_NULL);
382 gst_object_unref(GST_OBJECT(impl_->pipeline_));
389 bool TizenVideoDecodeAccelerator::CanDecodeOnIOThread(){
393 void TizenVideoDecodeAccelerator::StartDecoder() {
394 gst_element_set_state(impl_->pipeline_, GST_STATE_PLAYING);
397 void TizenVideoDecodeAccelerator::OnDecode(
398 scoped_ptr<BitstreamBufferRef> buffer_ref) {
403 if (!(buffer_ref->gst_buffer_ = gst_buffer_new())) {
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());
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";
421 // lifecycle of buffer_ref will be handled by gstreamer.
422 buffer_ref.release();
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);
432 } // namespace content