Android如何回调编码后的音视频数据

简介: 有开发者提到,在RTMP推送端的基础上,希望能回调编码后的音视频数据,便于开发者对接第三方系统,如GB28181.

有开发者提到,在RTMP推送端的基础上,希望能回调编码后的音视频数据,便于开发者对接第三方系统,如GB28181.


为此,我们加了一下接口:


1. 设置音视频callback


对应接口:

  /**
   * Set Audio Encoded Data Callback.
   *
   * @param audio_encoded_data_callback: Audio Encoded Data Callback.
   *
   * @return {0} if successful
   */
  public native int SmartPublisherSetAudioEncodedDataCallback(long handle, Object audio_encoded_data_callback);
  /**
   * Set Video Encoded Data Callback.
   *
   * @param video_encoded_data_callback: Video Encoded Data Callback.
   *
   * @return {0} if successful
   */
  public native int SmartPublisherSetVideoEncodedDataCallback(long handle, Object video_encoded_data_callback);

设置回调

libPublisher.SmartPublisherSetAudioEncodedDataCallback(publisherHandle, new PublisherAudioEncodedDataCallback());
libPublisher.SmartPublisherSetVideoEncodedDataCallback(publisherHandle, new PublisherVideoEncodedDataCallback());

2. 实现 PublisherAudioEncodedDataCallback 和 PublisherVideoEncodedDataCallback:

    class PublisherAudioEncodedDataCallback implements NTAudioDataCallback
    {
        private int audio_buffer_size = 0;
        private int param_info_size = 0;
        private ByteBuffer audio_buffer_ = null;
        private ByteBuffer parameter_info_ = null;
        @Override
        public ByteBuffer getAudioByteBuffer(int size)
        {
            //Log.i("getAudioByteBuffer", "size: " + size);
            if( size < 1 )
            {
                return null;
            }
            if ( size <= audio_buffer_size && audio_buffer_ != null )
            {
                return audio_buffer_;
            }
            audio_buffer_size = size + 512;
            audio_buffer_size = (audio_buffer_size+0xf) & (~0xf);
            audio_buffer_ = ByteBuffer.allocateDirect(audio_buffer_size);
            // Log.i("getAudioByteBuffer", "size: " + size + " buffer_size:" + audio_buffer_size);
            return audio_buffer_;
        }
        @Override
        public ByteBuffer getAudioParameterInfo(int size)
        {
            //Log.i("getAudioParameterInfo", "size: " + size);
            if(size < 1)
            {
                return null;
            }
            if ( size <= param_info_size &&  parameter_info_ != null )
            {
                return  parameter_info_;
            }
            param_info_size = size + 32;
            param_info_size = (param_info_size+0xf) & (~0xf);
            parameter_info_ = ByteBuffer.allocateDirect(param_info_size);
            //Log.i("getAudioParameterInfo", "size: " + size + " buffer_size:" + param_info_size);
            return parameter_info_;
        }
        public void onAudioDataCallback(int ret, int audio_codec_id, int sample_size, int is_key_frame, long timestamp, int sample_rate, int channel, int parameter_info_size, long reserve)
        {
            Log.i("onAudioDataCallback", "ret: " + ret + ", audio_codec_id: " + audio_codec_id + ", sample_size: " + sample_size + ", timestamp: " + timestamp +
                ",sample_rate:" + sample_rate + ",chn: " + channel + ", parameter_info_size:" + parameter_info_size);
            if ( audio_buffer_ == null)
                return;
            audio_buffer_.rewind();
            if ( ret == 0 && publisherHandle2 != 0 ) {
                libPublisher.SmartPublisherPostAudioEncodedData(publisherHandle2, audio_codec_id, audio_buffer_, sample_size, is_key_frame, timestamp, parameter_info_, parameter_info_size);
            }
        }
    }
    class PublisherVideoEncodedDataCallback implements NTVideoDataCallback
    {
        private int video_buffer_size = 0;
        private ByteBuffer video_buffer_ = null;
        @Override
        public ByteBuffer getVideoByteBuffer(int size)
        {
            //Log.i("getVideoByteBuffer", "size: " + size);
            if( size < 1 )
            {
                return null;
            }
            if ( size <= video_buffer_size &&  video_buffer_ != null )
            {
                return  video_buffer_;
            }
            video_buffer_size = size + 1024;
            video_buffer_size = (video_buffer_size+0xf) & (~0xf);
            video_buffer_ = ByteBuffer.allocateDirect(video_buffer_size);
            // Log.i("getVideoByteBuffer", "size: " + size + " buffer_size:" + video_buffer_size);
            return video_buffer_;
        }
        public void onVideoDataCallback(int ret, int video_codec_id, int sample_size, int is_key_frame, long timestamp, int width, int height, long presentation_timestamp)
        {
            Log.i("onVideoDataCallback", "ret: " + ret + ", video_codec_id: " + video_codec_id + ", sample_size: " + sample_size + ", is_key_frame: "+ is_key_frame +  ", timestamp: " + timestamp +
                ",width: " + width + ", height:" + height + ",presentation_timestamp:" + presentation_timestamp);
            if ( video_buffer_ == null)
                return;
            video_buffer_.rewind();
            if ( ret == 0 && publisherHandle2 !=0 ) {
                libPublisher.SmartPublisherPostVideoEncodedData(publisherHandle2, video_codec_id, video_buffer_, sample_size, is_key_frame, timestamp, presentation_timestamp);
            }
        }
    }

