bmp的码流相当费空间,所以一般在传输时,都会编码为其他格式,就效率来说,一般rgb32的原始码流在转换为yuv时是减半的,如果说对画面质素要求不高的,可以转换为jpg码流,当然h264是传输码流的首选,代码如下
#include
extern "C"
{
#include
#include
}
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"swscale.lib")
using namespace std;
int main()
{
char infile[] = "dove_BGRA.rgb";
char outfile[] = "out.264";
// 源图像参数
int width = 640;
int height = 360;
int fps = 25;
//1 打开RGB和H264文件
FILE *fpin = fopen(infile, "rb");
if (!fpin)
{
cout << infile << "open infile failed!" << endl;
getchar();
return -1;
}
FILE *fpout = fopen(outfile, "wb");
if (!fpout)
{
cout << "open outfile failed!" << endl;
exit(1);
}
// 创建RGB缓冲区同时分配内存
unsigned char *rgbBuf = new unsigned char[width*height * 4];
// 注册所有和编解码器有关的组件
av_register_all();
/* 2 创建编码器 */
// 查找编码器
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!codec)
{
cout << "avcodec_find_encoder AV_CODEC_ID_H264 failed!" << endl;
getchar();
return -1;
}
// 给编码器分配内存,返回对应编码器上下文
AVCodecContext *codecCtx = avcodec_alloc_context3(codec);
if (!codecCtx)
{
cout << "avcodec_alloc_context3 failed!" << endl;
getchar();
return -1;
}
// 配置编码器上下文的成员
codecCtx->width = width; // 设置编码视频宽度
codecCtx->height = height; // 设置编码视频高度
codecCtx->time_base.num = 1;
codecCtx->time_base.den = 25; // 设置帧率,num为分子,den为分母,如果是1/25则表示25帧/s
codecCtx->pix_fmt = AV_PIX_FMT_YUV420P; // 设置输出像素格式
// 打开编码器
int ret = avcodec_open2(codecCtx, codec, NULL);
if (ret < 0)
{
cout << "avcodec_open2 failed!" << endl;
getchar();
return -1;
}
cout << "avcodec_open2 success!" << endl;
// 3 创建视频重采样上下文:指定源和目标图像分辨率、格式
SwsContext *swsCtx = NULL;
swsCtx = sws_getCachedContext(swsCtx,
width, height, AV_PIX_FMT_BGRA,
width, height, AV_PIX_FMT_YUV420P,
SWS_BICUBIC,
NULL, NULL, NULL
);
//4 创建YUV视频帧并配置
AVFrame *yuvFrame = av_frame_alloc();
yuvFrame->format = AV_PIX_FMT_YUV420P;
yuvFrame->width = width;
yuvFrame->height = height;
ret = av_frame_get_buffer(yuvFrame, 32);
if (ret < 0)
{
cout << "av_frame_get_buffer failed!" << endl;
getchar();
return -1;
}
// 循环写视频文件
int pts = 0;
int count = 0;
for (;;)
{
//5 每次读取一帧RGB数据到rgbBuf,读取完毕则退出
int len = fread(rgbBuf, 1, width*height * 4, fpin);
if (len <= 0)
{
break;
}
//5 创建RGB视频帧并绑定RGB缓冲区(avpicture_fill是给rgbFrame初始化一些字段,并且会自动填充data和linesize)
AVFrame *rgbFrame = av_frame_alloc();
avpicture_fill((AVPicture *)rgbFrame, rgbBuf, AV_PIX_FMT_BGRA, width, height);
//7 像素格式转换,转换后的YUV数据存放在yuvFrame
int outSliceH = sws_scale(swsCtx, rgbFrame->data, rgbFrame->linesize, 0, height,
yuvFrame->data, yuvFrame->linesize
);
if (outSliceH <= 0)
break;
/* 8 H264编码 */
// 将未压缩的AVFrame数据(yuv)给编码器
yuvFrame->pts = count++ * (codecCtx->time_base.num * 1000 / codecCtx->time_base.den);
ret = avcodec_send_frame(codecCtx, yuvFrame);
if (ret != 0)
{
continue;
}
// 将编码数据保存在AVPacket
AVPacket pkt;
av_init_packet(&pkt);
ret = avcodec_receive_packet(codecCtx, &pkt);
if (ret != 0)
continue;
//9 写入H264文件
fwrite(pkt.data, 1, pkt.size, fpout);
//av_packet_unref(&pkt);
cout << "<" << pkt.size << ">";
}
// 关闭RGB和YUV文件
fclose(fpin);
fclose(fpout);
// 释放RGB缓冲区
delete rgbBuf;
//关闭编码器
avcodec_close(codecCtx);
//清理编码器上下文
avcodec_free_context(&codecCtx);
//清理视频重采样上下文
sws_freeContext(swsCtx);
cout << "======================end=========================" << endl;
getchar();
return 0;
}
jpg格式的文件也有许多操作库,经常用的libjpeg库,不过网络上有另外一套针对文件操作的jpg png bmp的综合库,stb_image,这套库支持的格式比较多,也比较常用,但是对于裸流来说,还是不太方便做一些操作,比如rgb的裸流的缩放等等效果。一般和ffmpeg这类编解码库结合来用,比较常见。
jpg格式的图像,一般是不支持alpha通道的,常用的是针对于灰度以及rgb格式文件的压缩,当然通道数越少,压缩效果越好,但是stb_image将写入jpg文件头封装在库内,很不方便使用。对于文件操作来说,还可以,但是对于流的操作就比较繁复了