花時間研究了一些ffmpeg的nvenc,原本想我已經有了cuvid,而後又搞出來了nvenc,應該能夠作個全套的英偉達的轉碼了,沒想到ffmpeg官網下載的動態庫沒有cuvid,windows上編譯cuvid又總是出錯,憂了個傷。windows
h264_nvenc是很容易調出來的,把編碼器ffmpeg源碼自帶的例子的編碼器換成h264_nvenc就好了。但是hevc_nvenc就花了我好多時間,感受調試技術仍是差了好多。ide
#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\n"); // 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\n"); // ret = AVERROR(ENOMEM); // return; //} printf("Encode video file %s\n", 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\n"); exit(1); } c = avcodec_alloc_context3(codec); if (!c) { fprintf(stderr, "Could not allocate video codec context\n"); 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(¶m, "preset", "medium", 0); av_dict_set(¶m, "tune", "zerolatency", 0); } //H.265 if (codec->id == AV_CODEC_ID_H265 || codec->id == AV_CODEC_ID_HEVC){ //av_dict_set(¶m, "x265-params", "qp=20", 0); av_dict_set(¶m, "x265-params", "crf=25", 0); av_dict_set(¶m, "preset", "fast", 0); av_dict_set(¶m, "tune", "zero-latency", 0); } /* open it */ if (avcodec_open2(c, codec, ¶m) < 0) { fprintf(stderr, "Could not open codec\n"); system("pause"); exit(1); } FILE *f; f = fopen(filename, "wb"); if (!f) { fprintf(stderr, "Could not open %s\n", filename); exit(1); } frame = av_frame_alloc(); if (!frame) { fprintf(stderr, "Could not allocate video frame\n"); 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\n"); 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\n"); exit(1); } if (got_output) { printf("Write frame %3d (size=%5d)\n", 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\n"); exit(1); } if (got_output) { printf("Write frame %3d (size=%5d)\n", 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("\n"); } 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纔有效。函數
因爲尚未編出支持cuvid的ffmpeg,因此解碼這裏就先不用cuvid了,用CPU來解碼。其實這樣有一個好處,就是對格式的要求低,cuvid對格式的輸入是有要求的,用這種方法全部用ffmpeg解碼後的數據均可以用nvenc來編碼,缺點固然是這樣比較慢了。ui
#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.\n"); return ; } if (avformat_find_stream_info(pFormatCtx, NULL)<0){ printf("Couldn't find stream information.\n"); 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.\n"); return ; } pCodecCtx = pFormatCtx->streams[videoindex]->codec; pCodec = avcodec_find_decoder(pCodecCtx->codec_id); if (pCodec == NULL){ printf("Codec not found.\n"); return ; } if (avcodec_open2(pCodecCtx, pCodec, NULL)<0){ printf("Could not open codec.\n"); 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 ----------------\n"); av_dump_format(pFormatCtx, 0, filepath, 0); printf("-------------------------------------------------\n"); 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\n", 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\n"); exit(1); } c = avcodec_alloc_context3(codec); if (!c) { fprintf(stderr, "Could not allocate video codec context\n"); 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(¶m, "preset", "medium", 0); av_dict_set(¶m, "tune", "zerolatency", 0); } //H.265 if (codec->id == AV_CODEC_ID_H265 || codec->id == AV_CODEC_ID_HEVC){ //av_dict_set(¶m, "x265-params", "qp=20", 0); av_dict_set(¶m, "x265-params", "crf=25", 0); av_dict_set(¶m, "preset", "fast", 0); av_dict_set(¶m, "tune", "zero-latency", 0); } /* open it */ if (avcodec_open2(c, codec, ¶m) < 0) { fprintf(stderr, "Could not open codec\n"); system("pause"); exit(1); } FILE *f; f = fopen(filename, "wb"); if (!f) { fprintf(stderr, "Could not open %s\n", 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.\n"); 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\n"); 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 幀 \n", (av_gettime() - iStart)/100000, (double)1000 * 1000000 / (av_gettime() - iStart)); printf("Write frame %3d (size=%5d)\n", 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\n"); exit(1); } if (got_output) { printf("Write frame %3d (size=%5d)\n", 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("\n"); } 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。spa
工程源碼:http://download.csdn.net/download/qq_33892166/9840113.net
源碼的ffmpg是64位的。命令行