Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >[技术分享]Android平台实时音视频录像模块设计之道

[技术分享]Android平台实时音视频录像模块设计之道

原创
作者头像
音视频牛哥
发布于 2023-05-23 07:38:35
发布于 2023-05-23 07:38:35
6380
举报

实现背景

录像有什么难的?无非就是数据过来,编码保存mp4而已,这可能是好多开发者在做录像模块的时候的思考输出。是的,确实不难,但是做好,或者和其他模块有非常好的逻辑配合,确实不容易。

好多开发者希望聊聊录像模块,实际上录像这块,需求层面的东西大家都清楚,无非就是设计的时候,做的更智能,逻辑清晰而已。

设计思路

以大牛直播SDK的录像模块的技术实现为例,我们在设计的时候,确保录像模块和RTMP推送、内置轻量级RTSP服务、转发模块、GB28181设备接入模块完全隔离,可以组合使用,也可以分开始用。

录像数据源,这块很好理解,无非就是编码前的yuv、nv12、nv21、rgb、pcm等( 比如Android camera、camera2,或者otg采集到的数据等),编码成H.264/H.265/AAC,或外部接口直接投递的编码后的264、h265、aac等。

录像模块的功能层面,比较好理解,比如需要支持随时录像,设置单个录像文件大小、录像路径等,并支持纯音频、纯视频、音视频录制模式,此外,最好支持录像过程中,暂停录像、恢复录像。 从开始录像,到录像结束,需要设计event callback,告诉上层逻辑,什么时候开始录像了,什么时候生成了个录像文件,路径是什么。

  • 文件格式:MP4;
  • 涉及相关库:libSmartPublisher.so
  • 头文件:SmartPublisherJniV2.java
  • Jar:smartavengine.jar

接口概述

Android录像模块接口概述

调用描述

接口

接口描述

录像设置

是否录像

SmartPublisherSetRecorder

设置是否启用本地录像

创建录像目录

SmartPublisherCreateFileDirectory

创建录像文件目录

设置录像目录

SmartPublisherSetRecorderDirectory

设置录像文件目录

音频录像

SmartPublisherSetRecorderAudio

音频录制开关

视频录像

SmartPublisherSetRecorderVideo

视频录制开关

设置录像文件大小

SmartPublisherSetRecorderFileMaxSize

设置每个录像文件的大小,比如100M,超过这个大小后,会自动生成下一个录像文件

开始录像

SmartPublisherStartRecorder

开始录像

暂停/恢复录像

SmartPublisherPauseRecorder

Pause recorder(暂停/恢复录像)

停止录像

SmartPublisherStopRecorder

停止录像

调用示例

录像配置

代码语言:java
AI代码解释
复制
void ConfigRecorderParam() {
        if (libPublisher != null && publisherHandle != 0) {
            if (recDir != null && !recDir.isEmpty()) {

                int ret = libPublisher.SmartPublisherCreateFileDirectory(recDir);
                if (0 == ret) {
                    if (0 != libPublisher.SmartPublisherSetRecorderDirectory(publisherHandle, recDir)) {
                        Log.e(TAG, "Set record dir failed , path:" + recDir);
                        return;
                    }

                    // 更细粒度控制录像的, 一般情况无需调用
                    //libPublisher.SmartPublisherSetRecorderAudio(publisherHandle, 0);
                    //libPublisher.SmartPublisherSetRecorderVideo(publisherHandle, 0);

                    if (0 != libPublisher.SmartPublisherSetRecorderFileMaxSize(publisherHandle, 200)) {
                        Log.e(TAG, "SmartPublisherSetRecorderFileMaxSize failed.");
                        return;
                    }

                } else {
                    Log.e(TAG, "Create record dir failed, path:" + recDir);
                }
            }
        }
    }

开始、停止录像

代码语言:java
AI代码解释
复制
class ButtonStartRecorderListener implements View.OnClickListener {
        public void onClick(View v) {
            if (isRecording) {
                stopRecorder();

                if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {
                    ConfigControlEnable(true);
                }

                btnStartRecorder.setText("实时录像");

                btnPauseRecorder.setText("暂停录像");
                btnPauseRecorder.setEnabled(false);
                isPauseRecording = true;

                return;
            }

            Log.i(TAG, "onClick start recorder..");

            if (libPublisher == null)
                return;

            if (!isPushingRtmp && !isRTSPPublisherRunning&& !isGB28181StreamRunning) {
                InitAndSetConfig();
            }

            ConfigRecorderParam();

            int startRet = libPublisher.SmartPublisherStartRecorder(publisherHandle);
            if (startRet != 0) {
                if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {
                    if (publisherHandle != 0) {
                        long handle = publisherHandle;
                        publisherHandle = 0;
                        libPublisher.SmartPublisherClose(handle);
                    }
                }

                Log.e(TAG, "Failed to start recorder.");
                return;
            }

            if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {
                CheckInitAudioRecorder();
                ConfigControlEnable(false);
            }

            startLayerPostThread();

            btnStartRecorder.setText("停止录像");
            isRecording = true;

            btnPauseRecorder.setEnabled(true);
            isPauseRecording = true;
        }
    }

