目前很多APP都含有自己的视频直播功能,市场上大家比较熟悉的是阿里和腾讯的两款直播服务,最初我选的视腾讯云直播集成方法也很简单,后来发现水印无法去除可以自定义,,项目要求无水印,交涉后组长说需要付费才能去除,直播画面画质不太好,免费版特别虚;因此转而使用阿里的直播服务,集成方法很简单,比较顺利。
1.根据文档来:http://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/assets/attach/137410/cn_zh/1583376879936/%E7%9B%B4%E6%92%AD%E6%8E%A8%E6%B5%81SDK%E5%BC%80%E5%8F%91%E6%96%87%E6%A1%A3%281%29.pdf?spm=a2c4g.11186623.2.11.40e14cce9yPVoE&file=%E7%9B%B4%E6%92%AD%E6%8E%A8%E6%B5%81SDK%E5%BC%80%E5%8F%91%E6%96%87%E6%A1%A3%281%29.pdf
2.在官网上把.so文件下载下来,放到自己的lib下
3.直播要用的aar和jar包放到相应的目录下,官网上有下载,美颜,人脸等jar可以根据自己项目需要添加。
1.build.gradle文件添加依赖: implementation files(‘libs/live-face-3.4.0.jar’)
implementation files(‘libs/commons-lang3-3.0.jar’)
implementation ‘com.aliyun.dpa:oss-android-sdk:+’;文中有关于aar包的添加,同样需要在build.gradle文件中引用,在build.gradle写全局引用。
2.集成系统要求最小API:15
3.不要忘了在manifest文件中打开权限,除了网络,读写以及相机权限,打开手电筒
可以使用闪光灯功能,注意:android6.0以后部分权限需要代码开启。
4.准备就绪后就开始开始自己的视频推流业务了,布局文件可以根据自家UI设计来,我这边用一个LivePushActivity接收和设置推流配置,嵌套一个Fragment实现具体推流逻辑;默认推流设置
private AlivcLivePushConfig getPushConfig() {
mAlivcLivePushConfig.setResolution(mDefinition);
mAlivcLivePushConfig.setInitialVideoBitrate(1400);
mAlivcLivePushConfig.setAudioBitRate(1000 * 64);
mAlivcLivePushConfig.setMinVideoBitrate(600);
mAlivcLivePushConfig.setTargetVideoBitrate(1400);
mAlivcLivePushConfig.setConnectRetryCount(5);
mAlivcLivePushConfig.setConnectRetryInterval(16);
mAlivcLivePushConfig.setFps(FPS_25);
mAlivcLivePushConfig.setCameraType(AlivcLivePushCameraTypeEnum.CAMERA_TYPE_BACK);//后置摄像头
mAlivcLivePushConfig.setPreviewDisplayMode(AlivcPreviewDisplayMode.ALIVC_LIVE_PUSHER_PREVIEW_ASPECT_FILL);//全屏剪裁模式
return mAlivcLivePushConfig;
}
```java
public class LivePushActivity extends AppCompatActivity {
private static final String TAG = "LivePushActivity";
private static final int FLING_MIN_DISTANCE = 50;
private static final int FLING_MIN_VELOCITY = 0;
private final long REFRESH_INTERVAL = 1000;
private static final String URL_KEY = "url_key";
private static final String ASYNC_KEY = "async_key";
private static final String AUDIO_ONLY_KEY = "audio_only_key";
private static final String VIDEO_ONLY_KEY = "video_only_key";
private static final String ORIENTATION_KEY = "orientation_key";
private static final String CAMERA_ID = "camera_id";
private static final String FLASH_ON = "flash_on";
private static final String AUTH_TIME = "auth_time";
private static final String PRIVACY_KEY = "privacy_key";
private static final String MIX_EXTERN = "mix_extern";
private static final String MIX_MAIN = "mix_main";
public static final int REQ_CODE_PUSH = 0x1112;
public static final int CAPTURE_PERMISSION_REQUEST_CODE = 0x1123;
public SurfaceView mPreviewView;
private ViewPager mViewPager;
private List mFragmentList = new ArrayList<>();
private FragmentAdapter mFragmentAdapter;
private GestureDetector mDetector;
private ScaleGestureDetector mScaleDetector;
private LivePushFragment mLivePushFragment;
private AlivcLivePushConfig mAlivcLivePushConfig;
private AlivcLivePusher mAlivcLivePusher = null;
private String mPushUrl = null;
private boolean mAsync = false;
private boolean mAudioOnly = false;
private boolean mVideoOnly = false;
private int mOrientation = ORIENTATION_PORTRAIT.ordinal();
private SurfaceStatus mSurfaceStatus = SurfaceStatus.UNINITED;
// private Handler mHandler = new Handler();
private boolean isPause = false;
private int mCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
private boolean mFlash = false;
private boolean mMixExtern = false;
private boolean mMixMain = false;
AlivcLivePushStatsInfo alivcLivePushStatsInfo = null;
TaoBeautyFilter taoBeautyFilter;
TaoFaceFilter taoFaceFilter;
private String mAuthTime = "";
private String mPrivacyKey = "";
// private ConnectivityChangedReceiver mChangedReceiver = new ConnectivityChangedReceiver();
private boolean videoThreadOn = false;
private boolean audioThreadOn = false;
private int mNetWork = 0;
private JsonsRootBean liveInfoBeanData = null;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
mPushUrl = getIntent().getStringExtra(URL_KEY);
mAsync = getIntent().getBooleanExtra(ASYNC_KEY, false);
mAudioOnly = getIntent().getBooleanExtra(AUDIO_ONLY_KEY, false);
mVideoOnly = getIntent().getBooleanExtra(VIDEO_ONLY_KEY, false);
mOrientation = getIntent().getIntExtra(ORIENTATION_KEY, ORIENTATION_PORTRAIT.ordinal());
mCameraId = getIntent().getIntExtra(CAMERA_ID, Camera.CameraInfo.CAMERA_FACING_FRONT);
mFlash = getIntent().getBooleanExtra(FLASH_ON, false);
mAuthTime = getIntent().getStringExtra(AUTH_TIME);
mPrivacyKey = getIntent().getStringExtra(PRIVACY_KEY);
mMixExtern = getIntent().getBooleanExtra(MIX_EXTERN, false);
mMixMain = getIntent().getBooleanExtra(MIX_MAIN, false);
liveInfoBeanData = (JsonsRootBean) getIntent().getSerializableExtra("liveInfo");
setOrientation(mOrientation);
setContentView(R.layout.activity_push);
initView();
mAlivcLivePushConfig = (AlivcLivePushConfig) getIntent().getSerializableExtra(AlivcLivePushConfig.CONFIG);
mAlivcLivePusher = new AlivcLivePusher();
try {
mAlivcLivePusher.init(getApplicationContext(), mAlivcLivePushConfig);
} catch (IllegalArgumentException e) {
e.printStackTrace();
showDialog(this, e.getMessage());
} catch (IllegalStateException e) {
e.printStackTrace();
showDialog(this, e.getMessage());
}
mAlivcLivePusher.setCustomDetect(new AlivcLivePushCustomDetect() {
@Override
public void customDetectCreate() {
taoFaceFilter = new TaoFaceFilter(getApplicationContext());
taoFaceFilter.customDetectCreate();
}
@Override
public long customDetectProcess(long data, int width, int height, int rotation, int format, long extra) {
if (taoFaceFilter != null) {
return taoFaceFilter.customDetectProcess(data, width, height, rotation, format, extra);
}
return 0;
}
@Override
public void customDetectDestroy() {
if (taoFaceFilter != null) {
taoFaceFilter.customDetectDestroy();
}
}
});
mLivePushFragment = new LivePushFragment().newInstance(mPushUrl, mAsync, mAudioOnly, mVideoOnly, mCameraId, mFlash, mAlivcLivePushConfig.getQualityMode().getQualityMode(), mAuthTime, mPrivacyKey, mMixExtern, mMixMain);
mLivePushFragment.setAlivcLivePusher(mAlivcLivePusher);
mLivePushFragment.setStateListener(mStateListener);
mLivePushFragment.setLiveInfoBean(liveInfoBeanData);
SharedPreferences sharedPreferences = getSharedPreferences(Config.SP_USER_MANAGER, Context.MODE_PRIVATE);
mLivePushFragment.setRealName(sharedPreferences.getString("realName", ""));
initViewPager();
mScaleDetector = new ScaleGestureDetector(getApplicationContext(), mScaleGestureDetector);
mDetector = new GestureDetector(getApplicationContext(), mGestureDetector);
mNetWork = NetWorkUtils.getAPNType(this);
}
public void initView() {
mPreviewView = (SurfaceView) findViewById(R.id.preview_view);
mPreviewView.getHolder().addCallback(mCallback);
if (NetUtils.getAPNType(this) != 1 && NetUtils.getAPNType(this) != 0) {
UIUtils.toastLongMessage("当前非wifi环境,请注意流量");
}
}
private void initViewPager() {
mViewPager = (ViewPager) findViewById(R.id.tv_pager);
// mFragmentList.add(mPushTextStatsFragment);
mFragmentList.add(mLivePushFragment);
// mFragmentList.add(mPushDiagramStatsFragment);
mFragmentAdapter = new FragmentAdapter(this.getSupportFragmentManager(), mFragmentList);
mViewPager.setAdapter(mFragmentAdapter);
// mViewPager.setCurrentItem(1);
mViewPager.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
// if(((ViewPager)view).getCurrentItem() == 1) {
if (motionEvent.getPointerCount() >= 2 && mScaleDetector != null) {
mScaleDetector.onTouchEvent(motionEvent);
} else if (motionEvent.getPointerCount() == 1 && mDetector != null) {
mDetector.onTouchEvent(motionEvent);
}
// }
return false;
}
});
}
private void setOrientation(int orientation) {
if (orientation == ORIENTATION_PORTRAIT.ordinal()) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} else if (orientation == ORIENTATION_LANDSCAPE_HOME_RIGHT.ordinal()) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
} else if (orientation == ORIENTATION_LANDSCAPE_HOME_LEFT.ordinal()) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
}
}
private GestureDetector.OnGestureListener mGestureDetector = new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent motionEvent) {
return false;
}
@Override
public void onShowPress(MotionEvent motionEvent) {
}
@Override
public boolean onSingleTapUp(MotionEvent motionEvent) {
if (mPreviewView.getWidth() > 0 && mPreviewView.getHeight() > 0) {
float x = motionEvent.getX() / mPreviewView.getWidth();
float y = motionEvent.getY() / mPreviewView.getHeight();
try {
mAlivcLivePusher.focusCameraAtAdjustedPoint(x, y, true);
} catch (IllegalStateException e) {
}
}
return true;
}
@Override
public boolean onScroll(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
return false;
}
@Override
public void onLongPress(MotionEvent motionEvent) {
}
@Override
public boolean onFling(MotionEvent motionEvent, MotionEvent motionEvent1, float v, float v1) {
if (motionEvent == null || motionEvent1 == null) {
return false;
}
if (motionEvent.getX() - motionEvent1.getX() > FLING_MIN_DISTANCE
&& Math.abs(v) > FLING_MIN_VELOCITY) {
// Fling left
} else if (motionEvent1.getX() - motionEvent.getX() > FLING_MIN_DISTANCE
&& Math.abs(v) > FLING_MIN_VELOCITY) {
// Fling right
}
return false;
}
};
private float scaleFactor = 1.0f;
private ScaleGestureDetector.OnScaleGestureListener mScaleGestureDetector = new ScaleGestureDetector.OnScaleGestureListener() {
@Override
public boolean onScale(ScaleGestureDetector scaleGestureDetector) {
if (scaleGestureDetector.getScaleFactor() > 1) {
scaleFactor += 0.5;
} else {
scaleFactor -= 2;
}
if (scaleFactor <= 1) {
scaleFactor = 1;
}
try {
if (scaleFactor >= mAlivcLivePusher.getMaxZoom()) {
scaleFactor = mAlivcLivePusher.getMaxZoom();
}
mAlivcLivePusher.setZoom((int) scaleFactor);
} catch (IllegalStateException e) {
}
return false;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector scaleGestureDetector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector scaleGestureDetector) {
}
};
SurfaceHolder.Callback mCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
if (mSurfaceStatus == SurfaceStatus.UNINITED) {
mSurfaceStatus = SurfaceStatus.CREATED;
if (mAlivcLivePusher != null) {
try {
if (mAsync) {
mAlivcLivePusher.startPreviewAysnc(mPreviewView);
} else {
mAlivcLivePusher.startPreview(mPreviewView);
}
if (mAlivcLivePushConfig.isExternMainStream()) {
startYUV(getApplicationContext());
}
} catch (IllegalArgumentException e) {
e.toString();
} catch (IllegalStateException e) {
e.toString();
}
}
} else if (mSurfaceStatus == SurfaceStatus.DESTROYED) {
mSurfaceStatus = SurfaceStatus.RECREATED;
}
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
mSurfaceStatus = SurfaceStatus.CHANGED;
if (mLivePushFragment != null) {
mLivePushFragment.setSurfaceView(mPreviewView);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
mSurfaceStatus = SurfaceStatus.DESTROYED;
}
};
public static void startActivity(Activity activity, JsonsRootBean liveInfoBean, AlivcLivePushConfig alivcLivePushConfig, String url, boolean async, boolean audioOnly, boolean videoOnly, AlivcPreviewOrientationEnum orientation, int cameraId, boolean isFlash, String authTime, String privacyKey, boolean mixExtern, boolean mixMain) {
Intent intent = new Intent(activity, LivePushActivity.class);
Bundle bundle = new Bundle();
bundle.putSerializable(AlivcLivePushConfig.CONFIG, alivcLivePushConfig);
bundle.putSerializable("liveInfo", liveInfoBean);
bundle.putString(URL_KEY, url);
bundle.putBoolean(ASYNC_KEY, async);
bundle.putBoolean(AUDIO_ONLY_KEY, audioOnly);
bundle.putBoolean(VIDEO_ONLY_KEY, videoOnly);
bundle.putInt(ORIENTATION_KEY, orientation.ordinal());
bundle.putInt(CAMERA_ID, cameraId);
bundle.putBoolean(FLASH_ON, isFlash);
bundle.putString(AUTH_TIME, authTime);
bundle.putString(PRIVACY_KEY, privacyKey);
bundle.putBoolean(MIX_EXTERN, mixExtern);
bundle.putBoolean(MIX_MAIN, mixMain);
intent.putExtras(bundle);
activity.startActivityForResult(intent, REQ_CODE_PUSH);
}
@Override
protected void onResume() {
super.onResume();
ShareUtils.putBoolean("livePasue", true);
if (mAlivcLivePusher != null) {
try {
if (!isPause) {
if (mAsync) {
mAlivcLivePusher.resumeAsync();
} else {
mAlivcLivePusher.resume();
}
}
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
}
}
@Override
protected void onPause() {
super.onPause();
if (mAlivcLivePusher != null) {
try {
if (mAlivcLivePusher != null/*.isPushing()*/) {//退出后台也走这个
if (ShareUtils.getBoolean("livePasue", true)) {
mAlivcLivePusher.pause();//这个时候不调暂停接口
ShareUtils.putBoolean("livePasue", true);
}
// mAlivcLivePusher.pause();//这个时候不调暂停接口
}
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
// if(mHandler != null) {
// mHandler.removeCallbacks(mRunnable);
// }
}
@Override
protected void onDestroy() {
EventBus.getDefault().unregister(this);
videoThreadOn = false;
audioThreadOn = false;
if (mAlivcLivePusher != null) {
try {
mAlivcLivePusher.destroy();
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
// if(mHandler != null) {
// mHandler.removeCallbacks(mRunnable);
// mHandler = null;
// }
// unregisterReceiver(mChangedReceiver);
mFragmentList = null;
mPreviewView = null;
mViewPager = null;
mFragmentAdapter = null;
mDetector = null;
mScaleDetector = null;
mLivePushFragment = null;
mAlivcLivePushConfig = null;
mAlivcLivePusher = null;
// mHandler = null;
alivcLivePushStatsInfo = null;
super.onDestroy();
}
public class FragmentAdapter extends FragmentPagerAdapter {
List fragmentList = new ArrayList<>();
public FragmentAdapter(FragmentManager fm, List fragmentList) {
super(fm);
this.fragmentList = fragmentList;
}
@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
@Override
public int getCount() {
return fragmentList.size();
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int rotation = getWindowManager().getDefaultDisplay().getRotation();
AlivcPreviewOrientationEnum orientationEnum;
if (mAlivcLivePusher != null) {
switch (rotation) {
case Surface.ROTATION_0:
orientationEnum = ORIENTATION_PORTRAIT;
break;
case Surface.ROTATION_90:
orientationEnum = ORIENTATION_LANDSCAPE_HOME_RIGHT;
break;
case Surface.ROTATION_270:
orientationEnum = ORIENTATION_LANDSCAPE_HOME_LEFT;
break;
default:
orientationEnum = ORIENTATION_PORTRAIT;
break;
}
try {
mAlivcLivePusher.setPreviewOrientation(orientationEnum);
} catch (IllegalStateException e) {
}
}
}
public AlivcLivePusher getLivePusher() {
return this.mAlivcLivePusher;
}
public SurfaceView getPreviewView() {
return this.mPreviewView;
}
private void showDialog(Context context, String message) {
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
dialog.setTitle(getString(R.string.dialog_title));
dialog.setMessage(message);
dialog.setNegativeButton(getString(R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
finish();
}
});
dialog.show();
}
private Runnable mRunnable = new Runnable() {
@Override
public void run() {
LogUtil.d(TAG, "====== mRunnable run ======");
new AsyncTask() {
@Override
protected AlivcLivePushStatsInfo doInBackground(AlivcLivePushStatsInfo... alivcLivePushStatsInfos) {
try {
alivcLivePushStatsInfo = mAlivcLivePusher.getLivePushStatsInfo();
} catch (IllegalStateException e) {
}
return alivcLivePushStatsInfo;
}
@Override
protected void onPostExecute(AlivcLivePushStatsInfo alivcLivePushStatsInfo) {
super.onPostExecute(alivcLivePushStatsInfo);
// if(mPushTextStatsFragment != null && mViewPager.getCurrentItem() == 0) {
// mPushTextStatsFragment.updateValue(alivcLivePushStatsInfo);
// } else if (mPushDiagramStatsFragment != null && mViewPager.getCurrentItem() == 2) {
// mPushDiagramStatsFragment.updateValue(alivcLivePushStatsInfo);
// }
//// mHandler.postDelayed(mRunnable, REFRESH_INTERVAL);
}
}.execute();
}
};
public interface PauseState {
void updatePause(boolean state);
}
private PauseState mStateListener = new PauseState() {
@Override
public void updatePause(boolean state) {
isPause = state;
}
};
class ConnectivityChangedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
if (mNetWork != NetWorkUtils.getAPNType(context)) {
mNetWork = NetWorkUtils.getAPNType(context);
if (mAlivcLivePusher != null) {
if (mAlivcLivePusher.isPushing()) {
try {
mAlivcLivePusher.reconnectPushAsync(null);
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
}
}
}
}
}
public void startYUV(final Context context) {
new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
private AtomicInteger atoInteger = new AtomicInteger(0);
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("LivePushActivity-readYUV-Thread" + atoInteger.getAndIncrement());
return t;
}
}).execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
videoThreadOn = true;
byte[] yuv;
InputStream myInput = null;
try {
File f = new File(Environment.getExternalStorageDirectory().getPath() + File.separator + "alivc_resource/capture0.yuv");
myInput = new FileInputStream(f);
byte[] buffer = new byte[1280 * 720 * 3 / 2];
int length = myInput.read(buffer);
//发数据
while (length > 0 && videoThreadOn) {
mAlivcLivePusher.inputStreamVideoData(buffer, 720, 1280, 720, 1280 * 720 * 3 / 2, System.nanoTime() / 1000, 0);
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
//发数据
length = myInput.read(buffer);
if (length <= 0) {
myInput.close();
myInput = new FileInputStream(f);
length = myInput.read(buffer);
}
}
myInput.close();
videoThreadOn = false;
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
quitThisLive();
}
return super.onKeyDown(keyCode, event);
}
private void quitThisLive() {
new CommomLiveDialog(this, R.style.ActionSheetDialogStyle, "", new CommomLiveDialog.OnCloseListener() {
@Override
public void onClick(Dialog dialog, boolean confirm) {
if (confirm) {
if (NetUtils.getAPNType(LivePushActivity.this) == 0) {
// mLivePushFragment.stopLivePusher();//无网只关闭直播
finish();
} else {
mLivePushFragment.exitLive();
}
} else {
dialog.dismiss();
}
}
}).show();
}
布局文件,使用SurfaceView上面贴一个ViewPager,用来写自定义布局:
```java
5.编写自己的推流逻辑,重画UI
```java
public class LivePushFragment extends Fragment implements Runnable {
public static final String TAG = "LivePushFragment";
private static final String URL_KEY = "url_key";
private static final String ASYNC_KEY = "async_key";
private static final String AUDIO_ONLY_KEY = "audio_only_key";
private static final String VIDEO_ONLY_KEY = "video_only_key";
private static final String QUALITY_MODE_KEY = "quality_mode_key";
private static final String CAMERA_ID = "camera_id";
private static final String FLASH_ON = "flash_on";
private static final String AUTH_TIME = "auth_time";
private static final String PRIVACY_KEY = "privacy_key";
private static final String MIX_EXTERN = "mix_extern";
private static final String MIX_MAIN = "mix_main";
private final long REFRESH_INTERVAL = 2000;
private Button mExit;
private ImageView mFlash;
private ImageView mCamera;
private AlivcLivePusher mAlivcLivePusher = null;
private String mPushUrl = null;
private SurfaceView mSurfaceView = null;
private boolean mAsync = false;
private boolean mAudio = false;
private boolean mVideoOnly = false;
private boolean isPushing = false;
private Handler mHandler = new Handler();
private LivePushActivity.PauseState mStateListener = null;
private int mCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT;
private boolean isFlash = false;
private boolean mMixExtern = false;
private boolean mMixMain = false;
private boolean flashState = true;
private int snapshotCount = 0;
private int mQualityMode = 0;
private TextView captureNumTv, captureThemeTv, delayTitleTv, liveStartNameTv, lookViewerTv;//人数和主题
private LinearLayout delayTimell;
private ImageView headerIv;
private GridView viewerGv;
private ImageView addViewerIv;
private CircleImageView viewer1, viewer2, viewer3;
ScheduledExecutorService mExecutorService = new ScheduledThreadPoolExecutor(5,
new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());
private boolean audioThreadOn = false;
private String mAuthString = "?auth_key=%1$d-%2$d-%3$d-%4$s";
private String mMd5String = "%1$s-%2$d-%3$d-%4$d-%5$s";
private String mTempUrl = null;
private String mAuthTime = "";
private String mPrivacyKey = "";
private Timer timer;
Vector<Integer> mDynamicals = new Vector<>();
private List<SelectPersonContainerBean.UserListBean> persons = new ArrayList<SelectPersonContainerBean.UserListBean>();
private List<SelectPersonContainerBean.UserListBean> tempPersons = new ArrayList<SelectPersonContainerBean.UserListBean>();
private JsonsRootBean liveInfoBean;
private String realName = "";
private JsonsRootBean jsonsRootBean = null;
public void setRealName(String realName) {
this.realName = realName;
}
public void setLiveInfoBean(JsonsRootBean liveInfoBean) {
this.liveInfoBean = liveInfoBean;
}
public static LivePushFragment newInstance(String url, boolean async, boolean mAudio, boolean mVideoOnly, int cameraId, boolean isFlash, int mode, String authTime, String privacyKey, boolean mixExtern, boolean mixMain) {
LivePushFragment livePushFragment = new LivePushFragment();
Bundle bundle = new Bundle();
bundle.putString(URL_KEY, url);
bundle.putBoolean(ASYNC_KEY, async);
bundle.putBoolean(AUDIO_ONLY_KEY, mAudio);
bundle.putBoolean(VIDEO_ONLY_KEY, mVideoOnly);
bundle.putInt(QUALITY_MODE_KEY, mode);
bundle.putInt(CAMERA_ID, cameraId);
bundle.putBoolean(FLASH_ON, isFlash);
bundle.putString(AUTH_TIME, authTime);
bundle.putString(PRIVACY_KEY, privacyKey);
bundle.putBoolean(MIX_EXTERN, mixExtern);
bundle.putBoolean(MIX_MAIN, mixMain);
livePushFragment.setArguments(bundle);
return livePushFragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mPushUrl = getArguments().getString(URL_KEY);
mTempUrl = mPushUrl;
mAsync = getArguments().getBoolean(ASYNC_KEY, false);
mAudio = getArguments().getBoolean(AUDIO_ONLY_KEY, false);
mVideoOnly = getArguments().getBoolean(VIDEO_ONLY_KEY, false);
mCameraId = getArguments().getInt(CAMERA_ID);
isFlash = getArguments().getBoolean(FLASH_ON, false);
mMixExtern = getArguments().getBoolean(MIX_EXTERN, false);
mMixMain = getArguments().getBoolean(MIX_MAIN, false);
mQualityMode = getArguments().getInt(QUALITY_MODE_KEY);
mAuthTime = getArguments().getString(AUTH_TIME);
mPrivacyKey = getArguments().getString(PRIVACY_KEY);
flashState = isFlash;
}
if (mAlivcLivePusher != null) {
mAlivcLivePusher.setLivePushInfoListener(mPushInfoListener);
mAlivcLivePusher.setLivePushErrorListener(mPushErrorListener);
mAlivcLivePusher.setLivePushNetworkListener(mPushNetworkListener);
mAlivcLivePusher.setLivePushBGMListener(mPushBGMListener);
isPushing = mAlivcLivePusher.isPushing();
}
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.push_fragment, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mFlash.setVisibility(mAudio ? View.GONE : View.VISIBLE);
mCamera.setVisibility(mAudio ? View.GONE : View.VISIBLE);
mFlash.setClickable(mCameraId == CAMERA_TYPE_BACK.getCameraId() ? false : true);//闪光灯根据摄像头切换
timerStart();//直播到计时开始计时
}
private void quitThisLive() {
new CommomLiveDialog(getContext(), R.style.ActionSheetDialogStyle, "", new CommomLiveDialog.OnCloseListener() {
@Override
public void onClick(Dialog dialog, boolean confirm) {
if (confirm) {
if (getActivity() != null) {
if (NetUtils.getAPNType(getActivity()) == 0) {
// stopLivePusher();//无网只关闭直播
getActivity().finish();
} else {
exitLive();
}
}
} else {
dialog.dismiss();
}
}
}).show();
}
//非正常操作退出的
// public void exitLiveError(final String msg) {
// if (liveInfoBean != null) {
// String url = App.getInstance().getIP() + Config.CREATE_LIVE_DELETE + "?userId=" + App.getInstance().getUserId() + "&liveId=" + liveInfoBean.getId();
// App.getInstance().getRequestQueue().add(new HeaderStringRequest(Request.Method.POST, url, new CustomResonse() {
// @Override
// public void onCustomResponse(String s) {
// LogUtils.i(s);
// ResponseBean responseBean = JsonParseUtil.getBean(s, ResponseBean.class);
// if (responseBean.getCode() == 200) {
// UIUtils.toastLongMessage(msg);
// getActivity().finish();
// }
// }
// }, new CustomErrorListener() {
// @Override
// public void onCustomErrorResponse(VolleyError volleyError) {
//
// }
// }));
// }
// }
public void stopLivePusher() {
if (timer1 != null) {
timerCancel();
}
if (mAlivcLivePusher != null) {
mAlivcLivePusher.stopPush();
stopPcm();
//停止推流
if (mStateListener != null) {
mStateListener.updatePause(false);
}
}
getActivity().finish();
}
View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
final int id = view.getId();
if (mAlivcLivePusher == null) {
if (getActivity() != null) {
mAlivcLivePusher = ((LivePushActivity) getActivity()).getLivePusher();
}
if (mAlivcLivePusher == null) {
return;
}
}
mExecutorService.execute(new Runnable() {
@Override
public void run() {
try {
switch (id) {
case R.id.flashButton:
//闪光灯
mAlivcLivePusher.setFlash(!mFlash.isSelected());
flashState = !mFlash.isSelected();
mFlash.post(new Runnable() {
@Override
public void run() {
mFlash.setSelected(!mFlash.isSelected());
}
});
break;
case R.id.switchFacingButton:
//切换摄像头
if (mCameraId == CAMERA_TYPE_FRONT.getCameraId()) {
mCameraId = CAMERA_TYPE_BACK.getCameraId();
} else {
mCameraId = CAMERA_TYPE_FRONT.getCameraId();
}
mAlivcLivePusher.switchCamera();
mFlash.post(new Runnable() {
@Override
public void run() {
mFlash.setClickable(mCameraId == CAMERA_TYPE_BACK.getCameraId() ? false : true);
if (mCameraId == CAMERA_TYPE_BACK.getCameraId()) {
mFlash.setSelected(false);
} else {
mFlash.setSelected(flashState);
}
}
});
break;
default:
break;
}
} catch (IllegalArgumentException e) {
showDialog(e.getMessage());
e.printStackTrace();
} catch (IllegalStateException e) {
showDialog(e.getMessage());
e.printStackTrace();
}
}
});
}
};
public void setAlivcLivePusher(AlivcLivePusher alivcLivePusher) {
this.mAlivcLivePusher = alivcLivePusher;
}
public void setStateListener(LivePushActivity.PauseState listener) {
this.mStateListener = listener;
}
public void setSurfaceView(SurfaceView surfaceView) {
this.mSurfaceView = surfaceView;
}
AlivcLivePushInfoListener mPushInfoListener = new AlivcLivePushInfoListener() {
@Override
public void onPreviewStarted(AlivcLivePusher pusher) {
// showToast(getString(R.string.start_preview));//开始预览
}
@Override
public void onPreviewStoped(AlivcLivePusher pusher) {
showToast(getString(R.string.stop_preview));
}
@Override
public void onPushStarted(AlivcLivePusher pusher) {
showToast(getString(R.string.start_push));
initTimer(0, 6000);
initTimer(1, 6000 * 10);
}
@Override
public void onFirstAVFramePushed(AlivcLivePusher pusher) {
}
@Override
public void onPushPauesed(AlivcLivePusher pusher) {//跳页面以后走这个
// showToast(getString(R.string.pause_push));
stopTimer();//停止更新UI
}
@Override
public void onPushResumed(AlivcLivePusher pusher) {//恢复
// showToast(getString(R.string.resume_push));
initTimer(0, 6000);
initTimer(1, 6000 * 10);
}
@Override
public void onPushStoped(AlivcLivePusher pusher) {
// showToast(getString(R.string.stop_push));
stopTimer();
}
/**
* 推流重启通知
*
* @param pusher AlivcLivePusher实例
*/
@Override
public void onPushRestarted(AlivcLivePusher pusher) {
// showToast(getString(R.string.restart_success));
}
@Override
public void onFirstFramePreviewed(AlivcLivePusher pusher) {
// showToast(getString(R.string.first_frame));
}
@Override
public void onDropFrame(AlivcLivePusher pusher, int countBef, int countAft) {
// showToast(getString(R.string.drop_frame) + ", 丢帧前:" + countBef + ", 丢帧后:" + countAft);
}
@Override
public void onAdjustBitRate(AlivcLivePusher pusher, int curBr, int targetBr) {
// showToast(getString(R.string.adjust_bitrate) + ", 当前码率:" + curBr + "Kps, 目标码率:" + targetBr + "Kps");
}
@Override
public void onAdjustFps(AlivcLivePusher pusher, int curFps, int targetFps) {
// showToast(getString(R.string.adjust_fps) + ", 当前帧率:" + curFps + ", 目标帧率:" + targetFps);
}
};
AlivcLivePushErrorListener mPushErrorListener = new AlivcLivePushErrorListener() {
@Override
public void onSystemError(AlivcLivePusher livePusher, AlivcLivePushError error) {
// showDialog(getString(R.string.system_error) + error.toString());
}
@Override
public void onSDKError(AlivcLivePusher livePusher, AlivcLivePushError error) {
if (error != null) {
// showDialog(getString(R.string.sdk_error) + error.toString());
if (mAlivcLivePusher != null) {
//启动重连
mAlivcLivePusher.setLivePushNetworkListener(mPushNetworkListener);
isPushing = mAlivcLivePusher.isPushing();
}
}
}
};
AlivcLivePushNetworkListener mPushNetworkListener = new AlivcLivePushNetworkListener() {
@Override
public void onNetworkPoor(AlivcLivePusher pusher) {
showNetWorkDialog(getString(R.string.network_poor), 0);
// mAlivcLivePusher.reconnectPushAsync(null);
}
@Override
public void onNetworkRecovery(AlivcLivePusher pusher) {
showToast(getString(R.string.network_recovery));
}
@Override
public void onReconnectStart(AlivcLivePusher pusher) {
if (NetUtils.getAPNType(getActivity()) == 0) {
// UIUtils.toastLongMessage("当前非wifi环境,请注意流量");
stopTimer();
showDialog(getString(R.string.tip_unavaild_net));
} else {
// initTimer();
showToastShort(getString(R.string.reconnect_start));
}
}
@Override
public void onReconnectFail(AlivcLivePusher pusher) {
if (NetUtils.getAPNType(getActivity()) == 0) {
stopTimer();
// UIUtils.toastLongMessage("当前非wifi环境,请注意流量");
showDialog(getString(R.string.tip_unavaild_net));
} else {
showDialog(getString(R.string.reconnect_fail));
}
}
@Override
public void onReconnectSucceed(AlivcLivePusher pusher) {
showToast(getString(R.string.reconnect_success));
}
@Override
public void onSendDataTimeout(AlivcLivePusher pusher) {
showDialog(getString(R.string.senddata_timeout));
}
@Override
public void onConnectFail(AlivcLivePusher pusher) {
showDialog(getString(R.string.connect_fail));
}
@Override
public void onConnectionLost(AlivcLivePusher pusher) {
// showToast("推流已断开");
// exitLiveError("推流已断开");
}
@Override
public String onPushURLAuthenticationOverdue(AlivcLivePusher pusher) {
showDialog("流即将过期,请更换url");
return getAuthString(mAuthTime);
}
@Override
public void onSendMessage(AlivcLivePusher pusher) {
showToast(getString(R.string.send_message));
}
@Override
public void onPacketsLost(AlivcLivePusher pusher) {
// showToast("推流丢包通知");
// exitLiveError("丢包了请重进");
}
};
private AlivcLivePushBGMListener mPushBGMListener = new AlivcLivePushBGMListener() {
@Override
public void onStarted() {
}
@Override
public void onStoped() {
}
@Override
public void onPaused() {
}
@Override
public void onResumed() {
}
@Override
public void onProgress(final long progress, final long duration) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
// if(mMusicDialog != null) {
// mMusicDialog.updateProgress(progress, duration);
// }
}
});
}
@Override
public void onCompleted() {
}
@Override
public void onDownloadTimeout() {
}
@Override
public void onOpenFailed() {
showDialog(getString(R.string.bgm_open_failed));
}
};
@Override
public void onDestroy() {
//stopPcm();
//stopYUV();
super.onDestroy();
stopTimer();
if (mExecutorService != null && !mExecutorService.isShutdown()) {
mExecutorService.shutdown();
}
}
private void showToast(final String text) {
if (getActivity() == null || text == null) {
return;
}
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
if (getActivity() != null) {
Toast toast = Toast.makeText(getActivity(), text, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
}
});
}
private void showToastShort(final String text) {
if (getActivity() == null || text == null) {
return;
}
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
if (getActivity() != null) {
Toast toast = Toast.makeText(getActivity(), text, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
}
});
}
private void showDialog(final String message) {
if (getActivity() == null || message == null) {
return;
}
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
if (getActivity() != null) {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.reconnectPushAsync(null);
}
// Toast.makeText(getActivity(),message,Toast.LENGTH_LONG).show();
Toast toast = Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
}
});
}
private void showNetWorkDialog(final String message, int index) {
if (getActivity() == null || message == null) {
return;
}
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
if (getActivity() != null) {
mAlivcLivePusher.reconnectPushAsync(null);
// Toast.makeText(getActivity(),message,Toast.LENGTH_LONG).show();
Toast toast = Toast.makeText(getActivity(), message, Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
}
});
}
@Override
public void run() {
// if(mIsPushing != null && mAlivcLivePusher != null) {
// try {
// isPushing = mAlivcLivePusher.isNetworkPushing();
// } catch (IllegalStateException e) {
// e.printStackTrace();
// }
// AlivcLivePushError error = mAlivcLivePusher.getLastError();
// if(!error.equals(AlivcLivePushError.ALIVC_COMMON_RETURN_SUCCESS)) {
// mIsPushing.setText(String.valueOf(isPushing)+", error code : "+error.getCode());
// } else {
// mIsPushing.setText(String.valueOf(isPushing));
// }
// }
// mHandler.postDelayed(this, REFRESH_INTERVAL);
}
@Override
public void onResume() {
super.onResume();
mHandler.post(this);
}
@Override
public void onPause() {
super.onPause();
mHandler.removeCallbacks(this);
}
private String getMD5(String string) {
byte[] hash;
try {
hash = MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return null;
}
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
if ((b & 0xFF) < 0x10) {
hex.append("0");
}
hex.append(Integer.toHexString(b & 0xFF));
}
return hex.toString();
}
private String getUri(String url) {
String result = "";
String temp = url.substring(7);
if (temp != null && !temp.isEmpty()) {
result = temp.substring(temp.indexOf("/"));
}
return result;
}
private String getAuthString(String time) {
if (!time.isEmpty() && !mPrivacyKey.isEmpty()) {
long tempTime = (System.currentTimeMillis() + Integer.valueOf(time)) / 1000;
String tempprivacyKey = String.format(mMd5String, getUri(mPushUrl), tempTime, 0, 0, mPrivacyKey);
String auth = String.format(mAuthString, tempTime, 0, 0, getMD5(tempprivacyKey));
mTempUrl = mPushUrl + auth;
} else {
mTempUrl = mPushUrl;
}
return mTempUrl;
}
private void startPCM(final Context context) {
new ScheduledThreadPoolExecutor(1, new ThreadFactory() {
private AtomicInteger atoInteger = new AtomicInteger(0);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("LivePushActivity-readPCM-Thread" + atoInteger.getAndIncrement());
return t;
}
}).execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
audioThreadOn = true;
byte[] pcm;
int allSended = 0;
int sizePerSecond = 44100 * 2;
InputStream myInput = null;
OutputStream myOutput = null;
boolean reUse = false;
long startPts = System.nanoTime() / 1000;
try {
File f = new File("/sdcard/alivc_resource/441.pcm");
myInput = new FileInputStream(f);
byte[] buffer = new byte[2048];
int length = myInput.read(buffer, 0, 2048);
while (length > 0 && audioThreadOn) {
long pts = System.nanoTime() / 1000;
mAlivcLivePusher.inputStreamAudioData(buffer, length, 44100, 1, pts);
allSended += length;
if ((allSended * 1000000L / sizePerSecond - 50000) > (pts - startPts)) {
try {
Thread.sleep(45);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
length = myInput.read(buffer);
if (length < 2048) {
myInput.close();
myInput = new FileInputStream(f);
length = myInput.read(buffer);
}
try {
Thread.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
myInput.close();
audioThreadOn = false;
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
private void stopPcm() {
audioThreadOn = false;
}
/**
* 倒数计时器
*/
private CountDownTimer timer1 = new CountDownTimer(3000, 1000) {
/**
* 固定间隔被调用,就是每隔countDownInterval会回调一次方法onTick
* @param millisUntilFinished
*/
@Override
public void onTick(long millisUntilFinished) {
Log.e("captureActivity===", delayTitleTv.getText().toString());
int index1 = Integer.parseInt(delayTitleTv.getText().toString());
if (index1 != 1) {
delayTitleTv.setText(index1 - 1 + "");
}
}
/**
* 倒计时完成时被调用
*/
@Override
public void onFinish() {
// delayTitleTv.setVisibility(View.GONE);
delayTimell.setVisibility(View.GONE);
startLive(true);//开始推流
if (timer1 != null) {
timerCancel();//取消
}
}
};
public void startLive(boolean isStart) {
final boolean isPush = isStart;
if (isPush) {
if (mAsync) {
mAlivcLivePusher.startPushAysnc(getAuthString(mAuthTime));
} else {
mAlivcLivePusher.startPush(getAuthString(mAuthTime));
}
if (mMixExtern) {
//startMixPCM(getActivity());
} else if (mMixMain) {
startPCM(getActivity());
}
if (liveInfoBean != null) {
sendViewerNotify();
}
} else {
if (mAlivcLivePusher != null) {
mAlivcLivePusher.stopPush();
stopPcm();
//停止推流
if (mStateListener != null) {
mStateListener.updatePause(false);
}
}
}
}
/**
* 取消倒计时
*/
public void timerCancel() {
timer1.cancel();
timer1 = null;
}
/**
* 开始倒计时
*/
public void timerStart() {
timer1.start();
}
// 停止定时任务
private void stopTimer() {
if (timer != null) {
timer.cancel();
// 一定设置为null,否则定时器不会被回收
timer = null;
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == Activity.RESULT_OK) {
List<SelectPersonContainerBean.UserListBean> ids = (List<SelectPersonContainerBean.UserListBean>) data.getSerializableExtra("ids");
// LogUtils.w(ids);
if (requestCode == 1) {
if (ids.size() > 0) {
if (persons.size() != tempPersons.size()) {
int index = persons.size() - tempPersons.size();
if (ids.size() > index) {
persons.clear();
persons.addAll(tempPersons);
persons.addAll(ids);
addViewer(ids);
}
} else {
persons.clear();
persons.addAll(tempPersons);
persons.addAll(ids);
addViewer(ids);
}
}
}
}
}
private void addViewer(List<SelectPersonContainerBean.UserListBean> ids) {
List<String> idList = new ArrayList<>();
for (int i = 0; i < ids.size(); i++) {
idList.add(ids.get(i).getId());
}
JSONArray jsonArray = new JSONArray(idList);
String url = App.getInstance().getIP() + Config.CREATE_LIVE_ADDUSER + "?userId=" + App.getInstance().getUserId() + "&liveId=" + liveInfoBean.getId();
App.getInstance().getRequestQueue().add(new HeaderJSONArrayRequest(Request.Method.POST, url, jsonArray, new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray response) {
Log.e("JSONArrayRequest", "添加成功" + response);
}
}, new CustomJsonArrayParseListener() {
@Override
public void onJsonArrayParse(String response) {
// Log.e("JSONArrayRequest","添加成功"+response);
// ResponseBean responseBean=JsonParseUtil.getBean(response,ResponseBean.class);
// if(responseBean.getCode()==200){
UIUtils.toastLongMessage("添加成功");
Log.e("JSONArrayRequest", "添加成功");
setPersonUnSelected();
// }
}
@Override
public void onCustomErrorResponse(VolleyError volleyError) {
}
}));
}
private void setPersonUnSelected() {
//设置选中的人为不可选状态
// List tempPersons = new ArrayList<>();
for (int i = 0; i < persons.size(); i++) {
persons.get(i).setUnCheckable(true);
}
}
}
页面布局:
```java
6.推流的地址要在官网申请
7.需要注意的是:蓝牙是否打开会影响推流,使得项目出现闪退;解决方法在manifest文件加上
##结束