当前位置: 首页 > news >正文

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直播练习实践

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

ITIL认证工具商-ManageEngine Servicedesk Plus

ServiceDesk Plus是Zoho Corporation旗下企业IT管理部门ManageEngine提供的统一服务管理解决方案。凭借其无限的可扩展性、情境化的IT和业务集成以及一键式工作流程自动化功能&#xff0c;IT领导者可以使用ServiceDesk Plus有效执行和控制跨不同业务部门和IT功能的复杂工作流程…...

https 的 CA证书和电子签名

https 的攻击者可能使用伪造的一对公私钥与客户端交互, 那么如何确保确实是该服务器的公钥呢? 这就诞生了CA颁发机构 CA颁发机构 服务器和客户端都信任指定的CA颁发机构 服务器上传服务器公钥, CA颁发机构做了什么 服务器公钥哈希, 记为 Hash使用 CA 私钥为 Hash 进行 CA 签…...

频繁刷新网页会对服务器造成哪些影响?

当用户在进行浏览网页的过程中频繁刷新页面时&#xff0c;浏览器会向服务器发送请求&#xff0c;服务器会对该请求进行处理并返回到相应的页面内容中&#xff0c;所以频繁刷新网页会对服务器造成影响&#xff0c;有可能会出现以下问题&#xff1a; 用户每次刷新网页都会向服务器…...

贪心算法(题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 基本语法&#xff1a; select 字段列表 from 表名列表 where 条件列表 group by 分组字段列表 having 分组后条件列表 order by 排序字段列表 limit 分页参数 基本查询 关键字&#xff1a;SELECT 查询多个字段&#xff1a;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 设计工具 导入图标后拖入画板里面&#xff0c;右键选择轮廓化 将图标导出...

2008-2020年各省城镇登记失业率数据

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

Linux——信号量和(环形队列消费者模型)

Linux——线程条件变量&#xff08;同步&#xff09;-CSDN博客 文章目录 目录 文章目录 前言 一、信号量是什么&#xff1f; 二、信号量 1、主要类型 2、操作 3、应用场景 三、信号量函数 1、sem_init 函数 2、sem_wait 函数 3、sem_post 函数 4、sem_destroy 函数 ​​​​​​…...

【JOIN】关键字在MySql中的详细使用

目录 INNER JOIN&#xff08;内连接&#xff09; LEFT JOIN&#xff08;左连接&#xff09; RIGHT JOIN&#xff08;右连接&#xff09; FULL JOIN&#xff08;全连接&#xff09; 示例图形化解释JOIN的不同类型 INNER JOIN&#xff1a; LEFT JOIN&#xff1a; RIGHT J…...

渗透测试--攻击常见的Web应用

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

window系统annaconda中同时安装paddle和pytorch环境

一、下载nvidia驱动 Download The Official NVIDIA Drivers | NVIDIA 查看GPU信息 nvidia-smi 二、安装cuda CUDA Toolkit 11.8 Downloads | NVIDIA Developer 按以下步骤下载cuda安装包&#xff0c;我使用的cuda11.8 下载后双击一路下一步安装即可。 查看cuda版本 nvcc …...

python-leetcode-简化路径

71. 简化路径 - 力扣&#xff08;LeetCode&#xff09; class Solution:def simplifyPath(self, path: str) -> str:# 使用栈来处理路径stack []# 分割路径&#xff0c;以 / 为分隔符parts path.split(/)for part in parts:if part or part .:# 空字符串或 .&#xff0…...

浅谈 PID 控制算法

PID 控制算法概念 在我们的生活中可能大家都没有听说过 PID 控制算法&#xff0c;但它可以说是无处不在&#xff0c;小到空调的温度控制、无人机的精准悬停、机器人运作系统&#xff0c;大到飞机和火箭的飞行姿态控制都有 PID 的身影。 PID 控制算法&#xff0c;即比例 - 积分…...

ailx10的专栏电子书(2022版)

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

WPS按双字段拆分工作表到独立工作簿-Excel易用宝

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

C++ Qt练习项目 日期时间数据 未完待续

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

vim文本编辑器

vim命令的使用&#xff1a; [rootxxx ~]# touch aa.txt #首先创建一个文件 [rootxxx ~]# vim aa.txt #vim进入文件aa.txt进行编辑 vim是vi的升级版&#xff0c;具有以下三种基本模式&#xff1a; 输入模式(编辑模式) 点击i进入编辑模式 &#xff08;说明…...

产品经理面试题总结2025【其一】

一、产品理解与定位 1、你如何理解产品经理这个角色&#xff1f; 作为一名互联网产品经理&#xff0c;我理解这个角色的核心在于成为产品愿景的制定者和执行的推动者。具体来说&#xff0c;产品经理是连接市场、用户和技术团队之间的桥梁&#xff0c;负责理解市场需求、用户痛…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(二)

HoST框架核心实现方法详解 - 论文深度解读(第二部分) 《Learning Humanoid Standing-up Control across Diverse Postures》 系列文章: 论文深度解读 + 算法与代码分析(二) 作者机构: 上海AI Lab, 上海交通大学, 香港大学, 浙江大学, 香港中文大学 论文主题: 人形机器人…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

PL0语法,分析器实现!

简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

Android15默认授权浮窗权限

我们经常有那种需求&#xff0c;客户需要定制的apk集成在ROM中&#xff0c;并且默认授予其【显示在其他应用的上层】权限&#xff0c;也就是我们常说的浮窗权限&#xff0c;那么我们就可以通过以下方法在wms、ams等系统服务的systemReady()方法中调用即可实现预置应用默认授权浮…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.

ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #&#xff1a…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...