Android RTMP直播练习实践
前言:本文只是练习,本文只是练习,本文只是练习!
直播的核心就是推流和拉流,我们就以RTMP的协议来实现下推流和拉流,其他的协议等我学习后再来补充
1.推流
1.1搭建流媒体服务器,具体搭建方法请参照Windows搭建RTMP服务器_rtmp服务器搭建-CSDN博客
一些软件需要搭梯子,如果没法下载的,可以联系下我,我私发,搭建好后,在浏览器里输入http://localhost:9091/stat
出现该界面,说明搭建成功
1.2推流,上面的搭建文章,有OBS推流和ffmpeg推流,这些推流是软件操作和命令行操作,在Andorid客户端不方便,我们这里采用WangShuo1143368701/WSLiveDemo: 音视频,直播SDK,rtmp推流,录制视频,滤镜。百万用户,线上迭代半年,已经稳定。这个库来进行推流
1.2.1首先build.gradle里进行依赖
implementation 'com.github.WangShuo1143368701:WSLiveDemo:v1.7'
1.2.2进行ndk配置
defaultConfig {applicationId "com.anssy.videolive"minSdk 24targetSdk 34versionCode 1versionName "1.0"ndk {// 设置支持的SO库架构(开发者可以根据需要,选择一个或多个平台的so)abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86", "arm64-v8a", "x86_64"}testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}
1.2.3 接下来,就是一些权限的申请,布局的编写,和对应代码的实现了,申请的权限是Camera和Record Audio
这里引入一个常用的权限框架
implementation 'com.github.getActivity:XXPermissions:20.0'
具体实现代码:
activity_live_video.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><me.lake.librestreaming.ws.StreamLiveCameraViewandroid:id="@+id/stream_previewView"android:layout_width="match_parent"android:layout_above="@id/bottom_layout"android:layout_height="match_parent"/><LinearLayoutandroid:layout_width="match_parent"android:orientation="horizontal"android:id="@+id/bottom_layout"android:layout_alignParentBottom="true"android:layout_height="wrap_content"><Buttonandroid:layout_width="0dp"android:text="开始推流"android:id="@+id/btn_startStreaming"android:layout_height="wrap_content"android:layout_weight="1"/><Buttonandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:text="停止推流"android:id="@+id/btn_stopStreaming"/><Buttonandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:id="@+id/watch_live"android:text="查看直播"/><Buttonandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:id="@+id/btn_startRecord"android:text="开始录制"/><Buttonandroid:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:id="@+id/btn_stopRecord"android:text="停止录制"/></LinearLayout>
</RelativeLayout>
MainActivity.kt 这里有个注意点,就是这个rtmp的地址,自己搞了半天,这个地址是你本地的IP+在niginx里config文件里面配置的端口号,然后后面的程序名称默认是live,也可以是你配置的程序,具体看画红框的部分
package com.anssy.videolive.uiimport android.content.Intent
import android.os.Bundle
import android.view.View
import android.view.View.OnClickListener
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.anssy.videolive.R
import com.anssy.videolive.databinding.ActivityLiveVideoBinding
import com.hjq.permissions.OnPermissionCallback
import com.hjq.permissions.Permission
import com.hjq.permissions.XXPermissions
import jp.co.cyberagent.android.gpuimage.GPUImageAddBlendFilter
import me.lake.librestreaming.core.listener.RESConnectionListener
import me.lake.librestreaming.filter.hardvideofilter.BaseHardVideoFilter
import me.lake.librestreaming.filter.hardvideofilter.HardVideoGroupFilter
import me.lake.librestreaming.ws.StreamAVOption
import me.lake.librestreaming.ws.StreamLiveCameraView
import me.lake.librestreaming.ws.filter.hardfilter.GPUImageBeautyFilter
import me.lake.librestreaming.ws.filter.hardfilter.extra.GPUImageCompatibleFilter
import java.util.LinkedList/*** @Description rtmp的直播练习* @Author yulu* @CreateTime 2025年01月21日 09:14:03*/class MainActivity :AppCompatActivity(),RESConnectionListener,OnClickListener{private lateinit var mLiveCameraView: StreamLiveCameraViewprivate lateinit var streamAVOption: StreamAVOptionprivate val rtmpUrl = "rtmp://192.168.0.209:1935/hls/"private lateinit var mMainViewBinding:ActivityLiveVideoBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)mMainViewBinding = ActivityLiveVideoBinding.inflate(layoutInflater)setContentView(mMainViewBinding.root)initView()requestPermission()}/*** 请求权限*/private fun requestPermission() {val permissionList: MutableList<String> = ArrayList()permissionList.add(Permission.CAMERA)permissionList.add(Permission.RECORD_AUDIO)XXPermissions.with(this).permission(permissionList).request(object : OnPermissionCallback {override fun onGranted(permissions: List<String>, all: Boolean) {if (all) {initConfig()}}override fun onDenied(permissions: List<String>, never: Boolean) {}})}private fun initView(){mLiveCameraView = mMainViewBinding.streamPreviewViewmMainViewBinding.btnStartStreaming.setOnClickListener(this)mMainViewBinding.btnStopStreaming.setOnClickListener(this)mMainViewBinding.btnStartRecord.setOnClickListener(this)mMainViewBinding.btnStopRecord.setOnClickListener(this)mMainViewBinding.watchLive.setOnClickListener(this)}/*** 进行相关配置*/private fun initConfig() {//参数配置 startstreamAVOption = StreamAVOption()streamAVOption.streamUrl = rtmpUrl//参数配置 endmLiveCameraView.init(this, streamAVOption)mLiveCameraView.addStreamStateListener(this)//设置滤镜组val files = LinkedList<BaseHardVideoFilter>()files.add(GPUImageCompatibleFilter(GPUImageBeautyFilter()))files.add(GPUImageCompatibleFilter(GPUImageAddBlendFilter()))mLiveCameraView.setHardVideoFilter(HardVideoGroupFilter(files))}override fun onDestroy() {super.onDestroy()mLiveCameraView.destroy()}override fun onOpenConnectionResult(result: Int) {//result 0成功 1 失败runOnUiThread {Toast.makeText(this,"打开推流连接 状态:$result 推流地址:$rtmpUrl", Toast.LENGTH_LONG).show()}}override fun onWriteError(result: Int) {runOnUiThread {Toast.makeText(this,"推流出错,请尝试重连",Toast.LENGTH_LONG).show()}}override fun onCloseConnectionResult(result: Int) {runOnUiThread {Toast.makeText(this,"关闭推流连接 状态:$result",Toast.LENGTH_LONG).show()}}override fun onClick(v: View) {when(v.id){//开始推流R.id.btn_startStreaming-> {if (!mLiveCameraView.isStreaming) {mLiveCameraView.startStreaming(rtmpUrl)} else {Toast.makeText(this, "未打开", Toast.LENGTH_SHORT).show()}}//结束推流R.id.btn_stopStreaming->{if (mLiveCameraView.isStreaming) {mLiveCameraView.stopStreaming()}}//查看直播R.id.watch_live->{val intent = Intent(this,VideoLiveActivity::class.java)intent.putExtra("url",this.rtmpUrl)startActivity(intent)}//开始录制R.id.btn_startRecord->{if (!mLiveCameraView.isRecord) {mLiveCameraView.startRecord()}}//结束录制R.id.btn_stopRecord->{if (mLiveCameraView.isRecord) {mLiveCameraView.stopRecord()}}}}}
弄完后,就可以推流了,推流成功的效果如下
2.拉流
2.1 VLC播放器播放,在VLC播放器中输入推流的地址
点击媒体->打开网络串流->输入地址
rtmp://192.168.0.209:1935/hls/
2.2使用IJK内核的播放器来播放该地址,这里引入一个第三方库
Doikki/DKVideoPlayer: Android Video Player. 安卓视频播放器,封装MediaPlayer、ExoPlayer、IjkPlayer。模仿抖音并实现预加载,列表播放,悬浮播放,广告播放,弹幕,视频水印,视频滤镜
build.gradle中依赖
implementation 'xyz.doikki.android.dkplayer:dkplayer-java:3.3.7'implementation 'xyz.doikki.android.dkplayer:player-ijk:3.3.7'implementation 'xyz.doikki.android.dkplayer:dkplayer-ui:3.3.7'
application中初始化
package com.anssy.videolive.baseimport android.app.Application
import xyz.doikki.videoplayer.ijk.IjkPlayerFactory
import xyz.doikki.videoplayer.player.VideoViewConfig
import xyz.doikki.videoplayer.player.VideoViewManager/*** @Description TODO* @Author yulu* @CreateTime 2025年01月21日 09:44:15*/class BaseApplication : Application() {override fun onCreate() {super.onCreate()VideoViewManager.setConfig(VideoViewConfig.newBuilder().setPlayerFactory(IjkPlayerFactory.create())//使用使用IjkPlayer解码 直播使用.build())}}
activity_video_play.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:background="@color/black"android:layout_height="match_parent"><xyz.doikki.videoplayer.player.VideoViewandroid:id="@+id/player"android:layout_centerInParent="true"android:layout_width="match_parent"android:layout_height="match_parent" /><LinearLayoutandroid:layout_width="45dp"android:layout_height="45dp"android:onClick="back"android:id="@+id/back_layout"android:orientation="horizontal"tools:ignore="UsingOnClickInXml"><ImageViewandroid:id="@+id/img_back"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:contentDescription="@null"android:scaleType="centerInside"android:src="@drawable/back_sting_white" /></LinearLayout>
</RelativeLayout>
VideoLiveActivity.kt
package com.anssy.videolive.uiimport android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.anssy.videolive.databinding.ActivityVideoPlayBinding
import xyz.doikki.videocontroller.StandardVideoController/*** @Description 用于直播播放的控制器* @Author yulu* @CreateTime 2025年01月21日 09:50:41*/class VideoLiveActivity : AppCompatActivity() {private lateinit var mVideoLiveVideoBinding: ActivityVideoPlayBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)mVideoLiveVideoBinding = ActivityVideoPlayBinding.inflate(layoutInflater)setContentView(mVideoLiveVideoBinding.root)initView()}private fun initView() {mVideoLiveVideoBinding.player.setUrl(if (intent.getStringExtra("url")==null) "rtmp://192.168.0.209:1935/hls/" else intent.getStringExtra("url")) //设置视频地址val controller = StandardVideoController(this)controller.addDefaultControlComponent("", false)mVideoLiveVideoBinding.player.setVideoController(controller) //设置控制器mVideoLiveVideoBinding.player.start() //开始播放,不调用则不自动播放}public fun back(view:View){finish()}override fun onPause() {super.onPause()mVideoLiveVideoBinding.player.pause()}override fun onResume() {super.onResume()mVideoLiveVideoBinding.player.resume()}override fun onDestroy() {super.onDestroy()mVideoLiveVideoBinding.player.release()}override fun onBackPressed() {if (!mVideoLiveVideoBinding.player.onBackPressed()) {super.onBackPressed()}}
}
播放效果:
20250121_112120
源码下载地址:rtmp练习,kotlin资源-CSDN文库
相关文章:

Android RTMP直播练习实践
前言:本文只是练习,本文只是练习,本文只是练习! 直播的核心就是推流和拉流,我们就以RTMP的协议来实现下推流和拉流,其他的协议等我学习后再来补充 1.推流 1.1搭建流媒体服务器,具体搭建方法请参…...

ITIL认证工具商-ManageEngine Servicedesk Plus
ServiceDesk Plus是Zoho Corporation旗下企业IT管理部门ManageEngine提供的统一服务管理解决方案。凭借其无限的可扩展性、情境化的IT和业务集成以及一键式工作流程自动化功能,IT领导者可以使用ServiceDesk Plus有效执行和控制跨不同业务部门和IT功能的复杂工作流程…...
https 的 CA证书和电子签名
https 的攻击者可能使用伪造的一对公私钥与客户端交互, 那么如何确保确实是该服务器的公钥呢? 这就诞生了CA颁发机构 CA颁发机构 服务器和客户端都信任指定的CA颁发机构 服务器上传服务器公钥, CA颁发机构做了什么 服务器公钥哈希, 记为 Hash使用 CA 私钥为 Hash 进行 CA 签…...
频繁刷新网页会对服务器造成哪些影响?
当用户在进行浏览网页的过程中频繁刷新页面时,浏览器会向服务器发送请求,服务器会对该请求进行处理并返回到相应的页面内容中,所以频繁刷新网页会对服务器造成影响,有可能会出现以下问题: 用户每次刷新网页都会向服务器…...

贪心算法(题1)区间选点
输出 2 #include <iostream> #include<algorithm>using namespace std;const int N 100010 ;int n; struct Range {int l,r;bool operator <(const Range &W)const{return r<W.r;} }range[N];int main() {scanf("%d",&n);for(int i0;i&l…...
JavaWeb开发学习笔记--MySQL
MySQL-DQL 基本语法: select 字段列表 from 表名列表 where 条件列表 group by 分组字段列表 having 分组后条件列表 order by 排序字段列表 limit 分页参数 基本查询 关键字:SELECT 查询多个字段:select 字…...

抖音小程序一键获取手机号
前端代码组件 <button v-if"!isFromOrderList"class"get-phone-btn" open-type"getPhoneNumber"getphonenumber"onGetPhoneNumber">一键获取</button>// 获取手机号回调onGetPhoneNumber(e) {var that this tt.login({f…...

iconfont等图标托管网站上传svg显示未轮廓化解决办法
打开即时设计 即时设计 - 可实时协作的专业 UI 设计工具 导入图标后拖入画板里面,右键选择轮廓化 将图标导出...

2008-2020年各省城镇登记失业率数据
2008-2020年各省城镇登记失业率数据 1、时间:2008-2020年 2、来源:国家统计局、统计年鉴 3、指标:行政区划代码、地区名称、年份、城镇登记失业率 4、范围:31省 5、指标说明:城镇登记失业率是指在一定时期内&…...

Linux——信号量和(环形队列消费者模型)
Linux——线程条件变量(同步)-CSDN博客 文章目录 目录 文章目录 前言 一、信号量是什么? 二、信号量 1、主要类型 2、操作 3、应用场景 三、信号量函数 1、sem_init 函数 2、sem_wait 函数 3、sem_post 函数 4、sem_destroy 函数 …...
【JOIN】关键字在MySql中的详细使用
目录 INNER JOIN(内连接) LEFT JOIN(左连接) RIGHT JOIN(右连接) FULL JOIN(全连接) 示例图形化解释JOIN的不同类型 INNER JOIN: LEFT JOIN: RIGHT J…...

渗透测试--攻击常见的Web应用
本文章咱主要讨论,常见Web应用的攻击手法,其中并不完全,因为Web应用是在太多无法囊括全部,但其中的手法思想却值得我们借鉴,所以俺在此做了记录,希望对大家有帮助!主要有以下内容: 1…...

window系统annaconda中同时安装paddle和pytorch环境
一、下载nvidia驱动 Download The Official NVIDIA Drivers | NVIDIA 查看GPU信息 nvidia-smi 二、安装cuda CUDA Toolkit 11.8 Downloads | NVIDIA Developer 按以下步骤下载cuda安装包,我使用的cuda11.8 下载后双击一路下一步安装即可。 查看cuda版本 nvcc …...

python-leetcode-简化路径
71. 简化路径 - 力扣(LeetCode) class Solution:def simplifyPath(self, path: str) -> str:# 使用栈来处理路径stack []# 分割路径,以 / 为分隔符parts path.split(/)for part in parts:if part or part .:# 空字符串或 .࿰…...
浅谈 PID 控制算法
PID 控制算法概念 在我们的生活中可能大家都没有听说过 PID 控制算法,但它可以说是无处不在,小到空调的温度控制、无人机的精准悬停、机器人运作系统,大到飞机和火箭的飞行姿态控制都有 PID 的身影。 PID 控制算法,即比例 - 积分…...

ailx10的专栏电子书(2022版)
最近整理了一下自己的知乎专栏,基于myBase和html help workshop做了一本电子书,一共20个章节,接近280M,19块9,有兴趣的同学私信我,记录了从我上学到工作这些年来的心得体会,以及学习历程&#x…...

WPS按双字段拆分工作表到独立工作簿-Excel易用宝
我们老板真是事多,他说要把这个工作表以月份和支付方式的维度,以这两个字段进行拆分工作表,而且拆分出来的表格要保存一个新的工作簿。 啥事都交给我,他还以为我有三头六臂呢,还好我有易用宝,可以轻松拆分…...

C++ Qt练习项目 日期时间数据 未完待续
个人学习笔记 新建项目 设计UI 实现组件功能 参考资料 4.7日期时间数据_哔哩哔哩_bilibili...

vim文本编辑器
vim命令的使用: [rootxxx ~]# touch aa.txt #首先创建一个文件 [rootxxx ~]# vim aa.txt #vim进入文件aa.txt进行编辑 vim是vi的升级版,具有以下三种基本模式: 输入模式(编辑模式) 点击i进入编辑模式 (说明…...
产品经理面试题总结2025【其一】
一、产品理解与定位 1、你如何理解产品经理这个角色? 作为一名互联网产品经理,我理解这个角色的核心在于成为产品愿景的制定者和执行的推动者。具体来说,产品经理是连接市场、用户和技术团队之间的桥梁,负责理解市场需求、用户痛…...
Android 蓝牙通信
Android 平台提供了完整的蓝牙 API,支持 传统蓝牙(Bluetooth Classic)和低功耗蓝牙(BBluetooth Low Energy, BLE)两种通信方式。 以下是开发蓝牙应用的关键知识点。 1. 基本概念 传统蓝牙(Bluetooth Classic) 适合大流量数据传输(如音频、文件传输) 典型协议: R…...

Windows账户管理,修改密码,创建帐户...(无需密码)
前言 我们使用wWindows操作系统时,账户是非常重要的概念 它不仅能够帮助我们区分文档主题权限等等 嗯还有最重要的解锁电脑的作用! 但想要管理他,不仅需要原本的密码,而且设置中的管理项也非常的不全。 Windows有一款netplwi…...

【数据结构】_排序
【本节目标】 排序的概念及其运用常见排序算法的实现排序算法复杂度及稳定性分析 1.排序的概念及其运用 1.1排序的概念 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。 1.2特性…...

vue+elementui 网站首页顶部菜单上下布局
菜单集合后台接口动态获取,保存到store vuex状态管理器 <template><div id"app"><el-menu:default-active"activeIndex2"class"el-menu-demo"mode"horizontal"select"handleSelect"background-…...

第3章——SSM整合
一、整合持久层框架MyBatis 1.准备数据库表及数据 创建数据库:springboot 使用IDEA工具自带的mysql插件来完成表的创建和数据的准备: 创建表 表创建成功后,为表准备数据,如下: 2.创建SpringBoot项目 使用脚手架创建…...

(nice!!!)(LeetCode每日一题)2434. 使用机器人打印字典序最小的字符串(贪心+栈)
题目:2434. 使用机器人打印字典序最小的字符串 思路:贪心栈,时间复杂度0(n)。 字符串t其实就是栈,后进先出。要让p的字典序最小,那当然是t每次弹出的字符,都小于或等于“剩下未入t里的字符串的字符”&#…...
无字母数字webshell的命令执行
在Web安全领域,WebShell是一种常见的攻击手段,通过它攻击者可以远程执行服务器上的命令,获取敏感信息或控制系统。而无字母数字WebShell则是其中一种特殊形式,通过避免使用字母和数字字符,来绕过某些安全机制的检测。 …...

vscode使用系列之快速生成html模板
一.欢迎来到我的酒馆 vscode,yyds! 目录 一.欢迎来到我的酒馆二.vscode下载安装1.关于vscode你需要知道2.开始下载安装 三.vscode快速创建html模板 二.vscode下载安装 1.关于vscode你需要知道 Q:为什么使用vscode? A:使用vscode写…...
亚矩阵云手机实测体验:稳定流畅背后的技术逻辑
最近在测试一款云手机服务时,发现亚矩阵的表现出乎意料地稳定。作为一个经常需要多设备协作的开发者,我对云手机的性能、延迟和稳定性要求比较高。经过一段时间的体验,分享一下真实感受,避免大家踩坑。 1. 云手机能解决什么问…...
本机无法远程别的计算机的方法
在本地计算机上修改组策略 按下 Win R 组合键打开运行窗口,输入 gpedit.msc 并回车,打开组策略编辑器。依次展开路径:计算机配置 > 管理模板 > 系统 > 凭据分配。在右侧找到并双击 加密 Oracle 修正 策略。选择 已启用,…...