1.基於FFMPEG將YUV420轉為jpg
阿新 • • 發佈:2018-11-20
最近學習FFMPEG,主要參考雷博的文章,瞭解了一些基礎的概念,看了一些程式碼,但是別人的始終是別人的╮(╯﹏╰)╭,看程式碼瞭解大概,但是始終有些概念不清晰,便動手敲一遍,加深印象。
將YUV420轉換為jpg格式,好多是用libjpeg開源庫去轉的,不過主要是學習ffmpeg的,所以不涉及這個開源庫的使用,有興趣的的童鞋可以百度一下這個庫的使用方法。廢話少說,言歸正傳。
剛開始是參考了雷博的文章,如程式碼1,從檔案中讀取一幀的YUV資料,然後將其進行編碼,然後呼叫av_write_frame函式將AVPacket裡面的資料寫到輸出檔案中去,在手敲程式碼的過程中,對解碼後的AVPakcet裡面的資料進行測試,發現AVPacket裡面的data其實就是一副picture 完整的資料,於是便直接將其寫入檔案中去,不再採用av_write_frame的方式,如程式碼2,大體上採用雷博的程式碼,在一些細節地方做了修改。
程式碼1:
int YUV_2_JPG1(char* pFile)
{
if (NULL == pFile) return -1;
AVFormatContext* pFormatCtx;
AVOutputFormat* pOutFmt;
AVStream* pStream;
AVCodecContext* pCodecCtx;
AVCodec* pCodec;
AVFrame* pFrame;
AVPacket pkt;
uint8_t* picture_buf;
int y_size = 0;
int size = 0;
int width = 640;
int height = 480;
const char* out_file = "test1.jpg";
int got_picture = 0;
int ret = 0;
FILE* in_file = fopen(pFile, "rb");
if (NULL == in_file)
{
printf("open input file error!\n");
return -1;
}
av_register_all();
pFormatCtx = avformat_alloc_context(); //分配AVFormatCtx
pOutFmt = av_guess_format("mjpeg", NULL, NULL); //設定輸出檔案格式
pFormatCtx->oformat = pOutFmt;
if (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0) //建立並初始化一個AVIOContext
{
printf("Could not open output file!\n");
return -1;
}
pStream = avformat_new_stream(pFormatCtx, 0);
if (NULL == pStream)
return -1;
/*設定相關資訊*/
pCodecCtx = pStream->codec;
pCodecCtx->codec_id = pOutFmt->video_codec;
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P;
pCodecCtx->width = width;
pCodecCtx->height = height;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;
//av_dump_format(pFormatCtx, 0, out_file, 1);
pCodec = avcodec_find_encoder(pCodecCtx->codec_id); //查詢編碼器
if (NULL == pCodec)
{
printf("can not find codec!\n");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
{
printf("con not open codec!\n");
return -1;
}
pFrame = av_frame_alloc();
size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);//獲取圖片的大小
picture_buf = (uint8_t*)av_malloc(size);
if (NULL == picture_buf)
{
printf("malloc picture buf error!\n");
return -1;
}
avpicture_fill((AVPicture*)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
avformat_write_header(pFormatCtx, NULL);
y_size = pCodecCtx->width * pCodecCtx->height;
av_new_packet(&pkt, y_size*2); //給AVPacket申請空間
if (fread(picture_buf, 1, y_size*3/2,in_file) != (y_size*3/2))
{
printf("read input file error!\n");
return -1;
}
pFrame->width = pCodecCtx->width;
pFrame->height = pCodecCtx->height;
pFrame->format = pCodecCtx->pix_fmt;
pFrame->data[0] = picture_buf;
pFrame->data[1] = picture_buf + y_size;
pFrame->data[2] = picture_buf + y_size*5/4;
ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);//編碼
if (ret < 0)
{
printf("encodec yuv data error!\n");
return -1;
}
if (1 == got_picture)
{
pkt.stream_index = pStream->index;
av_write_frame(pFormatCtx, &pkt); //寫資料
}
av_write_trailer(pFormatCtx);
av_free_packet(&pkt);
av_free(picture_buf);
av_free(pFrame);
avcodec_close(pStream->codec);
avio_close(pFormatCtx->pb);
avformat_free_context(pFormatCtx);
fclose(in_file);
return 0;
}
程式碼2:
int YUV_2_JPG2(char* pFile)
{
if (NULL == pFile) return -1;
AVFormatContext* pFormatCtx;
AVStream* pStream;
AVCodecContext* pCodecCtx;
AVCodec* pCodec;
AVFrame* pFrame;
AVPacket pkt;
uint8_t* picture_buf;
int y_size = 0;
int size = 0;
int width = 640;
int height = 480;
int got_picture = 0;
int ret = 0;
FILE* in_file = fopen(pFile, "rb");
if (NULL == in_file)
{
printf("open input file error!\n");
return -1;
}
FILE* out_file = fopen("test2.jpg", "wb");
if (NULL == out_file)
{
printf("open output file error!\n");
return -1;
}
av_register_all();
pFormatCtx = avformat_alloc_context(); //分配AVFormatCtx
pStream = avformat_new_stream(pFormatCtx, 0);
if (NULL == pStream)
return -1;
/*設定相關資訊*/
pCodecCtx = pStream->codec;
pCodecCtx->codec_id = AV_CODEC_ID_MJPEG;
pCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
pCodecCtx->pix_fmt = AV_PIX_FMT_YUVJ420P;
pCodecCtx->width = width;
pCodecCtx->height = height;
pCodecCtx->time_base.num = 1;
pCodecCtx->time_base.den = 25;
//av_dump_format(pFormatCtx, 0, out_file, 1);
pCodec = avcodec_find_encoder(pCodecCtx->codec_id); //查詢編碼器
if (NULL == pCodec)
{
printf("can not find codec!\n");
return -1;
}
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
{
printf("con not open codec!\n");
return -1;
}
pFrame = av_frame_alloc();
size = avpicture_get_size(pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);//獲取圖片的大小
picture_buf = (uint8_t*)av_malloc(size);
if (NULL == picture_buf)
{
printf("malloc picture buf error!\n");
return -1;
}
avpicture_fill((AVPicture*)pFrame, picture_buf, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height);
y_size = pCodecCtx->width * pCodecCtx->height;
av_new_packet(&pkt, y_size*2); //給AVPacket申請空間
if (fread(picture_buf, 1, y_size*3/2,in_file) != (y_size*3/2))
{
printf("read input file error!\n");
return -1;
}
pFrame->width = pCodecCtx->width;
pFrame->height = pCodecCtx->height;
pFrame->format = pCodecCtx->pix_fmt;
pFrame->data[0] = picture_buf;
pFrame->data[1] = picture_buf + y_size;
pFrame->data[2] = picture_buf + y_size*5/4;
ret = avcodec_encode_video2(pCodecCtx, &pkt, pFrame, &got_picture);//編碼
if (ret < 0)
{
printf("encodec yuv data error!\n");
return -1;
}
if (1 == got_picture)
{
fwrite(pkt.buf->data, 1, pkt.size, out_file);
}
av_free_packet(&pkt);
av_free(picture_buf);
av_free(pFrame);
avcodec_close(pStream->codec);
avformat_free_context(pFormatCtx);
fclose(in_file);
fclose(out_file);
return 0;
}