如何使用ffmpeg的库将YUV420P图像转换为JPEG ?

时间:2021-12-05 20:01:15

I'm trying to convert a YUV420P image (AV_PIX_FMT_YUV420P) to a JPEG using ffmpeg's libavformat and libavcodec. This is my code so far:

我正在尝试使用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);

I'm getting this error:

我得到这个错误:

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.

If there any mention of this conversion not being allowed in ffmpeg's documentation? If there is, how can we circumvent it? How does ffmpeg internally convert it when we use the command-line version of ffmpeg?

如果在ffmpeg文档中没有提到这种转换,那该怎么办?如果有,我们怎么能绕过它?当我们使用命令行版本的ffmpeg时,ffmpeg如何在内部转换它?

1 个解决方案

#1


3  

The following code is your issue:

以下代码是您的问题:

.pix_fmts       = (const enum AVPixelFormat[]){
    AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_NONE
},

As you can see, it wants AV_PIX_FMT_YUVJ420P, not YUV420P. This is an old leftover and is largely considered deprecated, but it has not yet been removed or marked for removal, and is still used in some places. The literal meaning of YUVJ420P is "YUV420P with JPEG color-range" (i.e. pixels use 0-255 range instead of 16-235 range, where 0 instead of 16 is black, and 255 instead of 235 is white). You can alternatively also do that using AV_PIX_FMT_YUV420P and then setting e.g. avctx->color_range to AVCOL_RANGE_JPEG.

如您所见,它需要AV_PIX_FMT_YUVJ420P,而不是YUV420P。这是一种旧的剩余物,在很大程度上被认为是弃用的,但它还没有被移除或标记为去除,并且在一些地方仍被使用。YUVJ420P的字面意思是“JPEG颜色范围的YUV420P”(即像素的使用范围是0-255,而不是16-235的范围,0代替16是黑色,255代替235是白色)。您还可以使用AV_PIX_FMT_YUV420P,然后设置例如avctx->color_range到AVCOL_RANGE_JPEG。

You probably don't care about that, you want to know, how can I convert AV_PIX_FMT_YUV420P to AV_PIX_FMT_YUVJ420P? Well, in this case, your input is JPEG so it probably uses JPEG range data (check that by checking avctx->color_range or avframe->color_range). Then you can simply change avframe->pix_fmt from AV_PIX_FMT_YUV420P to AV_PIX_FMT_YUVJ420P. If for whatever reason color_range is AVCOL_RANGE_MPEG, you can probably use libswscale to convert between the AV_PIX_FMT_YUV420P with MPEG-range and AV_PIX_FMT_YUVJ420P, see e.g. this example.

您可能不关心这个,您想知道,如何将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将AV_PIX_FMT_YUV420P与MPEG-range和AV_PIX_FMT_YUVJ420P进行转换,参见本例。

#1


3  

The following code is your issue:

以下代码是您的问题:

.pix_fmts       = (const enum AVPixelFormat[]){
    AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_NONE
},

As you can see, it wants AV_PIX_FMT_YUVJ420P, not YUV420P. This is an old leftover and is largely considered deprecated, but it has not yet been removed or marked for removal, and is still used in some places. The literal meaning of YUVJ420P is "YUV420P with JPEG color-range" (i.e. pixels use 0-255 range instead of 16-235 range, where 0 instead of 16 is black, and 255 instead of 235 is white). You can alternatively also do that using AV_PIX_FMT_YUV420P and then setting e.g. avctx->color_range to AVCOL_RANGE_JPEG.

如您所见,它需要AV_PIX_FMT_YUVJ420P,而不是YUV420P。这是一种旧的剩余物,在很大程度上被认为是弃用的,但它还没有被移除或标记为去除,并且在一些地方仍被使用。YUVJ420P的字面意思是“JPEG颜色范围的YUV420P”(即像素的使用范围是0-255,而不是16-235的范围,0代替16是黑色,255代替235是白色)。您还可以使用AV_PIX_FMT_YUV420P,然后设置例如avctx->color_range到AVCOL_RANGE_JPEG。

You probably don't care about that, you want to know, how can I convert AV_PIX_FMT_YUV420P to AV_PIX_FMT_YUVJ420P? Well, in this case, your input is JPEG so it probably uses JPEG range data (check that by checking avctx->color_range or avframe->color_range). Then you can simply change avframe->pix_fmt from AV_PIX_FMT_YUV420P to AV_PIX_FMT_YUVJ420P. If for whatever reason color_range is AVCOL_RANGE_MPEG, you can probably use libswscale to convert between the AV_PIX_FMT_YUV420P with MPEG-range and AV_PIX_FMT_YUVJ420P, see e.g. this example.

您可能不关心这个,您想知道,如何将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将AV_PIX_FMT_YUV420P与MPEG-range和AV_PIX_FMT_YUVJ420P进行转换,参见本例。