本文由陆业聪分享,原题“一文掌握直播技术:实时音视频采集、编码、传输与播放”,本文进行了排版和内容优化。

1、引言

从游戏、教育、电商到娱乐,直播技术的应用场景无处不在。随着移动端的网速越来越快,直播技术的普及和发展将更加迅速。

本文详细介绍了Android端直播技术的全貌,涵盖了从实时音视频采集、编码、传输到解码与播放的各个环节。文章还探讨了直播中音视频同步、编解码器选择、传输协议以及直播延迟优化等关键问题。希望本文能为你提供有关Andriod端直播技术的深入理解和实践指导。

 

2、系列文章

本文是系列文章中的第 11 篇,本系列总目录如下:

3、知识准备

音视频技术的门槛一直以来都相对较高,如果你对音视频相关技术的理论知识了解不多,建议务必优先阅读这几篇零基础音视频入门文章:

  1. 零基础,史上最通俗视频编码技术入门
  2. 爱奇艺技术分享:轻松诙谐,讲解视频编解码技术的过去、现在和将来
  3. 零基础入门:实时音视频技术基础知识全面盘点
  4. 快速掌握11个视频技术相关的基础概念

另外两篇入门提纳式的文章也可以一并阅读:

  1. 写给小白的实时音视频技术入门提纲
  2. 福利贴:最全实时音视频开发要用到的开源工程汇总

以上资料学习完成后,再回头来阅读本篇效果会更好一点。

4、实时音视频采集

4.1音视频采集设备与API

在 Android 设备中,音视频的采集主要依赖于摄像头和麦克风这两个硬件设备。摄像头负责图像的采集,麦克风则负责音频的采集。

为了调用这两个设备,Android 提供了 Camera API 和 AudioRecord API。通过这两个 API,我们可以方便地控制设备,获取音视频数据。

以下是具体实践步骤。

1)使用 Camera 或 Camera2 API 来调用摄像头:

// Camera API

Camera camera = Camera.open();

Camera.Parameters parameters = camera.getParameters();

parameters.setPreviewSize(width, height);

camera.setParameters(parameters);

camera.setPreviewCallback(previewCallback);

camera.startPreview();

// Camera2 API

CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);

String cameraId = cameraManager.getCameraIdList()[0];

CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(cameraId);

StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);

Size[] previewSizes = map.getOutputSizes(SurfaceTexture.class);

// 选择合适的预览尺寸

cameraManager.openCamera(cameraId, stateCallback, null);

2)使用 AudioRecord API 来调用麦克风:

int bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);

AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, channelConfig, audioFormat, bufferSize);

audioRecord.startRecording();

4.2音视频采集参数设置

音视频采集的质量和流畅度,很大程度上取决于采集参数的设置。这些参数包括分辨率、帧率和码率等。

具体是:

  • 1)分辨率:决定了图像的清晰度。高分辨率可以得到更清晰的图像,但也会增加数据量,可能导致网络传输压力增大;
  • 2)帧率:决定了视频的流畅度。高帧率可以得到更流畅的视频,但同样会增加数据量;
  • 3)码率:决定了音视频数据的压缩程度。高码率可以得到更高质量的音视频,但也会增加数据量。

在设置音视频采集参数时,需要根据网络状况和设备性能,做出合适的折衷。

以下是具体实践步骤。

1)设置摄像头的分辨率和帧率:

Camera.Parameters parameters = camera.getParameters();

parameters.setPreviewSize(width, height);

parameters.setPreviewFrameRate(frameRate);

camera.setParameters(parameters);

2)设置 AudioRecord 的采样率、声道数和音频格式:

int sampleRate = 44100;

int channelConfig = AudioFormat.CHANNEL_IN_MONO;

int audioFormat = AudioFormat.ENCODING_PCM_16BIT;

4.3音视频同步与时间戳处理

在直播中,音视频同步是一个重要的问题。

为了实现同步,我们需要为每帧音视频数据添加时间戳。时间戳记录了数据的采集时间,可以用来调整播放顺序,保证音视频的同步。在解码和播放时,播放器会根据时间戳,正确地排列和播放音视频数据。

