如何使用 ffmpeg 的库将 YUV420P 图像转换为 JPEG?
How to convert YUV420P image to JPEG using ffmpeg's libraries?
我正在尝试使用 ffmpeg 的 libavformat
和 libavcodec
将 YUV420P 图像 (AV_PIX_FMT_YUV420P
) 转换为 JPEG。到目前为止,这是我的代码:
AVFormatContext* pFormatCtx;
AVOutputFormat* fmt;
AVStream* video_st;
AVCodecContext* pCodecCtx;
AVCodec* pCodec;
uint8_t* picture_buf;
AVFrame* picture;
AVPacket pkt;
int y_size;
int got_picture=0;
int size;
int ret=0;
FILE *in_file = NULL; //YUV source
int in_w = 720, in_h = 576; //YUV's width and height
const char* out_file = "encoded_pic.jpg"; //Output file
in_file = fopen(argv[1], "rb");
av_register_all();
pFormatCtx = avformat_alloc_context();
fmt = NULL;
fmt = av_guess_format("mjpeg", NULL, NULL);
pFormatCtx->oformat = fmt;
//Output URL
if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0)
{
printf("Couldn't open output file.");
return -1;
}
video_st = avformat_new_stream(pFormatCtx, 0);
if (video_st==NULL)
return -1;
pCodecCtx = video_st->codec;
pCodecCtx->codec_id = fmt->video_codec;
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
pCodecCtx->width = in_w;
pCodecCtx->height = in_h;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;
//Output some information
av_dump_format(pFormatCtx, 0, out_file, 1);
pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
if (!pCodec)
{
printf("Codec not found.");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec,NULL) < 0){
printf("Could not open codec.\n");
return -1;
}
picture = avcodec_alloc_frame();
size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
picture_buf = (uint8_t *)av_malloc(size);
if (!picture_buf)
return -1;
avpicture_fill((AVPicture *)picture, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
//Write Header
avformat_write_header(pFormatCtx,NULL);
y_size = pCodecCtx->width * pCodecCtx->height;
av_new_packet(&pkt,y_size*3);
//Read YUV
if (fread(picture_buf, 1, y_size*3/2, in_file) <=0)
{
printf("Could not read input file.");
return -1;
}
picture->data[0] = picture_buf; // Y
picture->data[1] = picture_buf+ y_size; // U
picture->data[2] = picture_buf+ y_size*5/4; // V
//Encode
ret = avcodec_encode_video2(pCodecCtx, &pkt,picture, &got_picture);
if(ret < 0)
{
printf("Encode Error.\n");
return -1;
}
if (got_picture==1)
{
pkt.stream_index = video_st->index;
ret = av_write_frame(pFormatCtx, &pkt);
}
av_free_packet(&pkt);
//Write Trailer
av_write_trailer(pFormatCtx);
printf("Encode Successful.\n");
if (video_st)
{
avcodec_close(video_st->codec);
av_free(picture);
av_free(picture_buf);
}
avio_close(pFormatCtx->pb);
avformat_free_context(pFormatCtx);
fclose(in_file);
我收到这个错误:
Output #0, mjpeg, to 'encoded_pic.jpg':
Stream #0.0: Video: mjpeg, yuv420p, 720x576, q=2-31, 200 kb/s, 90k tbn, 25 tbc
[mjpeg @ 0x78c3a0] Specified pix_fmt is not supported
Could not open codec.
是否在 ffmpeg 的文档中提到不允许进行此转换?如果有,我们如何规避它?当我们使用命令行版本的ffmpeg时,ffmpeg内部是如何转换的?
以下code是您的问题:
.pix_fmts = (const enum AVPixelFormat[]){
AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_NONE
},
如你所见,它想要AV_PIX_FMT_YUVJ420P,而不是YUV420P。这是一个旧的遗留物,在很大程度上被认为已弃用,但它尚未被删除或标记为删除,并且仍在某些地方使用。 YUVJ420P的字面意思是"YUV420P with JPEG color-range"(即像素使用0-255范围而不是16-235范围,其中0而不是16是黑色,255而不是235是白色)。您也可以使用 AV_PIX_FMT_YUV420P 然后设置例如avctx->color_range 到 AVCOL_RANGE_JPEG.
你可能不关心这个,你想知道,如何将AV_PIX_FMT_YUV420P转换为AV_PIX_FMT_YUVJ420P?那么,在这种情况下,您的输入是 JPEG,因此它可能使用 JPEG 范围数据(通过检查 avctx->color_range 或 avframe->color_range 来检查)。然后你可以简单地将 avframe->pix_fmt 从 AV_PIX_FMT_YUV420P 更改为 AV_PIX_FMT_YUVJ420P。如果出于某种原因 color_range 是 AVCOL_RANGE_MPEG,您可以使用 libswscale to convert between the AV_PIX_FMT_YUV420P with MPEG-range and AV_PIX_FMT_YUVJ420P, see e.g. this example.
我正在尝试使用 ffmpeg 的 libavformat
和 libavcodec
将 YUV420P 图像 (AV_PIX_FMT_YUV420P
) 转换为 JPEG。到目前为止,这是我的代码:
AVFormatContext* pFormatCtx;
AVOutputFormat* fmt;
AVStream* video_st;
AVCodecContext* pCodecCtx;
AVCodec* pCodec;
uint8_t* picture_buf;
AVFrame* picture;
AVPacket pkt;
int y_size;
int got_picture=0;
int size;
int ret=0;
FILE *in_file = NULL; //YUV source
int in_w = 720, in_h = 576; //YUV's width and height
const char* out_file = "encoded_pic.jpg"; //Output file
in_file = fopen(argv[1], "rb");
av_register_all();
pFormatCtx = avformat_alloc_context();
fmt = NULL;
fmt = av_guess_format("mjpeg", NULL, NULL);
pFormatCtx->oformat = fmt;
//Output URL
if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0)
{
printf("Couldn't open output file.");
return -1;
}
video_st = avformat_new_stream(pFormatCtx, 0);
if (video_st==NULL)
return -1;
pCodecCtx = video_st->codec;
pCodecCtx->codec_id = fmt->video_codec;
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
pCodecCtx->width = in_w;
pCodecCtx->height = in_h;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;
//Output some information
av_dump_format(pFormatCtx, 0, out_file, 1);
pCodec = avcodec_find_encoder(pCodecCtx->codec_id);
if (!pCodec)
{
printf("Codec not found.");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec,NULL) < 0){
printf("Could not open codec.\n");
return -1;
}
picture = avcodec_alloc_frame();
size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
picture_buf = (uint8_t *)av_malloc(size);
if (!picture_buf)
return -1;
avpicture_fill((AVPicture *)picture, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
//Write Header
avformat_write_header(pFormatCtx,NULL);
y_size = pCodecCtx->width * pCodecCtx->height;
av_new_packet(&pkt,y_size*3);
//Read YUV
if (fread(picture_buf, 1, y_size*3/2, in_file) <=0)
{
printf("Could not read input file.");
return -1;
}
picture->data[0] = picture_buf; // Y
picture->data[1] = picture_buf+ y_size; // U
picture->data[2] = picture_buf+ y_size*5/4; // V
//Encode
ret = avcodec_encode_video2(pCodecCtx, &pkt,picture, &got_picture);
if(ret < 0)
{
printf("Encode Error.\n");
return -1;
}
if (got_picture==1)
{
pkt.stream_index = video_st->index;
ret = av_write_frame(pFormatCtx, &pkt);
}
av_free_packet(&pkt);
//Write Trailer
av_write_trailer(pFormatCtx);
printf("Encode Successful.\n");
if (video_st)
{
avcodec_close(video_st->codec);
av_free(picture);
av_free(picture_buf);
}
avio_close(pFormatCtx->pb);
avformat_free_context(pFormatCtx);
fclose(in_file);
我收到这个错误:
Output #0, mjpeg, to 'encoded_pic.jpg':
Stream #0.0: Video: mjpeg, yuv420p, 720x576, q=2-31, 200 kb/s, 90k tbn, 25 tbc
[mjpeg @ 0x78c3a0] Specified pix_fmt is not supported
Could not open codec.
是否在 ffmpeg 的文档中提到不允许进行此转换?如果有,我们如何规避它?当我们使用命令行版本的ffmpeg时,ffmpeg内部是如何转换的?
以下code是您的问题:
.pix_fmts = (const enum AVPixelFormat[]){
AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_NONE
},
如你所见,它想要AV_PIX_FMT_YUVJ420P,而不是YUV420P。这是一个旧的遗留物,在很大程度上被认为已弃用,但它尚未被删除或标记为删除,并且仍在某些地方使用。 YUVJ420P的字面意思是"YUV420P with JPEG color-range"(即像素使用0-255范围而不是16-235范围,其中0而不是16是黑色,255而不是235是白色)。您也可以使用 AV_PIX_FMT_YUV420P 然后设置例如avctx->color_range 到 AVCOL_RANGE_JPEG.
你可能不关心这个,你想知道,如何将AV_PIX_FMT_YUV420P转换为AV_PIX_FMT_YUVJ420P?那么,在这种情况下,您的输入是 JPEG,因此它可能使用 JPEG 范围数据(通过检查 avctx->color_range 或 avframe->color_range 来检查)。然后你可以简单地将 avframe->pix_fmt 从 AV_PIX_FMT_YUV420P 更改为 AV_PIX_FMT_YUVJ420P。如果出于某种原因 color_range 是 AVCOL_RANGE_MPEG,您可以使用 libswscale to convert between the AV_PIX_FMT_YUV420P with MPEG-range and AV_PIX_FMT_YUVJ420P, see e.g. this example.