mirror of
https://github.com/tuxbox-neutrino/libstb-hal.git
synced 2025-08-26 23:13:16 +02:00
libeplayer3/ffmpeg: rearrange seeking code
This commit is contained in:
@@ -98,7 +98,6 @@ static const char* FILENAME = "container_ffmpeg.c";
|
|||||||
/* Varaibles */
|
/* Varaibles */
|
||||||
/* ***************************** */
|
/* ***************************** */
|
||||||
|
|
||||||
static pthread_mutex_t mutex;
|
|
||||||
|
|
||||||
static pthread_t PlayThread;
|
static pthread_t PlayThread;
|
||||||
static int hasPlayThreadStarted = 0;
|
static int hasPlayThreadStarted = 0;
|
||||||
@@ -109,11 +108,7 @@ static unsigned char isContainerRunning = 0;
|
|||||||
|
|
||||||
static long long int latestPts = 0;
|
static long long int latestPts = 0;
|
||||||
|
|
||||||
static int restart_audio_resampling = 0;
|
float seek_sec_abs = 0.0, seek_sec_rel = 0.0;
|
||||||
static off_t seek_target_bytes = 0;
|
|
||||||
static int do_seek_target_bytes = 0;
|
|
||||||
static int64_t seek_target_seconds = 0.0;
|
|
||||||
static int do_seek_target_seconds = 0;
|
|
||||||
|
|
||||||
/* ***************************** */
|
/* ***************************** */
|
||||||
/* Prototypes */
|
/* Prototypes */
|
||||||
@@ -124,31 +119,6 @@ static int container_ffmpeg_seek(Context_t *context, float sec, int absolute);
|
|||||||
/* MISC Functions */
|
/* MISC Functions */
|
||||||
/* ***************************** */
|
/* ***************************** */
|
||||||
|
|
||||||
static int mutexInitialized = 0;
|
|
||||||
|
|
||||||
static void initMutex(void)
|
|
||||||
{
|
|
||||||
pthread_mutex_init(&mutex, NULL);
|
|
||||||
mutexInitialized = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void getMutex(const char *filename __attribute__((unused)), const char *function __attribute__((unused)), int line) {
|
|
||||||
ffmpeg_printf(100, "::%d requesting mutex\n", line);
|
|
||||||
|
|
||||||
if (!mutexInitialized)
|
|
||||||
initMutex();
|
|
||||||
|
|
||||||
pthread_mutex_lock(&mutex);
|
|
||||||
|
|
||||||
ffmpeg_printf(100, "::%d received mutex\n", line);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void releaseMutex(const char *filename __attribute__((unused)), const const char *function __attribute__((unused)), int line) {
|
|
||||||
pthread_mutex_unlock(&mutex);
|
|
||||||
|
|
||||||
ffmpeg_printf(100, "::%d released mutex\n", line);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char* Codec2Encoding(AVCodecContext *codec, int* version)
|
static char* Codec2Encoding(AVCodecContext *codec, int* version)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Codec ID: %ld (%.8lx)\n", (long)codec->codec_id, (long)codec->codec_id);
|
fprintf(stderr, "Codec ID: %ld (%.8lx)\n", (long)codec->codec_id, (long)codec->codec_id);
|
||||||
@@ -326,6 +296,9 @@ static void FFMPEGThread(Context_t *context) {
|
|||||||
strncpy(threadname, __func__, sizeof(threadname));
|
strncpy(threadname, __func__, sizeof(threadname));
|
||||||
threadname[16] = 0;
|
threadname[16] = 0;
|
||||||
prctl (PR_SET_NAME, (unsigned long)&threadname);
|
prctl (PR_SET_NAME, (unsigned long)&threadname);
|
||||||
|
|
||||||
|
hasPlayThreadStarted = 1;
|
||||||
|
|
||||||
int64_t lastPts = -1, currentVideoPts = -1, currentAudioPts = -1, showtime = 0, bofcount = 0;
|
int64_t lastPts = -1, currentVideoPts = -1, currentAudioPts = -1, showtime = 0, bofcount = 0;
|
||||||
off_t lastPos = -1;
|
off_t lastPos = -1;
|
||||||
AudioVideoOut_t avOut;
|
AudioVideoOut_t avOut;
|
||||||
@@ -335,6 +308,7 @@ static void FFMPEGThread(Context_t *context) {
|
|||||||
int out_sample_rate = 44100;
|
int out_sample_rate = 44100;
|
||||||
int out_channels = 2;
|
int out_channels = 2;
|
||||||
uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
|
uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
|
||||||
|
int restart_audio_resampling = 0;
|
||||||
|
|
||||||
ffmpeg_printf(10, "\n");
|
ffmpeg_printf(10, "\n");
|
||||||
|
|
||||||
@@ -355,28 +329,41 @@ static void FFMPEGThread(Context_t *context) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (context->playback->isSeeking) {
|
if (!context->playback || !context->playback->isPlaying)
|
||||||
ffmpeg_printf(10, "seeking\n");
|
|
||||||
|
|
||||||
usleep(100000);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMutex(FILENAME, __FUNCTION__,__LINE__);
|
|
||||||
|
|
||||||
if (!context->playback || !context->playback->isPlaying) {
|
|
||||||
releaseMutex(FILENAME, __FUNCTION__,__LINE__);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if (context->playback->BackWard && av_gettime() >= showtime)
|
int seek_target_flag = 0;
|
||||||
{
|
int64_t seek_target = INT64_MIN;
|
||||||
|
|
||||||
|
if (seek_sec_rel != 0.0) {
|
||||||
|
if (avContext->iformat->flags & AVFMT_TS_DISCONT) {
|
||||||
|
float br = (avContext->bit_rate) ? br = avContext->bit_rate / 8.0 : 180000.0;
|
||||||
|
seek_target_flag = AVSEEK_FLAG_BYTE;
|
||||||
|
seek_target = avio_tell(avContext->pb) + seek_sec_rel * br;
|
||||||
|
} else {
|
||||||
|
lastPts = (currentVideoPts > 0) ? currentVideoPts : currentAudioPts;
|
||||||
|
if (lastPts > 0)
|
||||||
|
seek_target = ((currentVideoPts > 0) ? currentVideoPts : currentAudioPts) + seek_sec_rel * AV_TIME_BASE;
|
||||||
|
}
|
||||||
|
seek_sec_rel = 0.0;
|
||||||
|
lastPts = -1;
|
||||||
|
lastPos = -1;
|
||||||
|
} else if (seek_sec_abs != 0.0) {
|
||||||
|
if (avContext->iformat->flags & AVFMT_TS_DISCONT) {
|
||||||
|
float br = (avContext->bit_rate) ? br = avContext->bit_rate / 8.0 : 180000.0;
|
||||||
|
seek_target_flag = AVSEEK_FLAG_BYTE;
|
||||||
|
seek_target = seek_sec_abs * br;
|
||||||
|
} else {
|
||||||
|
seek_target = seek_sec_abs * AV_TIME_BASE;
|
||||||
|
}
|
||||||
|
seek_sec_abs = 0.0;
|
||||||
|
lastPts = -1;
|
||||||
|
lastPos = -1;
|
||||||
|
} else if (context->playback->BackWard && av_gettime() >= showtime) {
|
||||||
context->output->Command(context, OUTPUT_CLEAR, "video");
|
context->output->Command(context, OUTPUT_CLEAR, "video");
|
||||||
|
|
||||||
if(bofcount == 1)
|
if(bofcount == 1) {
|
||||||
{
|
|
||||||
showtime = av_gettime();
|
showtime = av_gettime();
|
||||||
releaseMutex(FILENAME, __FUNCTION__,__LINE__);
|
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -391,20 +378,17 @@ static void FFMPEGThread(Context_t *context) {
|
|||||||
br = avContext->bit_rate / 8.0;
|
br = avContext->bit_rate / 8.0;
|
||||||
else
|
else
|
||||||
br = 180000.0;
|
br = 180000.0;
|
||||||
do_seek_target_seconds = 0;
|
|
||||||
do_seek_target_bytes = 1;
|
|
||||||
lastPos += context->playback->Speed * 8 * br;
|
lastPos += context->playback->Speed * 8 * br;
|
||||||
seek_target_bytes = lastPos;
|
seek_target_flag = AVSEEK_FLAG_BYTE;
|
||||||
|
seek_target = lastPos;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (lastPts < 0)
|
if (lastPts < 0)
|
||||||
lastPts = (currentVideoPts > 0) ? currentVideoPts : currentAudioPts;
|
lastPts = (currentVideoPts > 0) ? currentVideoPts : currentAudioPts;
|
||||||
|
|
||||||
if (lastPts > 0) {
|
if (lastPts > 0) {
|
||||||
do_seek_target_seconds = 1;
|
|
||||||
do_seek_target_bytes = 0;
|
|
||||||
lastPts += context->playback->Speed * 8 * AV_TIME_BASE;
|
lastPts += context->playback->Speed * 8 * AV_TIME_BASE;
|
||||||
seek_target_seconds = lastPts;
|
seek_target = lastPts;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
showtime = av_gettime() + 300000; //jump back every 300ms
|
showtime = av_gettime() + 300000; //jump back every 300ms
|
||||||
@@ -414,21 +398,15 @@ static void FFMPEGThread(Context_t *context) {
|
|||||||
lastPts = -1, lastPos = -1;
|
lastPts = -1, lastPos = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_seek_target_seconds || do_seek_target_bytes) {
|
if (seek_target > INT64_MIN) {
|
||||||
int res;
|
int res;
|
||||||
if (do_seek_target_seconds) {
|
if (seek_target < 0)
|
||||||
if (seek_target_seconds < 0)
|
seek_target = 0;
|
||||||
seek_target_seconds = 0;
|
res = avformat_seek_file(avContext, -1, INT64_MIN, seek_target, INT64_MAX, seek_target_flag);
|
||||||
res = avformat_seek_file(avContext, -1, INT64_MIN, seek_target_seconds, INT64_MAX, 0);
|
|
||||||
} else {
|
|
||||||
if (seek_target_bytes < 0)
|
|
||||||
seek_target_bytes = 0;
|
|
||||||
res = avformat_seek_file(avContext, -1, INT64_MIN, seek_target_bytes, INT64_MAX, AVSEEK_FLAG_BYTE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res < 0 && context->playback->BackWard)
|
if (res < 0 && context->playback->BackWard)
|
||||||
bofcount = 1;
|
bofcount = 1;
|
||||||
do_seek_target_seconds = do_seek_target_bytes = 0;
|
seek_target = INT64_MIN;
|
||||||
restart_audio_resampling = 1;
|
restart_audio_resampling = 1;
|
||||||
latestPts = 0;
|
latestPts = 0;
|
||||||
|
|
||||||
@@ -437,7 +415,6 @@ static void FFMPEGThread(Context_t *context) {
|
|||||||
for (i = 0; i < avContext->nb_streams; i++)
|
for (i = 0; i < avContext->nb_streams; i++)
|
||||||
if (avContext->streams[i]->codec && avContext->streams[i]->codec->codec)
|
if (avContext->streams[i]->codec && avContext->streams[i]->codec->codec)
|
||||||
avcodec_flush_buffers(avContext->streams[i]->codec);
|
avcodec_flush_buffers(avContext->streams[i]->codec);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AVPacket packet;
|
AVPacket packet;
|
||||||
@@ -446,13 +423,11 @@ static void FFMPEGThread(Context_t *context) {
|
|||||||
int av_res = av_read_frame(avContext, &packet);
|
int av_res = av_read_frame(avContext, &packet);
|
||||||
if (av_res == AVERROR(EAGAIN)) {
|
if (av_res == AVERROR(EAGAIN)) {
|
||||||
av_free_packet(&packet);
|
av_free_packet(&packet);
|
||||||
releaseMutex(FILENAME, __FUNCTION__,__LINE__);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (av_res) { // av_read_frame failed
|
if (av_res) { // av_read_frame failed
|
||||||
ffmpeg_err("no data ->end of file reached ?\n");
|
ffmpeg_err("no data ->end of file reached ?\n");
|
||||||
av_free_packet(&packet);
|
av_free_packet(&packet);
|
||||||
releaseMutex(FILENAME, __FUNCTION__,__LINE__);
|
|
||||||
break; // while
|
break; // while
|
||||||
}
|
}
|
||||||
long long int pts;
|
long long int pts;
|
||||||
@@ -844,7 +819,6 @@ static void FFMPEGThread(Context_t *context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
av_free_packet(&packet);
|
av_free_packet(&packet);
|
||||||
releaseMutex(FILENAME, __FUNCTION__,__LINE__);
|
|
||||||
} /* while */
|
} /* while */
|
||||||
|
|
||||||
if (context && context->playback && context->output && context->playback->abortRequested)
|
if (context && context->playback && context->output && context->playback->abortRequested)
|
||||||
@@ -855,7 +829,11 @@ static void FFMPEGThread(Context_t *context) {
|
|||||||
if (decoded_frame)
|
if (decoded_frame)
|
||||||
avcodec_free_frame(&decoded_frame);
|
avcodec_free_frame(&decoded_frame);
|
||||||
|
|
||||||
|
avformat_close_input(&avContext);
|
||||||
|
|
||||||
hasPlayThreadStarted = 0;
|
hasPlayThreadStarted = 0;
|
||||||
|
if (context->playback)
|
||||||
|
context->playback->isPlaying = 0;
|
||||||
|
|
||||||
ffmpeg_printf(10, "terminating\n");
|
ffmpeg_printf(10, "terminating\n");
|
||||||
}
|
}
|
||||||
@@ -1380,19 +1358,14 @@ static int container_ffmpeg_play(Context_t *context)
|
|||||||
|
|
||||||
if((error = pthread_create(&PlayThread, &attr, (void *)&FFMPEGThread, context)) != 0) {
|
if((error = pthread_create(&PlayThread, &attr, (void *)&FFMPEGThread, context)) != 0) {
|
||||||
ffmpeg_printf(10, "Error creating thread, error:%d:%s\n", error,strerror(error));
|
ffmpeg_printf(10, "Error creating thread, error:%d:%s\n", error,strerror(error));
|
||||||
|
|
||||||
hasPlayThreadStarted = 0;
|
|
||||||
ret = cERR_CONTAINER_FFMPEG_ERR;
|
ret = cERR_CONTAINER_FFMPEG_ERR;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ffmpeg_printf(10, "Created thread\n");
|
ffmpeg_printf(10, "Created thread\n");
|
||||||
|
|
||||||
hasPlayThreadStarted = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ffmpeg_printf(10, "A thread already exists!\n");
|
ffmpeg_printf(10, "A thread already exists!\n");
|
||||||
|
|
||||||
ret = cERR_CONTAINER_FFMPEG_ERR;
|
ret = cERR_CONTAINER_FFMPEG_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1403,7 +1376,6 @@ static int container_ffmpeg_play(Context_t *context)
|
|||||||
|
|
||||||
static int container_ffmpeg_stop(Context_t *context) {
|
static int container_ffmpeg_stop(Context_t *context) {
|
||||||
int ret = cERR_CONTAINER_FFMPEG_NO_ERROR;
|
int ret = cERR_CONTAINER_FFMPEG_NO_ERROR;
|
||||||
int wait_time = 20;
|
|
||||||
|
|
||||||
ffmpeg_printf(10, "\n");
|
ffmpeg_printf(10, "\n");
|
||||||
|
|
||||||
@@ -1412,124 +1384,31 @@ static int container_ffmpeg_stop(Context_t *context) {
|
|||||||
ffmpeg_err("Container not running\n");
|
ffmpeg_err("Container not running\n");
|
||||||
return cERR_CONTAINER_FFMPEG_ERR;
|
return cERR_CONTAINER_FFMPEG_ERR;
|
||||||
}
|
}
|
||||||
if (context->playback)
|
if (context->playback) {
|
||||||
context->playback->isPlaying = 0;
|
context->playback->isPlaying = 0;
|
||||||
|
context->playback->abortRequested = 1;
|
||||||
|
}
|
||||||
|
|
||||||
while ( (hasPlayThreadStarted != 0) && (--wait_time) > 0 ) {
|
while (hasPlayThreadStarted != 0)
|
||||||
ffmpeg_printf(10, "Waiting for ffmpeg thread to terminate itself, will try another %d times\n", wait_time);
|
|
||||||
|
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
}
|
|
||||||
|
|
||||||
if (wait_time == 0) {
|
|
||||||
ffmpeg_err( "Timeout waiting for thread!\n");
|
|
||||||
|
|
||||||
ret = cERR_CONTAINER_FFMPEG_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasPlayThreadStarted = 0;
|
|
||||||
terminating = 1;
|
terminating = 1;
|
||||||
|
|
||||||
getMutex(FILENAME, __FUNCTION__,__LINE__);
|
|
||||||
|
|
||||||
if (avContext)
|
if (avContext)
|
||||||
avformat_close_input(&avContext);
|
avformat_close_input(&avContext);
|
||||||
|
|
||||||
isContainerRunning = 0;
|
isContainerRunning = 0;
|
||||||
avformat_network_deinit();
|
avformat_network_deinit();
|
||||||
|
|
||||||
releaseMutex(FILENAME, __FUNCTION__,__LINE__);
|
|
||||||
|
|
||||||
ffmpeg_printf(10, "ret %d\n", ret);
|
ffmpeg_printf(10, "ret %d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int container_ffmpeg_seek(Context_t *context, float sec, int absolute) {
|
static int container_ffmpeg_seek(Context_t *context __attribute__((unused)), float sec, int absolute) {
|
||||||
Track_t * videoTrack = NULL;
|
|
||||||
Track_t * audioTrack = NULL;
|
|
||||||
Track_t * current = NULL;
|
|
||||||
|
|
||||||
if (absolute) {
|
|
||||||
ffmpeg_printf(10, "goto %f sec\n", sec);
|
|
||||||
|
|
||||||
if (sec < 0.0)
|
|
||||||
sec = 0.0;
|
|
||||||
} else {
|
|
||||||
ffmpeg_printf(10, "seeking %f sec\n", sec);
|
|
||||||
|
|
||||||
if (sec == 0.0)
|
|
||||||
{
|
|
||||||
ffmpeg_err("sec = 0.0 ignoring\n");
|
|
||||||
return cERR_CONTAINER_FFMPEG_ERR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context->manager->video->Command(context, MANAGER_GET_TRACK, &videoTrack);
|
|
||||||
context->manager->audio->Command(context, MANAGER_GET_TRACK, &audioTrack);
|
|
||||||
|
|
||||||
if (videoTrack != NULL)
|
|
||||||
current = videoTrack;
|
|
||||||
else if (audioTrack != NULL)
|
|
||||||
current = audioTrack;
|
|
||||||
|
|
||||||
if (current == NULL) {
|
|
||||||
ffmpeg_err( "no track available to seek\n");
|
|
||||||
return cERR_CONTAINER_FFMPEG_ERR;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMutex(FILENAME, __FUNCTION__,__LINE__);
|
|
||||||
|
|
||||||
if (!context->playback || !context->playback->isPlaying) {
|
|
||||||
releaseMutex(FILENAME, __FUNCTION__,__LINE__);
|
|
||||||
return cERR_CONTAINER_FFMPEG_NO_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
ffmpeg_printf(10, "iformat->flags %d\n", avContext->iformat->flags);
|
|
||||||
|
|
||||||
if (avContext->iformat->flags & AVFMT_TS_DISCONT)
|
|
||||||
{
|
|
||||||
/* konfetti: for ts streams seeking frame per seconds does not work (why?).
|
|
||||||
* I take this algo partly from ffplay.c.
|
|
||||||
*
|
|
||||||
* seeking per HTTP does still not work very good. forward seeks everytime
|
|
||||||
* about 10 seconds, backward does not work.
|
|
||||||
*/
|
|
||||||
|
|
||||||
off_t pos = avio_tell(avContext->pb);
|
|
||||||
|
|
||||||
ffmpeg_printf(10, "pos %lld %d\n", pos, avContext->bit_rate);
|
|
||||||
|
|
||||||
if (avContext->bit_rate)
|
|
||||||
{
|
|
||||||
sec *= avContext->bit_rate / 8.0;
|
|
||||||
ffmpeg_printf(10, "bit_rate %d\n", avContext->bit_rate);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sec *= 180000.0;
|
|
||||||
}
|
|
||||||
if (absolute)
|
if (absolute)
|
||||||
pos = sec;
|
seek_sec_abs = sec, seek_sec_rel = 0.0;
|
||||||
else
|
else
|
||||||
pos += sec;
|
seek_sec_abs = 0.0, seek_sec_rel = sec;
|
||||||
if (pos < 0)
|
|
||||||
pos = 0;
|
|
||||||
|
|
||||||
ffmpeg_printf(10, "1. seeking to position %lld bytes ->sec %f\n", pos, sec);
|
|
||||||
|
|
||||||
seek_target_bytes = pos;
|
|
||||||
do_seek_target_bytes = 1;
|
|
||||||
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
if (!absolute)
|
|
||||||
sec += ((float) current->pts / 90000.0f);
|
|
||||||
ffmpeg_printf(10, "2. seeking to position %f sec ->time base %f %d\n", sec, av_q2d(((AVStream*) current->stream)->time_base), AV_TIME_BASE);
|
|
||||||
|
|
||||||
seek_target_seconds = sec * AV_TIME_BASE;
|
|
||||||
do_seek_target_seconds = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
releaseMutex(FILENAME, __FUNCTION__,__LINE__);
|
|
||||||
return cERR_CONTAINER_FFMPEG_NO_ERROR;
|
return cERR_CONTAINER_FFMPEG_NO_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user