omxplayer/omxplayer.cpp

1827 lines
61 KiB
C++

/*
*
* 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 <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <stdint.h>
#include <termios.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <sys/ioctl.h>
#include <getopt.h>
#include <string.h>
#define AV_NOWARN_DEPRECATED
extern "C" {
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
};
#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 <string>
#include <utility>
#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; i<num_modes; i++)
{
TV_SUPPORTED_MODE_NEW_T *tv = supported_modes + i;
uint32_t score = 0;
uint32_t w = tv->width;
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<int,int> 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<sizeof layouts/sizeof *layouts; i++)
if (strcmp(optarg, layouts[i]) == 0)
{
m_layout = (enum PCMLayout)i;
break;
}
if (i == sizeof layouts/sizeof *layouts)
{
print_usage();
return 0;
}
break;
}
case dbus_name_opt:
m_dbus_name = optarg;
break;
case loop_opt:
if(m_incr != 0)
m_loop_from = m_incr;
m_loop = true;
break;
case 'b':
m_blank_background = true;
break;
case key_config_opt:
keymap = KeyConfig::parseConfigFile(optarg);
break;
case layer_opt:
m_layer = atoi(optarg);
break;
case display_opt:
m_display = atoi(optarg);
break;
case http_cookie_opt:
m_cookie = optarg;
break;
case http_user_agent_opt:
m_user_agent = optarg;
break;
case 0:
break;
case 'h':
print_usage();
return 0;
break;
case 'v':
print_version();
return 0;
break;
case 'k':
print_keybindings();
return 0;
break;
case ':':
return 0;
break;
default:
return 0;
break;
}
}
if (optind >= 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(&current_tv_state, 0, sizeof(TV_DISPLAY_STATE_T));
m_BcmHost.vc_tv_get_display_state(&current_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<Subtitle> 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;
}