为了处理视频帧数据和时间戳,我们需要将采集到的音视频帧数据和对应的时间戳封装成一个数据结构,然后将这个结构传递给编码器和传输模块。

以下是一个简单的处理方法。

1)首先,定义一个数据结构来保存音视频帧数据和时间戳:

public class FrameData {

public byte[] data;

public long timestamp;

public FrameData(byte[] data, long timestamp) {

this.data = data;

this.timestamp = timestamp;

}

}

2)在摄像头的预览回调中添加时间戳:

camera.setPreviewCallback(new Camera.PreviewCallback() {

@Override

public void onPreviewFrame(byte[] data, Camera camera) {

long timestamp = System.nanoTime();

// 处理视频帧数据和时间戳

FrameData frameData = new FrameData(data, timestamp);

// 将 frameData 传递给编码器和传输模块

}

});

3)在 AudioRecord 的录音循环中添加时间戳:

while (isRecording) {

long timestamp = System.nanoTime();

int bytesRead = audioRecord.read(buffer, 0, bufferSize);

// 处理音频帧数据和时间戳

FrameData frameData = new FrameData(Arrays.copyOf(buffer, bytesRead), timestamp);

// 将 frameData 传递给编码器和传输模块

}

4)在编码器和传输模块中,根据FrameData对象的时间戳来处理音视频帧数据。

例如,在编码时,将时间戳作为编码后的音视频数据的显示时间;在传输时,根据时间戳来调整发送顺序和发送速度。

这样,在解码和播放时,播放器可以根据时间戳正确地排列和播放音视频数据,实现同步。

5、音频编码

5.1音频编码格式对比

常见的音频编码格式有 AAC 和 Opus 等。AAC 具有较高的编码效率,而 Opus 则在实时通信中表现更优。

具体是:

  • 1)AAC编码格式:适用于非实时通信领域,如音乐、广播、视频等,具有较高的编码效率和广泛的设备兼容性,但在实时通信中的延迟优化较弱;
  • 2)Opus编码格式:适用于实时通信领域,如VoIP、在线会议、游戏语音等,具有高音质、低延迟和强网络适应性,但设备兼容性相对不如AAC。

5.2在Android中实现音频编码

在 Android 中实现音频编码,可以使用 Android 提供的 MediaCodec 类。MediaCodec 支持多种音频编码格式,如 AAC 和 Opus 等。要选择合适的编码格式,可以参考以下步骤。

1)创建一个 MediaCodec 编码器实例:

MediaCodec audioEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_AUDIO_AAC);

2)配置编码器参数:

MediaFormat audioFormat = MediaFormat.createAudioFormat(MediaFormat.MIMETYPE_AUDIO_AAC, sampleRate, channelCount);

audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);

audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);

audioEncoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

3)开始编码:

audioEncoder.start();

6、视频编码

6.1视频编码格式对比

常见的视频编码格式有 H.264、H.265 和 VP8 等。H.264 是当前最常用的编码格式,而 H.265 和 VP8 则在特定场景下有更好的性能。

具体是:

  • 1)H.264编码格式:适用于视频会议、网络直播、视频分享等场景,具有较高的压缩效率和广泛的设备兼容性,但压缩效率相比H.265较低;
  • 2)H.265编码格式:适用于4K、8K超高清视频、虚拟现实等需要高分辨率和高画质的场景,具有极高的压缩效率,但编解码复杂度高,需要更强的计算能力,且设备兼容性相对不如H.264;
  • 3)VP8编码格式:适用于网络视频通话、在线视频服务等对开源和免费有要求的场景,延迟低,适合实时通信,但压缩效率和视频质量不如H.264和H.265,且设备兼容性较差。

6.2 在Android中实现视频编码

在 Android 中实现视频编码,同样可以使用 MediaCodec 类。要选择合适的编码格式,可以参考以下步骤。

1)创建一个 MediaCodec 编码器实例:

MediaCodec videoEncoder = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);

2)配置编码器参数:

MediaFormat videoFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, width, height);

videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);

videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);

videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, iFrameInterval);

videoEncoder.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);

3)开始编码:

videoEncoder.start();

