zoukankan      html  css  js  c++  java
  • 【视频开发】【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位的。

  • 相关阅读:
    TinyEditor:简洁且易用的html所见即所得编辑器
    arguments.callee 调用自身
    java.io.IOException: 设备未就绪
    关于vcastr3播放器自动播放的问题
    javascript中IE浏览器不支持NEW DATE()带参数的解决方法
    Oracle常用查看表结构命令
    java通过文件头内容判断文件类型
    RS开发日期提示控件默认为昨天
    Cognos更新问题之利用Transform实现Cube增量更新
    SqlServer中从字符串中获取项目指标方法charindex月substring结合
  • 原文地址:https://www.cnblogs.com/huty/p/8517139.html
Copyright © 2011-2022 走看看