|
|
@@ -42,6 +42,7 @@ |
|
|
|
# include "libavfilter/avfilter.h" |
|
|
|
# include "libavfilter/avfiltergraph.h" |
|
|
|
# include "libavfilter/buffersink.h" |
|
|
|
# include "libavfilter/buffersrc.h" |
|
|
|
#endif |
|
|
|
|
|
|
|
#include "cmdutils.h" |
|
|
@@ -212,7 +213,10 @@ typedef struct VideoState { |
|
|
|
PtsCorrectionContext pts_ctx; |
|
|
|
|
|
|
|
#if CONFIG_AVFILTER |
|
|
|
AVFilterContext *in_video_filter; ///< the first filter in the video chain |
|
|
|
AVFilterContext *out_video_filter; ///< the last filter in the video chain |
|
|
|
int use_dr1; |
|
|
|
FrameBuffer *buffer_pool; |
|
|
|
#endif |
|
|
|
|
|
|
|
float skip_frames; |
|
|
@@ -1517,206 +1521,27 @@ static int get_video_frame(VideoState *is, AVFrame *frame, int64_t *pts, AVPacke |
|
|
|
} |
|
|
|
|
|
|
|
#if CONFIG_AVFILTER |
|
|
|
typedef struct { |
|
|
|
VideoState *is; |
|
|
|
AVFrame *frame; |
|
|
|
int use_dr1; |
|
|
|
} FilterPriv; |
|
|
|
|
|
|
|
static int input_get_buffer(AVCodecContext *codec, AVFrame *pic) |
|
|
|
{ |
|
|
|
AVFilterContext *ctx = codec->opaque; |
|
|
|
AVFilterBufferRef *ref; |
|
|
|
int perms = AV_PERM_WRITE; |
|
|
|
int i, w, h, stride[AV_NUM_DATA_POINTERS]; |
|
|
|
unsigned edge; |
|
|
|
int pixel_size; |
|
|
|
|
|
|
|
if (codec->codec->capabilities & CODEC_CAP_NEG_LINESIZES) |
|
|
|
perms |= AV_PERM_NEG_LINESIZES; |
|
|
|
|
|
|
|
if (pic->buffer_hints & FF_BUFFER_HINTS_VALID) { |
|
|
|
if (pic->buffer_hints & FF_BUFFER_HINTS_READABLE) perms |= AV_PERM_READ; |
|
|
|
if (pic->buffer_hints & FF_BUFFER_HINTS_PRESERVE) perms |= AV_PERM_PRESERVE; |
|
|
|
if (pic->buffer_hints & FF_BUFFER_HINTS_REUSABLE) perms |= AV_PERM_REUSE2; |
|
|
|
} |
|
|
|
if (pic->reference) perms |= AV_PERM_READ | AV_PERM_PRESERVE; |
|
|
|
|
|
|
|
w = codec->width; |
|
|
|
h = codec->height; |
|
|
|
avcodec_align_dimensions2(codec, &w, &h, stride); |
|
|
|
edge = codec->flags & CODEC_FLAG_EMU_EDGE ? 0 : avcodec_get_edge_width(); |
|
|
|
w += edge << 1; |
|
|
|
h += edge << 1; |
|
|
|
|
|
|
|
if (!(ref = avfilter_get_video_buffer(ctx->outputs[0], perms, w, h))) |
|
|
|
return -1; |
|
|
|
|
|
|
|
pixel_size = av_pix_fmt_descriptors[ref->format].comp[0].step_minus1 + 1; |
|
|
|
ref->video->w = codec->width; |
|
|
|
ref->video->h = codec->height; |
|
|
|
for (i = 0; i < 4; i ++) { |
|
|
|
unsigned hshift = (i == 1 || i == 2) ? av_pix_fmt_descriptors[ref->format].log2_chroma_w : 0; |
|
|
|
unsigned vshift = (i == 1 || i == 2) ? av_pix_fmt_descriptors[ref->format].log2_chroma_h : 0; |
|
|
|
|
|
|
|
if (ref->data[i]) { |
|
|
|
ref->data[i] += ((edge * pixel_size) >> hshift) + ((edge * ref->linesize[i]) >> vshift); |
|
|
|
} |
|
|
|
pic->data[i] = ref->data[i]; |
|
|
|
pic->linesize[i] = ref->linesize[i]; |
|
|
|
} |
|
|
|
pic->opaque = ref; |
|
|
|
pic->type = FF_BUFFER_TYPE_USER; |
|
|
|
pic->reordered_opaque = codec->reordered_opaque; |
|
|
|
pic->width = codec->width; |
|
|
|
pic->height = codec->height; |
|
|
|
pic->format = codec->pix_fmt; |
|
|
|
pic->sample_aspect_ratio = codec->sample_aspect_ratio; |
|
|
|
if (codec->pkt) pic->pkt_pts = codec->pkt->pts; |
|
|
|
else pic->pkt_pts = AV_NOPTS_VALUE; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static void input_release_buffer(AVCodecContext *codec, AVFrame *pic) |
|
|
|
{ |
|
|
|
memset(pic->data, 0, sizeof(pic->data)); |
|
|
|
avfilter_unref_buffer(pic->opaque); |
|
|
|
} |
|
|
|
|
|
|
|
static int input_reget_buffer(AVCodecContext *codec, AVFrame *pic) |
|
|
|
{ |
|
|
|
AVFilterBufferRef *ref = pic->opaque; |
|
|
|
|
|
|
|
if (pic->data[0] == NULL) { |
|
|
|
pic->buffer_hints |= FF_BUFFER_HINTS_READABLE; |
|
|
|
return codec->get_buffer(codec, pic); |
|
|
|
} |
|
|
|
|
|
|
|
if ((codec->width != ref->video->w) || (codec->height != ref->video->h) || |
|
|
|
(codec->pix_fmt != ref->format)) { |
|
|
|
av_log(codec, AV_LOG_ERROR, "Picture properties changed.\n"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
pic->reordered_opaque = codec->reordered_opaque; |
|
|
|
if (codec->pkt) pic->pkt_pts = codec->pkt->pts; |
|
|
|
else pic->pkt_pts = AV_NOPTS_VALUE; |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int input_init(AVFilterContext *ctx, const char *args, void *opaque) |
|
|
|
{ |
|
|
|
FilterPriv *priv = ctx->priv; |
|
|
|
AVCodecContext *codec; |
|
|
|
if (!opaque) return -1; |
|
|
|
|
|
|
|
priv->is = opaque; |
|
|
|
codec = priv->is->video_st->codec; |
|
|
|
codec->opaque = ctx; |
|
|
|
if (codec->codec->capabilities & CODEC_CAP_DR1) { |
|
|
|
priv->use_dr1 = 1; |
|
|
|
codec->get_buffer = input_get_buffer; |
|
|
|
codec->release_buffer = input_release_buffer; |
|
|
|
codec->reget_buffer = input_reget_buffer; |
|
|
|
codec->thread_safe_callbacks = 1; |
|
|
|
} |
|
|
|
|
|
|
|
priv->frame = avcodec_alloc_frame(); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static void input_uninit(AVFilterContext *ctx) |
|
|
|
{ |
|
|
|
FilterPriv *priv = ctx->priv; |
|
|
|
av_free(priv->frame); |
|
|
|
} |
|
|
|
|
|
|
|
static int input_request_frame(AVFilterLink *link) |
|
|
|
{ |
|
|
|
FilterPriv *priv = link->src->priv; |
|
|
|
AVFilterBufferRef *picref; |
|
|
|
int64_t pts = 0; |
|
|
|
AVPacket pkt; |
|
|
|
int ret; |
|
|
|
|
|
|
|
while (!(ret = get_video_frame(priv->is, priv->frame, &pts, &pkt))) |
|
|
|
av_free_packet(&pkt); |
|
|
|
if (ret < 0) |
|
|
|
return -1; |
|
|
|
|
|
|
|
if (priv->use_dr1) { |
|
|
|
picref = avfilter_ref_buffer(priv->frame->opaque, ~0); |
|
|
|
} else { |
|
|
|
picref = avfilter_get_video_buffer(link, AV_PERM_WRITE, link->w, link->h); |
|
|
|
av_image_copy(picref->data, picref->linesize, |
|
|
|
priv->frame->data, priv->frame->linesize, |
|
|
|
picref->format, link->w, link->h); |
|
|
|
} |
|
|
|
av_free_packet(&pkt); |
|
|
|
|
|
|
|
avfilter_copy_frame_props(picref, priv->frame); |
|
|
|
picref->pts = pts; |
|
|
|
|
|
|
|
avfilter_start_frame(link, picref); |
|
|
|
avfilter_draw_slice(link, 0, link->h, 1); |
|
|
|
avfilter_end_frame(link); |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int input_query_formats(AVFilterContext *ctx) |
|
|
|
{ |
|
|
|
FilterPriv *priv = ctx->priv; |
|
|
|
enum PixelFormat pix_fmts[] = { |
|
|
|
priv->is->video_st->codec->pix_fmt, PIX_FMT_NONE |
|
|
|
}; |
|
|
|
|
|
|
|
avfilter_set_common_formats(ctx, avfilter_make_format_list(pix_fmts)); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static int input_config_props(AVFilterLink *link) |
|
|
|
{ |
|
|
|
FilterPriv *priv = link->src->priv; |
|
|
|
AVCodecContext *c = priv->is->video_st->codec; |
|
|
|
|
|
|
|
link->w = c->width; |
|
|
|
link->h = c->height; |
|
|
|
link->time_base = priv->is->video_st->time_base; |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
static AVFilter input_filter = |
|
|
|
{ |
|
|
|
.name = "avplay_input", |
|
|
|
|
|
|
|
.priv_size = sizeof(FilterPriv), |
|
|
|
|
|
|
|
.init = input_init, |
|
|
|
.uninit = input_uninit, |
|
|
|
|
|
|
|
.query_formats = input_query_formats, |
|
|
|
|
|
|
|
.inputs = (AVFilterPad[]) {{ .name = NULL }}, |
|
|
|
.outputs = (AVFilterPad[]) {{ .name = "default", |
|
|
|
.type = AVMEDIA_TYPE_VIDEO, |
|
|
|
.request_frame = input_request_frame, |
|
|
|
.config_props = input_config_props, }, |
|
|
|
{ .name = NULL }}, |
|
|
|
}; |
|
|
|
|
|
|
|
static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const char *vfilters) |
|
|
|
{ |
|
|
|
char sws_flags_str[128]; |
|
|
|
char buffersrc_args[256]; |
|
|
|
int ret; |
|
|
|
AVFilterContext *filt_src = NULL, *filt_out = NULL, *filt_format; |
|
|
|
AVCodecContext *codec = is->video_st->codec; |
|
|
|
|
|
|
|
snprintf(sws_flags_str, sizeof(sws_flags_str), "flags=%d", sws_flags); |
|
|
|
graph->scale_sws_opts = av_strdup(sws_flags_str); |
|
|
|
|
|
|
|
if ((ret = avfilter_graph_create_filter(&filt_src, &input_filter, "src", |
|
|
|
NULL, is, graph)) < 0) |
|
|
|
snprintf(buffersrc_args, sizeof(buffersrc_args), "%d:%d:%d:%d:%d:%d:%d", |
|
|
|
codec->width, codec->height, codec->pix_fmt, |
|
|
|
is->video_st->time_base.num, is->video_st->time_base.den, |
|
|
|
codec->sample_aspect_ratio.num, codec->sample_aspect_ratio.den); |
|
|
|
|
|
|
|
|
|
|
|
if ((ret = avfilter_graph_create_filter(&filt_src, |
|
|
|
avfilter_get_by_name("buffer"), |
|
|
|
"src", buffersrc_args, NULL, |
|
|
|
graph)) < 0) |
|
|
|
return ret; |
|
|
|
if ((ret = avfilter_graph_create_filter(&filt_out, |
|
|
|
avfilter_get_by_name("buffersink"), |
|
|
@@ -1755,8 +1580,16 @@ static int configure_video_filters(AVFilterGraph *graph, VideoState *is, const c |
|
|
|
if ((ret = avfilter_graph_config(graph, NULL)) < 0) |
|
|
|
return ret; |
|
|
|
|
|
|
|
is->in_video_filter = filt_src; |
|
|
|
is->out_video_filter = filt_out; |
|
|
|
|
|
|
|
if (codec->codec->capabilities & CODEC_CAP_DR1) { |
|
|
|
is->use_dr1 = 1; |
|
|
|
codec->get_buffer = codec_get_buffer; |
|
|
|
codec->release_buffer = codec_release_buffer; |
|
|
|
codec->opaque = &is->buffer_pool; |
|
|
|
} |
|
|
|
|
|
|
|
return ret; |
|
|
|
} |
|
|
|
|
|
|
@@ -1772,25 +1605,34 @@ static int video_thread(void *arg) |
|
|
|
|
|
|
|
#if CONFIG_AVFILTER |
|
|
|
AVFilterGraph *graph = avfilter_graph_alloc(); |
|
|
|
AVFilterContext *filt_out = NULL; |
|
|
|
AVFilterContext *filt_out = NULL, *filt_in = NULL; |
|
|
|
int64_t pos; |
|
|
|
int last_w = is->video_st->codec->width; |
|
|
|
int last_h = is->video_st->codec->height; |
|
|
|
|
|
|
|
if ((ret = configure_video_filters(graph, is, vfilters)) < 0) |
|
|
|
goto the_end; |
|
|
|
filt_in = is->in_video_filter; |
|
|
|
filt_out = is->out_video_filter; |
|
|
|
#endif |
|
|
|
|
|
|
|
for (;;) { |
|
|
|
#if !CONFIG_AVFILTER |
|
|
|
AVPacket pkt; |
|
|
|
#else |
|
|
|
#if CONFIG_AVFILTER |
|
|
|
AVFilterBufferRef *picref; |
|
|
|
AVRational tb; |
|
|
|
#endif |
|
|
|
while (is->paused && !is->videoq.abort_request) |
|
|
|
SDL_Delay(10); |
|
|
|
|
|
|
|
ret = get_video_frame(is, frame, &pts_int, &pkt); |
|
|
|
if (ret < 0) |
|
|
|
goto the_end; |
|
|
|
av_free_packet(&pkt); |
|
|
|
|
|
|
|
if (!ret) |
|
|
|
continue; |
|
|
|
|
|
|
|
#if CONFIG_AVFILTER |
|
|
|
if ( last_w != is->video_st->codec->width |
|
|
|
|| last_h != is->video_st->codec->height) { |
|
|
@@ -1804,8 +1646,33 @@ static int video_thread(void *arg) |
|
|
|
last_w = is->video_st->codec->width; |
|
|
|
last_h = is->video_st->codec->height; |
|
|
|
} |
|
|
|
ret = av_buffersink_read(filt_out, &picref); |
|
|
|
if (picref) { |
|
|
|
|
|
|
|
frame->pts = pts_int; |
|
|
|
if (is->use_dr1) { |
|
|
|
FrameBuffer *buf = frame->opaque; |
|
|
|
AVFilterBufferRef *fb = avfilter_get_video_buffer_ref_from_arrays( |
|
|
|
frame->data, frame->linesize, |
|
|
|
AV_PERM_READ | AV_PERM_PRESERVE, |
|
|
|
frame->width, frame->height, |
|
|
|
frame->format); |
|
|
|
|
|
|
|
avfilter_copy_frame_props(fb, frame); |
|
|
|
fb->buf->priv = buf; |
|
|
|
fb->buf->free = filter_release_buffer; |
|
|
|
|
|
|
|
buf->refcount++; |
|
|
|
av_buffersrc_buffer(filt_in, fb); |
|
|
|
|
|
|
|
} else |
|
|
|
av_buffersrc_write_frame(filt_in, frame); |
|
|
|
|
|
|
|
while (ret >= 0) { |
|
|
|
ret = av_buffersink_read(filt_out, &picref); |
|
|
|
if (ret < 0) { |
|
|
|
ret = 0; |
|
|
|
break; |
|
|
|
} |
|
|
|
|
|
|
|
avfilter_copy_buf_props(frame, picref); |
|
|
|
|
|
|
|
pts_int = picref->pts; |
|
|
@@ -1813,35 +1680,22 @@ static int video_thread(void *arg) |
|
|
|
pos = picref->pos; |
|
|
|
frame->opaque = picref; |
|
|
|
|
|
|
|
ret = 1; |
|
|
|
} |
|
|
|
|
|
|
|
if (ret >= 0 && av_cmp_q(tb, is->video_st->time_base)) { |
|
|
|
av_unused int64_t pts1 = pts_int; |
|
|
|
pts_int = av_rescale_q(pts_int, tb, is->video_st->time_base); |
|
|
|
av_dlog(NULL, "video_thread(): " |
|
|
|
"tb:%d/%d pts:%"PRId64" -> tb:%d/%d pts:%"PRId64"\n", |
|
|
|
tb.num, tb.den, pts1, |
|
|
|
is->video_st->time_base.num, is->video_st->time_base.den, pts_int); |
|
|
|
if (av_cmp_q(tb, is->video_st->time_base)) { |
|
|
|
av_unused int64_t pts1 = pts_int; |
|
|
|
pts_int = av_rescale_q(pts_int, tb, is->video_st->time_base); |
|
|
|
av_dlog(NULL, "video_thread(): " |
|
|
|
"tb:%d/%d pts:%"PRId64" -> tb:%d/%d pts:%"PRId64"\n", |
|
|
|
tb.num, tb.den, pts1, |
|
|
|
is->video_st->time_base.num, is->video_st->time_base.den, pts_int); |
|
|
|
} |
|
|
|
pts = pts_int * av_q2d(is->video_st->time_base); |
|
|
|
ret = output_picture2(is, frame, pts, pos); |
|
|
|
} |
|
|
|
#else |
|
|
|
ret = get_video_frame(is, frame, &pts_int, &pkt); |
|
|
|
#endif |
|
|
|
|
|
|
|
if (ret < 0) |
|
|
|
goto the_end; |
|
|
|
|
|
|
|
if (!ret) |
|
|
|
continue; |
|
|
|
|
|
|
|
pts = pts_int * av_q2d(is->video_st->time_base); |
|
|
|
|
|
|
|
#if CONFIG_AVFILTER |
|
|
|
ret = output_picture2(is, frame, pts, pos); |
|
|
|
#else |
|
|
|
ret = output_picture2(is, frame, pts, pkt.pos); |
|
|
|
av_free_packet(&pkt); |
|
|
|
#endif |
|
|
|
|
|
|
|
if (ret < 0) |
|
|
|
goto the_end; |
|
|
|
|
|
|
@@ -2386,6 +2240,7 @@ static void stream_component_close(VideoState *is, int stream_index) |
|
|
|
|
|
|
|
ic->streams[stream_index]->discard = AVDISCARD_ALL; |
|
|
|
avcodec_close(avctx); |
|
|
|
free_buffer_pool(&is->buffer_pool); |
|
|
|
switch (avctx->codec_type) { |
|
|
|
case AVMEDIA_TYPE_AUDIO: |
|
|
|
is->audio_st = NULL; |
|
|
|