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 "media/base/tizen/media_source_player_gstreamer.h"
7 #include <gst/app/gstappsink.h>
8 #include <gst/app/gstappsrc.h>
9 #include <gst/interfaces/xoverlay.h>
10 #include <gst/video/video.h>
12 #include "base/process/process.h"
13 #include "media/base/tizen/media_player_manager_tizen.h"
15 #if defined(OS_TIZEN_MOBILE) && (defined(TIZEN_V_2_3) || defined(TIZEN_V_2_4))
16 #include <device/power.h>
21 const uint32 GST_VIDEO_SN12 = GST_MAKE_FOURCC('S','N','1','2');
23 const uint SN12_TILE_WIDTH = 64;
24 const uint SN12_TILE_HEIGHT = 32;
25 const uint SN12_TILE_SIZE = SN12_TILE_WIDTH * SN12_TILE_HEIGHT;
27 // Pipeline element name
28 const char* kPipelineName = "gst_pipeline";
30 // Update duration every 100ms.
31 const int kDurationUpdateInterval = 100;
33 // For smooth playback, seeking will be done to I-Frame + kSixteenMilliSeconds
34 // Reason to choose kSixteenMilliSeconds is duration of each video frame at
35 // 60 fps video will be ~16 milliseconds.
36 const int64 kSixteenMilliSeconds = 16000000;
38 const char *h264elements[] = {
47 const char *aacelements[] = {
49 #if defined(OS_TIZEN_MOBILE)
50 , "ffdec_aac", "autoaudiosink"
51 #elif defined(OS_TIZEN_TV)
52 , "omx_aacdec", "alsasink"
54 , "faad", "autoaudiosink"
58 // FIXME: This is derived from command-line pipeline on desktop and mobile.
59 // Need to find the pipeline on TV.
60 // Also the connection might be different when converting it to code.
61 const char *mp3elements[] = {
67 const AudioCodecGstElementsMapping AudioMapping[] = {
68 {media::kCodecAAC, aacelements},
69 {media::kCodecMP3, mp3elements},
70 {media::kUnknownAudioCodec, NULL}
73 const VideoCodecGstElementsMapping VideoMapping[] = {
74 {media::kCodecH264, h264elements},
75 {media::kUnknownVideoCodec, NULL}
78 GstClockTime ConvertToGstClockTime(double time) {
80 LOG(ERROR) << "Invalid time:" << time << " Reset to 0";
84 // Extract the integer part of the time (seconds) and the fractional part
85 // (microseconds). Attempt to round the microseconds so no floating point
86 // precision is lost and we can perform an accurate seek.
88 double microSeconds = std::modf(time, &seconds) * 1000000;
90 timeValue.tv_sec = static_cast<glong>(seconds);
91 timeValue.tv_usec = static_cast<glong>(lround(microSeconds / 10) * 10);
92 return GST_TIMEVAL_TO_TIME(timeValue);
95 double ConvertNanoSecondsToSeconds(int64 time) {
96 double seconds = static_cast<double>(time) /
97 (base::Time::kNanosecondsPerSecond);
105 static GstBusSyncReply gst_pipeline_message_cb(
108 gpointer user_data) {
109 MediaSourcePlayerGstreamer* player =
110 static_cast<MediaSourcePlayerGstreamer*>(user_data);
111 if (!player || player->IsPlayerDestructing())
114 player->HandleMessage(message);
115 gst_message_unref(message);
119 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
120 static Eina_Bool notify_damage_updated_cb(
124 MediaSourcePlayerGstreamer* player =
125 static_cast<MediaSourcePlayerGstreamer*>(user_data);
126 if (!player || player->IsPlayerDestructing())
127 return ECORE_CALLBACK_PASS_ON;
128 player->PlatformSurfaceUpdated();
129 return ECORE_CALLBACK_PASS_ON;
133 #if defined(OS_TIZEN_TV)
134 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
135 static int get_pixmap_id_cb(void* user_data) {
136 MediaSourcePlayerGstreamer* player =
137 static_cast<MediaSourcePlayerGstreamer*>(user_data);
138 return player->GetSurfaceID();
142 static ASM_cb_result_t media_player_audio_session_event_source_pause(
143 ASM_event_sources_t event_source,
145 MediaPlayerTizen* player =
146 static_cast<MediaPlayerTizen*>(user_data);
148 return ASM_CB_RES_IGNORE;
151 switch (event_source) {
152 case ASM_EVENT_SOURCE_CALL_START:
153 case ASM_EVENT_SOURCE_ALARM_START:
154 case ASM_EVENT_SOURCE_MEDIA:
155 case ASM_EVENT_SOURCE_EMERGENCY_START:
156 case ASM_EVENT_SOURCE_OTHER_PLAYER_APP:
157 case ASM_EVENT_SOURCE_RESOURCE_CONFLICT:
158 case ASM_EVENT_SOURCE_EARJACK_UNPLUG:
160 return ASM_CB_RES_PAUSE;
162 return ASM_CB_RES_NONE;
166 static ASM_cb_result_t media_player_audio_session_event_source_play(
167 ASM_event_sources_t event_source,
169 MediaPlayerTizen* player =
170 static_cast<MediaPlayerTizen*>(user_data);
172 return ASM_CB_RES_IGNORE;
175 switch (event_source) {
176 case ASM_EVENT_SOURCE_ALARM_END:
178 return ASM_CB_RES_PLAYING;
180 return ASM_CB_RES_NONE;
184 static ASM_cb_result_t media_player_private_audio_session_notify_cb(
186 ASM_event_sources_t event_source,
187 ASM_sound_commands_t command,
190 if (command == ASM_COMMAND_STOP || command == ASM_COMMAND_PAUSE)
191 return media_player_audio_session_event_source_pause(
192 event_source, user_data);
193 if (command == ASM_COMMAND_PLAY || command == ASM_COMMAND_RESUME)
194 return media_player_audio_session_event_source_play(
195 event_source, user_data);
196 return ASM_CB_RES_NONE;
200 static void on_gst_start_video_feed_cb(
204 MediaSourcePlayerGstreamer* player =
205 static_cast<MediaSourcePlayerGstreamer*>(user_data);
206 if (!player || player->IsPlayerDestructing())
208 player->OnReadDemuxedData(media::DemuxerStream::VIDEO);
212 static void on_gst_stop_video_feed_cb(GstAppSrc * pipeline,void *user_data) {
213 MediaSourcePlayerGstreamer* player =
214 static_cast<MediaSourcePlayerGstreamer*>(user_data);
215 if (!player || player->IsPlayerDestructing())
217 player->OnStopDemuxedData(media::DemuxerStream::VIDEO);
220 static gboolean on_gst_seek_video_feed_cb(
225 MediaSourcePlayerGstreamer* player =
226 static_cast<MediaSourcePlayerGstreamer*>(user_data);
227 if (!player || player->IsPlayerDestructing())
229 player->UpdateVideoSeekOffset(offset);
233 static GstFlowReturn on_gst_appsink_preroll(
235 gpointer user_data) {
236 MediaSourcePlayerGstreamer* player =
237 static_cast<MediaSourcePlayerGstreamer*>(user_data);
238 if (!player || player->IsPlayerDestructing())
239 return GST_FLOW_ERROR;
240 player->GetFrameDetails();
244 static GstFlowReturn on_gst_appsink_buffer(
246 gpointer user_data) {
247 MediaSourcePlayerGstreamer* player =
248 static_cast<MediaSourcePlayerGstreamer*>(user_data);
249 if (!player || player->IsPlayerDestructing())
250 return GST_FLOW_ERROR;
251 player->OnNewFrameAvailable(player->PullBuffer());
255 gboolean on_gst_seek_audio_feed_cb(
259 MediaSourcePlayerGstreamer* player =
260 static_cast<MediaSourcePlayerGstreamer*>(user_data);
261 if (!player || player->IsPlayerDestructing())
263 player->UpdateAudioSeekOffset(offset);
267 static void on_gst_start_audio_feed_cb (
268 GstAppSrc * pipeline,
271 MediaSourcePlayerGstreamer* player =
272 static_cast<MediaSourcePlayerGstreamer*>(user_data);
273 if (!player || player->IsPlayerDestructing())
275 player->OnReadDemuxedData(media::DemuxerStream::AUDIO);
278 static void on_gst_stop_audio_feed_cb(GstAppSrc* pipeline, void* user_data) {
279 MediaSourcePlayerGstreamer* player =
280 static_cast<MediaSourcePlayerGstreamer*>(user_data);
281 if (!player || player->IsPlayerDestructing())
283 player->OnStopDemuxedData(media::DemuxerStream::AUDIO);
286 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
287 static void on_video_sink_caps_changed_cb(
289 GParamSpec* gparamspec,
291 MediaSourcePlayerGstreamer* player =
292 static_cast<MediaSourcePlayerGstreamer*>(user_data);
293 if (!player || player->IsPlayerDestructing())
295 player->OnVideoConfigsChanged();
299 // Generating Unique string from given width and height.
300 std::string ConvertWidthAndHeightToString(int width, int height) {
301 std::ostringstream width_stream, height_stream;
302 width_stream << width;
303 height_stream << height;
304 return width_stream.str() + "X" + height_stream.str();
307 MediaSourcePlayerGstreamer::MediaSourcePlayerGstreamer(
309 scoped_ptr<DemuxerTizen> demuxer,
310 MediaPlayerManager* manager)
311 : MediaPlayerTizen(player_id, manager),
312 demuxer_(demuxer.Pass()),
313 main_loop_(base::MessageLoopProxy::current()),
314 #if defined(OS_TIZEN_TV)
315 audio_session_manager_(AudioSessionManager::CreateAudioSessionManager()),
319 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
325 is_xwindow_handle_set_(false),
326 is_backed_by_pixmap(false),
330 video_decoder_(NULL),
333 audio_decoder_(NULL),
338 audio_convert_(NULL),
339 audio_resampler_(NULL),
341 video_sink_pad_(NULL),
342 should_feed_audio_(true),
343 should_feed_video_(false),
350 is_paused_due_underflow_(false),
351 shared_memory_size(0),
355 is_demuxer_seeking_(false),
358 is_gst_pipeline_constructed_(false),
359 is_download_finished_(false),
360 is_end_reached_(false),
361 error_occured_(false),
362 raw_video_frame_size_(0),
363 video_seek_offset_(0),
364 audio_seek_offset_(0),
365 is_seeking_iframe_(false) {
366 demuxer_->Initialize(this);
367 audio_buffer_queue_.clear();
368 video_buffer_queue_.clear();
369 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
370 efl_pixmaps_map_.clear();
372 #if defined(OS_TIZEN_TV)
373 if (!audio_session_manager_->RegisterAudioSessionManager(
374 MM_SESSION_TYPE_SHARE,
375 media_player_private_audio_session_notify_cb, this))
380 MediaSourcePlayerGstreamer::~MediaSourcePlayerGstreamer() {
381 VLOG(1) << "MediaSourcePlayerGstreamer::" << __FUNCTION__
382 << "Player ID:" << GetPlayerId();
385 void MediaSourcePlayerGstreamer::Destroy() {
386 if (IsPlayerDestructing())
389 VLOG(1) << "MediaSourcePlayerGstreamer::" << __FUNCTION__
390 << "Player ID:" << GetPlayerId();
393 main_loop_->DeleteSoon(FROM_HERE, this);
396 void MediaSourcePlayerGstreamer::Play() {
397 if (!pipeline_ || error_occured_)
399 if (play_rate_ == 0.0) {
403 #if defined(OS_TIZEN_TV)
404 if (!audio_session_manager_->SetSoundState(ASM_STATE_PLAYING))
407 VLOG(1) << "MediaSourcePlayerGstreamer::" << __FUNCTION__
408 << "Player ID:" << GetPlayerId();
410 #if defined(OS_TIZEN_MOBILE) && (defined(TIZEN_V_2_3) || defined(TIZEN_V_2_4))
411 if (device_power_wakeup(false) != DEVICE_ERROR_NONE)
412 LOG(ERROR) << "|device_power_wakeup| request failed";
414 if (device_power_request_lock(POWER_LOCK_DISPLAY, 0) != DEVICE_ERROR_NONE)
415 LOG(ERROR) << "|device_power_request_lock| request failed";
418 gst_element_set_state(pipeline_, GST_STATE_PLAYING);
419 StartCurrentTimeUpdateTimer();
421 is_paused_due_underflow_ = false;
424 void MediaSourcePlayerGstreamer::Pause(bool is_media_related_action) {
425 if (!pipeline_ || error_occured_)
428 #if defined(OS_TIZEN_TV)
429 if (!audio_session_manager_->SetSoundState(ASM_STATE_PAUSE))
433 VLOG(1) << "MediaSourcePlayerGstreamer::" << __FUNCTION__;
434 gst_element_set_state(pipeline_, GST_STATE_PAUSED);
436 StopCurrentTimeUpdateTimer();
437 if (!is_media_related_action) {
439 #if defined(OS_TIZEN_MOBILE) && (defined(TIZEN_V_2_3) || defined(TIZEN_V_2_4))
440 if (device_power_release_lock(POWER_LOCK_DISPLAY) != DEVICE_ERROR_NONE)
441 LOG(ERROR) << "|device_power_release_lock| request failed";
444 is_paused_due_underflow_ = false;
449 void MediaSourcePlayerGstreamer::SetRate(double rate) {
450 VLOG(1) << "MediaSourcePlayerGstreamer::"
451 << __FUNCTION__ << " : " << rate;
453 if (play_rate_ == rate)
462 // If rate was zero and requested rate is non-zero, change the paused state
463 if(play_rate_ == 0.0 && rate != 0.0) {
465 StartCurrentTimeUpdateTimer();
470 RequestPlayerSeek(GetCurrentTime());
472 #if defined(OS_TIZEN_TV)
473 if (!audio_session_manager_->SetSoundState(ASM_STATE_PAUSE))
478 void MediaSourcePlayerGstreamer::RequestPlayerSeek(double seekTime) {
479 VLOG(1) << "MediaSourcePlayerGstreamer::" << __FUNCTION__
480 << " : " << seekTime;
481 if (is_demuxer_seeking_)
484 gst_element_get_state(pipeline_, &state, NULL, 250 * GST_NSECOND);
485 is_demuxer_seeking_ = true;
486 if (state == GST_STATE_PLAYING)
488 manager()->OnRequestSeek(GetPlayerId(), seekTime);
491 void MediaSourcePlayerGstreamer::Seek(const double time) {
493 gst_element_get_state(pipeline_, &state, NULL, 250 * GST_NSECOND);
495 is_seeking_iframe_ = false;
496 VLOG(1) << "MediaSourcePlayerGstreamer::" << __FUNCTION__
497 << " : " << time << " state : " << gst_element_state_get_name(state);
499 is_demuxer_seeking_ = true;
500 if (state == GST_STATE_PLAYING)
503 // Input to |FromMicroseconds| is |int64|. Additional multiplication
504 // is done to avoid data loss.
505 base::TimeDelta seek_time = base::TimeDelta::FromMicroseconds(
506 static_cast<int64>(time * base::Time::kMicrosecondsPerSecond));
507 demuxer_->RequestDemuxerSeek(seek_time);
510 void MediaSourcePlayerGstreamer::SeekInternal(const GstClockTime position) {
511 if (!pipeline_ || error_occured_)
514 GstClockTime startTime, endTime;
516 is_demuxer_seeking_ = false;
518 if (play_rate_ > 0) {
519 startTime = position;
520 endTime = GST_CLOCK_TIME_NONE;
526 VLOG(1) << "MediaSourcePlayerGstreamer::" << __FUNCTION__
527 << " : " << position;
529 UpdateSeekState(true);
530 audio_buffer_queue_.clear();
531 video_buffer_queue_.clear();
532 if(!gst_element_seek(pipeline_, play_rate_, GST_FORMAT_TIME,
533 static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE),
534 GST_SEEK_TYPE_SET, startTime, GST_SEEK_TYPE_SET, endTime)) {
535 LOG(ERROR) << "Seek to " << position << " failed";
536 HandleError(MediaPlayerTizen::NetworkStateDecodeError);
540 double MediaSourcePlayerGstreamer::GetCurrentTime() {
541 if (!pipeline_ || error_occured_)
544 gint64 current_time = 0;
545 GstFormat format = GST_FORMAT_TIME;
546 gst_element_query_position(pipeline_, &format, ¤t_time);
547 return ConvertNanoSecondsToSeconds(current_time);
550 void MediaSourcePlayerGstreamer::Release() {
551 VLOG(1) << "MediaSourcePlayerGstreamer::" << __FUNCTION__;
552 DCHECK(IsPlayerDestructing());
554 StopCurrentTimeUpdateTimer();
555 audio_buffer_queue_.clear();
556 video_buffer_queue_.clear();
559 GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline_));
561 g_signal_handlers_disconnect_by_func(
562 bus, reinterpret_cast<gpointer>(gst_pipeline_message_cb), this);
563 gst_bus_set_sync_handler(bus, NULL, NULL);
564 gst_object_unref(bus);
567 gst_element_set_state(pipeline_, GST_STATE_NULL);
568 gst_object_unref(pipeline_);
573 gst_object_unref(video_sink_pad_);
575 #if defined(OS_TIZEN_TV)
576 if (!audio_session_manager_->SetSoundState(ASM_STATE_STOP))
578 if (!audio_session_manager_->DeallocateResources())
581 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
582 UnregisterDamageHandler();
583 EflPixmapMap::iterator it = efl_pixmaps_map_.begin();
584 while (it != efl_pixmaps_map_.end()) {
585 efl_pixmap_ = it->second;
586 if (efl_pixmap_.get())
590 efl_pixmaps_map_.clear();
594 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
595 void MediaSourcePlayerGstreamer::UnregisterDamageHandler() {
597 ecore_x_damage_free(m_damage);
600 if (m_damageHandler) {
601 ecore_event_handler_del(m_damageHandler);
607 void MediaSourcePlayerGstreamer::SetVolume(double volume) {
609 g_object_set(G_OBJECT(audio_volume_), "volume", volume, NULL);
612 void MediaSourcePlayerGstreamer::OnDemuxerConfigsAvailable(
613 const DemuxerConfigs& configs) {
614 if (IsPlayerDestructing())
617 if ((configs.video_codec == kUnknownVideoCodec ||
618 configs.video_codec != kCodecH264) &&
619 (configs.audio_codec == kUnknownAudioCodec ||
620 (configs.audio_codec != kCodecAAC &&
621 configs.audio_codec != kCodecMP3))) {
622 LOG(ERROR) << "Audio and Video codecs not supported for MediaSource";
623 HandleError(MediaPlayerTizen::NetworkStateFormatError);
627 gst_width_ = configs.video_size.width();
628 gst_height_ = configs.video_size.height();
630 if (is_gst_pipeline_constructed_) {
633 is_gst_pipeline_constructed_ = true;
636 if (!gst_is_initialized()) {
637 gst_init_check(NULL, NULL, &err);
640 if (gst_is_initialized() && !err) {
641 pipeline_ = gst_pipeline_new(kPipelineName);
643 LOG(ERROR) << "Unable to Create |Pipeline|";
644 HandleError(MediaPlayerTizen::NetworkStateDecodeError);
649 while (VideoMapping[i].codec != kUnknownVideoCodec) {
650 if (configs.video_codec == VideoMapping[i].codec) {
651 media_type |= MEDIA_VIDEO_MASK;
652 video_appsrc_ = gst_element_factory_make("appsrc", "video-source");
653 video_parse_ = gst_element_factory_make(VideoMapping[i].elements[0],
655 video_queue_ = gst_element_factory_make("queue2", "video-queue");
656 video_decoder_ = gst_element_factory_make(VideoMapping[i].elements[1],
659 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
660 PrepareForVideoSink();
662 PrepareForVideoFrame();
665 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
666 if (is_backed_by_pixmap)
669 if (!video_appsrc_ || !video_parse_ || !video_queue_ ||
670 !video_decoder_ || !video_sink_) {
671 LOG(ERROR) << "Not all elements of video pipeline could be created";
672 ReleaseVideoElements();
673 HandleError(MediaPlayerTizen::NetworkStateDecodeError);
677 g_object_set(GST_OBJECT(video_appsrc_), "format", GST_FORMAT_TIME, NULL);
678 g_object_set(GST_OBJECT(video_appsrc_), "stream-type",
679 GST_APP_STREAM_TYPE_SEEKABLE, NULL);
680 g_object_set(GST_OBJECT(video_appsrc_), "do-timestamp", false, NULL);
682 // Will make the queue to send GST_MESSAGE_BUFFERING
683 g_object_set(G_OBJECT(video_queue_), "use-buffering", true, NULL);
685 // Will trigger need-data callback if buffer goes below 30%,
686 // default is 10%. Data type of property "low-percent" differs
687 // on desktop and TIZEN platform.
688 #if defined(OS_TIZEN)
689 g_object_set(G_OBJECT(video_queue_), "low-percent", (double)30.0, NULL);
691 g_object_set(G_OBJECT(video_queue_), "low-percent", 30, NULL);
694 gst_bin_add_many(GST_BIN(pipeline_), video_appsrc_, video_queue_,
695 video_parse_, video_decoder_, video_sink_, NULL);
697 // Why |video_queue_| is placed after |video_appsrc_|?
698 // For understanding puprose consider http://tinyurl.com/qos-iron url.
699 // For 1080p resolution of the video in above url, each decoded frame
700 // is of size 2304000 bytes ~ 2.19 MB. If video_queue_ is placed before
701 // |video_sink_| then queue will buffer decoded frames, so to buffer
702 // two second worth of data queue will require 2304000*24(fps)*2 ~ 96MB
703 // of queue size. This property can't be set for pixmap backed playback
704 // as frame size won't be available for pixmap backed | video_sink|.
705 // And this size varies from video to video.
707 // But if |video_queue_| is placed after |video_appsrc_|, queue will
708 // buffer encoded data. For the same video of 1080p, maximum encoded
709 // frame is of 115398byte ~ 0.110052109 MB. So for 2 sec data, queue
710 // need to buffer 5308308bytes in queue ~ 5MB, this can be set
711 // dynamically. Refer |OnDemuxerDataAvailable| for setting queue size.
713 gst_element_link_many(video_appsrc_,video_queue_, video_parse_,
714 video_decoder_, video_sink_, NULL);
715 static GstAppSrcCallbacks video_callbacks = {
716 on_gst_start_video_feed_cb,
717 on_gst_stop_video_feed_cb,
718 on_gst_seek_video_feed_cb, {NULL}};
720 // FIXME: Try fourth argument for destructy notification.
721 gst_app_src_set_callbacks(GST_APP_SRC(video_appsrc_), &video_callbacks,
729 while (AudioMapping[i].codec != kUnknownAudioCodec) {
730 if(configs.audio_codec == AudioMapping[i].codec) {
731 media_type |= MEDIA_AUDIO_MASK;
732 audio_appsrc_ = gst_element_factory_make("appsrc", "audio-source");
733 audio_queue_ = gst_element_factory_make("queue2", "audio-queue");
734 audio_parse_ = gst_element_factory_make(AudioMapping[i].elements[0],
736 audio_decoder_ = gst_element_factory_make(AudioMapping[i].elements[1],
738 audio_convert_ = gst_element_factory_make("audioconvert", "audio-convert");
739 audio_resampler_ = gst_element_factory_make(
740 "audioresample", "audio-resample");
741 audio_volume_ = gst_element_factory_make("volume", "volume");
742 audio_sink_ = gst_element_factory_make(
743 AudioMapping[i].elements[2], "audio-sink");
745 if (!audio_appsrc_ || !audio_queue_ || !audio_parse_ ||
746 !audio_decoder_ || !audio_convert_ || !audio_resampler_ ||
747 !audio_volume_ || !audio_sink_) {
748 LOG(ERROR) << "Not all elements of audio pipeline could be created";
749 ReleaseAudioElements();
750 HandleError(MediaPlayerTizen::NetworkStateDecodeError);
755 GST_OBJECT(audio_appsrc_),"format", GST_FORMAT_TIME, NULL);
756 g_object_set(GST_OBJECT(audio_appsrc_),"stream-type",
757 GST_APP_STREAM_TYPE_SEEKABLE, NULL);
758 g_object_set(GST_OBJECT(audio_appsrc_), "do-timestamp", false, NULL);
760 g_object_set(G_OBJECT(audio_queue_), "use-buffering", true, NULL);
762 #if defined(OS_TIZEN)
764 G_OBJECT(audio_queue_), "low-percent", (double)30.0, NULL);
766 g_object_set(G_OBJECT(audio_queue_), "low-percent", 30, NULL);
769 g_object_set(G_OBJECT(audio_volume_), "mute", false, NULL);
771 #if defined(OS_TIZEN_TV)
772 if (!audio_session_manager_->AllocateResources(audio_decoder_))
775 g_object_set(audio_sink_, "provide-clock", true, "device", "hw:0,0", NULL);
776 if (!audio_session_manager_->AllocateResources(audio_sink_))
780 gst_bin_add_many(GST_BIN(pipeline_), audio_appsrc_, audio_queue_,
781 audio_parse_, audio_decoder_, audio_convert_, audio_resampler_,
782 audio_volume_, audio_sink_, NULL);
784 gst_element_link_many(audio_appsrc_, audio_queue_, audio_parse_,
785 audio_decoder_, audio_convert_, audio_resampler_, audio_volume_,
788 static GstAppSrcCallbacks audio_callbacks = {
789 on_gst_start_audio_feed_cb,
790 on_gst_stop_audio_feed_cb,
791 on_gst_seek_audio_feed_cb, {NULL}};
793 gst_app_src_set_callbacks(GST_APP_SRC(audio_appsrc_), &audio_callbacks,
800 GstBus*bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline_));
802 LOG(ERROR) << "GStreamer bus creation failed";
803 HandleError(MediaPlayerTizen::NetworkStateDecodeError);
806 gst_bus_set_sync_handler(
807 bus, (GstBusSyncHandler)gst_pipeline_message_cb, this);
808 gst_object_unref(bus);
810 manager()->OnMediaDataChange(GetPlayerId(), video_format_,
811 gst_height_, gst_width_, media_type);
813 manager()->OnReadyStateChange(GetPlayerId(),
814 MediaPlayerTizen::ReadyStateHaveMetadata);
816 if (gst_element_set_state(pipeline_, GST_STATE_PAUSED) ==
817 GST_STATE_CHANGE_FAILURE)
818 LOG(ERROR) << "GStreamer state change failed";
820 LOG(ERROR) << "Unable to initialize GST";
821 HandleError(MediaPlayerTizen::NetworkStateDecodeError);
825 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
826 void MediaSourcePlayerGstreamer::PrepareForVideoSink() {
827 video_sink_ = gst_element_factory_make("xvimagesink", "sink");
828 if ( video_sink_ == NULL) {
829 PrepareForVideoFrame();
831 VLOG(1) << "MediaSource using |xvimagesink| for Video Playback";
832 is_backed_by_pixmap = true;
833 video_sink_pad_ = gst_element_get_static_pad(video_sink_, "sink");
834 if (video_sink_pad_) {
835 g_signal_connect(video_sink_pad_, "notify::caps",
836 G_CALLBACK(on_video_sink_caps_changed_cb), this);
838 LOG(ERROR) << " |video_sink_pad_| is NULL ?";
844 void MediaSourcePlayerGstreamer::PrepareForVideoFrame() {
845 is_xwindow_handle_set_ = true;
846 VLOG(1) << "MediaSource using |appsink| for Video Playback";
847 video_sink_ = gst_element_factory_make("appsink", "sink");
848 GstAppSinkCallbacks callbacks = {NULL,
849 on_gst_appsink_preroll,
850 on_gst_appsink_buffer,
851 NULL, {NULL, NULL, NULL}};
852 gst_app_sink_set_callbacks(GST_APP_SINK(video_sink_), &callbacks,
854 g_object_set(G_OBJECT(video_sink_), "max-buffers", (guint)1, NULL);
857 void MediaSourcePlayerGstreamer::ReadDemuxedData(
858 media::DemuxerStream::Type type) {
859 if (IsPlayerDestructing())
862 if (type == media::DemuxerStream::AUDIO) {
863 should_feed_audio_ = true;
864 } else if (type == media::DemuxerStream::VIDEO) {
865 should_feed_video_ = true;
867 LOG(ERROR) << "Unknown Media Type in MediaSourcePlayerGstreamer::"
871 demuxer_->RequestDemuxerData(type);
874 void MediaSourcePlayerGstreamer::OnReadDemuxedData(
875 media::DemuxerStream::Type type) {
876 if (IsPlayerDestructing()) {
877 LOG(ERROR) << __FUNCTION__ << "GST is deinitializing. Just return";
880 main_loop_->PostTask(FROM_HERE, base::Bind(
881 &MediaSourcePlayerGstreamer::ReadDemuxedData, base::Unretained(this),
885 void MediaSourcePlayerGstreamer::OnStopDemuxedData(
886 media::DemuxerStream::Type type) {
887 if (type == media::DemuxerStream::AUDIO)
888 should_feed_audio_ = false;
889 else if (type == media::DemuxerStream::VIDEO)
890 should_feed_video_ = false;
892 LOG(ERROR) << "Unknown Media Type in MediaSourcePlayerGstreamer::"
896 void MediaSourcePlayerGstreamer::OnDemuxerDataAvailable(
897 base::SharedMemoryHandle foreign_memory_handle,
898 const media::DemuxedBufferMetaData& meta_data) {
899 if (!pipeline_ || error_occured_) {
900 LOG(ERROR) << "Pipeline_ null or error occured";
903 if (meta_data.size <= 0) {
904 LOG(ERROR) << "ERROR : Size of shared memory is Zero";
908 if (is_seeking_ && !is_seeking_iframe_) {
909 if (meta_data.type == media::DemuxerStream::VIDEO) {
910 is_seeking_iframe_ = true;
911 if (video_seek_offset_ >
912 (guint64)(meta_data.timestamp.InMicroseconds() * 1000)) {
913 RequestPlayerSeek((double)(ConvertNanoSecondsToSeconds(
914 meta_data.timestamp.InMicroseconds() * 1000
915 + kSixteenMilliSeconds)));
918 } else if (meta_data.type == media::DemuxerStream::AUDIO) {
919 if ( audio_seek_offset_ >
920 (guint64)(meta_data.timestamp.InMicroseconds() * 1000))
925 ReadFromQueueIfAny(meta_data.type);
926 if (meta_data.type == media::DemuxerStream::VIDEO) {
927 if (meta_data.size > raw_video_frame_size_) {
928 // Dynamically Changing Video Queue Size for Smooth Playback.
929 // The default queue size limits are 100 buffers, 2MB of data,
930 // or two seconds worth of data, whichever is reached first.
931 // Adjust queue to contain two seconds worth of data for smooth playback.
932 // So need to adjust number of buffers (max-size-buffers >= 2*fps) and
933 // maximum size of queue (max-size-bytes >= 2*fps*meta_data.size).
935 // 1000000 micro seconds = 1 second.
936 // 2097152 bytes = 2 MB.
937 int no_frames_per_two_second , queue_size_for_two_sec;
938 raw_video_frame_size_ = meta_data.size;
939 no_frames_per_two_second = 2 * (1000000 /
940 (meta_data.time_duration.InMicroseconds()));
941 queue_size_for_two_sec =
942 raw_video_frame_size_ * no_frames_per_two_second;
943 if (no_frames_per_two_second > 100) {
944 g_object_set(G_OBJECT(video_queue_), "max-size-buffers",
945 no_frames_per_two_second, NULL);
947 if (queue_size_for_two_sec > 2097152) {
948 g_object_set(G_OBJECT(video_queue_), "max-size-bytes",
949 queue_size_for_two_sec, NULL);
953 if (meta_data.type == media::DemuxerStream::AUDIO && !should_feed_audio_) {
954 // Why store the DecoderBuffer? we have requested for buffer
955 // from demuxer but gstreamer asked to stop. So need to save
956 // this buffer and use it on next |need_data| call.
957 SaveDecoderBuffer(foreign_memory_handle, meta_data);
960 if (meta_data.type == media::DemuxerStream::VIDEO && !should_feed_video_) {
961 SaveDecoderBuffer(foreign_memory_handle, meta_data);
965 base::SharedMemory shared_memory(foreign_memory_handle, false);
966 shared_memory.Map(meta_data.size);
968 gint size = meta_data.size;
969 GstFlowReturn ret = GST_FLOW_OK;
970 ptr = g_malloc(size);
971 memcpy(ptr, shared_memory.memory(), size);
973 GstBuffer* buffer = gst_buffer_new();
974 GST_BUFFER_MALLOCDATA(buffer) = (uint8*)ptr;
975 GST_BUFFER_SIZE(buffer) = size;
976 GST_BUFFER_DATA(buffer) = GST_BUFFER_MALLOCDATA(buffer);
977 GST_BUFFER_TIMESTAMP (buffer) =
978 (guint64)(meta_data.timestamp.InMicroseconds() * 1000);
979 GST_BUFFER_DURATION (buffer) =
980 (guint64)(meta_data.time_duration.InMicroseconds() * 1000);
982 if (meta_data.type == media::DemuxerStream::AUDIO)
983 ret = gst_app_src_push_buffer(GST_APP_SRC(audio_appsrc_), buffer);
984 else if (meta_data.type == media::DemuxerStream::VIDEO)
985 ret = gst_app_src_push_buffer(GST_APP_SRC(video_appsrc_), buffer);
987 // gst_app_src_push_buffer() takes ownership of the buffer.
988 // Hence no need to unref buffer.
989 if (ret != GST_FLOW_OK) {
990 LOG(ERROR) << __FUNCTION__ << " : Gstreamer appsrc push failed : " << ret;
991 shared_memory.Close();
995 if (meta_data.type == media::DemuxerStream::AUDIO && should_feed_audio_)
996 OnReadDemuxedData(media::DemuxerStream::AUDIO);
997 else if (meta_data.type == media::DemuxerStream::VIDEO && should_feed_video_)
998 OnReadDemuxedData(media::DemuxerStream::VIDEO);
999 shared_memory.Close();
1003 void MediaSourcePlayerGstreamer::OnBufferMetaDataAvailable(
1004 const media::DemuxedBufferMetaData& meta_data) {
1006 if (!pipeline_ || error_occured_) {
1007 LOG(ERROR) << "Pipeline_ null or error occured";
1011 switch (meta_data.status) {
1012 case media::DemuxerStream::kAborted:
1013 // FIXME : Need to handle Aborted state Properly.
1014 if (meta_data.type == media::DemuxerStream::AUDIO && should_feed_audio_)
1015 OnReadDemuxedData(media::DemuxerStream::AUDIO);
1016 else if (meta_data.type == media::DemuxerStream::VIDEO &&
1018 OnReadDemuxedData(media::DemuxerStream::VIDEO);
1021 case media::DemuxerStream::kConfigChanged:
1022 if (meta_data.type == media::DemuxerStream::AUDIO && should_feed_audio_)
1023 OnReadDemuxedData(media::DemuxerStream::AUDIO);
1024 else if (meta_data.type == media::DemuxerStream::VIDEO &&
1026 OnReadDemuxedData(media::DemuxerStream::VIDEO);
1028 if (meta_data.type == media::DemuxerStream::AUDIO)
1029 VLOG(1) << "[BROWSER] : AUDIO::kConfigChanged";
1030 if (meta_data.type == media::DemuxerStream::VIDEO)
1031 VLOG(1) << "[BROWSER] : VIDEO::kConfigChanged";
1034 case media::DemuxerStream::kOk:
1035 if (meta_data.end_of_stream) {
1036 ReadFromQueueIfAny(meta_data.type);
1037 LOG(ERROR) <<"[BROWSER] : DemuxerStream::kOk but |end_of_stream|";
1038 if (meta_data.type == media::DemuxerStream::AUDIO)
1039 gst_app_src_end_of_stream(GST_APP_SRC(audio_appsrc_));
1040 if (meta_data.type == media::DemuxerStream::VIDEO)
1041 gst_app_src_end_of_stream(GST_APP_SRC(video_appsrc_));
1052 void MediaSourcePlayerGstreamer::ReadFromQueueIfAny(
1053 DemuxerStream::Type type) {
1054 if (!pipeline_ || error_occured_) {
1055 LOG(ERROR) << "Pipeline_ null or error occured";
1059 if (type == media::DemuxerStream::AUDIO) {
1060 if (audio_buffer_queue_.empty() || !should_feed_audio_)
1064 if (type == media::DemuxerStream::VIDEO) {
1065 if (video_buffer_queue_.empty() || !should_feed_video_)
1069 scoped_refptr<DecoderBuffer> decoder_buffer;
1070 if (type == media::DemuxerStream::AUDIO) {
1071 decoder_buffer = audio_buffer_queue_.front();
1072 audio_buffer_queue_.pop_front();
1074 decoder_buffer = video_buffer_queue_.front();
1075 video_buffer_queue_.pop_front();
1080 gint size = decoder_buffer.get()->data_size();
1081 ptr = g_malloc(size);
1082 memcpy(ptr, (void*)decoder_buffer.get()->writable_data(), size);
1083 GstBuffer* buffer = gst_buffer_new();
1084 GST_BUFFER_MALLOCDATA(buffer) = (uint8*)ptr;
1085 GST_BUFFER_SIZE(buffer) = size;
1086 GST_BUFFER_DATA(buffer) = GST_BUFFER_MALLOCDATA(buffer);
1087 GST_BUFFER_TIMESTAMP (buffer) =
1088 (guint64)(decoder_buffer.get()->timestamp().InMicroseconds() * 1000);
1089 GST_BUFFER_DURATION (buffer) =
1090 (guint64)(decoder_buffer.get()->duration().InMicroseconds() * 1000);
1092 if (type == media::DemuxerStream::AUDIO)
1093 ret = gst_app_src_push_buffer(GST_APP_SRC(audio_appsrc_), buffer);
1095 ret = gst_app_src_push_buffer(GST_APP_SRC(video_appsrc_), buffer);
1096 if (ret != GST_FLOW_OK)
1099 //Empty the Buffer before reading the new buffer from render process.
1100 ReadFromQueueIfAny(type);
1104 void MediaSourcePlayerGstreamer::SaveDecoderBuffer(
1105 base::SharedMemoryHandle foreign_memory_handle,
1106 const media::DemuxedBufferMetaData& meta_data) {
1107 if (!pipeline_ || error_occured_) {
1108 LOG(ERROR) << "Pipeline_ null or error occured";
1112 base::SharedMemory shared_memory(foreign_memory_handle, false);
1113 shared_memory.Map(meta_data.size);
1114 scoped_refptr<DecoderBuffer> buffer;
1115 buffer = DecoderBuffer::CopyFrom(static_cast<const uint8*> (
1116 shared_memory.memory()), meta_data.size);
1118 if (!buffer.get()) {
1119 LOG(ERROR) << "DecoderBuffer::CopyFrom failed";
1120 shared_memory.Close();
1124 buffer->set_timestamp(meta_data.timestamp);
1125 buffer->set_duration(meta_data.time_duration);
1127 if (meta_data.type == media::DemuxerStream::AUDIO)
1128 audio_buffer_queue_.push_back(buffer);
1130 video_buffer_queue_.push_back(buffer);
1132 shared_memory.Close();
1135 void MediaSourcePlayerGstreamer::GetFrameDetails() {
1136 if (!pipeline_ || error_occured_)
1141 GstStateChangeReturn ret = gst_element_get_state(pipeline_,
1142 &state, &pending, 250 * GST_NSECOND);
1143 VLOG(1) << "MediaSourcePlayerGstreamer::"
1145 << " GstStateChangeReturn: "
1146 << gst_element_state_change_return_get_name(ret)
1148 << gst_element_state_get_name(state)
1150 << gst_element_state_get_name(pending)
1151 << " ID " << GetPlayerId();
1153 // Get details only after prerolling.
1154 if (pending >= GST_STATE_PAUSED)
1155 main_loop_->PostTask(FROM_HERE, base::Bind(
1156 &MediaSourcePlayerGstreamer::OnGetFrameDetails,
1157 base::Unretained(this)));
1160 void MediaSourcePlayerGstreamer::OnGetFrameDetails() {
1161 if (!pipeline_ || IsPlayerDestructing() || error_occured_)
1164 GstBuffer* buffer = gst_app_sink_pull_preroll(GST_APP_SINK(video_sink_));
1168 GstCaps* caps = gst_buffer_get_caps(GST_BUFFER(buffer));
1170 gst_buffer_unref(buffer);
1174 // No need to unref |GstStructure|
1175 const GstStructure* str = gst_caps_get_structure(caps, 0);
1176 gst_caps_unref(caps);
1177 gst_buffer_unref(buffer);
1181 if (!gst_structure_get_int(str, "width", &gst_width_) ||
1182 !gst_structure_get_int(str, "height", &gst_height_) ||
1183 !gst_structure_get_fourcc(str, "format", &video_format_)) {
1184 LOG(ERROR) << "Pre-rolled buffer information could not be obtained";
1187 if(video_format_ == GST_VIDEO_SN12) {
1188 uint tile_w_align = ((gst_width_ - 1) / SN12_TILE_WIDTH + 2) & ~1;
1190 SN12_TILE_SIZE * tile_w_align * ((gst_height_-1)/SN12_TILE_HEIGHT+1)
1191 + (((gst_height_+1)&~1)/2) * tile_w_align * SN12_TILE_WIDTH;
1193 manager()->OnMediaDataChange(GetPlayerId(), video_format_,
1194 gst_height_, gst_width_, media_type);
1197 GstBuffer* MediaSourcePlayerGstreamer::PullBuffer() {
1198 return gst_app_sink_pull_buffer(GST_APP_SINK(video_sink_));
1201 void MediaSourcePlayerGstreamer::OnNewFrameAvailable(const GstBuffer* buffer) {
1202 if (!pipeline_ || error_occured_)
1206 if (!GST_BUFFER_DATA(buffer) || !GST_BUFFER_SIZE(buffer))
1209 if (!gst_width_ || !gst_height_)
1211 // FIXME: Cross check the end results.
1212 base::TimeDelta timestamp =
1213 base::TimeDelta::FromMicroseconds(
1214 GST_BUFFER_TIMESTAMP(buffer) /
1215 base::Time::kNanosecondsPerMicrosecond);
1217 uint8* buffer_data = GST_BUFFER_DATA(buffer);
1218 gsize buffer_size = GST_BUFFER_SIZE(buffer);
1220 if(video_format_ == GST_VIDEO_SN12)
1221 shared_memory_size = (bufsize_sn12_);
1223 shared_memory_size = (buffer_size);
1225 if (!shared_memory.CreateAndMapAnonymous(shared_memory_size)) {
1226 LOG (ERROR) << "Shared Memory creation failed.";
1227 gst_buffer_unref(GST_BUFFER(buffer));
1231 if (!shared_memory.ShareToProcess(base::Process::Current().Handle(),
1232 &foreign_memory_handle)) {
1233 LOG (ERROR) << "Shared Memory handle could not be obtained";
1234 shared_memory.Close();
1235 gst_buffer_unref(GST_BUFFER(buffer));
1239 memcpy(shared_memory.memory(), buffer_data, shared_memory_size);
1240 manager()->OnNewFrameAvailable(
1241 GetPlayerId(), foreign_memory_handle, shared_memory_size, timestamp);
1243 shared_memory.Close();
1244 gst_buffer_unref(GST_BUFFER(buffer));
1247 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
1248 void MediaSourcePlayerGstreamer::XWindowIdPrepared(GstMessage* message) {
1250 gst_structure_get_int(message->structure, "video-width", &width);
1251 gst_structure_get_int(message->structure, "video-height", &height);
1252 if ((gst_width_ != width) || (gst_height_ != height)) {
1253 LOG(ERROR) << "Demuxer Video Configs and Gstreamer Video Configs doesn't"
1254 <<" match.From Demuxer : width : "<<gst_width_
1255 << " and height :" <<gst_height_
1256 << " | From Gstreamer width : " <<width
1257 << " and Height : " <<height;
1259 gst_height_ = height;
1262 manager()->OnMediaDataChange(
1263 GetPlayerId(), video_format_, gst_height_, gst_width_, media_type);
1266 void MediaSourcePlayerGstreamer::PlatformSurfaceUpdated() {
1267 gint64 current_time = 0;
1268 GstFormat format = GST_FORMAT_TIME;
1269 gst_element_query_position(pipeline_, &format, ¤t_time);
1270 base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
1271 current_time / base::Time::kNanosecondsPerMicrosecond);
1272 manager()->OnPlatformSurfaceUpdated(GetPlayerId(), pixmap_id_, timestamp);
1275 int MediaSourcePlayerGstreamer::GetSurfaceID() const {
1279 void MediaSourcePlayerGstreamer::SetPixmap() {
1280 #if defined(OS_TIZEN_TV)
1281 // Using below statements on mobile to set pixmap was causing two issue.
1282 // 1. Video size was different than the required one whenever configaration
1284 // 2. Sometime black screen was appearing, while video was playing.
1285 // Hence for mobile keeping implementation which uses
1286 // |gst_x_overlay_set_window_handle|to sep Pixmap.
1287 g_object_set (video_sink_, "pixmap-id-callback", get_pixmap_id_cb, NULL);
1288 g_object_set (video_sink_, "pixmap-id-callback-userdata", this, NULL);
1290 gst_x_overlay_set_window_handle(GST_X_OVERLAY(video_sink_), pixmap_id_);
1292 m_damage = ecore_x_damage_new(pixmap_id_,
1293 ECORE_X_DAMAGE_REPORT_RAW_RECTANGLES);
1294 m_damageHandler = ecore_event_handler_add(ECORE_X_EVENT_DAMAGE_NOTIFY,
1295 notify_damage_updated_cb, this);
1296 g_object_set(video_sink_, "rotate", 0, NULL);
1297 is_xwindow_handle_set_ = true;
1300 void MediaSourcePlayerGstreamer::OnVideoConfigsChanged() {
1301 if (!pipeline_ || error_occured_)
1303 main_loop_->PostTask(FROM_HERE, base::Bind(
1304 &MediaSourcePlayerGstreamer::VideoConfigsChanged,
1305 base::Unretained(this)));
1308 void MediaSourcePlayerGstreamer::VideoConfigsChanged() {
1309 if (!pipeline_ || IsPlayerDestructing() || error_occured_)
1311 VLOG(1) << "Video Configs Changed, so changing the pixmap";
1314 if (gst_video_get_size(video_sink_pad_, &width, &height)) {
1315 if ((gst_width_ != width) || (gst_height_ != height)) {
1316 LOG(ERROR) << "Demuxer Video Configs and Gstreamer Video Configs doesn't"
1317 <<" match.From Demuxer : width : "<<gst_width_
1318 << " and height :" <<gst_height_
1319 << " | From Gstreamer width : " <<width
1320 << " and Height : " <<height;
1322 gst_height_ = height;
1323 UnregisterDamageHandler();
1326 manager()->OnMediaDataChange(
1327 GetPlayerId(), video_format_, gst_height_, gst_width_, media_type);
1332 void MediaSourcePlayerGstreamer::CreatePixmap() {
1333 bool is_create_new = false;
1334 std::string string_wh =
1335 ConvertWidthAndHeightToString(gst_width_, gst_height_);
1336 if (efl_pixmaps_map_.size() == 0)
1337 is_create_new = true;
1339 if (!is_create_new) {
1340 EflPixmapMap::iterator it = efl_pixmaps_map_.find(string_wh);
1341 if (it != efl_pixmaps_map_.end()) {
1342 is_create_new = false;
1343 efl_pixmap_ = it->second;
1344 pixmap_id_ = efl_pixmap_->GetId();
1346 is_create_new = true;
1350 if (is_create_new) {
1351 efl_pixmap_ = gfx::EflPixmap::Create(gfx::EflPixmap::SURFACE,
1352 gfx::Size(gst_width_, gst_height_));
1353 if (!efl_pixmap_.get()) {
1354 LOG(ERROR) << "gfx::EflPixmap::Create() failed to create Pixmap";
1357 pixmap_id_ = efl_pixmap_->GetId();
1358 efl_pixmaps_map_[string_wh] = efl_pixmap_;
1363 void MediaSourcePlayerGstreamer::OnDemuxerDurationChanged(
1364 base::TimeDelta duration) {
1365 duration_ = duration.InSecondsF();
1368 void MediaSourcePlayerGstreamer::OnDemuxerSeekDone(
1369 const base::TimeDelta& actual_browser_seek_time) {
1370 VLOG(1) << "MediaSourcePlayerGstreamer::" << __FUNCTION__ << " : "
1371 << actual_browser_seek_time.InSecondsF();
1372 SeekInternal(ConvertToGstClockTime(actual_browser_seek_time.InSecondsF()));
1375 bool MediaSourcePlayerGstreamer::HasVideo() {
1376 return media_type & MEDIA_VIDEO_MASK;
1379 bool MediaSourcePlayerGstreamer::HasAudio() {
1380 return media_type & MEDIA_AUDIO_MASK;
1383 void MediaSourcePlayerGstreamer::OnCurrentTimeUpdateTimerFired() {
1384 manager()->OnTimeUpdate(GetPlayerId(), GetCurrentTime());
1387 void MediaSourcePlayerGstreamer::StartCurrentTimeUpdateTimer() {
1388 if (!current_time_update_timer_.IsRunning()) {
1389 current_time_update_timer_.Start(
1391 base::TimeDelta::FromMilliseconds(kDurationUpdateInterval),
1392 this, &MediaSourcePlayerGstreamer::OnCurrentTimeUpdateTimerFired);
1396 void MediaSourcePlayerGstreamer::StopCurrentTimeUpdateTimer() {
1397 if (current_time_update_timer_.IsRunning())
1398 current_time_update_timer_.Stop();
1401 void MediaSourcePlayerGstreamer::HandleMessage(GstMessage* message) {
1402 if (!pipeline_ || error_occured_)
1404 if (!strcmp(kPipelineName, GST_MESSAGE_SRC_NAME(message)))
1405 VLOG(1) << "MediaSourcePlayerGstreamer::" << __FUNCTION__
1406 << " Message " << GST_MESSAGE_TYPE_NAME(message)
1407 << " received from element " << GST_MESSAGE_SRC_NAME(message)
1408 << " ID " << GetPlayerId();
1410 #if defined(TIZEN_MULTIMEDIA_PIXMAP_SUPPORT)
1411 if (!IsXWindowHadleSet()) {
1412 if (message->structure && gst_structure_has_name(
1413 message->structure, "prepare-xid")) {
1414 VLOG(1) << "Received message-prepare-xid";
1415 XWindowIdPrepared(message);
1421 switch (GST_MESSAGE_TYPE(message)) {
1422 case GST_MESSAGE_ERROR:
1424 gst_message_parse_error(message, &error, NULL);
1425 MediaPlayerTizen::NetworkState network_state_error;
1426 network_state_error = MediaPlayerTizen::NetworkStateEmpty;
1427 if (error->code == GST_STREAM_ERROR_CODEC_NOT_FOUND
1428 || error->code == GST_STREAM_ERROR_WRONG_TYPE
1429 || error->code == GST_STREAM_ERROR_FAILED
1430 || error->code == GST_RESOURCE_ERROR_NOT_FOUND) {
1431 network_state_error = MediaPlayerTizen::NetworkStateFormatError;
1432 } else if (error->domain == GST_RESOURCE_ERROR) {
1433 network_state_error = MediaPlayerTizen::NetworkStateNetworkError;
1435 network_state_error = MediaPlayerTizen::NetworkStateDecodeError;
1437 #if defined(OS_TIZEN_TV)
1438 if (!audio_session_manager_->SetSoundState(ASM_STATE_STOP))
1440 if (!audio_session_manager_->DeallocateResources())
1443 LOG(ERROR) << "Error Message : " << error->message << " Recieved From : "
1444 << GST_MESSAGE_SRC_NAME(message)
1445 << ", and Blink Error Code = " << network_state_error;
1446 g_error_free(error);
1447 HandleError(network_state_error);
1449 case GST_MESSAGE_EOS:
1450 VLOG(1) << "GST_MESSAGE_EOS";
1451 #if defined(OS_TIZEN_TV)
1452 if (!audio_session_manager_->SetSoundState(ASM_STATE_STOP))
1454 if (!audio_session_manager_->DeallocateResources())
1457 main_loop_->PostTask(FROM_HERE, base::Bind(
1458 &MediaSourcePlayerGstreamer::OnPlaybackComplete,
1459 base::Unretained(this)));
1461 case GST_MESSAGE_ASYNC_DONE:
1462 VLOG(1) << "HandleMessage : GST_MESSAGE_ASYNC_DONE : is_seeking_ = "
1465 is_seeking_iframe_ = false;
1466 main_loop_->PostTask(FROM_HERE, base::Bind(
1467 &MediaSourcePlayerGstreamer::UpdateSeekState,
1468 base::Unretained(this), false));
1470 // Initiate play for internal seeks.
1472 main_loop_->PostTask(FROM_HERE, base::Bind(
1473 &MediaSourcePlayerGstreamer::Play, base::Unretained(this)));
1475 manager()->OnTimeUpdate(GetPlayerId(), GetCurrentTime());
1476 main_loop_->PostTask(FROM_HERE, base::Bind(
1477 &MediaSourcePlayerGstreamer::OnTimeChanged, base::Unretained(this)));
1480 // FIXME: Do we need to pull caps from PARSER? Can ignore if its of no use.
1482 case GST_MESSAGE_STATE_CHANGED:
1483 if (strcmp(kPipelineName, GST_MESSAGE_SRC_NAME(message)))
1485 main_loop_->PostTask(FROM_HERE, base::Bind(
1486 &MediaSourcePlayerGstreamer::OnUpdateStates, base::Unretained(this)));
1488 case GST_MESSAGE_BUFFERING:
1490 gst_message_parse_buffering(message, &buffered);
1492 if (audio_queue_ && GST_MESSAGE_SRC(message) == GST_OBJECT(audio_queue_))
1493 audio_buffered_ = buffered;
1494 if (video_queue_ && GST_MESSAGE_SRC(message) == GST_OBJECT(video_queue_))
1495 video_buffered_ = buffered;
1497 if (!is_paused_due_underflow_ && playing_) {
1498 if (audio_buffered_ < 100 || video_buffered_ < 100) {
1499 main_loop_->PostTask(FROM_HERE, base::Bind(
1500 &MediaSourcePlayerGstreamer::HandleBufferingMessage,
1501 base::Unretained(this)));
1503 } else if (is_paused_due_underflow_ && playing_) {
1504 if ((!HasAudio() || audio_buffered_ == 100) &&
1505 (!HasVideo() || video_buffered_ == 100)) {
1506 main_loop_->PostTask(FROM_HERE, base::Bind(
1507 &MediaSourcePlayerGstreamer::HandleBufferingMessage,
1508 base::Unretained(this)));
1513 VLOG(1) << "Unhandled GStreamer message type: "
1514 << GST_MESSAGE_TYPE_NAME(message);
1519 void MediaSourcePlayerGstreamer::OnUpdateStates() {
1520 DCHECK(main_loop_->BelongsToCurrentThread());
1521 if (!pipeline_ || IsPlayerDestructing() || error_occured_)
1526 GstStateChangeReturn ret = gst_element_get_state(
1527 pipeline_, &state, &pending, 250 * GST_NSECOND);
1529 VLOG(1) << "MediaSourcePlayerGstreamer::"
1531 << " GstStateChangeReturn: "
1532 << gst_element_state_change_return_get_name(ret)
1534 << gst_element_state_get_name(state)
1536 << gst_element_state_get_name(pending)
1537 << " ID " << GetPlayerId();
1539 // FIXME: Handle all state changes
1541 case GST_STATE_CHANGE_SUCCESS:
1543 case GST_STATE_PAUSED:
1544 manager()->OnReadyStateChange(
1545 GetPlayerId(), MediaPlayerTizen::ReadyStateHaveEnoughData);
1548 VLOG(1) << "GStreamer unhandled state "
1549 << gst_element_state_get_name(state);
1553 case GST_STATE_CHANGE_FAILURE:
1554 LOG(ERROR) << "Failure: State: "
1555 << gst_element_state_get_name(state)
1557 << gst_element_state_get_name(pending);
1558 HandleError(MediaPlayerTizen::NetworkStateDecodeError);
1560 case GST_STATE_CHANGE_NO_PREROLL:
1564 VLOG(1) << "Unhandled return type: " << ret;
1569 void MediaSourcePlayerGstreamer::HandleBufferingMessage() {
1570 if (IsPlayerDestructing())
1572 if (!is_paused_due_underflow_ &&
1573 (audio_buffered_ < 100 || video_buffered_ < 100)) {
1574 is_paused_due_underflow_ = true;
1576 manager()->OnReadyStateChange(GetPlayerId(),
1577 MediaPlayerTizen::ReadyStateHaveCurrentData);
1578 manager()->OnNetworkStateChange(GetPlayerId(),
1579 MediaPlayerTizen::NetworkStateLoading);
1580 } else if (is_paused_due_underflow_ &&
1581 (!HasAudio() || audio_buffered_ == 100) &&
1582 (!HasVideo() || video_buffered_ == 100)) {
1583 is_paused_due_underflow_ = false;
1585 manager()->OnReadyStateChange(GetPlayerId(),
1586 MediaPlayerTizen::ReadyStateHaveEnoughData);
1587 manager()->OnNetworkStateChange(GetPlayerId(),
1588 MediaPlayerTizen::NetworkStateLoaded);
1592 void MediaSourcePlayerGstreamer::OnPlaybackComplete() {
1593 // GStreamer pipeline EOS time and media duration doesnt match.
1594 double time = GetCurrentTime() != duration_ ? duration_ : GetCurrentTime();
1595 is_end_reached_ = true;
1596 is_download_finished_ = false;
1597 StopCurrentTimeUpdateTimer();
1598 manager()->OnTimeUpdate(GetPlayerId(), time);
1599 manager()->OnTimeChanged(GetPlayerId());
1601 #if defined(OS_TIZEN_MOBILE) && (defined(TIZEN_V_2_3) || defined(TIZEN_V_2_4))
1602 if (device_power_release_lock(POWER_LOCK_DISPLAY) != DEVICE_ERROR_NONE)
1603 LOG(ERROR) << "|device_power_release_lock| request failed";
1608 void MediaSourcePlayerGstreamer::UpdateSeekState(bool state) {
1609 manager()->OnSeekStateChange(GetPlayerId(), state);
1610 is_seeking_ = state;
1613 void MediaSourcePlayerGstreamer::OnTimeChanged() {
1614 VLOG(1) << "OnTimeChanged" << "Player ID : " << GetPlayerId();
1615 DCHECK(main_loop_->BelongsToCurrentThread());
1616 manager()->OnTimeChanged(GetPlayerId());
1619 void MediaSourcePlayerGstreamer::HandleError(
1620 media::MediaPlayerTizen::NetworkState state) {
1621 error_occured_ = true;
1622 manager()->OnNetworkStateChange(GetPlayerId(), state);
1624 #if defined(OS_TIZEN_MOBILE) && (defined(TIZEN_V_2_3) || defined(TIZEN_V_2_4))
1625 if (device_power_release_lock(POWER_LOCK_DISPLAY) != DEVICE_ERROR_NONE)
1626 LOG(ERROR) << "|device_power_release_lock| request failed";
1631 void MediaSourcePlayerGstreamer::ReleaseAudioElements() {
1632 if (audio_appsrc_) {
1633 gst_object_unref(audio_appsrc_);
1634 audio_appsrc_ = NULL;
1638 gst_object_unref(audio_queue_);
1639 audio_queue_ = NULL;
1643 gst_object_unref(audio_parse_);
1644 audio_parse_ = NULL;
1647 if (audio_decoder_) {
1648 gst_object_unref(audio_decoder_);
1649 audio_decoder_ = NULL;
1652 if (audio_convert_) {
1653 gst_object_unref(audio_convert_);
1654 audio_convert_ = NULL;
1657 if (audio_resampler_) {
1658 gst_object_unref(audio_resampler_);
1659 audio_resampler_ = NULL;
1662 if (audio_volume_) {
1663 gst_object_unref(audio_volume_);
1664 audio_volume_ = NULL;
1668 gst_object_unref(audio_sink_);
1673 void MediaSourcePlayerGstreamer::ReleaseVideoElements() {
1675 gst_object_unref(video_appsrc_);
1676 video_appsrc_ = NULL;
1680 gst_object_unref(video_parse_);
1681 video_parse_ = NULL;
1685 gst_object_unref(video_queue_);
1686 video_queue_ = NULL;
1689 if(video_decoder_) {
1690 gst_object_unref(video_decoder_);
1691 video_decoder_ = NULL;
1695 gst_object_unref(video_sink_);
1700 } // namespace media