* 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>tags/n2.2-rc1
| @@ -621,6 +621,33 @@ would be more efficient. | |||||
| @item -copyinkf[:@var{stream_specifier}] (@emph{output,per-stream}) | @item -copyinkf[:@var{stream_specifier}] (@emph{output,per-stream}) | ||||
| When doing stream copy, copy also non-key frames found at the | When doing stream copy, copy also non-key frames found at the | ||||
| beginning. | 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 | @end table | ||||
| @section Audio Options | @section Audio Options | ||||
| @@ -489,6 +489,7 @@ static void ffmpeg_cleanup(int ret) | |||||
| avsubtitle_free(&input_streams[i]->prev_sub.subtitle); | avsubtitle_free(&input_streams[i]->prev_sub.subtitle); | ||||
| av_frame_free(&input_streams[i]->sub2video.frame); | av_frame_free(&input_streams[i]->sub2video.frame); | ||||
| av_freep(&input_streams[i]->filters); | av_freep(&input_streams[i]->filters); | ||||
| av_freep(&input_streams[i]->hwaccel_device); | |||||
| av_freep(&input_streams[i]); | 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) | if(ist->top_field_first>=0) | ||||
| decoded_frame->top_field_first = ist->top_field_first; | 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); | best_effort_timestamp= av_frame_get_best_effort_timestamp(decoded_frame); | ||||
| if(best_effort_timestamp != AV_NOPTS_VALUE) | 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); | 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(ist->filter_frame); | ||||
| av_frame_unref(decoded_frame); | av_frame_unref(decoded_frame); | ||||
| return err < 0 ? err : ret; | return err < 0 ? err : ret; | ||||
| @@ -1982,6 +1991,63 @@ static void print_sdp(void) | |||||
| av_freep(&avc); | 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) | static int init_input_stream(int ist_index, char *error, int error_len) | ||||
| { | { | ||||
| int ret; | int ret; | ||||
| @@ -1995,6 +2061,11 @@ static int init_input_stream(int ist_index, char *error, int error_len) | |||||
| return AVERROR(EINVAL); | 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); | av_opt_set_int(ist->st->codec, "refcounted_frames", 1, 0); | ||||
| if (!av_dict_get(ist->opts, "threads", NULL, 0)) | if (!av_dict_get(ist->opts, "threads", NULL, 0)) | ||||
| @@ -3326,6 +3397,8 @@ static int transcode(void) | |||||
| ist = input_streams[i]; | ist = input_streams[i]; | ||||
| if (ist->decoding_needed) { | if (ist->decoding_needed) { | ||||
| avcodec_close(ist->st->codec); | 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 */ | #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 */ | /* select an input stream for an output stream */ | ||||
| typedef struct StreamMap { | typedef struct StreamMap { | ||||
| int disabled; /* 1 is this mapping is disabled by a negative map */ | int disabled; /* 1 is this mapping is disabled by a negative map */ | ||||
| @@ -100,6 +112,10 @@ typedef struct OptionsContext { | |||||
| int nb_ts_scale; | int nb_ts_scale; | ||||
| SpecifierOpt *dump_attachment; | SpecifierOpt *dump_attachment; | ||||
| int nb_dump_attachment; | int nb_dump_attachment; | ||||
| SpecifierOpt *hwaccels; | |||||
| int nb_hwaccels; | |||||
| SpecifierOpt *hwaccel_devices; | |||||
| int nb_hwaccel_devices; | |||||
| /* output options */ | /* output options */ | ||||
| StreamMap *stream_maps; | StreamMap *stream_maps; | ||||
| @@ -275,6 +291,19 @@ typedef struct InputStream { | |||||
| int nb_filters; | int nb_filters; | ||||
| int reinit_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; | } InputStream; | ||||
| typedef struct InputFile { | typedef struct InputFile { | ||||
| @@ -431,6 +460,8 @@ extern float max_error_rate; | |||||
| extern const AVIOInterruptCB int_cb; | extern const AVIOInterruptCB int_cb; | ||||
| extern const OptionDef options[]; | extern const OptionDef options[]; | ||||
| extern const HWAccel hwaccels[]; | |||||
| void term_init(void); | void term_init(void); | ||||
| void term_exit(void); | void term_exit(void); | ||||
| @@ -654,7 +654,8 @@ static int configure_input_video_filter(FilterGraph *fg, InputFilter *ifilter, | |||||
| av_bprintf(&args, | av_bprintf(&args, | ||||
| "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:" | "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:" | ||||
| "pixel_aspect=%d/%d:sws_param=flags=%d", ist->resample_width, | "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, | tb.num, tb.den, sar.num, sar.den, | ||||
| SWS_BILINEAR + ((ist->st->codec->flags&CODEC_FLAG_BITEXACT) ? SWS_BITEXACT:0)); | SWS_BILINEAR + ((ist->st->codec->flags&CODEC_FLAG_BITEXACT) ? SWS_BITEXACT:0)); | ||||
| if (fr.num && fr.den) | if (fr.num && fr.den) | ||||
| @@ -62,6 +62,11 @@ | |||||
| outvar = o->name[i].u.type;\ | outvar = o->name[i].u.type;\ | ||||
| }\ | }\ | ||||
| } | } | ||||
| const HWAccel hwaccels[] = { | |||||
| { 0 }, | |||||
| }; | |||||
| char *vstats_filename; | char *vstats_filename; | ||||
| float audio_drift_threshold = 0.1; | float audio_drift_threshold = 0.1; | ||||
| @@ -557,7 +562,7 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) | |||||
| AVStream *st = ic->streams[i]; | AVStream *st = ic->streams[i]; | ||||
| AVCodecContext *dec = st->codec; | AVCodecContext *dec = st->codec; | ||||
| InputStream *ist = av_mallocz(sizeof(*ist)); | InputStream *ist = av_mallocz(sizeof(*ist)); | ||||
| char *framerate = NULL; | |||||
| char *framerate = NULL, *hwaccel = NULL, *hwaccel_device = NULL; | |||||
| if (!ist) | if (!ist) | ||||
| exit_program(1); | exit_program(1); | ||||
| @@ -612,6 +617,40 @@ static void add_input_streams(OptionsContext *o, AVFormatContext *ic) | |||||
| ist->top_field_first = -1; | ist->top_field_first = -1; | ||||
| MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic, st); | 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; | break; | ||||
| case AVMEDIA_TYPE_AUDIO: | case AVMEDIA_TYPE_AUDIO: | ||||
| ist->guess_layout_max = INT_MAX; | ist->guess_layout_max = INT_MAX; | ||||
| @@ -2835,6 +2874,12 @@ const OptionDef options[] = { | |||||
| "force key frames at specified timestamps", "timestamps" }, | "force key frames at specified timestamps", "timestamps" }, | ||||
| { "b", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_bitrate }, | { "b", OPT_VIDEO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_bitrate }, | ||||
| "video bitrate (please use -b:v)", "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 */ | /* audio options */ | ||||
| { "aframes", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_frames }, | { "aframes", OPT_AUDIO | HAS_ARG | OPT_PERFILE | OPT_OUTPUT, { .func_arg = opt_audio_frames }, | ||||