FFmpeg 入門(2):輸出視頻到屏幕

本文轉(zhuǎn)自:FFmpeg 入門(2):輸出視頻到屏幕 | www.samirchen.com

SDL

我們這里使用 SDL 來渲染視頻到屏幕。SDL 是 Simple Direct Layer 的縮寫,是一個優(yōu)秀的跨平臺多媒體庫,你可以從 http://www.libsdl.org 下載 SDL 的庫。

SDL 有很多可以將圖像繪制都屏幕的方法匈辱,其中有一個專門用于將視頻渲染到屏幕進(jìn)行播放,即 YUV overlay。

YUV(其實(shí)這里叫 YCbCr 更準(zhǔn)確)是不同于 RGB 的另一種存儲原始圖像數(shù)據(jù)的方式断楷。簡單來講,Y 是表示「亮度」崭别,U 和 V 表示「色度」冬筒,YUV 的關(guān)鍵是在于它的亮度信號 Y 和色度信號 U、V 是分離的茅主。那就是說即使只有 Y 信號分量而沒有 U舞痰、V 分量,我們?nèi)匀豢梢员硎境鰣D像诀姚,只不過圖像是黑白灰度圖像响牛。在YCbCr 中 Y 是指亮度分量,Cb 指藍(lán)色色度分量,而 Cr 指紅色色度分量呀打。

SDL 的 YUV overlay 可以接收一組 YUV 數(shù)據(jù)然后顯示它论衍。它支持 4 種不同的 YUV 格式,其中 「YV12」 是最快的聚磺。另一種 YUV 格式是 「YUV420P」也叫 「I420」坯台,基本上和 「YV12」 是一樣的,就是把 U 和 V 數(shù)組換了一下位置瘫寝。

  • YV12:亮度(行×列) + U(行×列/4) + V(行×列/4)
  • I420:亮度(行×列) + V(行×列/4) + U(行×列/4)

更多 YUV420P 相關(guān)的信息蜒蕾,可以看這里:Chroma subsampling

這里我們要做的是將上一個教程中的 SaveFrame() 函數(shù)去掉焕阿,換成渲染視頻的邏輯咪啡。這里我們需要了解下給如何使用 SDL 庫,首先我們需要 include SDL 庫并初始化它:

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

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

SDL_Init() 函數(shù)用來配置我們要用的功能特性暮屡,SDL_GetError() 是一個調(diào)試用的函數(shù)撤摸,用于輸出錯誤信息。

創(chuàng)建顯示界面

現(xiàn)在我們需要屏幕上的一塊界面來渲染媒體視頻褒纲,在 SDL 里這個界面叫做 SDL_Surface

SDL_Surface *screen = NULL;

// Make a screen to put our video.
#ifndef __DARWIN__
screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 0, 0);
#else
screen = SDL_SetVideoMode(pCodecCtx->width, pCodecCtx->height, 24, 0);
#endif
if (!screen) {
    fprintf(stderr, "SDL: could not set video mode - exiting\n");
    exit(1);
}

上面的代碼中 SDL_SetVideoMode() 函數(shù)使用我們給定的寬高創(chuàng)建了一塊界面准夷,其中第三個參數(shù)是表示每個像素的比特?cái)?shù),如果為 0 則表示使用當(dāng)前的顯示一樣的設(shè)置莺掠。但是在 OS X 上 0 是無效的衫嵌,這里在 OS X 上設(shè)為 24。

接著彻秆,我們創(chuàng)建 YUV overlay 來輸出我們的視頻楔绞,同時這里我們使用 SwsContext 來將圖像轉(zhuǎn)換為 YUV420 格式:

struct SwsContext *sws_ctx = NULL;  
SDL_Overlay *bmp = NULL;

// Allocate a place to put our YUV image on that 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);

如我們前面所說,用 FFmpeg 從視頻中獲得 YUV420P 的數(shù)據(jù)唇兑,用 SDL 支持的 YV12 來進(jìn)行渲染酒朵。

顯示圖像

現(xiàn)在我們要開始顯示圖像了,直接來看當(dāng)我們獲得完整的一幀圖像時的代碼:這里我們要創(chuàng)建一個 AVFrame 并將它的 datalinesize 的一組指針指向 YUV overlay:

// Did we get a video frame?
if (frameFinished) {
    SDL_LockYUVOverlay(bmp);

    AVFrame 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];

    // Convert the image into YUV format that SDL uses.
    sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pict.data, pict.linesize);

    SDL_UnlockYUVOverlay(bmp);
}

