diff options
author | Michael Niedermayer <michaelni@gmx.at> | 2013-11-23 14:26:27 +0100 |
---|---|---|
committer | Michael Niedermayer <michaelni@gmx.at> | 2013-11-23 14:31:15 +0100 |
commit | 62e10c375968d9cbbe2a0bc321e00eef52d32917 (patch) | |
tree | 8c8a3a4b025d3cf54abb44a3ab988c58c2c9e615 | |
parent | 94194bdcd7bfc2137ccd13e5c149567b9a917f4f (diff) | |
parent | 07fd0a22192805d56c635eb294dc26b0a54ae325 (diff) | |
download | ffmpeg-streaming-62e10c375968d9cbbe2a0bc321e00eef52d32917.zip ffmpeg-streaming-62e10c375968d9cbbe2a0bc321e00eef52d32917.tar.gz |
Merge commit '07fd0a22192805d56c635eb294dc26b0a54ae325'
* commit '07fd0a22192805d56c635eb294dc26b0a54ae325':
avconv: add infrastructure for using hwaccels
Conflicts:
ffmpeg.c
ffmpeg.h
ffmpeg_filter.c
ffmpeg_opt.c
Merged-by: Michael Niedermayer <michaelni@gmx.at>
-rw-r--r-- | doc/ffmpeg.texi | 27 | ||||
-rw-r--r-- | ffmpeg.c | 73 | ||||
-rw-r--r-- | ffmpeg.h | 31 | ||||
-rw-r--r-- | ffmpeg_filter.c | 3 | ||||
-rw-r--r-- | ffmpeg_opt.c | 47 |
5 files changed, 179 insertions, 2 deletions
diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index af4cf9f..1a2a801 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -621,6 +621,33 @@ would be more efficient. @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{ffmpeg} +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 @@ -489,6 +489,7 @@ static void ffmpeg_cleanup(int ret) avsubtitle_free(&input_streams[i]->prev_sub.subtitle); av_frame_free(&input_streams[i]->sub2video.frame); av_freep(&input_streams[i]->filters); + av_freep(&input_streams[i]->hwaccel_device); av_freep(&input_streams[i]); } @@ -1707,6 +1708,13 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output) if(ist->top_field_first>=0) decoded_frame->top_field_first = ist->top_field_first; + 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; + best_effort_timestamp= av_frame_get_best_effort_timestamp(decoded_frame); if(best_effort_timestamp != AV_NOPTS_VALUE) ist->next_pts = ist->pts = av_rescale_q(decoded_frame->pts = best_effort_timestamp, ist->st->time_base, AV_TIME_BASE_Q); @@ -1771,6 +1779,7 @@ static int decode_video(InputStream *ist, AVPacket *pkt, int *got_output) } } +fail: av_frame_unref(ist->filter_frame); av_frame_unref(decoded_frame); return err < 0 ? err : ret; @@ -1982,6 +1991,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 ret; @@ -1995,6 +2061,11 @@ static int init_input_stream(int ist_index, char *error, int error_len) return AVERROR(EINVAL); } + 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)) @@ -3326,6 +3397,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); } } @@ -56,6 +56,18 @@ #define MAX_STREAMS 1024 /* arbitrary sanity check value */ +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 */ @@ -100,6 +112,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; @@ -275,6 +291,19 @@ typedef struct InputStream { int nb_filters; int reinit_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 { @@ -431,6 +460,8 @@ extern float max_error_rate; extern const AVIOInterruptCB int_cb; extern const OptionDef options[]; +extern const HWAccel hwaccels[]; + void term_init(void); void term_exit(void); diff --git a/ffmpeg_filter.c b/ffmpeg_filter.c index 461cc90..ad97f0d 100644 --- a/ffmpeg_filter.c +++ b/ffmpeg_filter.c @@ -654,7 +654,8 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, av_bprintf(&args, "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:" "pixel_aspect=%d/%d:sws_param=flags=%d", ist->resample_width, - ist->resample_height, ist->resample_pix_fmt, + ist->resample_height, + ist->hwaccel_retrieve_data ? ist->hwaccel_retrieved_pix_fmt : ist->resample_pix_fmt, tb.num, tb.den, sar.num, sar.den, SWS_BILINEAR + ((ist->st->codec->flags&CODEC_FLAG_BITEXACT) ? SWS_BITEXACT:0)); if (fr.num && fr.den) diff --git a/ffmpeg_opt.c b/ffmpeg_opt.c index 8da1ce4..8e01c68 100644 --- a/ffmpeg_opt.c +++ b/ffmpeg_opt.c @@ -62,6 +62,11 @@ outvar = o->name[i].u.type;\ }\ } + +const HWAccel hwaccels[] = { + { 0 }, +}; + char *vstats_filename; float audio_drift_threshold = 0.1; @@ -557,7 +562,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); @@ -612,6 +617,40 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) ist->top_field_first = -1; MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic, st); + 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: ist->guess_layout_max = INT_MAX; @@ -2835,6 +2874,12 @@ const OptionDef options[] = { "force key frames at specified timestamps", "timestamps" }, { "b", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_bitrate }, "video bitrate (please use -b:v)", "bitrate" }, + { "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 }, |