|
|
|
@ -433,8 +433,7 @@ static int startavi(struct anim *anim)
|
|
|
|
|
anim->orientation = 0;
|
|
|
|
|
anim->framesize = anim->x * anim->y * 4;
|
|
|
|
|
|
|
|
|
|
anim->curposition = 0;
|
|
|
|
|
anim->preseek = 0;
|
|
|
|
|
anim->cur_position = 0;
|
|
|
|
|
|
|
|
|
|
# if 0
|
|
|
|
|
printf("x:%d y:%d size:%d interl:%d dur:%d\n",
|
|
|
|
@ -650,12 +649,12 @@ static int startffmpeg(struct anim *anim)
|
|
|
|
|
anim->orientation = 0;
|
|
|
|
|
anim->framesize = anim->x * anim->y * 4;
|
|
|
|
|
|
|
|
|
|
anim->curposition = -1;
|
|
|
|
|
anim->last_frame = 0;
|
|
|
|
|
anim->last_pts = -1;
|
|
|
|
|
anim->next_pts = -1;
|
|
|
|
|
anim->next_packet = av_packet_alloc();
|
|
|
|
|
anim->next_packet->stream_index = -1;
|
|
|
|
|
anim->cur_position = -1;
|
|
|
|
|
anim->cur_frame_final = 0;
|
|
|
|
|
anim->cur_pts = -1;
|
|
|
|
|
anim->cur_key_frame_pts = -1;
|
|
|
|
|
anim->cur_packet = av_packet_alloc();
|
|
|
|
|
anim->cur_packet->stream_index = -1;
|
|
|
|
|
|
|
|
|
|
anim->pFrame = av_frame_alloc();
|
|
|
|
|
anim->pFrameComplete = false;
|
|
|
|
@ -671,7 +670,7 @@ static int startffmpeg(struct anim *anim)
|
|
|
|
|
fprintf(stderr, "Could not allocate frame data.\n");
|
|
|
|
|
avcodec_free_context(&anim->pCodecCtx);
|
|
|
|
|
avformat_close_input(&anim->pFormatCtx);
|
|
|
|
|
av_packet_free(&anim->next_packet);
|
|
|
|
|
av_packet_free(&anim->cur_packet);
|
|
|
|
|
av_frame_free(&anim->pFrameRGB);
|
|
|
|
|
av_frame_free(&anim->pFrameDeinterlaced);
|
|
|
|
|
av_frame_free(&anim->pFrame);
|
|
|
|
@ -684,7 +683,7 @@ static int startffmpeg(struct anim *anim)
|
|
|
|
|
fprintf(stderr, "ffmpeg has changed alloc scheme ... ARGHHH!\n");
|
|
|
|
|
avcodec_free_context(&anim->pCodecCtx);
|
|
|
|
|
avformat_close_input(&anim->pFormatCtx);
|
|
|
|
|
av_packet_free(&anim->next_packet);
|
|
|
|
|
av_packet_free(&anim->cur_packet);
|
|
|
|
|
av_frame_free(&anim->pFrameRGB);
|
|
|
|
|
av_frame_free(&anim->pFrameDeinterlaced);
|
|
|
|
|
av_frame_free(&anim->pFrame);
|
|
|
|
@ -706,13 +705,6 @@ static int startffmpeg(struct anim *anim)
|
|
|
|
|
1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pCodecCtx->has_b_frames) {
|
|
|
|
|
anim->preseek = 25; /* FIXME: detect gopsize ... */
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
anim->preseek = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
anim->img_convert_ctx = sws_getContext(anim->x,
|
|
|
|
|
anim->y,
|
|
|
|
|
anim->pCodecCtx->pix_fmt,
|
|
|
|
@ -728,7 +720,7 @@ static int startffmpeg(struct anim *anim)
|
|
|
|
|
fprintf(stderr, "Can't transform color space??? Bailing out...\n");
|
|
|
|
|
avcodec_free_context(&anim->pCodecCtx);
|
|
|
|
|
avformat_close_input(&anim->pFormatCtx);
|
|
|
|
|
av_packet_free(&anim->next_packet);
|
|
|
|
|
av_packet_free(&anim->cur_packet);
|
|
|
|
|
av_frame_free(&anim->pFrameRGB);
|
|
|
|
|
av_frame_free(&anim->pFrameDeinterlaced);
|
|
|
|
|
av_frame_free(&anim->pFrame);
|
|
|
|
@ -769,13 +761,13 @@ static int startffmpeg(struct anim *anim)
|
|
|
|
|
/* postprocess the image in anim->pFrame and do color conversion
|
|
|
|
|
* and deinterlacing stuff.
|
|
|
|
|
*
|
|
|
|
|
* Output is anim->last_frame
|
|
|
|
|
* Output is anim->cur_frame_final
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static void ffmpeg_postprocess(struct anim *anim)
|
|
|
|
|
{
|
|
|
|
|
AVFrame *input = anim->pFrame;
|
|
|
|
|
ImBuf *ibuf = anim->last_frame;
|
|
|
|
|
ImBuf *ibuf = anim->cur_frame_final;
|
|
|
|
|
int filter_y = 0;
|
|
|
|
|
|
|
|
|
|
if (!anim->pFrameComplete) {
|
|
|
|
@ -902,7 +894,7 @@ static void ffmpeg_postprocess(struct anim *anim)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* decode one video frame also considering the packet read into next_packet */
|
|
|
|
|
/* decode one video frame also considering the packet read into cur_packet */
|
|
|
|
|
|
|
|
|
|
static int ffmpeg_decode_video_frame(struct anim *anim)
|
|
|
|
|
{
|
|
|
|
@ -910,40 +902,43 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
|
|
|
|
|
|
|
|
|
|
av_log(anim->pFormatCtx, AV_LOG_DEBUG, " DECODE VIDEO FRAME\n");
|
|
|
|
|
|
|
|
|
|
if (anim->next_packet->stream_index == anim->videoStream) {
|
|
|
|
|
av_packet_unref(anim->next_packet);
|
|
|
|
|
anim->next_packet->stream_index = -1;
|
|
|
|
|
if (anim->cur_packet->stream_index == anim->videoStream) {
|
|
|
|
|
av_packet_unref(anim->cur_packet);
|
|
|
|
|
anim->cur_packet->stream_index = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while ((rval = av_read_frame(anim->pFormatCtx, anim->next_packet)) >= 0) {
|
|
|
|
|
while ((rval = av_read_frame(anim->pFormatCtx, anim->cur_packet)) >= 0) {
|
|
|
|
|
av_log(anim->pFormatCtx,
|
|
|
|
|
AV_LOG_DEBUG,
|
|
|
|
|
"%sREAD: strID=%d (VID: %d) dts=%" PRId64 " pts=%" PRId64 " %s\n",
|
|
|
|
|
(anim->next_packet->stream_index == anim->videoStream) ? "->" : " ",
|
|
|
|
|
anim->next_packet->stream_index,
|
|
|
|
|
(anim->cur_packet->stream_index == anim->videoStream) ? "->" : " ",
|
|
|
|
|
anim->cur_packet->stream_index,
|
|
|
|
|
anim->videoStream,
|
|
|
|
|
(anim->next_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->dts,
|
|
|
|
|
(anim->next_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->next_packet->pts,
|
|
|
|
|
(anim->next_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
|
|
|
|
|
if (anim->next_packet->stream_index == anim->videoStream) {
|
|
|
|
|
(anim->cur_packet->dts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->dts,
|
|
|
|
|
(anim->cur_packet->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->cur_packet->pts,
|
|
|
|
|
(anim->cur_packet->flags & AV_PKT_FLAG_KEY) ? " KEY" : "");
|
|
|
|
|
if (anim->cur_packet->stream_index == anim->videoStream) {
|
|
|
|
|
anim->pFrameComplete = 0;
|
|
|
|
|
|
|
|
|
|
avcodec_send_packet(anim->pCodecCtx, anim->next_packet);
|
|
|
|
|
avcodec_send_packet(anim->pCodecCtx, anim->cur_packet);
|
|
|
|
|
anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
|
|
|
|
|
|
|
|
|
|
if (anim->pFrameComplete) {
|
|
|
|
|
anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame);
|
|
|
|
|
anim->cur_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame);
|
|
|
|
|
|
|
|
|
|
if (anim->pFrame->key_frame) {
|
|
|
|
|
anim->cur_key_frame_pts = anim->cur_pts;
|
|
|
|
|
}
|
|
|
|
|
av_log(anim->pFormatCtx,
|
|
|
|
|
AV_LOG_DEBUG,
|
|
|
|
|
" FRAME DONE: next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
|
|
|
|
|
" FRAME DONE: cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
|
|
|
|
|
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
|
|
|
|
|
(int64_t)anim->next_pts);
|
|
|
|
|
(int64_t)anim->cur_pts);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
av_packet_unref(anim->next_packet);
|
|
|
|
|
anim->next_packet->stream_index = -1;
|
|
|
|
|
av_packet_unref(anim->cur_packet);
|
|
|
|
|
anim->cur_packet->stream_index = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rval == AVERROR_EOF) {
|
|
|
|
@ -954,20 +949,23 @@ static int ffmpeg_decode_video_frame(struct anim *anim)
|
|
|
|
|
anim->pFrameComplete = avcodec_receive_frame(anim->pCodecCtx, anim->pFrame) == 0;
|
|
|
|
|
|
|
|
|
|
if (anim->pFrameComplete) {
|
|
|
|
|
anim->next_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame);
|
|
|
|
|
anim->cur_pts = av_get_pts_from_frame(anim->pFormatCtx, anim->pFrame);
|
|
|
|
|
|
|
|
|
|
if (anim->pFrame->key_frame) {
|
|
|
|
|
anim->cur_key_frame_pts = anim->cur_pts;
|
|
|
|
|
}
|
|
|
|
|
av_log(anim->pFormatCtx,
|
|
|
|
|
AV_LOG_DEBUG,
|
|
|
|
|
" FRAME DONE (after EOF): next_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
|
|
|
|
|
" FRAME DONE (after EOF): cur_pts=%" PRId64 ", guessed_pts=%" PRId64 "\n",
|
|
|
|
|
(anim->pFrame->pts == AV_NOPTS_VALUE) ? -1 : (int64_t)anim->pFrame->pts,
|
|
|
|
|
(int64_t)anim->next_pts);
|
|
|
|
|
(int64_t)anim->cur_pts);
|
|
|
|
|
rval = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (rval < 0) {
|
|
|
|
|
av_packet_unref(anim->next_packet);
|
|
|
|
|
anim->next_packet->stream_index = -1;
|
|
|
|
|
av_packet_unref(anim->cur_packet);
|
|
|
|
|
anim->cur_packet->stream_index = -1;
|
|
|
|
|
|
|
|
|
|
av_log(anim->pFormatCtx,
|
|
|
|
|
AV_LOG_ERROR,
|
|
|
|
@ -1021,6 +1019,35 @@ static int ffmpeg_seek_by_byte(AVFormatContext *pFormatCtx)
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int64_t ffmpeg_get_seek_pos(struct anim *anim, int position)
|
|
|
|
|
{
|
|
|
|
|
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.
|
|
|
|
|
*/
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int64_t ffmpeg_get_pts_to_search(struct anim *anim,
|
|
|
|
|
struct anim_index *tc_index,
|
|
|
|
|
int position)
|
|
|
|
@ -1045,77 +1072,60 @@ static int64_t ffmpeg_get_pts_to_search(struct anim *anim,
|
|
|
|
|
return pts_to_search;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check if the pts will get us the same frame that we already have in memory from last decode. */
|
|
|
|
|
static bool ffmpeg_pts_matches_last_frame(struct anim *anim, int64_t pts_to_search)
|
|
|
|
|
{
|
|
|
|
|
return anim->last_frame && anim->last_pts <= pts_to_search && anim->next_pts > pts_to_search;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Requested video frame is expected to be found within different GOP as last decoded frame.
|
|
|
|
|
* Seeking to new position and scanning is fastest way to get requested frame.
|
|
|
|
|
* Check whether ffmpeg_can_scan() and ffmpeg_pts_matches_last_frame() is false before using this
|
|
|
|
|
* function. */
|
|
|
|
|
static bool ffmpeg_can_seek(struct anim *anim, int position)
|
|
|
|
|
{
|
|
|
|
|
return position != anim->curposition + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Requested video frame is expected to be found within same GOP as last decoded frame.
|
|
|
|
|
* Decoding frames in sequence until frame matches requested one is fastest way to get it. */
|
|
|
|
|
static bool ffmpeg_can_scan(struct anim *anim, int position, struct anim_index *tc_index)
|
|
|
|
|
{
|
|
|
|
|
if (position > anim->curposition + 1 && anim->preseek && !tc_index &&
|
|
|
|
|
position - (anim->curposition + 1) < anim->preseek) {
|
|
|
|
|
return true;
|
|
|
|
|
if (anim->pFrame && anim->cur_frame_final) {
|
|
|
|
|
return labs(anim->cur_pts - pts_to_search) < anim->pFrame->pkt_duration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tc_index == NULL) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
|
|
|
|
|
int old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->curposition);
|
|
|
|
|
return IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ffmpeg_is_first_frame_decode(struct anim *anim, int position)
|
|
|
|
|
{
|
|
|
|
|
return position == 0 && anim->curposition == -1;
|
|
|
|
|
return position == 0 && anim->cur_position == -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Decode frames one by one until its PTS matches pts_to_search. */
|
|
|
|
|
static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_search)
|
|
|
|
|
{
|
|
|
|
|
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within preseek interval\n");
|
|
|
|
|
|
|
|
|
|
/* there seem to exist *very* silly GOP lengths out in the wild... */
|
|
|
|
|
int count = 1000;
|
|
|
|
|
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: within current GOP\n");
|
|
|
|
|
|
|
|
|
|
av_log(anim->pFormatCtx,
|
|
|
|
|
AV_LOG_DEBUG,
|
|
|
|
|
"SCAN start: considering pts=%" PRId64 " in search of %" PRId64 "\n",
|
|
|
|
|
(int64_t)anim->next_pts,
|
|
|
|
|
(int64_t)anim->cur_pts,
|
|
|
|
|
(int64_t)pts_to_search);
|
|
|
|
|
|
|
|
|
|
while (count > 0 && anim->next_pts < pts_to_search) {
|
|
|
|
|
int64_t start_gop_frame = anim->cur_key_frame_pts;
|
|
|
|
|
|
|
|
|
|
while (anim->cur_pts < pts_to_search) {
|
|
|
|
|
av_log(anim->pFormatCtx,
|
|
|
|
|
AV_LOG_DEBUG,
|
|
|
|
|
" WHILE: pts=%" PRId64 " in search of %" PRId64 "\n",
|
|
|
|
|
(int64_t)anim->next_pts,
|
|
|
|
|
(int64_t)anim->cur_pts,
|
|
|
|
|
(int64_t)pts_to_search);
|
|
|
|
|
if (!ffmpeg_decode_video_frame(anim)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
count--;
|
|
|
|
|
|
|
|
|
|
if (start_gop_frame != anim->cur_key_frame_pts) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (count == 0) {
|
|
|
|
|
|
|
|
|
|
if (start_gop_frame != anim->cur_key_frame_pts) {
|
|
|
|
|
/* We went into an other GOP frame. This should never happen as we should have positioned us
|
|
|
|
|
* correctly by seeking into the GOP frame that contains the frame we want. */
|
|
|
|
|
av_log(anim->pFormatCtx,
|
|
|
|
|
AV_LOG_ERROR,
|
|
|
|
|
"SCAN failed: completely lost in stream, "
|
|
|
|
|
"bailing out at PTS=%" PRId64 ", searching for PTS=%" PRId64 "\n",
|
|
|
|
|
(int64_t)anim->next_pts,
|
|
|
|
|
(int64_t)anim->cur_pts,
|
|
|
|
|
(int64_t)pts_to_search);
|
|
|
|
|
}
|
|
|
|
|
if (anim->next_pts == pts_to_search) {
|
|
|
|
|
if (anim->cur_pts == pts_to_search) {
|
|
|
|
|
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "SCAN HAPPY: we found our PTS!\n");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
@ -1123,22 +1133,23 @@ static void ffmpeg_decode_video_frame_scan(struct anim *anim, int64_t pts_to_sea
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Wrapper over av_seek_frame(), for formats that doesn't have it's own read_seek() or read_seek2()
|
|
|
|
|
* functions defined. When seeking in these formats, rule to seek to last necessary I-frame is not
|
|
|
|
|
* honored. It is not even guaranteed that I-frame, that must be 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)
|
|
|
|
|
/* Wrapper over av_seek_frame(), for formats that doesn't have its own read_seek() or
|
|
|
|
|
* read_seek2() functions defined. When seeking in these formats, rule to seek to last
|
|
|
|
|
* necessary I-frame is not honored. It is not even guaranteed that I-frame, that must be
|
|
|
|
|
* 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)
|
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
int64_t current_pos = *requested_pos;
|
|
|
|
|
|
|
|
|
|
/* This time offset maximum limit is arbitrary. If some files fails to decode it may be
|
|
|
|
|
* increased. Seek performance will be negatively affected. Small initial offset is necessary
|
|
|
|
|
* because encoder can re-arrange frames as it needs but within it's delay, which is usually
|
|
|
|
|
* small. */
|
|
|
|
|
* increased. Seek performance will be negatively affected. Small initial offset is
|
|
|
|
|
* necessary because encoder can re-arrange frames as it needs but within it's delay, which
|
|
|
|
|
* is usually small. */
|
|
|
|
|
for (int offset = 5; offset < 25; offset++) {
|
|
|
|
|
current_pos = requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate);
|
|
|
|
|
current_pos = *requested_pos - ((int64_t)(offset)*AV_TIME_BASE / frame_rate);
|
|
|
|
|
current_pos = max_ii(current_pos, 0);
|
|
|
|
|
|
|
|
|
|
/* Seek to timestamp. */
|
|
|
|
@ -1147,42 +1158,51 @@ static int ffmpeg_generic_seek_workaround(struct anim *anim, int64_t requested_p
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Read first video stream packet. */
|
|
|
|
|
AVPacket read_packet = {0};
|
|
|
|
|
while (av_read_frame(anim->pFormatCtx, &read_packet) >= 0) {
|
|
|
|
|
if (anim->next_packet->stream_index == anim->videoStream) {
|
|
|
|
|
AVPacket *read_packet = av_packet_alloc();
|
|
|
|
|
while (av_read_frame(anim->pFormatCtx, read_packet) >= 0) {
|
|
|
|
|
if (anim->cur_packet->stream_index == anim->videoStream) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If this packet contains I-frame, exit loop. This should be the frame that we need. */
|
|
|
|
|
if (read_packet.flags & AV_PKT_FLAG_KEY) {
|
|
|
|
|
bool is_key_frame = read_packet->flags & AV_PKT_FLAG_KEY;
|
|
|
|
|
av_packet_free(&read_packet);
|
|
|
|
|
if (is_key_frame) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*requested_pos = current_pos;
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Seek to last necessary I-frame and scan-decode until requested frame is found. */
|
|
|
|
|
static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_index *tc_index)
|
|
|
|
|
/* Seek to last necessary key frame. */
|
|
|
|
|
static int ffmpeg_seek_to_key_frame(struct anim *anim, int position, struct anim_index *tc_index)
|
|
|
|
|
{
|
|
|
|
|
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 pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
|
|
|
|
|
|
|
|
|
|
int64_t pos;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (tc_index) {
|
|
|
|
|
/* We can use timestamps generated from our indexer to seek. */
|
|
|
|
|
int new_frame_index = IMB_indexer_get_frame_index(tc_index, position);
|
|
|
|
|
int old_frame_index = IMB_indexer_get_frame_index(tc_index, anim->cur_position);
|
|
|
|
|
|
|
|
|
|
if (IMB_indexer_can_scan(tc_index, old_frame_index, new_frame_index)) {
|
|
|
|
|
/* No need to seek, return early. */
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
uint64_t dts;
|
|
|
|
|
|
|
|
|
|
pos = IMB_indexer_get_seek_pos(tc_index, new_frame_index);
|
|
|
|
|
dts = IMB_indexer_get_seek_pos_dts(tc_index, new_frame_index);
|
|
|
|
|
|
|
|
|
|
anim->cur_key_frame_pts = pos;
|
|
|
|
|
|
|
|
|
|
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek pos = %" PRId64 "\n", pos);
|
|
|
|
|
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "TC INDEX seek dts = %" PRIu64 "\n", dts);
|
|
|
|
|
|
|
|
|
@ -1198,22 +1218,9 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
pos = (int64_t)(position)*AV_TIME_BASE / 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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);
|
|
|
|
|
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "NO INDEX final seek pos = %" PRId64 "\n", pos);
|
|
|
|
|
|
|
|
|
|
AVFormatContext *format_ctx = anim->pFormatCtx;
|
|
|
|
@ -1222,11 +1229,44 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_
|
|
|
|
|
ret = av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ret = ffmpeg_generic_seek_workaround(anim, pos);
|
|
|
|
|
ret = ffmpeg_generic_seek_workaround(anim, &pos);
|
|
|
|
|
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "Adjusted final seek pos = %" PRId64 "\n", pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret >= 0) {
|
|
|
|
|
/* Double check if we need to seek and decode all packets. */
|
|
|
|
|
AVPacket *current_gop_start_packet = av_packet_alloc();
|
|
|
|
|
while (av_read_frame(anim->pFormatCtx, current_gop_start_packet) >= 0) {
|
|
|
|
|
if (current_gop_start_packet->stream_index == anim->videoStream) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
av_packet_unref(current_gop_start_packet);
|
|
|
|
|
}
|
|
|
|
|
bool same_gop = current_gop_start_packet->pts == anim->cur_key_frame_pts;
|
|
|
|
|
|
|
|
|
|
if (same_gop && position > anim->cur_position) {
|
|
|
|
|
/* Change back to our old frame position so we can simply continue decoding from there. */
|
|
|
|
|
AVPacket *temp = av_packet_alloc();
|
|
|
|
|
while (av_read_frame(anim->pFormatCtx, temp) >= 0) {
|
|
|
|
|
if (temp->stream_index == anim->videoStream && temp->pts == anim->cur_packet->pts) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
av_packet_unref(temp);
|
|
|
|
|
}
|
|
|
|
|
av_packet_free(¤t_gop_start_packet);
|
|
|
|
|
av_packet_free(&temp);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
anim->cur_key_frame_pts = current_gop_start_packet->pts;
|
|
|
|
|
av_packet_free(¤t_gop_start_packet);
|
|
|
|
|
/* Seek back so we are at the correct position after we decoded a frame. */
|
|
|
|
|
av_seek_frame(anim->pFormatCtx, -1, pos, AVSEEK_FLAG_BACKWARD);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
int64_t pts_to_search = ffmpeg_get_pts_to_search(anim, tc_index, position);
|
|
|
|
|
av_log(anim->pFormatCtx,
|
|
|
|
|
AV_LOG_ERROR,
|
|
|
|
|
"FETCH: "
|
|
|
|
@ -1234,25 +1274,19 @@ static void ffmpeg_seek_and_decode(struct anim *anim, int position, struct anim_
|
|
|
|
|
"): errcode = %d\n",
|
|
|
|
|
pos,
|
|
|
|
|
position,
|
|
|
|
|
(int64_t)pts_to_search,
|
|
|
|
|
pts_to_search,
|
|
|
|
|
ret);
|
|
|
|
|
}
|
|
|
|
|
avcodec_flush_buffers(anim->pCodecCtx);
|
|
|
|
|
|
|
|
|
|
anim->next_pts = -1;
|
|
|
|
|
anim->cur_pts = -1;
|
|
|
|
|
|
|
|
|
|
if (anim->next_packet->stream_index == anim->videoStream) {
|
|
|
|
|
av_packet_unref(anim->next_packet);
|
|
|
|
|
anim->next_packet->stream_index = -1;
|
|
|
|
|
if (anim->cur_packet->stream_index == anim->videoStream) {
|
|
|
|
|
av_packet_unref(anim->cur_packet);
|
|
|
|
|
anim->cur_packet->stream_index = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* memset(anim->pFrame, ...) ?? */
|
|
|
|
|
if (ret < 0) {
|
|
|
|
|
/* Seek failed. */
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ffmpeg_decode_video_frame_scan(anim, pts_to_search);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Type tc)
|
|
|
|
@ -1282,25 +1316,22 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
|
|
|
|
|
if (ffmpeg_pts_matches_last_frame(anim, pts_to_search)) {
|
|
|
|
|
av_log(anim->pFormatCtx,
|
|
|
|
|
AV_LOG_DEBUG,
|
|
|
|
|
"FETCH: frame repeat: last: %" PRId64 " next: %" PRId64 "\n",
|
|
|
|
|
(int64_t)anim->last_pts,
|
|
|
|
|
(int64_t)anim->next_pts);
|
|
|
|
|
IMB_refImBuf(anim->last_frame);
|
|
|
|
|
anim->curposition = position;
|
|
|
|
|
return anim->last_frame;
|
|
|
|
|
"FETCH: frame repeat: pts: %" PRId64 "\n",
|
|
|
|
|
(int64_t)anim->cur_pts);
|
|
|
|
|
IMB_refImBuf(anim->cur_frame_final);
|
|
|
|
|
anim->cur_position = position;
|
|
|
|
|
return anim->cur_frame_final;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ffmpeg_can_scan(anim, position, tc_index) || ffmpeg_is_first_frame_decode(anim, position)) {
|
|
|
|
|
if (position == anim->cur_position + 1 || ffmpeg_is_first_frame_decode(anim, position)) {
|
|
|
|
|
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: no seek necessary, just continue...\n");
|
|
|
|
|
ffmpeg_decode_video_frame(anim);
|
|
|
|
|
}
|
|
|
|
|
else if (ffmpeg_seek_to_key_frame(anim, position, tc_index) >= 0) {
|
|
|
|
|
ffmpeg_decode_video_frame_scan(anim, pts_to_search);
|
|
|
|
|
}
|
|
|
|
|
else if (ffmpeg_can_seek(anim, position)) {
|
|
|
|
|
ffmpeg_seek_and_decode(anim, position, tc_index);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
av_log(anim->pFormatCtx, AV_LOG_DEBUG, "FETCH: no seek necessary, just continue...\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IMB_freeImBuf(anim->last_frame);
|
|
|
|
|
IMB_freeImBuf(anim->cur_frame_final);
|
|
|
|
|
|
|
|
|
|
/* Certain versions of FFmpeg have a bug in libswscale which ends up in crash
|
|
|
|
|
* when destination buffer is not properly aligned. For example, this happens
|
|
|
|
@ -1320,23 +1351,20 @@ static ImBuf *ffmpeg_fetchibuf(struct anim *anim, int position, IMB_Timecode_Typ
|
|
|
|
|
*
|
|
|
|
|
* The issue was reported to FFmpeg under ticket #8747 in the FFmpeg tracker
|
|
|
|
|
* and is fixed in the newer versions than 4.3.1. */
|
|
|
|
|
anim->last_frame = IMB_allocImBuf(anim->x, anim->y, 32, 0);
|
|
|
|
|
anim->last_frame->rect = MEM_mallocN_aligned((size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf");
|
|
|
|
|
anim->last_frame->mall |= IB_rect;
|
|
|
|
|
anim->cur_frame_final = IMB_allocImBuf(anim->x, anim->y, 32, 0);
|
|
|
|
|
anim->cur_frame_final->rect = MEM_mallocN_aligned(
|
|
|
|
|
(size_t)4 * anim->x * anim->y, 32, "ffmpeg ibuf");
|
|
|
|
|
anim->cur_frame_final->mall |= IB_rect;
|
|
|
|
|
|
|
|
|
|
anim->last_frame->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace);
|
|
|
|
|
anim->cur_frame_final->rect_colorspace = colormanage_colorspace_get_named(anim->colorspace);
|
|
|
|
|
|
|
|
|
|
ffmpeg_postprocess(anim);
|
|
|
|
|
|
|
|
|
|
anim->last_pts = anim->next_pts;
|
|
|
|
|
anim->cur_position = position;
|
|
|
|
|
|
|
|
|
|
ffmpeg_decode_video_frame(anim);
|
|
|
|
|
IMB_refImBuf(anim->cur_frame_final);
|
|
|
|
|
|
|
|
|
|
anim->curposition = position;
|
|
|
|
|
|
|
|
|
|
IMB_refImBuf(anim->last_frame);
|
|
|
|
|
|
|
|
|
|
return anim->last_frame;
|
|
|
|
|
return anim->cur_frame_final;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void free_anim_ffmpeg(struct anim *anim)
|
|
|
|
@ -1348,7 +1376,7 @@ static void free_anim_ffmpeg(struct anim *anim)
|
|
|
|
|
if (anim->pCodecCtx) {
|
|
|
|
|
avcodec_free_context(&anim->pCodecCtx);
|
|
|
|
|
avformat_close_input(&anim->pFormatCtx);
|
|
|
|
|
av_packet_free(&anim->next_packet);
|
|
|
|
|
av_packet_free(&anim->cur_packet);
|
|
|
|
|
|
|
|
|
|
av_frame_free(&anim->pFrame);
|
|
|
|
|
|
|
|
|
@ -1369,7 +1397,7 @@ static void free_anim_ffmpeg(struct anim *anim)
|
|
|
|
|
av_frame_free(&anim->pFrameDeinterlaced);
|
|
|
|
|
|
|
|
|
|
sws_freeContext(anim->img_convert_ctx);
|
|
|
|
|
IMB_freeImBuf(anim->last_frame);
|
|
|
|
|
IMB_freeImBuf(anim->cur_frame_final);
|
|
|
|
|
}
|
|
|
|
|
anim->duration_in_frames = 0;
|
|
|
|
|
}
|
|
|
|
@ -1503,13 +1531,13 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
|
|
|
|
|
an_stringenc(anim->name, head, tail, digits, pic);
|
|
|
|
|
ibuf = IMB_loadiffname(anim->name, IB_rect, anim->colorspace);
|
|
|
|
|
if (ibuf) {
|
|
|
|
|
anim->curposition = position;
|
|
|
|
|
anim->cur_position = position;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case ANIM_MOVIE:
|
|
|
|
|
ibuf = movie_fetchibuf(anim, position);
|
|
|
|
|
if (ibuf) {
|
|
|
|
|
anim->curposition = position;
|
|
|
|
|
anim->cur_position = position;
|
|
|
|
|
IMB_convert_rgba_to_abgr(ibuf);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
@ -1517,7 +1545,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
|
|
|
|
|
case ANIM_AVI:
|
|
|
|
|
ibuf = avi_fetchibuf(anim, position);
|
|
|
|
|
if (ibuf) {
|
|
|
|
|
anim->curposition = position;
|
|
|
|
|
anim->cur_position = position;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
@ -1525,7 +1553,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
|
|
|
|
|
case ANIM_FFMPEG:
|
|
|
|
|
ibuf = ffmpeg_fetchibuf(anim, position, tc);
|
|
|
|
|
if (ibuf) {
|
|
|
|
|
anim->curposition = position;
|
|
|
|
|
anim->cur_position = position;
|
|
|
|
|
}
|
|
|
|
|
filter_y = 0; /* done internally */
|
|
|
|
|
break;
|
|
|
|
@ -1536,7 +1564,7 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim,
|
|
|
|
|
if (filter_y) {
|
|
|
|
|
IMB_filtery(ibuf);
|
|
|
|
|
}
|
|
|
|
|
BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->curposition + 1);
|
|
|
|
|
BLI_snprintf(ibuf->name, sizeof(ibuf->name), "%s.%04d", anim->name, anim->cur_position + 1);
|
|
|
|
|
}
|
|
|
|
|
return ibuf;
|
|
|
|
|
}
|
|
|
|
@ -1591,16 +1619,6 @@ bool IMB_anim_get_fps(struct anim *anim, short *frs_sec, float *frs_sec_base, bo
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void IMB_anim_set_preseek(struct anim *anim, int preseek)
|
|
|
|
|
{
|
|
|
|
|
anim->preseek = preseek;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int IMB_anim_get_preseek(struct anim *anim)
|
|
|
|
|
{
|
|
|
|
|
return anim->preseek;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int IMB_anim_get_image_width(struct anim *anim)
|
|
|
|
|
{
|
|
|
|
|
return anim->x;
|
|
|
|
|