停止录像封装

代码语言:java
AI代码解释
复制
//停止录像
    private void stopRecorder() {
        if(!isRecording)
            return;

        isRecording = false;

        if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning)
            stopLayerPostThread();

        if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {
            if (audioRecord_ != null) {
                Log.i(TAG, "stopRecorder, call audioRecord_.StopRecording..");

                audioRecord_.Stop();

                if (audioRecordCallback_ != null) {
                    audioRecord_.RemoveCallback(audioRecordCallback_);
                    audioRecordCallback_ = null;
                }

                audioRecord_ = null;
            }
        }

        if (null == libPublisher || 0 == publisherHandle)
            return;

        libPublisher.SmartPublisherStopRecorder(publisherHandle);

        if (!isPushingRtmp && !isRTSPPublisherRunning && !isGB28181StreamRunning) {
            releasePublisherHandle();
        }
    }

暂停/恢复录像

代码语言:java
AI代码解释
复制
class ButtonPauseRecorderListener implements View.OnClickListener {
        public void onClick(View v) {
            if (isRecording) {

                if(isPauseRecording)
                {
                    int ret = libPublisher.SmartPublisherPauseRecorder(publisherHandle, 1);

                    if (ret == 0)
                    {
                        isPauseRecording = false;
                        btnPauseRecorder.setText("恢复录像");
                    }
                    else if(ret == 3)
                    {
                        Log.e(TAG, "Pause recorder failed, please re-try again..");
                    }
                    else
                    {
                        Log.e(TAG, "Pause recorder failed..");
                    }
                }
                else
                {
                    int ret = libPublisher.SmartPublisherPauseRecorder(publisherHandle, 0);

                    if (ret == 0)
                    {
                        isPauseRecording = true;
                        btnPauseRecorder.setText("暂停录像");
                    }
                    else if(ret == 3)
                    {
                        Log.e(TAG, "Resume recorder failed, please re-try again..");
                    }
                    else
                    {
                        Log.e(TAG, "Resume recorder failed..");
                    }
                }
            }
        }
    }

event回调

代码语言:java
AI代码解释
复制
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_RECORDER_START_NEW_FILE:
     publisher_event = "开始一个新的录像文件 : " + param3;
     break;
case NTSmartEventID.EVENT_DANIULIVE_ERC_PUBLISHER_ONE_RECORDER_FILE_FINISHED:
     publisher_event = "已生成一个录像文件 : " + param3;
     break;

技术总结

