static int32_t ffmpeg_read_wrapper_base(void *opaque, uint8_t *buf, int32_t buf_size, uint8_t type) { int32_t len = 0; if (0 == PlaybackDieNow(0)) { len = ffmpeg_real_read_org(opaque, buf, buf_size); while(len < buf_size && g_context && 0 == PlaybackDieNow(0)) { if (type && len > 0) { break; } int32_t partLen = ffmpeg_real_read_org(opaque, buf+len, buf_size-len); if (partLen > 0) { len += partLen; finishTimeout = 0; continue; } else if (is_finish_timeout()) { len = 0; break; } update_finish_timeout(); usleep(100000); continue; } } //printf("len [%d] finishTimeout[%d]\n", len, finishTimeout); return len; }
static int32_t ffmpeg_read(void *opaque, uint8_t *buf, int32_t buf_size) { int32_t sumlen = 0; int32_t len = 0; int32_t count = 2000; while (sumlen < buf_size && (--count) > 0 && 0 == PlaybackDieNow(0)) { len = ffmpeg_read_real(opaque, buf, buf_size - sumlen); sumlen += len; buf += len; if (len == 0) { usleep(10000); } } if (count == 0) { if (sumlen == 0) { ffmpeg_err( "Timeout waiting for buffered data (buf_size=%d sumlen=%d)!\n", buf_size, sumlen); } else { ffmpeg_err( "Timeout, not all buffered data availabel (buf_size=%d sumlen=%d)!\n", buf_size, sumlen); } } return sumlen; }
ssize_t writev_with_retry(int fd, const struct iovec *iov, size_t ic) { ssize_t len = 0; int i = 0; for(i=0; i<ic; ++i) { write_with_retry(fd, iov[i].iov_base, iov[i].iov_len); len += iov[i].iov_len; if(PlaybackDieNow(0)) { return -1; } } return len; }
/* ***************************** */ ssize_t write_with_retry(int fd, const void *buf, size_t size) { ssize_t ret; int retval = 0; while(size > 0 && 0 == PlaybackDieNow(0)) { ret = write(fd, buf, size); //printf("[%d] write [%lld]\n", fd, ret); if (ret < 0) { switch(errno) { case EINTR: case EAGAIN: usleep(1000); continue; default: retval = -3; break; } if (retval < 0) { break; } } if (ret < 0) { return ret; } size -= ret; buf += ret; if(size > 0) { if( usleep(1000) ) { writer_err("usleep error \n"); } } } return 0; }
static void *TermThreadFun(void *arg) { const char *socket_path = "/tmp/iptvplayer_extplayer_term_fd"; 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); }
int main(int argc, char* argv[]) { 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 */ fprintf(stderr, "{\"EPLAYER3_EXTENDED\":{\"version\":%d}}\n", 51); 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("[-w] WMA1, 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"); 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(0 == pthread_create(&termThread, NULL, TermThreadFun, NULL)) 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); 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) { 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); fprintf(stderr, "{\"OUTPUT_OPEN\":{\"sts\":%d}}\n", commandRetVal); commandRetVal = g_player->playback->Command(g_player, PLAYBACK_PLAY, NULL); fprintf(stderr, "{\"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 && 0 == PlaybackDieNow(0)) { /* we made fgets non blocking */ if( NULL == fgets(argvBuff, sizeof(argvBuff)-1 , stdin) ) { /* wait for data - max 1s */ kbhit(); continue; } if(0 == argvBuff[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); fprintf(stderr, "{\"PLAYBACK_STOP\":{\"sts\":%d}}\n", commandRetVal); break; } case 'c': { commandRetVal = g_player->playback->Command(g_player, PLAYBACK_CONTINUE, NULL); fprintf(stderr, "{\"PLAYBACK_CONTINUE\":{\"sts\":%d}}\n", commandRetVal); break; } case 'p': { commandRetVal = g_player->playback->Command(g_player, PLAYBACK_PAUSE, NULL); fprintf(stderr, "{\"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); fprintf(stderr, "{\"PLAYBACK_SLOWMOTION\":{\"speed\":%d, \"sts\":%d}}\n", speed, commandRetVal); break; } case 'o': { int flags = 0; if( 1 == sscanf(argvBuff+1, "%d", &flags) ) { progressive_playback_set(flags); fprintf(stderr, "{\"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); fprintf(stderr, "{\"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); fprintf(stderr, "{\"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); fprintf(stderr, "{\"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); fprintf(stderr, "{\"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 (0 == commandRetVal) { fprintf(stderr, "{\"J\":{\"ms\":%"PRId64"}}\n", pts / 90); } if(0 == commandRetVal || force) { commandRetVal = g_player->playback->Command(g_player, PLAYBACK_LENGTH, (void*)&length); fprintf(stderr, "{\"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); fprintf(stderr, "{\"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); fprintf(stderr, "{\"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 (0 == commandRetVal) { int64_t lastPts = 0; commandRetVal = 1; if (g_player->container && g_player->container->selectedContainer) { commandRetVal = g_player->container->selectedContainer->Command(g_player->container, CONTAINER_LAST_PTS, &lastPts); } if (0 == commandRetVal && lastPts != INVALID_PTS_VALUE) { fprintf(stderr, "{\"J\":{\"ms\":%"PRId64",\"lms\":%"PRId64"}}\n", pts / 90, lastPts / 90); } else { fprintf(stderr, "{\"J\":{\"ms\":%"PRId64"}}\n", pts / 90); } } break; } case 'i': { PlaybackHandler_t *ptrP = g_player->playback; if(ptrP) { fprintf(stderr, "{\"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) ); fprintf(stderr, "\"BackWard\":%d, \"SlowMotion\":%d, \"Speed\":%d, \"AVSync\":%d,", ptrP->BackWard, ptrP->SlowMotion, ptrP->Speed, ptrP->AVSync); fprintf(stderr, " \"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; fprintf(stderr, "{\"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 && 1 == write(g_pfd[1], "x", 1)) { pthread_join(termThread, NULL); } pthread_mutex_destroy(&playbackStartMtx); close(g_pfd[0]); close(g_pfd[1]); exit(0); }
//flag 0: start direct //flag 1: from thread static void ffmpeg_filler(Context_t *context, int32_t id, int32_t* inpause, int32_t flag) { int32_t len = 0; int32_t rwdiff = ffmpeg_buf_size; uint8_t buf[FILLBUFPAKET]; if (ffmpeg_read_org == NULL || ffmpeg_seek_org == NULL) { ffmpeg_err("ffmpeg_read_org or ffmpeg_seek_org is NULL\n"); return; } while ((flag == 0 && avContextTab[0] != NULL && avContextTab[0]->pb != NULL && rwdiff > FILLBUFDIFF) || (flag == 1 && hasfillerThreadStarted[id] == 1 && avContextTab[0] != NULL && avContextTab[0]->pb != NULL && rwdiff > FILLBUFDIFF)) { if ( 0 == PlaybackDieNow(0)) { break; } if (flag == 0 && ffmpeg_buf_stop == 1) { ffmpeg_buf_stop = 0; break; } getfillerMutex(__FILE__, __FUNCTION__,__LINE__); //do a seek if (ffmpeg_do_seek != 0) { ffmpeg_do_seek_ret = ffmpeg_seek_org(avContextTab[0]->pb->opaque, avContextTab[0]->pb->pos + ffmpeg_do_seek, SEEK_SET); if (ffmpeg_do_seek_ret >= 0) { ffmpeg_buf_write = ffmpeg_buf; ffmpeg_buf_read = ffmpeg_buf; } ffmpeg_do_seek = 0; } if (ffmpeg_buf_read == ffmpeg_buf_write) { ffmpeg_buf_valid_size = 0; rwdiff = ffmpeg_buf_size; } if (ffmpeg_buf_read < ffmpeg_buf_write) { rwdiff = (ffmpeg_buf + ffmpeg_buf_size) - ffmpeg_buf_write; rwdiff += ffmpeg_buf_read - ffmpeg_buf; } if (ffmpeg_buf_read > ffmpeg_buf_write) { rwdiff = ffmpeg_buf_read - ffmpeg_buf_write; } int32_t size = FILLBUFPAKET; if (rwdiff - FILLBUFDIFF < size) { size = (rwdiff - FILLBUFDIFF); } if (ffmpeg_buf_write + size > ffmpeg_buf + ffmpeg_buf_size) { size = (ffmpeg_buf + ffmpeg_buf_size) - ffmpeg_buf_write; } if (ffmpeg_buf_write == ffmpeg_buf + ffmpeg_buf_size) { ffmpeg_buf_write = ffmpeg_buf; } releasefillerMutex(__FILE__, __FUNCTION__,__LINE__); if (size > 0) { if (flag == 1 && hasfillerThreadStarted[id] == 2) { break; } len = ffmpeg_read_org(avContextTab[0]->pb->opaque, buf, size); if (flag == 1 && hasfillerThreadStarted[id] == 2) { break; } ffmpeg_printf(20, "buffer-status (free buffer=%d)\n", rwdiff - FILLBUFDIFF - len); getfillerMutex(__FILE__, __FUNCTION__,__LINE__); if (len > 0) { memcpy(ffmpeg_buf_write, buf, len); ffmpeg_buf_write += len; } else { releasefillerMutex(__FILE__, __FUNCTION__,__LINE__); ffmpeg_err("read not ok ret=%d\n", len); break; } releasefillerMutex(__FILE__, __FUNCTION__,__LINE__); } else { //on long pause the server close the connection, so we use seek to reconnect if (context != NULL && context->playback != NULL && inpause != NULL) { if ((*inpause) == 0 && context->playback->isPaused) { (*inpause) = 1; } else if ((*inpause) == 1 && !context->playback->isPaused) { int32_t buflen = 0; (*inpause) = 0; getfillerMutex(__FILE__, __FUNCTION__,__LINE__); if (ffmpeg_buf_read < ffmpeg_buf_write) { buflen = ffmpeg_buf_write - ffmpeg_buf_read; } if (ffmpeg_buf_read > ffmpeg_buf_write) { buflen = (ffmpeg_buf + ffmpeg_buf_size) - ffmpeg_buf_read; buflen += ffmpeg_buf_write - ffmpeg_buf; } ffmpeg_seek_org(avContextTab[0]->pb->opaque, avContextTab[0]->pb->pos + buflen, SEEK_SET); releasefillerMutex(__FILE__, __FUNCTION__,__LINE__); } } } } }