libVLC 提取視頻每一幀

1
什么是幀
DVD 電影中的場景、從 YouTube 下載的剪輯、通過網(wǎng)絡(luò)攝像頭拍攝的內(nèi)容。。。無論是視頻還是動(dòng)畫,都是由一系列靜止的圖像組成。然后,這些圖像會(huì)一個(gè)接一個(gè)的播放,讓你的眼睛誤以為物體在移動(dòng)。圖像的播放速度越快,動(dòng)作看起來越流暢,畫面也越逼真。
一般來說,想要達(dá)到自然平滑的動(dòng)態(tài)效果,播放速率應(yīng)該在每秒 24-30 張圖像之間,每個(gè)圖像被稱為一幀。因此,我們通常會(huì)看到 FPS(每秒幀數(shù))這個(gè)詞,它突出顯示了移動(dòng)速度的細(xì)節(jié),因此而得名。
舉一個(gè)栗子 - 走路,我們看到的是這樣的:

其實(shí),真實(shí)情況是這樣的:

視頻文件也一樣,只不過它是將所有幀存儲(chǔ)在一起并按順序播放。對(duì)于一個(gè)典型的電影來說,存儲(chǔ)的總幀數(shù)甚至可以達(dá)到數(shù)十萬。如果要捕獲其中的某一幀圖像,則非常簡單,只需暫停視頻并按 Print Screen 鍵即可。
但倘若要從一個(gè)視頻剪輯中提取多個(gè)連續(xù)的幀,甚至是所有幀,那么一次捕捉一個(gè)圖像是非常低效和費(fèi)時(shí)的。出于這個(gè)原因,可以用 libVLC 實(shí)現(xiàn)一個(gè)程序,用于提取想要的視頻幀,并自動(dòng)保存到圖像文件(例如:jpg 或 png)中。
2
主要接口
要提取視頻中的每一幀,主要涉及以下核心 API。
先來看第一個(gè) -?libvlc_video_set_callbacks(),用于設(shè)置回調(diào)和私有數(shù)據(jù),將解碼后的視頻渲染到內(nèi)存中的自定義區(qū)域:
/**
?*? mp:媒體播放器
?*? lock:回調(diào)以鎖定視頻內(nèi)存(不能為 NULL)
?*? unlock:回調(diào)以解鎖視頻內(nèi)存(如果不需要,則為 NULL)
?*? display:回調(diào)以顯示視頻(如果不需要,則為 NULL)
?*? opaque:這三個(gè)回調(diào)的私有指針(作為第一個(gè)參數(shù))
?*/
void?libvlc_video_set_callbacks(?libvlc_media_player_t?*mp,
?????????????????????????????????libvlc_video_lock_cb?lock,
?????????????????????????????????libvlc_video_unlock_cb?unlock,
?????????????????????????????????libvlc_video_display_cb?display,
?????????????????????????????????void?*opaque?);
這個(gè)函數(shù)包含了五個(gè)參數(shù),其中有三個(gè)是函數(shù)指針:
//?當(dāng)需要解碼新的視頻幀時(shí),就會(huì)調(diào)用 lock 回調(diào)。
typedef?void?*(*libvlc_video_lock_cb)(void?*opaque,?void?**planes);
//?當(dāng)視頻幀解碼完成后,將調(diào)用 unlock 回調(diào)。
typedef?void?(*libvlc_video_unlock_cb)(void?*opaque,?void?*picture,
???????????????????????????????????????void?*const?*planes);
//?當(dāng)視頻幀需要顯示時(shí),將調(diào)用 display 回調(diào)。
typedef?void?(*libvlc_video_display_cb)(void?*opaque,?void?*picture);
此外,還可使用?libvlc_video_set_format()?或者?libvlc_video_set_format_callbacks()?配置解碼的格式。例如,設(shè)置解碼后的視頻色度和尺寸:
/**
?*? mp:媒體播放器
?*? chroma:標(biāo)識(shí)色度的四個(gè)字符的字符串(例如:"RV32"?或者?"YUYV")
?*? width:像素寬度
?*? height:像素高度
?*? pitch:線間距(以字節(jié)為單位)
?*/
void?libvlc_video_set_format(?libvlc_media_player_t?*mp,?const?char?*chroma,
??????????????????????????????unsigned?width,?unsigned?height,
??????????????????????????????unsigned?pitch?);
3
提取每一幀
現(xiàn)在,是時(shí)候一展身手了,來提取一個(gè)視頻中的連續(xù)幀:

視頻比較長,為了演示,只播放一段時(shí)間(這里是 10 秒),然后提取這段時(shí)間中的幀數(shù)據(jù):
#include?
#include?
#include?
#include?
#include?
//?定義輸出視頻的分辨率
#define?VIDEO_WIDTH???640
#define?VIDEO_HEIGHT??480
struct?Context?{
????QMutex?mutex;
????uchar?*pixels;
};
static?void?*lock(void?*opaque,?void?**planes)
{
????struct?Context?*ctx?=?static_cast(opaque);
????ctx->mutex.lock();
????//?告訴?VLC?將解碼的數(shù)據(jù)放到緩沖區(qū)中
????*planes?=?ctx->pixels;
????return?nullptr;
}
//?獲取?argb?圖片并保存到文件中
static?void?unlock(void?*opaque,?void?*picture,?void?*const?*planes)
{
????Q_UNUSED(picture);
????struct?Context?*ctx?=?static_cast(opaque);
????unsigned?char?*data?=?static_cast<unsigned?char?*>(*planes);
????static?int?frameCount?=?1;
????QImage?image(data,?VIDEO_WIDTH,?VIDEO_HEIGHT,?QImage::Format_ARGB32);
????image.save(QString("frame_%1.png").arg(frameCount++));
????ctx->mutex.unlock();
}
static?void?display(void?*opaque,?void?*picture)
{
????Q_UNUSED(picture);
????(void)opaque;
}
int?main()
{
????const?char?*localMrl?=?"Sample.mkv";
????struct?Context?ctx;
????ctx.pixels?=?new?uchar[VIDEO_WIDTH?*?VIDEO_HEIGHT?*?4];
????memset(ctx.pixels,?0,?VIDEO_WIDTH?*?VIDEO_HEIGHT?*?4);
????libvlc_instance_t?*instance;
????libvlc_media_player_t?*player;
????libvlc_media_t?*media;
????instance?=?libvlc_new(0,?nullptr);
????media?=?libvlc_media_new_path(instance,?localMrl);
????player?=?libvlc_media_player_new_from_media(media);
????//?設(shè)置回調(diào),用于提取幀或者在界面上顯示。
????libvlc_video_set_callbacks(player,?lock,?unlock,?display,?&ctx);
????libvlc_video_set_format(player,?"RGBA",?VIDEO_WIDTH,?VIDEO_HEIGHT,?VIDEO_WIDTH?*?4);
????libvlc_media_player_play(player);
????QThread::sleep(10);
????libvlc_media_release(media);
????libvlc_media_player_release(player);
????libvlc_release(instance);
????return?0;
}
這里為了保存圖片,我們用到了 Qt 中的一個(gè)類 - QImage。該類提供了與硬件無關(guān)的圖像表示,允許直接訪問像素?cái)?shù)據(jù),并可用作繪圖設(shè)備。
·END·
?
點(diǎn)個(gè)在看,么么噠!