7、硬件编码与软件编码的选择与优缺点

硬件编码利用 GPU 进行编码,性能更高,但兼容性较差;软件编码则兼容性更好,但性能较低。在实际应用中,需要根据设备性能和需求进行选择。

在 Android 中,MediaCodec 类会根据设备性能和需求自动选择硬件编码器或软件编码器。要强制使用硬件编码器或软件编码器,可以在创建 MediaCodec 实例时,指定编码器名称。

例如,要使用硬件 H.264 编码器,可以使用以下代码:

MediaCodec videoEncoder = MediaCodec.createByCodecName("OMX.google.h264.encoder");

8、传输协议

9、音视频解码与播放

9.1音视频解码器的选择与性能优化

解码器的选择会影响播放质量和性能。通常,硬件解码器性能更高,但兼容性较差;软件解码器兼容性较好,但性能较低。在实际应用中,需要根据设备性能和需求进行选择。

在 Android 中,解码器的选择可以通过 MediaCodec 类来实现。MediaCodec 支持硬件解码和软件解码,通常情况下,它会根据设备性能和需求自动选择解码器。

9.2音视频渲染与同步策略

在渲染音视频时,需要保证音视频同步。可以通过校准时间戳或者调整播放速度等方法实现同步。

在 Android 中,音视频的渲染可以通过 SurfaceView 或 TextureView 来实现。为了保证音视频同步,可以在渲染每帧数据时,根据时间戳来调整渲染速度。

以下是具体实践步骤。

1)创建一个 SurfaceView 或 TextureView:

SurfaceView surfaceView = new SurfaceView(context);

// 或

TextureView textureView = new TextureView(context);

2)在解码每帧数据时,根据时间戳来调整渲染速度:

long presentationTimeUs = bufferInfo.presentationTimeUs;

long delayUs = presentationTimeUs - System.nanoTime() / 1000;

if (delayUs > 0) {

Thread.sleep(delayUs / 1000);

}

decoder.releaseOutputBuffer(outputBufferIndex, true);

9.3播放器的缓冲与自适应码率调整

为了应对网络波动,播放器需要设置合适的缓冲策略。自适应码率调整则可以根据网络状况动态调整视频质量,以保证流畅度。

在 Android 中,播放器的缓冲策略可以通过 MediaPlayer 或 ExoPlayer 的 API 来设置。自适应码率调整则可以通过 ExoPlayer 的 TrackSelection API 来实现。

以下是具体实践步骤。

1)设置播放器的缓冲策略:

MediaPlayer mediaPlayer = new MediaPlayer();

mediaPlayer.setBufferingUpdateListener(new MediaPlayer.OnBufferingUpdateListener() {

@Override

public void onBufferingUpdate(MediaPlayer mp, int percent) {

// 更新缓冲进度

}

});

// 或

ExoPlayer exoPlayer = new SimpleExoPlayer.Builder(context).build();

exoPlayer.setBufferedPositionUpdateListener(new ExoPlayer.BufferedPositionUpdateListener() {

@Override

public void onBufferedPositionUpdate(long bufferedPosition) {

// 更新缓冲进度

}

});

2)设置自适应码率调整:

TrackSelection.Factory trackSelectionFactory = new AdaptiveTrackSelection.Factory();

DefaultTrackSelector trackSelector = new DefaultTrackSelector(context, trackSelectionFactory);

ExoPlayer exoPlayer = new SimpleExoPlayer.Builder(context).setTrackSelector(trackSelector).build();

10、直播架构概述

10.1直播架构图

以下是直播架构图:

解释一下:

  • 1)推流端:需要实现音视频采集、编码、传输等功能。关键组件包括采集模块、编码器、传输模块等;
  • 2)服务器端:负责接收、转发和存储音视频数据。关键组件包括负载均衡、转码、录制等功能模块;
  • 3)拉流端:需要实现音视频解码、渲染和播放等功能。关键组件包括解码器、渲染模块、播放器等。

10.2直播延迟与优化策略

直播延迟会影响用户体验。通过优化采集、编码、传输、解码等环节,可以降低延迟,提高实时性。

