我使用的ffmpeg是3.4版本 对应的帮助文档是api文档是
AVFormatContext:容器相关结构体,比如MP4,flv等。
AVCodecContext:编解码器相关结构体。
AVCodec:编解码器参数相关结构体。
AVStream 对流的抽象
AVOutputFormat:对输出文件格式的抽象
AvFrame:用来存原始帧数据,比如编码前的yuv数据,或者解码后的yuv数据。
AvPacket:用来存编码后的包数据,或者解码前的包数据,比如h264的帧数据I,P,B帧数据。
av_register_all();
注册相关编解码器,和混合器(视频和音频混合),和分流器(将视频和音频分流)
avformat_network_init();
网络相关的注册,如果还没有做网络相关的播放可以不用注册,比如你使用了rtmp相关的操作就需要注册。
result = avformat_open_input(&afc, path, 0, 0);
注意 :要先将afc = NULL ,不然会有异常发生。
这个是打开输入流
打开输入流并且读取文件头,比如MP4的头部信息,一般会有音频,视频相关的参数信息。
记得用avformat_close_input关闭。
result = avformat_find_stream_info(afc, 0);
这个获取输入的相关信息,上面已经打开了,现在我们就是要使用里面的相关信息,比如视频一般有视频流,或者音频流,甚至还有字幕流,今天我们就解码视频我们就关系视频就行了。
afc->nb_streams
这个是告诉我们这个文件一共有几个流(音频视频等)
for (int i = 0; i < afc->nb_streams; ++i) {
AVStream *avStream = afc->streams[i];
if (avStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
//视频
video_index = i;
videoCode = avcodec_find_decoder(avStream->codecpar->codec_id);
if (avStream->codecpar->format != AV_PIX_FMT_YUV420P) {
cj->callStr("目前只支持yuv420p的格式");
return RESULT_FAILD;
}
if (!videoCode) {
LOGE("VIDEO avcodec_find_decoder FAILD!");
return RESULT_FAILD;
}
} else if (avStream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
//音频
audio_index = i;
this->simpleRate = avStream->codecpar->sample_rate;
LOGE("audio samplerate %d ", avStream->codecpar->sample_rate);
audioCode = avcodec_find_decoder(avStream->codecpar->codec_id);
if (!audioCode) {
LOGE("audio avcodec_find_decoder FAILD!");
return RESULT_FAILD;
}
}
}
遍历的方式拿到音频,视频的流对应的index也可以说轨道,顺便也把他的解码code找到。
vc = avcodec_alloc_context3(videoCode);
申请一个AVCodecContext
用来解码使用的
avcodec_parameters_to_context(vc, afc->streams[video_index]->codecpar);
记得把对应的参数copy过去,不然解码器不知道容器中的视频是怎么样的。比如帧率,分辨率等。
result = avcodec_open2(vc, NULL, NULL);
初始化解码器。
result = av_read_frame(afc, pkt_);
这个是从容器中读取avpacket这个还是编码状态的packet
读取容器中的packet我是单独使用的线程,读取后分别根据是音频还是视频再分发出去,使用观察者模式。
result = avcodec_send_packet(vc, pck);
然后送入解码
result = avcodec_receive_frame(vc, vframe);
这个是解码后的数据
vframe->pts
就是当前帧的显示时间
还有关于avframe相关的参数
vframe->data[0]
vframe->data[1]
vframe->data[2]
如果我们数据是yuv420p相关的数据
那么data[0]指向的就是y的,data[1]指向的就是u,data[2]指向的就是v。
音频的pcm,如果是双声道的并且是平面存储的那么data[0]指向一个声道,data[1]指向一个声道。
比如双声道存储方式有: lrlrlrlrlr 也有llll rrrr.
l对应是左声道,r对应右声道
前者是直接存储在data[0]中,后者是分开成data[0]存储左声道,data[1]存储右声道。
vframe->linesize[0]
vframe->linesize[1]
vframe->linesize[2]
Linesize:视频的话就是一个图像横向有多少个字节(那不就是图像的宽度么?不全是),因为为了读写速度的原因,会有cpu相关的补齐措施,有可能是比width大的。
如果是对应yuv420p
Linesize[0]: y对应的字节数
Linesize[1]:u对应的字节数,一般是y的一半
Linesize[2]:v对应的字节数,同上
如果是音频的话就是对应的声道占多少个字节。
frame->nb_samples:这个是一帧音频有多少个采样(1024比较多),当然也只是我们这样叫,并不是1024就是一帧,这个不和视频一样,视频是一个画面就是一帧。
对应项目
myffmpeg
这是一个整体的app,现在只是将其中的小模块分解,记录下。
转载:https://blog.csdn.net/u010339039/article/details/88797731