//#include <iostream>
//1、目录要对应上
//2、头文件和lib 版本要对 ffmpeg3.42 ffmpeg4.2
//3、c++调用C语言的时候 需要一个extern
//extern "C"
//{
//
//}
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/opt.h>
}
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"swscale.lib")
int main()
{
//const char* url = "http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8";
const char *url = "1.mp4";
int ret = -1;
//测试和查看操作系统使用情况和支持库
printf("%s\n", avcodec_configuration());
//常规流程第一步注册
av_register_all();//注册所有的库
//注册 建立format空间
创建封装格式上下文 结构体常用数据有
1.AvinputFormatContext 一些和封装格式有关的数据
2.nb_streams: 输入文件信息和各种流的个数
3.duration:输入文件的时长
4.bit_rate:输入文件的码率
AVFormatContext * pFormat = avformat_alloc_context();
avformat_network_init();
常规配置头文件
AVDictionary* opt = NULL; //=》专用头文件 opt.h
av_dict_set(&opt, "rtsp_transport", "tcp", 0);
av_dict_set(&opt, "max_delay", "550", 0);
常规流程第二步通过URL将封装信息写入开辟好的format空间 并且 设置opt
ret = avformat_open_input(&pFormat, url,0, &opt);
if (ret!=0)
{
printf("avformat_open_input error");
system("pause");
return -1;
}
//寻找流文件 新版本和老版本的方法
//老的用的是遍历 新的是直接用新接口来做
printf("avformat_open_input success\n");
常规第三步1 读取媒体文件的format 以获取流信息
ret = avformat_find_stream_info(pFormat, 0);
if (ret < 0)
{
printf("avformat_find_stream_info error");
system("pause");
return -1;
}
printf("avformat_find_stream_info success");
//总时长
int time = pFormat->duration;
int mbtimer = (time / 1000000) / 60;
int mintimer = (time / 1000000) % 60;
printf("%d分:%d秒\n", mbtimer, mintimer);
av_dump_format(pFormat, 0, url, 0);
AVStream => int 寻找流
常规第三步2 读取媒体文件的format 以获取流信息
参数第三个为是否有指定流 如果-1 不指定走默认
参数第四个是否找相关的流 如果-1 不着
参数第五个是如果不为空 可以指定解码器相关的流 传0/NULL代表不需要
int videoStream = av_find_best_stream(pFormat, AVMEDIA_TYPE_VIDEO, -1, -1, 0,0);
int AudioStream = av_find_best_stream(pFormat, AVMEDIA_TYPE_AUDIO, -1, -1, 0,0);
寻找解码器
首先这四步函数在4.0已经修改 寻找对应解码器
结构为AvFormatContext -> AVstream[0] -> AvCodecContext ->AVCodec ->AVcodec ID
AVCodecContext *avctx = pFormat->streams[videoStream]->codec;
AVCodec * avcodec = avcodec_find_decoder(avctx->codec_id);
//const AVCodec *codec, AVDictionary **options
//打开解码器
ret = avcodec_open2(avctx, avcodec, 0);
if (ret !=0)
{
printf("avcodec_open2 error");
system("pause");
return -1;
}
printf("avcodec_open2 success");
创建Frame 空间 Frame 为解码后的帧文件格式
AVFrame* Frame = av_frame_alloc();
AVFrame* Frameyuv = av_frame_alloc();
从编码器上下文中 获得图像格式信息
int width = avctx->width;
int heigt = avctx->height;
int fmt = avctx->pix_fmt;//AVPixelFormat
printf("宽:%d,高:%d\n", width, heigt);
//分配空间 进行图像转换
目前4.0 已经放弃 获得解码后一帧文件大小
int nSize = avpicture_get_size(AV_PIX_FMT_YUV420P, width, heigt);
//数据具体格式转换 并且开辟空间
uint8_t* buff =(uint8_t*) av_malloc(nSize);
//目前4.0 已经放弃 一帧图像大小 这里不太懂 上面和下面的逻辑是什么 是否是写入还是缓冲区关系
avpicture_fill((AVPicture *)Frameyuv,
buff,
AV_PIX_FMT_YUV420P, width, heigt);
开辟AVPacket 其为编码后一个视频帧文件
AVPacket*packet = (AVPacket*)av_malloc(sizeof(AVPacket));
//转换上下文 写入转换前格式和转换后格式
SwsContext* sct = NULL;
sct = sws_getContext (width, heigt,
(AVPixelFormat)fmt,
width, heigt, AV_PIX_FMT_YUV420P,
SWS_BICUBIC,0,0,0);
//读帧
int FrameCount = 0;
int go;
//av_read_frame 不太懂
while (av_read_frame(pFormat,packet)>=0)
{
//判断是否是视频流
if (packet->stream_index == AVMEDIA_TYPE_VIDEO)
{
//解码视频流
ret = avcodec_decode_video2(avctx,Frame,&go,packet);
if (ret < 0)
{
printf("avcodec_decode_video2");
return -1;
}
//解码成功就转换数据
if (go)
{
//转换格式 大小等等操作的地方
int h = sws_scale(sct, (const uint8_t**)Frame->data,
Frame->linesize,
0,
heigt,
Frameyuv->data,
Frameyuv->linesize);
///文件写入
文件编码
//fwrite 查看码流
FrameCount++;
printf("当前高度为:%d,当前转换第%d帧\n",h, FrameCount);
}
}
av_free_packet(packet);
}
//free context();
sws_freeContext(sct);
av_frame_free(&Frame);
av_frame_free(&Frameyuv);
avformat_close_input(&pFormat);
avformat_free_context(pFormat);
return 0;
}
代码梳理ffmpeg解封装流程
最新推荐文章于 2022-10-05 16:55:05 发布