本文已参与[新人创作礼]活动,一起开启掘金创作之路
持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第5天,点击查看活动详情
-
小直播
小直播 App 是一套开源的完整的在线直播解决方案,它基于云直播服务、即时通信(IM)和对象存储服务(COS)构建,并使用云服务器(CVM)提供简单的后台服务,可以实现登录、注册、开播、房间列表、连麦互动、文字互动和弹幕消息等功能。
我这边来个最简单的教程:
第一步 下载SDK
Checkout 项目:gitee.com/cloudtencen… 下载下来以后然选择:Android/XiaoZhiBo导入Android Studio即可。
第二步 创建测试license
进入 console.cloud.tencent.com/live/licens…,使用微信或者QQ扫码登录后创建一个测试license即可
第三步 打开小直播项目中TCGlobalConfig.java
填写LICENCE_URL,LICENCE_KEY,APP_SVR_URL 三个值即可。
LICENCE_URL和LICENCE_KEY在第二步创建后会自动生成,填写即可 APP_SVR_URL 统一填写:xzb.qcloud.com
第四步 运行项目即可开播
当然仅限快速测试使用,上线需要正式的license和APP_SVR_URL
上线的时候也只需要更新ALICENCE_URL,LICENCE_KEY,APP_SVR_URL即可
-
腾讯云工具包 App
如果您是开发人员,可以直接下载我们的腾讯云工具包 App,工具包 中的每一个菜单项均对应 SDK 中的一项独立功能,您可以根据当前项目需要进行针对性的调试。
第一步 下载SDK
Checkout 项目:github.com/tencentyun/… 下载下来以后然选择:Demo导入即可。
第二步 创建测试license
进入 console.cloud.tencent.com/live/licens…,使用微信或者QQ扫码登录后创建一个测试license即可
第三步 修改配置
打开项目中DemoApplication.java填写licenceUrl和licenseKey 打开项目中GenerateTestUserSig.java填写BIZID,APPID,SDKAPPID,SECRETKEY
第四步 运行项目即可开播
setVideoQuality方法报错替换如下即可:
V2TXLiveDef.V2TXLiveVideoEncoderParam param = new V2TXLiveDef.V2TXLiveVideoEncoderParam(Resolution);
param.videoResolutionMode = ResolutionMode;
mLivePusher.setVideoQuality(param);
demo都跑通了,下面手写一个精简的推流和拉流功能吧。
-
精简推流拉流
主播推流,观众拉流 经过参考demo发现推流有摄像头推流和录屏推流,拉流就一种 推流只有一个推流地址,拉流有多种拉流地址(RTMP,FLV,HLS,低延时)
第一步 初始化
package com.z.zplay;
import android.app.Application;
import com.tencent.rtmp.TXLiveBase;
public class IApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
String licenceURL = "https://xxxxxxxxxxxxxxxxxxxxx.license";
String licenceKey = "xxxxxxxxxxxxxxxxxxxxxx";
TXLiveBase.getInstance().setLicence(this, licenceURL, licenceKey);
}
}
//other
implementation 'com.squareup.okhttp3:okhttp:3.6.0'
implementation 'com.squareup.retrofit2:retrofit:2.8.2'
implementation 'com.king.zxing:zxing-lite:1.1.2'
implementation 'org.greenrobot:eventbus:3.1.1'
//腾讯直播
implementation 'com.tencent.liteav:LiteAVSDK_Professional:latest.release'
implementation 'com.tencent.imsdk:imsdk:4.9.1'
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-feature android:name="android.hardware.Camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
第二步 获取拉流推流地址
不管你是要推流还是拉流都要先获取地址
//腾讯提供的测试地址,正式上线需要换成自己的
public static final String URL_FETCH_PUSH_URL = "https://lvb.qcloud.com/weapp/utils/get_test_pushurl";
private fun fetchPusherURL() {
val okHttpClient = OkHttpClient().newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.build()
val request = Request.Builder()
.url(Constants.URL_FETCH_PUSH_URL)
.addHeader("Content-Type", "application/json; charset=utf-8")
.build()
okHttpClient.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
@Throws(IOException::class)
override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
try {
val jsonRsp = JSONObject(response.body()!!.string())
val pusherURLDefault = jsonRsp.optString(Constants.URL_PUSH)
val rtmpPlayURL = jsonRsp.optString(Constants.URL_PLAY_RTMP)
val flvPlayURL = jsonRsp.optString(Constants.URL_PLAY_FLV)
val hlsPlayURL = jsonRsp.optString(Constants.URL_PLAY_HLS)
val realtimePlayURL = jsonRsp.optString(Constants.URL_PLAY_ACC)
startLivePusher(
pusherURLDefault,
rtmpPlayURL,
flvPlayURL,
hlsPlayURL,
realtimePlayURL
)
} catch (e: JSONException) {
e.printStackTrace()
}
}
}
})
}
private fun startLivePusher(
pushURL: String,
rtmpPlayURL: String,
flvPlayURL: String,
hlsPlayURL: String,
realtimePlayURL: String
) {
var intent = Intent()
if (clickType == 1) {
intent.setClass(this, PushActivity::class.java)
} else if (clickType == 2) {
intent.setClass(this, ScreenPushActivity::class.java)
}
intent.putExtra(Constants.INTENT_URL_PUSH, pushURL)
intent.putExtra(Constants.INTENT_URL_PLAY_RTMP, rtmpPlayURL)
intent.putExtra(Constants.INTENT_URL_PLAY_FLV, flvPlayURL)
intent.putExtra(Constants.INTENT_URL_PLAY_HLS, hlsPlayURL)
intent.putExtra(Constants.INTENT_URL_PLAY_ACC, realtimePlayURL)
startActivity(intent)
}
其中pushURL为推流地址,另外4个为拉流地址。
第三步 推流
摄像头推流
package com.z.zplay;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import com.king.zxing.util.CodeUtils;
import com.tencent.live2.V2TXLiveDef;
import com.tencent.live2.V2TXLivePusher;
import com.tencent.live2.impl.V2TXLivePusherImpl;
import com.tencent.rtmp.TXLiveConstants;
import com.tencent.rtmp.TXLog;
import com.tencent.rtmp.ui.TXCloudVideoView;
import static com.tencent.live2.V2TXLiveCode.V2TXLIVE_ERROR_INVALID_LICENSE;
import static com.tencent.live2.V2TXLiveDef.V2TXLiveVideoResolutionMode.V2TXLiveVideoResolutionModeLandscape;
import static com.tencent.live2.V2TXLiveDef.V2TXLiveVideoResolutionMode.V2TXLiveVideoResolutionModePortrait;
//先推流,后创建IM
public class PushActivity extends AppCompatActivity {
private V2TXLivePusher mLivePusher;
private TXCloudVideoView mPusherView;
private static final String TAG = "PushActivity";
private String mPusherURL = ""; // 推流地址
private String mRTMPPlayURL = ""; // RTMP 拉流地址
private String mFlvPlayURL = ""; // flv 拉流地址
private String mHlsPlayURL = ""; // hls 拉流地址
private String mRealtimePlayURL = ""; // 低延时拉流地址
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_push);
initData();
initPusher();
startPush();
}
private void initData() {
Intent intent = getIntent();
mPusherURL = intent.getStringExtra(Constants.INTENT_URL_PUSH);
mRTMPPlayURL = intent.getStringExtra(Constants.INTENT_URL_PLAY_RTMP);
mFlvPlayURL = intent.getStringExtra(Constants.INTENT_URL_PLAY_FLV);
mHlsPlayURL = intent.getStringExtra(Constants.INTENT_URL_PLAY_HLS);
mRealtimePlayURL = intent.getStringExtra(Constants.INTENT_URL_PLAY_ACC);
Bitmap qrCode = CodeUtils.createQRCode(mRTMPPlayURL, 200);
ImageView ivCode = findViewById(R.id.iv_code);
ivCode.setImageBitmap(qrCode);
}
private void initPusher() {
mPusherView = findViewById(R.id.pusher_tx_cloud_view);
mLivePusher = new V2TXLivePusherImpl(this, V2TXLiveDef.V2TXLiveMode.TXLiveMode_RTMP);
}
private void startPush() {
mLivePusher.setRenderView(mPusherView);
mLivePusher.startCamera(true);
mLivePusher.startPush(mPusherURL);
}
@Override
protected void onDestroy() {
super.onDestroy();
mLivePusher.stopCamera();
mLivePusher.stopPush();
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".PushActivity">
<com.tencent.rtmp.ui.TXCloudVideoView
android:id="@+id/pusher_tx_cloud_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/iv_code"
android:layout_width="100dp"
android:layout_height="100dp" />
</FrameLayout>
录屏推流
package com.z.zplay;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Toast;
import com.king.zxing.util.CodeUtils;
import com.tencent.live2.V2TXLiveDef;
import com.tencent.live2.V2TXLivePusher;
import com.tencent.live2.impl.V2TXLivePusherImpl;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import static com.tencent.live2.V2TXLiveCode.V2TXLIVE_OK;
public class ScreenPushActivity extends AppCompatActivity {
private Button btn_start_push;
private static V2TXLivePusher sLivePusher;
private String mPusherURL = ""; // 推流地址
private String mRTMPPlayURL = ""; // RTMP 拉流地址
private String mFlvPlayURL = ""; // flv 拉流地址
private String mHlsPlayURL = ""; // hls 拉流地址
private String mRealtimePlayURL = ""; // 低延时拉流地址
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_screen_push);
btn_start_push = findViewById(R.id.btn_start_push);
initData();
btn_start_push.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startPush(mPusherURL);
}
});
}
private void initData() {
Intent intent = getIntent();
mPusherURL = intent.getStringExtra(Constants.INTENT_URL_PUSH);
mRTMPPlayURL = intent.getStringExtra(Constants.INTENT_URL_PLAY_RTMP);
mFlvPlayURL = intent.getStringExtra(Constants.INTENT_URL_PLAY_FLV);
mHlsPlayURL = intent.getStringExtra(Constants.INTENT_URL_PLAY_HLS);
mRealtimePlayURL = intent.getStringExtra(Constants.INTENT_URL_PLAY_ACC);
Bitmap qrCode = CodeUtils.createQRCode(mRTMPPlayURL, 200);
ImageView ivCode = findViewById(R.id.iv_code);
ivCode.setImageBitmap(qrCode);
}
private void startPush(String pushURL) {
sLivePusher = new V2TXLivePusherImpl(this, V2TXLiveDef.V2TXLiveMode.TXLiveMode_RTMP);
sLivePusher.startMicrophone();
sLivePusher.startScreenCapture();
int result = sLivePusher.startPush(pushURL);
if (result == V2TXLIVE_OK) {
Toast.makeText(this, "push成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "push失败", Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
sLivePusher.stopScreenCapture();
sLivePusher.setObserver(null);
sLivePusher.stopPush();
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
tools:context=".ScreenPushActivity">
<ImageView
android:id="@+id/iv_code"
android:layout_width="100dp"
android:layout_height="100dp" />
<Button
android:id="@+id/btn_start_push"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开始录屏推流" />
</LinearLayout>
录屏推流需要多增加一个activity配置
<activity
android:name="com.tencent.rtmp.video.TXScreenCapture$TXScreenCaptureAssistantActivity"
android:theme="@android:style/Theme.Translucent" />
第四步 拉流
package com.z.zplay;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import com.tencent.live2.V2TXLiveDef;
import com.tencent.live2.V2TXLivePlayer;
import com.tencent.live2.impl.V2TXLivePlayerImpl;
import com.tencent.rtmp.ui.TXCloudVideoView;
public class GetPushActivity extends AppCompatActivity {
//这个URL就是推流生成的二维码的数据
private String mPlayURL = "";
private V2TXLivePlayer mLivePlayer; //直播拉流的视频播放器
private TXCloudVideoView mVideoView;
private V2TXLiveDef.V2TXLiveFillMode mRenderMode = V2TXLiveDef.V2TXLiveFillMode.V2TXLiveFillModeFit; //Player 当前渲染模式
private V2TXLiveDef.V2TXLiveRotation mRenderRotation = V2TXLiveDef.V2TXLiveRotation.V2TXLiveRotation0; //Player 当前渲染角度
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_get_push);
mPlayURL = getIntent().getStringExtra(Constants.INTENT_URL);
mVideoView = (TXCloudVideoView) findViewById(R.id.liveplayer_video_view);
mLivePlayer = new V2TXLivePlayerImpl(this);
startPlay();
}
private void startPlay() {
String playURL = mPlayURL;
mLivePlayer.setRenderView(mVideoView);
mLivePlayer.setRenderRotation(mRenderRotation);
mLivePlayer.setRenderFillMode(mRenderMode);
mLivePlayer.startPlay(playURL);
}
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".GetPushActivity">
<com.tencent.rtmp.ui.TXCloudVideoView
android:id="@+id/liveplayer_video_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
第五步 运行即可,可配合腾讯云工具包 App一块测试
以上都是最精简的可跑通的核心代码,其他配置的比如声道,美颜等按需增加。 推流和拉流不包含在直播间聊天的功能,如果需要聊天,那么需要增加腾讯云IM服务SDK
-
即时通信IM
第一步 下载SDK
Checkout 项目:github.com/tencentyun/… 下载下来以后然选择:Demo导入Android Studio即可。
第二步 创建测试应用
登录 即时通信 IM 控制台创建一个应用
第三步 配置
GenerateTestUserSig.java填写SDKAPPID,SECRETKEY即可