FFMPEG4.0 音频解码解封装 FFMPEG 音频封装编码 下面的函数方法基于最新的FFMPEG 4.0(4.X): 本文讲是如何从一个视频文件中提取出其中的图像数据,并将图像数据保存到文件中。 解码解封
FFMPEG 音频封装编码
下面的函数方法基于最新的FFMPEG 4.0(4.X):
本文讲是如何从一个视频文件中提取出其中的图像数据,并将图像数据保存到文件中。
解码解封装的过程与音频差不多,具体如下:
1.读取视频文件的格式信息
fmt_ctx = avformat_alloc_context(); avformat_open_input(&fmt_ctx,input,NULL,NULL); avformat_find_stream_info(fmt_ctx,NULL);
2.获取视频流
int st_index = av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0); LOGV("st_index = %d\n",st_index); AVStream *st = fmt_ctx->streams[st_index];
3.准备×××与解码context
AVCodec *codec = avcodec_find_decoder(st->codecpar->codec_id); AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
4.拷贝视频流中的参数到×××context中并打开×××
avcodec_parameters_to_context(codec_ctx,st->codecpar); avcodec_open2(codec_ctx,codec,NULL);
5.读取视频的格式、宽高信息
int width = codec_ctx->width; int height = codec_ctx->height; enum AVPixelFormat pixel_fmt = codec_ctx->pix_fmt;
6.申请图像存储空间
uint8_t *dst_buf[4] = {0}; int dst_linesize[4]; int size = av_image_alloc(dst_buf,dst_linesize,width,height,pixel_fmt,1);
7.申明存储原始数据与解码后数据的packet与frame
AVFrame *frame = av_frame_alloc(); AVPacket *packet = av_packet_alloc();
8.读取数据,只取用视频数据
int ret = av_read_frame(fmt_ctx,packet); //读取到的packet不仅仅是图像数据,还有音频、字幕等数据。 if(packet->stream_index != st_index) { continue; }
9.发送数据进行解码ret = avcodec_send_packet(codec_ctx,packet);
10.接收解码后的原始数据,这是个反复的过程,一个packet可能解码出好几个frame
ret = avcodec_receive_frame(codec_ctx,frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) //packet解码完了,需要sent break; if(ret < 0) { return 1; }
注意:收到的frame可能存在宽高或者fmt格式变化这种情况,后面的流程代码没有考虑这种情况(这种奇葩视频应该不会遇到)
if(frame->width != width || frame->height != height || frame->format != pixel_fmt) { LOGV("eeeeeeeeeeeee"); }
11.把frame中的数据拷贝到事先准备的dst_buf中。二维指针数组看作一位数组。av_image_copy(dst_buf,dst_linesize,frame->data,frame->linesize,pixel_fmt,width,height);
12.把数据写入文件。fwrite(dst_buf[0],1,size,out_file);
下面贴一段完整的示例代码,代码没有考虑失败的情况,结尾没有搞释放,也没有flush×××,示例只是为了掌握整个核心解码流程。
/* * demuxing_decode_video.c * * Created on: 2019年1月8日 * Author: deanliu */ #include <libavutil/imgutils.h> #include <libavutil/samplefmt.h> #include <libavutil/timestamp.h> #include <libavformat/avformat.h> static char log_buf[1024*8]; #define LOGV(...) av_log(NULL,AV_LOG_VERBOSE,__VA_ARGS__) void ffmpeg_log_callback(void* ptr, int level, const char* fmt, va_list vl) { static int print_prefix = 1; av_log_format_line(ptr,level,fmt,vl,log_buf,sizeof(log_buf),&print_prefix); fprintf(stderr,"%s",log_buf); } int main() { av_log_set_callback(ffmpeg_log_callback); char *input = "E:/测试音视频/12种格式视频/test.avi"; char *output = "d:/video.v"; FILE *out_file = fopen(output,"wb"); AVFormatContext *fmt_ctx; fmt_ctx = avformat_alloc_context(); avformat_open_input(&fmt_ctx,input,NULL,NULL); avformat_find_stream_info(fmt_ctx,NULL); int st_index = av_find_best_stream(fmt_ctx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,0); LOGV("st_index = %d\n",st_index); AVStream *st = fmt_ctx->streams[st_index]; AVCodec *codec = avcodec_find_decoder(st->codecpar->codec_id); AVCodecContext *codec_ctx = avcodec_alloc_context3(codec); avcodec_parameters_to_context(codec_ctx,st->codecpar); avcodec_open2(codec_ctx,codec,NULL); int width = codec_ctx->width; int height = codec_ctx->height; enum AVPixelFormat pixel_fmt = codec_ctx->pix_fmt; uint8_t *dst_buf[4] = {0}; int dst_linesize[4]; int size = av_image_alloc(dst_buf,dst_linesize,width,height,pixel_fmt,1); AVFrame *frame = av_frame_alloc(); AVPacket *packet = av_packet_alloc(); while(1) { LOGV("READ\n"); int ret = av_read_frame(fmt_ctx,packet); if(ret < 0){ LOGV("ret = %d\n",ret); break; } if(packet->stream_index != st_index) { continue; } LOGV("SENT\n"); ret = avcodec_send_packet(codec_ctx,packet); if(ret < 0){ return 1; } while(ret >= 0) { LOGV("receiver\n"); ret = avcodec_receive_frame(codec_ctx,frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) break; if(ret < 0) { return 1; } if(frame->width != width || frame->height != height || frame->format != pixel_fmt) { LOGV("eeeeeeeeeeeee"); } av_image_copy(dst_buf,dst_linesize,frame->data,frame->linesize,pixel_fmt,width,height); LOGV("dst_buf = %d,%d,%d,%d\n",dst_buf[2][0],dst_buf[1][1],dst_buf[0][2],dst_buf[0][3]); fwrite(dst_buf[0],1,size,out_file); } } LOGV("dst_linesize = %d,%d,%d,%d\n",dst_linesize[0],dst_linesize[1],dst_linesize[2],size); printf("Play the output video file with the command:\n" "ffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n", av_get_pix_fmt_name(pixel_fmt), width, height, output); LOGV("END!!"); fclose(out_file); return 0; }