19 #include "mediaplayergeneric.h"
27 #include <unordered_map>
36 const char* kMediaConfigFilePath =
"/etc/webkit_media_player_settings.json";
37 std::unordered_map<std::string, std::string> gMediaConfig;
38 bool gFlagGetVideoSinkFromPlaybin =
true;
40 bool loadMediaConfig(
const char* configPath)
42 if (!gMediaConfig.empty())
45 std::ifstream configStream(configPath);
46 if (!configStream.good())
49 std::string configBuffer((std::istreambuf_iterator<char>(configStream)),
50 std::istreambuf_iterator<char>());
54 root = json_loads(configBuffer.c_str(), 0, &error);
58 if(!json_is_object(root))
67 json_object_foreach(root, key, value)
69 if (!json_is_string(value))
71 LOG_INFO(
"\t%s=%s", key, json_string_value(value));
72 gMediaConfig[std::string(key)] = json_string_value(value);
76 auto it = gMediaConfig.find(
"get-video-sink-from-playbin");
77 if (it != gMediaConfig.end())
79 gFlagGetVideoSinkFromPlaybin = (it->second ==
"true");
81 if (!gFlagGetVideoSinkFromPlaybin)
83 it = gMediaConfig.find(
"video-sink");
84 gFlagGetVideoSinkFromPlaybin = (it == gMediaConfig.end()) || it->second.empty();
91 void mediaPlayerPrivateElementAddedCallback(GstBin *bin, GstElement *element, gpointer);
93 void configureElement(GstElement* element,
const char* elementName)
95 const std::string propsName = std::string(elementName).append(
"-props");
96 const auto& it = gMediaConfig.find(propsName);
97 if (it == gMediaConfig.end())
100 std::stringstream configStream(it->second);
101 std::string configValue;
102 while (std::getline(configStream, configValue,
','))
104 std::size_t idx = configValue.find(
"=");
105 std::string propName = configValue.substr(0, idx);
106 std::string propValue =
"";
107 if (idx != std::string::npos)
108 propValue = configValue.substr(idx+1,std::string::npos);
109 LOG_TRACE(
"set prop for %s: %s=%s", elementName, propName.c_str(), propValue.c_str());
110 gst_util_set_object_arg(G_OBJECT(element), propName.c_str(), propValue.c_str());
114 void configureElement(GstElement *element)
118 gchar* elementName = gst_element_get_name(element);
121 for (
int i = strlen(elementName) - 1; i >= 0 && isdigit(elementName[i]); --i) {
122 elementName[i] =
'\0';
124 configureElement(element, elementName);
127 if(GST_IS_BIN(element))
129 g_signal_connect (element,
"element-added", G_CALLBACK (mediaPlayerPrivateElementAddedCallback),
nullptr);
130 GValue item = G_VALUE_INIT;
131 GstIterator* it = gst_bin_iterate_elements(GST_BIN(element));
132 while(GST_ITERATOR_OK == gst_iterator_next(it, &item))
134 GstElement *next = GST_ELEMENT(g_value_get_object(&item));
135 configureElement(next);
136 g_value_reset (&item);
138 gst_iterator_free(it);
142 void mediaPlayerPrivateElementAddedCallback(GstBin *bin, GstElement *element, gpointer)
144 LOG_TRACE(
"added element=%s",GST_ELEMENT_NAME (element));
145 configureElement(element);
150 #define NOT_IMPLEMENTED() LOG_WARNING("Not implemented")
152 void MediaPlayerGeneric::busMessageCallback(GstBus* bus, GstMessage* msg, gpointer data)
155 self.handleBusMessage(bus, msg);
159 : m_playerClient(client)
160 , m_pipeline(nullptr)
162 , m_videoState(
MediaPlayer::RMF_VIDEO_BUFFER_HAVENOTHING)
163 , m_errorOccured(false)
164 , m_seekIsPending(false)
167 static std::once_flag loadConfigFlag;
168 std::call_once(loadConfigFlag, [](){
169 loadMediaConfig(kMediaConfigFilePath);
171 LOG_INFO(
"Generic video player created");
174 MediaPlayerGeneric::~MediaPlayerGeneric()
176 LOG_INFO(
"Generic video player destroying");
178 m_playerClient =
nullptr;
179 LOG_INFO(
"Generic video player destroyed");
182 MediaPlayer::RMFPlayerState MediaPlayerGeneric::rmf_playerState()
const
184 return m_playerState;
187 MediaPlayer::RMFVideoBufferState MediaPlayerGeneric::rmf_videoState()
const
192 bool MediaPlayerGeneric::rmf_load(
const std::string &url)
194 LOG_INFO(
"LOAD IS CALLED (%s)", url.c_str());
198 m_pipeline = gst_element_factory_make(
"playbin",
nullptr);
202 m_errorMsg =
"Failed to create playbin";
203 loadingFailed(MediaPlayer::RMF_PLAYER_DECODEERROR);
207 configureElement(pipeline());
209 std::string videoSinkName;
210 if(getenv(
"PLAYERSINKBIN_USE_WESTEROSSINK"))
212 videoSinkName =
"westerossink";
214 else if(!gFlagGetVideoSinkFromPlaybin)
216 videoSinkName = gMediaConfig[
"video-sink"];
218 if(!videoSinkName.empty())
220 GstElement* videoSink = gst_element_factory_make(videoSinkName.c_str(),
nullptr);
223 g_object_set(pipeline(),
"video-sink", videoSink, NULL);
224 configureElement(videoSink);
225 gst_object_unref(videoSink);
229 LOG_WARNING(
"Failed to create '%s', fallback to playbin defaults", videoSinkName.c_str());
234 g_object_set(pipeline(),
"uri", m_url.c_str(),
nullptr);
236 GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline()));
237 g_signal_connect(bus,
"message", G_CALLBACK(busMessageCallback),
this);
238 gst_bus_add_signal_watch(bus);
239 gst_object_unref(bus);
247 void MediaPlayerGeneric::rmf_play()
251 bool rc = changePipelineState(GST_STATE_PLAYING);
253 LOG_ERROR(
"Play failed for some unknown reason..");
254 loadingFailed(MediaPlayer::RMF_PLAYER_EMPTY);
260 void MediaPlayerGeneric::rmf_stop()
269 void MediaPlayerGeneric::rmf_pause()
273 changePipelineState(GST_STATE_PAUSED);
278 bool MediaPlayerGeneric::rmf_canItPlay()
const
280 return m_pipeline !=
nullptr && !m_url.empty();
283 bool MediaPlayerGeneric::rmf_isPaused()
const
285 GstState state = GST_STATE_NULL;
286 gst_element_get_state(pipeline(), &state, 0, 0);
287 return state <= GST_STATE_PAUSED;
290 float MediaPlayerGeneric::rmf_getDuration()
const
292 gint64 duration = GST_CLOCK_TIME_NONE;
294 gst_element_query_duration(pipeline(), GST_FORMAT_TIME, &duration);
295 if (rc &&
static_cast<GstClockTime
>(duration) != GST_CLOCK_TIME_NONE)
296 return static_cast<float>(duration) / GST_SECOND;
297 return std::numeric_limits<float>::infinity();;
300 float MediaPlayerGeneric::rmf_getCurrentTime()
const
302 gint64 position = GST_CLOCK_TIME_NONE;
303 GstQuery *query= gst_query_new_position(GST_FORMAT_TIME);
304 if (gst_element_query(pipeline(), query))
305 gst_query_parse_position(query, 0, &position);
306 gst_query_unref(query);
307 if (
static_cast<GstClockTime
>(position) != GST_CLOCK_TIME_NONE)
308 return static_cast<float>(position) / GST_SECOND;
312 std::string MediaPlayerGeneric::rmf_getMediaErrorMessage()
const
317 void MediaPlayerGeneric::rmf_setRate(
float speed)
321 float MediaPlayerGeneric::rmf_getRate()
const
326 void MediaPlayerGeneric::rmf_setVolume(
float volume)
328 gdouble vol =
static_cast<gdouble
>(volume);
329 g_object_set(pipeline(),
"volume", vol, NULL);
332 float MediaPlayerGeneric::rmf_getVolume()
const
335 g_object_get(pipeline(),
"volume", &volume, NULL);
336 return static_cast<float>(volume);
339 void MediaPlayerGeneric::rmf_setMute(
bool mute)
343 bool MediaPlayerGeneric::rmf_isMuted()
const
348 void MediaPlayerGeneric::rmf_seekToLivePosition()
352 void MediaPlayerGeneric::rmf_seekToStartPosition()
357 static GstClockTime convertPositionToGstClockTime(
float time)
362 float microSeconds = modf(time, &seconds) * 1000000;
364 timeValue.tv_sec =
static_cast<glong
>(seconds);
365 timeValue.tv_usec =
static_cast<glong
>(roundf(microSeconds / 10000) * 10000);
366 return GST_TIMEVAL_TO_TIME(timeValue);
369 void MediaPlayerGeneric::rmf_seek(
float time)
371 GstState state, pending;
372 gint64 startTime, endTime;
380 LOG_INFO(
"[Seek] seek attempt to %f secs", time);
383 float current = rmf_getCurrentTime();
387 LOG_INFO(
"HTML5 video: Seeking from %f to %f seconds", current, time);
389 GstClockTime clockTime = convertPositionToGstClockTime(time);
391 GstStateChangeReturn ret = gst_element_get_state(pipeline(), &state, &pending, 250 * GST_NSECOND);
392 LOG_INFO (
"state: %s, pending: %s, ret = %s", gst_element_state_get_name(state), gst_element_state_get_name(pending), gst_element_state_change_return_get_name(ret));
394 if (ret == GST_STATE_CHANGE_ASYNC || state < GST_STATE_PAUSED) {
395 m_seekIsPending =
true;
397 m_seekIsPending =
false;
399 startTime = clockTime;
400 endTime = GST_CLOCK_TIME_NONE;
403 if (!gst_element_seek(pipeline(), 1.0, GST_FORMAT_TIME,
static_cast<GstSeekFlags
>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE), GST_SEEK_TYPE_SET, startTime, GST_SEEK_TYPE_SET, endTime)) {
404 LOG_WARNING(
"[Seek] seeking to %f failed", time);
408 changePipelineState(GST_STATE_PLAYING);
415 bool MediaPlayerGeneric::rmf_isSeeking()
const
419 unsigned long MediaPlayerGeneric::rmf_getCCDecoderHandle()
const
424 std::string MediaPlayerGeneric::rmf_getAudioLanguages()
const
429 void MediaPlayerGeneric::rmf_setAudioLanguage(
const std::string &)
433 void MediaPlayerGeneric::rmf_setAudioMute(
bool isMuted)
437 void MediaPlayerGeneric::rmf_setEissFilterStatus(
bool status)
441 void MediaPlayerGeneric::rmf_setVideoZoom(
unsigned short zoomVal)
445 void MediaPlayerGeneric::rmf_setVideoBufferLength(
float bufferLength)
449 void MediaPlayerGeneric::rmf_setInProgressRecording(
bool isInProgress)
453 bool MediaPlayerGeneric::rmf_isItInProgressRecording()
const
458 void MediaPlayerGeneric::rmf_changeSpeed(
float speed,
short overshootTime)
462 std::string MediaPlayerGeneric::rmf_getMediaWarnDescription()
const
467 int MediaPlayerGeneric::rmf_getMediaWarnData()
const
472 std::string MediaPlayerGeneric::rmf_getAvailableAudioTracks()
const
477 std::string MediaPlayerGeneric::rmf_getCaptionDescriptor()
const
482 std::string MediaPlayerGeneric::rmf_getEISSDataBuffer()
const
487 void MediaPlayerGeneric::rmf_setNetworkBufferSize(
int bufferSize)
491 int MediaPlayerGeneric::rmf_getNetworkBufferSize()
const
496 void MediaPlayerGeneric::rmf_setVideoRectangle(
unsigned x,
unsigned y,
unsigned w,
unsigned h)
499 if (m_lastKnownRect == temp)
502 m_lastKnownRect = temp;
507 GstElement* videoSink;
508 g_object_get(pipeline(),
"video-sink", &videoSink,
nullptr);
512 gchar* rectStr = g_strdup_printf(
"%d,%d,%d,%d", x, y, w, h);
514 gst_object_unref(videoSink);
518 g_object_set(videoSink,
"rectangle", rectStr,
nullptr);
519 gst_object_unref(videoSink);
523 int MediaPlayerGeneric::rmf_getVideoPid() {
528 int MediaPlayerGeneric::rmf_getAudioPid() {
533 void MediaPlayerGeneric::rmf_setVideoKeySlot(
const char* str)
539 void MediaPlayerGeneric::rmf_setAudioKeySlot(
const char* str)
545 void MediaPlayerGeneric::rmf_deleteVideoKeySlot()
551 void MediaPlayerGeneric::rmf_deleteAudioKeySlot()
557 void MediaPlayerGeneric::handleBusMessage(GstBus* bus, GstMessage* msg)
559 bool isPlayerPipelineMessage = GST_MESSAGE_SRC(msg) ==
reinterpret_cast<GstObject*
>(pipeline());
561 switch (GST_MESSAGE_TYPE (msg))
563 case GST_MESSAGE_EOS: {
567 case GST_MESSAGE_STATE_CHANGED:
568 case GST_MESSAGE_ASYNC_DONE: {
569 if (isPlayerPipelineMessage)
573 case GST_MESSAGE_ERROR: {
574 GError *err =
nullptr;
575 gchar *dbg =
nullptr;
576 gst_message_parse_error (msg, &err, &dbg);
578 notifyError(err->message);
581 notifyError(
nullptr);
584 LOG_ERROR(
"[Debug details: %s]", dbg);
590 LOG_TRACE(
"Unhandled message type: %s", GST_MESSAGE_TYPE_NAME(msg));
595 void MediaPlayerGeneric::updateStates()
603 MediaPlayer::RMFPlayerState oldPlayerState = m_playerState;
604 MediaPlayer::RMFVideoBufferState oldVideoState = m_videoState;
606 GstState state, pending;
607 GstStateChangeReturn ret =
608 gst_element_get_state(pipeline(), &state, &pending, 250 * GST_NSECOND);
610 LOG_VERBOSE(
"updateStates(): state: %s, pending: %s, ret = %s",
611 gst_element_state_get_name(state),
612 gst_element_state_get_name(pending),
613 gst_element_state_change_return_get_name(ret));
617 case GST_STATE_CHANGE_SUCCESS:
619 if (state == GST_STATE_READY) {
620 m_videoState = MediaPlayer::RMF_VIDEO_BUFFER_HAVEMETADATA;
621 m_playerState = MediaPlayer::RMF_PLAYER_EMPTY;
622 }
else if (state == GST_STATE_PAUSED) {
623 m_videoState = MediaPlayer::RMF_VIDEO_BUFFER_HAVECURRENTDATA;
624 m_playerState = MediaPlayer::RMF_PLAYER_LOADING;
625 }
else if (state == GST_STATE_PLAYING) {
626 m_videoState = MediaPlayer::RMF_VIDEO_BUFFER_HAVEENOUGHDATA;
627 m_playerState = MediaPlayer::RMF_PLAYER_LOADED;
631 case GST_STATE_CHANGE_NO_PREROLL:
633 if (state == GST_STATE_READY) {
634 m_videoState = MediaPlayer::RMF_VIDEO_BUFFER_HAVENOTHING;
635 }
else if (state == GST_STATE_PAUSED) {
636 m_videoState = MediaPlayer::RMF_VIDEO_BUFFER_HAVEENOUGHDATA;
638 m_playerState = MediaPlayer::RMF_PLAYER_LOADING;
646 if (m_playerState != oldPlayerState && state == GST_STATE_PLAYING)
648 IntRect temp = m_lastKnownRect;
650 rmf_setVideoRectangle(temp.x(),temp.y(),temp.width(),temp.height());
653 if (m_playerState != oldPlayerState)
655 LOG_INFO(
"Player State Changed from %u to %u",
656 oldPlayerState, m_playerState);
657 m_playerClient->playerStateChanged();
659 if (m_videoState != oldVideoState)
661 LOG_INFO(
"Video State Changed from %u to %u",
662 oldVideoState, m_videoState);
663 m_playerClient->videoStateChanged();
666 if ((ret == GST_STATE_CHANGE_SUCCESS) && (state >= GST_STATE_PAUSED) && m_seekIsPending) {
667 LOG_INFO (
"[Seek] committing pending seek to %f", m_seekTime);
668 m_seekIsPending =
false;
669 rmf_seek(m_seekTime);
673 void MediaPlayerGeneric::endOfStream()
676 m_playerClient->mediaPlaybackCompleted();
679 void MediaPlayerGeneric::notifyError(
const char *message)
681 std::string tmp =
"130:ErrorCode:Unknown Error";
683 tmp = std::string(message);
685 LOG_ERROR(
"error message='%s'", m_errorMsg.c_str());
686 loadingFailed(MediaPlayer::RMF_PLAYER_DECODEERROR);
689 void MediaPlayerGeneric::loadingFailed(MediaPlayer::RMFPlayerState error)
691 LOG_ERROR(
"player state=%s", StateString(error));
692 m_errorOccured =
true;
693 if (m_playerState != error) {
694 m_playerState = error;
695 m_playerClient->playerStateChanged();
697 if (m_videoState != MediaPlayer::RMF_VIDEO_BUFFER_HAVENOTHING) {
698 m_videoState = MediaPlayer::RMF_VIDEO_BUFFER_HAVENOTHING;
699 m_playerClient->videoStateChanged();
703 GstElement* MediaPlayerGeneric::pipeline()
const
708 bool MediaPlayerGeneric::changePipelineState(GstState newState)
710 GstState currentState, pending;
712 gst_element_get_state(pipeline(), ¤tState, &pending, 0);
713 if (currentState == newState || pending == newState) {
714 LOG_INFO(
"Rejected state change to %s from %s with %s pending",
715 gst_element_state_get_name(newState),
716 gst_element_state_get_name(currentState),
717 gst_element_state_get_name(pending));
721 GstStateChangeReturn setStateResult = gst_element_set_state(pipeline(), newState);
722 if (setStateResult == GST_STATE_CHANGE_FAILURE) {
723 LOG_ERROR(
"Failed to change state to %s from %s with %s pending",
724 gst_element_state_get_name(newState),
725 gst_element_state_get_name(currentState),
726 gst_element_state_get_name(pending));
732 void MediaPlayerGeneric::cleanup()
737 GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline()));
738 gst_bus_remove_signal_watch(bus);
739 gst_object_unref(bus);
741 gst_element_set_state(pipeline(), GST_STATE_NULL);
742 gst_object_unref(m_pipeline);
744 m_pipeline =
nullptr;
745 m_playerState = MediaPlayer::RMF_PLAYER_EMPTY;
746 m_videoState = MediaPlayer::RMF_VIDEO_BUFFER_HAVENOTHING;
749 m_errorOccured =
false;