Как преобразовать изображение YUV420P в JPEG с помощью библиотек ffmpeg?

Я пытаюсь преобразовать изображение YUV420P (AV_PIX_FMT_YUV420P) в JPEG, используя libavformat и libavcodec ffmpeg. Это мой код до сих пор:

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 для командной строки?


person alasin    schedule 26.11.2015    source источник


Ответы (1)


Следующие 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 с цветовым диапазоном JPEG» (т. е. пиксели используют диапазон 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 для преобразования между AV_PIX_FMT_YUV420P с MPEG-диапазон и AV_PIX_FMT_YUVJ420P, см. например этот пример.

person Ronald S. Bultje    schedule 26.11.2015