1. 程式人生 > >1.基於FFMPEG將YUV420轉為jpg

1.基於FFMPEG將YUV420轉為jpg

最近學習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;
}