sigmaplayer/src/player.cpp

932 lines
19 KiB
C++

//////////////////////////////////////////////////////////////////////////
/**
* SigmaPlayer source project - File player source file.
* \file player.cpp
* \author bombur
* \version 0.1
* \date 22.10.2006
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
*/
//////////////////////////////////////////////////////////////////////////
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <pty.h>
#include <termios.h>
#include <sys/poll.h>
#include <sys/wait.h>
#include <libsp/sp_misc.h>
#include <libsp/sp_msg.h>
#include <libsp/sp_fip.h>
#include <libsp/sp_khwl.h>
#include <libsp/sp_mpeg.h>
#include <libsp/sp_cdrom.h>
#include "script.h"
#include "player.h"
#ifdef WIN32
#include "info/player_info.h"
#endif
#ifdef EXTERNAL_PLAYER
#define MSG if (player_msg) msg
static BOOL player_msg = TRUE;
#endif
#ifdef FILEPLAYER_DEBUG
//#define FILEPLAYER_DEBUG
//#define DEBUG_MSG msg
#define DEBUG_MSG printf
static int player_debug_num_packets = 0;
//extern "C"
//{
void player_printpacket(MpegPacket *packet)
{
DEBUG_MSG("[%d] t=%d fl=%ld\n", player_debug_num_packets++, packet->type, packet->flags);
DEBUG_MSG("SIZE=%ld\n", packet->size);
if (packet->pData != NULL)
{
int s = (packet->size + 7) / 8;
for (int i = 0; i < s; i += 8)
{
DEBUG_MSG("DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n",
packet->pData[i+0], packet->pData[i+1], packet->pData[i+2], packet->pData[i+3],
packet->pData[i+4], packet->pData[i+5], packet->pData[i+6], packet->pData[i+7]);
}
}
DEBUG_MSG("pts=%u+%u dts=%u scr=%u+%u vobu=%u+%u\n",
(unsigned)(packet->pts >> 32), (unsigned)packet->pts,
(unsigned)packet->dts,
(unsigned)(packet->scr >> 32), (unsigned)packet->scr,
(unsigned)(packet->vobu_sptm >> 32), (unsigned)packet->vobu_sptm);
DEBUG_MSG("ei=%d nfh=%d faup=%d at=%d r=%d,%d,%d\n",
(int)packet->encryptedinfo, (int)packet->nframeheaders, (int)packet->firstaccessunitpointer,
(int)packet->AudioType,
(int)packet->reserved[0], (int)packet->reserved[1], (int)packet->reserved[2]);
DEBUG_MSG("\n\n");
fflush(stdout);
}
/*
void player_feed(MpegPlayStruct *feed)
{
MpegPlayStruct *from = MPEG_PLAY_STRUCT;
if (from->in != NULL)
player_printpacket(from->in);
mpeg_feed(feed);
}
//}
int player_nextfeedpacket(MpegPlayStruct *feed)
{
MpegPlayStruct *from = MPEG_PLAY_STRUCT;
MpegPacket *feedin = feed->in;
if (feedin == NULL)
return FALSE;
player_printpacket(feedin);
feed->in = feed->in->next;
feed->num--;
feed->in_cnt++;
from->num++;
from->out_cnt++;
if (feedin->next == NULL)
feed->out = NULL;
if (from->out == NULL)
from->in = feedin;
else
from->out->next = feedin;
from->out = feedin;
feedin->next = NULL;
////// simulate budidx decrement
if (feedin->bufidx)
{
if (*(feedin->bufidx) > 0)
(*feedin->bufidx)--;
}
return TRUE;
}
void player_debug_packets()
{
for (;;)
{
//khwl_blockirq(TRUE);
while (MPEG_VIDEO_STRUCT->in != NULL)
{
player_nextfeedpacket(MPEG_VIDEO_STRUCT);
}
while (MPEG_AUDIO_STRUCT->in != NULL)
{
player_nextfeedpacket(MPEG_AUDIO_STRUCT);
}
while (MPEG_SPU_STRUCT->in != NULL)
{
player_nextfeedpacket(MPEG_SPU_STRUCT);
}
//khwl_blockirq(FALSE);
usleep(1000);
}
}
*/
#endif
///////////////////////////////////////////////////
class PlayerTerm
{
public:
/// ctor
PlayerTerm()
{
master_handle = -1;
slave_handle = -1;
old_stdin = -1;
old_stdout = -1;
pts_id = -1;
}
/// Create and open terminal
int Open();
/// Close terminal
void Close();
/// Return true if terminal is opened
bool IsOpened();
/// Set terminal to current stdin/stdout
void Set();
/// Restore default stdin/stdout
void Restore();
/// Read data from the terminal (non-blocked).
/// Returns number of bytes read.
int Read(char *data, int max_data_size);
/// Write zero-terminated string to the terminal.
void Write(const char *str);
public:
int master_handle, slave_handle;
int old_stdin, old_stdout;
int pts_id;
};
int PlayerTerm::Open()
{
if (slave_handle >= 0)
Restore();
if (master_handle >= 0)
Close();
char pts_str[10];
pts_id = openpty (&master_handle, &slave_handle, pts_str, NULL, NULL);
if (pts_id == -1)
{
msg_error("Player: Cannot open pty.\n");
return -1;
}
struct termios tattr;
if (tcgetattr (master_handle, &tattr) == -1)
{
msg_error("Player: pty getattr failed.\n");
return -1;
}
tattr.c_oflag &= ~ONLCR;
tattr.c_lflag &= ~ECHO;
tcsetattr(master_handle, TCSANOW, &tattr);
int flgs = fcntl(master_handle, F_GETFL, 0);
fcntl(master_handle, F_SETFL, flgs | O_NONBLOCK);
msg("Player: * pty #%d (%s) opened.\n", pts_id, pts_str);
return 0;
}
void PlayerTerm::Close()
{
if (master_handle >= 0)
{
close(master_handle);
master_handle = -1;
msg("Player: * pty #%d closed.\n", pts_id);
pts_id = -1;
}
}
void PlayerTerm::Set()
{
old_stdin = dup(STDIN_FILENO);
old_stdout = dup(STDOUT_FILENO);
close(STDIN_FILENO);
close(STDOUT_FILENO);
dup2(slave_handle, STDIN_FILENO);
dup2(slave_handle, STDOUT_FILENO);
}
void PlayerTerm::Restore()
{
dup2(old_stdin, STDIN_FILENO);
dup2(old_stdout, STDOUT_FILENO);
close(old_stdin);
close(old_stdout);
close(slave_handle);
old_stdin = -1;
old_stdout = -1;
slave_handle = -1;
}
bool PlayerTerm::IsOpened()
{
return master_handle >= 0;
}
void PlayerTerm::Write(const char *str)
{
if (master_handle >= 0)
write(master_handle, str, strlen(str) + 1);
}
int PlayerTerm::Read(char *data, int max_data_size)
{
struct pollfd pfd;
pfd.fd = master_handle;
pfd.events = POLLIN;
pfd.revents = 0;
if (poll(&pfd, 1, 100) <= 0)
return 0;
if (pfd.revents & POLLIN)
{
return read(master_handle, data, max_data_size);
}
return 0;
}
/////////////////////////////////////////////////////
static PlayerTerm id3term;
static void player_parseid3(char *data, int data_size);
static void stopid3();
enum PLAYER_READID3
{
PLAYER_READID3_NONE = 0,
PLAYER_READID3_TITLE,
PLAYER_READID3_ARTIST,
PLAYER_READID3_WIDTH,
PLAYER_READID3_HEIGHT,
PLAYER_READID3_CLRS,
};
static PLAYER_READID3 player_reading_id3 = PLAYER_READID3_NONE;
const int id3_max_data_size = 256;
static char id3_data[id3_max_data_size + 1];
static int id3id = -1;
inline void to_enter(char* &data, int &data_size)
{
char *end = strchr(data, '\n');
if (end != NULL)
{
*end = '\0';
data_size -= (end - data + 1);
data = end + 1;
} else
data_size = 0;
}
// called when child player process ends
static void player_sig_handler(int sig, siginfo_t *info, void *)
{
if (sig != SIGCHLD)
return;
msg("Player: * SIG handler (%d)!\n", info->si_pid);
int pstat;
waitpid(info->si_pid, &pstat, 0);
#ifdef EXTERNAL_PLAYER
if (info->si_pid == playerid)
stopfile();
#endif
if (info->si_pid == id3id)
stopid3();
}
void set_sig(void (*siga) (int, siginfo_t *, void *))
{
struct sigaction sig;
memset(&sig, 0, sizeof(sig));
if (siga != NULL)
sig.sa_flags = SA_SIGINFO;
sig.sa_sigaction = siga;
sigaction(SIGCHLD, &sig, NULL);
}
/////////////////////////////////////////////////////////////////////
#ifdef EXTERNAL_PLAYER
static PlayerTerm pterm;
const int player_max_data_size = 1024;
static char player_data[player_max_data_size + 1];
static char player_type[10];
static char vmodebuf[10];
static bool player_paused = false;
enum PLAYER_READINFO
{
PLAYER_READINFO_NONE = 0,
PLAYER_READINFO_TIME = 1,
PLAYER_READINFO_TITLE,
PLAYER_READINFO_ARTIST,
PLAYER_READINFO_VIDEOINFO,
PLAYER_READINFO_AUDIOINFO,
};
static PLAYER_READINFO player_reading_info = PLAYER_READINFO_NONE;
static int playerid = -1;
static BOOL playing = FALSE, video = FALSE, mpegplayer = FALSE;
static int mpegplayer_info_cnt = 0;
static int playfile(const char *filename, const char *type, int vmode);
static BOOL player_command(const char *command);
static void stopfile();
static void stopid3();
static void player_parseinfo(char *data, int data_size);
static void set_sig(void (*siga) (int, siginfo_t *, void *));
int playfile(const char *filename, const char *type, int vmode)
{
char *args1[] = { "/bin/fileplayer.bin", (char *)type, (char *)filename, (char *)vmodebuf, NULL };
char *args2[] = { "/bin/mpegplayer.bin", (char *)filename, (char *)vmodebuf, NULL };
char **args = (char **)args1;
video = FALSE;
mpegplayer = FALSE;
mpegplayer_info_cnt = 0;
sprintf(vmodebuf, "%d", vmode);
if (type != NULL)
{
if ((type[0] == 'M' && type[1] == 'P' && type[2] == 'G') ||
(type[0] == 'M' && type[1] == 'P' && type[2] == 'E' && type[3] == 'G') ||
(type[0] == 'V' && type[1] == 'O' && type[2] == 'B') ||
(type[0] == 'D' && type[1] == 'A' && type[2] == 'T')
)
{
args = (char **)args2;
video = TRUE;
mpegplayer = TRUE;
}
else
{
if ((type[0] == 'A' && type[1] == 'V' && type[2] == 'I'))
video = TRUE;
}
}
if (playerid != -1)
player_stop();
pterm.Open();
set_sig(player_sig_handler);
pterm.Set();
playerid = exec_file(args[0], (const char **)args);
pterm.Restore();
MSG("Player: * Started (%d)...\n", playerid);
playing = TRUE;
return 0;
}
void stopfile()
{
if (playerid < 0)
return;
fip_clear();
// we'll have to wait for the next player_loop()
playerid = -1;
playing = FALSE;
video = FALSE;
mpegplayer = FALSE;
MSG("Player: * Stopped.\n");
}
BOOL player_command(const char *command)
{
if (playerid < 0 || command == NULL)
return FALSE;
MSG("Player: *COMMAND = %s\n", command);
pterm.Write(command);
return TRUE;
}
///////////////////////////////////////////////////////////
int player_play(char *filepath)
{
#ifdef FILEPLAYER_DEBUG
player_debug_num_packets = 0;
/*
DWORD *ptr = (DWORD *)0x167FFE0;
*(ptr) = (DWORD)&player_printpacket;
*/
#endif
if (filepath == NULL) // resume playing
{
player_command("P\n");
return 0;
}
// start player
char *ext = strrchr(filepath, '.');
if (ext != NULL)
{
ext = ext + 1;
strncpy(player_type, ext, 10);
strupr(player_type);
int vmode = 0;
// TODO: set real vmode
player_paused = false;
return playfile(filepath, player_type, vmode);
}
// unknown file type...
return -1;
}
int player_info_loop()
{
#ifdef FILEPLAYER_DEBUG
// player_debug_packets();
#endif
if (pterm.IsOpened())
{
int player_data_size = pterm.Read(player_data, player_max_data_size);
//strcpy(player_data, "Info\n00:00:24\nN/A\nN/A\nN/A\nN/A\n");
//int player_data_size = strlen(player_data);
// parse data from player:
if (player_data_size > 0)
{
player_data[player_data_size] = '\0';
//MSG("data[%d]: %s\n", player_data_size, player_data);
if (!mpegplayer || mpegplayer_info_cnt++ < 10)
player_parseinfo(player_data, player_data_size);
}
}
// playing stopped, so clean-up
if (playerid < 0)
{
if (pterm.IsOpened())
{
pterm.Close();
player_reading_info = PLAYER_READINFO_NONE;
}
return 1;
}
return 0;
}
void player_parseinfo(char *data, int data_size)
{
while (data_size > 0)
{
if (player_reading_info == PLAYER_READINFO_TIME)
{
if (data_size > 7)
{
int timelen = 0;
if (isdigit(data[0]))
{
timelen = ((data[0] - '0') * 10 + (data[1] - '0')) * 3600;
timelen += ((data[3] - '0') * 10 + (data[4] - '0')) * 60;
timelen += ((data[6] - '0') * 10 + (data[7] - '0'));
}
script_totaltime_callback(timelen);
MSG("* TOTAL TIME: %d secs\n", timelen);
data += 8;
data_size -= 8;
player_reading_info = PLAYER_READINFO_TITLE;
to_enter(data, data_size);
continue;
}
data++;
data_size--;
}
else if (player_reading_info == PLAYER_READINFO_TITLE)
{
char *titl = data;
to_enter(data, data_size);
MSG("* TITLE: %s.\n", titl);
player_reading_info = PLAYER_READINFO_ARTIST;
continue;
}
else if (player_reading_info == PLAYER_READINFO_ARTIST)
{
char *artist = data;
to_enter(data, data_size);
MSG("* ARTIST: %s.\n", artist);
player_reading_info = PLAYER_READINFO_AUDIOINFO;
continue;
}
else if (player_reading_info == PLAYER_READINFO_AUDIOINFO)
{
char *ai = data;
to_enter(data, data_size);
script_audio_info_callback(ai);
MSG("* AUDIO: %s.\n", ai);
player_reading_info = PLAYER_READINFO_VIDEOINFO;
continue;
}
else if (player_reading_info == PLAYER_READINFO_VIDEOINFO)
{
char *vi = data;
to_enter(data, data_size);
script_video_info_callback(vi);
MSG("* VIDEO: %s.\n", vi);
player_reading_info = PLAYER_READINFO_NONE;
continue;
}
else
{
DWORD cmd = SafeGetDword(data);
if (cmd == 0x656d6954 && data_size >= 7) // "Time"
{
if (data[4] <= 99 && data[5] <= 59 && data[6] <= 59)
{
int tim = data[4] * 3600 + data[5] * 60 + data[6];
script_time_callback(tim);
MSG("PLAYER TIME = %d\n", tim);
}
data += 7;
data_size -= 7;
player_reading_info = PLAYER_READINFO_NONE;
}
else if (cmd == 0x6f666e49 && data_size > 4
&& data[4] == '\n') // "Info"
{
player_reading_info = PLAYER_READINFO_TIME;
data += 5;
data_size -= 5;
}
else if (cmd == 0x79616c50 && data_size >= 4) // "Play"
{
data += 4;
data_size -= 4;
player_reading_info = PLAYER_READINFO_NONE;
player_paused = false;
}
else if (cmd == 0x74696157 && data_size >= 4) // "Wait"
{
data += 4;
data_size -= 4;
script_error_callback(SCRIPT_ERROR_WAIT);
player_reading_info = PLAYER_READINFO_NONE;
}
else if (cmd == 0x41646142 && data_size >= 4) // "BadA"
{
data += 4;
data_size -= 4;
script_error_callback(SCRIPT_ERROR_BAD_AUDIO);
player_reading_info = PLAYER_READINFO_NONE;
}
else if (cmd == 0x43646142 && data_size >= 4) // "BadC"
{
data += 4;
data_size -= 4;
script_error_callback(SCRIPT_ERROR_BAD_CODEC);
player_reading_info = PLAYER_READINFO_NONE;
}
else
{
data++;
data_size--;
player_reading_info = PLAYER_READINFO_NONE;
}
}
}
}
BOOL player_pause()
{
player_paused = !player_paused;
player_command("P\n");
return TRUE;
}
BOOL player_stop()
{
int pstat;
if (playerid < 0)
return FALSE;
MSG("Player: * Stopping...\n");
// first, we ask player to stop...
player_command("X\n");
// sleep and wait for signals...
sleep(1);
usleep(200000);
// if it's still alive...
if (waitpid(playerid, &pstat, WNOHANG) == 0)
{
MSG("Player: * Stopping by force (%d)!\n", playerid);
khwl_stop();
// kill it by force
kill(playerid, SIGKILL);
int pstat;
waitpid(playerid, &pstat, 0);
khwl_stop();
// clear video garbage
if (video)
khwl_restoreparams();
}
stopfile();
return TRUE;
}
BOOL player_seek(int seconds)
{
if (player_paused)
{
player_command("P\n");
player_paused = false;
}
char seekcmd[10];
sprintf(seekcmd, "s%06d\n", seconds);
player_command(seekcmd);
return TRUE;
}
BOOL player_zoom_hor(int /*scale*/)
{
//player_command("zw15\n");
//zl15
//zr15
//zu15
//zd15
//zw15
//zw-15
//zh15
//zh-15
//zF
//zR reset zoom
//zC center scroll
return FALSE;
}
BOOL player_zoom_ver(int /*scale*/)
{
return FALSE;
}
BOOL player_scroll(int /*offx*/, int /*offy*/)
{
return FALSE;
}
int player_forward()
{
player_command("t1\n");
if (video && !mpegplayer)
return 1;
return 0;
}
int player_rewind()
{
player_command("t2\n");
if (video && !mpegplayer)
return 1;
return 0;
}
#endif
//////////////////////////////////////////////////////////////////////
// INFO
void stopid3()
{
if (id3id < 0)
return;
id3id = -1;
msg("Player: * ID3 Stopped.\n");
}
int player_id3_loop()
{
if (id3term.IsOpened())
{
int id3_data_size = id3term.Read(id3_data, id3_max_data_size);
//strcpy(id3_data, "Titl\nTitle\nAuth\nArtist\n");
//strcpy(id3_data, "Dims\n1000\n500\nClrs\n3\n");
//int id3_data_size = strlen(id3_data);
// parse data from fileinfo:
if (id3_data_size > 0)
{
id3_data[id3_data_size] = '\0';
//MSG("ID3DATA (%d): [%s]\n", id3_data_size, id3_data);
player_parseid3(id3_data, id3_data_size);
}
}
// id3 info process stopped, so clean-up
if (id3id < 0)
{
if (id3term.IsOpened())
{
id3term.Close();
player_reading_id3 = PLAYER_READID3_NONE;
}
return 1;
}
return 0;
}
void player_parseid3(char *data, int data_size)
{
static int width = 0;
while (data_size > 0)
{
if (player_reading_id3 == PLAYER_READID3_TITLE)
{
char *titl = data;
to_enter(data, data_size);
script_name_callback(titl);
player_reading_id3 = PLAYER_READID3_NONE;
continue;
}
else if (player_reading_id3 == PLAYER_READID3_ARTIST)
{
char *auth = data;
to_enter(data, data_size);
script_artist_callback(auth);
player_reading_id3 = PLAYER_READID3_NONE;
continue;
}
else if (player_reading_id3 == PLAYER_READID3_WIDTH)
{
char *w = data;
to_enter(data, data_size);
width = atoi(w);
player_reading_id3 = PLAYER_READID3_HEIGHT;
continue;
}
else if (player_reading_id3 == PLAYER_READID3_HEIGHT)
{
char *height = data;
to_enter(data, data_size);
script_framesize_callback(width, atoi(height));
player_reading_id3 = PLAYER_READID3_NONE;
continue;
}
else if (player_reading_id3 == PLAYER_READID3_CLRS)
{
char *clrs = data;
to_enter(data, data_size);
script_colorspace_callback(atoi(clrs));
player_reading_id3 = PLAYER_READID3_NONE;
continue;
}
else
{
DWORD cmd = SafeGetDword(data);
if (cmd == 0x6c746954 && data_size >= 5) // "Titl"
{
data += 5;
data_size -= 5;
player_reading_id3 = PLAYER_READID3_TITLE;
continue;
}
else if (cmd == 0x74737441 && data_size >= 5) // "Atst"
{
data += 5;
data_size -= 5;
player_reading_id3 = PLAYER_READID3_ARTIST;
continue;
}
else if (cmd == 0x736d6944 && data_size >= 5) // "Dims"
{
data += 5;
data_size -= 5;
width = 0;
player_reading_id3 = PLAYER_READID3_WIDTH;
continue;
}
else if (cmd == 0x73726c43 && data_size >= 5) // "Clrs"
{
data += 5;
data_size -= 5;
player_reading_id3 = PLAYER_READID3_CLRS;
continue;
}
else if (cmd == 0x72727245 && data_size >= 5) // "Errr"
{
data += 5;
data_size -= 5;
script_name_callback("");
script_artist_callback("");
//script_error_callback();
player_reading_id3 = PLAYER_READID3_NONE;
continue;
}
else if (cmd == 0x656e6f4e && data_size >= 5) // "None"
{
data += 5;
data_size -= 5;
script_name_callback("");
script_artist_callback("");
player_reading_id3 = PLAYER_READID3_NONE;
continue;
}
else
data_size = 0;
}
}
}
void player_startinfo(const char *fname, const char *charset)
{
#ifdef PLAYER_INFO_EMBED
player_getinfo(fname, charset);
#else
const char *args[] = { "/bin/fileinfo.bin", fname, charset, NULL };
if (id3id >= 0)
{
kill(id3id, SIGKILL);
int pstat;
waitpid(id3id, &pstat, 0);
id3id = -1;
}
id3term.Open();
set_sig(player_sig_handler);
id3term.Set();
id3id = exec_file(args[0], (const char **)args);
id3term.Restore();
#endif
}
#ifdef EXTERNAL_PLAYER
void player_setdebug(BOOL ison)
{
player_msg = ison == TRUE;
}
BOOL player_getdebug()
{
return player_msg;
}
#endif