ffmpeg:FFmpeg的名称来自MPEG视频编码标准,前面的“FF”代表“Fast Forward,是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。
平时我们下载的电影的文件的后缀(avi,mkv,rmvb等)就是所谓的封装方式,解封装就是将这些封装格式转为压缩的视频数据(h264)和压缩音频数据(aac),解码就是把压缩的视频数据(h264)和压缩音频数据(aac),解码成非压缩的视频颜色数据(YUV、RGB)和非压缩的音频抽样数据(pcm)。编码就是非压缩的视频颜色数据(YUV、RGB)和非压缩的音频抽样数据(pcm)编码成压缩的视频数据(h264)和压缩音频数据(aac)。
一般我们从摄像头RTSP获取得到视频流都是“裸流”,也就是原始数据流。得到的码流一般是h264,或者h265,用av_read_frame()来读取每一帧的数据,数据是存放在结构体AVpack里面。是再把它经过解码成YUV像素数据。数据是存放在结构体AVFrame里面。即把 AVpack 存放的数据 (h264、h265)解码成 AVFrame 存放的数据(YUV),可保存成YUV444、YUV422、YUV420,一般以 YUV420为主。
YUV420格式:Y(亮度)、U(色度)、V(浓度)。Y占8位,U、V每4个点共有一个,共8 + 8 / 4 + 8 / 4 = 12位,即3 / 2byte
YUV420的数据长度:
把视频流编码成YUV并保存成文件的流程:
实现过程如下:
#include
#include
#ifdef __cplusplus
extern "C"
{
#endif
/*Include ffmpeg header file*/
#include
#include
#include
#ifdef __cplusplus
}
#endif
int main()
{
AVFormatContext *pFormatCtx = NULL;
AVCodecContext *pCodecCtx = NULL;
AVCodec *pCodec = NULL;
AVDictionary *options = NULL;
AVPacket *packet = NULL;
AVFrame *pFrameYUV = NULL;
char filepath[] = "rtsp://admin:[email protected]:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1";
//av_register_all(); //函数在ffmpeg4.0以上版本已经被废弃,所以4.0以下版本就需要注册初始函数
av_dict_set(&options, "buffer_size", "1024000", 0); //设置缓存大小,1080p可将值跳到最大
av_dict_set(&options, "rtsp_transport", "tcp", 0); //以tcp的方式打开,
av_dict_set(&options, "stimeout", "5000000", 0); //设置超时断开链接时间,单位us
av_dict_set(&options, "max_delay", "500000", 0); //设置最大时延
pFormatCtx = avformat_alloc_context(); //用来申请AVFormatContext类型变量并初始化默认参数,申请的空间
//打开网络流或文件流
if (avformat_open_input(&pFormatCtx, filepath, NULL, &options) != 0)
{
printf("Couldn't open input stream.\n");
return 0;
}
//获取视频文件信息
if (avformat_find_stream_info(pFormatCtx, NULL)<0)
{
printf("Couldn't find stream information.\n");
return 0;
}
//查找码流中是否有视频流
int videoindex = -1;
unsigned i = 0;
for (i = 0; inb_streams; i++)
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
videoindex = i;
break;
}
if (videoindex == -1)
{
printf("Didn't find a video stream.\n");
return 0;
}
pCodecCtx = pFormatCtx->streams[videoindex]->codec;
pCodec = avcodec_find_decoder(pCodecCtx->codec_id); //寻找解码器
if (pCodec == NULL)
{
printf("Codec not found.\n");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec, NULL)<0) //打开解码器
{
printf("Could not open codec.\n");
return -1;
}
packet = (AVPacket *)av_malloc(sizeof(AVPacket)); // 申请空间,存放的每一帧数据 (h264、h265)
pFrameYUV = av_frame_alloc(); // 申请空间,存放每一帧编码后的YUV数据
AVStream *video_stream = pFormatCtx->streams[videoindex];
FILE *fp_YUV = NULL;
int got_picture = 0;
fp_YUV = fopen("getYUV.yuv", "wb");
//这边可以调整i的大小来改变文件中的视频时间,每 +1 就是一帧数据
for (i = 0; i < 200; i++)
{
if (av_read_frame(pFormatCtx, packet) >= 0)
{
if (packet->stream_index == videoindex)
{
avcodec_decode_video2(pCodecCtx,pFrameYUV, &got_picture, packet);
fwrite(pFrameYUV->data[0], 1, video_stream->codecpar->width*video_stream->codecpar->height,fp_YUV); //Y
fwrite(pFrameYUV->data[1], 1, video_stream->codecpar->width*video_stream->codecpar->height/4,fp_YUV);//U
fwrite(pFrameYUV->data[2], 1, video_stream->codecpar->width*video_stream->codecpar->height/4,fp_YUV);//V
printf("w: %d, H: %d\n", video_stream->codecpar->width, video_stream->codecpar->height);
}
av_packet_unref(packet);
}
}
fclose(fp_YUV);
av_free(packet);
av_frame_free(&pFrameYUV);
avformat_close_input(&pFormatCtx);
return 0;
}