1.读取视频流并转为RGB24
void readVideo(char* videoPath)
{
AVFormatContext* fmt=NULL;
AVCodecContext* codecCtx;
AVCodec* codec;
AVFrame* inFrame;
AVFrame* rgbFrame;
AVStream* videoStream;
int rgbSize;
uint8_t * rgbBuffer;
int numVideoStream;
av_register_all();
int ref=avformat_open_input(&fmt, videoPath, NULL, NULL);
ref=avformat_find_stream_info(fmt, NULL);
for(int n=0;n<fmt->nb_streams;n++)
{
if(fmt->streams[n]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
numVideoStream = n;
break;
}
}
codecCtx = fmt->streams[numVideoStream]->codec;
codec = avcodec_find_decoder(codecCtx->codec_id);
avcodec_open2(codecCtx, codec, NULL);
rgbFrame = av_frame_alloc();
inFrame = av_frame_alloc();
rgbSize = avpicture_get_size(AV_PIX_FMT_RGB24, codecCtx->width, codecCtx->height);
rgbBuffer = (uint8_t*)av_malloc(rgbSize);
avpicture_fill((AVPicture*)rgbFrame, rgbBuffer, AV_PIX_FMT_RGB24, codecCtx->width, codecCtx->height);
AVPacket pkt;
av_new_packet(&pkt, codecCtx->width*codecCtx->height*3);
while(av_read_frame(fmt, &pkt) == 0)
{
if(pkt.stream_index == numVideoStream)
{
int isOK;
avcodec_decode_video2(codecCtx, inFrame, &isOK, &pkt);
//为了保存为BMP,提前转置
inFrame->data[0] += inFrame->linesize[0] * (codecCtx->height - 1);
inFrame->linesize[0] *= -1;
inFrame->data[1] += inFrame->linesize[1] * (codecCtx->height / 2 - 1);
inFrame->linesize[1] *= -1;
inFrame->data[2] += inFrame->linesize[2] * (codecCtx->height / 2 - 1);
inFrame->linesize[2] *= -1;
//
struct SwsContext *img_convert_ctx = NULL;
img_convert_ctx =
sws_getCachedContext(img_convert_ctx, codecCtx->width,
codecCtx->height, codecCtx->pix_fmt,
codecCtx->width, codecCtx->height,
AV_PIX_FMT_RGB24, SWS_BICUBIC,
NULL, NULL, NULL);
if( !img_convert_ctx ) {
fprintf(stderr, "Cannot initialize sws conversion context\n");
exit(1);
}
sws_scale(img_convert_ctx, (const uint8_t* const*)inFrame->data,
inFrame->linesize, 0, codecCtx->height, rgbFrame->data,
rgbFrame->linesize);
//保存为PPM
static int numF = 0;
//SaveFrame(rgbFrame, codecCtx->width, codecCtx->height, numF);
//MyWriteJPEG(rgbFrame, codecCtx->width, codecCtx->height, numF);
char name[64]={0};
char* numSize;
static QString numSIZE = "00001";
int aaaa=001;
sprintf(name, "E:\\NewStart\\GoodFFMpeg\\ReadVideoSaveImg\\bmp\\JPEG00%d.bmp", numF);
saveBmp(name, rgbFrame->data[0], codecCtx->width, codecCtx->height, 24);
numF++;
std::cout<<numF<<" ";
if(numF%10 == 0)
std::cout<<std::endl;
}//numVideoStream
}//while
avformat_free_context(fmt);
av_free(rgbFrame);
av_free(inFrame);
}
红色的代码是我保存图片的地址,记得修改
//应为我知道我使用的视频是YUV420的,所以直接用了
inFrame->data[0] += inFrame->linesize[0] * (codecCtx->height - 1);
inFrame->linesize[0] *= -1;
inFrame->data[1] += inFrame->linesize[1] * (codecCtx->height / 2 - 1);
inFrame->linesize[1] *= -1;
inFrame->data[2] += inFrame->linesize[2] * (codecCtx->height / 2 - 1);
inFrame->linesize[2] *= -1;
在YUV420的帧数据中来提前转置图片,也可以在RGB24的帧数据中用我之前的方法转正
2.保存rgb24
bool saveBmp(char *bmpName, uint8_t *imgBuf, int width, int height,
int biBitCount, RGBQUAD *pColorTable=NULL)
{
//如果位图数据指针为0,则没有数据传入,函数返回
if(!imgBuf)
return 0;
//颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
int colorTablesize=0;
if(biBitCount==8)
colorTablesize=1024;
//待存储图像数据每行字节数为4的倍数
int lineByte=(width * biBitCount/8+3)/4*4;
//以二进制写的方式打开文件
FILE *fp=fopen(bmpName,"wb");
if(fp==0) return 0;
//申请位图文件头结构变量,填写文件头信息
BITMAPFILEHEADER fileHead;
fileHead.bfType = 0x4D42;//bmp类型
//bfSize是图像文件4个组成部分之和
fileHead.bfSize= sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
+ colorTablesize + lineByte*height;
fileHead.bfReserved1 = 0;
fileHead.bfReserved2 = 0;
//bfOffBits是图像文件前3个部分所需空间之和
fileHead.bfOffBits=54+colorTablesize;
//写文件头进文件
fwrite(&fileHead, sizeof(BITMAPFILEHEADER),1, fp);
//申请位图信息头结构变量,填写信息头信息
BITMAPINFOHEADER head;
head.biBitCount=biBitCount;
head.biClrImportant=0;
head.biClrUsed=0;
head.biCompression=0;
head.biHeight=height;
head.biPlanes=1;
head.biSize=40;
head.biSizeImage=lineByte*height;
head.biWidth=width;
head.biXPelsPerMeter=0;
head.biYPelsPerMeter=0;
//写位图信息头进内存
fwrite(&head, sizeof(BITMAPINFOHEADER),1, fp);
//如果灰度图像,有颜色表,写入文件
if(biBitCount==8)
fwrite(pColorTable, sizeof(RGBQUAD),256, fp);
//写位图数据进文件
fwrite(imgBuf, height*lineByte, 1, fp);
//关闭文件
fclose(fp);
return 1;
}
保存的代码是我网上COPY的,RGB24和32可以直接用
3.使用示例
void main()
{
readVideo("E:\\NewStart\\GoodFFMpeg\\Bmp2H264_Thread\\22.avi");
}