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

时间:2015-11-26 07:14:11

标签: jpeg yuv libavcodec libavformat

我尝试使用ffmpeg' s AV_PIX_FMT_YUV420Plibavformat将YUV420P图像(libavcodec)转换为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如何在内部转换它?

1 个答案:

答案 0 :(得分:4)

以下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的字面含义是“具有JPEG颜色范围的YUV420P”(即像素使用0-255范围而不是16-235范围,其中0而不是16是黑色,而255而不是235是白色)。您也可以使用AV_PIX_FMT_YUV420P然后设置例如avctx-&gt; color_range到AVCOL_RANGE_JPEG

您可能不关心,您想知道,如何将AV_PIX_FMT_YUV420P转换为AV_PIX_FMT_YUVJ420P ?那么,在这种情况下,您的输入是JPEG,因此它可能使用JPEG范围数据(通过检查avctx-&gt; color_range或avframe-&gt; color_range来检查)。然后,您只需将avframe-&gt; pix_fmt从AV_PIX_FMT_YUV420P更改为AV_PIX_FMT_YUVJ420P即可。如果无论出于何种原因,color_range是AVCOL_RANGE_MPEG,您可以使用libswscale在具有MPEG范围的AV_PIX_FMT_YUV420P和AV_PIX_FMT_YUVJ420P之间进行转换,例如, this example