一、模塊劃分
i) (主體模塊)視頻采集播放
ii) 顯示模式切換
iii) 拍照
iv) 錄像
v) fps顯示
vi) 錄像的瀏覽和刪除
回到頂部(go to top)
二、各模塊的實(shí)現(xiàn)
2.1(主體模塊)視頻采集播放
2.1.1 參考資料:
1)主體框架(解碼、讀幀)參考雷霄驊的:100行代碼實(shí)現(xiàn)最簡(jiǎn)單的基于FFMPEG+SDL的視頻播放器(SDL1.x)
主體框架的流程,可參考雷霄驊的上述博文,這里不再贅述
2)幀的顯示,參考:Android 使用 FFmpeg (二)——視屏流播放簡(jiǎn)單實(shí)現(xiàn)
幀顯示的流程,大致如下:
2.2 顯示模式切換
實(shí)現(xiàn)思路:利用av_filter的scale和pad功能,對(duì)獲取到的每一個(gè)原始幀進(jìn)行縮放和必要的四邊填充。
主框架體代碼參考FFMPEG filter使用實(shí)例(實(shí)現(xiàn)視頻縮放,裁剪,水印等),這里不再贅述。
至于怎樣實(shí)現(xiàn)兩種顯示模式下的scale參數(shù)(以及pad參數(shù))的切換, 尚未找到最優(yōu)方法(經(jīng)測(cè),av_opt_set()只對(duì)draw_text有效(詳見(jiàn)下文:2.5 fps顯示),而對(duì)scale和pad無(wú)效),
目前暫時(shí)采用較笨的辦法:
1)定義兩個(gè)filter_descr模板,以及對(duì)應(yīng)的AVFilterGraph、AVFilterContext
/* 用于保持長(zhǎng)寬比顯示模式 */
const char *m_filter_descr_template = 'scale=%d:%d,pad=%d:%d:%d:%d:blue,drawtext=fontfile=/sdcard/data/FreeSerif.ttf:fontsize=20:text=fps:x=(w-tw-%d):y=%d';
char m_filter_descr[200];
/* 用于全屏顯示模式 */
const char *m_filter_descr2_template = 'scale=%d:%d,pad=%d:%d:%d:%d:blue,drawtext=fontfile=/sdcard/data/FreeSerif.ttf:fontsize=20:text=fps:x=(w-tw-5):y=5';
char m_filter_descr2[200];
/* 用于保持長(zhǎng)寬比顯示模式 */
AVFilterContext *m_buffersink_ctx1;
AVFilterContext *m_buffersrc_ctx1;
AVFilterGraph *m_filter_graph1;
/* 用于全屏顯示模式 */
AVFilterContext *m_buffersink_ctx2;
AVFilterContext *m_buffersrc_ctx2;
AVFilterGraph *m_filter_graph2;
2)初始化時(shí),先調(diào)用keep_img_AR()來(lái)預(yù)先計(jì)算好兩種顯示模式對(duì)應(yīng)的filter_descr的值
int keep_img_AR(int nSrcW ,int nSrcH,int nDstW, int nDstH )
{
/* 計(jì)算保持寬高比例后上下有黑邊,還是左右有黑邊,黑邊多少 */
int imgW = 0,imgH = 0;
int padW = 0,padH = 0;
//必須規(guī)整為2的倍數(shù),否則ffmpeg計(jì)算pad時(shí)會(huì)報(bào)錯(cuò):Input area not within the padded area or zero-sized
nDstW = nDstW/2*2;
nDstH = nDstH/2*2;
imgW = nSrcW*nDstH/nSrcH/2*2;
imgH = nSrcH*nDstW/nSrcW/2*2;
if(imgW imgH = nDstH/2*2; //imgW = -1; } else if(imgH imgW = nDstW/2*2; //imgH = -1; } sprintf(m_filter_descr, m_filter_descr_template, imgW, imgH, nDstW, nDstH, padW, padH, padW+5, padH+5); sprintf(m_filter_descr2, m_filter_descr2_template, nDstW, nDstH, nDstW, nDstH, 0, 0); return 1 ; } 3)然后調(diào)用init_filters()來(lái)初始化m_filter_graph1、m_buffersink_ctx1、m_buffersrc_ctx1和m_filter_graph2、m_buffersink_ctx2、m_buffersrc_ctx2 init_filters()的代碼參考FFMPEG filter使用實(shí)例(實(shí)現(xiàn)視頻縮放,裁剪,水印等),這里不再贅述 4)而切換播放模式,其實(shí)就是切換(m_filter_graph1、m_buffersink_ctx1、m_buffersrc_ctx1)和(m_filter_graph2、m_buffersink_ctx2、m_buffersrc_ctx2)三元組 /** * 播放視頻時(shí)保持長(zhǎng)寬比 */ void playVideoKeepAspectRatio() { m_play_video_mode = PLAY_VIDEO_KEEP_ASPECT_RATIO; m_filter_graph = m_filter_graph1; m_buffersrc_ctx = m_buffersrc_ctx1; m_buffersink_ctx = m_buffersink_ctx1; } /** * 播放視頻時(shí)填滿顯示區(qū)域 */ void playVideoFullScreen( ) { m_play_video_mode = PLAY_VIDEO_FULL_SCREEN; m_filter_graph = m_filter_graph2; m_buffersrc_ctx = m_buffersrc_ctx2; m_buffersink_ctx = m_buffersink_ctx2; } 注:關(guān)于顯示模式切換,另一種實(shí)現(xiàn)的辦法是利用sws_scale()和av_picture_pad(),參考:使用ffmpeg的lib庫(kù)實(shí)現(xiàn)視頻窗口 原始寬高比例/拉伸鋪滿 但代碼量較大,而且經(jīng)測(cè)試,發(fā)現(xiàn)有些問(wèn)題,比如: - 加上av_filter的draw_text后,fps的顯示會(huì)出現(xiàn)小幅度的上下跳動(dòng),原因待查 - fps的定位較難實(shí)現(xiàn)(因?yàn)橐紤]到pad的寬度) 所以最終沒(méi)有采用這個(gè)辦法(但keep_img_AR()里計(jì)算scale和pad的算法參考了這篇文章)。 2.3 拍照 實(shí)現(xiàn)思路: 1)定義m_pFrameCur代表當(dāng)前獲取到的幀 2)在視頻播放函數(shù)videoStreamStartPlay()的while循環(huán)里,用av_frame_ref(m_pFrameCur, pFrame)使m_pFrameCur指向當(dāng)前獲取到的幀 3)__save_frame_2_jpeg(file_path, m_pFrameCur, m_input_codec_ctx->pix_fmt)實(shí)現(xiàn)把當(dāng)前幀保存到指定的文件中 代碼參考:ffmpeg實(shí)現(xiàn)mjpeg攝像頭的采集-預(yù)覽-拍照,這里不再贅述 2.4 錄像 參考:如何用FFmpeg API采集攝像頭視頻和麥克風(fēng)音頻,并實(shí)現(xiàn)錄制文件的功能 該文章的demo里,把錄像功能很好的封裝在了一個(gè)類CAVOutputStream里,我基本上原封不動(dòng)的拿來(lái)用于錄像功能的底層實(shí)現(xiàn)。 我所添加的工作,是在視頻播放函數(shù)videoStreamStartPlay()的while循環(huán)里,調(diào)用狀態(tài)機(jī)video_capture_state_machine(),代碼大致如下: void video_capture_state_machine(AVFrame *pFrame) { switch(m_video_capture_state) { case VIDEO_CAPTURE_START: LOGD('VIDEO_CAPTURE_START'); m_start_time = av_gettime(); m_OutputStream.SetVideoCodec(AV_CODEC_ID_H264); //設(shè)置視頻編碼器屬性 if(true == m_OutputStream.OpenOutputStream(m_save_video_path.c_str())) m_video_capture_state = VIDEO_CAPTURE_IN_PROGRESS; else m_video_capture_state = VIDEO_CAPTURE_IDLE; break; case VIDEO_CAPTURE_IN_PROGRESS: LOGD('VIDEO_CAPTURE_IN_PROGRESS'); m_OutputStream.write_video_frame(m_input_format_ctx->streams[m_video_stream_index], m_input_format_ctx->streams[m_video_stream_index]->codec->pix_fmt, pFrame, av_gettime() - m_start_time); break; case VIDEO_CAPTURE_STOP: LOGD('VIDEO_CAPTURE_STOP'); m_OutputStream.CloseOutput(); m_video_capture_state = VIDEO_CAPTURE_IDLE; break; default: if(m_video_capture_state == VIDEO_CAPTURE_IDLE){ LOGD('VIDEO_CAPTURE_IDLE'); } else{ LOGD('m_video_capture_state: %d', m_video_capture_state); } break; }//eo switch(m_video_capture_state) } 而native層和JAVA層的接口如下: /* 開(kāi)始錄像 */ void videoStreamStartCapture(const char* file_path) { m_save_video_path = file_path; m_video_capture_state = VIDEO_CAPTURE_START; } /* 停止錄像 */ void videoStreamStopCapture( ) { m_video_capture_state = VIDEO_CAPTURE_STOP; } 2.5 fps顯示 實(shí)現(xiàn)思路同:2.2 顯示模式切換。 而fps值的動(dòng)態(tài)顯示,是利用av_opt_set(filter_ctx_draw_text->priv, 'text', str_fps, 0 )來(lái)實(shí)現(xiàn)的。 2.6 錄像的瀏覽和刪除 實(shí)現(xiàn)思路:基本上利用了app的原框架,只做了少量改動(dòng)。主要如下: 1) MainActivity.java 當(dāng)用戶點(diǎn)擊“照片”按鈕后,彈出AlertDialog,提示選擇瀏覽類型,然后根據(jù)用戶的選擇,在startActivity(intent)前,調(diào)用 intent.putExtra('picturePath', picturePath); intent.putExtra('scan_type', ScanPicActivity.SCAN_TYPE_VIDEO); 或者 intent.putExtra('picturePath', videoRecordPath); intent.putExtra('scan_type', ScanPicActivity.SCAN_TYPE_PIC); 2)ScanPicActivity.java - 在init()函數(shù)中,scan_type = getIntent().getIntExtra('scan_type', SCAN_TYPE_PIC);保存當(dāng)前的瀏覽類型 - 在每一處涉及“jpeg”字符串的地方,都加入scan_type判斷。代碼從略,詳見(jiàn)項(xiàng)目源代碼 3)Generic.java 仿照getShrinkedPic(),添加函數(shù)getShrinkedPicFromVideo(),核心是ThumbnailUtils.createVideoThumbnail()。代碼從略,詳見(jiàn)項(xiàng)目源代碼 回到頂部(go to top) 參考資料: 1)韋東山嵌入式linux培訓(xùn)3期項(xiàng)目實(shí)戰(zhàn)之usb攝像頭監(jiān)控,手機(jī)App源代碼 2)Android 官方教程:https://developer.android.google.cn/guide/ 3)AndroidStudio3.x開(kāi)發(fā)調(diào)試Android-NDK的C++代碼 4)NDK開(kāi)發(fā)筆記---CMake構(gòu)建JNI 5)雷霄驊的博客系列文章:[總結(jié)]FFMPEG視音頻編解碼零基礎(chǔ)學(xué)習(xí)方法 6)Android 使用 FFmpeg (二)——視屏流播放簡(jiǎn)單實(shí)現(xiàn) 7)如何用FFmpeg API采集攝像頭視頻和麥克風(fēng)音頻,并實(shí)現(xiàn)錄制文件的功能 8)ffmpeg實(shí)現(xiàn)mjpeg攝像頭的采集-預(yù)覽-拍照 9)FFMPEG filter使用實(shí)例(實(shí)現(xiàn)視頻縮放,裁剪,水印等) 10)使用ffmpeg的lib庫(kù)實(shí)現(xiàn)視頻窗口 原始寬高比例/拉伸鋪滿 11)ffmpeg實(shí)現(xiàn)動(dòng)態(tài)調(diào)整字幕內(nèi)容 12)Ffmpeg用法總結(jié)(下) 13)在Android logcat中打印FFmpeg調(diào)試信息
上一篇:韋東山嵌入式Linux_3期之USB攝像頭監(jiān)控_手機(jī)App增加錄像功能(一)
下一篇:ALSA聲卡_從零編寫(xiě)之參數(shù)設(shè)置(基于優(yōu)龍F(tuán)S2410開(kāi)發(fā)板,UDA1341聲卡)
推薦閱讀最新更新時(shí)間:2025-07-13 22:38



設(shè)計(jì)資源 培訓(xùn) 開(kāi)發(fā)板 精華推薦
- 神經(jīng)形態(tài)芯片可能是革新機(jī)器人實(shí)時(shí)電機(jī)控制的未來(lái)
- 從三個(gè)方面理解ARM嵌入式系統(tǒng)
- 自動(dòng)報(bào)警 基于MCU的家庭防盜報(bào)警系統(tǒng)的設(shè)計(jì)
- 存儲(chǔ)控制器及其訪問(wèn)外設(shè)的原理
- 基于51系列單片機(jī)的智能照明控制系統(tǒng)設(shè)計(jì)方案
- 基于STM32的四旋翼飛行器控制系統(tǒng)
- 單片機(jī)應(yīng)用編程技巧解析
- 基于89C52的教室智能節(jié)能照明系統(tǒng)設(shè)計(jì)
- 一種新型的雨量光照傳感器的設(shè)計(jì)
- SSL4101T Green Chip III+ SMPS控制IC典型應(yīng)用電路
- LTM8064IY ±6A、5V(2 象限)模塊穩(wěn)壓器的典型應(yīng)用電路
- LM2902S 單電源函數(shù)發(fā)生器運(yùn)算放大器的典型應(yīng)用電路
- LTC3400ES6-1 單節(jié) AA 電池至 ±3V 同步升壓轉(zhuǎn)換器的典型應(yīng)用電路
- LTM8022,模塊穩(wěn)壓器使電源開(kāi)發(fā)變得快速而簡(jiǎn)單,在 3.6 至 36V 輸入范圍內(nèi)提供 3.3V/1A
- KIT33905BD3EVBE: 評(píng)估套件 - 33905D3,帶CAN和LIN的第二代SBC
- LTC3425EUH 演示板,4MHz,多相器同步升壓轉(zhuǎn)換器,2V 至 3Vin,2 個(gè)電路,5Vout1 @ 2A,5Vout2 @ 1.5A
- TC682 反相倍壓器的典型應(yīng)用
- VAR-DVK-OM37_CE7,基于安裝了 Windows Embedded Compact 7 的 VAR-SOM-OM37 SOM 處理器的開(kāi)發(fā)套件
- 具有低噪聲旁路的 LTM8057MPY 12V 反激式轉(zhuǎn)換器的典型應(yīng)用電路
- Bourns 發(fā)布全新大功率金屬片電流檢測(cè)電阻, 采用 SMD 2010 緊湊型封裝
- 意法半導(dǎo)體推出先進(jìn)的 1600 V IGBT,面向高性價(jià)比節(jié)能家電市場(chǎng)
- EDPF-NT+分散控制系統(tǒng)網(wǎng)絡(luò)防護(hù)解決方案
- 基于PLC控制的易驅(qū)變頻器在布袋除塵器上的應(yīng)用
- 如何利用伺服自動(dòng)化實(shí)現(xiàn)成本降低和產(chǎn)能最大化?
- 壓力傳感器有哪些抗干擾措施?
- 破局!補(bǔ)盲dToF固態(tài)激光雷達(dá)輪番“出手”,禾賽FT120也要靠邊
- 利用正壓送風(fēng)壓力傳感器自動(dòng)控制火災(zāi)風(fēng)口壓力
- 多個(gè)傳感器間相互位置關(guān)系校準(zhǔn)方法
- 樓宇自控BA系統(tǒng)傳感器有哪些?
- 首臺(tái)渦輪噴氣式無(wú)人機(jī)問(wèn)世 可通過(guò)高轉(zhuǎn)速推動(dòng)空氣飛行
- 波士頓動(dòng)力的人形仿生機(jī)器人Atlas越來(lái)越像人了,有何感想?
- 兆易創(chuàng)新總經(jīng)理朱一明辭職 何衛(wèi)代理總經(jīng)理
- 國(guó)家重點(diǎn)研發(fā)專項(xiàng)“工業(yè)機(jī)器人控制器智能升級(jí)”項(xiàng)目啟動(dòng)
- 人造神經(jīng)幫助機(jī)器人“解鎖”更多真實(shí)感覺(jué)
- 中非技術(shù)合作新亮點(diǎn):無(wú)人機(jī)助力非洲“零瘧疾”行動(dòng)
- 三菱重工與日立分道揚(yáng)鑣!旗下火力發(fā)電設(shè)備公司正式更名
- 【首發(fā)】鍵嘉機(jī)器人獲BV百度風(fēng)投數(shù)千萬(wàn)元A輪融資,浩悅資本擔(dān)任獨(dú)家財(cái)務(wù)顧問(wèn)
- 蘋(píng)果雄心勃勃的芯片計(jì)劃
- WiFi取得20年來(lái)最大進(jìn)步,F(xiàn)CC開(kāi)放6Ghz頻段
- 現(xiàn)行防盜報(bào)警模式的分析
- 小功率的AC/DC電路如何做到成本最低?
- 水氫發(fā)動(dòng)機(jī)正式下線,車(chē)輛只需加水即可行駛???
- IT職場(chǎng)生存的一些建議
- 分享TI專家 RS-485 使用要訣:記住這13點(diǎn)!
- 關(guān)于單片機(jī)測(cè)蓄電池剩余電量的問(wèn)題
- 內(nèi)核創(chuàng)建事件后,應(yīng)用端打開(kāi)錯(cuò)誤(指定的路徑無(wú)效)
- MC9S12DG128B的12864程序問(wèn)題
- 【sensorTile】使用Mbed進(jìn)行程序開(kāi)發(fā)的問(wèn)題及解決
- 線性可控硅電源調(diào)光和EMC問(wèn)題