1. 程式人生 > >Windows FFMPEG開發環境配置

Windows FFMPEG開發環境配置

sha packet har 視頻流 flow 信息 lines extern href

1.去FFMPEG網站上下載Dev版本的庫,裏面有我們需要的頭文件和lib文件,然後下載Shared版本的庫,裏面有我們需要的dll文件 http://ffmpeg.zeranoe.com/builds/ 技術分享圖片 記得區分32位和64位的庫,這裏碰到一個大坑,就是我下載的是64位的庫,但是創建工程的時候選的是32位的工程,導致鏈接的時候一直報 無法解析的外部符號 _av_register_all。。。(這個因為以前在Linux上使用的都是自己編譯出來的庫,所以沒註意這個坑)
最後通過這個鏈接解決的 https://stackoverflow.com/questions/20672777/linker-error-using-ffmpeg-with-visual-studio-2013-express

2.把Dev庫裏解壓出來的東西拷貝到工程中,Shared庫中解壓出來的東西拷貝到生成的bin文件目錄(如release)

技術分享圖片
G:\source\FFmpegDemo\FFmpegDemo\ffmpeg>
├─inc
│  ├─libavcodec
│  ├─libavdevice
│  ├─libavfilter
│  ├─libavformat
│  ├─libavutil
│  ├─libpostproc
│  ├─libswresample
│  └─libswscale
└─libs
        avcodec.lib
        avdevice.lib
        avfilter.lib
        avformat.lib
        avutil.lib
        postproc.lib
        swresample.lib
        swscale.lib
技術分享圖片

3.右擊工程“屬性”,“C/C++”——>“附加包含目錄”——>加入我們添加進來的頭文件的路徑

技術分享圖片

4.在源碼中鏈接lib文件

#pragma comment(lib,"ffmpeg\\libs\\avutil.lib")
#pragma comment(lib,"ffmpeg\\libs\\avformat.lib")
#pragma comment(lib,"ffmpeg\\libs\\avcodec.lib")
#pragma comment(lib,"ffmpeg\\libs\\swscale.lib")

源碼如下:

技術分享圖片
//main.cpp
#include <stdio.h>
#include <stdlib.h>

#pragma comment(lib,"ffmpeg\\libs\\avutil.lib")
#pragma comment(lib,"ffmpeg\\libs\\avformat.lib")
#pragma comment(lib,"ffmpeg\\libs\\avcodec.lib")
#pragma comment(lib,"ffmpeg\\libs\\swscale.lib")

extern "C" 
{

//編碼
#include "libavcodec/avcodec.h"
//封裝格式處理
#include "libavformat/avformat.h"
//像素處理
#include "libswscale/swscale.h"
};


int main(int argc, char* argv[])
{
    //獲取輸入輸出文件名
    const char *input = "test.mp4";
    const char *output = "test.yuv";

    //1.註冊所有組件
    av_register_all();

    //封裝格式上下文,統領全局的結構體,保存了視頻文件封裝格式的相關信息
    AVFormatContext *pFormatCtx = avformat_alloc_context();

    //2.打開輸入視頻文件
    if (avformat_open_input(&pFormatCtx, input, NULL, NULL) != 0)
    {
        printf("%s", "無法打開輸入視頻文件");
        return -1;
    }

    //3.獲取視頻文件信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        printf("%s", "無法獲取視頻文件信息");
        return -1;
    }

    //獲取視頻流的索引位置
    //遍歷所有類型的流(音頻流、視頻流、字幕流),找到視頻流
    int v_stream_idx = -1;
    int i = 0;
    //number of streams
    for (; i < pFormatCtx->nb_streams; i++)
    {
        //流的類型
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            v_stream_idx = i;
            break;
        }
    }

    if (v_stream_idx == -1)
    {
        printf("%s", "找不到視頻流\n");
        return -1;
    }

    //只有知道視頻的編碼方式,才能夠根據編碼方式去找到解碼器
    //獲取視頻流中的編解碼上下文
    AVCodecContext *pCodecCtx = pFormatCtx->streams[v_stream_idx]->codec;
    //4.根據編解碼上下文中的編碼id查找對應的解碼
    AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL)
    {
        printf("%s", "找不到解碼器\n");
        return -1;
    }

    //5.打開解碼器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0)
    {
        printf("%s", "解碼器無法打開\n");
        return -1;
    }

    //輸出視頻信息
    printf("視頻的文件格式:%s", pFormatCtx->iformat->name);
    printf("視頻時長:%d", (pFormatCtx->duration) / 1000000);
    printf("視頻的寬高:%d,%d", pCodecCtx->width, pCodecCtx->height);
    printf("解碼器的名稱:%s", pCodec->name);

    //準備讀取
    //AVPacket用於存儲一幀一幀的壓縮數據(H264)
    //緩沖區,開辟空間
    AVPacket *packet = (AVPacket*)av_malloc(sizeof(AVPacket));

    //AVFrame用於存儲解碼後的像素數據(YUV)
    //內存分配
    AVFrame *pFrame = av_frame_alloc();
    //YUV420
    AVFrame *pFrameYUV = av_frame_alloc();
    //只有指定了AVFrame的像素格式、畫面大小才能真正分配內存
    //緩沖區分配內存
    uint8_t *out_buffer = (uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
    //初始化緩沖區
    avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);

    //用於轉碼(縮放)的參數,轉之前的寬高,轉之後的寬高,格式等
    struct SwsContext *sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
        pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P,
        SWS_BICUBIC, NULL, NULL, NULL);
    int got_picture, ret;

    FILE *fp_yuv = fopen(output, "wb+");

    int frame_count = 0;

    //6.一幀一幀的讀取壓縮數據
    while (av_read_frame(pFormatCtx, packet) >= 0)
    {
        //只要視頻壓縮數據(根據流的索引位置判斷)
        if (packet->stream_index == v_stream_idx)
        {
            //7.解碼一幀視頻壓縮數據,得到視頻像素數據
            ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
            if (ret < 0)
            {
                printf("%s", "解碼錯誤");
                return -1;
            }

            //為0說明解碼完成,非0正在解碼
            if (got_picture)
            {
                //AVFrame轉為像素格式YUV420,寬高
                //2 6輸入、輸出數據
                //3 7輸入、輸出畫面一行的數據的大小 AVFrame 轉換是一行一行轉換的
                //4 輸入數據第一列要轉碼的位置 從0開始
                //5 輸入畫面的高度
                sws_scale(sws_ctx, pFrame->data, pFrame->linesize, 0, pCodecCtx->height,
                    pFrameYUV->data, pFrameYUV->linesize);

                //輸出到YUV文件
                //AVFrame像素幀寫入文件
                //data解碼後的圖像像素數據(音頻采樣數據)
                //Y 亮度 UV 色度(壓縮了) 人對亮度更加敏感
                //U V 個數是Y的1/4
                int y_size = pCodecCtx->width * pCodecCtx->height;
                fwrite(pFrameYUV->data[0], 1, y_size, fp_yuv);
                fwrite(pFrameYUV->data[1], 1, y_size / 4, fp_yuv);
                fwrite(pFrameYUV->data[2], 1, y_size / 4, fp_yuv);

                frame_count++;
                printf("解碼第%d幀\n", frame_count);
            }
        }

        //釋放資源
        av_free_packet(packet);
    }

    fclose(fp_yuv);

    av_frame_free(&pFrame);

    avcodec_close(pCodecCtx);

    avformat_free_context(pFormatCtx);


    return 0;
}

Windows FFMPEG開發環境配置