FFMPEG 4.4如何在Windows上将NV12格式转换为YUV420?
How does FFMPEG 4.4 convert NV12 format to YUV420 on Windows?
我想用FFmpeg将输入的NV12格式转换成YUV420P
我尝试使用sws_scale转换,但是颜色都是绿色。
我已经使用sws成功地将YUYV422转换为420P,但是无法将NV12转换为YUV420。
我应该改变什么?
这是我的代码:
QStringList inList = int_put_res.split("x");
m_in_width = inList[0].toInt();
m_in_hieght = inList[1].toInt();
QStringList outList = out_put_res.split("x");
m_out_width = outList[0].toInt();
m_out_hieght = outList[1].toInt();
int VSize = m_in_width * m_in_hieght;
AVPacket *packet;
AVCodecContext *enc_ctx = NULL;
AVFrame *image_inFrame= nullptr;
packet = av_packet_alloc();
int ret = 0;
int base = 0;
image_inFrame = av_frame_alloc();
open_encoder(m_out_width,m_out_hieght, &enc_ctx);
switch (AVPixelFormat(m_fmt_ctx->streams[m_videoindex]->codecpar->format))
{
case AV_PIX_FMT_YUV420P:
m_yuv420p_convert_ctx = initSWS(image_inFrame,NULL,m_in_width,m_in_hieght,AV_PIX_FMT_YUV420P,AV_PIX_FMT_YUV420P);
break;
case AV_PIX_FMT_YUYV422:
m_yuv420p_convert_ctx = initSWS(image_inFrame,NULL,m_in_width,m_in_hieght,AV_PIX_FMT_YUYV422,AV_PIX_FMT_YUV420P);
break;
case AV_PIX_FMT_NV12:
m_yuv420p_convert_ctx = initSWS(image_inFrame,NULL,m_in_width,m_in_hieght,AV_PIX_FMT_NV12,AV_PIX_FMT_YUV420P);
break;
}
m_frame = create_frame(m_out_width, m_out_hieght);
AVPacket *newpkt = av_packet_alloc();
if (!newpkt)
{
spdlog::error("failed to alloc avpacket!");
goto __ERROR;
}
if (!m_fmt_ctx)
{
goto __ERROR;
}
while (m_isStop != true)
{
if(av_read_frame(m_fmt_ctx, packet) != 0) {
goto __ERROR;
}
getSWS(m_yuv420p_convert_ctx,packet,image_inFrame,m_frame);
av_packet_unref(packet);
m_frame->pts = base++;
}
SwsContext *FFSDK::initSWS(AVFrame *scrFrame, AVFrame *dstFrame, int
image_width, int image_height,enum AVPixelFormat scrFmt, enum
AVPixelFormat dstFmt)
if(scrFrame != NULL){
av_image_alloc(scrFrame->data, scrFrame->linesize,image_width, image_height, scrFmt, 1);
}
if(dstFrame != NULL){
av_image_alloc(dstFrame->data, dstFrame->linesize, m_out_width, m_out_hieght, dstFmt, 1);
}
return sws_getContext(image_width,image_height,
scrFmt,
m_out_width, m_out_hieght, dstFmt,
SWS_BICUBIC,
nullptr,
nullptr,
nullptr);
}
void FFSDK::getSWS(SwsContext *context,AVPacket *packet,AVFrame *scrFrame, AVFrame *dstFrame)
{
scrFrame->data[0] = packet->data;
sws_scale(context,
scrFrame->data,
scrFrame->linesize, 0, m_in_hieght, dstFrame->data,
dstFrame->linesize);
}
从您发布的代码片段中我看不出问题所在。
绿色通常是零数据的结果(Y、U 和 V 的所有元素均为零)。
内容为零的原因可能是因为没有任何内容写入视频帧的目标(或源)缓冲区。
我创建了一个“自包含”代码示例,演示了使用 sws_scale
.
从 NV12 到 YUV420 的转换
首先使用 FFmpeg(命令行工具)构建合成输入框架。
该命令以原始 NV12 格式创建 320x240 视频帧:
ffmpeg -y -f lavfi -i testsrc=size=320x240:rate=1 -vcodec rawvideo -pix_fmt nv12 -frames 1 -f rawvideo nv12_image.bin
下一个代码示例应用以下阶段:
- 为源帧(NV12 格式)分配内存。
- 从二进制文件中读取 NV12 数据(用于测试)。
- 为目标帧分配内存(YUV420 格式)。
- 应用颜色 space 转换(使用
sws_scale
)。
- 将转换后的YUV420数据写入二进制文件(测试用)。
完整代码如下:
//Use extern "C", because the code is built as C++ (cpp file) and not C.
extern "C"
{
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libavutil/pixdesc.h>
#include <libavutil/imgutils.h>
}
int main()
{
int width = 320;
int height = 240; //The code sample assumes height is even.
int align = 0;
AVPixelFormat srcPxlFormat = AV_PIX_FMT_NV12;
AVPixelFormat dstPxlFormat = AV_PIX_FMT_YUV420P;
int sts;
//Source frame allocation
////////////////////////////////////////////////////////////////////////////
AVFrame* pNV12Frame = av_frame_alloc();
pNV12Frame->format = srcPxlFormat;
pNV12Frame->width = width;
pNV12Frame->height = height;
sts = av_frame_get_buffer(pNV12Frame, align);
if (sts < 0)
{
return -1; //Error!
}
////////////////////////////////////////////////////////////////////////////
//Read NV12 data from binary file (for testing)
////////////////////////////////////////////////////////////////////////////
//Use FFmpeg for building raw NV12 image (used as input).
//ffmpeg -y -f lavfi -i testsrc=size=320x240:rate=1 -vcodec rawvideo -pix_fmt nv12 -frames 1 -f rawvideo nv12_image.bin
FILE *f = fopen("nv12_image.bin", "rb");
if (f == NULL)
{
return -1; //Error!
}
//Read Y channel from nv12_image.bin (Y channel size is width x height).
//Reading row by row is required in rare cases when pNV12Frame->linesize[0] != width
uint8_t *Y = pNV12Frame->data[0]; //Pointer to Y color channel of the NV12 frame.
for (int row = 0; row < height; row++)
{
fread(Y + (uintptr_t)row * pNV12Frame->linesize[0], 1, width, f); //Read row (width pixels) to Y0.
}
//Read UV channel from nv12_image.bin (UV channel size is width x height/2).
uint8_t* UV = pNV12Frame->data[1]; //Pointer to UV color channels of the NV12 frame (ordered as UVUVUVUV...).
for (int row = 0; row < height/2; row++)
{
fread(UV + (uintptr_t)row * pNV12Frame->linesize[1], 1, width, f); //Read row (width pixels) to UV0.
}
fclose(f);
////////////////////////////////////////////////////////////////////////////
//Destination frame allocation
////////////////////////////////////////////////////////////////////////////
AVFrame *pYUV420Frame = av_frame_alloc();
pYUV420Frame->format = dstPxlFormat;
pYUV420Frame->width = width;
pYUV420Frame->height = height;
sts = av_frame_get_buffer(pYUV420Frame, align);
if (sts < 0)
{
return -1; //Error!
}
////////////////////////////////////////////////////////////////////////////
//Color space conversion
////////////////////////////////////////////////////////////////////////////
SwsContext *sws_context = sws_getContext(width,
height,
srcPxlFormat,
width,
height,
dstPxlFormat,
SWS_FAST_BILINEAR,
NULL,
NULL,
NULL);
if (sws_context == NULL)
{
return -1; //Error!
}
sts = sws_scale(sws_context, //struct SwsContext* c,
pNV12Frame->data, //const uint8_t* const srcSlice[],
pNV12Frame->linesize, //const int srcStride[],
0, //int srcSliceY,
pNV12Frame->height, //int srcSliceH,
pYUV420Frame->data, //uint8_t* const dst[],
pYUV420Frame->linesize); //const int dstStride[]);
if (sts != pYUV420Frame->height)
{
return -1; //Error!
}
////////////////////////////////////////////////////////////////////////////
//Write YUV420 data to binary file (for testing)
////////////////////////////////////////////////////////////////////////////
//Use FFmpeg for converting the binary image to PNG after saving the data.
//ffmpeg -y -f rawvideo -video_size 320x240 -pixel_format yuv420p -i yuv420_image.bin -pix_fmt rgb24 rgb_image.png
f = fopen("yuv420_image.bin", "wb");
if (f == NULL)
{
return -1; //Error!
}
//Write Y channel to yuv420_image.bin (Y channel size is width x height).
//Writing row by row is required in rare cases when pYUV420Frame->linesize[0] != width
Y = pYUV420Frame->data[0]; //Pointer to Y color channel of the YUV420 frame.
for (int row = 0; row < height; row++)
{
fwrite(Y + (uintptr_t)row * pYUV420Frame->linesize[0], 1, width, f); //Write row (width pixels) to file.
}
//Write U channel to yuv420_image.bin (U channel size is width/2 x height/2).
uint8_t* U = pYUV420Frame->data[1]; //Pointer to U color channels of the YUV420 frame.
for (int row = 0; row < height/2; row++)
{
fwrite(U + (uintptr_t)row * pYUV420Frame->linesize[1], 1, width/2, f); //Write row (width/2 pixels) to file.
}
//Write V channel to yuv420_image.bin (V channel size is width/2 x height/2).
uint8_t* V = pYUV420Frame->data[2]; //Pointer to V color channels of the YUV420 frame.
for (int row = 0; row < height/2; row++)
{
fwrite(V + (uintptr_t)row * pYUV420Frame->linesize[2], 1, width/2, f); //Write row (width/2 pixels) to file.
}
fclose(f);
////////////////////////////////////////////////////////////////////////////
//Cleanup
////////////////////////////////////////////////////////////////////////////
sws_freeContext(sws_context);
av_frame_free(&pYUV420Frame);
av_frame_free(&pNV12Frame);
////////////////////////////////////////////////////////////////////////////
return 0;
}
用于验证输出:
执行代码后,执行FFmpeg(命令行工具)
以下命令将原始二进制帧(YUV420 格式)转换为 PNG(RGB 格式)。
ffmpeg -y -f rawvideo -video_size 320x240 -pixel_format yuv420p -i yuv420_image.bin -pix_fmt rgb24 rgb_image.png
示例输出(从 YUV420 转换为 PNG 图像文件格式后):
希望能帮到您解决问题...
我想用FFmpeg将输入的NV12格式转换成YUV420P
我尝试使用sws_scale转换,但是颜色都是绿色。
我已经使用sws成功地将YUYV422转换为420P,但是无法将NV12转换为YUV420。
我应该改变什么?
这是我的代码:
QStringList inList = int_put_res.split("x");
m_in_width = inList[0].toInt();
m_in_hieght = inList[1].toInt();
QStringList outList = out_put_res.split("x");
m_out_width = outList[0].toInt();
m_out_hieght = outList[1].toInt();
int VSize = m_in_width * m_in_hieght;
AVPacket *packet;
AVCodecContext *enc_ctx = NULL;
AVFrame *image_inFrame= nullptr;
packet = av_packet_alloc();
int ret = 0;
int base = 0;
image_inFrame = av_frame_alloc();
open_encoder(m_out_width,m_out_hieght, &enc_ctx);
switch (AVPixelFormat(m_fmt_ctx->streams[m_videoindex]->codecpar->format))
{
case AV_PIX_FMT_YUV420P:
m_yuv420p_convert_ctx = initSWS(image_inFrame,NULL,m_in_width,m_in_hieght,AV_PIX_FMT_YUV420P,AV_PIX_FMT_YUV420P);
break;
case AV_PIX_FMT_YUYV422:
m_yuv420p_convert_ctx = initSWS(image_inFrame,NULL,m_in_width,m_in_hieght,AV_PIX_FMT_YUYV422,AV_PIX_FMT_YUV420P);
break;
case AV_PIX_FMT_NV12:
m_yuv420p_convert_ctx = initSWS(image_inFrame,NULL,m_in_width,m_in_hieght,AV_PIX_FMT_NV12,AV_PIX_FMT_YUV420P);
break;
}
m_frame = create_frame(m_out_width, m_out_hieght);
AVPacket *newpkt = av_packet_alloc();
if (!newpkt)
{
spdlog::error("failed to alloc avpacket!");
goto __ERROR;
}
if (!m_fmt_ctx)
{
goto __ERROR;
}
while (m_isStop != true)
{
if(av_read_frame(m_fmt_ctx, packet) != 0) {
goto __ERROR;
}
getSWS(m_yuv420p_convert_ctx,packet,image_inFrame,m_frame);
av_packet_unref(packet);
m_frame->pts = base++;
}
SwsContext *FFSDK::initSWS(AVFrame *scrFrame, AVFrame *dstFrame, int
image_width, int image_height,enum AVPixelFormat scrFmt, enum
AVPixelFormat dstFmt)
if(scrFrame != NULL){
av_image_alloc(scrFrame->data, scrFrame->linesize,image_width, image_height, scrFmt, 1);
}
if(dstFrame != NULL){
av_image_alloc(dstFrame->data, dstFrame->linesize, m_out_width, m_out_hieght, dstFmt, 1);
}
return sws_getContext(image_width,image_height,
scrFmt,
m_out_width, m_out_hieght, dstFmt,
SWS_BICUBIC,
nullptr,
nullptr,
nullptr);
}
void FFSDK::getSWS(SwsContext *context,AVPacket *packet,AVFrame *scrFrame, AVFrame *dstFrame)
{
scrFrame->data[0] = packet->data;
sws_scale(context,
scrFrame->data,
scrFrame->linesize, 0, m_in_hieght, dstFrame->data,
dstFrame->linesize);
}
从您发布的代码片段中我看不出问题所在。
绿色通常是零数据的结果(Y、U 和 V 的所有元素均为零)。
内容为零的原因可能是因为没有任何内容写入视频帧的目标(或源)缓冲区。
我创建了一个“自包含”代码示例,演示了使用 sws_scale
.
首先使用 FFmpeg(命令行工具)构建合成输入框架。
该命令以原始 NV12 格式创建 320x240 视频帧:ffmpeg -y -f lavfi -i testsrc=size=320x240:rate=1 -vcodec rawvideo -pix_fmt nv12 -frames 1 -f rawvideo nv12_image.bin
下一个代码示例应用以下阶段:
- 为源帧(NV12 格式)分配内存。
- 从二进制文件中读取 NV12 数据(用于测试)。
- 为目标帧分配内存(YUV420 格式)。
- 应用颜色 space 转换(使用
sws_scale
)。 - 将转换后的YUV420数据写入二进制文件(测试用)。
完整代码如下:
//Use extern "C", because the code is built as C++ (cpp file) and not C.
extern "C"
{
#include <libswscale/swscale.h>
#include <libavformat/avformat.h>
#include <libswresample/swresample.h>
#include <libavutil/pixdesc.h>
#include <libavutil/imgutils.h>
}
int main()
{
int width = 320;
int height = 240; //The code sample assumes height is even.
int align = 0;
AVPixelFormat srcPxlFormat = AV_PIX_FMT_NV12;
AVPixelFormat dstPxlFormat = AV_PIX_FMT_YUV420P;
int sts;
//Source frame allocation
////////////////////////////////////////////////////////////////////////////
AVFrame* pNV12Frame = av_frame_alloc();
pNV12Frame->format = srcPxlFormat;
pNV12Frame->width = width;
pNV12Frame->height = height;
sts = av_frame_get_buffer(pNV12Frame, align);
if (sts < 0)
{
return -1; //Error!
}
////////////////////////////////////////////////////////////////////////////
//Read NV12 data from binary file (for testing)
////////////////////////////////////////////////////////////////////////////
//Use FFmpeg for building raw NV12 image (used as input).
//ffmpeg -y -f lavfi -i testsrc=size=320x240:rate=1 -vcodec rawvideo -pix_fmt nv12 -frames 1 -f rawvideo nv12_image.bin
FILE *f = fopen("nv12_image.bin", "rb");
if (f == NULL)
{
return -1; //Error!
}
//Read Y channel from nv12_image.bin (Y channel size is width x height).
//Reading row by row is required in rare cases when pNV12Frame->linesize[0] != width
uint8_t *Y = pNV12Frame->data[0]; //Pointer to Y color channel of the NV12 frame.
for (int row = 0; row < height; row++)
{
fread(Y + (uintptr_t)row * pNV12Frame->linesize[0], 1, width, f); //Read row (width pixels) to Y0.
}
//Read UV channel from nv12_image.bin (UV channel size is width x height/2).
uint8_t* UV = pNV12Frame->data[1]; //Pointer to UV color channels of the NV12 frame (ordered as UVUVUVUV...).
for (int row = 0; row < height/2; row++)
{
fread(UV + (uintptr_t)row * pNV12Frame->linesize[1], 1, width, f); //Read row (width pixels) to UV0.
}
fclose(f);
////////////////////////////////////////////////////////////////////////////
//Destination frame allocation
////////////////////////////////////////////////////////////////////////////
AVFrame *pYUV420Frame = av_frame_alloc();
pYUV420Frame->format = dstPxlFormat;
pYUV420Frame->width = width;
pYUV420Frame->height = height;
sts = av_frame_get_buffer(pYUV420Frame, align);
if (sts < 0)
{
return -1; //Error!
}
////////////////////////////////////////////////////////////////////////////
//Color space conversion
////////////////////////////////////////////////////////////////////////////
SwsContext *sws_context = sws_getContext(width,
height,
srcPxlFormat,
width,
height,
dstPxlFormat,
SWS_FAST_BILINEAR,
NULL,
NULL,
NULL);
if (sws_context == NULL)
{
return -1; //Error!
}
sts = sws_scale(sws_context, //struct SwsContext* c,
pNV12Frame->data, //const uint8_t* const srcSlice[],
pNV12Frame->linesize, //const int srcStride[],
0, //int srcSliceY,
pNV12Frame->height, //int srcSliceH,
pYUV420Frame->data, //uint8_t* const dst[],
pYUV420Frame->linesize); //const int dstStride[]);
if (sts != pYUV420Frame->height)
{
return -1; //Error!
}
////////////////////////////////////////////////////////////////////////////
//Write YUV420 data to binary file (for testing)
////////////////////////////////////////////////////////////////////////////
//Use FFmpeg for converting the binary image to PNG after saving the data.
//ffmpeg -y -f rawvideo -video_size 320x240 -pixel_format yuv420p -i yuv420_image.bin -pix_fmt rgb24 rgb_image.png
f = fopen("yuv420_image.bin", "wb");
if (f == NULL)
{
return -1; //Error!
}
//Write Y channel to yuv420_image.bin (Y channel size is width x height).
//Writing row by row is required in rare cases when pYUV420Frame->linesize[0] != width
Y = pYUV420Frame->data[0]; //Pointer to Y color channel of the YUV420 frame.
for (int row = 0; row < height; row++)
{
fwrite(Y + (uintptr_t)row * pYUV420Frame->linesize[0], 1, width, f); //Write row (width pixels) to file.
}
//Write U channel to yuv420_image.bin (U channel size is width/2 x height/2).
uint8_t* U = pYUV420Frame->data[1]; //Pointer to U color channels of the YUV420 frame.
for (int row = 0; row < height/2; row++)
{
fwrite(U + (uintptr_t)row * pYUV420Frame->linesize[1], 1, width/2, f); //Write row (width/2 pixels) to file.
}
//Write V channel to yuv420_image.bin (V channel size is width/2 x height/2).
uint8_t* V = pYUV420Frame->data[2]; //Pointer to V color channels of the YUV420 frame.
for (int row = 0; row < height/2; row++)
{
fwrite(V + (uintptr_t)row * pYUV420Frame->linesize[2], 1, width/2, f); //Write row (width/2 pixels) to file.
}
fclose(f);
////////////////////////////////////////////////////////////////////////////
//Cleanup
////////////////////////////////////////////////////////////////////////////
sws_freeContext(sws_context);
av_frame_free(&pYUV420Frame);
av_frame_free(&pNV12Frame);
////////////////////////////////////////////////////////////////////////////
return 0;
}
用于验证输出:
执行代码后,执行FFmpeg(命令行工具)
以下命令将原始二进制帧(YUV420 格式)转换为 PNG(RGB 格式)。ffmpeg -y -f rawvideo -video_size 320x240 -pixel_format yuv420p -i yuv420_image.bin -pix_fmt rgb24 rgb_image.png
示例输出(从 YUV420 转换为 PNG 图像文件格式后):
希望能帮到您解决问题...