2015-11-26 6 views
1

J'essaie de convertir une image YUV420P (AV_PIX_FMT_YUV420P) en JPEG en utilisant libavformat de ffmpeg et libavcodec. Ceci est mon code à ce jour:Comment convertir une image YUV420P en JPEG en utilisant les bibliothèques de 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); 

Je reçois cette erreur:

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. 

Si toute mention de cette conversion ne sont pas autorisés dans la documentation de ffmpeg? S'il y a, comment pouvons-nous le contourner? Comment ffmpeg le convertit-il en interne lorsque nous utilisons la version en ligne de commande de ffmpeg?

Répondre

3

Le code suivant est votre problème:

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

Comme vous pouvez le voir, il veut AV_PIX_FMT_YUVJ420P, pas YUV420P. Ceci est un vieux reliquat et est en grande partie considéré comme obsolète, mais il n'a pas encore été retiré ou marqué pour suppression, et est toujours utilisé dans certains endroits. La signification littérale de YUVJ420P est "YUV420P avec une gamme de couleurs JPEG" (c'est-à-dire que les pixels utilisent la plage 0-255 au lieu de 16-235, où 0 au lieu de 16 est noir et 255 au lieu de 235 est blanc). Vous pouvez également le faire en utilisant AV_PIX_FMT_YUV420P, puis en définissant par ex. avctx-> color_range à AVCOL_RANGE_JPEG.

Cela ne vous intéresse pas, vous voulez savoir, comment puis-je convertir AV_PIX_FMT_YUV420P à AV_PIX_FMT_YUVJ420P? Eh bien, dans ce cas, votre entrée est JPEG donc il utilise probablement des données de plage JPEG (vérifiez cela en vérifiant avctx-> color_range ou avframe-> color_range). Ensuite, vous pouvez simplement changer avframe-> pix_fmt de AV_PIX_FMT_YUV420P à AV_PIX_FMT_YUVJ420P. Si, pour une raison quelconque, color_range est AVCOL_RANGE_MPEG, vous pouvez probablement utiliser libswscale pour convertir l'AV_PIX_FMT_YUV420P avec la plage MPEG et AV_PIX_FMT_YUVJ420P, voir par exemple. this example.