diff --git a/libeplayer3-arm/container/container_ffmpeg.c b/libeplayer3-arm/container/container_ffmpeg.c index f0f17c6..a8b4f46 100644 --- a/libeplayer3-arm/container/container_ffmpeg.c +++ b/libeplayer3-arm/container/container_ffmpeg.c @@ -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) diff --git a/libeplayer3-arm/include/common.h b/libeplayer3-arm/include/common.h index ae0f4d9..993f5b6 100644 --- a/libeplayer3-arm/include/common.h +++ b/libeplayer3-arm/include/common.h @@ -9,12 +9,16 @@ #include "playback.h" #include -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 diff --git a/libeplayer3-arm/main/exteplayer.c b/libeplayer3-arm/main/exteplayer.c index a6e0aea..85ab823 100644 --- a/libeplayer3-arm/main/exteplayer.c +++ b/libeplayer3-arm/main/exteplayer.c @@ -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) diff --git a/libeplayer3-arm/output/writer/mipsel/h264.c b/libeplayer3-arm/output/writer/mipsel/h264.c index 8fe4be9..b5b37da 100644 --- a/libeplayer3-arm/output/writer/mipsel/h264.c +++ b/libeplayer3-arm/output/writer/mipsel/h264.c @@ -396,7 +396,7 @@ static int writeData(WriterAVCallData_t *call) ic = 0; iov[ic++].iov_base = PesHeader; - //if (initialHeader) + if (!avc3) { if (CodecData) { diff --git a/libeplayer3-arm/output/writer/sh4/h264.c b/libeplayer3-arm/output/writer/sh4/h264.c index 39a6afa..3172c5b 100644 --- a/libeplayer3-arm/output/writer/sh4/h264.c +++ b/libeplayer3-arm/output/writer/sh4/h264.c @@ -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;