3. 提供开始回调数据和停止回调数据接口:

  /**
   * Start output Encoded Data(用于编码后的音视频数据回调)
   *
   * @return {0} if successful
   */
  public native int SmartPublisherStartOutputEncodedData(long handle);
  /**
   *  Stop output Encoded Data
   *
   * @return {0} if successful
   */
  public native int SmartPublisherStopOutputEncodedData(long handle);

4. 上层demo调用实例:

    class ButtonEncodedDataCallbackListener implements OnClickListener {
        public void onClick(View v) {
            if (isEncodedDatacallbackRunning) {
                stopEncodedDataCallback();
                if (!isPushing && !isRTSPPublisherRunning && !isRecording) {
                    ConfigControlEnable(true);
                }
                btnEncodedDataCallback.setText("启动编码数据回调");
                isEncodedDatacallbackRunning = false;
                if (publisherHandle2 != 0) {
                   libPublisher.SmartPublisherStopPublisher(publisherHandle2);
                    libPublisher.SmartPublisherClose(publisherHandle2);
                    publisherHandle2 = 0;
                }
                return;
            }
            Log.i(TAG, "onClick start encoded data callback..");
            if (libPublisher == null)
                return;
            if (!isPushing && !isRTSPPublisherRunning && !isRecording) {
                InitAndSetConfig();
            }
            libPublisher.SmartPublisherSetAudioEncodedDataCallback(publisherHandle, new PublisherAudioEncodedDataCallback());
            libPublisher.SmartPublisherSetVideoEncodedDataCallback(publisherHandle, new PublisherVideoEncodedDataCallback());
            int startRet = libPublisher.SmartPublisherStartOutputEncodedData(publisherHandle);
            if (startRet != 0) {
                isEncodedDatacallbackRunning = false;
                Log.e(TAG, "Failed to start encoded data callback.");
                return;
            }
            if (!isPushing && !isRTSPPublisherRunning && !isRecording) {
                if (pushType == 0 || pushType == 1) {
                    CheckInitAudioRecorder();    //enable pure video publisher..
                }
                ConfigControlEnable(false);
            }
            btnEncodedDataCallback.setText("停止编码数据回调");
            isEncodedDatacallbackRunning = true;
            int audio_opt = 2;
            int video_opt = 2;
            publisherHandle2 = libPublisher.SmartPublisherOpen(myContext, audio_opt, video_opt,
                    videoWidth, videoHeight);
            if (publisherHandle2 == 0) {
                Log.e(TAG, "sdk open failed!");
                return;
            }
            String relayUrl = "rtmp://player.daniulive.com:1935/hls/stream8888";
            libPublisher.SmartPublisherSetURL(publisherHandle2, relayUrl);
            libPublisher.SmartPublisherStartPublisher(publisherHandle2);
        }
    }
    ;
    //停止编码后数据回调
    private void stopEncodedDataCallback() {
        if(!isEncodedDatacallbackRunning)
        {
            return;
        }
        if (!isPushing && !isRTSPPublisherRunning && !isRecording) {
            if (audioRecord_ != null) {
                Log.i(TAG, "stopRecorder, call audioRecord_.StopRecording..");
                audioRecord_.Stop();
                if (audioRecordCallback_ != null) {
                    audioRecord_.RemoveCallback(audioRecordCallback_);
                    audioRecordCallback_ = null;
                }
                audioRecord_ = null;
            }
        }
        if (libPublisher != null) {
            libPublisher.SmartPublisherStopOutputEncodedData(publisherHandle);
        }
        if (!isPushing && !isRTSPPublisherRunning && !isRecording) {
            if (publisherHandle != 0) {
                if (libPublisher != null) {
                    libPublisher.SmartPublisherClose(publisherHandle);
                    publisherHandle = 0;
                }
            }
        }
    }

