From 07fd0a22192805d56c635eb294dc26b0a54ae325 Mon Sep 17 00:00:00 2001 From: Anton Khirnov Date: Sat, 2 Nov 2013 22:06:36 +0100 Subject: avconv: add infrastructure for using hwaccels --- avconv.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ avconv.h | 31 ++++++++++++++++++++++++ avconv_filter.c | 3 ++- avconv_opt.c | 46 +++++++++++++++++++++++++++++++++++- doc/avconv.texi | 27 +++++++++++++++++++++ 5 files changed, 178 insertions(+), 2 deletions(-) diff --git a/avconv.c b/avconv.c index 1e8c25eef2..b2d20dd38a 100644 --- a/avconv.c +++ b/avconv.c @@ -198,6 +198,7 @@ static void avconv_cleanup(int ret) av_frame_free(&input_streams[i]->filter_frame); av_dict_free(&input_streams[i]->opts); av_freep(&input_streams[i]->filters); + av_freep(&input_streams[i]->hwaccel_device); av_freep(&input_streams[i]); } @@ -1165,6 +1166,13 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output) return ret; } + if (ist->hwaccel_retrieve_data && decoded_frame->format == ist->hwaccel_pix_fmt) { + err = ist->hwaccel_retrieve_data(ist->st->codec, decoded_frame); + if (err < 0) + goto fail; + } + ist->hwaccel_retrieved_pix_fmt = decoded_frame->format; + decoded_frame->pts = guess_correct_pts(&ist->pts_ctx, decoded_frame->pkt_pts, decoded_frame->pkt_dts); pkt->size = 0; @@ -1212,6 +1220,7 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output) break; } +fail: av_frame_unref(ist->filter_frame); av_frame_unref(decoded_frame); return err < 0 ? err : ret; @@ -1359,6 +1368,63 @@ static void print_sdp(void) av_freep(&avc); } +static const HWAccel *get_hwaccel(enum AVPixelFormat pix_fmt) +{ + int i; + for (i = 0; hwaccels[i].name; i++) + if (hwaccels[i].pix_fmt == pix_fmt) + return &hwaccels[i]; + return NULL; +} + +static enum AVPixelFormat get_format(AVCodecContext *s, const enum AVPixelFormat *pix_fmts) +{ + InputStream *ist = s->opaque; + const enum AVPixelFormat *p; + int ret; + + for (p = pix_fmts; *p != -1; p++) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(*p); + const HWAccel *hwaccel; + + if (!(desc->flags & AV_PIX_FMT_FLAG_HWACCEL)) + break; + + hwaccel = get_hwaccel(*p); + if (!hwaccel || + (ist->active_hwaccel_id && ist->active_hwaccel_id != hwaccel->id) || + (ist->hwaccel_id != HWACCEL_AUTO && ist->hwaccel_id != hwaccel->id)) + continue; + + ret = hwaccel->init(s); + if (ret < 0) { + if (ist->hwaccel_id == hwaccel->id) { + av_log(NULL, AV_LOG_FATAL, + "%s hwaccel requested for input stream #%d:%d, " + "but cannot be initialized.\n", hwaccel->name, + ist->file_index, ist->st->index); + exit_program(1); + } + continue; + } + ist->active_hwaccel_id = hwaccel->id; + ist->hwaccel_pix_fmt = *p; + break; + } + + return *p; +} + +static int get_buffer(AVCodecContext *s, AVFrame *frame, int flags) +{ + InputStream *ist = s->opaque; + + if (ist->hwaccel_get_buffer && frame->format == ist->hwaccel_pix_fmt) + return ist->hwaccel_get_buffer(s, frame, flags); + + return avcodec_default_get_buffer2(s, frame, flags); +} + static int init_input_stream(int ist_index, char *error, int error_len) { int i, ret; @@ -1381,6 +1447,11 @@ static int init_input_stream(int ist_index, char *error, int error_len) } } + ist->st->codec->opaque = ist; + ist->st->codec->get_format = get_format; + ist->st->codec->get_buffer2 = get_buffer; + ist->st->codec->thread_safe_callbacks = 1; + av_opt_set_int(ist->st->codec, "refcounted_frames", 1, 0); if (!av_dict_get(ist->opts, "threads", NULL, 0)) @@ -2272,6 +2343,8 @@ static int transcode(void) ist = input_streams[i]; if (ist->decoding_needed) { avcodec_close(ist->st->codec); + if (ist->hwaccel_uninit) + ist->hwaccel_uninit(ist->st->codec); } } diff --git a/avconv.h b/avconv.h index cb3005d3bb..978195206e 100644 --- a/avconv.h +++ b/avconv.h @@ -48,6 +48,18 @@ #define VSYNC_CFR 1 #define VSYNC_VFR 2 +enum HWAccelID { + HWACCEL_NONE = 0, + HWACCEL_AUTO, +}; + +typedef struct HWAccel { + const char *name; + int (*init)(AVCodecContext *s); + enum HWAccelID id; + enum AVPixelFormat pix_fmt; +} HWAccel; + /* select an input stream for an output stream */ typedef struct StreamMap { int disabled; /* 1 is this mapping is disabled by a negative map */ @@ -94,6 +106,10 @@ typedef struct OptionsContext { int nb_ts_scale; SpecifierOpt *dump_attachment; int nb_dump_attachment; + SpecifierOpt *hwaccels; + int nb_hwaccels; + SpecifierOpt *hwaccel_devices; + int nb_hwaccel_devices; /* output options */ StreamMap *stream_maps; @@ -230,6 +246,19 @@ typedef struct InputStream { * currently video and audio only */ InputFilter **filters; int nb_filters; + + /* hwaccel options */ + enum HWAccelID hwaccel_id; + char *hwaccel_device; + + /* hwaccel context */ + enum HWAccelID active_hwaccel_id; + void *hwaccel_ctx; + void (*hwaccel_uninit)(AVCodecContext *s); + int (*hwaccel_get_buffer)(AVCodecContext *s, AVFrame *frame, int flags); + int (*hwaccel_retrieve_data)(AVCodecContext *s, AVFrame *frame); + enum AVPixelFormat hwaccel_pix_fmt; + enum AVPixelFormat hwaccel_retrieved_pix_fmt; } InputStream; typedef struct InputFile { @@ -355,6 +384,8 @@ extern const AVIOInterruptCB int_cb; extern const OptionDef options[]; +extern const HWAccel hwaccels[]; + void reset_options(OptionsContext *o); void show_usage(void); diff --git a/avconv_filter.c b/avconv_filter.c index 312cb5c212..ffccd93eee 100644 --- a/avconv_filter.c +++ b/avconv_filter.c @@ -433,7 +433,8 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, ist->st->sample_aspect_ratio : ist->st->codec->sample_aspect_ratio; snprintf(args, sizeof(args), "%d:%d:%d:%d:%d:%d:%d", ist->st->codec->width, - ist->st->codec->height, ist->st->codec->pix_fmt, + ist->st->codec->height, + ist->hwaccel_retrieve_data ? ist->hwaccel_retrieved_pix_fmt : ist->st->codec->pix_fmt, tb.num, tb.den, sar.num, sar.den); snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index, ist->file_index, ist->st->index); diff --git a/avconv_opt.c b/avconv_opt.c index 70cbd719cd..cbd8370eba 100644 --- a/avconv_opt.c +++ b/avconv_opt.c @@ -53,6 +53,10 @@ }\ } +const HWAccel hwaccels[] = { + { 0 }, +}; + char *vstats_filename; float audio_drift_threshold = 0.1; @@ -455,7 +459,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) AVStream *st = ic->streams[i]; AVCodecContext *dec = st->codec; InputStream *ist = av_mallocz(sizeof(*ist)); - char *framerate = NULL; + char *framerate = NULL, *hwaccel = NULL, *hwaccel_device = NULL; if (!ist) exit_program(1); @@ -488,6 +492,40 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) exit_program(1); } + MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st); + if (hwaccel) { + if (!strcmp(hwaccel, "none")) + ist->hwaccel_id = HWACCEL_NONE; + else if (!strcmp(hwaccel, "auto")) + ist->hwaccel_id = HWACCEL_AUTO; + else { + int i; + for (i = 0; hwaccels[i].name; i++) { + if (!strcmp(hwaccels[i].name, hwaccel)) { + ist->hwaccel_id = hwaccels[i].id; + break; + } + } + + if (!ist->hwaccel_id) { + av_log(NULL, AV_LOG_FATAL, "Unrecognized hwaccel: %s.\n", + hwaccel); + av_log(NULL, AV_LOG_FATAL, "Supported hwaccels: "); + for (i = 0; hwaccels[i].name; i++) + av_log(NULL, AV_LOG_FATAL, "%s ", hwaccels[i].name); + av_log(NULL, AV_LOG_FATAL, "\n"); + exit_program(1); + } + } + } + + MATCH_PER_STREAM_OPT(hwaccel_devices, str, hwaccel_device, ic, st); + if (hwaccel_device) { + ist->hwaccel_device = av_strdup(hwaccel_device); + if (!ist->hwaccel_device) + exit_program(1); + } + break; case AVMEDIA_TYPE_AUDIO: guess_input_channel_layout(ist); @@ -2282,6 +2320,12 @@ const OptionDef options[] = { { "force_key_frames", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT | OPT_SPEC | OPT_OUTPUT, { .off = OFFSET(forced_key_frames) }, "force key frames at specified timestamps", "timestamps" }, + { "hwaccel", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT | + OPT_SPEC | OPT_INPUT, { .off = OFFSET(hwaccels) }, + "use HW accelerated decoding", "hwaccel name" }, + { "hwaccel_device", OPT_VIDEO | OPT_STRING | HAS_ARG | OPT_EXPERT | + OPT_SPEC | OPT_INPUT, { .off = OFFSET(hwaccel_devices) }, + "select a device for HW acceleration" "devicename" }, /* audio options */ { "aframes", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_frames }, diff --git a/doc/avconv.texi b/doc/avconv.texi index 714b0e7ab3..40c105cc15 100644 --- a/doc/avconv.texi +++ b/doc/avconv.texi @@ -552,6 +552,33 @@ The timestamps must be specified in ascending order. @item -copyinkf[:@var{stream_specifier}] (@emph{output,per-stream}) When doing stream copy, copy also non-key frames found at the beginning. + +@item -hwaccel[:@var{stream_specifier}] @var{hwaccel} (@emph{input,per-stream}) +Use hardware acceleration to decode the matching stream(s). The allowed values +of @var{hwaccel} are: +@table @option +@item none +Do not use any hardware acceleration (the default). + +@item auto +Automatically select the hardware acceleration method. +@end table + +This option has no effect if the selected hwaccel is not available or not +supported by the chosen decoder. + +Note that most acceleration methods are intended for playback and will not be +faster than software decoding on modern CPUs. Additionally, @command{avconv} +will usually need to copy the decoded frames from the GPU memory into the system +memory, resulting in further performance loss. This option is thus mainly +useful for testing. + +@item -hwaccel_device[:@var{stream_specifier}] @var{hwaccel_device} (@emph{input,per-stream}) +Select a device to use for hardware acceleration. + +This option only makes sense when the @option{-hwaccel} option is also +specified. Its exact meaning depends on the specific hardware acceleration +method chosen. @end table @section Audio Options -- cgit v1.2.3