直播延迟优化策略有:

  • 1)优化采集模块:提高采集效率,减少数据处理时间;
  • 2)优化编码器:选择性能更高的编码器,减少编码时间;
  • 3)优化传输模块:优化网络传输策略,如使用更快的传输协议、提高网络带宽等;
  • 4)优化解码器:选择性能更高的解码器,减少解码时间;
  • 5)优化渲染模块和播放器:提高渲染效率,减少播放延迟。

11、本文小结

本文介绍了直播技术的全貌,涉及实时音视频采集到播放的各个环节。

以下是一个简化的直播流程图:

直播流程包括以下几个关键环节:

  • 1)实时音视频采集:通过摄像头和麦克风采集音视频数据,并进行参数设置和同步处理;
  • 2)音视频编码:将采集到的音视频数据进行编码,以便进行传输。选择合适的编码器和编码格式,如AAC、Opus、H.264、H.265和VP8等;
  • 3)传输协议:选择合适的传输协议,如RTMP、HLS和WebRTC等,以保证音视频数据的实时传输;
  • 4)服务器处理:服务器接收、转发和存储音视频数据,进行负载均衡、转码和录制等处理;
  • 5)音视频解码与播放:将接收到的音视频数据进行解码、渲染和播放,实现音视频同步和延迟优化。

在实际应用中,需要根据需求和场景选择合适的技术和策略,以实现高质量、低延迟的直播体验。

12、参考资料

[1] 详解音频编解码的原理、演进和应用选型

[2] 零基础,史上最通俗视频编码技术入门

[3] 理解实时音视频聊天中的延时问题一篇就够

[4] 浅谈开发实时视频直播平台的技术要点

[5] 福利贴:最全实时音视频开发要用到的开源工程汇总

[6] 爱奇艺技术分享:轻松诙谐,讲解视频编解码技术的过去、现在和将来

[7] 零基础入门:实时音视频技术基础知识全面盘点

[8] 实时音视频面视必备:快速掌握11个视频技术相关的基础概念

[9] 理论联系实际:实现一个简单地基于html]5的实时视频直播

[10] 实时视频直播客户端技术盘点:Native、html]5、WebRTC、微信小程序

[11] Android直播入门实践:动手搭建一套简单的直播系统

[12] 视频直播技术干货:一文读懂主流视频直播系统的推拉流架构、传输协议等

[13] 零基础入门:基于开源WebRTC,从0到1实现实时音视频聊天功能

[14] 实时音视频入门学习:开源工程WebRTC的技术原理和使用浅析

[15] 实时音视频开发理论必备:如何省流量?视频高度压缩背后的预测技术

[16] 万字长文详解QQ Linux端实时音视频背后的跨平台实践

(本文已同步发布于:http://www.52im.net/thread-4714-1-1.html

视频直播技术干货(十二):从入门到放弃,快速学习Android端直播技术的更多相关文章

  1. Spring+SpringMVC+MyBatis深入学习及搭建(十二)——SpringMVC入门程序(一)

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6999743.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十一)——S ...

  2. 《Django By Example》第十二章 中文 翻译 (个人学习,渣翻)

    书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第十二章,全书最后一章,终于到这章 ...

  3. 《vue.js2.0从入门到放弃》学习之路

    原文地址: Vue.js2.0从入门到放弃---入门实例(一):http://blog.csdn.net/u013182762/article/details/53021374 Vue.js2.0从入 ...

  4. 【第十二篇】- Git 服务器搭建之Spring Cloud直播商城 b2b2c电子商务技术总结

    Git 服务器搭建 上一章节中我们远程仓库使用了 Github,Github 公开的项目是免费的,2019 年开始 Github 私有存储库也可以无限制使用. 这当然我们也可以自己搭建一台 Git 服 ...

  5. Android IOS WebRTC 音视频开发总结(十二)-- sufaceview

    谈到音视频不得不谈谈对视频呈现的理解,为了让大家能有一个更好的理解,先看看android里面SurfaceView的原理,后续陆续分享其绘画原理. 说明:本文是转载的,转载自哪里我也不知道,貌似经过很 ...

  6. Android IOS WebRTC 音视频开发总结(十)-- webrtc入门002

    继续上一篇中未翻译完成的部分,主要包括下面三个部分: 1,扩展:WebRTC多方通话. 2,MCU Multipoint Control Unit. 2, 扩展:VOIP,电话,消息通讯. 注意:翻译 ...

  7. opencv探索之路(十二):感兴趣区域ROI和logo添加技术

    在图像处理领域,有一个非常重要的名词ROI. 什么是ROI? 它的英文全称是Region Of Interest,对应的中文解释就是感兴趣区域. 感兴趣区域,就是我们从图像中选择一个图像区域,这个区域 ...

  8. Spring+SpringMVC+MyBatis深入学习及搭建(十二)——SpringMVC入门程序

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6999743.html 前面讲到:Spring+SpringMVC+MyBatis深入学习及搭建(十一)--S ...

  9. 第四十二篇 入门机器学习——Numpy的基本操作——索引相关

    No.1. 使用np.argmin和np.argmax来获取向量元素中最小值和最大值的索引 No.2. 使用np.random.shuffle将向量中的元素顺序打乱,操作后,原向量发生改变:使用np. ...

  10. Jmeter(十二) - 从入门到精通 - JMeter逻辑控制器 - 终篇(详解教程)

    1.简介 Jmeter官网对逻辑控制器的解释是:“Logic Controllers determine the order in which Samplers are processed.”. 意思 ...

