mirror of
https://github.com/tuxbox-fork-migrations/recycled-ni-libstb-hal.git
synced 2025-08-26 15:02:43 +02:00
Origin commit data
------------------
Branch: master
Commit: 0737aa3ba6
Author: vanhofen <vanhofen@gmx.de>
Date: 2024-03-25 (Mon, 25 Mar 2024)
Origin message was:
------------------
- addition to "small fix"
------------------
No further description and justification available within origin commit message!
------------------
This commit was generated by Migit
1037 lines
28 KiB
C
1037 lines
28 KiB
C
/*
|
|
* eplayer3: command line playback using libeplayer3
|
|
*
|
|
* 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-1307 USA
|
|
*
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sched.h>
|
|
#include <signal.h>
|
|
#include <inttypes.h>
|
|
#include <stdarg.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/prctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/time.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <errno.h>
|
|
|
|
#include <pthread.h>
|
|
|
|
#include "common.h"
|
|
#include "misc.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#define DUMP_BOOL(x) x == 0 ? "false" : "true"
|
|
#define IPTV_MAX_FILE_PATH 1024
|
|
|
|
extern int ffmpeg_av_dict_set(const char *key, const char *value, int flags);
|
|
extern void aac_software_decoder_set(const int32_t val);
|
|
extern void aac_latm_software_decoder_set(const int32_t val);
|
|
extern void dts_software_decoder_set(const int32_t val);
|
|
extern void wma_software_decoder_set(const int32_t val);
|
|
extern void ac3_software_decoder_set(const int32_t val);
|
|
extern void eac3_software_decoder_set(const int32_t val);
|
|
extern void mp3_software_decoder_set(const int32_t val);
|
|
extern void amr_software_decoder_set(const int32_t val);
|
|
extern void vorbis_software_decoder_set(const int32_t val);
|
|
extern void opus_software_decoder_set(const int32_t val);
|
|
|
|
extern void rtmp_proto_impl_set(const int32_t val);
|
|
extern void flv2mpeg4_converter_set(const int32_t val);
|
|
extern void sel_program_id_set(const int32_t val);
|
|
|
|
extern void pcm_resampling_set(int32_t val);
|
|
extern void stereo_software_decoder_set(int32_t val);
|
|
extern void insert_pcm_as_lpcm_set(int32_t val);
|
|
extern void progressive_playback_set(int32_t val);
|
|
|
|
extern OutputHandler_t OutputHandler;
|
|
extern PlaybackHandler_t PlaybackHandler;
|
|
extern ContainerHandler_t ContainerHandler;
|
|
extern ManagerHandler_t ManagerHandler;
|
|
|
|
static Context_t *g_player = NULL;
|
|
|
|
static void TerminateAllSockets(void)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 1024; ++i)
|
|
{
|
|
if (shutdown(i, SHUT_RDWR) == 0)
|
|
{
|
|
/* yes, I know that this is not good practice and I know what this could cause
|
|
* but in this use case it can be accepted.
|
|
* We must close socket because without closing it recv will return 0 (after shutdown)
|
|
* 0 is not correctly handled by external libraries
|
|
*/
|
|
close(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int g_pfd[2] = {-1, -1}; /* Used to wake terminate thread and kbhit */
|
|
static int isPlaybackStarted = 0;
|
|
static pthread_mutex_t playbackStartMtx;
|
|
|
|
static int32_t g_windows_width = 1280;
|
|
static int32_t g_windows_height = 720;
|
|
static char *g_graphic_sub_path;
|
|
|
|
int32_t GetGraphicWindowWidth()
|
|
{
|
|
return g_windows_width;
|
|
}
|
|
|
|
int32_t GetGraphicWindowHeight()
|
|
{
|
|
return g_windows_height;
|
|
}
|
|
|
|
static void TerminateWakeUp()
|
|
{
|
|
int ret = write(g_pfd[1], "x", 1);
|
|
if (ret != 1)
|
|
{
|
|
printf("TerminateWakeUp write return %d\n", ret);
|
|
}
|
|
}
|
|
|
|
static void *TermThreadFun(void *arg __attribute__((unused)))
|
|
{
|
|
const char *socket_path = "/tmp/.exteplayerterm.socket";
|
|
struct sockaddr_un addr;
|
|
int fd = -1;
|
|
int cl = -1;
|
|
int nfds = 1;
|
|
fd_set readfds;
|
|
|
|
unlink(socket_path);
|
|
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
|
|
{
|
|
perror("TermThreadFun socket error");
|
|
goto finish;
|
|
}
|
|
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sun_family = AF_UNIX;
|
|
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path) - 1);
|
|
|
|
if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
|
|
{
|
|
perror("TermThreadFun bind error");
|
|
goto finish;
|
|
}
|
|
|
|
if (listen(fd, 1) == -1)
|
|
{
|
|
perror("TermThreadFun listen error");
|
|
goto finish;
|
|
}
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_SET(g_pfd[0], &readfds);
|
|
FD_SET(fd, &readfds);
|
|
|
|
nfds = fd > g_pfd[0] ? fd + 1 : g_pfd[0] + 1;
|
|
|
|
while (select(nfds, &readfds, NULL, NULL, NULL) == -1 && errno == EINTR)
|
|
{
|
|
/* Restart if interrupted by signal */
|
|
continue;
|
|
}
|
|
|
|
if (FD_ISSET(fd, &readfds))
|
|
{
|
|
pthread_mutex_lock(&playbackStartMtx);
|
|
PlaybackDieNow(1);
|
|
if (isPlaybackStarted)
|
|
TerminateAllSockets();
|
|
else
|
|
kill(getpid(), SIGINT);
|
|
pthread_mutex_unlock(&playbackStartMtx);
|
|
}
|
|
|
|
finish:
|
|
close(cl);
|
|
close(fd);
|
|
pthread_exit(NULL);
|
|
}
|
|
|
|
static void map_inter_file_path(char *filename)
|
|
{
|
|
if (strncmp(filename, "iptv://", 7) == 0)
|
|
{
|
|
FILE *f = fopen(filename + 7, "r");
|
|
if (NULL != f)
|
|
{
|
|
size_t num = fread(filename, 1, IPTV_MAX_FILE_PATH - 1, f);
|
|
fclose(f);
|
|
if (num > 0 && filename[num - 1] == '\n')
|
|
{
|
|
filename[num - 1] = '\0';
|
|
}
|
|
else
|
|
{
|
|
filename[num] = '\0';
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int kbhit(void)
|
|
{
|
|
struct timeval tv;
|
|
fd_set readfds;
|
|
|
|
tv.tv_sec = 1;
|
|
tv.tv_usec = 0;
|
|
|
|
FD_ZERO(&readfds);
|
|
FD_SET(0, &readfds);
|
|
FD_SET(g_pfd[0], &readfds);
|
|
|
|
if (select(g_pfd[0] + 1, &readfds, NULL, NULL, &tv) == -1)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if (FD_ISSET(0, &readfds))
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void SetBuffering()
|
|
{
|
|
static char buff[2048];
|
|
memset(buff, '\0', sizeof(buff));
|
|
if (setvbuf(stderr, buff, _IOLBF, sizeof(buff)))
|
|
{
|
|
printf("SetBuffering: failed to change the buffer of stderr\n");
|
|
}
|
|
|
|
// make fgets not blocking
|
|
int flags = fcntl(stdin->_fileno, F_GETFL, 0);
|
|
fcntl(stdin->_fileno, F_SETFL, flags | O_NONBLOCK);
|
|
}
|
|
|
|
static void SetNice(int prio)
|
|
{
|
|
#if 0
|
|
setpriority(PRIO_PROCESS, 0, -8);
|
|
|
|
int prio = sched_get_priority_max(SCHED_RR) / 2;
|
|
struct sched_param param =
|
|
{
|
|
.sched_priority = prio
|
|
};
|
|
sched_setscheduler(0, SCHED_RR, ¶m);
|
|
#else
|
|
//int prevPrio = getpriority(PRIO_PROCESS, 0);
|
|
if (setpriority(PRIO_PROCESS, 0, prio) == -1)
|
|
{
|
|
printf("setpriority - failed\n");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int HandleTracks(const Manager_t *ptrManager, const PlaybackCmd_t playbackSwitchCmd, const char *argvBuff)
|
|
{
|
|
int commandRetVal = 0;
|
|
|
|
if (NULL == ptrManager || NULL == argvBuff || 2 != strnlen(argvBuff, 2))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
switch (argvBuff[1])
|
|
{
|
|
case 'l':
|
|
{
|
|
char **TrackList = NULL;
|
|
ptrManager->Command(g_player, MANAGER_LIST, &TrackList);
|
|
if (NULL != TrackList)
|
|
{
|
|
int i = 0;
|
|
int Id = -1;
|
|
char *pch;
|
|
char Name[] = " ";
|
|
E2iStartMsg();
|
|
E2iSendMsg("{\"%c_%c\": [", argvBuff[0], argvBuff[1]);
|
|
for (i = 0; TrackList[i] != NULL; i += 2)
|
|
{
|
|
pch = strtok(TrackList[i], " ");
|
|
if (pch != NULL)
|
|
{
|
|
Id = atoi(pch);
|
|
pch = strtok(NULL, " ");
|
|
if (pch != NULL)
|
|
{
|
|
snprintf(Name, sizeof(Name), "%s", pch);
|
|
}
|
|
}
|
|
if (0 < i)
|
|
{
|
|
E2iSendMsg(", ");
|
|
}
|
|
E2iSendMsg("{\"id\":%d,\"e\":\"%s\",\"n\":\"%s\"}", Id, TrackList[i + 1], Name);
|
|
free(TrackList[i]);
|
|
free(TrackList[i + 1]);
|
|
}
|
|
E2iSendMsg("]}\n");
|
|
E2iEndMsg();
|
|
free(TrackList);
|
|
}
|
|
else
|
|
{
|
|
// not tracks
|
|
E2iSendMsg("{\"%c_%c\": []}\n", argvBuff[0], argvBuff[1]);
|
|
}
|
|
break;
|
|
}
|
|
case 'c':
|
|
{
|
|
TrackDescription_t *track = NULL;
|
|
ptrManager->Command(g_player, MANAGER_GET_TRACK_DESC, &track);
|
|
if (NULL != track)
|
|
{
|
|
if ('a' == argvBuff[0] || 's' == argvBuff[0])
|
|
{
|
|
E2iSendMsg("{\"%c_%c\":{\"id\":%d,\"e\":\"%s\",\"n\":\"%s\"}}\n", argvBuff[0], argvBuff[1], track->Id, track->Encoding, track->Name);
|
|
}
|
|
else // video
|
|
{
|
|
E2iSendMsg("{\"%c_%c\":{\"id\":%d,\"e\":\"%s\",\"n\":\"%s\",\"w\":%d,\"h\":%d,\"f\":%u,\"p\":%d,\"an\":%d,\"ad\":%d}}\n", \
|
|
argvBuff[0], argvBuff[1], track->Id, track->Encoding, track->Name, track->width, track->height, track->frame_rate, track->progressive, track->aspect_ratio_num, track->aspect_ratio_den);
|
|
}
|
|
free(track->Encoding);
|
|
free(track->Name);
|
|
free(track);
|
|
}
|
|
else
|
|
{
|
|
// no tracks
|
|
if ('a' == argvBuff[0] || 's' == argvBuff[0])
|
|
{
|
|
E2iSendMsg("{\"%c_%c\":{\"id\":%d,\"e\":\"%s\",\"n\":\"%s\"}}\n", argvBuff[0], argvBuff[1], -1, "", "");
|
|
}
|
|
else // video
|
|
{
|
|
E2iSendMsg("{\"%c_%c\":{\"id\":%d,\"e\":\"%s\",\"n\":\"%s\",\"w\":%d,\"h\":%d,\"f\":%u,\"p\":%d}}\n", argvBuff[0], argvBuff[1], -1, "", "", -1, -1, 0, -1);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
/* switch command available only for audio and subtitle tracks */
|
|
if ('a' == argvBuff[0] || 's' == argvBuff[0])
|
|
{
|
|
int ok = 0;
|
|
int id = -1;
|
|
if ('i' == argvBuff[1])
|
|
{
|
|
int idx = -1;
|
|
ok = sscanf(argvBuff + 2, "%d", &idx);
|
|
if (idx >= 0)
|
|
{
|
|
char **TrackList = NULL;
|
|
ptrManager->Command(g_player, MANAGER_LIST, &TrackList);
|
|
if (NULL != TrackList)
|
|
{
|
|
int i = 0;
|
|
char *pch;
|
|
for (i = 0; TrackList[i] != NULL; i += 2)
|
|
{
|
|
if (idx == i)
|
|
{
|
|
pch = strtok(TrackList[i], " ");
|
|
if (pch != NULL)
|
|
id = atoi(pch);
|
|
}
|
|
free(TrackList[i]);
|
|
free(TrackList[i + 1]);
|
|
}
|
|
free(TrackList);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
id = idx;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ok = sscanf(argvBuff + 1, "%d", &id);
|
|
}
|
|
|
|
if (id >= 0 || (ok == 1 && id == -1))
|
|
{
|
|
commandRetVal = g_player->playback->Command(g_player, playbackSwitchCmd, (void *)&id);
|
|
E2iSendMsg("{\"%c_%c\":{\"id\":%d,\"sts\":%d}}\n", argvBuff[0], 's', id, commandRetVal);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return commandRetVal;
|
|
}
|
|
|
|
static void UpdateVideoTrack()
|
|
{
|
|
HandleTracks(g_player->manager->video, (PlaybackCmd_t) -1, "vc");
|
|
}
|
|
|
|
static int ParseParams(int argc, char *argv[], PlayFiles_t *playbackFiles, int *pAudioTrackIdx, int *subtitleTrackIdx, uint32_t *linuxDvbBufferSizeMB)
|
|
{
|
|
int ret = 0;
|
|
int c;
|
|
while ((c = getopt(argc, argv, "G:W:H:A:V:U:we3dlsrimva:n:x:u:c:h:o:p:P:t:9:0:1:4:f:b:F:S:O:T:")) != -1)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'G':
|
|
g_graphic_sub_path = optarg;
|
|
break;
|
|
case 'W':
|
|
{
|
|
int val = atoi(optarg);
|
|
if (val)
|
|
g_windows_width = val;
|
|
break;
|
|
}
|
|
case 'H':
|
|
{
|
|
int val = atoi(optarg);
|
|
if (val)
|
|
g_windows_height = val;
|
|
break;
|
|
}
|
|
case 'a':
|
|
{
|
|
int flag = atoi(optarg);
|
|
printf("Software decoder will be used for AAC codec\n");
|
|
aac_software_decoder_set(flag & 0x01);
|
|
aac_latm_software_decoder_set(flag & 0x02);
|
|
break;
|
|
}
|
|
case 'e':
|
|
printf("Software decoder will be used for EAC3 codec\n");
|
|
eac3_software_decoder_set(1);
|
|
break;
|
|
case 'A':
|
|
printf("Software decoder will be used for AMR codec\n");
|
|
amr_software_decoder_set(atoi(optarg));
|
|
break;
|
|
case 'V':
|
|
printf("Software decoder will be used for VORBIS codec\n");
|
|
vorbis_software_decoder_set(atoi(optarg));
|
|
break;
|
|
case 'U':
|
|
printf("Software decoder will be used for OPUS codec\n");
|
|
vorbis_software_decoder_set(atoi(optarg));
|
|
break;
|
|
case '3':
|
|
printf("Software decoder will be used for AC3 codec\n");
|
|
ac3_software_decoder_set(1);
|
|
break;
|
|
case 'd':
|
|
printf("Software decoder will be used for DTS codec\n");
|
|
dts_software_decoder_set(1);
|
|
break;
|
|
case 'm':
|
|
printf("Software decoder will be used for MP3 codec\n");
|
|
mp3_software_decoder_set(1);
|
|
break;
|
|
case 'w':
|
|
printf("Software decoder will be used for WMA codec\n");
|
|
wma_software_decoder_set(1);
|
|
break;
|
|
case 'l':
|
|
printf("Audio software decoding as LPCM\n");
|
|
insert_pcm_as_lpcm_set(1);
|
|
break;
|
|
case 's':
|
|
printf("Software decoder will decode to stereo\n");
|
|
stereo_software_decoder_set(1);
|
|
break;
|
|
case 'r':
|
|
printf("Software decoder do not use PCM resampling\n");
|
|
pcm_resampling_set(0);
|
|
break;
|
|
case 'o':
|
|
printf("Set progressive download to %d\n", atoi(optarg));
|
|
progressive_playback_set(atoi(optarg));
|
|
break;
|
|
case 'p':
|
|
SetNice(atoi(optarg));
|
|
break;
|
|
case 'P':
|
|
sel_program_id_set(atoi(optarg));
|
|
break;
|
|
case 't':
|
|
*pAudioTrackIdx = atoi(optarg);
|
|
break;
|
|
case '9':
|
|
*subtitleTrackIdx = atoi(optarg);
|
|
break;
|
|
case 'x':
|
|
if (optarg[0] != '\0')
|
|
{
|
|
playbackFiles->szSecondFile = malloc(IPTV_MAX_FILE_PATH);
|
|
playbackFiles->szSecondFile[0] = '\0';
|
|
strncpy(playbackFiles->szSecondFile, optarg, IPTV_MAX_FILE_PATH - 1);
|
|
playbackFiles->szSecondFile[IPTV_MAX_FILE_PATH] = '\0';
|
|
map_inter_file_path(playbackFiles->szSecondFile);
|
|
}
|
|
break;
|
|
case 'h':
|
|
ffmpeg_av_dict_set("headers", optarg, 0);
|
|
break;
|
|
case 'u':
|
|
ffmpeg_av_dict_set("user-agent", optarg, 0);
|
|
break;
|
|
case 'c':
|
|
printf("For now cookies should be set via headers option!\n");
|
|
ffmpeg_av_dict_set("cookies", optarg, 0);
|
|
break;
|
|
case 'i':
|
|
printf("Play in (infinity) loop.\n");
|
|
PlaybackHandler.isLoopMode = 1;
|
|
break;
|
|
case 'v':
|
|
printf("Use live TS stream mode.\n");
|
|
PlaybackHandler.isTSLiveMode = 1;
|
|
break;
|
|
case 'n':
|
|
printf("Force rtmp protocol implementation\n");
|
|
rtmp_proto_impl_set(atoi(optarg));
|
|
break;
|
|
case '0':
|
|
ffmpeg_av_dict_set("video_rep_index", optarg, 0);
|
|
break;
|
|
case '1':
|
|
ffmpeg_av_dict_set("audio_rep_index", optarg, 0);
|
|
break;
|
|
case '4':
|
|
#ifdef HAVE_FLV2MPEG4_CONVERTER
|
|
flv2mpeg4_converter_set(atoi(optarg));
|
|
#endif
|
|
break;
|
|
case 'f':
|
|
{
|
|
char *ffopt = strdup(optarg);
|
|
char *ffval = strchr(ffopt, '=');
|
|
if (ffval)
|
|
{
|
|
*ffval = '\0';
|
|
ffval += 1;
|
|
ffmpeg_av_dict_set(ffopt, ffval, 0);
|
|
}
|
|
free(ffopt);
|
|
break;
|
|
}
|
|
case 'b':
|
|
*linuxDvbBufferSizeMB = 1024 * 1024 * atoi(optarg);
|
|
break;
|
|
case 'S':
|
|
playbackFiles->iFirstFileSize = (uint64_t) strtoull(optarg, (char **)NULL, 10);
|
|
break;
|
|
case 'O':
|
|
playbackFiles->iFirstMoovAtomOffset = (uint64_t) strtoull(optarg, (char **)NULL, 10);
|
|
break;
|
|
case 'F':
|
|
if (optarg[0] != '\0')
|
|
{
|
|
playbackFiles->szFirstMoovAtomFile = malloc(IPTV_MAX_FILE_PATH);
|
|
playbackFiles->szFirstMoovAtomFile[0] = '\0';
|
|
strncpy(playbackFiles->szFirstMoovAtomFile, optarg, IPTV_MAX_FILE_PATH - 1);
|
|
playbackFiles->szFirstMoovAtomFile[IPTV_MAX_FILE_PATH] = '\0';
|
|
map_inter_file_path(playbackFiles->szFirstMoovAtomFile);
|
|
}
|
|
break;
|
|
case 'T':
|
|
PlaybackHandler.httpTimeout = (uint32_t) strtoul(optarg, NULL, 10);
|
|
printf("Setting http timeout to %u ms\n", PlaybackHandler.httpTimeout);
|
|
break;
|
|
|
|
default:
|
|
printf("?? getopt returned character code 0%o ??\n", c);
|
|
ret = -1;
|
|
}
|
|
}
|
|
|
|
if (ret == 0 && optind < argc)
|
|
{
|
|
ret = 0;
|
|
playbackFiles->szFirstFile = malloc(IPTV_MAX_FILE_PATH);
|
|
playbackFiles->szFirstFile[0] = '\0';
|
|
|
|
if (NULL == strstr(argv[optind], "://"))
|
|
{
|
|
strcpy(playbackFiles->szFirstFile, "file://");
|
|
}
|
|
strcat(playbackFiles->szFirstFile, argv[optind]);
|
|
playbackFiles->szFirstFile[IPTV_MAX_FILE_PATH - 1] = '\0';
|
|
map_inter_file_path(playbackFiles->szFirstFile);
|
|
printf("file: [%s]\n", playbackFiles->szFirstFile);
|
|
++optind;
|
|
}
|
|
else
|
|
{
|
|
ret = -1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
int ignored __attribute__((unused)) = system("echo 'encoder' > /proc/stb/avs/0/input");
|
|
|
|
pthread_t termThread;
|
|
int isTermThreadStarted = 0;
|
|
|
|
int audioTrackIdx = -1;
|
|
int subtitleTrackIdx = -1;
|
|
|
|
uint32_t linuxDvbBufferSizeMB = 0;
|
|
|
|
char argvBuff[256];
|
|
memset(argvBuff, '\0', sizeof(argvBuff));
|
|
int commandRetVal = -1;
|
|
|
|
/* inform client that we can handle additional commands */
|
|
E2iSendMsg("{\"EPLAYER3_EXTENDED\":{\"version\":%d}}\n", 68);
|
|
|
|
PlayFiles_t playbackFiles;
|
|
memset(&playbackFiles, 0x00, sizeof(playbackFiles));
|
|
|
|
if (0 != ParseParams(argc, argv, &playbackFiles, &audioTrackIdx, &subtitleTrackIdx, &linuxDvbBufferSizeMB))
|
|
{
|
|
printf("Usage: exteplayer3 filePath [-u user-agent] [-c cookies] [-h headers] [-p prio] [-a] [-d] [-w] [-l] [-s] [-i] [-t audioTrackId] [-9 subtitleTrackId] [-x separateAudioUri] plabackUri\n");
|
|
printf("[-b size] Linux DVB output buffer size in MB\n");
|
|
printf("[-a 0|1|2|3] AAC software decoding - 1 bit - AAC ADTS, 2 - bit AAC LATM\n");
|
|
printf("[-e] EAC3 software decoding\n");
|
|
printf("[-3] AC3 software decoding\n");
|
|
printf("[-d] DTS software decoding\n");
|
|
printf("[-m] MP3 software decoding\n");
|
|
printf("[-A 0|1] disable|enable AMR software decoding\n");
|
|
printf("[-V 0|1] disable|enable VORBIS software decoding\n");
|
|
printf("[-U 0|1] disable|enable AMR software decoding\n");
|
|
printf("[-w] WMA2, WMA/PRO software decoding\n");
|
|
printf("[-l] software decoder use LPCM for injection (otherwise wav PCM will be used)\n");
|
|
printf("[-s] software decoding as stereo [downmix]\n");
|
|
#ifdef HAVE_FLV2MPEG4_CONVERTER
|
|
printf("[-4 0|1] - disable/enable flv2mpeg4 converter\n");
|
|
#endif
|
|
printf("[-i] play in infinity loop\n");
|
|
printf("[-v] switch to live TS stream mode\n");
|
|
printf("[-n 0|1|2] rtmp force protocol implementation auto(0) native/ffmpeg(1) or librtmp(2)\n");
|
|
printf("[-o 0|1] set progressive download\n");
|
|
printf("[-p value] nice value\n");
|
|
printf("[-P value] select Program ID from multi-service stream\n");
|
|
printf("[-t id] audio track ID switched on at start\n");
|
|
printf("[-9 id] subtitle track ID switched on at start\n");
|
|
printf("[-h headers] set custom HTTP headers \"Name: value\\r\\nName: value\\r\\n\"\n");
|
|
printf("[-u user-agent] set custom http User-Agent header\n");
|
|
printf("[-c cookies] set cookies - not working at now, please use -h instead\n");
|
|
printf("[-x separateAudioUri]\n");
|
|
printf("[-0 idx] video MPEG-DASH representation index\n");
|
|
printf("[-1 idx] audio MPEG-DASH representation index\n");
|
|
printf("[-f ffopt=ffval] any other ffmpeg option\n");
|
|
printf("[-F path to additional file with moov atom data (used for mp4 playback in progressive download mode)\n");
|
|
printf("[-O moov atom offset in the original file (used for mp4 playback in progressive download mode)\n");
|
|
printf("[-S remote file size (used for mp4 playback in progressive download mode)\n");
|
|
printf("[-G path (directory where graphic subtitles frames will be saved)\n");
|
|
printf("[-W osd window width (width of the window used to scale graphic subtitle frame)\n");
|
|
printf("[-H osd window height (height of the window used to scale graphic subtitle frame)\n");
|
|
exit(1);
|
|
}
|
|
|
|
g_player = malloc(sizeof(Context_t));
|
|
if (NULL == g_player)
|
|
{
|
|
printf("g_player allocate error\n");
|
|
exit(1);
|
|
}
|
|
|
|
pthread_mutex_init(&playbackStartMtx, NULL);
|
|
do
|
|
{
|
|
int flags = 0;
|
|
|
|
if (pipe(g_pfd) == -1)
|
|
break;
|
|
|
|
/* Make read and write ends of pipe nonblocking */
|
|
if ((flags = fcntl(g_pfd[0], F_GETFL)) == -1)
|
|
break;
|
|
|
|
/* Make read end nonblocking */
|
|
flags |= O_NONBLOCK;
|
|
if (fcntl(g_pfd[0], F_SETFL, flags) == -1)
|
|
break;
|
|
|
|
if ((flags = fcntl(g_pfd[1], F_GETFL)) == -1)
|
|
break;
|
|
|
|
/* Make write end nonblocking */
|
|
flags |= O_NONBLOCK;
|
|
if (fcntl(g_pfd[1], F_SETFL, flags) == -1)
|
|
break;
|
|
|
|
if (pthread_create(&termThread, NULL, TermThreadFun, NULL) == 0)
|
|
isTermThreadStarted = 1;
|
|
}
|
|
while (0);
|
|
|
|
g_player->playback = &PlaybackHandler;
|
|
g_player->output = &OutputHandler;
|
|
g_player->container = &ContainerHandler;
|
|
g_player->manager = &ManagerHandler;
|
|
|
|
// make sure to kill myself when parent dies
|
|
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
|
|
|
SetBuffering();
|
|
|
|
//Registrating output devices
|
|
g_player->output->Command(g_player, OUTPUT_ADD, "audio");
|
|
g_player->output->Command(g_player, OUTPUT_ADD, "video");
|
|
g_player->output->Command(g_player, OUTPUT_ADD, "subtitle");
|
|
|
|
//Set LINUX DVB additional write buffer size
|
|
if (linuxDvbBufferSizeMB)
|
|
g_player->output->Command(g_player, OUTPUT_SET_BUFFER_SIZE, &linuxDvbBufferSizeMB);
|
|
|
|
g_player->manager->video->Command(g_player, MANAGER_REGISTER_UPDATED_TRACK_INFO, UpdateVideoTrack);
|
|
if (strncmp(playbackFiles.szFirstFile, "rtmp", 4) && strncmp(playbackFiles.szFirstFile, "ffrtmp", 4))
|
|
{
|
|
g_player->playback->noprobe = 1;
|
|
}
|
|
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_OPEN, &playbackFiles);
|
|
E2iSendMsg("{\"PLAYBACK_OPEN\":{\"OutputName\":\"%s\", \"file\":\"%s\", \"sts\":%d}}\n", g_player->output->Name, playbackFiles.szFirstFile, commandRetVal);
|
|
if (commandRetVal < 0)
|
|
{
|
|
if (NULL != g_player)
|
|
{
|
|
free(g_player);
|
|
}
|
|
return 10;
|
|
}
|
|
|
|
{
|
|
pthread_mutex_lock(&playbackStartMtx);
|
|
isPlaybackStarted = 1;
|
|
pthread_mutex_unlock(&playbackStartMtx);
|
|
|
|
commandRetVal = g_player->output->Command(g_player, OUTPUT_OPEN, NULL);
|
|
E2iSendMsg("{\"OUTPUT_OPEN\":{\"sts\":%d}}\n", commandRetVal);
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_PLAY, NULL);
|
|
E2iSendMsg("{\"PLAYBACK_PLAY\":{\"sts\":%d}}\n", commandRetVal);
|
|
|
|
if (g_player->playback->isPlaying)
|
|
{
|
|
PlaybackDieNowRegisterCallback(TerminateWakeUp);
|
|
|
|
HandleTracks(g_player->manager->video, (PlaybackCmd_t) -1, "vc");
|
|
HandleTracks(g_player->manager->audio, (PlaybackCmd_t) -1, "al");
|
|
if (audioTrackIdx >= 0)
|
|
{
|
|
static char cmd[128] = ""; // static to not allocate on stack
|
|
sprintf(cmd, "ai%d\n", audioTrackIdx);
|
|
commandRetVal = HandleTracks(g_player->manager->audio, PLAYBACK_SWITCH_AUDIO, cmd);
|
|
}
|
|
HandleTracks(g_player->manager->audio, (PlaybackCmd_t) -1, "ac");
|
|
|
|
HandleTracks(g_player->manager->subtitle, (PlaybackCmd_t) -1, "sl");
|
|
if (subtitleTrackIdx >= 0)
|
|
{
|
|
static char cmd[128] = ""; // static to not allocate on stack
|
|
sprintf(cmd, "si%d\n", subtitleTrackIdx);
|
|
commandRetVal = HandleTracks(g_player->manager->subtitle, PLAYBACK_SWITCH_SUBTITLE, cmd);
|
|
}
|
|
HandleTracks(g_player->manager->subtitle, (PlaybackCmd_t) -1, "sc");
|
|
}
|
|
|
|
while (g_player->playback->isPlaying && PlaybackDieNow(0) == 0)
|
|
{
|
|
/* we made fgets non blocking */
|
|
if (NULL == fgets(argvBuff, sizeof(argvBuff) - 1, stdin))
|
|
{
|
|
/* wait for data - max 1s */
|
|
kbhit();
|
|
continue;
|
|
}
|
|
|
|
if (argvBuff[0] == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch (argvBuff[0])
|
|
{
|
|
case 'v':
|
|
{
|
|
HandleTracks(g_player->manager->video, (PlaybackCmd_t) -1, argvBuff);
|
|
break;
|
|
}
|
|
case 'a':
|
|
{
|
|
HandleTracks(g_player->manager->audio, PLAYBACK_SWITCH_AUDIO, argvBuff);
|
|
break;
|
|
}
|
|
case 's':
|
|
{
|
|
HandleTracks(g_player->manager->subtitle, PLAYBACK_SWITCH_SUBTITLE, argvBuff);
|
|
break;
|
|
}
|
|
case 'q':
|
|
{
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_STOP, NULL);
|
|
E2iSendMsg("{\"PLAYBACK_STOP\":{\"sts\":%d}}\n", commandRetVal);
|
|
break;
|
|
}
|
|
case 'c':
|
|
{
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_CONTINUE, NULL);
|
|
E2iSendMsg("{\"PLAYBACK_CONTINUE\":{\"sts\":%d}}\n", commandRetVal);
|
|
break;
|
|
}
|
|
case 'p':
|
|
{
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_PAUSE, NULL);
|
|
E2iSendMsg("{\"PLAYBACK_PAUSE\":{\"sts\":%d}}\n", commandRetVal);
|
|
break;
|
|
}
|
|
case 'm':
|
|
{
|
|
int speed = 0;
|
|
sscanf(argvBuff + 1, "%d", &speed);
|
|
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_SLOWMOTION, &speed);
|
|
E2iSendMsg("{\"PLAYBACK_SLOWMOTION\":{\"speed\":%d, \"sts\":%d}}\n", speed, commandRetVal);
|
|
break;
|
|
}
|
|
case 'o':
|
|
{
|
|
int flags = 0;
|
|
if (sscanf(argvBuff + 1, "%d", &flags) == 1)
|
|
{
|
|
progressive_playback_set(flags);
|
|
E2iSendMsg("{\"PROGRESSIVE_DOWNLOAD\":{\"flags\":%d, \"sts\":0}}\n", flags);
|
|
}
|
|
break;
|
|
}
|
|
case 'f':
|
|
{
|
|
int speed = 0;
|
|
sscanf(argvBuff + 1, "%d", &speed);
|
|
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_FASTFORWARD, &speed);
|
|
E2iSendMsg("{\"PLAYBACK_FASTFORWARD\":{\"speed\":%d, \"sts\":%d}}\n", speed, commandRetVal);
|
|
break;
|
|
}
|
|
case 'b':
|
|
{
|
|
int speed = 0;
|
|
sscanf(argvBuff + 1, "%d", &speed);
|
|
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_FASTBACKWARD, &speed);
|
|
E2iSendMsg("{\"PLAYBACK_FASTBACKWARD\":{\"speed\":%d, \"sts\":%d}}\n", speed, commandRetVal);
|
|
break;
|
|
}
|
|
case 'g':
|
|
{
|
|
int32_t gotoPos = 0;
|
|
int64_t length = 0;
|
|
int32_t lengthInt = 0;
|
|
int64_t sec = 0;
|
|
int8_t force = ('f' == argvBuff[1]) ? 1 : 0; // f - force, c - check
|
|
|
|
sscanf(argvBuff + 2, "%d", &gotoPos);
|
|
if (0 <= gotoPos || force)
|
|
{
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_LENGTH, (void *)&length);
|
|
E2iSendMsg("{\"PLAYBACK_LENGTH\":{\"length\":%"PRId64", \"sts\":%d}}\n", length, commandRetVal);
|
|
lengthInt = (int32_t)length;
|
|
if (10 <= lengthInt || force)
|
|
{
|
|
sec = gotoPos;
|
|
if (!force && gotoPos >= lengthInt)
|
|
{
|
|
sec = lengthInt - 10;
|
|
}
|
|
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_SEEK_ABS, (void *)&sec);
|
|
E2iSendMsg("{\"PLAYBACK_SEEK_ABS\":{\"sec\":%"PRId64", \"sts\":%d}}\n", sec, commandRetVal);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'k':
|
|
{
|
|
int32_t seek = 0;
|
|
int64_t length = 0;
|
|
int32_t lengthInt = 0;
|
|
int64_t sec = 0;
|
|
int64_t pts = 0;
|
|
int32_t CurrentSec = 0;
|
|
int8_t force = ('f' == argvBuff[1]) ? 1 : 0; // f - force, c - check
|
|
|
|
sscanf(argvBuff + 2, "%d", &seek);
|
|
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_PTS, &pts);
|
|
CurrentSec = (int32_t)(pts / 90000);
|
|
|
|
if (commandRetVal == 0)
|
|
{
|
|
E2iSendMsg("{\"J\":{\"ms\":%"PRId64"}}\n", pts / 90);
|
|
}
|
|
|
|
if (commandRetVal == 0 || force)
|
|
{
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_LENGTH, (void *)&length);
|
|
E2iSendMsg("{\"PLAYBACK_LENGTH\":{\"length\":%"PRId64", \"sts\":%d}}\n", length, commandRetVal);
|
|
|
|
lengthInt = (int32_t)length;
|
|
if (10 <= lengthInt || force)
|
|
{
|
|
int32_t ergSec = CurrentSec + seek;
|
|
if (!force && 0 > ergSec)
|
|
{
|
|
sec = CurrentSec * -1; // jump to start position
|
|
}
|
|
else if (!force && ergSec >= lengthInt)
|
|
{
|
|
sec = (lengthInt - CurrentSec) - 5;
|
|
if (0 < sec)
|
|
{
|
|
sec = 0; // no jump we are at the end
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sec = seek;
|
|
}
|
|
}
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_SEEK, (void *)&sec);
|
|
E2iSendMsg("{\"PLAYBACK_SEEK\":{\"sec\":%"PRId64", \"sts\":%d}}\n", sec, commandRetVal);
|
|
}
|
|
break;
|
|
}
|
|
case 'l':
|
|
{
|
|
int64_t length = 0;
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_LENGTH, (void *)&length);
|
|
E2iSendMsg("{\"PLAYBACK_LENGTH\":{\"length\":%"PRId64", \"sts\":%d}}\n", length, commandRetVal);
|
|
break;
|
|
}
|
|
case 'j':
|
|
{
|
|
int64_t pts = 0;
|
|
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_PTS, &pts);
|
|
if (commandRetVal == 0)
|
|
{
|
|
int64_t lastPts = 0;
|
|
commandRetVal = 1;
|
|
|
|
if (g_player->container && g_player->container->selectedContainer)
|
|
{
|
|
commandRetVal = g_player->container->selectedContainer->Command((Context_t *)g_player->container, CONTAINER_LAST_PTS, &lastPts);
|
|
}
|
|
|
|
if (commandRetVal == 0 && lastPts != INVALID_PTS_VALUE)
|
|
{
|
|
E2iSendMsg("{\"J\":{\"ms\":%"PRId64",\"lms\":%"PRId64"}}\n", pts / 90, lastPts / 90);
|
|
}
|
|
else
|
|
{
|
|
E2iSendMsg("{\"J\":{\"ms\":%"PRId64"}}\n", pts / 90);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case 'i':
|
|
{
|
|
PlaybackHandler_t *ptrP = g_player->playback;
|
|
if (ptrP)
|
|
{
|
|
E2iSendMsg("{\"PLAYBACK_INFO\":{ \"isPlaying\":%s, \"isPaused\":%s, \"isForwarding\":%s, \"isSeeking\":%s, \"isCreationPhase\":%s,", \
|
|
DUMP_BOOL(ptrP->isPlaying), DUMP_BOOL(ptrP->isPaused), DUMP_BOOL(ptrP->isForwarding), DUMP_BOOL(ptrP->isSeeking), DUMP_BOOL(ptrP->isCreationPhase));
|
|
E2iSendMsg("\"BackWard\":%d, \"SlowMotion\":%d, \"Speed\":%d, \"AVSync\":%d,", ptrP->BackWard, ptrP->SlowMotion, ptrP->Speed, ptrP->AVSync);
|
|
E2iSendMsg(" \"isVideo\":%s, \"isAudio\":%s, \"isSubtitle\":%s, \"isDvbSubtitle\":%s, \"isTeletext\":%s, \"mayWriteToFramebuffer\":%s, \"abortRequested\":%s }}\n", \
|
|
DUMP_BOOL(ptrP->isVideo), DUMP_BOOL(ptrP->isAudio), DUMP_BOOL(0), DUMP_BOOL(0), DUMP_BOOL(0), DUMP_BOOL(0), DUMP_BOOL(ptrP->abortRequested));
|
|
}
|
|
|
|
break;
|
|
}
|
|
case 'n':
|
|
{
|
|
//uint8_t loop = 0;
|
|
if ('1' == argvBuff[1] || '0' == argvBuff[1])
|
|
{
|
|
PlaybackHandler_t *ptrP = g_player->playback;
|
|
if (ptrP)
|
|
{
|
|
ptrP->isLoopMode = '1' == argvBuff[1] ? 1 : 0;
|
|
E2iSendMsg("{\"N\":{ \"isLoop\":%s }}\n", DUMP_BOOL(ptrP->isLoopMode));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_player->output->Command(g_player, OUTPUT_CLOSE, NULL);
|
|
}
|
|
|
|
if (NULL != g_player)
|
|
{
|
|
free(g_player);
|
|
}
|
|
|
|
if (isTermThreadStarted && write(g_pfd[1], "x", 1) == 1)
|
|
{
|
|
pthread_join(termThread, NULL);
|
|
}
|
|
|
|
pthread_mutex_destroy(&playbackStartMtx);
|
|
|
|
close(g_pfd[0]);
|
|
close(g_pfd[1]);
|
|
|
|
exit(0);
|
|
}
|