嵌入式linux摄像头录像存储,韦东山嵌入式Linux_3期之USB摄像头监控_手机App增添录像功效(二)...

一、模块划分

i) (主体模块)视频采集播放

ii) 显示模式切换

iii) 摄影

iv) 录像

v) fps显示

vi) 录像的浏览和删除

二、各模块的实现

2.1(主体模块)视频采集播放

2.1.1 参考资料:

主体框架的流程,可参考雷霄骅的上述博文,这里不再赘述

帧显示的流程,大致如下:

13859c8cdd19b48cef4b2f164f90d883.png

2.2 显示模式切换

实现思绪:行使av_filter的scale和pad功效,对获取到的每一个原始帧举行缩放和需要的四边填充。

至于怎样实现两种显示模式下的scale参数(以及pad参数)的切换, 尚未找到最优方式(经测,av_opt_set()只对draw_text有用(详见下文:2.5 fps显示),而对scale和pad无效),

现在暂时接纳较笨的设施:

1)界说两个filter_descr模板,以及对应的AVFilterGraph、AVFilterContext

/* 用于保持长宽比显示模式 */

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

/* 用于保持长宽比显示模式 */

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)初始化时,先挪用keep_img_AR()来预先盘算好两种显示模式对应的filter_descr的值

int keep_img_AR(int nSrcW ,int nSrcH,int nDstW, int nDstH )

{

/* 盘算保持宽高比例后上下有黑边,照样左右有黑边,黑边若干 */

int imgW = 0,imgH = 0;

int padW = 0,padH = 0;

//必须规整为2的倍数,否则ffmpeg盘算pad时会报错: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

padW=(nDstW-imgW)/2;

imgH = nDstH/2*2;

//imgW = -1;

}

else if(imgH

padH=(nDstH-imgH)/2;

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)然后挪用init_filters()来初始化m_filter_graph1、m_buffersink_ctx1、m_buffersrc_ctx1和m_filter_graph2、m_buffersink_ctx2、m_buffersrc_ctx2

4)而切换播放模式,实在就是切换(m_filter_graph1、m_buffersink_ctx1、m_buffersrc_ctx1)和(m_filter_graph2、m_buffersink_ctx2、m_buffersrc_ctx2)三元组

/**

* 播放视频时保持长宽比

*/

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;

}

/**

* 播放视频时填满显示区域

*/

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;

}

注:关于显示模式切换,另一种实现的设施是行使sws_scale()和av_picture_pad(),参考:使用ffmpeg的lib库实现视频窗口 原始宽高比例/拉伸铺满

但代码量较大,而且经测试,发现有些问题,好比:

– 加上av_filter的draw_text后,fps的显示会泛起小幅度的上下跳动,缘故原由待查

– fps的定位较难实现(由于要考虑到pad的宽度)

以是最终没有接纳这个设施(但keep_img_AR()里盘算scale和pad的算法参考了这篇文章)。

2.3 摄影

实现思绪:

1)界说m_pFrameCur代表当前获取到的帧

2)在视频播放函数videoStreamStartPlay()的while循环里,用av_frame_ref(m_pFrameCur, pFrame)使m_pFrameCur指向当前获取到的帧

3)__save_frame_2_jpeg(file_path, m_pFrameCur, m_input_codec_ctx->pix_fmt)实现把当前帧保留到指定的文件中

2.4 录像

该文章的demo里,把录像功效很好的封装在了一个类CAVOutputStream里,我基本上原封不动的拿来用于录像功效的底层实现。

我所添加的事情,是在视频播放函数videoStreamStartPlay()的while循环里,挪用状态机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); //设置视频编码器属性

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层的接口如下:

/* 最先录像 */

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显示

而fps值的动态显示,是行使av_opt_set(filter_ctx_draw_text->priv, “text”, str_fps, 0 )来实现的。

2.6 录像的浏览和删除

实现思绪:基本上行使了app的原框架,只做了少量改动。主要如下:

1) MainActivity.java

当用户点击“照片”按钮后,弹出AlertDialog,提醒选择浏览类型,然后凭据用户的选择,在startActivity(intent)前,挪用

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()函数中,scan_type =  getIntent().getIntExtra(“scan_type”, SCAN_TYPE_PIC);保留当前的浏览类型

– 在每一处涉及“jpeg”字符串的地方,都加入scan_type判断。代码从略,详见项目源代码

3)Generic.java

模仿getShrinkedPic(),添加函数getShrinkedPicFromVideo(),焦点是ThumbnailUtils.createVideoThumbnail()。代码从略,详见项目源代码

参考资料:

1)韦东山嵌入式linux培训3期项目实战之usb摄像头监控,手机App源代码

原文链接:https://www.cnblogs.com/normalmanzhao2003/p/12695432.html

本站声明:网站内容来源于网络,若有侵权,请联系我们,我们将及时处理。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值