diff --git a/.gitignore b/.gitignore index 9a851162..3a695f8d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,11 @@ ffmpeg help.h keys.h patch.flag +.cproject +.project +.settings +omxplayer-dist +omxplayer-dist.tgz +omxplayer.1 +version.h +MAN diff --git a/KeyConfig.cpp b/KeyConfig.cpp index 2aadbd8f..78faa075 100644 --- a/KeyConfig.cpp +++ b/KeyConfig.cpp @@ -62,7 +62,21 @@ int convertStringToAction(string str_action) return KeyConfig::ACTION_SHOW_SUBTITLES; if(str_action == "HIDE_SUBTITLES") return KeyConfig::ACTION_HIDE_SUBTITLES; - + if(str_action == "TOGGLE_TITLE") + return KeyConfig::ACTION_TOGGLE_TITLE; + if(str_action == "SHOW_TITLE") + return KeyConfig::ACTION_SHOW_TITLE; + if(str_action == "HIDE_TITLE") + return KeyConfig::ACTION_HIDE_TITLE; + if(str_action == "TOGGLE_TIME") + return KeyConfig::ACTION_TOGGLE_TIME; + if(str_action == "SHOW_TIME") + return KeyConfig::ACTION_SHOW_TIME; + if(str_action == "HIDE_TIME") + return KeyConfig::ACTION_HIDE_TIME; + if(str_action == "SET_TITLE") + return KeyConfig::ACTION_SET_TITLE; + return -1; } /* Grabs the substring prior to the ':', this is the Action */ @@ -128,6 +142,12 @@ map KeyConfig::buildDefaultKeymap() keymap['v'] = ACTION_STEP; keymap['w'] = ACTION_SHOW_SUBTITLES; keymap['x'] = ACTION_HIDE_SUBTITLES; + keymap['t'] = ACTION_TOGGLE_TITLE; + keymap['e'] = ACTION_SHOW_TITLE; + keymap['r'] = ACTION_HIDE_TITLE; + keymap['y'] = ACTION_TOGGLE_TIME; + keymap['g'] = ACTION_SHOW_TIME; + keymap['h'] = ACTION_HIDE_TIME; return keymap; } diff --git a/KeyConfig.h b/KeyConfig.h index e90a2ea6..5e87a0a4 100644 --- a/KeyConfig.h +++ b/KeyConfig.h @@ -45,6 +45,13 @@ class KeyConfig ACTION_PLAY = 36, ACTION_CHANGE_FILE = 37, ACTION_SET_LAYER = 38, + ACTION_TOGGLE_TITLE = 39, + ACTION_HIDE_TITLE = 40, + ACTION_SHOW_TITLE = 41, + ACTION_TOGGLE_TIME = 42, + ACTION_HIDE_TIME = 43, + ACTION_SHOW_TIME = 44, + ACTION_SET_TITLE = 45 }; #define KEY_LEFT 0x5b44 diff --git a/OMXControl.cpp b/OMXControl.cpp index 7345d8b2..fa224b2a 100644 --- a/OMXControl.cpp +++ b/OMXControl.cpp @@ -1125,6 +1125,56 @@ OMXControlResult OMXControl::handle_event(DBusMessage *m) dbus_respond_ok(m); return KeyConfig::ACTION_HIDE_SUBTITLES; } + else if (dbus_message_is_method_call(m, OMXPLAYER_DBUS_INTERFACE_PLAYER, "SetTitle")) + { + DBusError error; + dbus_error_init(&error); + + const char *title; + dbus_message_get_args(m, &error, DBUS_TYPE_STRING, &title, DBUS_TYPE_INVALID); + + if (dbus_error_is_set(&error)) + { + CLog::Log(LOGWARNING, "Set title D-Bus Error: %s", error.message ); + dbus_error_free(&error); + dbus_respond_ok(m); + return KeyConfig::ACTION_BLANK; + } + else + { + dbus_respond_string(m, title); + return OMXControlResult(KeyConfig::ACTION_SET_TITLE, title); + } + } + else if (dbus_message_is_method_call(m, OMXPLAYER_DBUS_INTERFACE_PLAYER, "GetTitle")) + { + dbus_respond_string(m, subtitles->GetTitle().c_str()); + return KeyConfig::ACTION_BLANK; + } + else if (dbus_message_is_method_call(m, OMXPLAYER_DBUS_INTERFACE_PLAYER, "ShowTitle")) + { + subtitles->SetTitleVisible(true); + dbus_respond_ok(m); + return KeyConfig::ACTION_SHOW_TITLE; + } + else if (dbus_message_is_method_call(m, OMXPLAYER_DBUS_INTERFACE_PLAYER, "HideTitle")) + { + subtitles->SetTitleVisible(false); + dbus_respond_ok(m); + return KeyConfig::ACTION_HIDE_TITLE; + } + else if (dbus_message_is_method_call(m, OMXPLAYER_DBUS_INTERFACE_PLAYER, "ShowTime")) + { + subtitles->SetTimeVisible(true); + dbus_respond_ok(m); + return KeyConfig::ACTION_SHOW_TIME; + } + else if (dbus_message_is_method_call(m, OMXPLAYER_DBUS_INTERFACE_PLAYER, "HideTime")) + { + subtitles->SetTimeVisible(false); + dbus_respond_ok(m); + return KeyConfig::ACTION_HIDE_TIME; + } else if (dbus_message_is_method_call(m, OMXPLAYER_DBUS_INTERFACE_PLAYER, "OpenUri")) { DBusError error; diff --git a/OMXPlayerSubtitles.cpp b/OMXPlayerSubtitles.cpp index 7f074c3e..13363289 100644 --- a/OMXPlayerSubtitles.cpp +++ b/OMXPlayerSubtitles.cpp @@ -23,6 +23,7 @@ #include "utils/ScopeExit.h" #include "utils/Clamp.h" #include "utils/log.h" +#include "utils/Strprintf.h" #include #include @@ -41,6 +42,7 @@ OMXPlayerSubtitles::OMXPlayerSubtitles() BOOST_NOEXCEPT m_thread_stopped(), m_font_size(), m_centered(), + m_title_centered(), m_ghost_box(), m_lines(), m_av_clock(), @@ -58,12 +60,17 @@ bool OMXPlayerSubtitles::Open(size_t stream_count, vector&& external_subtitles, const string& font_path, const string& italic_font_path, + const string& title_font_path, float font_size, + float title_font_size, bool centered, + bool title_centered, bool ghost_box, unsigned int lines, int display, int layer, - OMXClock* clock) BOOST_NOEXCEPT + OMXClock* clock, + const string& title, + bool show_time) BOOST_NOEXCEPT { assert(!m_open); @@ -78,18 +85,27 @@ bool OMXPlayerSubtitles::Open(size_t stream_count, m_font_path = font_path; m_italic_font_path = italic_font_path; + m_title_font_path = title_font_path; m_font_size = font_size; + m_title_font_size = title_font_size; m_centered = centered; + m_title_centered = title_centered; m_ghost_box = ghost_box; m_lines = lines; m_av_clock = clock; m_display = display; m_layer = layer; + m_title = title; + m_show_title = (title != ""); + m_show_time = show_time; if(!Create()) return false; SendToRenderer(Message::Flush{m_external_subtitles}); + SendToRenderer(Message::SetTitle{m_title}); + SendToRenderer(Message::SetShowTitle{m_show_title}); + SendToRenderer(Message::SetShowTime{m_show_time}); #ifndef NDEBUG m_open = true; @@ -118,8 +134,8 @@ void OMXPlayerSubtitles::Process() { try { - RenderLoop(m_font_path, m_italic_font_path, m_font_size, m_centered, - m_ghost_box, m_lines, m_av_clock); + RenderLoop(m_font_path, m_italic_font_path, m_title_font_path, m_font_size, m_title_font_size, + m_centered, m_title_centered, m_ghost_box, m_lines, m_av_clock); } catch(Enforce_error& e) { @@ -146,8 +162,11 @@ Iterator FindSubtitle(Iterator begin, Iterator end, int time) void OMXPlayerSubtitles:: RenderLoop(const string& font_path, const string& italic_font_path, + const string& title_font_path, float font_size, + float title_font_size, bool centered, + bool title_centered, bool ghost_box, unsigned int lines, OMXClock* clock) @@ -155,9 +174,12 @@ RenderLoop(const string& font_path, SubtitleRenderer renderer(m_display, m_layer, font_path, italic_font_path, + title_font_path, font_size, - 0.01f, 0.06f, + title_font_size, + 0.06f, 0.06f, centered, + title_centered, 0xDD, ghost_box ? 0x80 : 0, lines); @@ -174,12 +196,36 @@ RenderLoop(const string& font_path, bool osd{}; chrono::time_point osd_stop; int delay{}; + bool show_time{}; auto GetCurrentTime = [&] { return static_cast(clock->OMXMediaTime()/1000) - delay; }; + auto PrepareTime = [&] + { + extern OMXReader m_omx_reader; + auto t = (unsigned) (m_av_clock->OMXMediaTime()*1e-3); + auto dur = m_omx_reader.GetStreamLength() / 1000; + + std::string time_curr; + std::string time_total; + int hours = t/3600000; + if (hours) + time_curr = strprintf("%02d:%02d:%02d", hours, (t/60000)%60, (t/1000)%60); + else + time_curr = strprintf("%02d:%02d", (t/60000)%60, (t/1000)%60); + + hours = dur/3600; + if (hours) + time_total = strprintf("%02d:%02d:%02d", hours, (dur/60)%60, dur%60); + else + time_total = strprintf("%02d:%02d", (dur/60)%60, dur%60); + + renderer.prepare_time(time_curr + " / " + time_total); + }; + auto TryPrepare = [&](int time) { for(; next_index != subtitles.size(); ++next_index) @@ -214,6 +260,9 @@ RenderLoop(const string& font_path, } }; + auto show_time_offset = GetCurrentTime() % 1000; + chrono::time_point show_time_next_update; + for(;;) { int timeout = INT_MAX; @@ -230,7 +279,18 @@ RenderLoop(const string& font_path, have_next ? subtitles[next_index].start - now : INT_MAX; - timeout = min(min(till_stop, till_next_start), 1000); + int till_next_show_time = 1000; + if (show_time) + till_next_show_time = 1000 - (GetCurrentTime() % 1000) + show_time_offset; + + timeout = min(min(till_next_show_time, min(till_stop, till_next_start)), 1000); + } + + if(show_time) + { + procrustes(timeout, + chrono::duration_cast( + show_time_next_update - chrono::steady_clock::now()).count()); } if(osd) @@ -269,6 +329,8 @@ RenderLoop(const string& font_path, [&](Message::DisplayText&& args) { renderer.prepare(args.text_lines); + if (show_time) + PrepareTime(); // Also regenerate displayed time. renderer.show_next(); showing = true; osd = true; @@ -279,6 +341,33 @@ RenderLoop(const string& font_path, [&](Message::SetRect&& args) { renderer.set_rect(args.x1, args.y1, args.x2, args.y2); + }, + [&](Message::SetTitle&& args) + { + m_title = args.title; + if (m_show_title) + renderer.prepare_title(m_title); + else + renderer.prepare_title(""); + renderer.redraw(); + }, + [&](Message::SetShowTitle&& args) + { + m_show_title = args.show; + if (m_show_title) + renderer.prepare_title(m_title); + else + renderer.prepare_title(""); + renderer.redraw(); + }, + [&](Message::SetShowTime&& args) + { + show_time = args.show; + if (show_time) + PrepareTime(); + else + renderer.prepare_time(""); + renderer.redraw(); }); if(exit) break; @@ -299,11 +388,20 @@ RenderLoop(const string& font_path, if(osd && chrono::steady_clock::now() >= osd_stop) osd = false; + bool redraw_needed = false; + + if (show_time && chrono::steady_clock::now() >= show_time_next_update) { + show_time_next_update = chrono::steady_clock::now() + chrono::seconds(1); + PrepareTime(); + redraw_needed = true; + } + if(!osd && current_stop <= now) { if(have_next && subtitles[next_index].start <= now) { renderer.show_next(); + redraw_needed = false; // printf("show error: %i ms\n", now - subtitles[next_index].start); showing = true; current_stop = subtitles[next_index].stop; @@ -315,10 +413,14 @@ RenderLoop(const string& font_path, else if(showing) { renderer.hide(); + redraw_needed = false; // printf("hide error: %i ms\n", now - current_stop); showing = false; } } + + if(redraw_needed) + renderer.redraw(); } } @@ -408,6 +510,27 @@ void OMXPlayerSubtitles::SetVisible(bool visible) BOOST_NOEXCEPT } } +void OMXPlayerSubtitles::SetTitle(std::string line) BOOST_NOEXCEPT +{ + assert(m_open); + + SendToRenderer(Message::SetTitle{line}); +} + +void OMXPlayerSubtitles::SetTitleVisible(bool visible) BOOST_NOEXCEPT +{ + assert(m_open); + + SendToRenderer(Message::SetShowTitle{visible}); +} + +void OMXPlayerSubtitles::SetTimeVisible(bool visible) BOOST_NOEXCEPT +{ + assert(m_open); + + SendToRenderer(Message::SetShowTime{visible}); +} + void OMXPlayerSubtitles::SetActiveStream(size_t index) BOOST_NOEXCEPT { assert(m_open); @@ -500,4 +623,4 @@ void OMXPlayerSubtitles::DisplayText(const std::string& text, int duration) BOOS void OMXPlayerSubtitles::SetSubtitleRect(int x1, int y1, int x2, int y2) BOOST_NOEXCEPT { SendToRenderer(Message::SetRect{x1, y1, x2, y2}); -} \ No newline at end of file +} diff --git a/OMXPlayerSubtitles.h b/OMXPlayerSubtitles.h index 91c374c0..df7ca961 100644 --- a/OMXPlayerSubtitles.h +++ b/OMXPlayerSubtitles.h @@ -43,12 +43,17 @@ class OMXPlayerSubtitles : public OMXThread std::vector&& external_subtitles, const std::string& font_path, const std::string& italic_font_path, + const std::string& title_font_path, float font_size, + float title_font_size, bool centered, + bool title_centered, bool ghost_box, unsigned int lines, int display, int layer, - OMXClock* clock) BOOST_NOEXCEPT; + OMXClock* clock, + const string& title, + bool show_time) BOOST_NOEXCEPT; void Close() BOOST_NOEXCEPT; void Flush() BOOST_NOEXCEPT; void Resume() BOOST_NOEXCEPT; @@ -61,7 +66,31 @@ class OMXPlayerSubtitles : public OMXThread assert(m_open); return m_visible; } - + + void SetTitle(std::string line) BOOST_NOEXCEPT; + + std::string GetTitle() BOOST_NOEXCEPT + { + assert(m_open); + return m_title; + } + + void SetTitleVisible(bool visible) BOOST_NOEXCEPT; + + bool GetTitleVisible() BOOST_NOEXCEPT + { + assert(m_open); + return m_show_title; + } + + void SetTimeVisible(bool visible) BOOST_NOEXCEPT; + + bool GetTimeVisible() BOOST_NOEXCEPT + { + assert(m_open); + return m_show_time; + } + void SetActiveStream(size_t index) BOOST_NOEXCEPT; size_t GetActiveStream() BOOST_NOEXCEPT @@ -125,6 +154,18 @@ class OMXPlayerSubtitles : public OMXThread int x2; int y2; }; + struct SetTitle + { + std::string title; + }; + struct SetShowTitle + { + bool show; + }; + struct SetShowTime + { + bool show; + }; }; template @@ -141,8 +182,11 @@ class OMXPlayerSubtitles : public OMXThread void Process(); void RenderLoop(const std::string& font_path, const std::string& italic_font_path, + const std::string& title_font_path, float font_size, + float title_font_size, bool centered, + bool title_centered, bool ghost_box, unsigned int lines, OMXClock* clock); @@ -159,7 +203,10 @@ class OMXPlayerSubtitles : public OMXThread Message::SetPaused, Message::SetDelay, Message::DisplayText, - Message::SetRect> m_mailbox; + Message::SetRect, + Message::SetTitle, + Message::SetShowTitle, + Message::SetShowTime> m_mailbox; bool m_visible; bool m_use_external_subtitles; size_t m_active_index; @@ -167,13 +214,19 @@ class OMXPlayerSubtitles : public OMXThread std::atomic m_thread_stopped; std::string m_font_path; std::string m_italic_font_path; + std::string m_title_font_path; float m_font_size; + float m_title_font_size; bool m_centered; + bool m_title_centered; bool m_ghost_box; unsigned int m_lines; OMXClock* m_av_clock; int m_display; int m_layer; + std::string m_title; + bool m_show_title; + bool m_show_time; #ifndef NDEBUG bool m_open; diff --git a/README.md b/README.md index c0f6d5d9..3b53ad80 100644 --- a/README.md +++ b/README.md @@ -51,67 +51,72 @@ Copy over `omxplayer-dist/*` to the Pi `/`. Usage: omxplayer [OPTIONS] [FILE] - -h --help Print this help - -v --version Print version info - -k --keys Print key bindings - -n --aidx index Audio stream index : e.g. 1 - -o --adev device Audio out device : e.g. hdmi/local/both/alsa[:device] - -i --info Dump stream format and exit - -I --with-info dump stream format before playback - -s --stats Pts and buffer stats - -p --passthrough Audio passthrough - -d --deinterlace Force deinterlacing - --nodeinterlace Force no deinterlacing - --nativedeinterlace let display handle interlace - --anaglyph type convert 3d to anaglyph - --advanced[=0] Enable/disable advanced deinterlace for HD videos (default enabled) - -w --hw Hw audio decoding - -3 --3d mode Switch tv into 3d mode (e.g. SBS/TB) - -M --allow-mvc Allow decoding of both views of MVC stereo stream - -y --hdmiclocksync Display refresh rate to match video (default) - -z --nohdmiclocksync Do not adjust display refresh rate to match video - -t --sid index Show subtitle with index - -r --refresh Adjust framerate/resolution to video - -g --genlog Generate log file - -l --pos n Start position (hh:mm:ss) - -b --blank[=0xAARRGGBB] Set the video background color to black (or optional ARGB value) - --loop Loop file. Ignored if file not seekable - --no-boost-on-downmix Don't boost volume when downmixing - --vol n set initial volume in millibels (default 0) - --amp n set initial amplification in millibels (default 0) - --no-osd Do not display status information on screen - --no-keys Disable keyboard input (prevents hangs for certain TTYs) - --subtitles path External subtitles in UTF-8 srt format - --font path Default: /usr/share/fonts/truetype/freefont/FreeSans.ttf - --italic-font path Default: /usr/share/fonts/truetype/freefont/FreeSansOblique.ttf - --font-size size Font size in 1/1000 screen height (default: 55) - --align left/center Subtitle alignment (default: left) - --no-ghost-box No semitransparent boxes behind subtitles - --lines n Number of lines in the subtitle buffer (default: 3) - --win 'x1 y1 x2 y2' Set position of video window - --win x1,y1,x2,y2 Set position of video window - --crop 'x1 y1 x2 y2' Set crop area for input video - --crop x1,y1,x2,y2 Set crop area for input video - --aspect-mode type Letterbox, fill, stretch. Default: stretch if win is specified, letterbox otherwise - --audio_fifo n Size of audio output fifo in seconds - --video_fifo n Size of video output fifo in MB - --audio_queue n Size of audio input queue in MB - --video_queue n Size of video input queue in MB - --threshold n Amount of buffered data required to finish buffering [s] - --timeout n Timeout for stalled file/network operations (default 10s) - --orientation n Set orientation of video (0, 90, 180 or 270) - --fps n Set fps of video where timestamps are not present - --live Set for live tv or vod type stream - --layout Set output speaker layout (e.g. 5.1) - --dbus_name name default: org.mpris.MediaPlayer2.omxplayer - --key-config Uses key bindings in instead of the default - --alpha Set video transparency (0..255) - --layer n Set video render layer number (higher numbers are on top) - --display n Set display to output to - --cookie 'cookie' Send specified cookie as part of HTTP requests - --user-agent 'ua' Send specified User-Agent as part of HTTP requests - --lavfdopts 'opts' Options passed to libavformat, e.g. 'probesize:250000,...' - --avdict 'opts' Options passed to demuxer, e.g., 'rtsp_transport:tcp,...' + -h --help Print this help + -v --version Print version info + -k --keys Print key bindings + -n --aidx index Audio stream index : e.g. 1 + -o --adev device Audio out device : e.g. hdmi/local/both/alsa[:device] + -i --info Dump stream format and exit + -I --with-info dump stream format before playback + -s --stats Pts and buffer stats + -p --passthrough Audio passthrough + -d --deinterlace Force deinterlacing + --nodeinterlace Force no deinterlacing + --nativedeinterlace let display handle interlace + --anaglyph type convert 3d to anaglyph + --advanced[=0] Enable/disable advanced deinterlace for HD videos (default enabled) + -w --hw Hw audio decoding + -3 --3d mode Switch tv into 3d mode (e.g. SBS/TB) + -M --allow-mvc Allow decoding of both views of MVC stereo stream + -y --hdmiclocksync Display refresh rate to match video (default) + -z --nohdmiclocksync Do not adjust display refresh rate to match video + -t --sid index Show subtitle with index + -r --refresh Adjust framerate/resolution to video + -g --genlog Generate log file + -l --pos n Start position (hh:mm:ss) + -b --blank[=0xAARRGGBB] Set the video background color to black (or optional ARGB value) + --loop Loop file. Ignored if file not seekable + --no-boost-on-downmix Don't boost volume when downmixing + --vol n set initial volume in millibels (default 0) + --amp n set initial amplification in millibels (default 0) + --no-osd Do not display status information on screen + --no-keys Disable keyboard input (prevents hangs for certain TTYs) + --subtitles path External subtitles in UTF-8 srt format + --font path Default: /usr/share/fonts/truetype/freefont/FreeSans.ttf + --italic-font path Default: /usr/share/fonts/truetype/freefont/FreeSansOblique.ttf + --title-font path Default: /usr/share/fonts/truetype/freefont/FreeSans.ttf + --font-size size Font size in 1/1000 screen height (default: 55) + --title-font-size size Title font size in 1/1000 screen height (default: 25) + --align left/center Subtitle alignment (default: left) + --title-align left/center Title alignment (default: left) + --title string Display static title string + -T --show-time Show elapsed and total time + --no-ghost-box No semitransparent boxes behind subtitles + --lines n Number of lines in the subtitle buffer (default: 3) + --win 'x1 y1 x2 y2' Set position of video window + --win x1,y1,x2,y2 Set position of video window + --crop 'x1 y1 x2 y2' Set crop area for input video + --crop x1,y1,x2,y2 Set crop area for input video + --aspect-mode type Letterbox, fill, stretch. Default: stretch if win is specified, letterbox otherwise + --audio_fifo n Size of audio output fifo in seconds + --video_fifo n Size of video output fifo in MB + --audio_queue n Size of audio input queue in MB + --video_queue n Size of video input queue in MB + --threshold n Amount of buffered data required to finish buffering [s] + --timeout n Timeout for stalled file/network operations (default 10s) + --orientation n Set orientation of video (0, 90, 180 or 270) + --fps n Set fps of video where timestamps are not present + --live Set for live tv or vod type stream + --layout Set output speaker layout (e.g. 5.1) + --dbus_name name default: org.mpris.MediaPlayer2.omxplayer + --key-config Uses key bindings in instead of the default + --alpha Set video transparency (0..255) + --layer n Set video render layer number (higher numbers are on top) + --display n Set display to output to + --cookie 'cookie' Send specified cookie as part of HTTP requests + --user-agent 'ua' Send specified User-Agent as part of HTTP requests + --lavfdopts 'opts' Options passed to libavformat, e.g. 'probesize:250000,...' + --avdict 'opts' Options passed to demuxer, e.g., 'rtsp_transport:tcp,...' For example: @@ -137,6 +142,12 @@ Key bindings to control omxplayer while playing: x hide subtitles d decrease subtitle delay (- 250 ms) f increase subtitle delay (+ 250 ms) + t toggle static title display + e show static title + r hide static title + y toggle time display + g show time + h hide time q exit omxplayer p / space pause/resume - decrease volume @@ -164,6 +175,8 @@ The list of valid [action]s roughly corresponds to the list of default key bindi PREVIOUS_SUBTITLE NEXT_SUBTITLE TOGGLE_SUBTITLE + SHOW_SUBTITLES + HIDE_SUBTITLES DECREASE_SUBTITLE_DELAY INCREASE_SUBTITLE_DELAY EXIT @@ -175,6 +188,12 @@ The list of valid [action]s roughly corresponds to the list of default key bindi SEEK_BACK_LARGE SEEK_FORWARD_LARGE STEP + TOGGLE_TITLE + SHOW_TITLE + HIDE_TITLE + TOGGLE_TIME + SHOW_TIME + HIDE_TIME Valid [key]s include all alpha-numeric characters and most symbols, as well as: @@ -496,6 +515,54 @@ Turns off subtitles. :-------------: | ------- Return | `null` +##### SetTitle (w) + +Sets the current static title. + + Params | Type | Description +:-------------: | --------- | -------------------------------- +1 | `string` | Static title to display. + +##### GetTitle (ro) + +The current static title. + + Params | Type +:-------------: | --------- + Return | `string` + +##### ShowTitle + +Turns on title display. + + Params | Type +:-------------: | ------- + Return | `null` + +##### HideTitle + +Turns off title display. + + Params | Type +:-------------: | ------- + Return | `null` + +##### ShowTime + +Turns on time display. + + Params | Type +:-------------: | ------- + Return | `null` + +##### HideTime + +Turns off time display. + + Params | Type +:-------------: | ------- + Return | `null` + ##### GetSource The current file or stream that is being played. diff --git a/SubtitleRenderer.cpp b/SubtitleRenderer.cpp index 540162ff..6acf04c2 100644 --- a/SubtitleRenderer.cpp +++ b/SubtitleRenderer.cpp @@ -88,7 +88,7 @@ class BoxRenderer { } }; -void SubtitleRenderer::load_glyph(InternalChar ch) { +void SubtitleRenderer::load_glyph(InternalChar ch, bool title) { VGfloat escapement[2]{}; auto load_glyph_internal = @@ -180,6 +180,13 @@ void SubtitleRenderer::load_glyph(InternalChar ch) { } }; + if (title) { + load_glyph_internal(ft_face_title_, vg_font_title_, false); + glyphs_title_[ch].advance = escapement[0]; + load_glyph_internal(ft_face_title_, vg_font_title_border_, true); + return; + } + if (!ch.italic()) { load_glyph_internal(ft_face_, vg_font_, false); glyphs_[ch].advance = escapement[0]; @@ -191,10 +198,13 @@ void SubtitleRenderer::load_glyph(InternalChar ch) { } } -int SubtitleRenderer::get_text_width(const std::vector& text) { +int SubtitleRenderer::get_text_width(const std::vector& text, bool title) { int width = 0; for (auto c = text.begin(); c != text.end(); ++c) { - width += glyphs_.at(*c).advance; + if (title) + width += glyphs_title_.at(*c).advance; + else + width += glyphs_.at(*c).advance; } return width; } @@ -217,10 +227,15 @@ get_internal_chars(const std::string& str, TagTracker& tag_tracker) { } void SubtitleRenderer:: -prepare_glyphs(const std::vector& text) { +prepare_glyphs(const std::vector& text, bool title) { for (auto c = text.begin(); c != text.end(); ++c) { - if (glyphs_.find(*c) == glyphs_.end()) - load_glyph(*c); + if (title) { + if (glyphs_title_.find(*c) == glyphs_title_.end()) + load_glyph(*c, title); + } else { + if (glyphs_.find(*c) == glyphs_.end()) + load_glyph(*c, title); + } } } @@ -264,14 +279,19 @@ SubtitleRenderer:: SubtitleRenderer(int display, int layer, const std::string& font_path, const std::string& italic_font_path, + const std::string& title_font_path, float font_size, + float title_font_size, float margin_left, float margin_bottom, bool centered, + bool title_centered, unsigned int white_level, unsigned int box_opacity, unsigned int lines) -: prepared_(), +: show_subtitle_(), + title_prepared_(), + time_prepared_(), dispman_element_(), dispman_display_(), display_(), @@ -279,19 +299,26 @@ SubtitleRenderer(int display, int layer, surface_(), vg_font_(), vg_font_border_(), + vg_font_title_(), + vg_font_title_border_(), ft_library_(), ft_face_(), ft_face_italic_(), + ft_face_title_(), ft_stroker_(), + prepared_lines_(), + prepared_lines_active_(), centered_(centered), + title_centered_(title_centered), white_level_(white_level), box_opacity_(box_opacity), - font_size_(font_size) + font_size_(font_size), + title_font_size_(title_font_size) { try { ENFORCE(graphics_get_display_size(display, &screen_width_, &screen_height_) >= 0); - initialize_fonts(font_path, italic_font_path); + initialize_fonts(font_path, italic_font_path, title_font_path); int abs_margin_bottom = static_cast(margin_bottom * screen_height_ + 0.5f) - config_.box_offset; @@ -299,15 +326,16 @@ SubtitleRenderer(int display, int layer, int buffer_padding = (config_.line_height+2)/4; int buffer_bottom = clamp(abs_margin_bottom + config_.box_offset - buffer_padding, 0, (int) screen_height_-1); - int buffer_top = clamp(buffer_bottom + config_.line_height * (int) lines + buffer_padding*2, + int buffer_top = clamp(buffer_bottom + config_.title_line_height + config_.title_line_padding + + config_.line_height * (int) lines + buffer_padding*2, 0, (int) screen_height_-1); config_.buffer_x = 0; config_.buffer_y = screen_height_ - buffer_top - 1; config_.buffer_width = screen_width_; config_.buffer_height = buffer_top - buffer_bottom + 1; - config_.margin_left = (screen_width_ - screen_height_) / 2 + - static_cast(margin_left * screen_height_ + 0.5f); + config_.margin_left = static_cast(margin_left * screen_width_ + 0.5f); + config_.margin_bottom = abs_margin_bottom - buffer_bottom; config_fullscreen_ = config_; // save full-screen config for scaling reference. @@ -328,21 +356,27 @@ void SubtitleRenderer::destroy() { void SubtitleRenderer:: initialize_fonts(const std::string& font_path, - const std::string& italic_font_path) { + const std::string& italic_font_path, + const std::string& title_font_path) { ENFORCE(!FT_Init_FreeType(&ft_library_)); ENFORCE2(!FT_New_Face(ft_library_, font_path.c_str(), 0, &ft_face_), "Unable to open font"); ENFORCE2(!FT_New_Face(ft_library_, italic_font_path.c_str(), 0, &ft_face_italic_), "Unable to open italic font"); + ENFORCE2(!FT_New_Face(ft_library_, title_font_path.c_str(), 0, &ft_face_title_), + "Unable to open title font"); + uint32_t font_size = font_size_*screen_height_; + uint32_t title_font_size = title_font_size_*screen_height_; ENFORCE(!FT_Set_Pixel_Sizes(ft_face_, 0, font_size)); ENFORCE(!FT_Set_Pixel_Sizes(ft_face_italic_, 0, font_size)); + ENFORCE(!FT_Set_Pixel_Sizes(ft_face_title_, 0, title_font_size)); - auto get_bbox = [this](char32_t cp) { - auto glyph_index = FT_Get_Char_Index(ft_face_, cp); - ENFORCE(!FT_Load_Glyph(ft_face_, glyph_index, FT_LOAD_NO_HINTING)); + auto get_bbox = [this](char32_t cp, FT_Face& font) { + auto glyph_index = FT_Get_Char_Index(font, cp); + ENFORCE(!FT_Load_Glyph(font, glyph_index, FT_LOAD_NO_HINTING)); FT_Glyph glyph; - ENFORCE(!FT_Get_Glyph(ft_face_->glyph, &glyph)); + ENFORCE(!FT_Get_Glyph(font->glyph, &glyph)); SCOPE_EXIT {FT_Done_Glyph(glyph);}; FT_BBox bbox; FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_PIXELS, &bbox); @@ -350,15 +384,24 @@ initialize_fonts(const std::string& font_path, }; constexpr float padding_factor = 0.05f; - int y_min = get_bbox('g').yMin; - int y_max = get_bbox('M').yMax; + int y_min = get_bbox('g', ft_face_).yMin; + int y_max = get_bbox('M', ft_face_).yMax; y_max += -y_min*0.7f; config_.line_height = y_max - y_min; - const int v_padding = config_.line_height*padding_factor + 0.5f; + int v_padding = config_.line_height*padding_factor + 0.5f; config_.line_height += v_padding*2; config_.box_offset = y_min-v_padding; config_.box_h_padding = config_.line_height/5.0f + 0.5f; + y_min = get_bbox('g', ft_face_title_).yMin; + y_max = get_bbox('M', ft_face_title_).yMax; + y_max += -y_min*0.7f; + config_.title_line_height = y_max - y_min; + v_padding = config_.title_line_height*padding_factor + 0.5f; + config_.title_line_height += v_padding*2; + config_.title_line_padding = config_.line_height * 0.5f + 0.5f; + config_.title_box_offset = y_min-v_padding; + config_.title_box_h_padding = config_.title_line_height/5.0f + 0.5f; constexpr float border_thickness = 0.044f; ENFORCE(!FT_Stroker_New(ft_library_, &ft_stroker_)); @@ -376,6 +419,7 @@ void SubtitleRenderer::destroy_fonts() { ft_library_ = {}; ft_face_ = {}; ft_face_italic_ = {}; + ft_face_title_ = {}; ft_stroker_ = {}; } } @@ -496,6 +540,9 @@ void SubtitleRenderer::initialize_vg() { create_vg_font(vg_font_); create_vg_font(vg_font_border_); + create_vg_font(vg_font_title_); + create_vg_font(vg_font_title_border_); + // VGfloat color[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; // vgSetfv(VG_CLEAR_COLOR, 4, color); } @@ -519,22 +566,64 @@ void SubtitleRenderer:: prepare(const std::vector& text_lines) BOOST_NOEXCEPT { const int n_lines = text_lines.size(); TagTracker tag_tracker; + PreparedSubtitleLines& lines = prepared_lines_[!prepared_lines_active_]; + + lines.internal_lines_.resize(n_lines); + lines.line_widths_.resize(n_lines); + lines.line_positions_.resize(n_lines); - internal_lines_.resize(n_lines); - line_widths_.resize(n_lines); - line_positions_.resize(n_lines); for (int i = 0; i < n_lines; ++i) { - internal_lines_[i] = get_internal_chars(text_lines[i], tag_tracker); - prepare_glyphs(internal_lines_[i]); - line_widths_[i] = get_text_width(internal_lines_[i]); - line_positions_[i].second = config_.margin_bottom + (n_lines-i-1)*config_.line_height; + lines.internal_lines_[i] = get_internal_chars(text_lines[i], tag_tracker); + prepare_glyphs(lines.internal_lines_[i], false); + lines.line_widths_[i] = get_text_width(lines.internal_lines_[i], false); + lines.line_positions_[i].second = config_.margin_bottom + + (n_lines-i-1)*config_.line_height; if (centered_) - line_positions_[i].first = config_.buffer_width/2 - line_widths_[i]/2; + lines.line_positions_[i].first = config_.buffer_width/2 - lines.line_widths_[i]/2; else - line_positions_[i].first = config_.margin_left; + lines.line_positions_[i].first = config_.margin_left; + } + + lines.prepared_ = true; +} + +void SubtitleRenderer:: +prepare_time(const std::string& line) BOOST_NOEXCEPT { + TagTracker tag_tracker; + + if (line == "") { + time_prepared_ = false; + return; + } + + internal_time_ = get_internal_chars(line, tag_tracker); + prepare_glyphs(internal_time_, true); + time_width_ = get_text_width(internal_time_, true); + time_position_.second = config_.margin_bottom; + time_position_.first = config_.buffer_width - time_width_ - config_.margin_left; + + time_prepared_ = true; +} + +void SubtitleRenderer:: +prepare_title(const std::string& line) BOOST_NOEXCEPT { + TagTracker tag_tracker; + + if (line == "") { + title_prepared_ = false; + return; } - prepared_ = true; + internal_title_line_ = get_internal_chars(line, tag_tracker); + prepare_glyphs(internal_title_line_, true); + title_line_width_ = get_text_width(internal_title_line_, true); + title_line_position_.second = config_.margin_bottom; + if (title_centered_) + title_line_position_.first = config_.buffer_width/2 - title_line_width_/2; + else + title_line_position_.first = config_.margin_left; + + title_prepared_ = true; } void SubtitleRenderer::clear() BOOST_NOEXCEPT { @@ -542,18 +631,84 @@ void SubtitleRenderer::clear() BOOST_NOEXCEPT { assert(!vgGetError()); } -void SubtitleRenderer::draw() BOOST_NOEXCEPT { - clear(); +void SubtitleRenderer::draw_time(bool clear_needed) BOOST_NOEXCEPT { + if (clear_needed) + clear(); + + // time graybox + { + BoxRenderer box_renderer(box_opacity_); + box_renderer.push(time_position_.first - config_.box_h_padding, + time_position_.second + config_.title_box_offset, + time_width_ + config_.title_box_h_padding*2, + config_.title_line_height); + box_renderer.render(); + } + + // time background + draw_text(vg_font_title_border_, + internal_time_, + time_position_.first, time_position_.second, + 0); + + // time foreground + draw_text(vg_font_title_, + internal_time_, + time_position_.first, time_position_.second, + white_level_); +} + +void SubtitleRenderer::draw_title(bool clear_needed) BOOST_NOEXCEPT { + if (clear_needed) + clear(); + + // title line graybox + { + BoxRenderer box_renderer(box_opacity_); + box_renderer.push(title_line_position_.first - config_.box_h_padding, + title_line_position_.second + config_.title_box_offset, + title_line_width_ + config_.title_box_h_padding*2, + config_.title_line_height); + box_renderer.render(); + } + + // title line background + draw_text(vg_font_title_border_, + internal_title_line_, + title_line_position_.first, title_line_position_.second, + 0); + + // title line foreground + draw_text(vg_font_title_, + internal_title_line_, + title_line_position_.first, title_line_position_.second, + white_level_); +} + +void SubtitleRenderer::draw(bool clear_needed) BOOST_NOEXCEPT { + PreparedSubtitleLines& lines = prepared_lines_[prepared_lines_active_]; - const auto n_lines = internal_lines_.size(); + if (clear_needed) + clear(); + + const auto n_lines = lines.internal_lines_.size(); + + int title_line_height = config_.title_line_height; + int title_line_padding = config_.title_line_padding; + + if (!title_prepared_) { + title_line_height = 0; + title_line_padding = 0; + } // font graybox { BoxRenderer box_renderer(box_opacity_); for (size_t i = 0; i < n_lines; ++i) { - box_renderer.push(line_positions_[i].first - config_.box_h_padding, - line_positions_[i].second + config_.box_offset, - line_widths_[i] + config_.box_h_padding*2, + box_renderer.push(lines.line_positions_[i].first - config_.box_h_padding, + lines.line_positions_[i].second + config_.box_offset + + title_line_height + title_line_padding, + lines.line_widths_[i] + config_.box_h_padding*2, config_.line_height); } box_renderer.render(); @@ -562,20 +717,20 @@ void SubtitleRenderer::draw() BOOST_NOEXCEPT { //font background for (size_t i = 0; i < n_lines; ++i) { draw_text(vg_font_border_, - internal_lines_[i], - line_positions_[i].first, line_positions_[i].second, + lines.internal_lines_[i], + lines.line_positions_[i].first, lines.line_positions_[i].second + + title_line_height + title_line_padding, 0); } //font foreground for (size_t i = 0; i < n_lines; ++i) { draw_text(vg_font_, - internal_lines_[i], - line_positions_[i].first, line_positions_[i].second, + lines.internal_lines_[i], + lines.line_positions_[i].first, lines.line_positions_[i].second + + title_line_height + title_line_padding, white_level_); } - - prepared_ = false; } void SubtitleRenderer::swap_buffers() BOOST_NOEXCEPT { @@ -598,6 +753,10 @@ void SubtitleRenderer::set_rect(int x1, int y1, int x2, int y2) BOOST_NOEXCEPT config_.box_h_padding = config_fullscreen_.box_h_padding * height_mod + 0.5f; config_.margin_left = config_fullscreen_.margin_left * width_mod + 0.5f; config_.margin_bottom = config_fullscreen_.margin_bottom * height_mod + 0.5f; + config_.title_line_height = config_fullscreen_.title_line_height * height_mod + 0.5f; + config_.title_line_padding = config_fullscreen_.title_line_padding * height_mod + 0.5f; + config_.title_box_offset = config_fullscreen_.title_box_offset * height_mod + 0.5f; + config_.title_box_h_padding = config_fullscreen_.title_box_h_padding * height_mod + 0.5f; // resize dispmanx element ENFORCE(dispman_element_); @@ -615,7 +774,10 @@ void SubtitleRenderer::set_rect(int x1, int y1, int x2, int y2) BOOST_NOEXCEPT // resize font glyphs_.clear(); // clear cached glyphs + glyphs_title_.clear(); float font_size = height*font_size_; + float title_font_size = height*title_font_size_; ENFORCE(!FT_Set_Pixel_Sizes(ft_face_, 0, font_size)); ENFORCE(!FT_Set_Pixel_Sizes(ft_face_italic_, 0, font_size)); -} \ No newline at end of file + ENFORCE(!FT_Set_Pixel_Sizes(ft_face_title_, 0, title_font_size)); +} diff --git a/SubtitleRenderer.h b/SubtitleRenderer.h index 3f60798f..a60ae2f0 100644 --- a/SubtitleRenderer.h +++ b/SubtitleRenderer.h @@ -91,6 +91,10 @@ typedef struct { int box_h_padding; int margin_left; int margin_bottom; + int title_line_height; + int title_line_padding; + int title_box_offset; + int title_box_h_padding; } SubtitleConfig; class SubtitleRenderer { @@ -100,34 +104,58 @@ class SubtitleRenderer { SubtitleRenderer(int display, int layer, const std::string& font_path, const std::string& italic_font_path, + const std::string& title_font_path, float font_size, + float title_font_size, float margin_left, float margin_bottom, bool centered, + bool title_centered, unsigned int white_level, unsigned int box_opacity, unsigned int lines); ~SubtitleRenderer() BOOST_NOEXCEPT; void prepare(const std::vector& text_lines) BOOST_NOEXCEPT; + void prepare_time(const std::string& line) BOOST_NOEXCEPT; + void prepare_title(const std::string& line) BOOST_NOEXCEPT; void unprepare() BOOST_NOEXCEPT { - prepared_ = false; + prepared_lines_[prepared_lines_active_].prepared_ = false; } - void show_next() BOOST_NOEXCEPT { - if (prepared_) { - // puts("Expensive show_next!"); - draw(); + void redraw() BOOST_NOEXCEPT { + bool clear_needed = true; + + if (title_prepared_) { + draw_title(clear_needed); + clear_needed = false; + } + + if (time_prepared_) { + draw_time(clear_needed); + clear_needed = false; } + + if (show_subtitle_) + draw(clear_needed); + else if (clear_needed) + clear(); + swap_buffers(); } + void show_next() BOOST_NOEXCEPT { + prepared_lines_active_ = !prepared_lines_active_; + if (prepared_lines_[prepared_lines_active_].prepared_) + show_subtitle_ = true; + + redraw(); + } + void hide() BOOST_NOEXCEPT { - clear(); - swap_buffers(); - if (prepared_) - draw(); + show_subtitle_ = false; + redraw(); } void set_rect(int width, int height, int x, int y) BOOST_NOEXCEPT; @@ -138,7 +166,7 @@ class SubtitleRenderer { InternalChar(char32_t codepoint, bool italic) { val = codepoint | (static_cast(italic) << 31); } - + bool operator ==(const InternalChar& other) const { return val == other.val; } @@ -159,6 +187,13 @@ class SubtitleRenderer { int advance; }; + struct PreparedSubtitleLines { + std::vector> internal_lines_; + std::vector> line_positions_; + std::vector line_widths_; + bool prepared_; + }; + static void draw_text(VGFont font, const std::vector& text, int x, int y, @@ -166,22 +201,27 @@ class SubtitleRenderer { void destroy(); void initialize_fonts(const std::string& font_name, - const std::string& italic_font_path); + const std::string& italic_font_path, + const std::string& title_font_path); void destroy_fonts(); void initialize_vg(); void destroy_vg(); void initialize_window(int display, int layer); void destroy_window(); void clear() BOOST_NOEXCEPT; - void draw() BOOST_NOEXCEPT; + void draw_time(bool clear_needed) BOOST_NOEXCEPT; + void draw_title(bool clear_needed) BOOST_NOEXCEPT; + void draw(bool clear_needed) BOOST_NOEXCEPT; void swap_buffers() BOOST_NOEXCEPT; - void prepare_glyphs(const std::vector& text); - void load_glyph(InternalChar ch); - int get_text_width(const std::vector& text); + void prepare_glyphs(const std::vector& text, bool title); + void load_glyph(InternalChar ch, bool title); + int get_text_width(const std::vector& text, bool title); std::vector get_internal_chars(const std::string& str, TagTracker& tag_tracker); - bool prepared_; + bool show_subtitle_; + bool title_prepared_; + bool time_prepared_; DISPMANX_ELEMENT_HANDLE_T dispman_element_; DISPMANX_DISPLAY_HANDLE_T dispman_display_; EGLDisplay display_; @@ -189,20 +229,31 @@ class SubtitleRenderer { EGLSurface surface_; VGFont vg_font_; VGFont vg_font_border_; + VGFont vg_font_title_; + VGFont vg_font_title_border_; FT_Library ft_library_; FT_Face ft_face_; FT_Face ft_face_italic_; + FT_Face ft_face_title_; FT_Stroker ft_stroker_; + PreparedSubtitleLines prepared_lines_[2]; + int prepared_lines_active_; std::unordered_map glyphs_; - std::vector> internal_lines_; - std::vector> line_positions_; - std::vector line_widths_; + std::unordered_map glyphs_title_; + std::vector internal_title_line_; + std::pair title_line_position_; + int title_line_width_; + std::vector internal_time_; + std::pair time_position_; + int time_width_; bool centered_; + bool title_centered_; unsigned int white_level_; unsigned int box_opacity_; uint32_t screen_width_; uint32_t screen_height_; float font_size_; + float title_font_size_; SubtitleConfig config_fullscreen_; SubtitleConfig config_; }; diff --git a/dbuscontrol.sh b/dbuscontrol.sh index f88a5a8b..a0337617 100755 --- a/dbuscontrol.sh +++ b/dbuscontrol.sh @@ -35,7 +35,7 @@ openuri) ;; volume) - volume=`dbus-send --print-reply=double --session --reply-timeout=500 --dest=org.mpris.MediaPlayer2.omxplayer /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Set string:"org.mpris.MediaPlayer2.Player" string:"Volume" ${2:+double:}$2` + volume=`dbus-send --print-reply=literal --session --reply-timeout=500 --dest=org.mpris.MediaPlayer2.omxplayer /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Set string:"org.mpris.MediaPlayer2.Player" string:"Volume" ${2:+double:}$2` [ $? -ne 0 ] && exit 1 volume="$(awk '{print $2}' <<< "$volume")" echo "Volume: $volume" @@ -104,6 +104,41 @@ hidesubtitles) showsubtitles) dbus-send --print-reply=literal --session --dest=org.mpris.MediaPlayer2.omxplayer /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Action int32:31 >/dev/null ;; + +settitle) + dbus-send --print-reply=literal --session --dest=org.mpris.MediaPlayer2.omxplayer /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.SetTitle string:"$2" >/dev/null + ;; + +gettitle) + title=$(dbus-send --print-reply=literal --session --reply-timeout=500 --dest=org.mpris.MediaPlayer2.omxplayer /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.GetTitle) + [ $? -ne 0 ] && exit 1 + echo "$title" | sed 's/^ *//' + ;; + +toggletitle) + dbus-send --print-reply=literal --session --dest=org.mpris.MediaPlayer2.omxplayer /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Action int32:39 >/dev/null + ;; + +hidetitle) + dbus-send --print-reply=literal --session --dest=org.mpris.MediaPlayer2.omxplayer /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Action int32:40 >/dev/null + ;; + +showtitle) + dbus-send --print-reply=literal --session --dest=org.mpris.MediaPlayer2.omxplayer /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Action int32:41 >/dev/null + ;; + +toggletime) + dbus-send --print-reply=literal --session --dest=org.mpris.MediaPlayer2.omxplayer /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Action int32:42 >/dev/null + ;; + +hidetime) + dbus-send --print-reply=literal --session --dest=org.mpris.MediaPlayer2.omxplayer /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Action int32:43 >/dev/null + ;; + +showtime) + dbus-send --print-reply=literal --session --dest=org.mpris.MediaPlayer2.omxplayer /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Action int32:44 >/dev/null + ;; + getsource) source=$(dbus-send --print-reply=literal --session --reply-timeout=500 --dest=org.mpris.MediaPlayer2.omxplayer /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.GetSource) [ $? -ne 0 ] && exit 1 diff --git a/omxplayer.cpp b/omxplayer.cpp index 0d4ab5e5..8f8e9ff3 100644 --- a/omxplayer.cpp +++ b/omxplayer.cpp @@ -85,11 +85,15 @@ std::string m_external_subtitles_path; bool m_has_external_subtitles = false; std::string m_font_path = "/usr/share/fonts/truetype/freefont/FreeSans.ttf"; std::string m_italic_font_path = "/usr/share/fonts/truetype/freefont/FreeSansOblique.ttf"; +std::string m_title_font_path = "/usr/share/fonts/truetype/freefont/FreeSans.ttf"; std::string m_dbus_name = "org.mpris.MediaPlayer2.omxplayer"; bool m_asked_for_font = false; bool m_asked_for_italic_font = false; float m_font_size = 0.055f; +bool m_asked_for_title_font = false; +float m_title_font_size = 0.025f; bool m_centered = false; +bool m_title_centered = false; bool m_ghost_box = true; unsigned int m_subtitle_lines = 3; bool m_Pause = false; @@ -114,6 +118,9 @@ bool m_has_audio = false; bool m_has_subtitle = false; bool m_gen_log = false; bool m_loop = false; +std::string m_title; +bool m_show_title = false; +bool m_show_time = false; enum{ERROR=-1,SUCCESS,ONEBYTE}; @@ -532,6 +539,10 @@ int main(int argc, char *argv[]) const int font_opt = 0x100; const int italic_font_opt = 0x201; const int font_size_opt = 0x101; + const int title_opt = 0x214; + const int title_font_opt = 0x215; + const int title_font_size_opt = 0x216; + const int title_align_opt = 0x217; const int align_opt = 0x102; const int no_ghost_box_opt = 0x203; const int subtitles_opt = 0x103; @@ -600,6 +611,11 @@ int main(int argc, char *argv[]) { "font", required_argument, NULL, font_opt }, { "italic-font", required_argument, NULL, italic_font_opt }, { "font-size", required_argument, NULL, font_size_opt }, + { "title", required_argument, NULL, title_opt }, + { "title-font", required_argument, NULL, title_font_opt }, + { "title-font-size", required_argument, NULL, title_font_size_opt }, + { "title-align", required_argument, NULL, title_align_opt }, + { "show-time", no_argument, NULL, 'T' }, { "align", required_argument, NULL, align_opt }, { "no-ghost-box", no_argument, NULL, no_ghost_box_opt }, { "subtitles", required_argument, NULL, subtitles_opt }, @@ -646,7 +662,7 @@ int main(int argc, char *argv[]) //Build default keymap just in case the --key-config option isn't used map keymap = KeyConfig::buildDefaultKeymap(); - while ((c = getopt_long(argc, argv, "wiIhvkn:l:o:cslb::pd3:Myzt:rg", longopts, NULL)) != -1) + while ((c = getopt_long(argc, argv, "wiIhvkn:l:o:cslb::pd3:Myzt:rgT", longopts, NULL)) != -1) { switch (c) { @@ -759,6 +775,9 @@ int main(int argc, char *argv[]) m_loop_from = m_incr; } break; + case 'T': + m_show_time = true; + break; case no_osd_opt: m_osd = false; break; @@ -780,6 +799,20 @@ int main(int argc, char *argv[]) m_font_size = thousands*0.001f; } break; + case title_font_opt: + m_title_font_path = optarg; + m_asked_for_title_font = true; + break; + case title_font_size_opt: + { + const int thousands = atoi(optarg); + if (thousands > 0) + m_title_font_size = thousands*0.001f; + } + break; + case title_align_opt: + m_title_centered = !strcmp(optarg, "center"); + break; case align_opt: m_centered = !strcmp(optarg, "center"); break; @@ -813,6 +846,9 @@ int main(int argc, char *argv[]) m_config_video.aspectMode = 0; } break; + case title_opt: + m_title = optarg; + break; case vol_opt: m_Volume = atoi(optarg); break; @@ -960,6 +996,12 @@ int main(int argc, char *argv[]) return EXIT_FAILURE; } + if(m_asked_for_title_font && !Exists(m_title_font_path)) + { + PrintFileNotFound(m_title_font_path); + return EXIT_FAILURE; + } + if(m_has_external_subtitles && !Exists(m_external_subtitles_path)) { PrintFileNotFound(m_external_subtitles_path); @@ -1115,12 +1157,17 @@ int main(int argc, char *argv[]) std::move(external_subtitles), m_font_path, m_italic_font_path, + m_title_font_path, m_font_size, + m_title_font_size, m_centered, + m_title_centered, m_ghost_box, m_subtitle_lines, m_config_video.display, m_config_video.layer + 1, - m_av_clock)) + m_av_clock, + m_title, + m_show_time)) goto do_exit; if(m_config_video.dst_rect.x2 > 0 && m_config_video.dst_rect.y2 > 0) m_player_subtitles.SetSubtitleRect(m_config_video.dst_rect.x1, m_config_video.dst_rect.y1, m_config_video.dst_rect.x2, m_config_video.dst_rect.y2); @@ -1535,6 +1582,40 @@ int main(int argc, char *argv[]) m_Volume / 100.0f)); printf("Current Volume: %.2fdB\n", m_Volume / 100.0f); break; + case KeyConfig::ACTION_SET_TITLE: + m_title = result.getWinArg(); + m_player_subtitles.SetTitle(m_title); + break; + case KeyConfig::ACTION_TOGGLE_TITLE: + if(m_title != "") + { + m_player_subtitles.SetTitleVisible(!m_player_subtitles.GetTitleVisible()); + } + break; + case KeyConfig::ACTION_HIDE_TITLE: + if(m_title != "") + { + m_player_subtitles.SetTitleVisible(false); + } + break; + case KeyConfig::ACTION_SHOW_TITLE: + if(m_title != "") + { + m_player_subtitles.SetTitleVisible(true); + } + break; + case KeyConfig::ACTION_TOGGLE_TIME: + m_show_time = !m_show_time; + m_player_subtitles.SetTimeVisible(m_show_time); + break; + case KeyConfig::ACTION_HIDE_TIME: + m_show_time = false; + m_player_subtitles.SetTimeVisible(m_show_time); + break; + case KeyConfig::ACTION_SHOW_TIME: + m_show_time = true; + m_player_subtitles.SetTimeVisible(m_show_time); + break; default: break; }