我使用的ffmpeg是3.4版本 对应的帮助文档是api文档是

ffmpeg 3.4api文档

AVFormatContext:容器相关结构体,比如MP4,flv等。
AVCodecContext:编解码器相关结构体。
AVCodec:编解码器参数相关结构体。
AVStream 对流的抽象
AVOutputFormat:对输出文件格式的抽象
AvFrame:用来存原始帧数据,比如编码前的yuv数据,或者解码后的yuv数据。
AvPacket:用来存编码后的包数据,或者解码前的包数据,比如h264的帧数据I,P,B帧数据。

av_register_all();

注册相关编解码器,和混合器(视频和音频混合),和分流器(将视频和音频分流)
音视频之解码相关-LMLPHP

avformat_network_init();

网络相关的注册,如果还没有做网络相关的播放可以不用注册,比如你使用了rtmp相关的操作就需要注册。

result = avformat_open_input(&afc, path, 0, 0);
注意 :要先将afc = NULL ,不然会有异常发生。
这个是打开输入流
音视频之解码相关-LMLPHP

打开输入流并且读取文件头,比如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,现在只是将其中的小模块分解,记录下。

04-01 04:20