Commit e5bda1db248319cdc3307b308ba0624ca6aceb92

Authored by 王琳
1 parent fd2149c2

更新推流SDK,添加视讯云推流逻辑demo

app/libs/libcnlivestream.jar
No preview for this file type
app/src/main/AndroidManifest.xml
@@ -36,6 +36,11 @@ @@ -36,6 +36,11 @@
36 android:configChanges="orientation|keyboardHidden|screenSize" 36 android:configChanges="orientation|keyboardHidden|screenSize"
37 android:launchMode="singleTop" 37 android:launchMode="singleTop"
38 android:screenOrientation="portrait" /> 38 android:screenOrientation="portrait" />
  39 + <activity
  40 + android:name=".CNStreamActivity"
  41 + android:configChanges="orientation|keyboardHidden|screenSize"
  42 + android:launchMode="singleTop"
  43 + android:screenOrientation="portrait"></activity>
39 </application> 44 </application>
40 45
41 </manifest> 46 </manifest>
42 \ No newline at end of file 47 \ No newline at end of file
app/src/main/java/com/cnlive/demo/stream/CNStreamActivity.java 0 → 100644
  1 +package com.cnlive.demo.stream;
  2 +
  3 +import android.Manifest;
  4 +import android.content.Context;
  5 +import android.content.Intent;
  6 +import android.content.pm.ActivityInfo;
  7 +import android.content.pm.PackageManager;
  8 +import android.opengl.GLSurfaceView;
  9 +import android.os.Build;
  10 +import android.support.v4.app.ActivityCompat;
  11 +import android.support.v7.app.AppCompatActivity;
  12 +import android.os.Bundle;
  13 +import android.util.Log;
  14 +import android.view.KeyEvent;
  15 +import android.view.View;
  16 +import android.widget.Button;
  17 +import android.widget.Toast;
  18 +
  19 +import com.cnlive.libs.stream.Streamer;
  20 +import com.cnlive.libs.stream.base.IStreamer;
  21 +import com.cnlive.libs.stream.model.ActivityConfig;
  22 +
  23 +import static com.cnlive.libs.stream.base.IStreamer.ENCODE_METHOD_HARDWARE;
  24 +import static com.cnlive.libs.stream.base.IStreamer.VIDEO_RESOLUTION_360P;
  25 +
  26 +/**
  27 + * CNStreamActivity 本Activity主要展示的是视讯云推流SDK的使用方法,对于一些其他的设置(如:摄像头转换、美颜等)请参考StreamActivity
  28 + */
  29 +public class CNStreamActivity extends AppCompatActivity implements View.OnClickListener {
  30 + private static final String TAG = "CNStreamActivity";
  31 + private GLSurfaceView surfaceView;
  32 + private Button button;
  33 +
  34 + private Streamer mStreamer;
  35 +
  36 + private String activityId;
  37 + private String channelId;
  38 + private final static int PERMISSION_REQUEST_CAMERA_AUDIOREC = 1;
  39 + private boolean mRecording = false;
  40 +
  41 + public static final String ACTIVITYID = "activityId";
  42 + public static final String CHANNELID = "channelId";
  43 +
  44 + public static void startActivity(Context context,
  45 + String activityID,
  46 + String channelID) {
  47 + Intent intent = new Intent(context, CNStreamActivity.class);
  48 + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  49 + intent.putExtra(ACTIVITYID, activityID);
  50 + intent.putExtra(CHANNELID, channelID);
  51 + context.startActivity(intent);
  52 + }
  53 +
  54 + @Override
  55 + protected void onCreate(Bundle savedInstanceState) {
  56 + super.onCreate(savedInstanceState);
  57 + setContentView(R.layout.activity_cnstream);
  58 + initView();
  59 + initData();
  60 + initStream();
  61 + }
  62 +
  63 +
  64 + /**
  65 + * 初始化view
  66 + */
  67 + private void initView() {
  68 + surfaceView = (GLSurfaceView) findViewById(R.id.cnSurfaceView);
  69 + button = (Button) findViewById(R.id.startStream);
  70 + }
  71 +
  72 + /**
  73 + * 得到到推流所需的参数
  74 + */
  75 + private void initData() {
  76 + Bundle bundle = getIntent().getExtras();
  77 + activityId = bundle.getString(ACTIVITYID);
  78 + channelId = bundle.getString(CHANNELID);
  79 + }
  80 +
  81 + /**
  82 + * 初始化推流SDK,设置一些推流所需的基本参数
  83 + */
  84 + private void initStream() {
  85 + //初始化推流SDK
  86 + mStreamer = new Streamer(this);
  87 + //设置预览编码帧率
  88 + mStreamer.setPreviewFps(15);
  89 + //设置推流编码帧率
  90 + mStreamer.setTargetFps(15);
  91 + //设置初始、最大、最小视频码率
  92 + mStreamer.setVideoKBitrate(800 * 3 / 4, 800, 800 / 4);
  93 + //设置音频编码码率
  94 + mStreamer.setAudioBitrate(48);
  95 + //分别设置预览和推流分辨率
  96 + mStreamer.setPreviewResolution(VIDEO_RESOLUTION_360P);
  97 + mStreamer.setTargetResolution(VIDEO_RESOLUTION_360P);
  98 + //设置编码模式(硬编/软编/软编兼容)
  99 + mStreamer.setEncodeMethod(ENCODE_METHOD_HARDWARE);
  100 +
  101 + //设置预览View
  102 + mStreamer.setDisplayPreview(surfaceView);
  103 + //设置Info回调,可以收到相关通知信息
  104 + mStreamer.setOnInfoListener(mOnInfoListener);
  105 + //设置错误回调,收到该回调后,一般是发生了严重错误,比如网络断开等
  106 + mStreamer.setOnErrorListener(mOnErrorListener);
  107 + }
  108 +
  109 + @Override
  110 + public void onClick(View v) {
  111 + switch (v.getId()) {
  112 + case R.id.startStream:
  113 + //开始或结束推流
  114 + if (mRecording) {
  115 + stopStream();
  116 + } else {
  117 + startStream();
  118 + }
  119 + break;
  120 + }
  121 + }
  122 +
  123 + /**
  124 + * 开始推流
  125 + */
  126 + private void startStream() {
  127 + mStreamer.startStream(activityConfig);
  128 + button.setText("结束推流");
  129 + mRecording = true;
  130 + }
  131 +
  132 + /**
  133 + * 结束推流
  134 + */
  135 + private void stopStream() {
  136 + mStreamer.stopStream();
  137 + button.setText("开始推流");
  138 + mRecording = false;
  139 + }
  140 +
  141 + /**
  142 + * 点击退出当前页面
  143 + */
  144 + @Override
  145 + public boolean onKeyDown(int keyCode, KeyEvent event) {
  146 + switch (keyCode) {
  147 + case KeyEvent.KEYCODE_BACK:
  148 + mRecording = false;
  149 + mStreamer.stopStream();
  150 + CNStreamActivity.this.finish();
  151 + break;
  152 + default:
  153 + break;
  154 + }
  155 + return true;
  156 + }
  157 +
  158 +
  159 + /**
  160 + * 采集的状态依赖于 Activity 的生命周期,所以必须在 Activity 的生命周期中也调用 SDK 相应的接口
  161 + */
  162 + @Override
  163 + public void onResume() {
  164 + super.onResume();
  165 + startCameraPreviewWithPermCheck();
  166 + mStreamer.resume();
  167 + }
  168 +
  169 + public void onPause() {
  170 + super.onPause();
  171 + mStreamer.pause();
  172 + //停止照相机预览
  173 + mStreamer.stopCameraPreview();
  174 + }
  175 +
  176 + @Override
  177 + public void onDestroy() {
  178 + super.onDestroy();
  179 + mStreamer.release();
  180 + }
  181 +
  182 + /**
  183 + * 摄像头权限检测
  184 + */
  185 + private void startCameraPreviewWithPermCheck() {
  186 + int cameraPerm = ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
  187 + int audioPerm = ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO);
  188 + if (cameraPerm != PackageManager.PERMISSION_GRANTED ||
  189 + audioPerm != PackageManager.PERMISSION_GRANTED) {
  190 + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
  191 + Log.e(TAG, "No CAMERA or AudioRecord permission, please check");
  192 + Toast.makeText(this, "No CAMERA or AudioRecord permission, please check",
  193 + Toast.LENGTH_LONG).show();
  194 + } else {
  195 + String[] permissions = {Manifest.permission.CAMERA,
  196 + Manifest.permission.RECORD_AUDIO,
  197 + Manifest.permission.READ_EXTERNAL_STORAGE};
  198 + ActivityCompat.requestPermissions(this, permissions,
  199 + PERMISSION_REQUEST_CAMERA_AUDIOREC);
  200 + }
  201 + } else {
  202 + mStreamer.startCameraPreview();
  203 + }
  204 + }
  205 +
  206 + /**
  207 + * 推流状态回调
  208 + */
  209 + private IStreamer.OnInfoListener mOnInfoListener = new IStreamer.OnInfoListener() {
  210 + @Override
  211 + public void onInfo(int what, int msg1, int msg2) {
  212 + Toast.makeText(CNStreamActivity.this, TAG + " " + what, Toast.LENGTH_SHORT).show();
  213 + switch (what) {
  214 + case IStreamer.cnstream_camera_init_done:
  215 + //初始化完毕
  216 + Log.d(TAG, "cnstream_camera_init_done");
  217 + break;
  218 + case IStreamer.cnstream_open_stream_succ:
  219 + //推流成功
  220 + Log.d(TAG, "cnstream_open_stream_success");
  221 + break;
  222 + case IStreamer.cnstream_frame_send_slow:
  223 + //网络状态不佳,数据发送可能有延迟 发送当前帧的时长
  224 + Log.d(TAG, "cnstream_frame_send_slow");
  225 + Toast.makeText(CNStreamActivity.this, "Network not good!", Toast.LENGTH_SHORT).show();
  226 + break;
  227 + case IStreamer.cnstream_est_bw_raise:
  228 + //码率上调了 码率上调的目标值 单位bps
  229 + Log.d(TAG, "BW raise to" + msg1 / 1000 + "kbps");
  230 + break;
  231 + case IStreamer.cnstream_est_bw_drop:
  232 + //码率下调了 码率下调到的值 单位bps
  233 + Log.d(TAG, "BW drop to" + msg1 / 1000 + "kbps");
  234 + break;
  235 + default:
  236 + Log.d(TAG, "OnInfo: " + what + " msg1 " + msg1 + " msg2 " + msg2);
  237 + break;
  238 + }
  239 + }
  240 + };
  241 +
  242 + /**
  243 + * 推流错误回调
  244 + */
  245 + private IStreamer.OnErrorListener mOnErrorListener = new IStreamer.OnErrorListener() {
  246 + @Override
  247 + public void onError(int what, int msg1, int msg2) {
  248 +
  249 + /**
  250 + * 判断是否有异常,如果有SDK内部会停止推流;开发人员根据自己的需求可以做一些自己的操作;
  251 + * 本demo中改变了按钮的状态,让其从推流状态改为停止推流状态,这样点击按钮则可以继续进行推流
  252 + */
  253 + if (what < 0) {
  254 + mRecording = false;
  255 + button.setText("开始直播");
  256 + button.postInvalidate();
  257 + }
  258 +
  259 + Toast.makeText(CNStreamActivity.this, TAG + " " + what, Toast.LENGTH_SHORT).show();
  260 + switch (what) {
  261 + case IStreamer.cnstream_error_start_stream_server:
  262 + //开始直播时请求服务器失败
  263 + break;
  264 + case IStreamer.cnstream_error_start_stream_network:
  265 + //开始直播时网络连接失败
  266 + break;
  267 + case IStreamer.cnstream_error_start_stream_other:
  268 + //开始直播活动失败
  269 + break;
  270 + case IStreamer.cnstream_error_create_stream_server:
  271 + //创建直播活动请求服务器失败
  272 + break;
  273 + case IStreamer.cnstream_error_create_stream_network:
  274 + //创建直播活动时网络连接失败
  275 + break;
  276 + case IStreamer.cnstream_error_create_stream_other:
  277 + //创建直播活动时失败
  278 + break;
  279 + case IStreamer.cnstream_record_error:
  280 + //直播录制失败
  281 + break;
  282 + case IStreamer.cnstream_error_stop_stream:
  283 + //停止直播活动失败
  284 + break;
  285 + case IStreamer.cnstream_socket_disconnect:
  286 + //推流Socket异常断开
  287 + break;
  288 + case IStreamer.cnstream_init_error_instop:
  289 + //初始化失败 正在停止推流
  290 + break;
  291 + case IStreamer.cnstream_error_dns_parse_failed:
  292 + //url域名解析失败
  293 + Log.d(TAG, "cnstream_error_dns_parse_failed");
  294 + break;
  295 + case IStreamer.cnstream_error_connect_failed:
  296 + //网络连接失败,无法建立连接
  297 + Log.d(TAG, "cnstream_error_connect_failed");
  298 + break;
  299 + case IStreamer.cnstream_error_publish_failed:
  300 + //跟RTMP服务器完成握手后,向{streamname}推流失败)
  301 + Log.d(TAG, "cnstream_error_publish_failed");
  302 + break;
  303 + case IStreamer.cnstream_error_connect_breaked:
  304 + //网络连接断开
  305 + Log.d(TAG, "cnstream_error_connect_breaked");
  306 + break;
  307 + case IStreamer.cnstream_error_av_async:
  308 + //音视频采集pts差值超过5s 音视频采集pts具体差值,单位ms
  309 + Log.d(TAG, "cnstream_error_av_async " + msg1 + " ms");
  310 + break;
  311 + case IStreamer.cnstream_video_encoder_error_unsupported:
  312 + //编码器初始化失败
  313 + Log.d(TAG, "cnstream_video_encoder_error_unsupported");
  314 + break;
  315 + case IStreamer.cnstream_video_encoder_error_unknown:
  316 + //视频编码失败
  317 + Log.d(TAG, "cnstream_video_encoder_error_unknown");
  318 + break;
  319 + case IStreamer.cnstream_audio_encoder_error_unsupported:
  320 + //音频初始化失败
  321 + Log.d(TAG, "cnstream_audio_encoder_error_unsupported");
  322 + break;
  323 + case IStreamer.cnstream_audio_encoder_error_unknown:
  324 + //音频编码失败
  325 + Log.d(TAG, "cnstream_audio_encoder_error_unknown");
  326 + break;
  327 + case IStreamer.cnstream_audio_recorder_error_start_failed:
  328 + //录音开启失败
  329 + Log.d(TAG, "cnstream_audio_recorder_error_start_failed");
  330 + break;
  331 + case IStreamer.cnstream_audio_recorder_error_unknown:
  332 + //录音开启未知错误
  333 + Log.d(TAG, "cnstream_audio_recorder_error_unknown");
  334 + break;
  335 + case IStreamer.cnstream_camera_error_unknown:
  336 + //摄像头未知错误
  337 + Log.d(TAG, "cnstream_camera_error_unknown");
  338 + break;
  339 + case IStreamer.cnstream_camera_error_start_failed:
  340 + //打开摄像头失败
  341 + Log.d(TAG, "cnstream_camera_error_start_failed");
  342 + break;
  343 + case IStreamer.cnstream_camera_error_server_died:
  344 + //系统Camera服务进程退出
  345 + Log.d(TAG, "cnstream_camera_error_server_died");
  346 + break;
  347 + default:
  348 + Log.d(TAG, "what= " + what + " msg1= " + msg1 + " msg2= " + msg2);
  349 + break;
  350 + }
  351 + }
  352 + };
  353 +
  354 + /**
  355 + * 设置推流参数(注意:必传参数为activityId、channelId)
  356 + */
  357 + ActivityConfig activityConfig = new ActivityConfig() {
  358 + @Override
  359 + public String getActivityID() {
  360 + //活动ID,确保SP下的唯一性(即appId唯一) ,不能含下划线,不支持转义字符和urlencode会处理的特殊字符,如下! # $ % & ‘ ( )* + , . / : ; = ? @ [ / ]
  361 + return activityId;
  362 + }
  363 +
  364 + @Override
  365 + public String getActivityName() {
  366 + //活动名称
  367 + return "";
  368 + }
  369 +
  370 + @Override
  371 + public int getActivityStatus() {
  372 + //活动状态,0:未开始 /1:已开始 /2:已结束 /3 已下线,默认为0
  373 + return 0;
  374 + }
  375 +
  376 + @Override
  377 + public int getIsHorizontalScreen() {
  378 + //屏幕方向,0:垂直即竖屏 /1:水平即横屏 ,默认为1
  379 + return 0;
  380 + }
  381 +
  382 + @Override
  383 + public String getChannelID() {
  384 + //播放通道;PGC业务:平台分配/ UGC业务:主播ID生成,确保SP下的唯一性,不能含下划线,不支持转义字符和urlencode会处理的特殊字符,如下! # $ % & ‘ ( )* + , . / : ; = ? @ [ / ]
  385 + return channelId;
  386 + }
  387 +
  388 + @Override
  389 + public String getCoverImgUrl() {
  390 + //活动封面
  391 + return "";
  392 + }
  393 +
  394 + @Override
  395 + public String getActivityCategory() {
  396 + //活动分类ID
  397 + return "";
  398 + }
  399 +
  400 + @Override
  401 + public String getExtensions() {
  402 + //扩展字段,参数格式为Map对象的Json字符串(主播昵称、头像、)
  403 + return "";
  404 + }
  405 +
  406 + @Override
  407 + public String getUuid() {
  408 + return "";
  409 + }
  410 +
  411 + @Override
  412 + public String getCallbackAcitvityStatushttpPostUrl() {
  413 + //回调通知活动状态(错误,结束、心跳异常等状态),详情见回调接口说明
  414 + return "";
  415 + }
  416 + };
  417 +}
