在ffmpeg中,网络视频流h264为什么默认的转为YUV而不是其他格式 文章中介绍了,h264解码的时候是直接解码为yuv的,如果在使用的过程中 需要用到rgb的格式,我们该如何来转换这种格式呢?
在上面的文章中,我们已经知道了ffmpeg
中,使用avcodec_send_packet
和avcodec_receive_frame
对h264进行了解码,这时候编码已经 变为yuv了。
那问题就变为了,如何把yuv格式转变为rgb。对于yuv和rgb来说,这两种只是格式的不同而已,映射空间的不同,也就是说,通过映射,我们可以把yuv转换为rgb。
在ffmpeg
中,通过空间的转换,使用到的函数是:sws_getContext
和sws_scale
。
代码如下:
AVFrame* decode_to_rgb(AVFrame* frame) {
// 创建一个swsContext,用于YUV到RGB的转换
SwsContext* swsContext = sws_getContext(frame->width, frame->height, (AVPixelFormat)frame->format,
frame->width, frame->height, AV_PIX_FMT_RGB24,
SWS_BILINEAR, NULL, NULL, NULL);
if (!swsContext) {
// 错误处理...
}
// 创建一个新的AVFrame,用于存储RGB数据
AVFrame* rgbFrame = av_frame_alloc();
rgbFrame->format = AV_PIX_FMT_RGB24;
rgbFrame->width = frame->width;
rgbFrame->height = frame->height;
av_frame_get_buffer(rgbFrame, 0);
// 将YUV数据转换为RGB
sws_scale(swsContext, frame->data, frame->linesize, 0, frame->height,
rgbFrame->data, rgbFrame->linesize);
// 释放swsContext
sws_freeContext(swsContext);
return rgbFrame;
}
通过上面的程序,我们可以知道,yuv和rgb的数据,是存在frame->data中的,每个frame代表了一帧,也就是代表了一张图片,在上一篇文章中,如果你还记得的话,那么h264的数据是放在AVPacket
中的。
既然每一帧是一张图片,我们能不能也把AVFrame
编码为jpg
的图片,这是可以的。
bool yuv_to_jpeg(void* framev) {
AVFrame* frame = (AVFrame*)framev;
const AVCodec* jpegCodec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
if (!jpegCodec) {
return false;
}
AVCodecContext* jpegContext = avcodec_alloc_context3(jpegCodec);
if (!jpegContext) {
return false;
}
jpegContext->pix_fmt = AV_PIX_FMT_YUVJ420P;
jpegContext->height = frame->height;
jpegContext->width = frame->width;
jpegContext->time_base.den = 20;
jpegContext->time_base.num = 1;
if (frame->height <= 0)return false;
int ret = avcodec_open2(jpegContext, jpegCodec, NULL);
if (ret < 0) {
//char* ret =(char*) av_err2str(ret);
return false;
}
AVPacket* packet;
packet = av_packet_alloc();
// 发送帧到编码器
if (avcodec_send_frame(jpegContext, frame) < 0) {
// 错误处理...
}
if (avcodec_receive_packet(jpegContext, packet) == 0) {
// 如果编码器输出了JPEG数据,将其保存到文件
FILE* JPEGFile;
char JPEGFName[256];
static int i = 0;
sprintf(JPEGFName, "jpg//dvr-%06d.jpg", ++i);
JPEGFile = fopen(JPEGFName, "wb");
fwrite(packet->data, 1, packet->size, JPEGFile);
fclose(JPEGFile);
}
av_packet_unref(packet);
avcodec_close(jpegContext);
return true;
}
因为jpg是一种编码格式,所有会用到avcodec_send_packet
和avcodec_receive_frame
,编码的内容存在packet
中,ffmpeg
都帮我们把jpg的格式填充在packet
中了,我们只需要把数据直接保存在文件就可以得到图片了。
所有的代码都已在git上。