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 36 android:configChanges="orientation|keyboardHidden|screenSize"
37 37 android:launchMode="singleTop"
38 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 44 </application>
40 45  
41 46 </manifest>
42 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 12  
13 13 private static final String TAG = MainActivity.class.getSimpleName();
14 14 private Button mConnectButton;
  15 + private Button mCNStreamerBtn;
15 16  
16 17 private RadioButton mLandscapeButton;
17 18 private RadioButton mPortraitButton;
... ... @@ -19,6 +20,10 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
19 20 private EditText activityId;
20 21 private EditText channelId;
21 22  
  23 + private String activityID = "";
  24 + private String channelID = "";
  25 + private boolean landscape;
  26 +
22 27 @Override
23 28 protected void onCreate(Bundle savedInstanceState) {
24 29  
... ... @@ -28,6 +33,9 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
28 33 mConnectButton = (Button) findViewById(R.id.connectBT);
29 34 mConnectButton.setOnClickListener(this);
30 35  
  36 + mCNStreamerBtn = (Button) findViewById(R.id.cnStreamer);
  37 + mCNStreamerBtn.setOnClickListener(this);
  38 +
31 39 mLandscapeButton = (RadioButton) findViewById(R.id.orientationbutton1);
32 40 mPortraitButton = (RadioButton) findViewById(R.id.orientationbutton2);
33 41  
... ... @@ -35,23 +43,29 @@ public class MainActivity extends AppCompatActivity implements View.OnClickListe
35 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 58 @Override
39 59 public void onClick(View view) {
40 60 switch (view.getId()) {
41 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 69 break;
56 70 default:
57 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 41  
42 42 /**
43 43 * Created by Administrator on 2016/8/29.
  44 + * StreamActivity 本 Activity主要展示的是推流SDK的具体用法,包括视讯云的逻辑和推流的设置,如果只想了解视讯云的逻辑,请参考CNStreamActivity
44 45 */
45 46 public class StreamActivity extends Activity {
46 47 private static final String TAG = "StreamActivity";
... ... @@ -85,13 +86,12 @@ public class StreamActivity extends Activity {
85 86 public static final String CHANNELID = "channelId";
86 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 90 String activityID,
90 91 String channelID,
91 92 boolean isLandscape) {
92 93 Intent intent = new Intent(context, StreamActivity.class);
93 94 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
94   - intent.putExtra("type", fromType);
95 95 intent.putExtra(ACTIVITYID, activityID);
96 96 intent.putExtra(CHANNELID, channelID);
97 97 intent.putExtra(LANDSCAPE, isLandscape);
... ... @@ -125,33 +125,44 @@ public class StreamActivity extends Activity {
125 125 mChronometer = (Chronometer) findViewById(R.id.chronometer);
126 126  
127 127 mObserverButton = new ButtonObserver();
  128 + //开始/结束推流按钮
128 129 mShootingText = (TextView) findViewById(R.id.click_to_shoot);
129 130 mShootingText.setOnClickListener(mObserverButton);
  131 + //返回按钮
130 132 mDeleteView = findViewById(R.id.backoff);
131 133 mDeleteView.setOnClickListener(mObserverButton);
  134 + //转换摄像头
132 135 mSwitchCameraView = findViewById(R.id.switch_cam);
133 136 mSwitchCameraView.setOnClickListener(mObserverButton);
  137 + //闪光灯
134 138 mFlashView = findViewById(R.id.flash);
135 139 mFlashView.setOnClickListener(mObserverButton);
136   -
137 140 mCheckBoxObserver = new CheckBoxObserver();
  141 + //美艳
138 142 mBeautyCheckBox = (CheckBox) findViewById(R.id.click_to_switch_beauty);
139 143 mBeautyCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
  144 + //背景音乐
140 145 mBgmCheckBox = (CheckBox) findViewById(R.id.bgm);
141 146 mBgmCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
  147 + //耳返
142 148 mAudioPreviewCheckBox = (CheckBox) findViewById(R.id.ear_mirror);
143 149 mAudioPreviewCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
  150 + //静音
144 151 mMuteCheckBox = (CheckBox) findViewById(R.id.mute);
145 152 mMuteCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
  153 + //水印
146 154 mWaterMarkCheckBox = (CheckBox) findViewById(R.id.watermark);
147 155 mWaterMarkCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
  156 + //前置摄像头镜像
148 157 mFrontMirrorCheckBox = (CheckBox) findViewById(R.id.front_camera_mirror);
149 158 mFrontMirrorCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
  159 + //纯音频推流
150 160 mAudioOnlyCheckBox = (CheckBox) findViewById(R.id.audio_only);
151 161 mAudioOnlyCheckBox.setOnCheckedChangeListener(mCheckBoxObserver);
152 162 }
153 163  
154 164 private void initStreamer() {
  165 + //初始化推流SDK
155 166 mStreamer = new Streamer(this);
156 167 //设置预览编码帧率
157 168 mStreamer.setPreviewFps(15);
... ... @@ -181,11 +192,11 @@ public class StreamActivity extends Activity {
181 192  
182 193 // 开启推流统计功能
183 194 mStreamer.setEnableStreamStatModule(true);
184   - //设置前置摄像头镜像
  195 + //设置前置摄像头镜像 默认不开启
185 196 mStreamer.setFrontCameraMirror(mFrontMirrorCheckBox.isChecked());
186   - //静音
  197 + //静音 默认不开启
187 198 mStreamer.setMuteAudio(mMuteCheckBox.isChecked());
188   - //耳返
  199 + //耳返 默认不开启
189 200 mStreamer.setEnableAudioPreview(mAudioPreviewCheckBox.isChecked());
190 201 //设置Info回调,可以收到相关通知信息
191 202 mStreamer.setOnInfoListener(mOnInfoListener);
... ... @@ -248,7 +259,7 @@ public class StreamActivity extends Activity {
248 259 return true;
249 260 }
250 261  
251   - //TODO 开始推流
  262 + // 开始推流
252 263 private void startStream() {
253 264 mStreamer.startStream(activityConfig);
254 265 mShootingText.setText(STOP_STRING);
... ... @@ -256,7 +267,7 @@ public class StreamActivity extends Activity {
256 267 mRecording = true;
257 268 }
258 269  
259   - //TODO 结束推流
  270 + // 结束推流
260 271 private void stopStream() {
261 272  
262 273 mStreamer.stopStream();
... ... @@ -418,7 +429,27 @@ public class StreamActivity extends Activity {
418 429 //显示水印
419 430 private void showWaterMark() {
420 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 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 453 mStreamer.showWaterMarkTime(0.03f, 0.01f, 0.35f, Color.RED, 1.0f);
423 454 } else {
424 455 mStreamer.showWaterMarkLogo(mLogoPath, 0.05f, 0.09f, 0, 0.08f, 0.8f);
... ... @@ -460,6 +491,14 @@ public class StreamActivity extends Activity {
460 491 alertDialog = new AlertDialog.Builder(this)
461 492 .setTitle("请选择美颜滤镜")
462 493 .setSingleChoiceItems(
  494 + /**
  495 + * BEAUTY_DISABLE 禁用美颜 无
  496 + * DENOISE 自然 美白(中)+磨皮(低)+红润(低)
  497 + * SKIN_WHITEN 白肤 美白(强)+磨皮(中)+红润(低)
  498 + * BEAUTY_ILLUSION 柔肤 美白(中)+磨皮(中)+红润(低)
  499 + * BEAUTY_SMOOTH 粉嫩 美白(中)+磨皮(强)+红润(中)
  500 + * BEAUTY_SOFT 嫩肤 美白(中)+磨皮(中)+红润(强)
  501 + */
463 502 new String[]{"BEAUTY_SOFT", "SKIN_WHITEN", "BEAUTY_ILLUSION", "DENOISE",
464 503 "BEAUTY_SMOOTH"}, -1,
465 504 new DialogInterface.OnClickListener() {
... ... @@ -495,21 +534,24 @@ public class StreamActivity extends Activity {
495 534 public void onInfo(int what, int msg1, int msg2) {
496 535 switch (what) {
497 536 case IStreamer.cnstream_camera_init_done:
  537 + //初始化完毕
498 538 Log.d(TAG, "cnstream_camera_init_done");
499 539 break;
500 540 case IStreamer.cnstream_open_stream_succ:
  541 + //推流成功
501 542 Log.d(TAG, "cnstream_open_stream_success");
502   - mChronometer.setBase(SystemClock.elapsedRealtime());
503   - mChronometer.start();
504 543 break;
505 544 case IStreamer.cnstream_frame_send_slow:
  545 + //网络状态不佳,数据发送可能有延迟 发送当前帧的时长
506 546 Log.d(TAG, "cnstream_frame_send_slow");
507 547 Toast.makeText(StreamActivity.this, "Network not good!", Toast.LENGTH_SHORT).show();
508 548 break;
509 549 case IStreamer.cnstream_est_bw_raise:
  550 + //码率上调了 码率上调的目标值 单位bps
510 551 Log.d(TAG, "BW raise to" + msg1 / 1000 + "kbps");
511 552 break;
512 553 case IStreamer.cnstream_est_bw_drop:
  554 + //码率下调了 码率下调到的值 单位bps
513 555 Log.d(TAG, "BW drop to" + msg1 / 1000 + "kbps");
514 556 break;
515 557 default:
... ... @@ -525,10 +567,11 @@ public class StreamActivity extends Activity {
525 567  
526 568 if (what < 0) {
527 569 mRecording = false;
528   - mShootingText.setText(START_STRING);
  570 + mShootingText.setText("开始直播");
529 571 mShootingText.postInvalidate();
530 572 }
531 573  
  574 + Toast.makeText(StreamActivity.this, TAG + " " + what, Toast.LENGTH_SHORT).show();
532 575 switch (what) {
533 576 case IStreamer.cnstream_error_start_stream_server:
534 577 //开始直播时请求服务器失败
... ... @@ -561,67 +604,65 @@ public class StreamActivity extends Activity {
561 604 //初始化失败 正在停止推流
562 605 break;
563 606 case IStreamer.cnstream_error_dns_parse_failed:
  607 + //url域名解析失败
564 608 Log.d(TAG, "cnstream_error_dns_parse_failed");
565 609 break;
566 610 case IStreamer.cnstream_error_connect_failed:
  611 + //网络连接失败,无法建立连接
567 612 Log.d(TAG, "cnstream_error_connect_failed");
568 613 break;
569 614 case IStreamer.cnstream_error_publish_failed:
  615 + //跟RTMP服务器完成握手后,向{streamname}推流失败)
570 616 Log.d(TAG, "cnstream_error_publish_failed");
571 617 break;
572 618 case IStreamer.cnstream_error_connect_breaked:
  619 + //网络连接断开
573 620 Log.d(TAG, "cnstream_error_connect_breaked");
574 621 break;
575 622 case IStreamer.cnstream_error_av_async:
  623 + //音视频采集pts差值超过5s 音视频采集pts具体差值,单位ms
576 624 Log.d(TAG, "cnstream_error_av_async " + msg1 + " ms");
577 625 break;
578 626 case IStreamer.cnstream_video_encoder_error_unsupported:
  627 + //编码器初始化失败
579 628 Log.d(TAG, "cnstream_video_encoder_error_unsupported");
580 629 break;
581 630 case IStreamer.cnstream_video_encoder_error_unknown:
  631 + //视频编码失败
582 632 Log.d(TAG, "cnstream_video_encoder_error_unknown");
583 633 break;
584 634 case IStreamer.cnstream_audio_encoder_error_unsupported:
  635 + //音频初始化失败
585 636 Log.d(TAG, "cnstream_audio_encoder_error_unsupported");
586 637 break;
587 638 case IStreamer.cnstream_audio_encoder_error_unknown:
  639 + //音频编码失败
588 640 Log.d(TAG, "cnstream_audio_encoder_error_unknown");
589 641 break;
590 642 case IStreamer.cnstream_audio_recorder_error_start_failed:
  643 + //录音开启失败
591 644 Log.d(TAG, "cnstream_audio_recorder_error_start_failed");
592 645 break;
593 646 case IStreamer.cnstream_audio_recorder_error_unknown:
  647 + //录音开启未知错误
594 648 Log.d(TAG, "cnstream_audio_recorder_error_unknown");
595 649 break;
596 650 case IStreamer.cnstream_camera_error_unknown:
  651 + //摄像头未知错误
597 652 Log.d(TAG, "cnstream_camera_error_unknown");
598 653 break;
599 654 case IStreamer.cnstream_camera_error_start_failed:
  655 + //打开摄像头失败
600 656 Log.d(TAG, "cnstream_camera_error_start_failed");
601 657 break;
602 658 case IStreamer.cnstream_camera_error_server_died:
  659 + //系统Camera服务进程退出
603 660 Log.d(TAG, "cnstream_camera_error_server_died");
604 661 break;
605 662 default:
606 663 Log.d(TAG, "what= " + what + " msg1= " + msg1 + " msg2= " + msg2);
607 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 670 ActivityConfig activityConfig = new ActivityConfig() {
630 671 @Override
631 672 public String getActivityID() {
  673 + //活动ID,确保SP下的唯一性(即appId唯一) ,不能含下划线,不支持转义字符和urlencode会处理的特殊字符,如下! # $ % & ‘ ( )* + , . / : ; = ? @ [ / ]
632 674 return activityId;
633 675 }
634 676  
635 677 @Override
636 678 public String getActivityName() {
  679 + //活动名称
637 680 return "";
638 681 }
639 682  
640 683 @Override
641 684 public int getActivityStatus() {
  685 + //活动状态,0:未开始 /1:已开始 /2:已结束 /3 已下线,默认为0
642 686 return 0;
643 687 }
644 688  
645 689 @Override
646 690 public int getIsHorizontalScreen() {
  691 + //屏幕方向,0:垂直即竖屏 /1:水平即横屏 ,默认为1
647 692 return isHorizontalScreen;
648 693 }
649 694  
650 695 @Override
651 696 public String getChannelID() {
  697 + //播放通道;PGC业务:平台分配/ UGC业务:主播ID生成,确保SP下的唯一性,不能含下划线,不支持转义字符和urlencode会处理的特殊字符,如下! # $ % & ‘ ( )* + , . / : ; = ? @ [ / ]
652 698 return channelId;
653 699 }
654 700  
655 701 @Override
656   - public String getStartTime() {
657   - return "";
658   - }
659   -
660   - @Override
661   - public String getEndTime() {
662   - return "";
663   - }
664   -
665   - @Override
666 702 public String getCoverImgUrl() {
  703 + //活动封面
667 704 return "";
668 705 }
669 706  
670 707 @Override
671 708 public String getActivityCategory() {
  709 + //活动分类ID
672 710 return "";
673 711 }
674 712  
675 713 @Override
676 714 public String getExtensions() {
  715 + //扩展字段,参数格式为Map对象的Json字符串(主播昵称、头像、)
677 716 return "";
678 717 }
679 718  
... ... @@ -684,6 +723,7 @@ public class StreamActivity extends Activity {
684 723  
685 724 @Override
686 725 public String getCallbackAcitvityStatushttpPostUrl() {
  726 + //回调通知活动状态(错误,结束、心跳异常等状态),详情见回调接口说明
687 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 80 android:text="竖屏" />
81 81 </RadioGroup>
82 82  
  83 +
83 84 <Button
84 85 android:id="@+id/connectBT"
85 86 android:layout_width="match_parent"
86 87 android:layout_height="wrap_content"
87 88 android:layout_alignParentBottom="true"
88 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 98 </RelativeLayout>
92 99 \ No newline at end of file
... ...