app/src/main/java/com/cnlive/demo/stream/MainActivity.java
@@ -12,6 +12,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe @@ -12,6 +12,7 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
12 12
13 private static final String TAG = MainActivity.class.getSimpleName(); 13 private static final String TAG = MainActivity.class.getSimpleName();
14 private Button mConnectButton; 14 private Button mConnectButton;
  15 + private Button mCNStreamerBtn;
15 16
16 private RadioButton mLandscapeButton; 17 private RadioButton mLandscapeButton;
17 private RadioButton mPortraitButton; 18 private RadioButton mPortraitButton;
@@ -19,6 +20,10 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe @@ -19,6 +20,10 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
19 private EditText activityId; 20 private EditText activityId;
20 private EditText channelId; 21 private EditText channelId;
21 22
  23 + private String activityID = "";
  24 + private String channelID = "";
  25 + private boolean landscape;
  26 +
22 @Override 27 @Override
23 protected void onCreate(Bundle savedInstanceState) { 28 protected void onCreate(Bundle savedInstanceState) {
24 29
@@ -28,6 +33,9 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe @@ -28,6 +33,9 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
28 mConnectButton = (Button) findViewById(R.id.connectBT); 33 mConnectButton = (Button) findViewById(R.id.connectBT);
29 mConnectButton.setOnClickListener(this); 34 mConnectButton.setOnClickListener(this);
30 35
  36 + mCNStreamerBtn = (Button) findViewById(R.id.cnStreamer);
  37 + mCNStreamerBtn.setOnClickListener(this);
  38 +
31 mLandscapeButton = (RadioButton) findViewById(R.id.orientationbutton1); 39 mLandscapeButton = (RadioButton) findViewById(R.id.orientationbutton1);
32 mPortraitButton = (RadioButton) findViewById(R.id.orientationbutton2); 40 mPortraitButton = (RadioButton) findViewById(R.id.orientationbutton2);
33 41
@@ -35,23 +43,29 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe @@ -35,23 +43,29 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
35 channelId = (EditText) findViewById(R.id.channelId); 43 channelId = (EditText) findViewById(R.id.channelId);
36 } 44 }
37 45
  46 + private void intentData() {
  47 +
  48 + if (!TextUtils.isEmpty(activityId.getText().toString())) {
  49 + activityID = activityId.getText().toString();
  50 + }
  51 +
  52 + if (!TextUtils.isEmpty(channelId.getText().toString())) {
  53 + channelID = channelId.getText().toString();
  54 + }
  55 + landscape = mLandscapeButton.isChecked();
  56 + }
  57 +