此demo为了便于演示方便,另启动了个新的推送实例,音视频编码后的数据,通过新的实例,调用编码后的音视频数据接口,继续推RTMP出去。


此Demo的优越性在于,回调编码后的音视频数据功能,可与推RTMP、录像、内置RTSP服务SDK组合使用,亦可单独使用。

相关文章
|
16天前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
121 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
2月前
|
开发框架 Android开发 iOS开发
安卓与iOS开发中的跨平台策略:一次编码,多平台部署
在移动应用开发的广阔天地中,安卓和iOS两大阵营各占一方。随着技术的发展,跨平台开发框架应运而生,它们承诺着“一次编码,到处运行”的便捷。本文将深入探讨跨平台开发的现状、挑战以及未来趋势,同时通过代码示例揭示跨平台工具的实际运用。
173 3
|
3月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
353 4
|
4月前
|
Java 程序员 开发工具
Android|修复阿里云播放器下载不回调的问题
虽然 GC 带来了很多便利,但在实际编码时,我们也需要注意对象的生命周期管理,该存活的存活,该释放的释放,避免因为 GC 导致的问题。
58 2
|
4月前
|
存储 大数据 数据库
Android经典面试题之Intent传递数据大小为什么限制是1M?
在 Android 中,使用 Intent 传递数据时存在约 1MB 的大小限制,这是由于 Binder 机制的事务缓冲区限制、Intent 的设计初衷以及内存消耗和性能问题所致。推荐使用文件存储、SharedPreferences、数据库存储或 ContentProvider 等方式传递大数据。
162 0
|
5月前
|
XML IDE 开发工具
🔧Android Studio高级技巧大公开!效率翻倍,编码不再枯燥无味!🛠️
【9月更文挑战第11天】在软件开发领域,Android Studio凭借其强大的功能成为Android开发者的首选IDE。本文将揭示一些提升开发效率的高级技巧,包括自定义代码模板、重构工具、高级调试技巧及多模块架构。通过对比传统方法,这些技巧不仅能简化编码流程,还能显著提高生产力。例如,自定义模板可一键插入常用代码块;重构工具能智能分析并安全执行代码更改;高级调试技巧如条件断点有助于快速定位问题;多模块架构则提升了大型项目的可维护性和团队协作效率。掌握这些技巧,将使你的开发之旅更加高效与愉悦。
98 5
|
5月前
|
Web App开发 网络协议 Android开发
Android平台一对一音视频通话方案大比拼:WebRTC VS RTMP VS RTSP,谁才是王者?
【9月更文挑战第4天】本文详细对比了在Android平台上实现一对一音视频通话时常用的WebRTC、RTMP及RTSP三种技术方案。从技术原理、性能表现与开发难度等方面进行了深入分析,并提供了示例代码。WebRTC适合追求低延迟和高质量的场景,但开发成本较高;RTMP和RTSP则在简化开发流程的同时仍能保持较好的传输效果,适用于不同需求的应用场景。
290 1
|
6月前
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
351 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
6月前
|
开发工具 Android开发
Android项目架构设计问题之SDK内部减少每次回调时的冗余判断逻辑如何解决
Android项目架构设计问题之SDK内部减少每次回调时的冗余判断逻辑如何解决
54 0

热门文章

最新文章

  • 1
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
  • 2
    Android历史版本与APK文件结构
  • 3
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 4
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 5
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 6
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 7
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 8
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 9
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
  • 10
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 1
    【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
    14
  • 2
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    28
  • 3
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    34
  • 4
    Android历史版本与APK文件结构
    121
  • 5
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    29
  • 6
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    23
  • 7
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
    60
  • 8
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    37
  • 9
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
    73
  • 10
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    121