/* * * Copyright (C) 2012 Edgar Hucek * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #define AV_NOWARN_DEPRECATED extern "C" { #include #include }; #include "OMXStreamInfo.h" #include "utils/log.h" #include "DllAvUtil.h" #include "DllAvFormat.h" #include "DllAvCodec.h" #include "linux/RBP.h" #include "OMXVideo.h" #include "OMXAudioCodecOMX.h" #include "utils/PCMRemap.h" #include "OMXClock.h" #include "OMXAudio.h" #include "OMXReader.h" #include "OMXPlayerVideo.h" #include "OMXPlayerAudio.h" #include "OMXPlayerSubtitles.h" #include "OMXControl.h" #include "DllOMX.h" #include "Srt.h" #include "KeyConfig.h" #include "utils/Strprintf.h" #include "Keyboard.h" #include #include #include "version.h" // when we repeatedly seek, rather than play continuously #define TRICKPLAY(speed) (speed < 0 || speed > 4 * DVD_PLAYSPEED_NORMAL) #define DISPLAY_TEXT(text, ms) if(m_osd) m_player_subtitles.DisplayText(text, ms) #define DISPLAY_TEXT_SHORT(text) DISPLAY_TEXT(text, 1000) #define DISPLAY_TEXT_LONG(text) DISPLAY_TEXT(text, 2000) typedef enum {CONF_FLAGS_FORMAT_NONE, CONF_FLAGS_FORMAT_SBS, CONF_FLAGS_FORMAT_TB, CONF_FLAGS_FORMAT_FP } FORMAT_3D_T; enum PCMChannels *m_pChannelMap = NULL; volatile sig_atomic_t g_abort = false; bool m_passthrough = false; long m_Volume = 0; long m_Amplification = 0; bool m_Deinterlace = false; bool m_NoDeinterlace = false; bool m_NativeDeinterlace = false; OMX_IMAGEFILTERANAGLYPHTYPE m_anaglyph = OMX_ImageFilterAnaglyphNone; bool m_HWDecode = false; std::string deviceString = ""; int m_use_hw_audio = false; bool m_osd = true; bool m_no_keys = false; 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_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_centered = false; bool m_ghost_box = true; unsigned int m_subtitle_lines = 3; bool m_Pause = false; OMXReader m_omx_reader; int m_audio_index_use = 0; bool m_thread_player = false; OMXClock *m_av_clock = NULL; OMXControl m_omxcontrol; Keyboard *m_keyboard = NULL; COMXStreamInfo m_hints_audio; COMXStreamInfo m_hints_video; OMXPacket *m_omx_pkt = NULL; bool m_hdmi_clock_sync = false; bool m_no_hdmi_clock_sync = false; bool m_stop = false; int m_subtitle_index = -1; DllBcmHost m_BcmHost; OMXPlayerVideo m_player_video; OMXPlayerAudio m_player_audio; OMXPlayerSubtitles m_player_subtitles; int m_tv_show_info = 0; bool m_has_video = false; bool m_has_audio = false; bool m_has_subtitle = false; float m_display_aspect = 0.0f; bool m_boost_on_downmix = true; bool m_gen_log = false; bool m_loop = false; int m_layer = 0; int m_display = 0; enum{ERROR=-1,SUCCESS,ONEBYTE}; void sig_handler(int s) { if (s==SIGINT && !g_abort) { signal(SIGINT, SIG_DFL); g_abort = true; return; } signal(SIGABRT, SIG_DFL); signal(SIGSEGV, SIG_DFL); signal(SIGFPE, SIG_DFL); if (NULL != m_keyboard) { m_keyboard->Close(); } abort(); } void print_usage() { printf( #include "help.h" ); } void print_keybindings() { printf( #include "keys.h" ); } void print_version() { printf("omxplayer - Commandline multimedia player for the Raspberry Pi\n"); printf(" Build date: %s\n", VERSION_DATE); printf(" Version : %s [%s]\n", VERSION_HASH, VERSION_BRANCH); printf(" Repository: %s\n", VERSION_REPO); } static void PrintSubtitleInfo() { auto count = m_omx_reader.SubtitleStreamCount(); size_t index = 0; if(m_has_external_subtitles) { ++count; if(!m_player_subtitles.GetUseExternalSubtitles()) index = m_player_subtitles.GetActiveStream() + 1; } else if(m_has_subtitle) { index = m_player_subtitles.GetActiveStream(); } printf("Subtitle count: %d, state: %s, index: %d, delay: %d\n", count, m_has_subtitle && m_player_subtitles.GetVisible() ? " on" : "off", index+1, m_has_subtitle ? m_player_subtitles.GetDelay() : 0); } static void FlushStreams(double pts); static void SetSpeed(int iSpeed) { if(!m_av_clock) return; m_omx_reader.SetSpeed(iSpeed); // flush when in trickplay mode if (TRICKPLAY(iSpeed) || TRICKPLAY(m_av_clock->OMXPlaySpeed())) FlushStreams(DVD_NOPTS_VALUE); m_av_clock->OMXSetSpeed(iSpeed); } static float get_display_aspect_ratio(HDMI_ASPECT_T aspect) { float display_aspect; switch (aspect) { case HDMI_ASPECT_4_3: display_aspect = 4.0/3.0; break; case HDMI_ASPECT_14_9: display_aspect = 14.0/9.0; break; case HDMI_ASPECT_16_9: display_aspect = 16.0/9.0; break; case HDMI_ASPECT_5_4: display_aspect = 5.0/4.0; break; case HDMI_ASPECT_16_10: display_aspect = 16.0/10.0; break; case HDMI_ASPECT_15_9: display_aspect = 15.0/9.0; break; case HDMI_ASPECT_64_27: display_aspect = 64.0/27.0; break; default: display_aspect = 16.0/9.0; break; } return display_aspect; } static float get_display_aspect_ratio(SDTV_ASPECT_T aspect) { float display_aspect; switch (aspect) { case SDTV_ASPECT_4_3: display_aspect = 4.0/3.0; break; case SDTV_ASPECT_14_9: display_aspect = 14.0/9.0; break; case SDTV_ASPECT_16_9: display_aspect = 16.0/9.0; break; default: display_aspect = 4.0/3.0; break; } return display_aspect; } static void FlushStreams(double pts) { m_av_clock->OMXStop(); m_av_clock->OMXPause(); if(m_has_video) m_player_video.Flush(); if(m_has_audio) m_player_audio.Flush(); if(pts != DVD_NOPTS_VALUE) m_av_clock->OMXMediaTime(pts); if(m_has_subtitle) m_player_subtitles.Flush(); if(m_omx_pkt) { m_omx_reader.FreePacket(m_omx_pkt); m_omx_pkt = NULL; } } static void CallbackTvServiceCallback(void *userdata, uint32_t reason, uint32_t param1, uint32_t param2) { sem_t *tv_synced = (sem_t *)userdata; switch(reason) { case VC_HDMI_UNPLUGGED: break; case VC_HDMI_STANDBY: break; case VC_SDTV_NTSC: case VC_SDTV_PAL: case VC_HDMI_HDMI: case VC_HDMI_DVI: // Signal we are ready now sem_post(tv_synced); break; default: break; } } void SetVideoMode(int width, int height, int fpsrate, int fpsscale, FORMAT_3D_T is3d) { int32_t num_modes = 0; int i; HDMI_RES_GROUP_T prefer_group; HDMI_RES_GROUP_T group = HDMI_RES_GROUP_CEA; float fps = 60.0f; // better to force to higher rate if no information is known uint32_t prefer_mode; if (fpsrate && fpsscale) fps = DVD_TIME_BASE / OMXReader::NormalizeFrameduration((double)DVD_TIME_BASE * fpsscale / fpsrate); //Supported HDMI CEA/DMT resolutions, preferred resolution will be returned TV_SUPPORTED_MODE_NEW_T *supported_modes = NULL; // query the number of modes first int max_supported_modes = m_BcmHost.vc_tv_hdmi_get_supported_modes_new(group, NULL, 0, &prefer_group, &prefer_mode); if (max_supported_modes > 0) supported_modes = new TV_SUPPORTED_MODE_NEW_T[max_supported_modes]; if (supported_modes) { num_modes = m_BcmHost.vc_tv_hdmi_get_supported_modes_new(group, supported_modes, max_supported_modes, &prefer_group, &prefer_mode); if(m_gen_log) { CLog::Log(LOGDEBUG, "EGL get supported modes (%d) = %d, prefer_group=%x, prefer_mode=%x\n", group, num_modes, prefer_group, prefer_mode); } } TV_SUPPORTED_MODE_NEW_T *tv_found = NULL; if (num_modes > 0 && prefer_group != HDMI_RES_GROUP_INVALID) { uint32_t best_score = 1<<30; uint32_t scan_mode = m_NativeDeinterlace; for (i=0; iwidth; uint32_t h = tv->height; uint32_t r = tv->frame_rate; /* Check if frame rate match (equal or exact multiple) */ if(fabs(r - 1.0f*fps) / fps < 0.002f) score += 0; else if(fabs(r - 2.0f*fps) / fps < 0.002f) score += 1<<8; else score += (1<<16) + (1<<20)/r; // bad - but prefer higher framerate /* Check size too, only choose, bigger resolutions */ if(width && height) { /* cost of too small a resolution is high */ score += max((int)(width -w), 0) * (1<<16); score += max((int)(height-h), 0) * (1<<16); /* cost of too high a resolution is lower */ score += max((int)(w-width ), 0) * (1<<4); score += max((int)(h-height), 0) * (1<<4); } // native is good if (!tv->native) score += 1<<16; // interlace is bad if (scan_mode != tv->scan_mode) score += (1<<16); // wanting 3D but not getting it is a negative if (is3d == CONF_FLAGS_FORMAT_SBS && !(tv->struct_3d_mask & HDMI_3D_STRUCT_SIDE_BY_SIDE_HALF_HORIZONTAL)) score += 1<<18; if (is3d == CONF_FLAGS_FORMAT_TB && !(tv->struct_3d_mask & HDMI_3D_STRUCT_TOP_AND_BOTTOM)) score += 1<<18; if (is3d == CONF_FLAGS_FORMAT_FP && !(tv->struct_3d_mask & HDMI_3D_STRUCT_FRAME_PACKING)) score += 1<<18; // prefer square pixels modes float par = get_display_aspect_ratio((HDMI_ASPECT_T)tv->aspect_ratio)*(float)tv->height/(float)tv->width; score += fabs(par - 1.0f) * (1<<12); /*printf("mode %dx%d@%d %s%s:%x par=%.2f score=%d\n", tv->width, tv->height, tv->frame_rate, tv->native?"N":"", tv->scan_mode?"I":"", tv->code, par, score);*/ if (score < best_score) { tv_found = tv; best_score = score; } } } if(tv_found) { char response[80]; printf("Output mode %d: %dx%d@%d %s%s:%x\n", tv_found->code, tv_found->width, tv_found->height, tv_found->frame_rate, tv_found->native?"N":"", tv_found->scan_mode?"I":"", tv_found->code); if (m_NativeDeinterlace && tv_found->scan_mode) vc_gencmd(response, sizeof response, "hvs_update_fields %d", 1); // if we are closer to ntsc version of framerate, let gpu know int ifps = (int)(fps+0.5f); bool ntsc_freq = fabs(fps*1001.0f/1000.0f - ifps) < fabs(fps-ifps); /* inform TV of ntsc setting */ HDMI_PROPERTY_PARAM_T property; property.property = HDMI_PROPERTY_PIXEL_CLOCK_TYPE; property.param1 = ntsc_freq ? HDMI_PIXEL_CLOCK_TYPE_NTSC : HDMI_PIXEL_CLOCK_TYPE_PAL; property.param2 = 0; /* inform TV of any 3D settings. Note this property just applies to next hdmi mode change, so no need to call for 2D modes */ property.property = HDMI_PROPERTY_3D_STRUCTURE; property.param1 = HDMI_3D_FORMAT_NONE; property.param2 = 0; if (is3d != CONF_FLAGS_FORMAT_NONE) { if (is3d == CONF_FLAGS_FORMAT_SBS && tv_found->struct_3d_mask & HDMI_3D_STRUCT_SIDE_BY_SIDE_HALF_HORIZONTAL) property.param1 = HDMI_3D_FORMAT_SBS_HALF; else if (is3d == CONF_FLAGS_FORMAT_TB && tv_found->struct_3d_mask & HDMI_3D_STRUCT_TOP_AND_BOTTOM) property.param1 = HDMI_3D_FORMAT_TB_HALF; else if (is3d == CONF_FLAGS_FORMAT_FP && tv_found->struct_3d_mask & HDMI_3D_STRUCT_FRAME_PACKING) property.param1 = HDMI_3D_FORMAT_FRAME_PACKING; m_BcmHost.vc_tv_hdmi_set_property(&property); } printf("ntsc_freq:%d %s\n", ntsc_freq, property.param1 == HDMI_3D_FORMAT_SBS_HALF ? "3DSBS" : property.param1 == HDMI_3D_FORMAT_TB_HALF ? "3DTB" : property.param1 == HDMI_3D_FORMAT_FRAME_PACKING ? "3DFP":""); sem_t tv_synced; sem_init(&tv_synced, 0, 0); m_BcmHost.vc_tv_register_callback(CallbackTvServiceCallback, &tv_synced); int success = m_BcmHost.vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, (HDMI_RES_GROUP_T)group, tv_found->code); if (success == 0) sem_wait(&tv_synced); m_BcmHost.vc_tv_unregister_callback(CallbackTvServiceCallback); sem_destroy(&tv_synced); } if (supported_modes) delete[] supported_modes; } bool Exists(const std::string& path) { struct stat buf; auto error = stat(path.c_str(), &buf); return !error || errno != ENOENT; } bool IsURL(const std::string& str) { auto result = str.find("://"); if(result == std::string::npos || result == 0) return false; for(size_t i = 0; i < result; ++i) { if(!isalpha(str[i])) return false; } return true; } bool IsPipe(const std::string& str) { if (str.compare(0, 5, "pipe:") == 0) return true; return false; } static int get_mem_gpu(void) { char response[80] = ""; int gpu_mem = 0; if (vc_gencmd(response, sizeof response, "get_mem gpu") == 0) vc_gencmd_number_property(response, "gpu", &gpu_mem); return gpu_mem; } static void blank_background(bool enable) { if (!enable) return; // we create a 1x1 black pixel image that is added to display just behind video DISPMANX_DISPLAY_HANDLE_T display; DISPMANX_UPDATE_HANDLE_T update; DISPMANX_RESOURCE_HANDLE_T resource; DISPMANX_ELEMENT_HANDLE_T element; int ret; uint32_t vc_image_ptr; VC_IMAGE_TYPE_T type = VC_IMAGE_RGB565; uint16_t image = 0x0000; // black int layer = m_layer - 1; VC_RECT_T dst_rect, src_rect; display = vc_dispmanx_display_open(m_display); assert(display); resource = vc_dispmanx_resource_create( type, 1 /*width*/, 1 /*height*/, &vc_image_ptr ); assert( resource ); vc_dispmanx_rect_set( &dst_rect, 0, 0, 1, 1); ret = vc_dispmanx_resource_write_data( resource, type, sizeof(image), &image, &dst_rect ); assert(ret == 0); vc_dispmanx_rect_set( &src_rect, 0, 0, 1<<16, 1<<16); vc_dispmanx_rect_set( &dst_rect, 0, 0, 0, 0); update = vc_dispmanx_update_start(0); assert(update); element = vc_dispmanx_element_add(update, display, layer, &dst_rect, resource, &src_rect, DISPMANX_PROTECTION_NONE, NULL, NULL, (DISPMANX_TRANSFORM_T)0 ); assert(element); ret = vc_dispmanx_update_submit_sync( update ); assert( ret == 0 ); } int main(int argc, char *argv[]) { signal(SIGSEGV, sig_handler); signal(SIGABRT, sig_handler); signal(SIGFPE, sig_handler); signal(SIGINT, sig_handler); bool m_send_eos = false; bool m_packet_after_seek = false; bool m_seek_flush = false; bool m_new_win_pos = false; bool m_chapter_seek = false; std::string m_filename; double m_incr = 0; double m_loop_from = 0; CRBP g_RBP; COMXCore g_OMX; bool m_stats = false; bool m_dump_format = false; bool m_dump_format_exit = false; FORMAT_3D_T m_3d = CONF_FLAGS_FORMAT_NONE; bool m_refresh = false; double startpts = 0; CRect DestRect = {0,0,0,0}; bool m_blank_background = false; bool sentStarted = false; float audio_fifo_size = 0.0; // zero means use default float video_fifo_size = 0.0; float audio_queue_size = 0.0; float video_queue_size = 0.0; float m_threshold = -1.0f; // amount of audio/video required to come out of buffering float m_timeout = 10.0f; // amount of time file/network operation can stall for before timing out int m_orientation = -1; // unset float m_fps = 0.0f; // unset bool m_live = false; // set to true for live tv or vod for low buffering enum PCMLayout m_layout = PCM_LAYOUT_2_0; TV_DISPLAY_STATE_T tv_state; double last_seek_pos = 0; bool idle = false; std::string m_cookie = ""; std::string m_user_agent = ""; const int font_opt = 0x100; const int italic_font_opt = 0x201; const int font_size_opt = 0x101; const int align_opt = 0x102; const int no_ghost_box_opt = 0x203; const int subtitles_opt = 0x103; const int lines_opt = 0x104; const int pos_opt = 0x105; const int vol_opt = 0x106; const int audio_fifo_opt = 0x107; const int video_fifo_opt = 0x108; const int audio_queue_opt = 0x109; const int video_queue_opt = 0x10a; const int no_deinterlace_opt = 0x10b; const int threshold_opt = 0x10c; const int timeout_opt = 0x10f; const int boost_on_downmix_opt = 0x200; const int no_boost_on_downmix_opt = 0x207; const int key_config_opt = 0x10d; const int amp_opt = 0x10e; const int no_osd_opt = 0x202; const int orientation_opt = 0x204; const int fps_opt = 0x208; const int live_opt = 0x205; const int layout_opt = 0x206; const int dbus_name_opt = 0x209; const int loop_opt = 0x20a; const int layer_opt = 0x20b; const int no_keys_opt = 0x20c; const int anaglyph_opt = 0x20d; const int native_deinterlace_opt = 0x20e; const int display_opt = 0x20f; const int http_cookie_opt = 0x300; const int http_user_agent_opt = 0x301; struct option longopts[] = { { "info", no_argument, NULL, 'i' }, { "with-info", no_argument, NULL, 'I' }, { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, { "keys", no_argument, NULL, 'k' }, { "aidx", required_argument, NULL, 'n' }, { "adev", required_argument, NULL, 'o' }, { "stats", no_argument, NULL, 's' }, { "passthrough", no_argument, NULL, 'p' }, { "vol", required_argument, NULL, vol_opt }, { "amp", required_argument, NULL, amp_opt }, { "deinterlace", no_argument, NULL, 'd' }, { "nodeinterlace",no_argument, NULL, no_deinterlace_opt }, { "nativedeinterlace",no_argument, NULL, native_deinterlace_opt }, { "anaglyph", required_argument, NULL, anaglyph_opt }, { "hw", no_argument, NULL, 'w' }, { "3d", required_argument, NULL, '3' }, { "hdmiclocksync", no_argument, NULL, 'y' }, { "nohdmiclocksync", no_argument, NULL, 'z' }, { "refresh", no_argument, NULL, 'r' }, { "genlog", no_argument, NULL, 'g' }, { "sid", required_argument, NULL, 't' }, { "pos", required_argument, NULL, 'l' }, { "blank", no_argument, NULL, 'b' }, { "font", required_argument, NULL, font_opt }, { "italic-font", required_argument, NULL, italic_font_opt }, { "font-size", required_argument, NULL, font_size_opt }, { "align", required_argument, NULL, align_opt }, { "no-ghost-box", no_argument, NULL, no_ghost_box_opt }, { "subtitles", required_argument, NULL, subtitles_opt }, { "lines", required_argument, NULL, lines_opt }, { "win", required_argument, NULL, pos_opt }, { "audio_fifo", required_argument, NULL, audio_fifo_opt }, { "video_fifo", required_argument, NULL, video_fifo_opt }, { "audio_queue", required_argument, NULL, audio_queue_opt }, { "video_queue", required_argument, NULL, video_queue_opt }, { "threshold", required_argument, NULL, threshold_opt }, { "timeout", required_argument, NULL, timeout_opt }, { "boost-on-downmix", no_argument, NULL, boost_on_downmix_opt }, { "no-boost-on-downmix", no_argument, NULL, no_boost_on_downmix_opt }, { "key-config", required_argument, NULL, key_config_opt }, { "no-osd", no_argument, NULL, no_osd_opt }, { "no-keys", no_argument, NULL, no_keys_opt }, { "orientation", required_argument, NULL, orientation_opt }, { "fps", required_argument, NULL, fps_opt }, { "live", no_argument, NULL, live_opt }, { "layout", required_argument, NULL, layout_opt }, { "dbus_name", required_argument, NULL, dbus_name_opt }, { "loop", no_argument, NULL, loop_opt }, { "layer", required_argument, NULL, layer_opt }, { "display", required_argument, NULL, display_opt }, { "cookie", required_argument, NULL, http_cookie_opt }, { "user-agent", required_argument, NULL, http_user_agent_opt }, { 0, 0, 0, 0 } }; #define S(x) (int)(DVD_PLAYSPEED_NORMAL*(x)) int playspeeds[] = {S(0), S(1/16.0), S(1/8.0), S(1/4.0), S(1/2.0), S(0.975), S(1.0), S(1.125), S(-32.0), S(-16.0), S(-8.0), S(-4), S(-2), S(-1), S(1), S(2.0), S(4.0), S(8.0), S(16.0), S(32.0)}; const int playspeed_slow_min = 0, playspeed_slow_max = 7, playspeed_rew_max = 8, playspeed_rew_min = 13, playspeed_normal = 14, playspeed_ff_min = 15, playspeed_ff_max = 19; int playspeed_current = playspeed_normal; double m_last_check_time = 0.0; float m_latency = 0.0f; int c; std::string mode; //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:cslbpd3:yzt:rg", longopts, NULL)) != -1) { switch (c) { case 'r': m_refresh = true; break; case 'g': m_gen_log = true; break; case 'y': m_hdmi_clock_sync = true; break; case 'z': m_no_hdmi_clock_sync = true; break; case '3': mode = optarg; if(mode != "SBS" && mode != "TB" && mode != "FP") { print_usage(); return 0; } if(mode == "TB") m_3d = CONF_FLAGS_FORMAT_TB; else if(mode == "FP") m_3d = CONF_FLAGS_FORMAT_FP; else m_3d = CONF_FLAGS_FORMAT_SBS; break; case 'd': m_Deinterlace = true; break; case no_deinterlace_opt: m_NoDeinterlace = true; break; case native_deinterlace_opt: m_NoDeinterlace = true; m_NativeDeinterlace = true; break; case anaglyph_opt: m_anaglyph = (OMX_IMAGEFILTERANAGLYPHTYPE)atoi(optarg); break; case 'w': m_use_hw_audio = true; break; case 'p': m_passthrough = true; break; case 's': m_stats = true; break; case 'o': deviceString = optarg; if(deviceString != "local" && deviceString != "hdmi" && deviceString != "both") { print_usage(); return 0; } deviceString = "omx:" + deviceString; break; case 'i': m_dump_format = true; m_dump_format_exit = true; break; case 'I': m_dump_format = true; break; case 't': m_subtitle_index = atoi(optarg) - 1; if(m_subtitle_index < 0) m_subtitle_index = 0; break; case 'n': m_audio_index_use = atoi(optarg); break; case 'l': { if(strchr(optarg, ':')) { unsigned int h, m, s; if(sscanf(optarg, "%u:%u:%u", &h, &m, &s) == 3) m_incr = h*3600 + m*60 + s; } else { m_incr = atof(optarg); } if(m_loop) m_loop_from = m_incr; } break; case no_osd_opt: m_osd = false; break; case no_keys_opt: m_no_keys = true; break; case font_opt: m_font_path = optarg; m_asked_for_font = true; break; case italic_font_opt: m_italic_font_path = optarg; m_asked_for_italic_font = true; break; case font_size_opt: { const int thousands = atoi(optarg); if (thousands > 0) m_font_size = thousands*0.001f; } break; case align_opt: m_centered = !strcmp(optarg, "center"); break; case no_ghost_box_opt: m_ghost_box = false; break; case subtitles_opt: m_external_subtitles_path = optarg; m_has_external_subtitles = true; break; case lines_opt: m_subtitle_lines = std::max(atoi(optarg), 1); break; case pos_opt: sscanf(optarg, "%f %f %f %f", &DestRect.x1, &DestRect.y1, &DestRect.x2, &DestRect.y2) == 4 || sscanf(optarg, "%f,%f,%f,%f", &DestRect.x1, &DestRect.y1, &DestRect.x2, &DestRect.y2); break; case vol_opt: m_Volume = atoi(optarg); break; case amp_opt: m_Amplification = atoi(optarg); break; case boost_on_downmix_opt: m_boost_on_downmix = true; break; case no_boost_on_downmix_opt: m_boost_on_downmix = false; break; case audio_fifo_opt: audio_fifo_size = atof(optarg); break; case video_fifo_opt: video_fifo_size = atof(optarg); break; case audio_queue_opt: audio_queue_size = atof(optarg); break; case video_queue_opt: video_queue_size = atof(optarg); break; case threshold_opt: m_threshold = atof(optarg); break; case timeout_opt: m_timeout = atof(optarg); break; case orientation_opt: m_orientation = atoi(optarg); break; case fps_opt: m_fps = atof(optarg); break; case live_opt: m_live = true; break; case layout_opt: { const char *layouts[] = {"2.0", "2.1", "3.0", "3.1", "4.0", "4.1", "5.0", "5.1", "7.0", "7.1"}; unsigned i; for (i=0; i= argc) { print_usage(); return 0; } m_filename = argv[optind]; auto PrintFileNotFound = [](const std::string& path) { printf("File \"%s\" not found.\n", path.c_str()); }; bool filename_is_URL = IsURL(m_filename); if(!filename_is_URL && !IsPipe(m_filename) && !Exists(m_filename)) { PrintFileNotFound(m_filename); return 0; } if(m_asked_for_font && !Exists(m_font_path)) { PrintFileNotFound(m_font_path); return 0; } if(m_asked_for_italic_font && !Exists(m_italic_font_path)) { PrintFileNotFound(m_italic_font_path); return 0; } if(m_has_external_subtitles && !Exists(m_external_subtitles_path)) { PrintFileNotFound(m_external_subtitles_path); return 0; } if(!m_has_external_subtitles && !filename_is_URL) { auto subtitles_path = m_filename.substr(0, m_filename.find_last_of(".")) + ".srt"; if(Exists(subtitles_path)) { m_external_subtitles_path = subtitles_path; m_has_external_subtitles = true; } } bool m_audio_extension = false; const CStdString m_musicExtensions = ".nsv|.m4a|.flac|.aac|.strm|.pls|.rm|.rma|.mpa|.wav|.wma|.ogg|.mp3|.mp2|.m3u|.mod|.amf|.669|.dmf|.dsm|.far|.gdm|" ".imf|.it|.m15|.med|.okt|.s3m|.stm|.sfx|.ult|.uni|.xm|.sid|.ac3|.dts|.cue|.aif|.aiff|.wpl|.ape|.mac|.mpc|.mp+|.mpp|.shn|.zip|.rar|" ".wv|.nsf|.spc|.gym|.adx|.dsp|.adp|.ymf|.ast|.afc|.hps|.xsp|.xwav|.waa|.wvs|.wam|.gcm|.idsp|.mpdsp|.mss|.spt|.rsd|.mid|.kar|.sap|" ".cmc|.cmr|.dmc|.mpt|.mpd|.rmt|.tmc|.tm8|.tm2|.oga|.url|.pxml|.tta|.rss|.cm3|.cms|.dlt|.brstm|.mka"; if (m_filename.find_last_of(".") != string::npos) { CStdString extension = m_filename.substr(m_filename.find_last_of(".")); if (!extension.IsEmpty() && m_musicExtensions.Find(extension.ToLower()) != -1) m_audio_extension = true; } if(m_gen_log) { CLog::SetLogLevel(LOG_LEVEL_DEBUG); CLog::Init("./"); } else { CLog::SetLogLevel(LOG_LEVEL_NONE); } g_RBP.Initialize(); g_OMX.Initialize(); blank_background(m_blank_background); int gpu_mem = get_mem_gpu(); int min_gpu_mem = 64; if (gpu_mem > 0 && gpu_mem < min_gpu_mem) printf("Only %dM of gpu_mem is configured. Try running \"sudo raspi-config\" and ensure that \"memory_split\" has a value of %d or greater\n", gpu_mem, min_gpu_mem); m_av_clock = new OMXClock(); int control_err = m_omxcontrol.init( m_av_clock, &m_player_audio, &m_player_subtitles, &m_omx_reader, m_dbus_name ); if (false == m_no_keys) { m_keyboard = new Keyboard(); } if (NULL != m_keyboard) { m_keyboard->setKeymap(keymap); m_keyboard->setDbusName(m_dbus_name); } m_thread_player = true; if(!m_omx_reader.Open(m_filename.c_str(), m_dump_format, m_live, m_timeout, m_cookie.c_str(), m_user_agent.c_str())) goto do_exit; if (m_dump_format_exit) goto do_exit; m_has_video = m_omx_reader.VideoStreamCount(); m_has_audio = m_audio_index_use < 0 ? false : m_omx_reader.AudioStreamCount(); m_has_subtitle = m_has_external_subtitles || m_omx_reader.SubtitleStreamCount(); m_loop = m_loop && m_omx_reader.CanSeek(); if (m_audio_extension) { CLog::Log(LOGWARNING, "%s - Ignoring video in audio filetype:%s", __FUNCTION__, m_filename.c_str()); m_has_video = false; } if(m_filename.find("3DSBS") != string::npos || m_filename.find("HSBS") != string::npos) m_3d = CONF_FLAGS_FORMAT_SBS; else if(m_filename.find("3DTAB") != string::npos || m_filename.find("HTAB") != string::npos) m_3d = CONF_FLAGS_FORMAT_TB; // 3d modes don't work without switch hdmi mode if (m_3d != CONF_FLAGS_FORMAT_NONE || m_NativeDeinterlace) m_refresh = true; // you really don't want want to match refresh rate without hdmi clock sync if ((m_refresh || m_NativeDeinterlace) && !m_no_hdmi_clock_sync) m_hdmi_clock_sync = true; if(!m_av_clock->OMXInitialize()) goto do_exit; if(m_hdmi_clock_sync && !m_av_clock->HDMIClockSync()) goto do_exit; m_av_clock->OMXStateIdle(); m_av_clock->OMXStop(); m_av_clock->OMXPause(); m_omx_reader.GetHints(OMXSTREAM_AUDIO, m_hints_audio); m_omx_reader.GetHints(OMXSTREAM_VIDEO, m_hints_video); if (m_fps > 0.0f) m_hints_video.fpsrate = m_fps * DVD_TIME_BASE, m_hints_video.fpsscale = DVD_TIME_BASE; if(m_audio_index_use > 0) m_omx_reader.SetActiveStream(OMXSTREAM_AUDIO, m_audio_index_use-1); if(m_has_video && m_refresh) { memset(&tv_state, 0, sizeof(TV_DISPLAY_STATE_T)); m_BcmHost.vc_tv_get_display_state(&tv_state); SetVideoMode(m_hints_video.width, m_hints_video.height, m_hints_video.fpsrate, m_hints_video.fpsscale, m_3d); } // get display aspect TV_DISPLAY_STATE_T current_tv_state; memset(¤t_tv_state, 0, sizeof(TV_DISPLAY_STATE_T)); m_BcmHost.vc_tv_get_display_state(¤t_tv_state); if(current_tv_state.state & ( VC_HDMI_HDMI | VC_HDMI_DVI )) { //HDMI or DVI on m_display_aspect = get_display_aspect_ratio((HDMI_ASPECT_T)current_tv_state.display.hdmi.aspect_ratio); } else { //composite on m_display_aspect = get_display_aspect_ratio((SDTV_ASPECT_T)current_tv_state.display.sdtv.display_options.aspect); } m_display_aspect *= (float)current_tv_state.display.hdmi.height/(float)current_tv_state.display.hdmi.width; if (m_orientation >= 0) m_hints_video.orientation = m_orientation; if(m_has_video && !m_player_video.Open(m_hints_video, m_av_clock, DestRect, m_Deinterlace ? VS_DEINTERLACEMODE_FORCE:m_NoDeinterlace ? VS_DEINTERLACEMODE_OFF:VS_DEINTERLACEMODE_AUTO, m_anaglyph, m_hdmi_clock_sync, m_thread_player, m_display_aspect, m_display, m_layer, video_queue_size, video_fifo_size)) goto do_exit; if(m_has_subtitle || m_osd) { std::vector external_subtitles; if(m_has_external_subtitles && !ReadSrt(m_external_subtitles_path, external_subtitles)) { puts("Unable to read the subtitle file."); goto do_exit; } if(!m_player_subtitles.Open(m_omx_reader.SubtitleStreamCount(), std::move(external_subtitles), m_font_path, m_italic_font_path, m_font_size, m_centered, m_ghost_box, m_subtitle_lines, m_display, m_layer + 1, m_av_clock)) goto do_exit; } if(m_has_subtitle) { if(!m_has_external_subtitles) { if(m_subtitle_index != -1) { m_player_subtitles.SetActiveStream( std::min(m_subtitle_index, m_omx_reader.SubtitleStreamCount()-1)); } m_player_subtitles.SetUseExternalSubtitles(false); } if(m_subtitle_index == -1 && !m_has_external_subtitles) m_player_subtitles.SetVisible(false); } m_omx_reader.GetHints(OMXSTREAM_AUDIO, m_hints_audio); if (deviceString == "") { if (m_BcmHost.vc_tv_hdmi_audio_supported(EDID_AudioFormat_ePCM, 2, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit ) == 0) deviceString = "omx:hdmi"; else deviceString = "omx:local"; } if ((m_hints_audio.codec == CODEC_ID_AC3 || m_hints_audio.codec == CODEC_ID_EAC3) && m_BcmHost.vc_tv_hdmi_audio_supported(EDID_AudioFormat_eAC3, 2, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit ) != 0) m_passthrough = false; if (m_hints_audio.codec == CODEC_ID_DTS && m_BcmHost.vc_tv_hdmi_audio_supported(EDID_AudioFormat_eDTS, 2, EDID_AudioSampleRate_e44KHz, EDID_AudioSampleSize_16bit ) != 0) m_passthrough = false; if(m_has_audio && !m_player_audio.Open(m_hints_audio, m_av_clock, &m_omx_reader, deviceString, m_passthrough, m_use_hw_audio, m_boost_on_downmix, m_thread_player, m_live, m_layout, audio_queue_size, audio_fifo_size)) goto do_exit; if(m_has_audio) { m_player_audio.SetVolume(pow(10, m_Volume / 2000.0)); if (m_Amplification) m_player_audio.SetDynamicRangeCompression(m_Amplification); } if (m_threshold < 0.0f) m_threshold = m_live ? 0.7f : 0.2f; PrintSubtitleInfo(); m_av_clock->OMXReset(m_has_video, m_has_audio); m_av_clock->OMXStateExecute(); sentStarted = true; while(!m_stop) { if(g_abort) goto do_exit; double now = m_av_clock->GetAbsoluteClock(); bool update = false; if (m_last_check_time == 0.0 || m_last_check_time + DVD_MSEC_TO_TIME(20) <= now) { update = true; m_last_check_time = now; } if (update) { OMXControlResult result = control_err ? (OMXControlResult)m_keyboard->getEvent() : m_omxcontrol.getEvent(); double oldPos, newPos; switch(result.getKey()) { case KeyConfig::ACTION_SHOW_INFO: m_tv_show_info = !m_tv_show_info; vc_tv_show_info(m_tv_show_info); break; case KeyConfig::ACTION_DECREASE_SPEED: if (playspeed_current < playspeed_slow_min || playspeed_current > playspeed_slow_max) playspeed_current = playspeed_slow_max-1; playspeed_current = std::max(playspeed_current-1, playspeed_slow_min); SetSpeed(playspeeds[playspeed_current]); DISPLAY_TEXT_SHORT( strprintf("Playspeed: %.3f", playspeeds[playspeed_current]/1000.0f)); printf("Playspeed %.3f\n", playspeeds[playspeed_current]/1000.0f); m_Pause = false; break; case KeyConfig::ACTION_INCREASE_SPEED: if (playspeed_current < playspeed_slow_min || playspeed_current > playspeed_slow_max) playspeed_current = playspeed_slow_max-1; playspeed_current = std::min(playspeed_current+1, playspeed_slow_max); SetSpeed(playspeeds[playspeed_current]); DISPLAY_TEXT_SHORT( strprintf("Playspeed: %.3f", playspeeds[playspeed_current]/1000.0f)); printf("Playspeed %.3f\n", playspeeds[playspeed_current]/1000.0f); m_Pause = false; break; case KeyConfig::ACTION_REWIND: if (playspeed_current >= playspeed_ff_min && playspeed_current <= playspeed_ff_max) { playspeed_current = playspeed_normal; m_seek_flush = true; } else if (playspeed_current < playspeed_rew_max || playspeed_current > playspeed_rew_min) playspeed_current = playspeed_rew_min; else playspeed_current = std::max(playspeed_current-1, playspeed_rew_max); SetSpeed(playspeeds[playspeed_current]); DISPLAY_TEXT_SHORT( strprintf("Playspeed: %.3f", playspeeds[playspeed_current]/1000.0f)); printf("Playspeed %.3f\n", playspeeds[playspeed_current]/1000.0f); m_Pause = false; break; case KeyConfig::ACTION_FAST_FORWARD: if (playspeed_current >= playspeed_rew_max && playspeed_current <= playspeed_rew_min) { playspeed_current = playspeed_normal; m_seek_flush = true; } else if (playspeed_current < playspeed_ff_min || playspeed_current > playspeed_ff_max) playspeed_current = playspeed_ff_min; else playspeed_current = std::min(playspeed_current+1, playspeed_ff_max); SetSpeed(playspeeds[playspeed_current]); DISPLAY_TEXT_SHORT( strprintf("Playspeed: %.3f", playspeeds[playspeed_current]/1000.0f)); printf("Playspeed %.3f\n", playspeeds[playspeed_current]/1000.0f); m_Pause = false; break; case KeyConfig::ACTION_STEP: m_av_clock->OMXStep(); printf("Step\n"); { auto t = (unsigned) (m_av_clock->OMXMediaTime()*1e-3); auto dur = m_omx_reader.GetStreamLength() / 1000; DISPLAY_TEXT_SHORT( strprintf("Step\n%02d:%02d:%02d.%03d / %02d:%02d:%02d", (t/3600000), (t/60000)%60, (t/1000)%60, t%1000, (dur/3600), (dur/60)%60, dur%60)); } break; case KeyConfig::ACTION_PREVIOUS_AUDIO: if(m_has_audio) { int new_index = m_omx_reader.GetAudioIndex() - 1; if(new_index >= 0) { m_omx_reader.SetActiveStream(OMXSTREAM_AUDIO, new_index); DISPLAY_TEXT_SHORT( strprintf("Audio stream: %d", m_omx_reader.GetAudioIndex() + 1)); } } break; case KeyConfig::ACTION_NEXT_AUDIO: if(m_has_audio) { m_omx_reader.SetActiveStream(OMXSTREAM_AUDIO, m_omx_reader.GetAudioIndex() + 1); DISPLAY_TEXT_SHORT( strprintf("Audio stream: %d", m_omx_reader.GetAudioIndex() + 1)); } break; case KeyConfig::ACTION_PREVIOUS_CHAPTER: if(m_omx_reader.GetChapterCount() > 0) { m_omx_reader.SeekChapter(m_omx_reader.GetChapter() - 1, &startpts); DISPLAY_TEXT_LONG(strprintf("Chapter %d", m_omx_reader.GetChapter())); FlushStreams(startpts); m_seek_flush = true; m_chapter_seek = true; } else { m_incr = -600.0; } break; case KeyConfig::ACTION_NEXT_CHAPTER: if(m_omx_reader.GetChapterCount() > 0) { m_omx_reader.SeekChapter(m_omx_reader.GetChapter() + 1, &startpts); DISPLAY_TEXT_LONG(strprintf("Chapter %d", m_omx_reader.GetChapter())); FlushStreams(startpts); m_seek_flush = true; m_chapter_seek = true; } else { m_incr = 600.0; } break; case KeyConfig::ACTION_PREVIOUS_SUBTITLE: if(m_has_subtitle) { if(!m_player_subtitles.GetUseExternalSubtitles()) { if (m_player_subtitles.GetActiveStream() == 0) { if(m_has_external_subtitles) { DISPLAY_TEXT_SHORT("Subtitle file:\n" + m_external_subtitles_path); m_player_subtitles.SetUseExternalSubtitles(true); } } else { auto new_index = m_player_subtitles.GetActiveStream()-1; DISPLAY_TEXT_SHORT(strprintf("Subtitle stream: %d", new_index+1)); m_player_subtitles.SetActiveStream(new_index); } } m_player_subtitles.SetVisible(true); PrintSubtitleInfo(); } break; case KeyConfig::ACTION_NEXT_SUBTITLE: if(m_has_subtitle) { if(m_player_subtitles.GetUseExternalSubtitles()) { if(m_omx_reader.SubtitleStreamCount()) { assert(m_player_subtitles.GetActiveStream() == 0); DISPLAY_TEXT_SHORT("Subtitle stream: 1"); m_player_subtitles.SetUseExternalSubtitles(false); } } else { auto new_index = m_player_subtitles.GetActiveStream()+1; if(new_index < (size_t) m_omx_reader.SubtitleStreamCount()) { DISPLAY_TEXT_SHORT(strprintf("Subtitle stream: %d", new_index+1)); m_player_subtitles.SetActiveStream(new_index); } } m_player_subtitles.SetVisible(true); PrintSubtitleInfo(); } break; case KeyConfig::ACTION_TOGGLE_SUBTITLE: if(m_has_subtitle) { m_player_subtitles.SetVisible(!m_player_subtitles.GetVisible()); PrintSubtitleInfo(); } break; case KeyConfig::ACTION_HIDE_SUBTITLES: if(m_has_subtitle) { m_player_subtitles.SetVisible(false); PrintSubtitleInfo(); } break; case KeyConfig::ACTION_SHOW_SUBTITLES: if(m_has_subtitle) { m_player_subtitles.SetVisible(true); PrintSubtitleInfo(); } break; case KeyConfig::ACTION_DECREASE_SUBTITLE_DELAY: if(m_has_subtitle && m_player_subtitles.GetVisible()) { auto new_delay = m_player_subtitles.GetDelay() - 250; DISPLAY_TEXT_SHORT(strprintf("Subtitle delay: %d ms", new_delay)); m_player_subtitles.SetDelay(new_delay); PrintSubtitleInfo(); } break; case KeyConfig::ACTION_INCREASE_SUBTITLE_DELAY: if(m_has_subtitle && m_player_subtitles.GetVisible()) { auto new_delay = m_player_subtitles.GetDelay() + 250; DISPLAY_TEXT_SHORT(strprintf("Subtitle delay: %d ms", new_delay)); m_player_subtitles.SetDelay(new_delay); PrintSubtitleInfo(); } break; case KeyConfig::ACTION_EXIT: m_stop = true; goto do_exit; break; case KeyConfig::ACTION_SEEK_BACK_SMALL: if(m_omx_reader.CanSeek()) m_incr = -30.0; break; case KeyConfig::ACTION_SEEK_FORWARD_SMALL: if(m_omx_reader.CanSeek()) m_incr = 30.0; break; case KeyConfig::ACTION_SEEK_FORWARD_LARGE: if(m_omx_reader.CanSeek()) m_incr = 600.0; break; case KeyConfig::ACTION_SEEK_BACK_LARGE: if(m_omx_reader.CanSeek()) m_incr = -600.0; break; case KeyConfig::ACTION_SEEK_RELATIVE: m_incr = result.getArg() * 1e-6; break; case KeyConfig::ACTION_SEEK_ABSOLUTE: newPos = result.getArg() * 1e-6; oldPos = m_av_clock->OMXMediaTime()*1e-6; m_incr = newPos - oldPos; break; case KeyConfig::ACTION_PAUSE: m_Pause = !m_Pause; if (m_av_clock->OMXPlaySpeed() != DVD_PLAYSPEED_NORMAL && m_av_clock->OMXPlaySpeed() != DVD_PLAYSPEED_PAUSE) { printf("resume\n"); playspeed_current = playspeed_normal; SetSpeed(playspeeds[playspeed_current]); m_seek_flush = true; } if(m_Pause) { if(m_has_subtitle) m_player_subtitles.Pause(); auto t = (unsigned) (m_av_clock->OMXMediaTime()*1e-6); auto dur = m_omx_reader.GetStreamLength() / 1000; DISPLAY_TEXT_LONG(strprintf("Pause\n%02d:%02d:%02d / %02d:%02d:%02d", (t/3600), (t/60)%60, t%60, (dur/3600), (dur/60)%60, dur%60)); } else { if(m_has_subtitle) m_player_subtitles.Resume(); auto t = (unsigned) (m_av_clock->OMXMediaTime()*1e-6); auto dur = m_omx_reader.GetStreamLength() / 1000; DISPLAY_TEXT_SHORT(strprintf("Play\n%02d:%02d:%02d / %02d:%02d:%02d", (t/3600), (t/60)%60, t%60, (dur/3600), (dur/60)%60, dur%60)); } break; case KeyConfig::ACTION_MOVE_VIDEO: sscanf(result.getWinArg(), "%f %f %f %f", &DestRect.x1, &DestRect.y1, &DestRect.x2, &DestRect.y2); m_has_video = true; m_new_win_pos = true; m_seek_flush = true; break; case KeyConfig::ACTION_HIDE_VIDEO: m_has_video = false; m_player_video.Close(); if (m_live) { m_omx_reader.Close(); idle = true; } break; case KeyConfig::ACTION_UNHIDE_VIDEO: m_has_video = true; if (m_live) { idle = false; if(!m_omx_reader.Open(m_filename.c_str(), m_dump_format, true)) goto do_exit; } m_new_win_pos = true; m_seek_flush = true; break; case KeyConfig::ACTION_DECREASE_VOLUME: m_Volume -= 300; m_player_audio.SetVolume(pow(10, m_Volume / 2000.0)); DISPLAY_TEXT_SHORT(strprintf("Volume: %.2f dB", m_Volume / 100.0f)); printf("Current Volume: %.2fdB\n", m_Volume / 100.0f); break; case KeyConfig::ACTION_INCREASE_VOLUME: m_Volume += 300; m_player_audio.SetVolume(pow(10, m_Volume / 2000.0)); DISPLAY_TEXT_SHORT(strprintf("Volume: %.2f dB", m_Volume / 100.0f)); printf("Current Volume: %.2fdB\n", m_Volume / 100.0f); break; default: break; } } if (idle) { usleep(10000); continue; } if(m_seek_flush || m_incr != 0) { double seek_pos = 0; double pts = 0; if(m_has_subtitle) m_player_subtitles.Pause(); if (!m_chapter_seek) { pts = m_av_clock->OMXMediaTime(); seek_pos = (pts ? pts / DVD_TIME_BASE : last_seek_pos) + m_incr; last_seek_pos = seek_pos; seek_pos *= 1000.0; if(m_omx_reader.SeekTime((int)seek_pos, m_incr < 0.0f, &startpts)) { if (!m_new_win_pos) { unsigned t = (unsigned)(startpts*1e-6); auto dur = m_omx_reader.GetStreamLength() / 1000; DISPLAY_TEXT_LONG(strprintf("Seek\n%02d:%02d:%02d / %02d:%02d:%02d", (t/3600), (t/60)%60, t%60, (dur/3600), (dur/60)%60, dur%60)); printf("Seek to: %02d:%02d:%02d\n", (t/3600), (t/60)%60, t%60); } FlushStreams(startpts); } } sentStarted = false; if (m_omx_reader.IsEof()) goto do_exit; // Depending on what caused the seek either do a quick reset or full re-open // of the video player. When the unhide video action is taken (m_new_win_pos // is true) do a full re-open because the player was already closed. However // for all other seeks (like looping or an explicit seek), do a quick reset // to reduce stuttering. if (!m_new_win_pos) { // Quick reset to reduce delay during loop & seek. if (m_has_video && !m_player_video.Reset()) goto do_exit; } else { // Full re-open because video player was closed. if (m_has_video && !m_player_video.Open(m_hints_video, m_av_clock, DestRect, m_Deinterlace ? VS_DEINTERLACEMODE_FORCE:m_NoDeinterlace ? VS_DEINTERLACEMODE_OFF:VS_DEINTERLACEMODE_AUTO, m_anaglyph, m_hdmi_clock_sync, m_thread_player, m_display_aspect, m_display, m_layer, video_queue_size, video_fifo_size)) goto do_exit; } CLog::Log(LOGDEBUG, "Seeked %.0f %.0f %.0f\n", DVD_MSEC_TO_TIME(seek_pos), startpts, m_av_clock->OMXMediaTime()); m_av_clock->OMXPause(); if(m_has_subtitle) m_player_subtitles.Resume(); m_packet_after_seek = false; m_seek_flush = false; m_new_win_pos= false; m_incr = 0; } else if(m_packet_after_seek && TRICKPLAY(m_av_clock->OMXPlaySpeed())) { double seek_pos = 0; double pts = 0; pts = m_av_clock->OMXMediaTime(); seek_pos = (pts / DVD_TIME_BASE); seek_pos *= 1000.0; if(m_omx_reader.SeekTime((int)seek_pos, m_av_clock->OMXPlaySpeed() < 0, &startpts)) ; //FlushStreams(DVD_NOPTS_VALUE); CLog::Log(LOGDEBUG, "Seeked %.0f %.0f %.0f\n", DVD_MSEC_TO_TIME(seek_pos), startpts, m_av_clock->OMXMediaTime()); //unsigned t = (unsigned)(startpts*1e-6); unsigned t = (unsigned)(pts*1e-6); printf("Seek to: %02d:%02d:%02d\n", (t/3600), (t/60)%60, t%60); m_packet_after_seek = false; } /* player got in an error state */ if(m_player_audio.Error()) { printf("audio player error. emergency exit!!!\n"); goto do_exit; } if (update) { /* when the video/audio fifos are low, we pause clock, when high we resume */ double stamp = m_av_clock->OMXMediaTime(); double audio_pts = m_player_audio.GetCurrentPTS(); double video_pts = m_player_video.GetCurrentPTS(); if (0 && m_av_clock->OMXIsPaused()) { double old_stamp = stamp; if (audio_pts != DVD_NOPTS_VALUE && (stamp == 0 || audio_pts < stamp)) stamp = audio_pts; if (video_pts != DVD_NOPTS_VALUE && (stamp == 0 || video_pts < stamp)) stamp = video_pts; if (old_stamp != stamp) { m_av_clock->OMXMediaTime(stamp); stamp = m_av_clock->OMXMediaTime(); } } float audio_fifo = audio_pts == DVD_NOPTS_VALUE ? 0.0f : audio_pts / DVD_TIME_BASE - stamp * 1e-6; float video_fifo = video_pts == DVD_NOPTS_VALUE ? 0.0f : video_pts / DVD_TIME_BASE - stamp * 1e-6; float threshold = std::min(0.1f, (float)m_player_audio.GetCacheTotal() * 0.1f); bool audio_fifo_low = false, video_fifo_low = false, audio_fifo_high = false, video_fifo_high = false; if(m_stats) { static int count; if ((count++ & 7) == 0) printf("M:%8.0f V:%6.2fs %6dk/%6dk A:%6.2f %6.02fs/%6.02fs Cv:%6dk Ca:%6dk \r", stamp, video_fifo, (m_player_video.GetDecoderBufferSize()-m_player_video.GetDecoderFreeSpace())>>10, m_player_video.GetDecoderBufferSize()>>10, audio_fifo, m_player_audio.GetDelay(), m_player_audio.GetCacheTotal(), m_player_video.GetCached()>>10, m_player_audio.GetCached()>>10); } if(m_tv_show_info) { static unsigned count; if ((count++ & 7) == 0) { char response[80]; if (m_player_video.GetDecoderBufferSize() && m_player_audio.GetCacheTotal()) vc_gencmd(response, sizeof response, "render_bar 4 video_fifo %d %d %d %d", (int)(100.0*m_player_video.GetDecoderBufferSize()-m_player_video.GetDecoderFreeSpace())/m_player_video.GetDecoderBufferSize(), (int)(100.0*video_fifo/m_player_audio.GetCacheTotal()), 0, 100); if (m_player_audio.GetCacheTotal()) vc_gencmd(response, sizeof response, "render_bar 5 audio_fifo %d %d %d %d", (int)(100.0*audio_fifo/m_player_audio.GetCacheTotal()), (int)(100.0*m_player_audio.GetDelay()/m_player_audio.GetCacheTotal()), 0, 100); vc_gencmd(response, sizeof response, "render_bar 6 video_queue %d %d %d %d", m_player_video.GetLevel(), 0, 0, 100); vc_gencmd(response, sizeof response, "render_bar 7 audio_queue %d %d %d %d", m_player_audio.GetLevel(), 0, 0, 100); } } if (audio_pts != DVD_NOPTS_VALUE) { audio_fifo_low = m_has_audio && audio_fifo < threshold; audio_fifo_high = !m_has_audio || (audio_pts != DVD_NOPTS_VALUE && audio_fifo > m_threshold); } if (video_pts != DVD_NOPTS_VALUE) { video_fifo_low = m_has_video && video_fifo < threshold; video_fifo_high = !m_has_video || (video_pts != DVD_NOPTS_VALUE && video_fifo > m_threshold); } CLog::Log(LOGDEBUG, "Normal M:%.0f (A:%.0f V:%.0f) P:%d A:%.2f V:%.2f/T:%.2f (%d,%d,%d,%d) A:%d%% V:%d%% (%.2f,%.2f)\n", stamp, audio_pts, video_pts, m_av_clock->OMXIsPaused(), audio_pts == DVD_NOPTS_VALUE ? 0.0:audio_fifo, video_pts == DVD_NOPTS_VALUE ? 0.0:video_fifo, m_threshold, audio_fifo_low, video_fifo_low, audio_fifo_high, video_fifo_high, m_player_audio.GetLevel(), m_player_video.GetLevel(), m_player_audio.GetDelay(), (float)m_player_audio.GetCacheTotal()); // keep latency under control by adjusting clock (and so resampling audio) if (m_live) { float latency = DVD_NOPTS_VALUE; if (m_has_audio && audio_pts != DVD_NOPTS_VALUE) latency = audio_fifo; else if (!m_has_audio && m_has_video && video_pts != DVD_NOPTS_VALUE) latency = video_fifo; if (!m_Pause && latency != DVD_NOPTS_VALUE) { if (m_av_clock->OMXIsPaused()) { if (latency > m_threshold) { CLog::Log(LOGDEBUG, "Resume %.2f,%.2f (%d,%d,%d,%d) EOF:%d PKT:%p\n", audio_fifo, video_fifo, audio_fifo_low, video_fifo_low, audio_fifo_high, video_fifo_high, m_omx_reader.IsEof(), m_omx_pkt); m_av_clock->OMXResume(); m_latency = latency; } } else { m_latency = m_latency*0.99f + latency*0.01f; float speed = 1.0f; if (m_latency < 0.5f*m_threshold) speed = 0.990f; else if (m_latency < 0.9f*m_threshold) speed = 0.999f; else if (m_latency > 2.0f*m_threshold) speed = 1.010f; else if (m_latency > 1.1f*m_threshold) speed = 1.001f; m_av_clock->OMXSetSpeed(S(speed)); m_av_clock->OMXSetSpeed(S(speed), true, true); CLog::Log(LOGDEBUG, "Live: %.2f (%.2f) S:%.3f T:%.2f\n", m_latency, latency, speed, m_threshold); } } } else if(!m_Pause && (m_omx_reader.IsEof() || m_omx_pkt || TRICKPLAY(m_av_clock->OMXPlaySpeed()) || (audio_fifo_high && video_fifo_high))) { if (m_av_clock->OMXIsPaused()) { CLog::Log(LOGDEBUG, "Resume %.2f,%.2f (%d,%d,%d,%d) EOF:%d PKT:%p\n", audio_fifo, video_fifo, audio_fifo_low, video_fifo_low, audio_fifo_high, video_fifo_high, m_omx_reader.IsEof(), m_omx_pkt); m_av_clock->OMXResume(); } } else if (m_Pause || audio_fifo_low || video_fifo_low) { if (!m_av_clock->OMXIsPaused()) { if (!m_Pause) m_threshold = std::min(2.0f*m_threshold, 16.0f); CLog::Log(LOGDEBUG, "Pause %.2f,%.2f (%d,%d,%d,%d) %.2f\n", audio_fifo, video_fifo, audio_fifo_low, video_fifo_low, audio_fifo_high, video_fifo_high, m_threshold); m_av_clock->OMXPause(); } } } if (!sentStarted) { CLog::Log(LOGDEBUG, "COMXPlayer::HandleMessages - player started RESET"); m_av_clock->OMXReset(m_has_video, m_has_audio); sentStarted = true; } if(!m_omx_pkt) m_omx_pkt = m_omx_reader.Read(); if(m_omx_pkt) m_send_eos = false; if(m_omx_reader.IsEof() && !m_omx_pkt) { // demuxer EOF, but may have not played out data yet if ( (m_has_video && m_player_video.GetCached()) || (m_has_audio && m_player_audio.GetCached()) ) { OMXClock::OMXSleep(10); continue; } if (m_loop) { m_incr = m_loop_from - (m_av_clock->OMXMediaTime() ? m_av_clock->OMXMediaTime() / DVD_TIME_BASE : last_seek_pos); continue; } if (!m_send_eos && m_has_video) m_player_video.SubmitEOS(); if (!m_send_eos && m_has_audio) m_player_audio.SubmitEOS(); m_send_eos = true; if ( (m_has_video && !m_player_video.IsEOS()) || (m_has_audio && !m_player_audio.IsEOS()) ) { OMXClock::OMXSleep(10); continue; } break; } if(m_has_video && m_omx_pkt && m_omx_reader.IsActive(OMXSTREAM_VIDEO, m_omx_pkt->stream_index)) { if (TRICKPLAY(m_av_clock->OMXPlaySpeed())) { m_packet_after_seek = true; } if(m_player_video.AddPacket(m_omx_pkt)) m_omx_pkt = NULL; else OMXClock::OMXSleep(10); } else if(m_has_audio && m_omx_pkt && !TRICKPLAY(m_av_clock->OMXPlaySpeed()) && m_omx_pkt->codec_type == AVMEDIA_TYPE_AUDIO) { if(m_player_audio.AddPacket(m_omx_pkt)) m_omx_pkt = NULL; else OMXClock::OMXSleep(10); } else if(m_has_subtitle && m_omx_pkt && !TRICKPLAY(m_av_clock->OMXPlaySpeed()) && m_omx_pkt->codec_type == AVMEDIA_TYPE_SUBTITLE) { auto result = m_player_subtitles.AddPacket(m_omx_pkt, m_omx_reader.GetRelativeIndex(m_omx_pkt->stream_index)); if (result) m_omx_pkt = NULL; else OMXClock::OMXSleep(10); } else { if(m_omx_pkt) { m_omx_reader.FreePacket(m_omx_pkt); m_omx_pkt = NULL; } else OMXClock::OMXSleep(10); } } do_exit: if (m_stats) printf("\n"); if (m_stop) { unsigned t = (unsigned)(m_av_clock->OMXMediaTime()*1e-6); printf("Stopped at: %02d:%02d:%02d\n", (t/3600), (t/60)%60, t%60); } if (m_NativeDeinterlace) { char response[80]; vc_gencmd(response, sizeof response, "hvs_update_fields %d", 0); } if(m_has_video && m_refresh && tv_state.display.hdmi.group && tv_state.display.hdmi.mode) { m_BcmHost.vc_tv_hdmi_power_on_explicit_new(HDMI_MODE_HDMI, (HDMI_RES_GROUP_T)tv_state.display.hdmi.group, tv_state.display.hdmi.mode); } m_av_clock->OMXStop(); m_av_clock->OMXStateIdle(); m_player_subtitles.Close(); m_player_video.Close(); m_player_audio.Close(); if (NULL != m_keyboard) { m_keyboard->Close(); } if(m_omx_pkt) { m_omx_reader.FreePacket(m_omx_pkt); m_omx_pkt = NULL; } m_omx_reader.Close(); m_av_clock->OMXDeinitialize(); if (m_av_clock) delete m_av_clock; vc_tv_show_info(0); g_OMX.Deinitialize(); g_RBP.Deinitialize(); printf("have a nice day ;)\n"); return 1; }