由于我們需要向 YUV overlay 寫入數(shù)據(jù)扎附,這時候我們先給它加鎖蔫耽。AVFrame 這個結(jié)構(gòu)體的 data 數(shù)組中存儲的是 4 個通道指針,可以指向不同的通道帕棉,由于我們這里處理的 YUV420P 只有 Y针肥、Cb、Cr 這 3 個通道香伴,所以這里只有 3 組數(shù)據(jù)慰枕。處理其他格式時可能會遇到有 4 個通道數(shù)據(jù)的,比如 alpha 通道之類的即纲。linesize 是每個通道的數(shù)據(jù)尺寸具帮。在 YUV overlay 中與 data 和 linesize 對應(yīng)的是 pixelspitches(在 SDL 里 pitches 指的是一個 line 的數(shù)據(jù)的寬度)。我們這里做的便是將 pict.data 的 3 個數(shù)組指針指向 YUV overlay 對應(yīng)的數(shù)據(jù)。這樣一來蜂厅,當(dāng)我們向 pict 中寫入數(shù)據(jù)時匪凡,其實(shí)我們是寫到了 YUV overlay 中,overlay 中的內(nèi)存則已經(jīng)是分配好了的掘猿。我們在前面通過 sws_ctx 設(shè)置了目標(biāo)格式為 AV_PIX_FMT_YUV420P病游,接著我們用 sws_scale() 函數(shù)來完成轉(zhuǎn)換。

渲染圖像

接著我們要做的就是調(diào)用 SDL_DisplayYUVOverlay() 函數(shù)讓 SDL 來渲染我們給它的數(shù)據(jù)稠通,這時我們需要傳入一個矩形區(qū)域數(shù)據(jù)告訴它從哪個點(diǎn)開始渲染衬衬,以及對應(yīng)的寬度和高度。SDL 會幫我們做好縮放改橘,并能夠使用 GPU 來使圖像渲染更快滋尉。

SDL_Rect rect;
if (frameFinished) {
    /* ... code ... */

    // Convert the image into YUV format that SDL uses.
    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);
}

// Free the packet that was allocated by av_read_frame.
av_packet_unref(&packet);

到這里,我們的視頻就可以顯示了飞主。

接下來狮惜,我們來看看 SDL 的另一個功能:事件系統(tǒng)。SDL 的事件系統(tǒng)使得你可以接收用戶的輸入碌识,從而完成一些控制操作碾篡,在多線程編程時這個尤其有用。在這里丸冕,我們處理一個簡單的 SDL_QUIT 事件耽梅,讓我們可以退出程序。

SDL_Event event;

SDL_PollEvent(&event);
switch (event.type) {
    case SDL_QUIT:
        printf("SDL_QUIT\n");
        SDL_Quit();
        exit(0);
        break;
    default:
        break;
}

以上便是我們這節(jié)教程的全部內(nèi)容胖烛,其中的完整代碼你可以從這里獲得:https://github.com/samirchen/TestFFmpeg

編譯執(zhí)行

你可以使用下面的命令編譯它:

$ gcc -o tutorial02 tutorial02.c -lavutil -lavformat -lavcodec -lswscale -lz -lm `sdl-config --cflags --libs`

找一個視頻文件,你可以這樣執(zhí)行一下試試:

$ tutorial02 myvideofile.mp4
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末诅迷,一起剝皮案震驚了整個濱河市佩番,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌罢杉,老刑警劉巖趟畏,帶你破解...
    沈念sama閱讀 222,000評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異滩租,居然都是意外死亡赋秀,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評論 3 399
  • 文/潘曉璐 我一進(jìn)店門律想,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猎莲,“玉大人,你說我怎么就攤上這事技即≈荩” “怎么了?”我有些...
    開封第一講書人閱讀 168,561評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長身笤。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么骄恶? 我笑而不...
    開封第一講書人閱讀 59,782評論 1 298
  • 正文 為了忘掉前任琉苇,我火速辦了婚禮,結(jié)果婚禮上娇钱,老公的妹妹穿的比我還像新娘涤久。我一直安慰自己,他們只是感情好忍弛,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評論 6 397
  • 文/花漫 我一把揭開白布响迂。 她就那樣靜靜地躺著,像睡著了一般细疚。 火紅的嫁衣襯著肌膚如雪蔗彤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評論 1 310
  • 那天疯兼,我揣著相機(jī)與錄音然遏,去河邊找鬼。 笑死吧彪,一個胖子當(dāng)著我的面吹牛待侵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播姨裸,決...
    沈念sama閱讀 40,952評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼秧倾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了傀缩?” 一聲冷哼從身側(cè)響起那先,我...
    開封第一講書人閱讀 39,852評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎赡艰,沒想到半個月后售淡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡慷垮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評論 3 341
  • 正文 我和宋清朗相戀三年揖闸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片料身。...
    茶點(diǎn)故事閱讀 40,615評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡汤纸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出惯驼,到底是詐尸還是另有隱情蹲嚣,我是刑警寧澤递瑰,帶...
    沈念sama閱讀 36,303評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站隙畜,受9級特大地震影響抖部,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜议惰,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評論 3 334
  • 文/蒙蒙 一慎颗、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧言询,春花似錦俯萎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至辆憔,卻和暖如春撇眯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背虱咧。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評論 1 272
  • 我被黑心中介騙來泰國打工熊榛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人腕巡。 一個月前我還...
    沈念sama閱讀 49,041評論 3 377
  • 正文 我出身青樓玄坦,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绘沉。 傳聞我的和親對象是個殘疾皇子煎楣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評論 2 359

推薦閱讀更多精彩內(nèi)容