38 @Override 58 @Override
39 public void onClick(View view) { 59 public void onClick(View view) {
40 switch (view.getId()) { 60 switch (view.getId()) {
41 case R.id.connectBT: 61 case R.id.connectBT:
42 - String activityID = "";  
43 - String channelID = "";  
44 - boolean landscape;  
45 -  
46 - if (!TextUtils.isEmpty(activityId.getText().toString())) {  
47 - activityID = activityId.getText().toString();  
48 - }  
49 -  
50 - if (!TextUtils.isEmpty(channelId.getText().toString())) {  
51 - channelID = channelId.getText().toString();  
52 - }  
53 - landscape = mLandscapeButton.isChecked();  
54 - StreamActivity.startActivity(getApplicationContext(), 0, activityID, channelID, landscape); 62 + intentData();
  63 + StreamActivity.startActivity(getApplicationContext(), activityID, channelID, landscape);
  64 + break;
  65 +
  66 + case R.id.cnStreamer:
  67 + intentData();
  68 + CNStreamActivity.startActivity(getApplicationContext(), activityID, channelID);
55 break; 69 break;
56 default: 70 default:
57 break; 71 break;
app/src/main/java/com/cnlive/demo/stream/StreamActivity.java
@@ -41,6 +41,7 @@ import static com.cnlive.libs.stream.model.Filters.SKIN_WHITEN; @@ -41,6 +41,7 @@ import static com.cnlive.libs.stream.model.Filters.SKIN_WHITEN;
41 41
42 /** 42 /**
43 * Created by Administrator on 2016/8/29. 43 * Created by Administrator on 2016/8/29.
  44 + * StreamActivity 本 Activity主要展示的是推流SDK的具体用法,包括视讯云的逻辑和推流的设置,如果只想了解视讯云的逻辑,请参考CNStreamActivity
44 */ 45 */
45 public class StreamActivity extends Activity { 46 public class StreamActivity extends Activity {
46 private static final String TAG = "StreamActivity"; 47 private static final String TAG = "StreamActivity";
@@ -85,13 +86,12 @@ public class StreamActivity extends Activity { @@ -85,13 +86,12 @@ public class StreamActivity extends Activity {
85 public static final String CHANNELID = "channelId"; 86 public static final String CHANNELID = "channelId";
86 public final static String LANDSCAPE = "landscape"; 87 public final static String LANDSCAPE = "landscape";
87 88
88 - public static void startActivity(Context context, int fromType, 89 + public static void startActivity(Context context,
89 String activityID, 90 String activityID,
90 String channelID, 91 String channelID,
91 boolean isLandscape) { 92 boolean isLandscape) {
92 Intent intent = new Intent(context, StreamActivity.class); 93 Intent intent = new Intent(context, StreamActivity.class);
93 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 94 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
94 - intent.putExtra("type", fromType);  
95 intent.putExtra(ACTIVITYID, activityID); 95 intent.putExtra(ACTIVITYID, activityID);
96 intent.putExtra(CHANNELID, channelID); 96 intent.putExtra(CHANNELID, channelID);
97 intent.putExtra(LANDSCAPE, isLandscape); 97 intent.putExtra(LANDSCAPE, isLandscape);
@@ -125,33 +125,44 @@ public class StreamActivity extends Activity { @@ -125,33 +125,44 @@ public class StreamActivity extends Activity {
125 mChronometer = (Chronometer) findViewById(R.id.chronometer); 125 mChronometer = (Chronometer) findViewById(R.id.chronometer);
126 126
127 mObserverButton = new ButtonObserver(); 127 mObserverButton = new ButtonObserver();
  128 + //开始/结束推流按钮
128 mShootingText = (TextView) findViewById(R.id.click_to_shoot); 129 mShootingText = (TextView) findViewById(R.id.click_to_shoot);
129 mShootingText.setOnClickListener(mObserverButton); 130 mShootingText.setOnClickListener(mObserverButton);
  131 + //返回按钮
130 mDeleteView = findViewById(R.id.backoff); 132 mDeleteView = findViewById(R.id.backoff);
131 mDeleteView.setOnClickListener(mObserverButton); 133 mDeleteView.setOnClickListener(mObserverButton);
  134 + //转换摄像头
132 mSwitchCameraView = findViewById(R.id.switch_cam); 135 mSwitchCameraView = findViewById(R.id.switch_cam);
133 mSwitchCameraView.setOnClickListener(mObserverButton); 136 mSwitchCameraView.setOnClickListener(mObserverButton);
  137 + //闪光灯
134 mFlashView = findViewById(R.id.flash); 138 mFlashView = findViewById(R.id.flash);
135 mFlashView.setOnClickListener(mObserverButton); 139 mFlashView.setOnClickListener(mObserverButton);
136 -  
137 mCheckBoxObserver = new CheckBoxObserver(); 140 mCheckBoxObserver = new CheckBoxObserver();
  141 + //美艳
138 mBeautyCheckBox = (CheckBox) findViewById(R.id.click_to_switch_beauty); 142 mBeautyCheckBox = (CheckBox) findViewById(R.id.click_to_switch_beauty);
139 mBeautyCheckBox.setOnCheckedChangeListener(mCheckBoxObserver); 143 mBeautyCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
  144 + //背景音乐
140 mBgmCheckBox = (CheckBox) findViewById(R.id.bgm); 145 mBgmCheckBox = (CheckBox) findViewById(R.id.bgm);
141 mBgmCheckBox.setOnCheckedChangeListener(mCheckBoxObserver); 146 mBgmCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
  147 + //耳返
142 mAudioPreviewCheckBox = (CheckBox) findViewById(R.id.ear_mirror); 148 mAudioPreviewCheckBox = (CheckBox) findViewById(R.id.ear_mirror);
143 mAudioPreviewCheckBox.setOnCheckedChangeListener(mCheckBoxObserver); 149 mAudioPreviewCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
  150 + //静音
144 mMuteCheckBox = (CheckBox) findViewById(R.id.mute); 151 mMuteCheckBox = (CheckBox) findViewById(R.id.mute);
145 mMuteCheckBox.setOnCheckedChangeListener(mCheckBoxObserver); 152 mMuteCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
  153 + //水印
146 mWaterMarkCheckBox = (CheckBox) findViewById(R.id.watermark); 154 mWaterMarkCheckBox = (CheckBox) findViewById(R.id.watermark);
147 mWaterMarkCheckBox.setOnCheckedChangeListener(mCheckBoxObserver); 155 mWaterMarkCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
  156 + //前置摄像头镜像
148 mFrontMirrorCheckBox = (CheckBox) findViewById(R.id.front_camera_mirror); 157 mFrontMirrorCheckBox = (CheckBox) findViewById(R.id.front_camera_mirror);
149 mFrontMirrorCheckBox.setOnCheckedChangeListener(mCheckBoxObserver); 158 mFrontMirrorCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
  159 + //纯音频推流
150 mAudioOnlyCheckBox = (CheckBox) findViewById(R.id.audio_only); 160 mAudioOnlyCheckBox = (CheckBox) findViewById(R.id.audio_only);
151 mAudioOnlyCheckBox.setOnCheckedChangeListener(mCheckBoxObserver); 161 mAudioOnlyCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
152 } 162 }
153 163
154 private void initStreamer() { 164 private void initStreamer() {
  165 + //初始化推流SDK
155 mStreamer = new Streamer(this); 166 mStreamer = new Streamer(this);
156 //设置预览编码帧率 167 //设置预览编码帧率
157 mStreamer.setPreviewFps(15); 168 mStreamer.setPreviewFps(15);
@@ -181,11 +192,11 @@ public class StreamActivity extends Activity { @@ -181,11 +192,11 @@ public class StreamActivity extends Activity {
181 192
182 // 开启推流统计功能 193 // 开启推流统计功能
183 mStreamer.setEnableStreamStatModule(true); 194 mStreamer.setEnableStreamStatModule(true);
184 - //设置前置摄像头镜像 195 + //设置前置摄像头镜像 默认不开启
185 mStreamer.setFrontCameraMirror(mFrontMirrorCheckBox.isChecked()); 196 mStreamer.setFrontCameraMirror(mFrontMirrorCheckBox.isChecked());
186 - //静音 197 + //静音 默认不开启
187 mStreamer.setMuteAudio(mMuteCheckBox.isChecked()); 198 mStreamer.setMuteAudio(mMuteCheckBox.isChecked());
188 - //耳返 199 + //耳返 默认不开启
189 mStreamer.setEnableAudioPreview(mAudioPreviewCheckBox.isChecked()); 200 mStreamer.setEnableAudioPreview(mAudioPreviewCheckBox.isChecked());
190 //设置Info回调,可以收到相关通知信息 201 //设置Info回调,可以收到相关通知信息
191 mStreamer.setOnInfoListener(mOnInfoListener); 202 mStreamer.setOnInfoListener(mOnInfoListener);
@@ -248,7 +259,7 @@ public class StreamActivity extends Activity { @@ -248,7 +259,7 @@ public class StreamActivity extends Activity {
248 return true; 259 return true;
249 } 260 }
250 261
251 - //TODO 开始推流 262 + // 开始推流
252 private void startStream() { 263 private void startStream() {
253 mStreamer.startStream(activityConfig); 264 mStreamer.startStream(activityConfig);
254 mShootingText.setText(STOP_STRING); 265 mShootingText.setText(STOP_STRING);
@@ -256,7 +267,7 @@ public class StreamActivity extends Activity { @@ -256,7 +267,7 @@ public class StreamActivity extends Activity {
256 mRecording = true; 267 mRecording = true;
257 } 268 }
258 269
259 - //TODO 结束推流 270 + // 结束推流
260 private void stopStream() { 271 private void stopStream() {
261 272
262 mStreamer.stopStream(); 273 mStreamer.stopStream();
@@ -418,7 +429,27 @@ public class StreamActivity extends Activity { @@ -418,7 +429,27 @@ public class StreamActivity extends Activity {
418 //显示水印 429 //显示水印
419 private void showWaterMark() { 430 private void showWaterMark() {
420 if (!mIsLandscape) { 431 if (!mIsLandscape) {
  432 + /**
  433 + * 设置并显示logo水印
  434 + *
  435 + * @param path logo图片文件的路径
  436 + * @param x logo的显示位置,0-1之间,相对于视频
  437 + * @param y logo的显示位置,0-1之间,相对于视频
  438 + * @param w logo的显示宽度,0-1之间,相对于视频,为0时会根据h及logo图片的比例自适应
  439 + * @param h logo的显示高度,0-1之间,相对于视频,为0时会根据w及logo图片的比例自适应
  440 + * @param alpha logo的透明度,0-1之间
  441 + */
421 mStreamer.showWaterMarkLogo(mLogoPath, 0.08f, 0.04f, 0.20f, 0, 0.8f); 442 mStreamer.showWaterMarkLogo(mLogoPath, 0.08f, 0.04f, 0.20f, 0, 0.8f);
  443 +
  444 + /**
  445 + * 在推流视频中显示时间水印
  446 + *
  447 + * @param x 时间戳的显示位置,0-1之间,相对于视频
  448 + * @param y 时间戳的显示位置,0-1之间,相对于视频
  449 + * @param w 时间戳的显示宽度,0-1之间,相对于视频,高度会自适应
  450 + * @param color 时间戳的颜色
  451 + * @param alpha 时间戳的显示透明度,0-1之间
  452 + */
422 mStreamer.showWaterMarkTime(0.03f, 0.01f, 0.35f, Color.RED, 1.0f); 453 mStreamer.showWaterMarkTime(0.03f, 0.01f, 0.35f, Color.RED, 1.0f);
423 } else { 454 } else {
424 mStreamer.showWaterMarkLogo(mLogoPath, 0.05f, 0.09f, 0, 0.08f, 0.8f); 455 mStreamer.showWaterMarkLogo(mLogoPath, 0.05f, 0.09f, 0, 0.08f, 0.8f);
@@ -460,6 +491,14 @@ public class StreamActivity extends Activity { @@ -460,6 +491,14 @@ public class StreamActivity extends Activity {
460 alertDialog = new AlertDialog.Builder(this) 491 alertDialog = new AlertDialog.Builder(this)
461 .setTitle("请选择美颜滤镜") 492 .setTitle("请选择美颜滤镜")
462 .setSingleChoiceItems( 493 .setSingleChoiceItems(
  494 + /**
  495 + * BEAUTY_DISABLE 禁用美颜 无
  496 + * DENOISE 自然 美白(中)+磨皮(低)+红润(低)
  497 + * SKIN_WHITEN 白肤 美白(强)+磨皮(中)+红润(低)
  498 + * BEAUTY_ILLUSION 柔肤 美白(中)+磨皮(中)+红润(低)
  499 + * BEAUTY_SMOOTH 粉嫩 美白(中)+磨皮(强)+红润(中)
  500 + * BEAUTY_SOFT 嫩肤 美白(中)+磨皮(中)+红润(强)
  501 + */
463 new String[]{"BEAUTY_SOFT", "SKIN_WHITEN", "BEAUTY_ILLUSION", "DENOISE", 502 new String[]{"BEAUTY_SOFT", "SKIN_WHITEN", "BEAUTY_ILLUSION", "DENOISE",
464 "BEAUTY_SMOOTH"}, -1, 503 "BEAUTY_SMOOTH"}, -1,
465 new DialogInterface.OnClickListener() { 504 new DialogInterface.OnClickListener() {
@@ -495,21 +534,24 @@ public class StreamActivity extends Activity { @@ -495,21 +534,24 @@ public class StreamActivity extends Activity {
495 public void onInfo(int what, int msg1, int msg2) { 534 public void onInfo(int what, int msg1, int msg2) {
496 switch (what) { 535 switch (what) {
497 case IStreamer.cnstream_camera_init_done: 536 case IStreamer.cnstream_camera_init_done:
  537 + //初始化完毕
498 Log.d(TAG, "cnstream_camera_init_done"); 538 Log.d(TAG, "cnstream_camera_init_done");
499 break; 539 break;
500 case IStreamer.cnstream_open_stream_succ: 540 case IStreamer.cnstream_open_stream_succ:
  541 + //推流成功
501 Log.d(TAG, "cnstream_open_stream_success"); 542 Log.d(TAG, "cnstream_open_stream_success");
502 - mChronometer.setBase(SystemClock.elapsedRealtime());  
503 - mChronometer.start();  
504 break; 543 break;
505 case IStreamer.cnstream_frame_send_slow: 544 case IStreamer.cnstream_frame_send_slow:
  545 + //网络状态不佳,数据发送可能有延迟 发送当前帧的时长
506 Log.d(TAG, "cnstream_frame_send_slow"); 546 Log.d(TAG, "cnstream_frame_send_slow");
507 Toast.makeText(StreamActivity.this, "Network not good!", Toast.LENGTH_SHORT).show(); 547 Toast.makeText(StreamActivity.this, "Network not good!", Toast.LENGTH_SHORT).show();
508 break; 548 break;
509 case IStreamer.cnstream_est_bw_raise: 549 case IStreamer.cnstream_est_bw_raise:
  550 + //码率上调了 码率上调的目标值 单位bps
510 Log.d(TAG, "BW raise to" + msg1 / 1000 + "kbps"); 551 Log.d(TAG, "BW raise to" + msg1 / 1000 + "kbps");
511 break; 552 break;
512 case IStreamer.cnstream_est_bw_drop: 553 case IStreamer.cnstream_est_bw_drop:
  554 + //码率下调了 码率下调到的值 单位bps
513 Log.d(TAG, "BW drop to" + msg1 / 1000 + "kbps"); 555 Log.d(TAG, "BW drop to" + msg1 / 1000 + "kbps");
514 break; 556 break;
515 default: 557 default:
@@ -525,10 +567,11 @@ public class StreamActivity extends Activity { @@ -525,10 +567,11 @@ public class StreamActivity extends Activity {
525 567
526 if (what < 0) { 568 if (what < 0) {
527 mRecording = false; 569 mRecording = false;
528 - mShootingText.setText(START_STRING); 570 + mShootingText.setText("开始直播");
529 mShootingText.postInvalidate(); 571 mShootingText.postInvalidate();
530 } 572 }
531 573
  574 + Toast.makeText(StreamActivity.this, TAG + " " + what, Toast.LENGTH_SHORT).show();
532 switch (what) { 575 switch (what) {
533 case IStreamer.cnstream_error_start_stream_server: 576 case IStreamer.cnstream_error_start_stream_server:
534 //开始直播时请求服务器失败 577 //开始直播时请求服务器失败
@@ -561,67 +604,65 @@ public class StreamActivity extends Activity { @@ -561,67 +604,65 @@ public class StreamActivity extends Activity {
561 //初始化失败 正在停止推流 604 //初始化失败 正在停止推流
562 break; 605 break;
563 case IStreamer.cnstream_error_dns_parse_failed: 606 case IStreamer.cnstream_error_dns_parse_failed:
  607 + //url域名解析失败
564 Log.d(TAG, "cnstream_error_dns_parse_failed"); 608 Log.d(TAG, "cnstream_error_dns_parse_failed");
565 break; 609 break;
566 case IStreamer.cnstream_error_connect_failed: 610 case IStreamer.cnstream_error_connect_failed:
  611 + //网络连接失败,无法建立连接
567 Log.d(TAG, "cnstream_error_connect_failed"); 612 Log.d(TAG, "cnstream_error_connect_failed");
568 break; 613 break;
569 case IStreamer.cnstream_error_publish_failed: 614 case IStreamer.cnstream_error_publish_failed:
  615 + //跟RTMP服务器完成握手后,向{streamname}推流失败)
570 Log.d(TAG, "cnstream_error_publish_failed"); 616 Log.d(TAG, "cnstream_error_publish_failed");
571 break; 617 break;
572 case IStreamer.cnstream_error_connect_breaked: 618 case IStreamer.cnstream_error_connect_breaked:
  619 + //网络连接断开
573 Log.d(TAG, "cnstream_error_connect_breaked"); 620 Log.d(TAG, "cnstream_error_connect_breaked");
574 break; 621 break;
575 case IStreamer.cnstream_error_av_async: 622 case IStreamer.cnstream_error_av_async:
  623 + //音视频采集pts差值超过5s 音视频采集pts具体差值,单位ms
576 Log.d(TAG, "cnstream_error_av_async " + msg1 + " ms"); 624 Log.d(TAG, "cnstream_error_av_async " + msg1 + " ms");
577 break; 625 break;
578 case IStreamer.cnstream_video_encoder_error_unsupported: 626 case IStreamer.cnstream_video_encoder_error_unsupported:
  627 + //编码器初始化失败
579 Log.d(TAG, "cnstream_video_encoder_error_unsupported"); 628 Log.d(TAG, "cnstream_video_encoder_error_unsupported");
580 break; 629 break;
581 case IStreamer.cnstream_video_encoder_error_unknown: 630 case IStreamer.cnstream_video_encoder_error_unknown:
  631 + //视频编码失败
582 Log.d(TAG, "cnstream_video_encoder_error_unknown"); 632 Log.d(TAG, "cnstream_video_encoder_error_unknown");
583 break; 633 break;
584 case IStreamer.cnstream_audio_encoder_error_unsupported: 634 case IStreamer.cnstream_audio_encoder_error_unsupported:
  635 + //音频初始化失败
585 Log.d(TAG, "cnstream_audio_encoder_error_unsupported"); 636 Log.d(TAG, "cnstream_audio_encoder_error_unsupported");
586 break; 637 break;
587 case IStreamer.cnstream_audio_encoder_error_unknown: 638 case IStreamer.cnstream_audio_encoder_error_unknown:
  639 + //音频编码失败
588 Log.d(TAG, "cnstream_audio_encoder_error_unknown"); 640 Log.d(TAG, "cnstream_audio_encoder_error_unknown");
589 break; 641 break;
590 case IStreamer.cnstream_audio_recorder_error_start_failed: 642 case IStreamer.cnstream_audio_recorder_error_start_failed:
  643 + //录音开启失败
591 Log.d(TAG, "cnstream_audio_recorder_error_start_failed"); 644 Log.d(TAG, "cnstream_audio_recorder_error_start_failed");
592 break; 645 break;
593 case IStreamer.cnstream_audio_recorder_error_unknown: 646 case IStreamer.cnstream_audio_recorder_error_unknown:
  647 + //录音开启未知错误
594 Log.d(TAG, "cnstream_audio_recorder_error_unknown"); 648 Log.d(TAG, "cnstream_audio_recorder_error_unknown");
595 break; 649 break;
596 case IStreamer.cnstream_camera_error_unknown: 650 case IStreamer.cnstream_camera_error_unknown:
  651 + //摄像头未知错误
597 Log.d(TAG, "cnstream_camera_error_unknown"); 652 Log.d(TAG, "cnstream_camera_error_unknown");
598 break; 653 break;
599 case IStreamer.cnstream_camera_error_start_failed: 654 case IStreamer.cnstream_camera_error_start_failed:
  655 + //打开摄像头失败
600 Log.d(TAG, "cnstream_camera_error_start_failed"); 656 Log.d(TAG, "cnstream_camera_error_start_failed");
601 break; 657 break;
602 case IStreamer.cnstream_camera_error_server_died: 658 case IStreamer.cnstream_camera_error_server_died:
  659 + //系统Camera服务进程退出
603 Log.d(TAG, "cnstream_camera_error_server_died"); 660 Log.d(TAG, "cnstream_camera_error_server_died");
604 break; 661 break;
605 default: 662 default:
606 Log.d(TAG, "what= " + what + " msg1= " + msg1 + " msg2= " + msg2); 663 Log.d(TAG, "what= " + what + " msg1= " + msg1 + " msg2= " + msg2);
607 break; 664 break;
608 } 665 }
609 - switch (what) {  
610 - case IStreamer.cnstream_camera_error_unknown:  
611 - case IStreamer.cnstream_camera_error_start_failed:  
612 - case IStreamer.cnstream_audio_recorder_error_start_failed:  
613 - case IStreamer.cnstream_audio_recorder_error_unknown:  
614 - break;  
615 - case IStreamer.cnstream_camera_error_server_died:  
616 - mStreamer.stopCameraPreview();  
617 - mMainHandler.postDelayed(new Runnable() {  
618 - @Override  
619 - public void run() {  
620 - startCameraPreviewWithPermCheck();  
621 - }  
622 - }, 5000);  
623 - break;  
624 - }  
625 } 666 }
626 }; 667 };
627 668
@@ -629,51 +670,49 @@ public class StreamActivity extends Activity { @@ -629,51 +670,49 @@ public class StreamActivity extends Activity {
629 ActivityConfig activityConfig = new ActivityConfig() { 670 ActivityConfig activityConfig = new ActivityConfig() {
630 @Override 671 @Override
631 public String getActivityID() { 672 public String getActivityID() {
  673 + //活动ID,确保SP下的唯一性(即appId唯一) ,不能含下划线,不支持转义字符和urlencode会处理的特殊字符,如下! # $ % & ‘ ( )* + , . / : ; = ? @ [ / ]
632 return activityId; 674 return activityId;
633 } 675 }
634 676
635 @Override 677 @Override
636 public String getActivityName() { 678 public String getActivityName() {
  679 + //活动名称
637 return ""; 680 return "";
638 } 681 }
639 682
640 @Override 683 @Override
641 public int getActivityStatus() { 684 public int getActivityStatus() {
  685 + //活动状态,0:未开始 /1:已开始 /2:已结束 /3 已下线,默认为0
642 return 0; 686 return 0;
643 } 687 }
644 688
645 @Override 689 @Override
646 public int getIsHorizontalScreen() { 690 public int getIsHorizontalScreen() {
  691 + //屏幕方向,0:垂直即竖屏 /1:水平即横屏 ,默认为1
647 return isHorizontalScreen; 692 return isHorizontalScreen;
648 } 693 }
649 694
650 @Override 695 @Override
651 public String getChannelID() { 696 public String getChannelID() {
  697 + //播放通道;PGC业务:平台分配/ UGC业务:主播ID生成,确保SP下的唯一性,不能含下划线,不支持转义字符和urlencode会处理的特殊字符,如下! # $ % & ‘ ( )* + , . / : ; = ? @ [ / ]
652 return channelId; 698 return channelId;
653 } 699 }
654 700
655 @Override 701 @Override
656 - public String getStartTime() {  
657 - return "";  
658 - }  
659 -  
660 - @Override  
661 - public String getEndTime() {  
662 - return "";  
663 - }  
664 -  
665 - @Override  
666 public String getCoverImgUrl() { 702 public String getCoverImgUrl() {
  703 + //活动封面
667 return ""; 704 return "";
668 } 705 }
669 706
670 @Override 707 @Override
671 public String getActivityCategory() { 708 public String getActivityCategory() {
  709 + //活动分类ID
672 return ""; 710 return "";
673 } 711 }
674 712
675 @Override 713 @Override
676 public String getExtensions() { 714 public String getExtensions() {
  715 + //扩展字段,参数格式为Map对象的Json字符串(主播昵称、头像、)
677 return ""; 716 return "";
678 } 717 }
679 718
@@ -684,6 +723,7 @@ public class StreamActivity extends Activity { @@ -684,6 +723,7 @@ public class StreamActivity extends Activity {
684 723
685 @Override 724 @Override
686 public String getCallbackAcitvityStatushttpPostUrl() { 725 public String getCallbackAcitvityStatushttpPostUrl() {
  726 + //回调通知活动状态(错误,结束、心跳异常等状态),详情见回调接口说明
687 return ""; 727 return "";
688 } 728 }
689 }; 729 };
app/src/main/res/layout/activity_cnstream.xml 0 → 100644
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + xmlns:tools="http://schemas.android.com/tools"
  4 + android:id="@+id/activity_cnstream"
  5 + android:layout_width="match_parent"
  6 + android:layout_height="match_parent"
  7 + android:background="#00000000"
  8 + tools:context="com.cnlive.demo.stream.CNStreamActivity">
  9 +
  10 + <android.opengl.GLSurfaceView
  11 + android:id="@+id/cnSurfaceView"
  12 + android:layout_width="match_parent"
  13 + android:layout_height="match_parent" />
  14 +
  15 + <Button
  16 + android:id="@+id/startStream"
  17 + android:layout_width="wrap_content"
  18 + android:layout_height="wrap_content"
  19 + android:layout_alignParentBottom="true"
  20 + android:layout_centerHorizontal="true"
  21 + android:layout_marginBottom="16dp"
  22 + android:onClick="onClick"
  23 + android:text="开始推流" />
  24 +
  25 +</RelativeLayout>
app/src/main/res/layout/activity_main.xml
@@ -80,12 +80,19 @@ @@ -80,12 +80,19 @@
80 android:text="竖屏" /> 80 android:text="竖屏" />
81 </RadioGroup> 81 </RadioGroup>
82 82
  83 +
83 <Button 84 <Button
84 android:id="@+id/connectBT" 85 android:id="@+id/connectBT"
85 android:layout_width="match_parent" 86 android:layout_width="match_parent"
86 android:layout_height="wrap_content" 87 android:layout_height="wrap_content"
87 android:layout_alignParentBottom="true" 88 android:layout_alignParentBottom="true"
88 android:layout_marginBottom="16dp" 89 android:layout_marginBottom="16dp"
89 - android:text="准备直播" /> 90 + android:text="推流基本操作" />
90 91
  92 + <Button
  93 + android:id="@+id/cnStreamer"
  94 + android:layout_width="match_parent"
  95 + android:layout_height="wrap_content"
  96 + android:layout_above="@+id/connectBT"
  97 + android:text="CNStreamerDemo" />
91 </RelativeLayout> 98 </RelativeLayout>
92 \ No newline at end of file 99 \ No newline at end of file