• 【视频开发】【CUDA开发】ffmpeg nvenc编码


     花时间研究了一些ffmpeg的nvenc,本来想我已经有了cuvid,然后又搞出来了nvenc,应该可以做个全套的英伟达的转码了,没想到ffmpeg官网下载的动态库没有cuvid,windows上编译cuvid又老是出错,忧了个伤。

    1.nvenc编码

        h264_nvenc是很容易调出来的,把编码器ffmpeg源码自带的例子的编码器换成h264_nvenc就行了。可是hevc_nvenc就花了我好多时间,感觉调试技术还是差了好多。

    复制代码
    #include "stdafx.h"
    
    /*
    * Video encoding example
    */
    static void video_encode_example(const char *filename)
    {
        AVCodec *codec;
        AVCodecContext *c = NULL;
        int i, ret, x, y, got_output;
        AVFrame *frame;
        AVPacket pkt;
        uint8_t endcode[] = { 0, 0, 1, 0xb7 };
    
        av_log_set_level(64);
    
        //AVBufferRef *device_ref = NULL;
        //AVBufferRef *hw_frames_ctx = NULL;
        //hw_frames_ctx = (AVBufferRef *)av_mallocz(sizeof(AVBufferRef));
        //if (!hw_frames_ctx) {
        //    ret = AVERROR(ENOMEM);
        //    return ;
        //}
    
        //ret = av_hwdevice_ctx_create(&device_ref, AV_HWDEVICE_TYPE_CUDA,"CUDA", NULL, 0);
        //if (ret < 0)
        //    return;
    
        //hw_frames_ctx = av_hwframe_ctx_alloc(device_ref);
        //if (!hw_frames_ctx) {
        //    av_log(NULL, AV_LOG_ERROR, "av_hwframe_ctx_alloc failed
    ");
        //    ret = AVERROR(ENOMEM);
        //    return;
        //}
        //av_buffer_unref(&device_ref);
    
        //c->hw_frames_ctx = av_buffer_ref(hw_frames_ctx);
        //if (!hw_frames_ctx) {
        //    av_log(NULL, AV_LOG_ERROR, "av_buffer_ref failed
    ");
        //    ret = AVERROR(ENOMEM);
        //    return;
        //}
    
        printf("Encode video file %s
    ", filename);
    
        /* find the video encoder */
        codec = avcodec_find_encoder_by_name("hevc_nvenc");
        //codec = avcodec_find_encoder(AV_CODEC_ID_H265);
        //codec = avcodec_find_encoder_by_name("h264_nvenc");
        //codec = avcodec_find_encoder(AV_CODEC_ID_H264);
        if (!codec) {
            fprintf(stderr, "Codec not found
    ");
            exit(1);
        }
    
        c = avcodec_alloc_context3(codec);
        if (!c) {
            fprintf(stderr, "Could not allocate video codec context
    ");
            exit(1);
        }
    
        /* put sample parameters */
        c->bit_rate = 400000;
        /* resolution must be a multiple of two */
        c->width = 352;
        c->height = 288;
        /* frames per second */
        c->time_base.num = 1;
        c->time_base.den = 25;
        /* emit one intra frame every ten frames
        * check frame pict_type before passing frame
        * to encoder, if frame->pict_type is AV_PICTURE_TYPE_I
        * then gop_size is ignored and the output of encoder
        * will always be I frame irrespective to gop_size
        */
        c->gop_size = 10;
        c->max_b_frames = 1;
        c->pix_fmt = AV_PIX_FMT_YUV420P;//AV_PIX_FMT_CUDA;
        c->max_b_frames = 0;
    
        AVDictionary *param = 0;
        //H.264
        if (codec->id == AV_CODEC_ID_H264) {
            av_dict_set(&param, "preset", "medium", 0);
            av_dict_set(&param, "tune", "zerolatency", 0);
        }
        //H.265
        if (codec->id == AV_CODEC_ID_H265 || codec->id == AV_CODEC_ID_HEVC){
            //av_dict_set(&param, "x265-params", "qp=20", 0);
            av_dict_set(&param, "x265-params", "crf=25", 0);
            av_dict_set(&param, "preset", "fast", 0);
            av_dict_set(&param, "tune", "zero-latency", 0);
        }
    
        /* open it */
        if (avcodec_open2(c, codec, &param) < 0) {
            fprintf(stderr, "Could not open codec
    ");
            system("pause");
            exit(1);
        }
    
        FILE *f;
        f = fopen(filename, "wb");
        if (!f) {
            fprintf(stderr, "Could not open %s
    ", filename);
            exit(1);
        }
    
        frame = av_frame_alloc();
        if (!frame) {
            fprintf(stderr, "Could not allocate video frame
    ");
            exit(1);
        }
        frame->format = c->pix_fmt;
        frame->width = c->width;
        frame->height = c->height;
    
        /* the image can be allocated by any means and av_image_alloc() is
        * just the most convenient way if av_malloc() is to be used */
        ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height,
            c->pix_fmt, 32);
        if (ret < 0) {
            fprintf(stderr, "Could not allocate raw picture buffer
    ");
            exit(1);
        }
    
        /* encode 1 second of video */
        for (i = 0; i < 500; i++) {
            av_init_packet(&pkt);
            pkt.data = NULL;    // packet data will be allocated by the encoder
            pkt.size = 0;
    
            fflush(stdout);
            /* prepare a dummy image */
            /* Y */
            for (y = 0; y < c->height; y++) {
                for (x = 0; x < c->width; x++) {
                    frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
                }
            }
    
            /* Cb and Cr */
            for (y = 0; y < c->height / 2; y++) {
                for (x = 0; x < c->width / 2; x++) {
                    frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
                    frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
                }
            }
    
            frame->pts = i;
    
            /* encode the image */
            ret = avcodec_encode_video2(c, &pkt, frame, &got_output);
            if (ret < 0) {
                fprintf(stderr, "Error encoding frame
    ");
                exit(1);
            }
    
            if (got_output) {
                printf("Write frame %3d (size=%5d)
    ", i, pkt.size);
                fwrite(pkt.data, 1, pkt.size, f);
                av_packet_unref(&pkt);
            }
        }
    
        /* get the delayed frames */
        for (got_output = 1; got_output; i++) {
            fflush(stdout);
    
            ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
            if (ret < 0) {
                fprintf(stderr, "Error encoding frame
    ");
                exit(1);
            }
    
            if (got_output) {
                printf("Write frame %3d (size=%5d)
    ", i, pkt.size);
                fwrite(pkt.data, 1, pkt.size, f);
                av_packet_unref(&pkt);
            }
        }
    
        /* add sequence end code to have a real MPEG file */
        fwrite(endcode, 1, sizeof(endcode), f);
        fclose(f);
    
        avcodec_close(c);
        av_free(c);
        av_freep(&frame->data[0]);
        av_frame_free(&frame);
        printf("
    ");
    }
    
    int main(int argc, char **argv)
    {
        /* register all the codecs */
        avcodec_register_all();
    
        avcodec_register_all();
    
        avdevice_register_all();
    
        avfilter_register_all();
        av_register_all();
        avformat_network_init();
    
        video_encode_example("test.hevc");
    
        system("pause");
    
        return 0;
    }
    复制代码

    代码中av_log_set_level(64);可以帮助输出中间信息,参数可以自行设置,参数为越大,能输出的信息等级越多,我的问题就是通过这个函数知道的,然后到源码中找对应处才最终解决。编码器为hevc_nvenc时max_b_frames必须为0,即代码中的 c->max_b_frames = 0;另外c->pix_fmt = AV_PIX_FMT_YUV420P;//AV_PIX_FMT_CUDA;这行代码需要注意,设置为AV_PIX_FMT_YUV420P意味着数据是从内存读取的,设置为AV_PIX_FMT_CUDA意味着数据在显存中,AV_PIX_FMT_CUDA与cuvid是一起的,只有编出来的ffmpeg支持cuvid时AV_PIX_FMT_CUDA才有效。

    2.用nvenc做转码

        由于还没有编出支持cuvid的ffmpeg,所以解码这里就先不用cuvid了,用CPU来解码。其实这样有一个好处,就是对格式的要求低,cuvid对格式的输入是有要求的,用这种方法所有用ffmpeg解码后的数据都可以用nvenc来编码,缺点当然是这样比较慢了。

    复制代码
    #include "stdafx.h"
    
    #include <stdio.h>
    #include <io.h>
    
    /*
    * Video encoding example
    */
    static void video_encode_example(const char *filename)
    {
        AVCodec *codec;
        AVCodecContext *c = NULL;
        int i, ret, x, y, got_output;
        AVPacket pkt;
        uint8_t endcode[] = { 0, 0, 1, 0xb7 };
    
        av_log_set_level(64);
    
        AVFormatContext    *pFormatCtx;
        int                videoindex;
        AVCodecContext    *pCodecCtx;
        AVCodec            *pCodec;
        AVFrame    *pFrame, *pFrameYUV;
        uint8_t *out_buffer;
    
        char filepath[] = "H:\nvenc\灿烂人生1280.rmvb";
    
        av_register_all();
        avformat_network_init();
        pFormatCtx = avformat_alloc_context();
    
        if (avformat_open_input(&pFormatCtx, filepath, NULL, NULL) != 0){
            printf("Couldn't open input stream.
    ");
            return ;
        }
        if (avformat_find_stream_info(pFormatCtx, NULL)<0){
            printf("Couldn't find stream information.
    ");
            return ;
        }
        videoindex = -1;
        for (i = 0; i<pFormatCtx->nb_streams; i++)
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
            videoindex = i;
            break;
        }
    
        if (videoindex == -1){
            printf("Didn't find a video stream.
    ");
            return ;
        }
    
        pCodecCtx = pFormatCtx->streams[videoindex]->codec;
        pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
        if (pCodec == NULL){
            printf("Codec not found.
    ");
            return ;
        }
        if (avcodec_open2(pCodecCtx, pCodec, NULL)<0){
            printf("Could not open codec.
    ");
            return ;
        }
    
        pFrame = av_frame_alloc();
        pFrameYUV = av_frame_alloc();
        out_buffer = (uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
        avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
    
        //Output Info-----------------------------
        printf("--------------- File Information ----------------
    ");
        av_dump_format(pFormatCtx, 0, filepath, 0);
        printf("-------------------------------------------------
    ");
        struct SwsContext *img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
            pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
    
    
        printf("Encode video file %s
    ", filename);
    
        /* find the video encoder */
        codec = avcodec_find_encoder_by_name("hevc_nvenc");
        //codec = avcodec_find_encoder(AV_CODEC_ID_H265);
        //codec = avcodec_find_encoder_by_name("h264_nvenc");
        //codec = avcodec_find_encoder(AV_CODEC_ID_H264);
        if (!codec) {
            fprintf(stderr, "Codec not found
    ");
            exit(1);
        }
    
        c = avcodec_alloc_context3(codec);
        if (!c) {
            fprintf(stderr, "Could not allocate video codec context
    ");
            exit(1);
        }
    
        /* put sample parameters */
        c->bit_rate = pCodecCtx->bit_rate;
        /* resolution must be a multiple of two */
        c->width = pCodecCtx->width;
        c->height = pCodecCtx->height;
        c->time_base = pCodecCtx->time_base;
        c->gop_size = pCodecCtx->gop_size;
    
        c->pix_fmt = AV_PIX_FMT_YUV420P;
        c->max_b_frames = 0;
    
        AVDictionary *param = 0;
        //H.264
        if (codec->id == AV_CODEC_ID_H264) {
            av_dict_set(&param, "preset", "medium", 0);
            av_dict_set(&param, "tune", "zerolatency", 0);
        }
        //H.265
        if (codec->id == AV_CODEC_ID_H265 || codec->id == AV_CODEC_ID_HEVC){
            //av_dict_set(&param, "x265-params", "qp=20", 0);
            av_dict_set(&param, "x265-params", "crf=25", 0);
            av_dict_set(&param, "preset", "fast", 0);
            av_dict_set(&param, "tune", "zero-latency", 0);
        }
    
        /* open it */
        if (avcodec_open2(c, codec, &param) < 0) {
            fprintf(stderr, "Could not open codec
    ");
            system("pause");
            exit(1);
        }
    
        FILE *f;
        f = fopen(filename, "wb");
        if (!f) {
            fprintf(stderr, "Could not open %s
    ", filename);
            exit(1);
        }
    
        AVPacket *packet;
        packet = (AVPacket *)av_malloc(sizeof(AVPacket));
        int got_picture;
        int iCount = 0;
        int64_t iStart = av_gettime();
        while (av_read_frame(pFormatCtx, packet) >= 0){
            if (packet->stream_index == videoindex){
                ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
                if (ret < 0){
                    printf("Decode Error.
    ");
                    return ;
                }
                if (got_picture){
                    sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
                        pFrameYUV->data, pFrameYUV->linesize);
    
                    av_init_packet(&pkt);
                    pkt.data = NULL;    // packet data will be allocated by the encoder
                    pkt.size = 0;
    
                    pFrameYUV->width = c->width;
                    pFrameYUV->height = c->height;
                    pFrameYUV->format = c->pix_fmt;
    
                    /* encode the image */
                    ret = avcodec_encode_video2(c, &pkt, pFrameYUV, &got_output);
                    if (ret < 0) {
                        fprintf(stderr, "Error encoding frame
    ");
                        exit(1);
                    }
    
                    if (got_output) {
    
                        iCount++;
                        fwrite(pkt.data, 1, pkt.size, f);
                        av_packet_unref(&pkt);
    
                        if (iCount % 1000 == 0)
                        {
                            printf("1000帧用时:%d   平均每秒 %f 帧 
    ", (av_gettime() - iStart)/100000, (double)1000 * 1000000 / (av_gettime() - iStart));
                            printf("Write frame %3d (size=%5d)
    ", i, pkt.size);
    
                            int fd = _fileno(f); //获取文件描述符
                            _commit(fd);
    
                            iStart = av_gettime();
                        }
    
                        
                    }
    
                }
            }
            av_free_packet(packet);
        }
    
        /* get the delayed frames */
        for (got_output = 1; got_output; i++) {
            fflush(stdout);
    
            ret = avcodec_encode_video2(c, &pkt, NULL, &got_output);
            if (ret < 0) {
                fprintf(stderr, "Error encoding frame
    ");
                exit(1);
            }
    
            if (got_output) {
                printf("Write frame %3d (size=%5d)
    ", i, pkt.size);
                fwrite(pkt.data, 1, pkt.size, f);
                av_packet_unref(&pkt);
            }
        }
    
        /* add sequence end code to have a real MPEG file */
        fwrite(endcode, 1, sizeof(endcode), f);
        fclose(f);
    
        avcodec_close(c);
        av_free(c);
        printf("
    ");
    }
    
    int main(int argc, char **argv)
    {
        /* register all the codecs */
        avcodec_register_all();
    
        avcodec_register_all();
    
        avdevice_register_all();
    
        avfilter_register_all();
        av_register_all();
        avformat_network_init();
    
        video_encode_example("H:\nvenc\test.hevc");
    
        system("pause");
    
        return 0;
    }
    复制代码

    上面用nvenc编出来的并没有封装成文件。h264格式potplayer还可以直接播放,hevc格式就不行了。hevc必须先封装,比如封装成mp4文件,然后才能播放。如何封装的例子网上比较多,我这里给个ffmpeg命令行的示例:

    ffmpeg -i f:25国.265 -c:v copy -f mp4 f:25国.mp4

     

    最后,nvenc对显卡的要求好像比较高,注意查看自己的显卡是否支持nvenc。

    工程源码:http://download.csdn.net/download/qq_33892166/9840113

    源码的ffmpg是64位的。

  • 相关阅读:
    WF4.0 Beta1 自定义跟踪
    WF4.0 Beta1 流程设计器与Activity Designer
    新版本工作流平台的 (二) 权限算法(组织结构部分)
    WF4.0 Beta1 WorkflowInvoker
    WF4.0 基础篇 (十) Collection 集合操作
    WF4.0 基础篇 (十五) TransactionScope 事物容器
    WF4.0 基础篇 (六) 数据的传递 Arguments 参数
    WF4B1 的Procedural Activity 之InvokeMethod , InvokeMethod<T> 使用
    WF4.0 Beta1 异常处理
    WF4.0 Beta1 变量 Variables
  • 原文地址:https://www.cnblogs.com/huty/p/8517139.html
Copyright © 2020-2023  润新知