随机推荐

  1. 快速搭建hadoop,zk,hbase的基础集群

    1. ZK集群,Hadoop集群,Hbase集群安装 Linux121 Linux122 Linux123 Hadoop MySQL ZK HBASE 1.1 安装Vmware,安装虚拟机集群 1.1 ...

  2. 连接ORACLE数据库,是否必须要安装oracle客户端

    公司升级Oracle版本(12g~19C),服务器端是DBA团队升级.客户端的升级工作由各个团队自己负责完成.突然想为什么Oracle提供的JDBC Driver需要客户端的软件.没有Oracle客户 ...

  3. Java新特性-四大函数式接口

    四大函数式接口指的是Consumer.Function.Predicate.Supplier,位于java.util.function包下: 函数式编程 lamabda表达式 函数式接口:在java中 ...

  4. C# 动态调用webservice代码

    /// <summary> /// 动态调用WebService /// </summary> /// <param name="url">UR ...

  5. SyncOOD:增加OOD目标检测鲁棒性,自动化数据助您一臂之力 | ECCV'24

    本文是对公开论文的核心提炼,而非直接翻译,旨在进行学术交流.如有任何侵权问题,请及时联系号主以便删除. 来源:晓飞的算法工程笔记 公众号,转载请注明出处 论文: Can OOD Object Dete ...

  6. 基于Java+SpringBoot+Mysql实现的快递柜寄取快递系统功能实现七

    一.前言介绍: 1.1 项目摘要 随着电子商务的迅猛发展和城市化进程的加快,快递业务量呈现出爆炸式增长的趋势.传统的快递寄取方式,如人工配送和定点领取,已经无法满足现代社会的快速.便捷需求.这些问题不 ...

  7. [TAD] Triangles of Absolute Differences-反帕斯卡三角形

    [IMO2018] Triangles of Absolute Differences-反帕斯卡三角形 前言 叠甲 笔者不是学数竞的,在此感谢我的数竞生为我讲解题目. 笔者学艺不精,且知识面浅薄. 所 ...

  8. javascript数组合并效率对比

    1.数组元素量级大而合并次数少时,性能对比: concat() > push() > [-array1,-array2] 2.数组元素少但合并次数多时,性能对比: push() > ...

  9. (Python基础教程之十八)Python字典交集–比较两个字典

    Python示例,用于查找2个或更多词典之间的常见项目,即字典相交项目. 1.使用"&"运算符的字典交集 最简单的方法是查找键,值或项的交集,即 & 在两个字典之间 ...

  10. openjdk和jdk的区别与联系

    使用过LINUX的人都应该知道,在大多数LINUX发行版本里,内置或者通过软件源安装JDK的话,都是安装的openjdk,那么到底什么是openjdk,它与sun jdk有什么关系和区别呢?历史上的原 ...