ffmpeg解碼視頻並輸出到屏幕

* 學習自雷神的博客和視頻,圖片來自他的課件,雷神博客:* bash

http://blog.csdn.NET/leixiaohua1020/數據結構

 

ffmpeg基礎知識

ffmpeg的庫ide

  • avdecoc: 編解碼
  • avformat: 封裝格式的處理(mkv,mp4,avi)
  • swscale: 視頻像素數據格式轉換(經常使用於解碼後視頻的裁剪)
  • avutil: 工具庫
  • avfilter: 濾鏡特效處理
  • avdevice: 各類設備的輸入輸出
  • postproc: 後加工
  • swresample: 音頻採樣數據格式轉換

ffmpeg的執行流程函數

        

  • avcodec_decode_open2()這個函數是解碼函數, 最主要的一個函數。
  •   圖中解碼流程是:獲取一個pakcet, 而後調用解碼函數,把AVPacket結構中的data轉換爲AVFrame結構的data。
  • AVPacket結構存儲一幀壓縮的編碼數據。
  • AVFrame結構存儲一幀解碼後的像素數據(對音頻則是採樣數據)。
  • AVFrame結構的元素data是雙重指針,YUV數據來講包含data[0],data[1],data[2]分別存Y、U、V數據,注意每幀中U、V數據是Y數據的四分之一大小(對420P來講)。『Y:亮度數據, U,V:色差數據,因爲人的眼睛對亮度更敏感,故而YUV數據中存更多的Y而減小UV的數據。當只有Y數據的時候,顯示爲黑白』 
  • 解碼出來的數據可能函數無效像素。須要用sws_scale()函數處理。
  • 如圖: 

         

 

ffmpeg 解碼相關結構體工具

        

  • AVFormatContext是一個統籌全局的結構, 包含一些視頻文件名,視頻時長,視頻碼率等封裝格式信息。 
  • AVInputFormat包含一些具體的視頻格式信息,每種視頻格式對應一個這個結構。
  •  通常來講視頻文件有兩個流:視頻流和音頻流。有幾個流就有幾個AVStream數據結構, 通常視頻流的index==0(也有其餘狀況), AVStream在AVFormatContext中是一個雙重指針。 
  • AVCodecContext 編解碼器上下文結構體,保存音視頻編解碼相關信息。
  • AVCodec 每種視/音頻編解碼器(例如h264)對應一個該結構體。

SDL顯示YUV圖像流程:post

        

  •  SDL_Surface就是使用SDL的時候彈出的那個窗口。
  • SDL_Overlay用於顯示YUV數據。一個SDL_Overlay對應一幀YUV數據。
  • SDL_Rect用於肯定SDL_Overlay顯示的位置。

代碼 

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>

#include <stdio.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>

int main(int argc, char *argv[]) {
    AVFormatContext *pFormatCtx = NULL;
    int             i, videoStream;
    AVCodecContext  *pCodecCtx = NULL;
    AVCodec         *pCodec = NULL;
    AVFrame         *pFrame = NULL;
    AVPacket        packet;
    int             frameFinished;

    AVDictionary    *optionDict = NULL;
    struct SwsContext *sws_ctx = NULL;

    SDL_Overlay     *bmp = NULL;
    SDL_Surface     *screen = NULL;
    SDL_Rect        rect;
    SDL_Event       event;

    if(argc < 2){
        fprintf(stderr, "Usage: test <file> \n");
        exit(1);
    }

    av_register_all();

    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){
        fprintf(stderr,"Could not initialize SDL - %s " + *SDL_GetError());
        exit(1);
    }

    /*
    *打開一個文件
    */
    if(avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
        return -1;

    /*
	 *爲pFormatCtx->streams填充上正確的信息
	 */
    if(avformat_find_stream_info(pFormatCtx, NULL) < 0)
        return -1;

    /*
	 *手工調試函數,將文件信息在終端輸出
	 */
    av_dump_format(pFormatCtx, 0, argv[1], 0);


    videoStream=-1;
	for ( i = 0; i < pFormatCtx->nb_streams; i++)
	  if(pFormatCtx -> streams[i] -> codec -> codec_type == AVMEDIA_TYPE_VIDEO) {
	    videoStream = i;
	    break;
	  }

	if(videoStream == -1)
	  return -1;

    /*
     *從 vedio stream 中獲取對應的解碼器上下文的指針
     */
    pCodecCtx = pFormatCtx -> streams[videoStream] -> codec;

    /*
     *根據 codec_id 找到對應的解碼器
     */
    pCodec = avcodec_find_decoder(pCodecCtx -> codec_id);

    if(pCodec == NULL){
        fprintf(stderr, "Unsupported codec ! \n");
        return -1;
    }

    /*
     * 打開解碼器
     */
    if(avcodec_open2(pCodecCtx, pCodec, &optionDict) <0 )
        return -1;

    /*
     * 爲frame 申請內存
     */
    pFrame = av_frame_alloc();

    #ifdef __DARWIN__
        screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);
    #else
        screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0);
    #endif // __DARWIN__

    if(!screen){
        fprintf(stderr, "SDL : could not set video mode - exiting \n");
        exit(1);
    }

    /*
     * 申請一個 overlay , 將 yuv數據給 screen
     */
    bmp = SDL_CreateYUVOverlay(pCodecCtx->width, pCodecCtx->height, SDL_YV12_OVERLAY, screen);

    sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
                             AV_PIX_FMT_YUV420P, SWS_BILINEAR, NULL, NULL, NULL);

    i = 0;
    while (av_read_frame(pFormatCtx, &packet) >= 0){

        if(packet.stream_index == videoStream){
            
            //爲視頻流解碼
            avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &packet);

            if(frameFinished){
                SDL_LockYUVOverlay(bmp);
             
                AVPicture pict;
                pict.data[0] = bmp->pixels[0];
                pict.data[1] = bmp->pixels[2];
                pict.data[2] = bmp->pixels[1];

                pict.linesize[0] = bmp->pitches[0];
                pict.linesize[1] = bmp->pitches[2];
                pict.linesize[2] = bmp->pitches[1];

                sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize,
                          0, pCodecCtx->height, pict.data, pict.linesize);

                SDL_UnlockYUVOverlay(bmp);

                rect.x = 0;
                rect.y = 0;
                rect.w = pCodecCtx->width;
                rect.h = pCodecCtx->height;

                SDL_DisplayYUVOverlay(bmp, &rect);
                SDL_Delay(10);

            }
        }

        av_free_packet(&packet);
        SDL_PollEvent(&event);

        switch (event.type) {

            case SDL_QUIT:
                SDL_Quit();
                exit(0);
                break;

            default:
                break;
        }

    }


    av_free(pFrame);

    avcodec_close(pCodecCtx);

    avformat_close_input(&pFormatCtx);

    return 0;
}

編譯:學習

gcc -o playvedio playvedio.c -lavutil -lavformat -lavcodec -lavutil -lswscale -lSDL

運行:ui

./playvedio 文件路徑+文件名
相關文章
相關標籤/搜索