2. 打开文件:avformat_open_input()
3. 从文件中宽态提取流信息:avformat_find_stream_info()
4. 查找视频流音频流索引:av_find_best_stream()
5. 查找对应的解码器:avcodec_find_decoder()
6. 打开编解码器:avcodec_open2()
7. 为解码帧分配内存:av_frame_alloc()
8. 不停地从码流中提取出帧数据:av_read_frame()
9. 进行解码:avcodec_send_packet()
10. 获取解码后的数据:avcodec_receive_frame()
11. 解码完槐碰后,释放解码器:avcodec_close()
12. 关闭输入文件:avformat_close_input()
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
}
//输入文件路径
char *fileName = ""
int main()
{
//output file
FILE *outputfile_video = nullptr
FILE *outputfile_audio = nullptr
char *outputfilename_video = "output_video.yuv"
char *outputfilename_audio = "output_audio.pcm"
outputfile_video = fopen(outputfilename_video, "wb")
outputfile_audio = fopen(outputfilename_audio, "wb")
AVFormatContext *formatctx = NULL
AVCodec *videoCodec = NULL
AVCodec *audioCodec = NULL
AVCodecContext *videoCodecctx = NULL
AVCodecContext *audioCodecctx = NULL
AVFrame *frame = NULL
AVFrame *frameYUV = NULL
AVPacket avpkt
AVPacket *pavpkt = &avpkt
//init
av_register_all()
formatctx = avformat_alloc_context()
//打开文件
if (avformat_open_input(&formatctx,fileName,NULL,NULL)!=0)
{
printf("could not open input stream.\n")
return -1
}
//查找流信息
if (avformat_find_stream_info(formatctx, NULL) <0)
{
printf("could not find stream information.\n")
return -1
}
//==============video================
//查找视频流
int videoindex = av_find_best_stream(formatctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0)
if (videoindex <0)
{
printf("could not find a video stream.\n")
return -1
}
//find the video decoder
videoCodecctx = avcodec_alloc_context3(NULL)
if (videoCodecctx == NULL)
{
printf("could not allocate video AVCodecContext.\n")
return -1
}
avcodec_parameters_to_context(videoCodecctx, formatctx->streams[videoindex]->codecpar)
videoCodec = avcodec_find_decoder(videoCodecctx->codec_id)
if (videoCodec == NULL)
{
printf("vidoe codec not found.\n")
return -1
}
//open the video decoder
if (avcodec_open2(videoCodecctx, videoCodec, NULL) <0)
{
printf("could not open video codec.\n")
return -1
}
//=====================================
//================audio===================
//查找音频
int audioindex = av_find_best_stream(formatctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0)
if (audioindex <0)
{
printf("could not find a audio stream.\n")
return -1
}
//find the audio decoder
audioCodecctx = avcodec_alloc_context3(NULL)
if (audioCodecctx == NULL)
{
printf("could not allocate audio AVCodecContext.\n")
return -1
}
avcodec_parameters_to_context(audioCodecctx, formatctx->streams[audioindex]->codecpar)
audioCodec = avcodec_find_decoder(audioCodecctx->codec_id)
if (audioCodec == NULL)
{
printf("audio codec not found.\n")
return -1
}
//open the audio decoder
if (avcodec_open2(audioCodecctx, audioCodec, NULL) <0)
{
printf("could not open audio codec.\n")
return -1
}
//========================================
//output information
av_dump_format(formatctx, videoindex, fileName, 0)
av_init_packet(&avpkt)
frame = av_frame_alloc()
frameYUV = av_frame_alloc()
int video_dst_bufsize = av_image_alloc((uint8_t**)frameYUV->data, frameYUV->linesize, videoCodecctx->width, videoCodecctx->height, videoCodecctx->pix_fmt, 1)
printf("decode video file %s to %s and %s\n", fileName, outputfilename_video, outputfilename_audio)
//decode
int frame_count = 0
while (av_read_frame(formatctx,pavpkt)>=0)
{
if (pavpkt->stream_index == videoindex)
{
if (avcodec_send_packet(videoCodecctx, pavpkt) != 0)
{
printf("input AVPacket to video decoder failed!\n")
return -1
}
while (0 == avcodec_receive_frame(videoCodecctx, frame))
{
av_image_copy((uint8_t **)frameYUV->data,frameYUV->linesize,(const uint8_t **)frame->data,frame->linesize,videoCodecctx->pix_fmt,videoCodecctx->width,videoCodecctx->height)
fwrite(frameYUV->data[0], 1, video_dst_bufsize, outputfile_video)
printf("decoded frame index: %d\n", frame_count)
frame_count++
}
}
else if (pavpkt->stream_index == audioindex)
{
if (avcodec_send_packet(audioCodecctx, pavpkt) != 0)
{
printf("input AVPacket to audio decoder failed!\n")
return -1
}
while (0 == avcodec_receive_frame(audioCodecctx, frame))
{
size_t unpadded_linesize = frame->nb_samples*av_get_bytes_per_sample((AVSampleFormat)frame->format)
fwrite(frame->extended_data[0], 1, unpadded_linesize, outputfile_audio)
printf("decoded audio.\n")
}
}
av_packet_unref(pavpkt)
}
fclose(outputfile_video)
fclose(outputfile_audio)
av_frame_free(&frame)
av_frame_free(&frameYUV)
avcodec_close(videoCodecctx)
avcodec_close(audioCodecctx)
avformat_close_input(&formatctx)
return 0
}
下一篇将介绍一下 PCM文件编码为AAC
第一步:组册组件
av_register_all()
例如:编码器、解码器等等…
第二步:打开封装格式->打开文件
例如:.mp4、.mov、.wmv文件等等...
avformat_open_input()
第三步:查找旁族视频流
如果是视频解码,那么查找视频流,如果是音频解码,那么就查找音频流
avformat_find_stream_info()
第四步:查找视频解码器
1、查找视频流索引位置
2、根据视频流索引,获取解码器上下文
3、根据解码器上下文,获得解码器ID,然后查找解码器
第五步:打开解码器
avcodec_open2()
第六步:读取视频压缩数据->循环读取
没读取一帧数据,立马解码一帧数据
第七步:视频解码->播放视频->得到视频像素数据
第八步:关闭解码器->解码完成
//第一步:组册组件
av_register_all()
//第二步:打开封装格式->打开文件
//参数一:封装格式上下文
//作用:保存整个视频信息(解码器、编码器等等...)
//信息:码率、帧率等...
AVFormatContext* avformat_context = avformat_alloc_context()
//参数二:视频路径
const char *url = [jinFilePath UTF8String]
//参数三:指定输入的格式
//参数四:设置默认参数
int avformat_open_input_result = avformat_open_input(&avformat_context, url,NULL, NULL)
if (avformat_open_input_result !=0){
NSLog("打开文件失败")
//不同的平台替换不同平台log日志
return
}
//第三步:查找视频流->拿到视频信息
//参数一:封装格式上下文
//哗启轮参数二:指定默认配置
int avformat_find_stream_info_result = avformat_find_stream_info(avformat_context,NULL)
if (avformat_find_stream_info_result <0){
NSLog(" 查找失败")
return
}
//第四步:查找视频解码器
//1、查找视频流索引位置
int av_stream_index = -1
for (int i =0i <avformat_context->nb_streams++i) {
//判断流类型:视频流、音频流、字母流等等...
if (avformat_context->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
av_stream_index = i
break
}
}
//2、根据视频流索引,获取解码器上下文
AVCodecContext *avcodec_context = avformat_context->streams[av_stream_index]->codec
//3、根据解码器上下文,获得解码器ID,然后查找解码器
AVCodec *avcodec = avcodec_find_decoder(avcodec_context->codec_id)
//第五步:打开乱信解码器
int avcodec_open2_result = avcodec_open2(avcodec_context, avcodec,NULL)
if (avcodec_open2_result !=0){
NSLog("打开解码器失败")
return
}
//第六步:读取视频压缩数据->循环读取
//1、分析av_read_frame参数
//参数一:封装格式上下文
//参数二:一帧压缩数据 = 一张图片
//av_read_frame()
//结构体大小计算:字节对齐原则
AVPacket* packet = (AVPacket*)av_malloc(sizeof(AVPacket))
//3.2 解码一帧视频压缩数据->进行解码(作用:用于解码 *** 作)
//开辟一块内存空间
AVFrame* avframe_in = av_frame_alloc()
int decode_result =0
//4、注意:在这里我们不能够保证解码出来的一帧视频像素数据格式是yuv格式
//参数一:源文件->原始视频像素数据格式宽
//参数二:源文件->原始视频像素数据格式高
//参数三:源文件->原始视频像素数据格式类型
//参数四:目标文件->目标视频像素数据格式宽
//参数五:目标文件->目标视频像素数据格式高
//参数六:目标文件->目标视频像素数据格式类型
SwsContext *swscontext = sws_getContext(avcodec_context->width,
avcodec_context->height,
avcodec_context->pix_fmt,
avcodec_context->width,
avcodec_context->height,
AV_PIX_FMT_YUV420P,
SWS_BICUBIC,
NULL,
NULL,
NULL)
//创建一个yuv420视频像素数据格式缓冲区(一帧数据)
AVFrame* avframe_yuv420p = av_frame_alloc()
//给缓冲区设置类型->yuv420类型
//得到YUV420P缓冲区大小
//参数一:视频像素数据格式类型->YUV420P格式
//参数二:一帧视频像素数据宽 = 视频宽
//参数三:一帧视频像素数据高 = 视频高
//参数四:字节对齐方式->默认是1
int buffer_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,
avcodec_context->width,
avcodec_context->height,
1)
//开辟一块内存空间
uint8_t *out_buffer = (uint8_t *)av_malloc(buffer_size)
//向avframe_yuv420p->填充数据
//参数一:目标->填充数据(avframe_yuv420p)
//参数二:目标->每一行大小
//参数三:原始数据
//参数四:目标->格式类型
//参数五:宽
//参数六:高
//参数七:字节对齐方式
av_image_fill_arrays(avframe_yuv420p->data,
avframe_yuv420p->linesize,
out_buffer,
AV_PIX_FMT_YUV420P,
avcodec_context->width,
avcodec_context->height,
1)
int y_size, u_size, v_size
//5.2 将yuv420p数据写入.yuv文件中
//打开写入文件
const char *outfile = [joutFilePath UTF8String]
FILE* file_yuv420p = fopen(outfile,"wb+")
if (file_yuv420p ==NULL){
NSLog("输出文件打开失败")
return
}
int current_index =0
while (av_read_frame(avformat_context, packet) >=0){
//>=:读取到了
//<0:读取错误或者读取完毕
//2、是否是我们的视频流
if (packet->stream_index == av_stream_index){
//第七步:解码
//学习一下C基础,结构体
//3、解码一帧压缩数据->得到视频像素数据->yuv格式
//采用新的API
//3.1 发送一帧视频压缩数据
avcodec_send_packet(avcodec_context, packet)
//3.2 解码一帧视频压缩数据->进行解码(作用:用于解码 *** 作)
decode_result = avcodec_receive_frame(avcodec_context, avframe_in)
if (decode_result ==0){
//解码成功
//4、注意:在这里我们不能够保证解码出来的一帧视频像素数据格式是yuv格式
//视频像素数据格式很多种类型: yuv420P、yuv422p、yuv444p等等...
//保证:我的解码后的视频像素数据格式统一为yuv420P->通用的格式
//进行类型转换: 将解码出来的视频像素点数据格式->统一转类型为yuv420P
//sws_scale作用:进行类型转换的
//参数一:视频像素数据格式上下文
//参数二:原来的视频像素数据格式->输入数据
//参数三:原来的视频像素数据格式->输入画面每一行大小
//参数四:原来的视频像素数据格式->输入画面每一行开始位置(填写:0->表示从原点开始读取)
//参数五:原来的视频像素数据格式->输入数据行数
//参数六:转换类型后视频像素数据格式->输出数据
//参数七:转换类型后视频像素数据格式->输出画面每一行大小
sws_scale(swscontext,
(const uint8_t *const *)avframe_in->data,
avframe_in->linesize,
0,
avcodec_context->height,
avframe_yuv420p->data,
avframe_yuv420p->linesize)
//方式一:直接显示视频上面去
//方式二:写入yuv文件格式
//5、将yuv420p数据写入.yuv文件中
//5.1 计算YUV大小
//分析一下原理?
//Y表示:亮度
//UV表示:色度
//有规律
//YUV420P格式规范一:Y结构表示一个像素(一个像素对应一个Y)
//YUV420P格式规范二:4个像素点对应一个(U和V: 4Y = U = V)
y_size = avcodec_context->width * avcodec_context->height
u_size = y_size /4
v_size = y_size /4
//5.2 写入.yuv文件
//首先->Y数据
fwrite(avframe_yuv420p->data[0], 1, y_size, file_yuv420p)
//其次->U数据
fwrite(avframe_yuv420p->data[1], 1, u_size, file_yuv420p)
//再其次->V数据
fwrite(avframe_yuv420p->data[2], 1, v_size, file_yuv420p)
current_index++
NSLog("当前解码第%d帧", current_index)
}
}
}
//第八步:释放内存资源,关闭解码器
av_packet_free(&packet)
fclose(file_yuv420p)
av_frame_free(&avframe_in)
av_frame_free(&avframe_yuv420p)
free(out_buffer)
avcodec_close(avcodec_context)
avformat_free_context(avformat_context)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)