From 2d229cf43579125da3374bdc4c52c2e6343ca950 Mon Sep 17 00:00:00 2001 From: TangoCash Date: Fri, 3 Nov 2017 21:18:53 +0100 Subject: [PATCH] update generic gstreamer Origin commit data ------------------ Branch: master Commit: https://github.com/neutrino-images/ni-libstb-hal/commit/8218368064688850fe84cde4970e2e8613156935 Author: TangoCash Date: 2017-11-03 (Fri, 03 Nov 2017) ------------------ No further description and justification available within origin commit message! ------------------ This commit was generated by Migit --- generic-pc/Makefile.am | 4 +- generic-pc/playback_gst.h | 93 ++-- generic-pc/playback_gst_10.cpp | 924 +++++++++++++++++++++------------ 3 files changed, 658 insertions(+), 363 deletions(-) diff --git a/generic-pc/Makefile.am b/generic-pc/Makefile.am index 58f5114..11f6db1 100644 --- a/generic-pc/Makefile.am +++ b/generic-pc/Makefile.am @@ -36,7 +36,9 @@ if ENABLE_GSTREAMER_10 libgeneric_la_SOURCES += \ playback_gst_10.cpp AM_LDFLAGS += \ - -lgstreamer-1.0 + -lgstreamer-1.0 \ + -lgsttag-1.0 \ + -lgstmpegts-1.0 else libgeneric_la_SOURCES += \ playback.cpp diff --git a/generic-pc/playback_gst.h b/generic-pc/playback_gst.h index 6d78fa1..a58de78 100644 --- a/generic-pc/playback_gst.h +++ b/generic-pc/playback_gst.h @@ -26,7 +26,8 @@ #include -typedef enum { +typedef enum +{ STATE_STOP, STATE_PLAY, STATE_PAUSE, @@ -35,55 +36,67 @@ typedef enum { STATE_SLOW } playstate_t; -typedef enum { +typedef enum +{ PLAYMODE_TS = 0, PLAYMODE_FILE, } playmode_t; +struct AVFormatContext; class cPlayback { - private: - bool playing; +private: + bool playing, first; + bool decoders_closed; - int mSpeed; - int mAudioStream; + int mSpeed; + int mAudioStream; + int init_jump; - public: - playstate_t playstate; - - cPlayback(int); - bool Open(playmode_t PlayMode); - void Close(void); - bool Start(char *filename, int vpid, int vtype, int apid, int ac3, int duration, std::string headers = ""); - bool Start(std::string filename, std::string headers = ""); - bool Play(void); - bool SyncAV(void); - - bool Stop(void); - bool SetAPid(int pid, bool ac3); - bool SetSubtitlePid(int pid); - bool SetTeletextPid(int pid); +public: + playstate_t playstate; - void trickSeek(int ratio); - bool SetSpeed(int speed); - bool SetSlow(int slow); - bool GetSpeed(int &speed) const; - bool GetPosition(int &position, int &duration); - void GetPts(uint64_t &pts); - bool SetPosition(int position, bool absolute = false); - void FindAllPids(int *apids, unsigned int *ac3flags, unsigned int *numpida, std::string *language); - void FindAllSubtitlePids(int *pids, unsigned int *numpids, std::string *language); - void FindAllTeletextsubtitlePids(int *pids, unsigned int *numpidt, std::string *tlanguage, int *mags, int *pages); - void RequestAbort(void); - void FindAllSubs(uint16_t *pids, unsigned short *supported, uint16_t *numpida, std::string *language); - bool SelectSubtitles(int pid); - uint64_t GetReadCount(void); - void GetChapters(std::vector &positions, std::vector &titles); - void GetMetadata(std::vector &keys, std::vector &values); - // - ~cPlayback(); - void getMeta(); + cPlayback(int); + bool Open(playmode_t PlayMode); + void Close(void); + bool Start(char *filename, int vpid, int vtype, int apid, int ac3, int duration, std::string headers = ""); + bool Start(std::string filename, std::string headers = ""); + bool Play(void); + bool SyncAV(void); + + bool Stop(void); + bool SetAPid(int pid, bool ac3); + bool SetSubtitlePid(int pid); + bool SetTeletextPid(int pid); + + void trickSeek(int ratio); + bool SetSpeed(int speed); + bool SetSlow(int slow); + bool GetSpeed(int &speed) const; + bool GetPosition(int &position, int &duration); + void GetPts(uint64_t &pts); + int GetAPid(void); + int GetVPid(void); + int GetSubtitlePid(void); + bool SetPosition(int position, bool absolute = false); + void FindAllPids(int *apids, unsigned int *ac3flags, unsigned int *numpida, std::string *language); + void FindAllSubtitlePids(int *pids, unsigned int *numpids, std::string *language); + void FindAllTeletextsubtitlePids(int *pids, unsigned int *numpidt, std::string *tlanguage, int *mags, int *pages); + void RequestAbort(void); + void FindAllSubs(uint16_t *pids, unsigned short *supported, uint16_t *numpida, std::string *language); + bool SelectSubtitles(int pid); + uint64_t GetReadCount(void); + void GetChapters(std::vector &positions, std::vector &titles); + void GetMetadata(std::vector &keys, std::vector &values); + AVFormatContext *GetAVFormatContext(); + void ReleaseAVFormatContext(); + std::string extra_headers; + std::string user_agent; + + // + ~cPlayback(); + void getMeta(); }; #endif diff --git a/generic-pc/playback_gst_10.cpp b/generic-pc/playback_gst_10.cpp index e386f12..1d5d9fb 100644 --- a/generic-pc/playback_gst_10.cpp +++ b/generic-pc/playback_gst_10.cpp @@ -46,29 +46,39 @@ static const char * FILENAME = "[playback.cpp]"; #include +#include +#include #include #include typedef enum { - GST_PLAY_FLAG_VIDEO = 0x00000001, - GST_PLAY_FLAG_AUDIO = 0x00000002, - GST_PLAY_FLAG_TEXT = 0x00000004, - GST_PLAY_FLAG_VIS = 0x00000008, - GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010, - GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020, - GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040, - GST_PLAY_FLAG_DOWNLOAD = 0x00000080, - GST_PLAY_FLAG_BUFFERING = 0x000000100 + GST_PLAY_FLAG_VIDEO = (1 << 0), + GST_PLAY_FLAG_AUDIO = (1 << 1), + GST_PLAY_FLAG_TEXT = (1 << 2), + GST_PLAY_FLAG_VIS = (1 << 3), + GST_PLAY_FLAG_SOFT_VOLUME = (1 << 4), + GST_PLAY_FLAG_NATIVE_AUDIO = (1 << 5), + GST_PLAY_FLAG_NATIVE_VIDEO = (1 << 6), + GST_PLAY_FLAG_DOWNLOAD = (1 << 7), + GST_PLAY_FLAG_BUFFERING = (1 << 8), + GST_PLAY_FLAG_DEINTERLACE = (1 << 9), + GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10), + GST_PLAY_FLAG_FORCE_FILTERS = (1 << 11), } GstPlayFlags; GstElement * m_gst_playbin = NULL; GstElement * audioSink = NULL; GstElement * videoSink = NULL; + gchar * uri = NULL; -GstTagList * m_stream_tags = 0; +GstTagList * m_stream_tags = NULL; static int end_eof = 0; +#define HTTP_TIMEOUT 30 +// taken from record.h +#define REC_MAX_APIDS 20 +int real_apids[REC_MAX_APIDS]; extern GLFramebuffer *glfb; @@ -78,191 +88,323 @@ gint match_sinktype(const GValue *velement, const gchar *type) return strcmp(g_type_name(G_OBJECT_TYPE(element)), type); } +void resetPids() +{ + for (unsigned int i = 0; i < REC_MAX_APIDS; i++) { + real_apids[i] = 0; + } +} + +void processMpegTsSection(GstMpegtsSection* section) +{ + resetPids(); + int cnt = 0; + if (section->section_type == GST_MPEGTS_SECTION_PMT) { + const GstMpegtsPMT* pmt = gst_mpegts_section_get_pmt(section); + for (guint i = 0; i < pmt->streams->len; ++i) { + const GstMpegtsPMTStream* stream = static_cast(g_ptr_array_index(pmt->streams, i)); + if (stream->stream_type == 0x05 || stream->stream_type >= 0x80) { + lt_info_c( "%s:%s Audio Stream pid: %d\n", FILENAME, __FUNCTION__, stream->pid); + real_apids[cnt] = stream->pid; + cnt++; + } + } + } +} + +void playbinNotifySource(GObject *object, GParamSpec *unused, gpointer user_data) +{ + GstElement *source = NULL; + cPlayback *_this = (cPlayback*)user_data; + g_object_get(object, "source", &source, NULL); + + if (source) + { + if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "timeout") != 0) + { + GstElementFactory *factory = gst_element_get_factory(source); + if (factory) + { + const gchar *sourcename = gst_plugin_feature_get_name(GST_PLUGIN_FEATURE(factory)); + if (!strcmp(sourcename, "souphttpsrc")) + { + g_object_set(G_OBJECT(source), "timeout", HTTP_TIMEOUT, NULL); + } + } + } + if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "ssl-strict") != 0) + { + g_object_set(G_OBJECT(source), "ssl-strict", FALSE, NULL); + } + if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "user-agent") != 0 && !_this->user_agent.empty()) + { + g_object_set(G_OBJECT(source), "user-agent", _this->user_agent.c_str(), NULL); + } + if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "extra-headers") != 0 && !_this->extra_headers.empty()) + { + GstStructure *extras = gst_structure_new_empty("extras"); + size_t pos = 0; + while (pos != std::string::npos) + { + std::string name, value; + size_t start = pos; + size_t len = std::string::npos; + pos = _this->extra_headers.find('=', pos); + if (pos != std::string::npos) + { + len = pos - start; + pos++; + name = _this->extra_headers.substr(start, len); + start = pos; + len = std::string::npos; + pos = _this->extra_headers.find('&', pos); + if (pos != std::string::npos) + { + len = pos - start; + pos++; + } + value = _this->extra_headers.substr(start, len); + } + if (!name.empty() && !value.empty()) + { + GValue header; + lt_info_c( "%s:%s setting extra-header '%s:%s'\n", FILENAME, __FUNCTION__, name.c_str(), value.c_str()); + memset(&header, 0, sizeof(GValue)); + g_value_init(&header, G_TYPE_STRING); + g_value_set_string(&header, value.c_str()); + gst_structure_set_value(extras, name.c_str(), &header); + } + else + { + lt_info_c( "%s:%s Invalid header format %s\n", FILENAME, __FUNCTION__, _this->extra_headers.c_str()); + break; + } + } + if (gst_structure_n_fields(extras) > 0) + { + g_object_set(G_OBJECT(source), "extra-headers", extras, NULL); + } + gst_structure_free(extras); + } + gst_object_unref(source); + } +} + GstBusSyncReply Gst_bus_call(GstBus * bus, GstMessage *msg, gpointer user_data) { gchar * sourceName; - + // source GstObject * source; source = GST_MESSAGE_SRC(msg); - + if (!GST_IS_OBJECT(source)) return GST_BUS_DROP; - + sourceName = gst_object_get_name(source); - switch (GST_MESSAGE_TYPE(msg)) + switch (GST_MESSAGE_TYPE(msg)) { - case GST_MESSAGE_EOS: - { - g_message("End-of-stream"); - end_eof = 1; - break; - } - - case GST_MESSAGE_ERROR: - { - gchar * debug; - GError *err; - gst_message_parse_error(msg, &err, &debug); - g_free (debug); - lt_info_c( "%s:%s - GST_MESSAGE_ERROR: %s (%i) from %s\n", FILENAME, __FUNCTION__, err->message, err->code, sourceName ); - if ( err->domain == GST_STREAM_ERROR ) - { - if ( err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND ) - { - if ( g_strrstr(sourceName, "videosink") ) - lt_info_c( "%s:%s - GST_MESSAGE_ERROR: videosink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event??? - else if ( g_strrstr(sourceName, "audiosink") ) - lt_info_c( "%s:%s - GST_MESSAGE_ERROR: audioSink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event??? - } - } - g_error_free(err); + case GST_MESSAGE_EOS: + { + g_message("End-of-stream"); + end_eof = 1; + break; + } - end_eof = 1; // NOTE: just to exit - - break; - } - - case GST_MESSAGE_INFO: + case GST_MESSAGE_ERROR: + { + gchar * debug; + GError *err; + gst_message_parse_error(msg, &err, &debug); + g_free (debug); + lt_info_c( "%s:%s - GST_MESSAGE_ERROR: %s (%i) from %s\n", FILENAME, __FUNCTION__, err->message, err->code, sourceName ); + if ( err->domain == GST_STREAM_ERROR ) { - gchar *debug; - GError *inf; - - gst_message_parse_info (msg, &inf, &debug); - g_free (debug); - if ( inf->domain == GST_STREAM_ERROR && inf->code == GST_STREAM_ERROR_DECODE ) + if ( err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND ) { if ( g_strrstr(sourceName, "videosink") ) - lt_info_c( "%s:%s - GST_MESSAGE_INFO: videosink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event??? + lt_info_c( "%s:%s - GST_MESSAGE_ERROR: videosink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event??? + else if ( g_strrstr(sourceName, "audiosink") ) + lt_info_c( "%s:%s - GST_MESSAGE_ERROR: audioSink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event??? } - g_error_free(inf); - break; } - - case GST_MESSAGE_TAG: - { - GstTagList *tags, *result; - gst_message_parse_tag(msg, &tags); - - result = gst_tag_list_merge(m_stream_tags, tags, GST_TAG_MERGE_REPLACE); - if (result) - { - if (m_stream_tags) - gst_tag_list_free(m_stream_tags); - m_stream_tags = result; - } - - const GValue *gv_image = gst_tag_list_get_value_index(tags, GST_TAG_IMAGE, 0); - if ( gv_image ) - { - GstBuffer *buf_image; - buf_image = gst_value_get_buffer (gv_image); - int fd = open("/tmp/.id3coverart", O_CREAT|O_WRONLY|O_TRUNC, 0644); - if(fd >= 0) - { - GstMapInfo Info; - gst_buffer_map(buf_image, &Info,(GstMapFlags)( GST_MAP_READ)); - int ret = write(fd, Info.data, Info.size); - close(fd); - gst_buffer_unmap(buf_image, &Info); - lt_info_c( "%s:%s - GST_MESSAGE_INFO: cPlayback::state /tmp/.id3coverart %d bytes written\n", FILENAME, __FUNCTION__ , ret); - } - //FIXME: how shall playback handle this event??? - } - gst_tag_list_free(tags); - lt_debug_c( "%s:%s - GST_MESSAGE_INFO: update info tags\n", FILENAME, __FUNCTION__); //FIXME: how shall playback handle this event??? - break; - } - - case GST_MESSAGE_STATE_CHANGED: - { - if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin)) - break; + g_error_free(err); - GstState old_state, new_state; - gst_message_parse_state_changed(msg, &old_state, &new_state, NULL); - - if(old_state == new_state) - break; - lt_info_c( "%s:%s - GST_MESSAGE_STATE_CHANGED: state transition %s -> %s\n", FILENAME, __FUNCTION__, gst_element_state_get_name(old_state), gst_element_state_get_name(new_state)); - - GstStateChange transition = (GstStateChange)GST_STATE_TRANSITION(old_state, new_state); - - switch(transition) - { - case GST_STATE_CHANGE_NULL_TO_READY: - { - } break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - { - GstIterator *children; - if (audioSink) - { - gst_object_unref(GST_OBJECT(audioSink)); - audioSink = NULL; - } - - if (videoSink) - { - gst_object_unref(GST_OBJECT(videoSink)); - videoSink = NULL; - } - children = gst_bin_iterate_recurse(GST_BIN(m_gst_playbin)); - GValue r = G_VALUE_INIT; - gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, &r, (gpointer)"GstDVBAudioSink"); - audioSink = GST_ELEMENT_CAST(g_value_dup_object (&r)); - g_value_unset (&r); - gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, &r, (gpointer)"GstDVBVideoSink"); - videoSink = GST_ELEMENT_CAST(g_value_dup_object (&r)); - g_value_unset (&r); - gst_iterator_free(children); - - } break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - { - } break; - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - { - } break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - { - if (audioSink) - { - gst_object_unref(GST_OBJECT(audioSink)); - audioSink = NULL; - } - if (videoSink) - { - gst_object_unref(GST_OBJECT(videoSink)); - videoSink = NULL; - } - } break; - case GST_STATE_CHANGE_READY_TO_NULL: - { - } break; - } - break; - } -#if 0 - case GST_MESSAGE_ELEMENT: + end_eof = 1; // NOTE: just to exit + + break; + } + + case GST_MESSAGE_INFO: + { + gchar *debug; + GError *inf; + + gst_message_parse_info (msg, &inf, &debug); + g_free (debug); + if ( inf->domain == GST_STREAM_ERROR && inf->code == GST_STREAM_ERROR_DECODE ) { - if(gst_structure_has_name(gst_message_get_structure(msg), "prepare-window-handle")) + if ( g_strrstr(sourceName, "videosink") ) + lt_info_c( "%s:%s - GST_MESSAGE_INFO: videosink\n", FILENAME, __FUNCTION__ ); //FIXME: how shall playback handle this event??? + } + g_error_free(inf); + break; + } + + case GST_MESSAGE_TAG: + { + GstTagList *tags, *result; + gst_message_parse_tag(msg, &tags); + + result = gst_tag_list_merge(m_stream_tags, tags, GST_TAG_MERGE_REPLACE); + if (result) + { + if (m_stream_tags && gst_tag_list_is_equal(m_stream_tags, result)) { - // set window id - gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC (msg)), glfb->getWindowID()); - - // reshape window - gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC (msg)), 0, 0, glfb->getOSDWidth(), glfb->getOSDHeight()); - - // sync frames - gst_video_overlay_expose(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC (msg))); + gst_tag_list_unref(tags); + gst_tag_list_unref(result); + break; } + if (m_stream_tags) + gst_tag_list_unref(m_stream_tags); + m_stream_tags = gst_tag_list_copy(result); + gst_tag_list_unref(result); + } + + const GValue *gv_image = gst_tag_list_get_value_index(tags, GST_TAG_IMAGE, 0); + if ( gv_image ) + { + GstBuffer *buf_image; + GstSample *sample; + sample = (GstSample *)g_value_get_boxed(gv_image); + buf_image = gst_sample_get_buffer(sample); + int fd = open("/tmp/.id3coverart", O_CREAT|O_WRONLY|O_TRUNC, 0644); + if (fd >= 0) + { + guint8 *data; + gsize size; + GstMapInfo map; + gst_buffer_map(buf_image, &map, GST_MAP_READ); + data = map.data; + size = map.size; + int ret = write(fd, data, size); + gst_buffer_unmap(buf_image, &map); + close(fd); + lt_info_c("%s:%s - /tmp/.id3coverart %d bytes written\n", FILENAME, __FUNCTION__, ret); + } + } + gst_tag_list_unref(tags); + lt_debug_c( "%s:%s - GST_MESSAGE_INFO: update info tags\n", FILENAME, __FUNCTION__); //FIXME: how shall playback handle this event??? + break; + } + case GST_MESSAGE_ELEMENT: + { +#if 0 + if(gst_structure_has_name(gst_message_get_structure(msg), "prepare-window-handle")) + { + // set window id + gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC (msg)), glfb->getWindowID()); + + // reshape window + gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC (msg)), 0, 0, glfb->getOSDWidth(), glfb->getOSDHeight()); + + // sync frames + gst_video_overlay_expose(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC (msg))); } #endif - break; - default: + GstMpegtsSection* section = gst_message_parse_mpegts_section(msg); + if (section) { + processMpegTsSection(section); + gst_mpegts_section_unref(section); + } + } + case GST_MESSAGE_STATE_CHANGED: + { + if(GST_MESSAGE_SRC(msg) != GST_OBJECT(m_gst_playbin)) break; + + GstState old_state, new_state; + gst_message_parse_state_changed(msg, &old_state, &new_state, NULL); + + if(old_state == new_state) + break; + lt_info_c( "%s:%s - GST_MESSAGE_STATE_CHANGED: state transition %s -> %s\n", FILENAME, __FUNCTION__, gst_element_state_get_name(old_state), gst_element_state_get_name(new_state)); + + GstStateChange transition = (GstStateChange)GST_STATE_TRANSITION(old_state, new_state); + + switch(transition) + { + case GST_STATE_CHANGE_NULL_TO_READY: + { + } break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + { + GstIterator *children; + GValue r = { 0, }; + + if (audioSink) + { + gst_object_unref(GST_OBJECT(audioSink)); + audioSink = NULL; + } + + if (videoSink) + { + gst_object_unref(GST_OBJECT(videoSink)); + videoSink = NULL; + } + children = gst_bin_iterate_recurse(GST_BIN(m_gst_playbin)); + + if (gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, &r, (gpointer)"GstDVBAudioSink")) + { + audioSink = GST_ELEMENT_CAST(g_value_dup_object (&r)); + g_value_unset (&r); + lt_info_c( "%s %s - audio sink created\n", FILENAME, __FUNCTION__); + } + + gst_iterator_free(children); + children = gst_bin_iterate_recurse(GST_BIN(m_gst_playbin)); + + if (gst_iterator_find_custom(children, (GCompareFunc)match_sinktype, &r, (gpointer)"GstDVBVideoSink")) + { + videoSink = GST_ELEMENT_CAST(g_value_dup_object (&r)); + g_value_unset (&r); + lt_info_c( "%s %s - video sink created\n", FILENAME, __FUNCTION__); + } + gst_iterator_free(children); + + } + break; + case GST_STATE_CHANGE_PAUSED_TO_PLAYING: + { + } break; + case GST_STATE_CHANGE_PLAYING_TO_PAUSED: + { + } break; + case GST_STATE_CHANGE_PAUSED_TO_READY: + { + if (audioSink) + { + gst_object_unref(GST_OBJECT(audioSink)); + audioSink = NULL; + } + if (videoSink) + { + gst_object_unref(GST_OBJECT(videoSink)); + videoSink = NULL; + } + } + break; + case GST_STATE_CHANGE_READY_TO_NULL: + { + } break; + } + break; + } + break; + default: + break; } return GST_BUS_DROP; @@ -270,53 +412,59 @@ GstBusSyncReply Gst_bus_call(GstBus * bus, GstMessage *msg, gpointer user_data) cPlayback::cPlayback(int num) -{ +{ lt_info( "%s:%s\n", FILENAME, __FUNCTION__); - const gchar *nano_str; - guint major, minor, micro, nano; + const gchar *nano_str; + guint major, minor, micro, nano; gst_init(NULL, NULL); gst_version (&major, &minor, µ, &nano); - if (nano == 1) - nano_str = "(CVS)"; - else if (nano == 2) - nano_str = "(Prerelease)"; + if (nano == 1) + nano_str = "(CVS)"; + else if (nano == 2) + nano_str = "(Prerelease)"; else - nano_str = ""; + nano_str = ""; - lt_info( "%s:%s - This program is linked against GStreamer %d.%d.%d %s\n", - FILENAME, __FUNCTION__, - major, minor, micro, nano_str); + lt_info( "%s:%s - This program is linked against GStreamer %d.%d.%d %s\n", + FILENAME, __FUNCTION__, + major, minor, micro, nano_str); mAudioStream = 0; mSpeed = 0; playing = false; playstate = STATE_STOP; + decoders_closed = false; + first = false; } cPlayback::~cPlayback() -{ +{ lt_info( "%s:%s\n", FILENAME, __FUNCTION__); //FIXME: all deleting stuff is done in Close() + if (m_stream_tags) + gst_tag_list_unref(m_stream_tags); } //Used by Fileplay bool cPlayback::Open(playmode_t PlayMode) { lt_info("%s: PlayMode %d\n", __func__, PlayMode); + + init_jump = -1; return true; } // used by movieplay void cPlayback::Close(void) -{ +{ lt_info( "%s:%s\n", FILENAME, __FUNCTION__); Stop(); - + // disconnect bus handler if (m_gst_playbin) { @@ -326,9 +474,6 @@ void cPlayback::Close(void) gst_object_unref(bus); lt_info( "%s:%s - GST bus handler closed\n", FILENAME, __FUNCTION__); } - - if (m_stream_tags) - gst_tag_list_free(m_stream_tags); // close gst if (m_gst_playbin) @@ -337,24 +482,26 @@ void cPlayback::Close(void) { gst_object_unref(GST_OBJECT(audioSink)); audioSink = NULL; - + lt_info( "%s:%s - GST audio Sink closed\n", FILENAME, __FUNCTION__); } - + if (videoSink) { gst_object_unref(GST_OBJECT(videoSink)); videoSink = NULL; - + lt_info( "%s:%s - GST video Sink closed\n", FILENAME, __FUNCTION__); } - + // unref m_gst_playbin gst_object_unref (GST_OBJECT (m_gst_playbin)); lt_info( "%s:%s - GST playbin closed\n", FILENAME, __FUNCTION__); - + m_gst_playbin = NULL; + } + } // start @@ -367,16 +514,32 @@ bool cPlayback::Start(char *filename, int /*vpid*/, int /*vtype*/, int /*apid*/, { lt_info( "%s:%s\n", FILENAME, __FUNCTION__); + if (!headers.empty()) + extra_headers = headers; + else + extra_headers.clear(); + + resetPids(); + mAudioStream = 0; - + init_jump = -1; + + gst_tag_list_unref(m_stream_tags); + m_stream_tags = NULL; + + unlink("/tmp/.id3coverart"); + //create playback path - char file[400] = {""}; bool isHTTP = false; if(!strncmp("http://", filename, 7)) { isHTTP = true; } + else if(!strncmp("https://", filename, 8)) + { + isHTTP = true; + } else if(!strncmp("file://", filename, 7)) { isHTTP = false; @@ -397,18 +560,20 @@ bool cPlayback::Start(char *filename, int /*vpid*/, int /*vtype*/, int /*apid*/, { isHTTP = true; } - else - strcat(file, "file://"); - - strcat(file, filename); - + if (isHTTP) - uri = g_uri_escape_string(filename, G_URI_RESERVED_CHARS_GENERIC_DELIMITERS, true); + uri = g_strdup_printf ("%s", filename); else uri = g_filename_to_uri(filename, NULL, NULL); - + lt_info("%s:%s - filename=%s\n", FILENAME, __FUNCTION__, filename); + guint flags = GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | \ + GST_PLAY_FLAG_TEXT | GST_PLAY_FLAG_NATIVE_VIDEO; + + /* increase the default 2 second / 2 MB buffer limitations to 5s / 5MB */ + int m_buffer_size = 5*1024*1024; + // create gst pipeline m_gst_playbin = gst_element_factory_make ("playbin", "playbin"); @@ -416,57 +581,65 @@ bool cPlayback::Start(char *filename, int /*vpid*/, int /*vtype*/, int /*apid*/, { lt_info("%s:%s - m_gst_playbin\n", FILENAME, __FUNCTION__); - guint flags; - g_object_get(G_OBJECT (m_gst_playbin), "flags", &flags, NULL); - /* avoid video conversion, let the (hardware) sinks handle that */ - flags |= GST_PLAY_FLAG_NATIVE_VIDEO; - /* volume control is done by hardware */ - flags &= ~GST_PLAY_FLAG_SOFT_VOLUME; + if(isHTTP) + { + g_signal_connect (G_OBJECT (m_gst_playbin), "notify::source", G_CALLBACK (playbinNotifySource), this); + + // set buffer size + g_object_set(G_OBJECT(m_gst_playbin), "buffer-size", m_buffer_size, NULL); + g_object_set(G_OBJECT(m_gst_playbin), "buffer-duration", 5LL * GST_SECOND, NULL); + flags |= GST_PLAY_FLAG_BUFFERING; + } + + g_object_set(G_OBJECT (m_gst_playbin), "flags", flags, NULL); g_object_set(G_OBJECT (m_gst_playbin), "uri", uri, NULL); - g_object_set(G_OBJECT (m_gst_playbin), "flags", flags, NULL); - + //gstbus handler GstBus * bus = gst_pipeline_get_bus( GST_PIPELINE(m_gst_playbin) ); gst_bus_set_sync_handler(bus, Gst_bus_call, NULL, NULL); - gst_object_unref(bus); - + gst_object_unref(bus); + + first = true; + // state playing - gst_element_set_state(m_gst_playbin, GST_STATE_PLAYING); - + if(isHTTP) + { + gst_element_set_state(GST_ELEMENT(m_gst_playbin), GST_STATE_PLAYING); + playstate = STATE_PLAY; + } + else + { + gst_element_set_state(GST_ELEMENT(m_gst_playbin), GST_STATE_PAUSED); + playstate = STATE_PAUSE; + } + playing = true; - playstate = STATE_PLAY; } else { lt_info("%s:%s - failed to create GStreamer pipeline!, sorry we can not play\n", FILENAME, __FUNCTION__); playing = false; - + return false; } - - g_free(uri); - // set buffer size - /* increase the default 2 second / 2 MB buffer limitations to 5s / 5MB */ - int m_buffer_size = 5*1024*1024; - //g_object_set(G_OBJECT(m_gst_playbin), "buffer-duration", 5LL * GST_SECOND, NULL); - g_object_set(G_OBJECT(m_gst_playbin), "buffer-size", m_buffer_size, NULL); - + g_free(uri); + return true; } bool cPlayback::Play(void) { - lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); + lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); - if(playing == true) + if(playing == true) return true; - + if(m_gst_playbin) { gst_element_set_state(GST_ELEMENT(m_gst_playbin), GST_STATE_PLAYING); - + playing = true; playstate = STATE_PLAY; } @@ -476,11 +649,11 @@ bool cPlayback::Play(void) } bool cPlayback::Stop(void) -{ - if(playing == false) +{ + if(playing == false) return false; lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); - + // stop if(m_gst_playbin) { @@ -488,20 +661,20 @@ bool cPlayback::Stop(void) } playing = false; - + lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); - + playstate = STATE_STOP; return true; } -bool cPlayback::SetAPid(int pid , bool /*ac3*/) +bool cPlayback::SetAPid(int pid, bool /*ac3*/) { lt_info("%s: pid %i\n", __func__, pid); - + int current_audio; - + if(pid != mAudioStream) { g_object_set (G_OBJECT (m_gst_playbin), "current-audio", pid, NULL); @@ -514,37 +687,29 @@ bool cPlayback::SetAPid(int pid , bool /*ac3*/) void cPlayback::trickSeek(int ratio) { - bool validposition = false; + GstFormat fmt = GST_FORMAT_TIME; gint64 pos = 0; - int position; - int duration; - - if( GetPosition(position, duration) ) - { - validposition = true; - pos = position; - } gst_element_set_state(m_gst_playbin, GST_STATE_PLAYING); - - if (validposition) + + if (gst_element_query_position(m_gst_playbin, fmt, &pos)) { if(ratio >= 0.0) - gst_element_seek(m_gst_playbin, ratio, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SKIP), GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_SET, -1); + gst_element_seek(m_gst_playbin, ratio, fmt, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SKIP), GST_SEEK_TYPE_SET, pos, GST_SEEK_TYPE_SET, -1); else - gst_element_seek(m_gst_playbin, ratio, GST_FORMAT_TIME, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SKIP), GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, pos); + gst_element_seek(m_gst_playbin, ratio, fmt, (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_SKIP), GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_SET, pos); } } bool cPlayback::SetSpeed(int speed) -{ - lt_info( "%s:%s speed %d\n", FILENAME, __FUNCTION__, speed); +{ + lt_info( "%s:%s speed %d\n", FILENAME, __FUNCTION__, speed); - if(playing == false) + if(playing == false) return false; if(m_gst_playbin) - { + { // pause if(speed == 0) { @@ -574,6 +739,12 @@ bool cPlayback::SetSpeed(int speed) // playstate = STATE_REW; } + + if (init_jump > -1) + { + SetPosition(init_jump,true); + init_jump = -1; + } } mSpeed = speed; @@ -582,10 +753,10 @@ bool cPlayback::SetSpeed(int speed) } bool cPlayback::SetSlow(int slow) -{ - lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); +{ + lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); - if(playing == false) + if(playing == false) return false; if(m_gst_playbin) @@ -610,8 +781,8 @@ bool cPlayback::GetSpeed(int &speed) const // in milliseconds bool cPlayback::GetPosition(int &position, int &duration) { - if(playing == false) - return false; + if(playing == false) + return false; //EOF if(end_eof) @@ -619,18 +790,26 @@ bool cPlayback::GetPosition(int &position, int &duration) end_eof = 0; return false; } - + if(m_gst_playbin) { //position GstFormat fmt = GST_FORMAT_TIME; //Returns time in nanosecs - gint64 pts = 0; - unsigned long long int sec = 0; - - gst_element_query_position(m_gst_playbin, fmt, &pts); + if (audioSink || videoSink) + { + g_signal_emit_by_name(audioSink ? audioSink : videoSink, "get-decoder-time", &pts); + if (!GST_CLOCK_TIME_IS_VALID(pts)) + { + lt_info( "%s - %d failed\n", __FUNCTION__, __LINE__); + } + } + else + { + if(!gst_element_query_position(m_gst_playbin, fmt, &pts)) + lt_info( "%s - %d failed\n", __FUNCTION__, __LINE__); + } position = pts / 1000000.0; - // duration GstFormat fmt_d = GST_FORMAT_TIME; //Returns time in nanosecs double length = 0; @@ -638,33 +817,46 @@ bool cPlayback::GetPosition(int &position, int &duration) gst_element_query_duration(m_gst_playbin, fmt_d, &len); length = len / 1000000.0; - if(length < 0) + if(length < 0) length = 0; - + duration = (int)(length); } - + return true; } bool cPlayback::SetPosition(int position, bool absolute) { lt_info("%s: pos %d abs %d playing %d\n", __func__, position, absolute, playing); - - if(playing == false) - return false; - + gint64 time_nanoseconds; gint64 pos; GstFormat fmt = GST_FORMAT_TIME; - + GstState state; + if(m_gst_playbin) { - gst_element_query_position(m_gst_playbin, fmt, &pos); - time_nanoseconds = pos + (position * 1000000.0); - if(time_nanoseconds < 0) - time_nanoseconds = 0; - + gst_element_get_state(m_gst_playbin, &state, NULL, GST_CLOCK_TIME_NONE); + + if ( (state == GST_STATE_PAUSED) && first) + { + init_jump = position; + first = false; + return false; + } + if (!absolute) + { + gst_element_query_position(m_gst_playbin, fmt, &pos); + time_nanoseconds = pos + (position * 1000000.0); + if(time_nanoseconds < 0) + time_nanoseconds = 0; + } + else + { + time_nanoseconds = position * 1000000.0; + } + gst_element_seek(m_gst_playbin, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, GST_SEEK_TYPE_SET, time_nanoseconds, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE); } @@ -672,72 +864,62 @@ bool cPlayback::SetPosition(int position, bool absolute) } void cPlayback::FindAllPids(int *apids, unsigned int *ac3flags, unsigned int *numpida, std::string * language) -{ +{ lt_info( "%s:%s\n", FILENAME, __FUNCTION__); if(m_gst_playbin) { gint i, n_audio = 0; - //GstStructure * structure = NULL; - + // get audio g_object_get (m_gst_playbin, "n-audio", &n_audio, NULL); - printf("%s: %d audio\n", __FUNCTION__, n_audio); - + lt_info("%s: %d audio\n", __FUNCTION__, n_audio); + if(n_audio == 0) return; - + + language->clear(); + for (i = 0; i < n_audio; i++) { // apids - apids[i]=i; - + apids[i]= real_apids[i] ? real_apids[i] : i; + GstPad * pad = 0; g_signal_emit_by_name (m_gst_playbin, "get-audio-pad", i, &pad); + GstCaps * caps = gst_pad_get_current_caps(pad); + gst_object_unref(pad); + if (!caps) continue; - + GstStructure * structure = gst_caps_get_structure(caps, 0); - //const gchar *g_type = gst_structure_get_name(structure); - - //if (!structure) - //return atUnknown; - //ac3flags[0] = 0; + GstTagList * tags = NULL; + gchar * g_lang = NULL; + gchar * g_codec = NULL; // ac3flags if ( gst_structure_has_name (structure, "audio/mpeg")) { gint mpegversion, layer = -1; - + if (!gst_structure_get_int (structure, "mpegversion", &mpegversion)) - //return atUnknown; ac3flags[i] = 0; - switch (mpegversion) + switch (mpegversion) { - case 1: - /* - { - gst_structure_get_int (structure, "layer", &layer); - if ( layer == 3 ) - return atMP3; - else - return atMPEG; - ac3flags[0] = 4; - break; - } - */ - ac3flags[i] = 4; - case 2: - //return atAAC; - ac3flags[i] = 5; - case 4: - //return atAAC; - ac3flags[i] = 5; - default: - //return atUnknown; - ac3flags[i] = 0; + case 1: + ac3flags[i] = 4; + case 2: + //return atAAC; + ac3flags[i] = 5; + case 4: + //return atAAC; + ac3flags[i] = 5; + default: + //return atUnknown; + ac3flags[i] = 0; } } else if ( gst_structure_has_name (structure, "audio/x-ac3") || gst_structure_has_name (structure, "audio/ac3") ) @@ -749,26 +931,38 @@ void cPlayback::FindAllPids(int *apids, unsigned int *ac3flags, unsigned int *nu else if ( gst_structure_has_name (structure, "audio/x-raw-int") ) //return atPCM; ac3flags[i] = 0; - + gst_caps_unref(caps); + + //(ac3flags[i] > 2) ? ac3flags[i] = 1 : ac3flags[i] = 0; + + g_signal_emit_by_name (m_gst_playbin, "get-audio-tags", i, &tags); + if (tags && GST_IS_TAG_LIST(tags)) + { + if (gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &g_lang)) + { + language[i] = std::string(gst_tag_get_language_name(g_lang)).c_str(); + lt_info("%s: language:%s\n", __FUNCTION__, language[i].c_str()); + g_free(g_lang); + } + gst_tag_list_free(tags); + } } - - // numpids *numpida=i; } } void cPlayback::getMeta() { - if(playing) + if(playing) return; } bool cPlayback::SyncAV(void) { - lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); + lt_info( "%s:%s playing %d\n", FILENAME, __FUNCTION__, playing); - if(playing == false ) + if(playing == false ) return false; return true; @@ -800,6 +994,62 @@ void cPlayback::GetMetadata(std::vector &keys, std::vector