FFmpeg总结
FFmpeg 开源音视频库,主要用来处理视频数据等,可以编码,解码,缩放,音频重采样等功能
1:mp4文件抽取H264文件
2:H264文件抽取YUV文件
抽取出yuv数据主要包含3步
1:首先将mp4文件中的H264码流文件抽取出来
2:将h264文件送进去解码
3:得到解码后的每一帧数据,进行保存
上述3步的步骤代码详细如下:
首先是将对后续要使用的ffmpeg API 和对象初始化
// 初始化基本参数,主要包括
// avpacket 解码前数据
// avCodec AVCodec指针对象
// avCodecParserContext AVCodecParserContext指针对象,主要
// avCodecContext AVCodecContext指针对象 编码器codec的上下文
// avFrame AVFrame 指针对象 存放解码后数据
avPacket = av_packet_alloc();
if (!avPacket) {
LOGE("AVPACKET open failed");
goto end;
}
memset(input + INBUF_SIZE_NEW, 0, AV_INPUT_BUFFER_PADDING_SIZE);
avCodec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!avCodec) {
LOGE("avcodec_find_decoder open failed");
goto end;
}
LOGE("avcodec_find_decoder open sucess");
avCodecParserContext = av_parser_init(avCodec->id);
if (!avCodecParserContext) {
LOGE("avCodecParserContext init failed");
goto end;
}
LOGE("av_parser_init open sucess");
avCodecContext = avcodec_alloc_context3(avCodec);
// av_find_best_stream(avFormatContext,AVMEDIA_TYPE_VIDEO,)
if (!avCodecContext) {
LOGE("avcodec_alloc_context3 failed");
goto end;
}
LOGE("avcodec_alloc_context3 open sucess");
result = avcodec_open2(avCodecContext, avCodec, nullptr);
if (result < 0) {
LOGE("avcodec_open2 failed");
goto end;
}
LOGE("avcodec_open2 open sucess");
file = fopen(src_file, "rbe");
if (!file) {
LOGE("open file failed, error is %s:", strerror(errno));
}
LOGE("fopen open sucess");
avFrame = av_frame_alloc();
if (!avFrame) {
LOGE("avframe alloc failed");
goto end;
}
第二步将文件读取的数据进行读取倒avpacket中
while (!feof(file)) {
//每次从文件中读取INBUF_SIZE_NEW + AV_INPUT_BUFFER_PADDING_SIZE = 4096 +64 个大小的数据
data_size = fread(input, 1, INBUF_SIZE_NEW, file);
if (!data_size) {
break;
}
LOGE("fread sucess :%d ", data_size);
data = input;
while (data_size > 0) {
//将4096 +64 个数据中读取到result个数据到avPacket
result = av_parser_parse2(avCodecParserContext, avCodecContext, &avPacket->data,
&avPacket->size, data, data_size, AV_NOPTS_VALUE,
AV_NOPTS_VALUE, 0);
LOGE("av_parser_parse2 open sucess");
if (result < 0) {
LOGE("av_parser_parse2 failed");
goto end;;
}
//指针后移
data += result;
data_size -= result;
//数据送去解码,并保存成yuv文件
if (avPacket->size) {
decode(avCodecContext, avFrame, avPacket, des_file);
}
LOGE("decode sucess");
}
}
decode(avCodecContext, avFrame, nullptr, des_file);
将avpacket数据送到解码器中解码
static void decode(AVCodecContext *avCodecContext, AVFrame *avFrame, AVPacket *pkt,
const char *filename) {
char buf[1024];
int ret;
//将packet发送给codec
ret = avcodec_send_packet(avCodecContext, pkt);
if (ret < 0) {
__android_log_print(ANDROID_LOG_ERROR, TAG_ZHF, "Error sending a packet for decoding: %s\n",
av_err2str(ret));
return;
}
while (ret >= 0) {
//解码出frame并存入avFrame参数
ret = avcodec_receive_frame(avCodecContext, avFrame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
return;
} else if (ret < 0) {
__android_log_write(ANDROID_LOG_ERROR, TAG_ZHF, "Error during decoding\n");
return;
}
//为防止文件太多观察不便,每20个avFrame中抽取一个并保存为文件
if (avCodecContext->frame_number % 20 == 0) {
__android_log_print(ANDROID_LOG_ERROR, TAG_ZHF, "saving avFrame %3d\n",
avCodecContext->frame_number);
/* the picture is allocated by the decoder. no need to
free it */
//拼接文件名
//C库函数:int snprintf(char *str, size_t size, const char *format, ...),将可变参数(...)按照format格式化成字符串,
//并将字符串复制到str中,size为要写入的字符的最大数目,超过size会被截断。
snprintf(buf, sizeof(buf), "%s-%d.yuv", filename, avCodecContext->frame_number);
yuv_save(avFrame, buf);
}
}
}
将数据保存成yuv文件
tatic void yuv_save(AVFrame *avFrame, char *filename) {
FILE *file;
file = fopen(filename, "we");
if (!file) {
__android_log_write(ANDROID_LOG_ERROR, TAG_ZHF, "Could not open out file\n");
__android_log_write(ANDROID_LOG_ERROR, TAG_ZHF, strerror(errno));
return;
}
int width = avFrame->width;
int height = avFrame->height;
for (int i = 0; i < height; i++)
fwrite(avFrame->data[0] + i * avFrame->linesize[0], 1, width, file);
for (int j = 0; j < height / 2; j++)
fwrite(avFrame->data[1] + j * avFrame->linesize[1], 1, width / 2, file);
for (int k = 0; k < height / 2; k++)
fwrite(avFrame->data[2] + k * avFrame->linesize[2], 1, width / 2, file);
fclose(file);
}
yuv文件展示.png
详细代码参考github:
网友评论