mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 23:13:16 +02:00
Add possibility to provide moov atom data as separate file.
This will allow playback of MP4 files which are under download and moov atom is located at the end of file. Signed-off-by: max_10 <max_10@gmx.de>
This commit is contained in:
@@ -1353,40 +1353,142 @@ static int32_t interrupt_cb(void *ctx)
|
||||
}
|
||||
|
||||
#ifdef SAM_CUSTOM_IO
|
||||
typedef struct CustomIOCtx_t
|
||||
{
|
||||
FILE *pFile;
|
||||
FILE *pMoovFile;
|
||||
int64_t iOffset;
|
||||
|
||||
char *szFile;
|
||||
uint64_t iFileSize;
|
||||
char *szMoovAtomFile;
|
||||
uint64_t iMoovAtomOffset;
|
||||
} CustomIOCtx_t;
|
||||
|
||||
CustomIOCtx_t *custom_io_tab[IPTV_AV_CONTEXT_MAX_NUM] = {NULL, NULL};
|
||||
|
||||
int SAM_ReadFunc(void *ptr, uint8_t *buffer, int lSize)
|
||||
{
|
||||
size_t ret = fread((void *) buffer, (size_t) 1, (size_t) lSize, (FILE *)ptr);
|
||||
return (int)ret;
|
||||
CustomIOCtx_t *io = (CustomIOCtx_t *)ptr;
|
||||
int ret = 0;
|
||||
|
||||
if (!io->pMoovFile)
|
||||
{
|
||||
ret = (int)fread((void *) buffer, (size_t) 1, (size_t) lSize, io->pFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (io->iOffset < io->iMoovAtomOffset)
|
||||
{
|
||||
ret = (int)fread((void *) buffer, (size_t) 1, (size_t) lSize, io->pFile);
|
||||
buffer += ret;
|
||||
lSize -= ret;
|
||||
}
|
||||
|
||||
if (io->iOffset + ret >= io->iMoovAtomOffset)
|
||||
{
|
||||
if (ret)
|
||||
{
|
||||
if (fseeko(io->pMoovFile, io->iOffset + ret - io->iMoovAtomOffset, SEEK_SET))
|
||||
{
|
||||
// something goes wrong
|
||||
ffmpeg_err("fseeko on moov atom file fail \n");
|
||||
lSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret += (int)fread((void *) buffer, (size_t) 1, (size_t) lSize, io->pMoovFile);
|
||||
}
|
||||
|
||||
io->iOffset += ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// whence: SEEK_SET, SEEK_CUR, SEEK_END (like fseek) and AVSEEK_SIZE
|
||||
int64_t SAM_SeekFunc(void *ptr, int64_t pos, int whence)
|
||||
{
|
||||
if (AVSEEK_SIZE == whence)
|
||||
CustomIOCtx_t *io = (CustomIOCtx_t *)ptr;
|
||||
int64_t ret = -1;
|
||||
if (!io->pMoovFile)
|
||||
{
|
||||
return -1;
|
||||
if (AVSEEK_SIZE != whence)
|
||||
{
|
||||
ret = (int64_t)fseeko(io->pFile, (off_t)pos, whence);
|
||||
if (0 == ret)
|
||||
{
|
||||
ret = (int64_t)ftello(io->pFile);
|
||||
}
|
||||
}
|
||||
}
|
||||
int ret = fseeko((FILE *)ptr, (off_t)pos, whence);
|
||||
if (0 == ret)
|
||||
else
|
||||
{
|
||||
return (off_t)ftello((FILE *)ptr);
|
||||
switch (whence)
|
||||
{
|
||||
case SEEK_SET:
|
||||
ret = pos;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
ret += pos;
|
||||
break;
|
||||
case SEEK_END:
|
||||
ret = io->iFileSize + pos;
|
||||
break;
|
||||
case AVSEEK_SIZE:
|
||||
return io->iFileSize;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret >= 0 && ret <= io->iFileSize)
|
||||
{
|
||||
if (ret < io->iMoovAtomOffset)
|
||||
{
|
||||
if (!fseeko(io->pFile, (off_t)ret, SEEK_SET))
|
||||
io->iOffset = ret;
|
||||
else
|
||||
ret = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!fseeko(io->pMoovFile, (off_t)(ret - io->iMoovAtomOffset), SEEK_SET))
|
||||
io->iOffset = ret;
|
||||
else
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = -1;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
AVIOContext *container_ffmpeg_get_avio_context(char *filename, size_t avio_ctx_buffer_size)
|
||||
AVIOContext *container_ffmpeg_get_avio_context(CustomIOCtx_t *custom_io, size_t avio_ctx_buffer_size)
|
||||
{
|
||||
if (strstr(filename, "file://") == filename)
|
||||
{
|
||||
filename += 7;
|
||||
}
|
||||
if (strstr(custom_io->szFile, "file://") == custom_io->szFile)
|
||||
custom_io->szFile += 7;
|
||||
|
||||
FILE *pFile = fopen(filename, "rb");
|
||||
if (NULL == pFile)
|
||||
custom_io->pFile = fopen(custom_io->szFile, "rb");
|
||||
if (NULL == custom_io->pFile)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (custom_io->szMoovAtomFile && custom_io->szMoovAtomFile[0] != '\0')
|
||||
{
|
||||
if (strstr(custom_io->szMoovAtomFile, "file://") == custom_io->szMoovAtomFile)
|
||||
custom_io->szMoovAtomFile += 7;
|
||||
|
||||
custom_io->pMoovFile = fopen(custom_io->szMoovAtomFile, "rb");
|
||||
if (NULL == custom_io->pMoovFile)
|
||||
{
|
||||
fclose(custom_io->pFile);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
AVIOContext *avio_ctx = NULL;
|
||||
uint8_t *avio_ctx_buffer = NULL;
|
||||
|
||||
@@ -1395,7 +1497,7 @@ AVIOContext *container_ffmpeg_get_avio_context(char *filename, size_t avio_ctx_b
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size, 0, pFile, &SAM_ReadFunc, NULL, &SAM_SeekFunc);
|
||||
avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size, 0, custom_io, &SAM_ReadFunc, NULL, &SAM_SeekFunc);
|
||||
if (!avio_ctx)
|
||||
{
|
||||
return NULL;
|
||||
@@ -1404,7 +1506,7 @@ AVIOContext *container_ffmpeg_get_avio_context(char *filename, size_t avio_ctx_b
|
||||
}
|
||||
#endif
|
||||
|
||||
int32_t container_ffmpeg_init_av_context(Context_t *context, char *filename, int32_t AVIdx)
|
||||
int32_t container_ffmpeg_init_av_context(Context_t *context, char *filename, uint64_t fileSize, char *moovAtomFile, uint64_t moovAtomOffset, int32_t AVIdx)
|
||||
{
|
||||
int32_t err = 0;
|
||||
AVInputFormat *fmt = NULL;
|
||||
@@ -1416,7 +1518,16 @@ int32_t container_ffmpeg_init_av_context(Context_t *context, char *filename, int
|
||||
if (0 == strstr(filename, "://") ||
|
||||
0 == strncmp(filename, "file://", 7))
|
||||
{
|
||||
AVIOContext *avio_ctx = container_ffmpeg_get_avio_context(filename, 4096);
|
||||
AVIOContext *avio_ctx = NULL;
|
||||
custom_io_tab[AVIdx] = malloc(sizeof(CustomIOCtx_t));
|
||||
sizeof(custom_io_tab[AVIdx], 0x00, sizeof(CustomIOCtx_t));
|
||||
|
||||
custom_io_tab[AVIdx]->szFile = filename;
|
||||
custom_io_tab[AVIdx]->iFileSize = fileSize;
|
||||
custom_io_tab[AVIdx]->szMoovAtomFile = moovAtomFile;
|
||||
custom_io_tab[AVIdx]->iMoovAtomOffset = moovAtomOffset;
|
||||
|
||||
avio_ctx = container_ffmpeg_get_avio_context(custom_io_tab[AVIdx], 4096);
|
||||
if (avio_ctx)
|
||||
{
|
||||
avContextTab[AVIdx]->pb = avio_ctx;
|
||||
@@ -1424,6 +1535,8 @@ int32_t container_ffmpeg_init_av_context(Context_t *context, char *filename, int
|
||||
}
|
||||
else
|
||||
{
|
||||
free(custom_io_tab[AVIdx]);
|
||||
custom_io_tab[AVIdx] = NULL;
|
||||
return cERR_CONTAINER_FFMPEG_OPEN;
|
||||
}
|
||||
}
|
||||
@@ -1782,15 +1895,17 @@ int32_t container_ffmpeg_init(Context_t *context, PlayFiles_t *playFilesNames)
|
||||
av_log_set_callback(ffmpeg_silen_callback);
|
||||
|
||||
context->playback->abortRequested = 0;
|
||||
int32_t res = container_ffmpeg_init_av_context(context, playFilesNames->szFirstFile, 0);
|
||||
int32_t res = container_ffmpeg_init_av_context(context, playFilesNames->szFirstFile, playFilesNames->iFirstFileSize, \
|
||||
playFilesNames->szFirstMoovAtomFile, playFilesNames->iFirstMoovAtomOffset, 0);
|
||||
if (0 != res)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
if (playFilesNames->szSecondFile)
|
||||
if (playFilesNames->szSecondFile && playFilesNames->szSecondFile[0] != '\0')
|
||||
{
|
||||
res = container_ffmpeg_init_av_context(context, playFilesNames->szSecondFile, 1);
|
||||
res = container_ffmpeg_init_av_context(context, playFilesNames->szSecondFile, playFilesNames->iSecondFileSize, \
|
||||
playFilesNames->szSecondMoovAtomFile, playFilesNames->iSecondMoovAtomOffset, 1);
|
||||
}
|
||||
|
||||
if (0 != res)
|
||||
|
@@ -9,12 +9,16 @@
|
||||
#include "playback.h"
|
||||
#include <pthread.h>
|
||||
|
||||
typedef char PlayFilesTab_t[2];
|
||||
|
||||
typedef struct PlayFiles_t
|
||||
{
|
||||
char *szFirstFile;
|
||||
char *szSecondFile;
|
||||
char *szFirstMoovAtomFile;
|
||||
char *szSecondMoovAtomFile;
|
||||
uint64_t iFirstFileSize;
|
||||
uint64_t iSecondFileSize;
|
||||
uint64_t iFirstMoovAtomOffset;
|
||||
uint64_t iSecondMoovAtomOffset;
|
||||
} PlayFiles_t;
|
||||
|
||||
typedef struct Context_s
|
||||
|
@@ -504,14 +504,14 @@ static void UpdateVideoTrack()
|
||||
HandleTracks(g_player->manager->video, (PlaybackCmd_t) - 1, "vc");
|
||||
}
|
||||
|
||||
static int ParseParams(int argc, char *argv[], char *file, char *audioFile, int *pAudioTrackIdx, int *subtitleTrackIdx, uint32_t *linuxDvbBufferSizeMB)
|
||||
static int ParseParams(int argc, char *argv[], PlayFiles_t *playbackFiles, int *pAudioTrackIdx, int *subtitleTrackIdx, uint32_t *linuxDvbBufferSizeMB)
|
||||
{
|
||||
int ret = 0;
|
||||
int c;
|
||||
//int digit_optind = 0;
|
||||
//int aopt = 0, bopt = 0;
|
||||
//char *copt = 0, *dopt = 0;
|
||||
while ((c = getopt(argc, argv, "we3dlsrimva:n:x:u:c:h:o:p:P:t:9:0:1:4:f:b:")) != -1)
|
||||
while ((c = getopt(argc, argv, "we3dlsrimva:n:x:u:c:h:o:p:P:t:9:0:1:4:f:b:F:S:O:")) != -1)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
@@ -572,8 +572,13 @@ static int ParseParams(int argc, char *argv[], char *file, char *audioFile, int
|
||||
*subtitleTrackIdx = atoi(optarg);
|
||||
break;
|
||||
case 'x':
|
||||
strncpy(audioFile, optarg, IPTV_MAX_FILE_PATH - 1);
|
||||
map_inter_file_path(audioFile);
|
||||
if (optarg[0] != '\0')
|
||||
{
|
||||
playbackFiles->szSecondFile = malloc(IPTV_MAX_FILE_PATH);
|
||||
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);
|
||||
@@ -624,6 +629,21 @@ static int ParseParams(int argc, char *argv[], char *file, char *audioFile, int
|
||||
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);
|
||||
strncpy(playbackFiles->szFirstMoovAtomFile, optarg, IPTV_MAX_FILE_PATH - 1);
|
||||
playbackFiles->szFirstMoovAtomFile[IPTV_MAX_FILE_PATH] = '\0';
|
||||
map_inter_file_path(playbackFiles->szFirstMoovAtomFile);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("?? getopt returned character code 0%o ??\n", c);
|
||||
ret = -1;
|
||||
@@ -633,14 +653,15 @@ static int ParseParams(int argc, char *argv[], char *file, char *audioFile, int
|
||||
if (0 == ret && optind < argc)
|
||||
{
|
||||
ret = 0;
|
||||
|
||||
playbackFiles->szFirstFile = malloc(IPTV_MAX_FILE_PATH);
|
||||
if (NULL == strstr(argv[optind], "://"))
|
||||
{
|
||||
strcpy(file, "file://");
|
||||
strcpy(playbackFiles->szFirstFile, "file://");
|
||||
}
|
||||
strcat(file, argv[optind]);
|
||||
map_inter_file_path(file);
|
||||
printf("file: [%s]\n", file);
|
||||
strcat(playbackFiles->szFirstFile, argv[optind]);
|
||||
playbackFiles->szFirstFile[IPTV_MAX_FILE_PATH] = '\0';
|
||||
map_inter_file_path(playbackFiles->szFirstFile);
|
||||
printf("file: [%s]\n", playbackFiles->szFirstFile);
|
||||
++optind;
|
||||
}
|
||||
else
|
||||
@@ -654,11 +675,6 @@ int main(int argc, char *argv[])
|
||||
{
|
||||
pthread_t termThread;
|
||||
int isTermThreadStarted = 0;
|
||||
char file[IPTV_MAX_FILE_PATH];
|
||||
memset(file, '\0', sizeof(file));
|
||||
|
||||
char audioFile[IPTV_MAX_FILE_PATH];
|
||||
memset(audioFile, '\0', sizeof(audioFile));
|
||||
|
||||
int audioTrackIdx = -1;
|
||||
int subtitleTrackIdx = -1;
|
||||
@@ -669,9 +685,11 @@ int main(int argc, char *argv[])
|
||||
memset(argvBuff, '\0', sizeof(argvBuff));
|
||||
int commandRetVal = -1;
|
||||
/* inform client that we can handle additional commands */
|
||||
fprintf(stderr, "{\"EPLAYER3_EXTENDED\":{\"version\":%d}}\n", 45);
|
||||
fprintf(stderr, "{\"EPLAYER3_EXTENDED\":{\"version\":%d}}\n", 46);
|
||||
|
||||
if (0 != ParseParams(argc, argv, file, audioFile, &audioTrackIdx, &subtitleTrackIdx, &linuxDvbBufferSizeMB))
|
||||
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");
|
||||
@@ -701,6 +719,9 @@ int main(int argc, char *argv[])
|
||||
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");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -762,19 +783,13 @@ int main(int argc, char *argv[])
|
||||
|
||||
|
||||
g_player->manager->video->Command(g_player, MANAGER_REGISTER_UPDATED_TRACK_INFO, UpdateVideoTrack);
|
||||
if (strncmp(file, "rtmp", 4) && strncmp(file, "ffrtmp", 4))
|
||||
if (strncmp(playbackFiles.szFirstFile, "rtmp", 4) && strncmp(playbackFiles.szFirstFile, "ffrtmp", 4))
|
||||
{
|
||||
g_player->playback->noprobe = 1;
|
||||
}
|
||||
|
||||
PlayFiles_t playbackFiles = {file, NULL};
|
||||
if ('\0' != audioFile[0])
|
||||
{
|
||||
playbackFiles.szSecondFile = audioFile;
|
||||
}
|
||||
|
||||
commandRetVal = g_player->playback->Command(g_player, PLAYBACK_OPEN, &playbackFiles);
|
||||
fprintf(stderr, "{\"PLAYBACK_OPEN\":{\"OutputName\":\"%s\", \"file\":\"%s\", \"sts\":%d}}\n", g_player->output->Name, file, commandRetVal);
|
||||
fprintf(stderr, "{\"PLAYBACK_OPEN\":{\"OutputName\":\"%s\", \"file\":\"%s\", \"sts\":%d}}\n", g_player->output->Name, playbackFiles.szFirstFile, commandRetVal);
|
||||
if (commandRetVal < 0)
|
||||
{
|
||||
if (NULL != g_player)
|
||||
|
@@ -396,7 +396,7 @@ static int writeData(WriterAVCallData_t *call)
|
||||
ic = 0;
|
||||
iov[ic++].iov_base = PesHeader;
|
||||
|
||||
//if (initialHeader)
|
||||
if (!avc3)
|
||||
{
|
||||
if (CodecData)
|
||||
{
|
||||
|
@@ -100,7 +100,7 @@ const uint8_t Head[] = {0, 0, 0, 1};
|
||||
static int32_t initialHeader = 1;
|
||||
static uint32_t NalLengthBytes = 1;
|
||||
static int avc3 = 0;
|
||||
|
||||
static int sps_pps_in_stream = 0;
|
||||
/* ***************************** */
|
||||
/* Prototypes */
|
||||
/* ***************************** */
|
||||
@@ -263,12 +263,23 @@ static int32_t writeData(void *_call)
|
||||
(call->len > 3) && ((call->data[0] == 0x00 && call->data[1] == 0x00 && call->data[2] == 0x00 && call->data[3] == 0x01) ||
|
||||
(call->data[0] == 0xff && call->data[1] == 0xff && call->data[2] == 0xff && call->data[3] == 0xff))))
|
||||
{
|
||||
uint32_t i = 0;
|
||||
uint8_t InsertPrivData = !sps_pps_in_stream;
|
||||
uint32_t PacketLength = 0;
|
||||
uint32_t FakeStartCode = /*(call->Version << 8) | */PES_VERSION_FAKE_START_CODE;
|
||||
|
||||
uint32_t FakeStartCode = PES_VERSION_FAKE_START_CODE;
|
||||
iov[ic++].iov_base = PesHeader;
|
||||
initialHeader = 0;
|
||||
if (initialHeader)
|
||||
|
||||
while (InsertPrivData && i < 36 && (call->len - i) > 5)
|
||||
{
|
||||
if ((call->data[i] == 0x00 && call->data[i + 1] == 0x00 && call->data[i + 2] == 0x00 && call->data[i + 3] == 0x01 && (call->data[i + 4] == 0x67 || call->data[i + 4] == 0x68)))
|
||||
{
|
||||
InsertPrivData = 0;
|
||||
sps_pps_in_stream = 1;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
if (InsertPrivData && call->private_size > 0 /*&& initialHeader*/) // some rtsp streams can update codec data at runtime
|
||||
{
|
||||
initialHeader = 0;
|
||||
iov[ic].iov_base = call->private_data;
|
||||
@@ -299,7 +310,7 @@ static int32_t writeData(void *_call)
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (initialHeader)
|
||||
if (!avc3)
|
||||
{
|
||||
uint8_t *private_data = call->private_data;
|
||||
uint32_t private_size = call->private_size;
|
||||
|
Reference in New Issue
Block a user