Merge commit '07fd0a22192805d56c635eb294dc26b0a54ae325'
authorMichael Niedermayer <michaelni@gmx.at>
Sat, 23 Nov 2013 13:26:27 +0000 (14:26 +0100)
committerMichael Niedermayer <michaelni@gmx.at>
Sat, 23 Nov 2013 13:31:15 +0000 (14:31 +0100)
* 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>
1  2 
doc/ffmpeg.texi
ffmpeg.c
ffmpeg.h
ffmpeg_filter.c
ffmpeg_opt.c

diff --cc doc/ffmpeg.texi
@@@ -621,6 -552,33 +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.
 -faster than software decoding on modern CPUs. Additionally, @command{avconv}
+ @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
diff --cc ffmpeg.c
+++ b/ffmpeg.c
@@@ -486,9 -197,8 +486,10 @@@ static void ffmpeg_cleanup(int ret
          av_frame_free(&input_streams[i]->decoded_frame);
          av_frame_free(&input_streams[i]->filter_frame);
          av_dict_free(&input_streams[i]->opts);
 +        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]);
      }
  
@@@ -1704,23 -1166,15 +1705,30 @@@ static int decode_video(InputStream *is
          return ret;
      }
  
 -    ist->hwaccel_retrieved_pix_fmt = decoded_frame->format;
 +    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);
 +
 +    if (debug_ts) {
 +        av_log(NULL, AV_LOG_INFO, "decoder -> ist_index:%d type:video "
 +                "frame_pts:%s frame_pts_time:%s best_effort_ts:%"PRId64" best_effort_ts_time:%s keyframe:%d frame_type:%d \n",
 +                ist->st->index, av_ts2str(decoded_frame->pts),
 +                av_ts2timestr(decoded_frame->pts, &ist->st->time_base),
 +                best_effort_timestamp,
 +                av_ts2timestr(best_effort_timestamp, &ist->st->time_base),
 +                decoded_frame->key_frame, decoded_frame->pict_type);
 +    }
  
 -    decoded_frame->pts = guess_correct_pts(&ist->pts_ctx, decoded_frame->pkt_pts,
 -                                           decoded_frame->pkt_dts);
      pkt->size = 0;
  
      if (ist->st->sample_aspect_ratio.num)
                  break;
          } else
              f = decoded_frame;
 -
 -        err = av_buffersrc_add_frame(ist->filters[i]->filter, f);
 -        if (err < 0)
 -            break;
 +        ret = av_buffersrc_add_frame_flags(ist->filters[i]->filter, f, AV_BUFFERSRC_FLAG_PUSH);
 +        if (ret == AVERROR_EOF) {
 +            ret = 0; /* ignore */
 +        } else if (ret < 0) {
 +            av_log(NULL, AV_LOG_FATAL,
 +                   "Failed to inject frame into filter network: %s\n", av_err2str(ret));
 +            exit_program(1);
 +        }
      }
  
+ fail:
      av_frame_unref(ist->filter_frame);
      av_frame_unref(decoded_frame);
      return err < 0 ? err : ret;
@@@ -1982,11 -1368,67 +1991,68 @@@ 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;
 +    int ret;
      InputStream *ist = input_streams[ist_index];
 +
      if (ist->decoding_needed) {
          AVCodec *codec = ist->dec;
          if (!codec) {
              return AVERROR(EINVAL);
          }
  
 -        /* update requested sample format for the decoder based on the
 -           corresponding encoder sample format */
 -        for (i = 0; i < nb_output_streams; i++) {
 -            OutputStream *ost = output_streams[i];
 -            if (ost->source_index == ist_index) {
 -                update_sample_fmt(ist->st->codec, codec, ost->st->codec);
 -                break;
 -            }
 -        }
 -
+         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))
diff --cc ffmpeg.h
+++ b/ffmpeg.h
  #define VSYNC_PASSTHROUGH 0
  #define VSYNC_CFR         1
  #define VSYNC_VFR         2
 +#define VSYNC_VSCFR       0xfe
 +#define VSYNC_DROP        0xff
 +
 +#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 */
@@@ -274,7 -247,18 +290,20 @@@ typedef struct InputStream 
      InputFilter **filters;
      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,11 -383,10 +460,13 @@@ extern float max_error_rate
  extern const AVIOInterruptCB int_cb;
  
  extern const OptionDef options[];
 -
+ extern const HWAccel hwaccels[];
 -void reset_options(OptionsContext *o);
 +
 +void term_init(void);
 +void term_exit(void);
 +
 +void reset_options(OptionsContext *o, int is_input);
  void show_usage(void);
  
  void opt_output_file(void *optctx, const char *filename);
diff --cc ffmpeg_filter.c
@@@ -648,17 -432,10 +648,18 @@@ static int configure_input_video_filter
      sar = ist->st->sample_aspect_ratio.num ?
            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->hwaccel_retrieve_data ? ist->hwaccel_retrieved_pix_fmt : ist->st->codec->pix_fmt,
 -             tb.num, tb.den, sar.num, sar.den);
 +    if(!sar.den)
 +        sar = (AVRational){0,1};
 +    av_bprint_init(&args, 0, 1);
 +    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)
 +        av_bprintf(&args, ":frame_rate=%d/%d", fr.num, fr.den);
      snprintf(name, sizeof(name), "graph %d input from stream %d:%d", fg->index,
               ist->file_index, ist->st->index);
  
diff --cc ffmpeg_opt.c
      }\
  }
  
 +#define MATCH_PER_TYPE_OPT(name, type, outvar, fmtctx, mediatype)\
 +{\
 +    int i;\
 +    for (i = 0; i < o->nb_ ## name; i++) {\
 +        char *spec = o->name[i].specifier;\
 +        if (!strcmp(spec, mediatype))\
 +            outvar = o->name[i].u.type;\
 +    }\
 +}
++
+ const HWAccel hwaccels[] = {
+     { 0 },
+ };
  char *vstats_filename;
  
  float audio_drift_threshold = 0.1;
@@@ -609,13 -492,42 +614,47 @@@ static void add_input_streams(OptionsCo
                  exit_program(1);
              }
  
 +            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;
 +            MATCH_PER_STREAM_OPT(guess_layout_max, i, ist->guess_layout_max, ic, st);
              guess_input_channel_layout(ist);
  
              ist->resample_sample_fmt     = dec->sample_fmt;
@@@ -2833,8 -2320,12 +2872,14 @@@ 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" },
 +    { "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 },