从视频中提取图片,涉及到以下流程的处理 。
- 打开输出文件,解封装,找到视频流索引
- 通过视频context初始化decoder
- 对视频帧进行解码
- 初始化输出环境,新建输出流
- 获取图片编码器
- 对帧进行编码 并写到图片文件中
了解以上处理流程以后,对于代码的编写就会了然于胸。
https://github.com/kongxs/ffmpeg_demos/tree/master/toPics
1. 找到视频流
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
| char *file = "/Users/kongxiangshu/Desktop/1111.mp4"; char *outDir = "/Users/kongxiangshu/Desktop/pics"; AVFormatContext *in_context ; in_context = avformat_alloc_context(); if (avformat_open_input(&in_context, file, NULL, NULL) < 0 ) { LOGE("can not open file %s \n" , file); exit(1); } if( avformat_find_stream_info(in_context, NULL) < 0 ) { LOGE("can not find media info \n"); exit(1); } // av_dump_format(in_context, -1, file, 0); int best_stream_index = -1; if(best_stream_index = av_find_best_stream(in_context, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0) < 0 ){ LOGE("can not find video stream \n "); exit(1); } LOGE("best stream index : %d , \n" , best_stream_index); for(int i = 0; i< in_context->nb_streams;i++) { AVStream *stream = in_context->streams[i]; if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { best_stream_index = i; } }
|
2. 通过视频流 获取并打开解码器
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
| AVStream *beststream = in_context->streams[best_stream_index]; AVCodec *codec = avcodec_find_decoder(beststream->codecpar->codec_id); if( codec == NULL) { LOGE("can not find codec \n"); exit(1); } AVCodecContext *codec_context = avcodec_alloc_context3(codec); if (codec_context == NULL) { LOGE("can not alloc codec context \n "); exit(1); } // param if(avcodec_parameters_to_context(codec_context, beststream->codecpar) < 0){ LOGE("copy codec param error "); exit(1); } if ( avcodec_open2(codec_context, codec, NULL) < 0 ) { LOGE("can not open codec \n"); exit(1); }
|
3. 解码frame
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
| while (av_read_frame(in_context, &pkt) >= 0) { if (pkt.stream_index == best_stream_index) { AVStream *stream = in_context->streams[pkt.stream_index] ; if (stream == NULL) { LOGE("error to get stream in read freme \n "); exit(1); } // decode frame if (avcodec_send_packet(codec_context, &pkt) < 0) { LOGE("packet decode failed \n") ; exit(1); } avcodec_receive_frame(codec_context, frame); // ready to save pics snprintf(buf, sizeof(buf), "%s/Demo-%d.jpg", outDir, frame_count); // sprintf(name,"asdffasd"); if (frame_count % 25 == 0) { savePics(frame,buf); } frame_count ++ ; av_packet_unref(&pkt); } }
|
4. 编码并输出图片文件
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
| void savePics(AVFrame *frame, char *name) { //对frame进行编码 ,并保存到文件中 printf("%s \n" , name); AVFormatContext *out_context ; out_context = avformat_alloc_context(); if ( avformat_alloc_output_context2(&out_context, NULL, NULL, name)) { LOGE("输出环境初始化失败 \n "); exit(1); } AVStream *stream = avformat_new_stream(out_context, 0) ; if (stream == NULL) { LOGE("scan not get new stream \n"); } AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG); if (codec == NULL) { LOGE("can not find jpeg codec"); exit(1); } AVCodecContext *avcodec_context = avcodec_alloc_context3(codec); if (avcodec_context == NULL) { LOGE("codec context failed "); exit(1); } avcodec_context->width = frame->width; avcodec_context->height = frame->height; avcodec_context->time_base.num = 100; avcodec_context->time_base.den = 1000; avcodec_context->pix_fmt = AV_PIX_FMT_YUVJ420P; avcodec_context->codec_id = codec->id; avcodec_context->codec_type = AVMEDIA_TYPE_VIDEO; avcodec_open2(avcodec_context, codec, NULL); //writer heaer avformat_write_header(out_context, NULL); //编码帧 int ret = avcodec_send_frame(avcodec_context, frame); if (ret < 0) { LOGE("encode error \n"); exit(1); } int y_size = frame->width * frame->height; //Encode // 给AVPacket分配足够大的空间 AVPacket pkt; av_new_packet(&pkt, y_size * 3); ret = avcodec_receive_packet(avcodec_context, &pkt); if (ret < 0) { LOGE("receive frame error \n "); exit(1); } av_write_frame(out_context, &pkt); av_write_trailer(out_context); av_packet_unref(&pkt); avformat_free_context(out_context); }
|