1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
| int main(int argc, char *argv[]) { AVFormatContext *input_ctx = NULL; int video_stream, ret; AVStream *video = NULL; AVCodecContext *decoder_ctx = NULL; const AVCodec *decoder = NULL; AVPacket *packet = NULL; enum AVHWDeviceType type; int i;
if (argc < 4) { fprintf(stderr, "Usage: %s <device type> <input file> <output file>\n", argv[0]); return -1; } //根据传入加速器名字,找到对应的type type = av_hwdevice_find_type_by_name(argv[1]); if (type == AV_HWDEVICE_TYPE_NONE) { fprintf(stderr, "Device type %s is not supported.\n", argv[1]); fprintf(stderr, "Available device types:"); //没找到加速器,打印ffmepg 支持的加速器列表 while((type = av_hwdevice_iterate_types(type)) != AV_HWDEVICE_TYPE_NONE) fprintf(stderr, " %s", av_hwdevice_get_type_name(type)); fprintf(stderr, "\n"); return -1; } //申请packet保存解码前数据 packet = av_packet_alloc(); if (!packet) { fprintf(stderr, "Failed to allocate AVPacket\n"); return -1; }
//打开输入的文件,关联到AVFormatContext if (avformat_open_input(&input_ctx, argv[2], NULL, NULL) != 0) { fprintf(stderr, "Cannot open input file '%s'\n", argv[2]); return -1; } //填充AVFormatContext,获取解封装信息 if (avformat_find_stream_info(input_ctx, NULL) < 0) { fprintf(stderr, "Cannot find input stream information.\n"); return -1; }
//找到video 对应的stream index, 获取对应的解码器信息 ret = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0); if (ret < 0) { fprintf(stderr, "Cannot find a video stream in the input file\n"); return -1; } video_stream = ret;
//遍历获取解码器对应的硬件配置信息 for (i = 0;; i++) { const AVCodecHWConfig *config = avcodec_get_hw_config(decoder, i); //如果没找到支持的config,结束程序 if (!config) { fprintf(stderr, "Decoder %s does not support device type %s.\n", decoder->name, av_hwdevice_get_type_name(type)); return -1; } /* 如果config的device_type匹配我们设置的类型,并且config的methods的值为AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX 保存支持的hw_pix_fmt,跳出循环 关于AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX 如果我们选择了此种类型,需要在调用avcodec_open2()打开解码器之前,给AVCodecContext.hw_device_ctx设置正确的值。 */ if (config->methods & AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX && config->device_type == type) { hw_pix_fmt = config->pix_fmt; break; } } //创建解码器上下文 if (!(decoder_ctx = avcodec_alloc_context3(decoder))) return AVERROR(ENOMEM);
video = input_ctx->streams[video_stream]; //给解码器上下文填充参数,复制从文件中读的的解码器参数 if (avcodec_parameters_to_context(decoder_ctx, video->codecpar) < 0) return -1; //get_format是一个函数指针,我们给ffmpeg提供一个函数,ffmpeg 调用该函数确定解码帧的数据格式 decoder_ctx->get_format = get_hw_format;
//根据AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX的定义,我们需要给ctx->hw_device_ctx赋值 if (hw_decoder_init(decoder_ctx, type) < 0) return -1; //打开解码器 if ((ret = avcodec_open2(decoder_ctx, decoder, NULL)) < 0) { fprintf(stderr, "Failed to open codec for stream #%u\n", video_stream); return -1; }
/* open the file to dump raw data */ output_file = fopen(argv[3], "w+b");
/* actual decoding and dump the raw data */ while (ret >= 0) { //从文件中循环读视频帧,存入packet if ((ret = av_read_frame(input_ctx, packet)) < 0) break;
if (video_stream == packet->stream_index) //解码packet,写文件 ret = decode_write(decoder_ctx, packet);
av_packet_unref(packet); } //冲洗解码器 /* flush the decoder */ ret = decode_write(decoder_ctx, NULL);
if (output_file) fclose(output_file); av_packet_free(&packet); avcodec_free_context(&decoder_ctx); avformat_close_input(&input_ctx); av_buffer_unref(&hw_device_ctx);
return 0; }
|