ffmpeg解封装流程
设置参数
打开url之前可以选择设置参数
AVDictionary *opts = nullptr;
av_dict_set(&opts,"rtsp_transport","tcp",0);//传输媒体流协议为tcp,默认为udp 可不设置
av_dict_set(&opts,"stimeout","1000000",0);//链接超时1秒, -stimeout 设置在你要解析的url之后是没有作用的
打开解封装上下文
//打开封装上下文
AVFormatContext *c = nullptr;
auto re = avformat_open_input(&c,url,
nullptr,//封装器的格式 可以根据后缀或者文件头 自动探测
&opts//参数设置 ,rtsp 需要设置
);
url 为本地文件地址或者网络地址 如 海康摄像头的地址:
rtsp://test:x12345678@192.168.2.64/h264/ch1/main/av_stream
如果设置了opts 别忘了释放
if(opts){
av_dict_free(&opts);
}
获取媒体信息
re = avformat_find_stream_info(c,nullptr);
//可以打印媒体信息
av_dump_format(c,0,url,0);
获取音频流和视频流相关信息
for(int i = 0;i< c->nb_streams;i++){
//音频
if( c->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
audio_index_ = i;
//也可以直接复制 因为内存完全一样 audio_time_base = streams[i]->time_base;
audio_time_base_.den = c->streams[i]->time_base.den;
audio_time_base_.num = c->streams[i]->time_base.num;
}else if( c->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
video_index_ = i;
video_time_base_.den = c->streams[i]->time_base.den;
video_time_base_.num = c->streams[i]->time_base.num;
video_codec_id_ = c->streams[i]->codecpar->codec_id;
}
}
创建视频解码上下文
AVCodec *dcodec = nullptr;
dcodec = avcodec_find_decoder(video_codec_id_);
auto decodeContext = avcodec_alloc_context3(dcodec);
可以设置一下参数默认值
decodeContext->time_base = {1,25};
decodeContext->pix_fmt = AV_PIX_FMT_YUV420P;
decodeContext->thread_count = 16;
复制视频参数给解码上下文
video_index_为视频流索引
avcodec_parameters_to_context(decodeContext,c->streams[video_index_]->codecpar);
打开视频解码器
auto re = avcodec_open2(decodeContext,NULL,NULL);
创建音频解码上下文同视频一样
audio_index_为音频流索引
AVCodec *dacodec = nullptr;
dacodec = avcodec_find_decoder(c->streams[audio_index_]->codecpar->codec_id);
auto audioDecodeContext = avcodec_alloc_context3(dacodec);
avcodec_parameters_to_context(audioDecodeContext,c->streams[audio_index_]->codecpar);
auto re = avcodec_open2(decodeContext,NULL,NULL);
解封装开始
AVPacket pkt;
while (true) {
//读出来完整一帧 一般这里在没连接成功时会有阻塞
auto re = av_read_frame(con_,&pkt);
//这里一般使用数组把解封装出来的pkt 保存起来,因为解封装和解码是线程并发执行的 要注意 线程之间的资源调用 一般使用锁来解决
pkt_list_.Push(pkt);
}
解码过程
分配AVFrame
AVFrame frame_ = av_frame_alloc();
从数组里面取出pkt
auto pkt = pkt_list_.Pop();
//con_ 区别为音视频解码上下文
auto re = avcodec_send_packet(con_,pkt);
av_packet_free(&pkt);
auto re = avcodec_receive_frame(con_,frame_);
解码出来的frame 如果是音频的话建议放到数组中缓存起来,因为音频播放速度可能会赶不上解码的速度
auto f = av_frame_alloc();
av_frame_ref(f,frame_);
frames_.push_back(f);
//如果用数组存储的话 记得释放frame_
if(frame_){
av_frame_free(&frame_);
}
至此解封装完成 音频播放可以用sdl 或者其他的库
网友评论