X-Git-Url: https://git.cinelerra-gg.org/git/?p=goodguy%2Fcinelerra.git;a=blobdiff_plain;f=cinelerra-5.1%2Fcinelerra%2Fffmpeg.C;h=47c603f2f72feedc8429243dd4c7dd06f9783ab0;hp=873abc2e45992454e00887dff701616f478f747d;hb=8e67d840c5a93f77de021102a4f0bfc4e07504f4;hpb=5a46f81a504562b7c8a0c4ff940e2b5b5c068ded diff --git a/cinelerra-5.1/cinelerra/ffmpeg.C b/cinelerra-5.1/cinelerra/ffmpeg.C index 873abc2e..47c603f2 100644 --- a/cinelerra-5.1/cinelerra/ffmpeg.C +++ b/cinelerra-5.1/cinelerra/ffmpeg.C @@ -33,6 +33,7 @@ #include "libmjpeg.h" #include "mainerror.h" #include "mwindow.h" +#include "preferences.h" #include "vframe.h" #ifdef FFMPEG3 @@ -98,6 +99,12 @@ void FFrame::dequeue() fst->dequeue(this); } +void FFrame::set_hw_frame(AVFrame *frame) +{ + av_frame_free(&frm); + frm = frame; +} + int FFAudioStream::read(float *fp, long len) { long n = len * nch; @@ -263,6 +270,8 @@ FFStream::FFStream(FFMPEG *ffmpeg, AVStream *st, int fidx) seek_pos = curr_pos = 0; seeked = 1; eof = 0; reading = writing = 0; + hw_pixfmt = AV_PIX_FMT_NONE; + hw_device_ctx = 0; flushed = 0; need_packet = 1; frame = fframe = 0; @@ -278,6 +287,7 @@ FFStream::~FFStream() if( reading > 0 || writing > 0 ) avcodec_close(avctx); if( avctx ) avcodec_free_context(&avctx); if( fmt_ctx ) avformat_close_input(&fmt_ctx); + if( hw_device_ctx ) av_buffer_unref(&hw_device_ctx); if( bsfc ) av_bsf_free(&bsfc); while( frms.first ) frms.remove(frms.first); if( filter_graph ) avfilter_graph_free(&filter_graph); @@ -323,6 +333,27 @@ int FFStream::encode_activate() return writing; } +static AVPixelFormat hw_pix_fmt = AV_PIX_FMT_NONE; // protected by ff_lock +static enum AVPixelFormat get_hw_format(AVCodecContext *ctx, + const enum AVPixelFormat *pix_fmts) +{ + for( const enum AVPixelFormat *p=pix_fmts; *p!=AV_PIX_FMT_NONE; ++p ) + if( *p == hw_pix_fmt ) return *p; + fprintf(stderr, "Failed to get HW surface format.\n"); + return hw_pix_fmt = AV_PIX_FMT_NONE; +} + + +AVHWDeviceType FFStream::decode_hw_activate() +{ + return AV_HWDEVICE_TYPE_NONE; +} + +int FFStream::decode_hw_format(AVCodec *decoder, AVHWDeviceType type) +{ + return 0; +} + int FFStream::decode_activate() { if( reading < 0 && (reading=ffmpeg->decode_activate()) > 0 ) { @@ -331,6 +362,8 @@ int FFStream::decode_activate() AVDictionary *copts = 0; av_dict_copy(&copts, ffmpeg->opts, 0); int ret = 0; + AVHWDeviceType hw_type = decode_hw_activate(); + // this should be avformat_copy_context(), but no copy avail ret = avformat_open_input(&fmt_ctx, ffmpeg->fmt_ctx->url, ffmpeg->fmt_ctx->iformat, &copts); @@ -339,7 +372,7 @@ int FFStream::decode_activate() st = fmt_ctx->streams[fidx]; load_markers(); } - if( ret >= 0 && st != 0 ) { + while( ret >= 0 && st != 0 && !reading ) { AVCodecID codec_id = st->codecpar->codec_id; AVCodec *decoder = avcodec_find_decoder(codec_id); avctx = avcodec_alloc_context3(decoder); @@ -347,20 +380,50 @@ int FFStream::decode_activate() eprintf(_("cant allocate codec context\n")); ret = AVERROR(ENOMEM); } + if( ret >= 0 && hw_type != AV_HWDEVICE_TYPE_NONE ) { + ret = decode_hw_format(decoder, hw_type); + if( !ret ) hw_type = AV_HWDEVICE_TYPE_NONE; + } if( ret >= 0 ) { avcodec_parameters_to_context(avctx, st->codecpar); if( !av_dict_get(copts, "threads", NULL, 0) ) avctx->thread_count = ffmpeg->ff_cpus(); ret = avcodec_open2(avctx, decoder, &copts); } + if( ret >= 0 && hw_type != AV_HWDEVICE_TYPE_NONE ) { + if( need_packet ) { + need_packet = 0; + ret = read_packet(); + } + if( ret >= 0 ) { + AVPacket *pkt = (AVPacket*)ipkt; + ret = avcodec_send_packet(avctx, pkt); + if( ret < 0 || hw_pix_fmt == AV_PIX_FMT_NONE ) { + ff_err(ret, "HW device init failed, using SW decode.\nfile:%s\n", + ffmpeg->fmt_ctx->url); + avcodec_close(avctx); + avcodec_free_context(&avctx); + av_buffer_unref(&hw_device_ctx); + hw_device_ctx = 0; + hw_type = AV_HWDEVICE_TYPE_NONE; + int flags = AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY; + int idx = st->index; + av_seek_frame(fmt_ctx, idx, INT64_MIN, flags); + need_packet = 1; flushed = 0; + seeked = 1; st_eof(0); + ret = 0; + continue; + } + } + } if( ret >= 0 ) { reading = 1; } else eprintf(_("open decoder failed\n")); } - else - eprintf(_("can't clone input file\n")); + if( ret < 0 ) + eprintf(_("can't open input file: %s\n"), ffmpeg->fmt_ctx->url); av_dict_free(&copts); ff_unlock(); } @@ -392,10 +455,11 @@ int FFStream::decode(AVFrame *frame) AVPacket *pkt = ret > 0 ? (AVPacket*)ipkt : 0; if( pkt ) { if( pkt->stream_index != st->index ) continue; - if( !pkt->data | !pkt->size ) continue; + if( !pkt->data || !pkt->size ) continue; } if( (ret=avcodec_send_packet(avctx, pkt)) < 0 ) { - ff_err(ret, "FFStream::decode: avcodec_send_packet failed\n"); + ff_err(ret, "FFStream::decode: avcodec_send_packet failed.\nfile:%s\n", + ffmpeg->fmt_ctx->url); break; } need_packet = 0; @@ -477,14 +541,15 @@ int FFStream::write_packet(FFPacket &pkt) } } if( ret < 0 ) - ff_err(ret, "FFStream::write_packet: write packet failed\n"); + ff_err(ret, "FFStream::write_packet: write packet failed.\nfile:%s\n", + ffmpeg->fmt_ctx->url); return ret; } int FFStream::encode_frame(AVFrame *frame) { int pkts = 0, ret = 0; - for( int retry=100; --retry>=0; ) { + for( int retry=MAX_RETRY; --retry>=0; ) { if( frame || !pkts ) ret = avcodec_send_frame(avctx, frame); if( !ret && frame ) return pkts; @@ -501,7 +566,8 @@ int FFStream::encode_frame(AVFrame *frame) if( ret < 0 ) break; } } - ff_err(ret, "FFStream::encode_frame: encode failed\n"); + ff_err(ret, "FFStream::encode_frame: encode failed.\nfile: %s\n", + ffmpeg->fmt_ctx->url); return -1; } @@ -515,7 +581,8 @@ int FFStream::flush() close_stats_file(); } if( ret < 0 ) - ff_err(ret, "FFStream::flush"); + ff_err(ret, "FFStream::flush failed\n:file:%s\n", + ffmpeg->fmt_ctx->url); return ret >= 0 ? 0 : 1; } @@ -564,7 +631,8 @@ int FFStream::write_stats_file() if( avctx->stats_out && (ret=strlen(avctx->stats_out)) > 0 ) { int len = fwrite(avctx->stats_out, 1, ret, stats_fp); if( ret != len ) - ff_err(ret = AVERROR(errno), "FFStream::write_stats_file"); + ff_err(ret = AVERROR(errno), "FFStream::write_stats_file.\n%file:%s\n", + ffmpeg->fmt_ctx->url); } return ret; } @@ -656,7 +724,8 @@ int FFStream::seek(int64_t no, double rate) //some codecs need more than one pkt to resync if( ret == AVERROR_INVALIDDATA ) ret = 0; if( ret < 0 ) { - ff_err(ret, "FFStream::avcodec_send_packet failed\n"); + ff_err(ret, "FFStream::avcodec_send_packet failed.\nseek:%s\n", + ffmpeg->fmt_ctx->url); break; } } @@ -766,9 +835,11 @@ int FFAudioStream::decode_frame(AVFrame *frame) frame->best_effort_timestamp = AV_NOPTS_VALUE; int ret = avcodec_receive_frame(avctx, frame); if( ret < 0 ) { - if( first_frame || ret == AVERROR(EAGAIN) ) return 0; + if( first_frame ) return 0; + if( ret == AVERROR(EAGAIN) ) return 0; if( ret == AVERROR_EOF ) { st_eof(1); return 0; } - ff_err(ret, "FFAudioStream::decode_frame: Could not read audio frame\n"); + ff_err(ret, "FFAudioStream::decode_frame: Could not read audio frame.\nfile:%s\n", + ffmpeg->fmt_ctx->url); return -1; } int64_t pkt_ts = frame->best_effort_timestamp; @@ -940,15 +1011,136 @@ FFVideoStream::~FFVideoStream() { } +AVHWDeviceType FFVideoStream::decode_hw_activate() +{ + AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE; + const char *hw_dev = ffmpeg->opt_hw_dev; + if( !hw_dev ) hw_dev = getenv("CIN_HW_DEV"); + if( !hw_dev ) hw_dev = ffmpeg->ff_hw_dev(); + if( hw_dev && *hw_dev && strcmp(_("none"), hw_dev) ) { + type = av_hwdevice_find_type_by_name(hw_dev); + if( type == AV_HWDEVICE_TYPE_NONE ) { + fprintf(stderr, "Device type %s is not supported.\n", hw_dev); + fprintf(stderr, "Available device types:"); + while( (type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE ) + fprintf(stderr, " %s", av_hwdevice_get_type_name(type)); + fprintf(stderr, "\n"); + } + } + return type; +} + +int FFVideoStream::decode_hw_format(AVCodec *decoder, AVHWDeviceType type) +{ + int ret = 0; + hw_pix_fmt = AV_PIX_FMT_NONE; + for( int i=0; ; ++i ) { + const AVCodecHWConfig *config = avcodec_get_hw_config(decoder, i); + if( !config ) { + fprintf(stderr, "Decoder %s does not support device type %s.\n", + decoder->name, av_hwdevice_get_type_name(type)); + break; + } + if( (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX) != 0 && + config->device_type == type ) { + hw_pix_fmt = config->pix_fmt; + break; + } + } + if( hw_pix_fmt >= 0 ) { + hw_pixfmt = hw_pix_fmt; + avctx->get_format = get_hw_format; + ret = av_hwdevice_ctx_create(&hw_device_ctx, type, 0, 0, 0); + if( ret >= 0 ) { + avctx->hw_device_ctx = av_buffer_ref(hw_device_ctx); + ret = 1; + } + else + ff_err(ret, "Failed HW device create.\ndev:%s\n", + av_hwdevice_get_type_name(type)); + } + return ret; +} + +AVHWDeviceType FFVideoStream::encode_hw_activate(const char *hw_dev) +{ + AVBufferRef *hw_device_ctx = 0; + AVBufferRef *hw_frames_ref = 0; + AVHWDeviceType type = AV_HWDEVICE_TYPE_NONE; + if( strcmp(_("none"), hw_dev) ) { + type = av_hwdevice_find_type_by_name(hw_dev); + if( type != AV_HWDEVICE_TYPE_VAAPI ) { + fprintf(stderr, "currently, only vaapi hw encode is supported\n"); + type = AV_HWDEVICE_TYPE_NONE; + } + } + if( type != AV_HWDEVICE_TYPE_NONE ) { + int ret = av_hwdevice_ctx_create(&hw_device_ctx, AV_HWDEVICE_TYPE_VAAPI, 0, 0, 0); + if( ret < 0 ) { + ff_err(ret, "Failed to create a HW device.\n"); + type = AV_HWDEVICE_TYPE_NONE; + } + } + if( type != AV_HWDEVICE_TYPE_NONE ) { + hw_frames_ref = av_hwframe_ctx_alloc(hw_device_ctx); + if( !hw_frames_ref ) { + fprintf(stderr, "Failed to create HW frame context.\n"); + type = AV_HWDEVICE_TYPE_NONE; + } + } + if( type != AV_HWDEVICE_TYPE_NONE ) { + AVHWFramesContext *frames_ctx = (AVHWFramesContext *)(hw_frames_ref->data); + frames_ctx->format = AV_PIX_FMT_VAAPI; + frames_ctx->sw_format = AV_PIX_FMT_NV12; + frames_ctx->width = width; + frames_ctx->height = height; + frames_ctx->initial_pool_size = 0; // 200; + int ret = av_hwframe_ctx_init(hw_frames_ref); + if( ret >= 0 ) { + avctx->hw_frames_ctx = av_buffer_ref(hw_frames_ref); + if( !avctx->hw_frames_ctx ) ret = AVERROR(ENOMEM); + } + if( ret < 0 ) { + ff_err(ret, "Failed to initialize HW frame context.\n"); + type = AV_HWDEVICE_TYPE_NONE; + } + av_buffer_unref(&hw_frames_ref); + } + return type; +} + +int FFVideoStream::encode_hw_write(FFrame *picture) +{ + int ret = 0; + AVFrame *hw_frm = 0; + switch( avctx->pix_fmt ) { + case AV_PIX_FMT_VAAPI: + hw_frm = av_frame_alloc(); + if( !hw_frm ) { ret = AVERROR(ENOMEM); break; } + ret = av_hwframe_get_buffer(avctx->hw_frames_ctx, hw_frm, 0); + if( ret < 0 ) break; + ret = av_hwframe_transfer_data(hw_frm, *picture, 0); + if( ret < 0 ) break; + picture->set_hw_frame(hw_frm); + return 0; + default: + return 0; + } + av_frame_free(&hw_frm); + ff_err(ret, "Error while transferring frame data to GPU.\n"); + return ret; +} + int FFVideoStream::decode_frame(AVFrame *frame) { int first_frame = seeked; seeked = 0; int ret = avcodec_receive_frame(avctx, frame); if( ret < 0 ) { - if( first_frame || ret == AVERROR(EAGAIN) ) return 0; + if( first_frame ) return 0; if( ret == AVERROR(EAGAIN) ) return 0; if( ret == AVERROR_EOF ) { st_eof(1); return 0; } - ff_err(ret, "FFVideoStream::decode_frame: Could not read video frame\n"); + ff_err(ret, "FFVideoStream::decode_frame: Could not read video frame.\nfile:%s\n,", + ffmpeg->fmt_ctx->url); return -1; } int64_t pkt_ts = frame->best_effort_timestamp; @@ -997,7 +1189,14 @@ int FFVideoStream::video_seek(int64_t pos) int FFVideoStream::init_frame(AVFrame *picture) { - picture->format = avctx->pix_fmt; + switch( avctx->pix_fmt ) { + case AV_PIX_FMT_VAAPI: + picture->format = AV_PIX_FMT_NV12; + break; + default: + picture->format = avctx->pix_fmt; + break; + } picture->width = avctx->width; picture->height = avctx->height; int ret = av_frame_get_buffer(picture, 32); @@ -1015,6 +1214,8 @@ int FFVideoStream::encode(VFrame *vframe) frame->pts = curr_pos; ret = convert_pixfmt(vframe, frame); } + if( ret >= 0 && avctx->hw_frames_ctx ) + encode_hw_write(picture); if( ret >= 0 ) { picture->queue(curr_pos); ++curr_pos; @@ -1139,6 +1340,22 @@ int FFVideoConvert::convert_picture_vframe(VFrame *frame, AVFrame *ip, AVFrame * } AVPixelFormat pix_fmt = (AVPixelFormat)ip->format; + FFVideoStream *vid =(FFVideoStream *)this; + if( pix_fmt == vid->hw_pixfmt ) { + int ret = 0; + if( !sw_frame && !(sw_frame=av_frame_alloc()) ) + ret = AVERROR(ENOMEM); + if( !ret ) { + ret = av_hwframe_transfer_data(sw_frame, ip, 0); + ip = sw_frame; + pix_fmt = (AVPixelFormat)ip->format; + } + if( ret < 0 ) { + eprintf(_("Error retrieving data from GPU to CPU\nfile: %s\n"), + vid->ffmpeg->fmt_ctx->url); + return -1; + } + } convert_ctx = sws_getCachedContext(convert_ctx, ip->width, ip->height, pix_fmt, frame->get_w(), frame->get_h(), ofmt, SWS_POINT, NULL, NULL, NULL); if( !convert_ctx ) { @@ -1149,7 +1366,8 @@ int FFVideoConvert::convert_picture_vframe(VFrame *frame, AVFrame *ip, AVFrame * int ret = sws_scale(convert_ctx, ip->data, ip->linesize, 0, ip->height, ipic->data, ipic->linesize); if( ret < 0 ) { - ff_err(ret, "FFVideoConvert::convert_picture_frame: sws_scale() failed\n"); + ff_err(ret, "FFVideoConvert::convert_picture_frame: sws_scale() failed\nfile: %s\n", + vid->ffmpeg->fmt_ctx->url); return -1; } return 0; @@ -1328,6 +1546,7 @@ FFMPEG::FFMPEG(FileBase *file_base) opt_duration = -1; opt_video_filter = 0; opt_audio_filter = 0; + opt_hw_dev = 0; fflags = 0; char option_path[BCTEXTLEN]; set_option_path(option_path, "%s", "ffmpeg.opts"); @@ -1347,6 +1566,7 @@ FFMPEG::~FFMPEG() av_dict_free(&opts); delete [] opt_video_filter; delete [] opt_audio_filter; + delete [] opt_hw_dev; } int FFMPEG::check_sample_rate(AVCodec *codec, int sample_rate) @@ -1572,20 +1792,20 @@ int FFMPEG::can_render(const char *fformat, const char *type) int FFMPEG::get_ff_option(const char *nm, const char *options, char *value) { - for( const char *cp=options; *cp!=0; ) { - char line[BCTEXTLEN], *bp = line, *ep = bp+sizeof(line)-1; - while( bp < ep && *cp && *cp!='\n' ) *bp++ = *cp++; - if( *cp ) ++cp; - *bp = 0; - if( !line[0] || line[0] == '#' || line[0] == ';' ) continue; - char key[BCSTRLEN], val[BCTEXTLEN]; - if( FFMPEG::scan_option_line(line, key, val) ) continue; - if( !strcmp(key, nm) ) { - strncpy(value, val, BCSTRLEN); - return 0; - } - } - return 1; + for( const char *cp=options; *cp!=0; ) { + char line[BCTEXTLEN], *bp = line, *ep = bp+sizeof(line)-1; + while( bp < ep && *cp && *cp!='\n' ) *bp++ = *cp++; + if( *cp ) ++cp; + *bp = 0; + if( !line[0] || line[0] == '#' || line[0] == ';' ) continue; + char key[BCSTRLEN], val[BCTEXTLEN]; + if( FFMPEG::scan_option_line(line, key, val) ) continue; + if( !strcmp(key, nm) ) { + strncpy(value, val, BCSTRLEN); + return 0; + } + } + return 1; } void FFMPEG::scan_audio_options(Asset *asset, EDL *edl) @@ -1618,7 +1838,7 @@ void FFMPEG::load_audio_options(Asset *asset, EDL *edl) { char options_path[BCTEXTLEN]; set_option_path(options_path, "audio/%s", asset->acodec); - if( !load_options(options_path, + if( !load_options(options_path, asset->ff_audio_options, sizeof(asset->ff_audio_options)) ) scan_audio_options(asset, edl); @@ -1665,7 +1885,7 @@ void FFMPEG::load_video_options(Asset *asset, EDL *edl) { char options_path[BCTEXTLEN]; set_option_path(options_path, "video/%s", asset->vcodec); - if( !load_options(options_path, + if( !load_options(options_path, asset->ff_video_options, sizeof(asset->ff_video_options)) ) scan_video_options(asset, edl); @@ -1792,6 +2012,8 @@ int FFMPEG::read_options(FILE *fp, const char *options, AVDictionary *&opts) opt_video_filter = cstrdup(val); else if( !strcmp(key, "audio_filter") ) opt_audio_filter = cstrdup(val); + else if( !strcmp(key, "cin_hw_dev") ) + opt_hw_dev = cstrdup(val); else if( !strcmp(key, "loglevel") ) set_loglevel(val); else @@ -1948,8 +2170,10 @@ int FFMPEG::init_decoder(const char *filename) ff_lock("FFMPEG::init_decoder"); av_register_all(); char file_opts[BCTEXTLEN]; - char *bp = strrchr(strcpy(file_opts, filename), '/'); - char *sp = strrchr(!bp ? file_opts : bp, '.'); + strcpy(file_opts, filename); + char *bp = strrchr(file_opts, '/'); + if( !bp ) bp = file_opts; + char *sp = strrchr(bp, '.'); if( !sp ) sp = bp + strlen(bp); FILE *fp = 0; AVInputFormat *ifmt = 0; @@ -2265,9 +2489,20 @@ int FFMPEG::open_encoder(const char *type, const char *spec) vid->frame_rate = asset->frame_rate; AVPixelFormat pix_fmt = av_get_pix_fmt(asset->ff_pixel_format); + if( opt_hw_dev != 0 ) { + AVHWDeviceType hw_type = vid->encode_hw_activate(opt_hw_dev); + switch( hw_type ) { + case AV_HWDEVICE_TYPE_VAAPI: + pix_fmt = AV_PIX_FMT_VAAPI; + break; + case AV_HWDEVICE_TYPE_NONE: + default: break; + } + } if( pix_fmt == AV_PIX_FMT_NONE ) pix_fmt = codec->pix_fmts ? codec->pix_fmts[0] : AV_PIX_FMT_YUV420P; ctx->pix_fmt = pix_fmt; + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(pix_fmt); int mask_w = (1<log2_chroma_w)-1; ctx->width = (vid->width+mask_w) & ~mask_w; @@ -2882,6 +3117,11 @@ int FFMPEG::ff_cpus() return file_base->file->cpus; } +const char *FFMPEG::ff_hw_dev() +{ + return &file_base->file->preferences->use_hw_dev[0]; +} + int FFVideoStream::create_filter(const char *filter_spec, AVCodecParameters *avpar) { avfilter_register_all();