Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/imbuf/intern/anim_movie.c')
-rw-r--r--source/blender/imbuf/intern/anim_movie.c159
1 files changed, 105 insertions, 54 deletions
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index 47514308ae4..dbca16ca82b 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -425,6 +425,7 @@ static int startavi(struct anim *anim)
}
anim->duration_in_frames = anim->avi->header->TotalFrames;
+ anim->start_offset = 0.0f;
anim->params = NULL;
anim->x = anim->avi->header->Width;
@@ -597,6 +598,13 @@ static int startffmpeg(struct anim *anim)
return -1;
}
+ double video_start = 0;
+ double pts_time_base = av_q2d(video_stream->time_base);
+
+ if (video_stream->start_time != AV_NOPTS_VALUE) {
+ video_start = video_stream->start_time * pts_time_base;
+ }
+
frame_rate = av_guess_frame_rate(pFormatCtx, video_stream, NULL);
anim->duration_in_frames = 0;
@@ -616,10 +624,49 @@ static int startffmpeg(struct anim *anim)
}
}
}
- /* Fall back to the container. */
+ /* Fall back to manually estimating the video stream duration.
+ * This is because the video stream duration can be shorter than the pFormatCtx->duration.
+ */
if (anim->duration_in_frames == 0) {
- anim->duration_in_frames = (int)(pFormatCtx->duration * av_q2d(frame_rate) / AV_TIME_BASE +
- 0.5f);
+ double stream_dur;
+
+ if (video_stream->duration != AV_NOPTS_VALUE) {
+ stream_dur = video_stream->duration * pts_time_base;
+ }
+ else {
+ double audio_start = 0;
+
+ /* Find audio stream to guess the duration of the video.
+ * Sometimes the audio AND the video stream have a start offset.
+ * The difference between these is the offset we want to use to
+ * calculate the video duration.
+ */
+ for (i = 0; i < pFormatCtx->nb_streams; i++) {
+ if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
+ AVStream *audio_stream = pFormatCtx->streams[i];
+ if (audio_stream->start_time != AV_NOPTS_VALUE) {
+ audio_start = audio_stream->start_time * av_q2d(audio_stream->time_base);
+ }
+ break;
+ }
+ }
+
+ if (video_start > audio_start) {
+ stream_dur = (double)pFormatCtx->duration / AV_TIME_BASE - (video_start - audio_start);
+ }
+ else {
+ /* The video stream starts before or at the same time as the audio stream!
+ * We have to assume that the video stream is as long as the full pFormatCtx->duration.
+ */
+ stream_dur = (double)pFormatCtx->duration / AV_TIME_BASE;
+ }
+ }
+ anim->duration_in_frames = (int)(stream_dur * av_q2d(frame_rate) + 0.5f);
+ }
+
+ double ctx_start = 0;
+ if (pFormatCtx->start_time != AV_NOPTS_VALUE) {
+ ctx_start = (double)pFormatCtx->start_time / AV_TIME_BASE;
}
frs_num = frame_rate.num;
@@ -634,6 +681,9 @@ static int startffmpeg(struct anim *anim)
anim->frs_sec = frs_num;
anim->frs_sec_base = frs_den;
+ /* Save the relative start time for the video. IE the start time in relation to where playback
+ * starts. */
+ anim->start_offset = video_start - ctx_start;
anim->params = 0;
@@ -1019,33 +1069,21 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx)
return false;
}
-static int64_t ffmpeg_get_seek_pos(struct anim *anim, int position)
+static int64_t ffmpeg_get_seek_pts(struct anim *anim, int64_t pts_to_search)
{
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
- double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
- int64_t st_time = anim->pFormatCtx->start_time;
- int64_t pos = (int64_t)(position)*AV_TIME_BASE;
- /* Step back half a time base position to make sure that we get the requested
- * frame and not the one after it.
+ AVRational frame_rate = v_st->r_frame_rate;
+ AVRational time_base = v_st->time_base;
+ double steps_per_frame = (double)(frame_rate.den * time_base.den) /
+ (double)(frame_rate.num * time_base.num);
+ /* Step back half a frame position to make sure that we get the requested
+ * frame and not the one after it. This is a workaround as ffmpeg will
+ * sometimes not seek to a frame after the requested pts even if
+ * AVSEEK_FLAG_BACKWARD is specified.
*/
- pos -= (AV_TIME_BASE / 2);
- pos /= frame_rate;
-
- av_log(anim->pFormatCtx,
- AV_LOG_DEBUG,
- "NO INDEX seek pos = %" PRId64 ", st_time = %" PRId64 "\n",
- pos,
- (st_time != AV_NOPTS_VALUE) ? st_time : 0);
-
- if (pos < 0) {
- pos = 0;
- }
-
- if (st_time != AV_NOPTS_VALUE) {
- pos += st_time;
- }
+ int64_t pts = pts_to_search - (steps_per_frame / 2);
- return pos;
+ return pts;
}
/* This gives us an estimate of which pts our requested frame will have.
@@ -1062,17 +1100,18 @@ static int64_t ffmpeg_get_pts_to_search(struct anim *anim,
pts_to_search = IMB_indexer_get_pts(tc_index, new_frame_index);
}
else {
- int64_t st_time = anim->pFormatCtx->start_time;
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
- AVRational frame_rate = av_guess_frame_rate(anim->pFormatCtx, v_st, NULL);
+ int64_t start_pts = v_st->start_time;
+ AVRational frame_rate = v_st->r_frame_rate;
AVRational time_base = v_st->time_base;
- int64_t steps_per_frame = (frame_rate.den * time_base.den) / (frame_rate.num * time_base.num);
- pts_to_search = position * steps_per_frame;
+ double steps_per_frame = (double)(frame_rate.den * time_base.den) /
+ (double)(frame_rate.num * time_base.num);
- if (st_time != AV_NOPTS_VALUE && st_time != 0) {
- int64_t start_frame = (double)st_time / AV_TIME_BASE * av_q2d(frame_rate);
- pts_to_search += start_frame * steps_per_frame;
+ pts_to_search = round(position * steps_per_frame);
+
+ if (start_pts != AV_NOPTS_VALUE) {
+ pts_to_search += start_pts;
}
}
return pts_to_search;
@@ -1156,23 +1195,29 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea
* decoded will be read. See https://trac.ffmpeg.org/ticket/1607 and
* https://developer.blender.org/T86944. */
static int ffmpeg_generic_seek_workaround(struct anim *anim,
- int64_t *requested_pos,
+ int64_t *requested_pts,
int64_t pts_to_search)
{
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
- double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
- int64_t current_pos = *requested_pos;
+ AVRational frame_rate = v_st->r_frame_rate;
+ AVRational time_base = v_st->time_base;
+
+ double steps_per_frame = (double)(frame_rate.den * time_base.den) /
+ (double)(frame_rate.num * time_base.num);
+
+ int64_t current_pts = *requested_pts;
int64_t offset = 0;
int64_t cur_pts, prev_pts = -1;
/* Step backward frame by frame until we find the key frame we are looking for. */
- while (current_pos != 0) {
- current_pos = *requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate);
- current_pos = max_ii(current_pos, 0);
+ while (current_pts != 0) {
+ current_pts = *requested_pts - (int64_t)round(offset * steps_per_frame);
+ current_pts = MAX2(current_pts, 0);
/* Seek to timestamp. */
- if (av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD) < 0) {
+ if (av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD) <
+ 0) {
break;
}
@@ -1198,21 +1243,22 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim,
/* We found the I-frame we were looking for! */
break;
}
- if (cur_pts == prev_pts) {
- /* We got the same key frame packet twice.
- * This probably means that we have hit the beginning of the stream. */
- break;
- }
+ }
+
+ if (cur_pts == prev_pts) {
+ /* We got the same key frame packet twice.
+ * This probably means that we have hit the beginning of the stream. */
+ break;
}
prev_pts = cur_pts;
offset++;
}
- *requested_pos = current_pos;
+ *requested_pts = current_pts;
/* Re-seek to timestamp that gave I-frame, so it can be read by decode function. */
- return av_seek_frame(anim->pFormatCtx, -1, current_pos, AVSEEK_FLAG_BACKWARD);
+ return av_seek_frame(anim->pFormatCtx, anim->videoStream, current_pts, AVSEEK_FLAG_BACKWARD);
}
/* Seek to last necessary key frame. */
@@ -1260,13 +1306,13 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim,
else {
/* We have to manually seek with ffmpeg to get to the key frame we want to start decoding from.
*/
- pos = ffmpeg_get_seek_pos(anim, position);
+ pos = ffmpeg_get_seek_pts(anim, pts_to_search);
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
AVFormatContext *format_ctx = anim->pFormatCtx;
if (format_ctx->iformat->read_seek2 || format_ctx->iformat->read_seek) {
- ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
+ ret = av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD);
}
else {
ret = ffmpeg_generic_seek_workaround(anim, &pos, pts_to_search);
@@ -1311,7 +1357,7 @@ static int ffmpeg_seek_to_key_frame(struct anim *anim,
anim->cur_key_frame_pts = gop_pts;
/* Seek back so we are at the correct position after we decoded a frame. */
- av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
+ av_seek_frame(anim->pFormatCtx, anim->videoStream, pos, AVSEEK_FLAG_BACKWARD);
}
}
@@ -1351,18 +1397,18 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
struct anim_index *tc_index = IMB_anim_open_index(anim, tc);
int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
AVStream *v_st = anim->pFormatCtx->streams[anim->videoStream];
- double frame_rate = av_q2d(av_guess_frame_rate(anim->pFormatCtx, v_st, NULL));
+ double frame_rate = av_q2d(v_st->r_frame_rate);
double pts_time_base = av_q2d(v_st->time_base);
- int64_t st_time = anim->pFormatCtx->start_time;
+ int64_t start_pts = v_st->start_time;
av_log(anim->pFormatCtx,
AV_LOG_DEBUG,
- "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, st_time=%" PRId64
+ "FETCH: looking for PTS=%" PRId64 " (pts_timebase=%g, frame_rate=%g, start_pts=%" PRId64
")\n",
(int64_t)pts_to_search,
pts_time_base,
frame_rate,
- st_time);
+ start_pts);
if (ffmpeg_pts_matches_last_frame(anim, pts_to_search)) {
av_log(anim->pFormatCtx,
@@ -1637,6 +1683,11 @@ int IMB_anim_get_duration(struct anim *anim, IMB_Timecode_Type tc)
return IMB_indexer_get_duration(idx);
}
+double IMD_anim_get_offset(struct anim *anim)
+{
+ return anim->start_offset;
+}
+
bool IMB_anim_get_fps(struct anim *anim, short *frs_sec, float *frs_sec_base, bool no_av_base)
{
double frs_sec_base_double;