当前位置: 首页 > 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文库

http://www.lryc.cn/news/524283.html

相关文章:

  • ITIL认证工具商-ManageEngine Servicedesk Plus
  • https 的 CA证书和电子签名
  • 频繁刷新网页会对服务器造成哪些影响?
  • 贪心算法(题1)区间选点
  • JavaWeb开发学习笔记--MySQL
  • 抖音小程序一键获取手机号
  • iconfont等图标托管网站上传svg显示未轮廓化解决办法
  • 2008-2020年各省城镇登记失业率数据
  • Linux——信号量和(环形队列消费者模型)
  • 【JOIN】关键字在MySql中的详细使用
  • 渗透测试--攻击常见的Web应用
  • window系统annaconda中同时安装paddle和pytorch环境
  • python-leetcode-简化路径
  • 浅谈 PID 控制算法
  • ailx10的专栏电子书(2022版)
  • WPS按双字段拆分工作表到独立工作簿-Excel易用宝
  • C++ Qt练习项目 日期时间数据 未完待续
  • vim文本编辑器
  • 产品经理面试题总结2025【其一】
  • 资料03:【TODOS案例】微信小程序开发bilibili
  • 玉米植物结构受乙烯生物合成基因 ZmACS7 的调控
  • C#语言的函数实现
  • 1.6 从 GPT-1 到 GPT-3.5:一路的风云变幻
  • TypeScript - 利用GPT辅助学习
  • VMware虚拟机迁移到阿里云
  • 【STM32-学习笔记-15-】MAX7219点阵屏模块
  • 高并发内存池_CentralCache(中心缓存)和PageCache(页缓存)申请内存的设计
  • elementUI Table组件实现表头吸顶效果
  • 语言模型的价值定位与技术突破:从信息处理到创新认知
  • 微信小程序使用上拉加载onReachBottom。页面拖不动。一直无法触发上拉的事件。