录像模块,单纯地实现不难,如果是需要和GB28181设备接入模块、RTMP推送、轻量级RTSP服务模块一起使用的时候,需要考虑的就多了,感兴趣的开发者,可以酌情参考。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
GB28181设备接入侧录像查询和录像下载技术探究之实时录像
我们在对接GB28181设备接入侧的时候,除了常规实时音视频按需上传外,还有个重要的功能,就是本地实时录像,录像后的数据,在执法记录仪等前端设备留底,然后,到工作站拷贝到专门的平台。
音视频牛哥
2023/07/16
6700
GB28181设备接入侧录像查询和录像下载技术探究之实时录像
Android平台GB28181设备接入模块如何实现实时视频和本地录像双码流编码
我们在做Android平台GB28181设备接入模块的时候,遇到这样的场景,比如执法记录仪或智慧工地等场景下,由于GB28181设备接入模块,注册到国标平台后,平时只是心跳保持,或还有实时位置订阅,查看视频的时候,是按需看,而且有时候,网络环境并不是太好,所以,催生了这样一个诉求:部分开发者希望能本地录像的时候,录制高分辨率(比如1920*1080),国标平台侧发起实时视频查看请求的时候,上传低分辨率(如1280*720)数据,有点类似于IPC的主码流和子码流。
音视频牛哥
2023/05/23
4940
Android平台GB28181设备接入模块如何实现实时视频和本地录像双码流编码
Android平台GB28181设备接入模块之按需编码和双码流编码
我们在做执法记录仪或指挥系统的时候,会遇到这样的情况,大多场景下,我们是不需要把设备端的数据,实时传给国标平台端的,默认只需要本地录像留底,如果指挥中心需要查看前端设备实时数据的时候,发起视频播放请求,设备侧再推送数据到平台侧,如需语音广播,只要发起语音广播(broadcast),GB28181设备接入侧响应,然后发送INVITE请求等,完成语音广播和语音对讲。此外,考虑到设备侧的上行带宽瓶颈,一般来说,本地录像需要尽可能清晰(比如1920*1080分辨率),上传视频数据,传输1280*720分辨率,也就是我们传统意义提到的双码流编码。
音视频牛哥
2023/07/25
4330
Android平台GB28181设备接入模块之按需编码和双码流编码
Android平台GB28181设备接入端如何实现本地录像?
实现Android平台GB28181设备接入的时候,有个功能点不可避免,那就是本地录像,实际上,在实现GB28181设备接入模块之前,我们前些年做RTMP推送和轻量级RTSP服务的时候,早已经实现了本地录像功能。
音视频牛哥
2022/10/04
4310
Android平台GB28181设备接入端如何实现本地录像?
Android平台实现内网无纸化会议|智慧教室|实时同屏功能
1. 组网:无线组网,需要好的AP模块才能撑得住大的并发流量,推送端到AP,最好是有线网链接;
音视频牛哥
2021/05/12
1.7K0
Android平台RTSP/RTMP推送端回调编码后的音视频数据
有开发者提到,在RTMP/RTSP推送端的基础上,希望能回调编码后的音视频数据,便于开发者对接第三方系统,如GB28181.
音视频牛哥
2019/09/17
8020
Android平台GB28181设备接入模块开发填坑指南
为什么要开发Android平台GB28181设备接入模块?这个问题不再赘述,在做Android平台GB28181客户端的时候,媒体数据这块,我们已经有了很好的积累,因为在此之前,我们就开发了非常成熟的RTMP推送、轻量级RTSP服务、录像模块、针对音视频的对接处理单元。这让我们在做Android平台GB28181设备接入模块的时候,可以有更多的精力在信令交互和国标平台对接。
音视频牛哥
2023/11/26
7210
Android平台GB28181设备接入模块开发填坑指南
Android平台摄像头麦克风视音频采集录像之MediaRecorder还是SmartPublisher
在 Android 中录制摄像头采集的数据到 MP4 文件,我们可以用系统自带的MediaRecorder,也可以用第三方成熟的摄像头采集录制库,本文就两种方案,做个大概的梳理。
音视频牛哥
2024/11/21
1940
Android平台摄像头麦克风视音频采集录像之MediaRecorder还是SmartPublisher
Android平台音视频推送选RTMP还是GB28181?
早在2015年,我们发布了RTMP直播推送模块,那时候音视频直播这块场景需求,还不像现在这么普遍,我们做这块的初衷,主要是为了实现移动单兵应急指挥系统的低延迟音视频数据传输。好多开发者可能会疑惑,走RTMP怎么可能低延迟?网上看到的RTMP推拉流延迟,总归要2-3秒起,如果是自己实现框架,RTMP推拉流逻辑自己实现的话,延迟确实可以控制在毫秒级,这个已无需赘述。
音视频牛哥
2023/05/26
5130
Android平台音视频推送选RTMP还是GB28181?
Android平台GB28181设备接入侧如何同时对外输出RTSP流?
GB28181的应用场景非常广泛,如公共安全、交通管理、企业安全、教育、医疗等众多领域,细分场景可用于如执法记录仪、智能安全帽、智能监控、智慧零售、智慧教育、远程办公、明厨亮灶、智慧交通、智慧工地、雪亮工程、平安乡村、生产运输、车载终端等:
音视频牛哥
2023/07/28
2440
Android平台GB28181设备接入侧如何同时对外输出RTSP流?
Android平台基于RTMP或RTSP的一对一音视频互动技术方案探讨
随着智能门禁等物联网产品的普及,越来越多的开发者对音视频互动体验提出了更高的要求。目前市面上大多一对一互动都是基于WebRTC,优点不再赘述,我们这里先说说可能需要面临的问题:WebRTC的服务器部署非常复杂,可以私有部署,但是非常复杂。传输基于UDP,很难保证传输质量,由于UDP是不可靠的传输协议,在复杂的公网网络环境下,各种突发流量、偶尔的传输错误、网络抖动、超时等等都会引起丢包异常,都会在一定程度上影响音视频通信的质量,难以应对复杂的互联网环境,如跨区跨运营商、低带宽、高丢包等场景,行话说的好:从demo到实用,中间还差1万个WebRTC。
音视频牛哥
2021/11/22
6800
Android平台实现系统内录(捕获播放的音频)并推送RTMP服务技术方案探究
几年来,我们在做无纸化同屏或在线教育相关场景的时候,总是被一件事情困扰:如何实现Android平台的系统内录,并推送到其他播放端,常用的场景比如做无纸化会议或教育的时候,主讲人或老师需要放一个视频,该怎么办呢?这里我们分析三种可行的技术方案:
音视频牛哥
2022/11/10
2.2K0
Android平台GB28181设备接入端实现实时快照
Android平台GB28181设计开发的时候,有个功能必不可少的:实时快照,特别是用于执法记录仪等场景下,用于图像留底或分析等考量。
音视频牛哥
2022/10/04
2910
Android平台实现mp4文件实时推送RTMP|轻量级RTSP服务|GB28181平台
好多开发者有这样的诉求,想把本地录制的MP4文件,以实时流数据的形式,推送到RTMP服务器,注入轻量级RTSP服务,或者对接到GB28181平台,这块前几年我们就有对接。
音视频牛哥
2022/09/29
3970
如何实现Android平台GB28181设备对接Camera2数据
在写如何实现Android平台GB28181设备对接Camera2数据说明之前,我在前两年的blog就有针对camera2的RTMP直播推送模块做过技术分享:
音视频牛哥
2022/09/27
7210
如何实现Android平台GB28181设备对接Camera2数据
Android平台实现屏幕数据采集并推送至RTMP服务器
随着无纸化、智慧教室等场景的普及,好多企业或者开发者开始寻求更高效稳定低延迟的RTMP同屏方案,本文以大牛直播SDK(Github)的同屏demo(对应工程:SmartServicePublisherV2)为例,介绍下如何采集编码推送RTMP数据到流媒体服务器。
音视频牛哥
2020/06/14
1.2K0
Android平台实现RTSP|RTMP转GB28181网关接入
在事先Android平台RTSP、RTMP转GB28181网关之前,我们已经实现了Android平台GB28181的接入,可实现Android平台采集到的音视频数据,编码后,打包按需发到GB28181服务平台。此外,拉流端,我们已经有了成熟的RTSP和RTMP拉流播放方案。
音视频牛哥
2022/04/19
7260
Android平台实现RTSP|RTMP转GB28181网关接入
跨平台轻量级RTSP服务模块设计思路及实现探讨
为满足内网无纸化/电子教室等内网超低延迟需求,避免让用户配置单独的服务器,我们发布了轻量级RTSP服务模块,轻量级RTSP服务解决的核心痛点是避免用户或者开发者单独部署RTSP或者RTMP服务,实现本地的音视频数据(如摄像头、麦克风),编码后,汇聚到内置RTSP服务,对外提供可供拉流的RTSP URL,轻量级RTSP服务,适用于内网环境下,对并发要求不高的场景,支持H.264/H.265,支持RTSP鉴权、单播、组播模式,考虑到单个服务承载能力,我们支持同时创建多个RTSP服务,并支持获取当前RTSP服务会话连接数。
音视频牛哥
2023/07/09
3110
跨平台轻量级RTSP服务模块设计思路及实现探讨
GB/T28181-2022图像抓拍规范解读及技术实现
GB28181-2022相对2016,增加了设备软件升级、图像抓拍信令流程和协议接口。我们先回顾下规范说明:
音视频牛哥
2023/03/06
1.6K0
GB/T28181-2022图像抓拍规范解读及技术实现
Android平台GB28181设备接入端如何降低资源占用和性能消耗
我们在做GB28181设备接入模块的时候,考虑到好多设备性能一般,我们一般的设计思路是,先注册设备到平台侧,平台侧发calalog过来,获取设备信息,然后,设备侧和国标平台侧维持心跳,如果有位置订阅信息,按照订阅时间间隔,实时上报设备位置信息。
音视频牛哥
2023/08/06
2680
Android平台GB28181设备接入端如何降低资源占用和性能消耗
推荐阅读
相关推荐
GB28181设备接入侧录像查询和录像下载技术探究之实时录像
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档