直播app是当下流行的视频社交类平台,也是重要的流量渠道,短视频同样是重要的流量之王,在直播中加入短视频源码功能,实现强强联合,有效提高了直播app的可玩性和用户粘性。云豹直播作为优质的直播平台源码提供商,在短视频功能方面必有其独到之处。下面为大家简单介绍一下云豹直播短视频源码功能的实现方式。
如上图所示,在拍摄视频页面,可以选择摄像头方向,闪光灯,美颜,背景音乐,拍摄速率等,也可选择本地已有的视频,点击底部中间的拍摄按钮开始拍摄,再次点击暂停拍摄,拍摄时长大于5秒后,可进入下一步的编辑页面。在编辑页面可以设置视频的滤镜,裁剪片段,以及添加特效等,最后进入发布页面。在发布页面可以设置视频的标题,所在城市等信息。 部分代码如下:
public class VideoPublishActivity extends AbsActivity implements ITXLivePlayListener, View.OnClickListener { public static void forward(Context context, String videoPath, String videoWaterPath, int saveType, int musicId) { Intent intent = new Intent(context, VideoPublishActivity.class); intent.putExtra(Constants.VIDEO_PATH, videoPath); intent.putExtra(Constants.VIDEO_PATH_WATER, videoWaterPath); intent.putExtra(Constants.VIDEO_SAVE_TYPE, saveType); intent.putExtra(Constants.VIDEO_MUSIC_ID, musicId); context.startActivity(intent); } private static final String TAG = "VideoPublishActivity"; private static final int REQ_CODE_GOODS = 100; private static final int REQ_CODE_CLASS = 101; private TextView mNum; private TextView mLocation; private TXCloudVideoView mTXCloudVideoView; private TXLivePlayer mPlayer; private String mVideoPath; private String mVideoPathWater; private boolean mPlayStarted;//播放是否开始了 private boolean mPaused;//生命周期暂停 private RecyclerView mRecyclerView; private ConfigBean mConfigBean; private VideoPubShareAdapter mAdapter; private VideoUploadStrategy mUploadStrategy; private EditText mInput; private String mVideoTitle;//视频标题 private Dialog mLoading; private MobShareUtil mMobShareUtil; private int mSaveType; private int mMusicId; private View mBtnPub; private CheckBox mCheckBox; private TextView mGoodsName; private View mBtnGoodsAdd; private TextView mVideoClassName; private int mVideoClassId; private int mGoodsType;//type 绑定的内容类型 0 没绑定 1 商品 2 付费内容 private String mGoodsId;//绑定的内容Id @Override protected int getLayoutId() { return R.layout.activity_video_publish; } @Override protected void main() { getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); setTitle(WordUtil.getString(R.string.video_pub)); Intent intent = getIntent(); mVideoPath = intent.getStringExtra(Constants.VIDEO_PATH); mVideoPathWater = intent.getStringExtra(Constants.VIDEO_PATH_WATER); mSaveType = intent.getIntExtra(Constants.VIDEO_SAVE_TYPE, Constants.VIDEO_SAVE_SAVE_AND_PUB); if (TextUtils.isEmpty(mVideoPath)) { return; } mMusicId = intent.getIntExtra(Constants.VIDEO_MUSIC_ID, 0); mBtnPub = findViewById(R.id.btn_pub); mBtnPub.setOnClickListener(this); mRecyclerView = findViewById(R.id.recyclerView); mRecyclerView.setHasFixedSize(true); mRecyclerView.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false)); CommonAppConfig.getInstance().getConfig(new CommonCallback<ConfigBean>() { @Override public void callback(ConfigBean bean) { mConfigBean = bean; if (mRecyclerView != null) { mAdapter = new VideoPubShareAdapter(mContext, bean); mRecyclerView.setAdapter(mAdapter); } } }); mNum = (TextView) findViewById(R.id.num); mInput = (EditText) findViewById(R.id.input); mInput.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (mNum != null) { mNum.setText(s.length() + "/50"); } } @Override public void afterTextChanged(Editable s) { } }); mLocation = findViewById(R.id.location); mLocation.setText(CommonAppConfig.getInstance().getCity()); mCheckBox = findViewById(R.id.checkbox); mCheckBox.setOnClickListener(this); mBtnGoodsAdd = findViewById(R.id.btn_goods_add); mVideoClassName = findViewById(R.id.video_class_name); mGoodsName = findViewById(R.id.goods_name); findViewById(R.id.btn_video_class).setOnClickListener(this); mTXCloudVideoView = findViewById(R.id.video_view); mPlayer = new TXLivePlayer(mContext); mPlayer.setConfig(new TXLivePlayConfig()); mPlayer.setPlayerView(mTXCloudVideoView); mPlayer.enableHardwareDecode(false); mPlayer.setRenderRotation(TXLiveConstants.RENDER_ROTATION_PORTRAIT); mPlayer.setRenderMode(TXLiveConstants.RENDER_MODE_FULL_FILL_SCREEN); mPlayer.setPlayListener(this); int result = mPlayer.startPlay(mVideoPath, TXLivePlayer.PLAY_TYPE_LOCAL_VIDEO); if (result == 0) { mPlayStarted = true; } VideoHttpUtil.getConcatGoods(new HttpCallback() { @Override public void onSuccess(int code, String msg, String[] info) { if (code == 0 && info.length > 0) { int isShop = JSON.parseObject(info[0]).getIntValue("isshop"); if (isShop == 1) { if (mBtnGoodsAdd != null) { mBtnGoodsAdd.setVisibility(View.VISIBLE); mBtnGoodsAdd.setOnClickListener(VideoPublishActivity.this); } } } } }); } @Override public void onPlayEvent(int e, Bundle bundle) { switch (e) { case TXLiveConstants.PLAY_EVT_PLAY_END://播放结束 onReplay(); break; case TXLiveConstants.PLAY_EVT_CHANGE_RESOLUTION: onVideoSizeChanged(bundle.getInt("EVT_PARAM1", 0), bundle.getInt("EVT_PARAM2", 0)); break; } } @Override public void onNetStatus(Bundle bundle) { } /** * 获取到视频宽高回调 */ public void onVideoSizeChanged(float videoWidth, float videoHeight) { if (mTXCloudVideoView != null && videoWidth > 0 && videoHeight > 0) { FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) mTXCloudVideoView.getLayoutParams(); if (videoWidth / videoHeight > 0.5625f) {//横屏 9:16=0.5625 params.height = (int) (mTXCloudVideoView.getWidth() / videoWidth * videoHeight); params.gravity = Gravity.CENTER; mTXCloudVideoView.requestLayout(); } } } /** * 循环播放 */ private void onReplay() { if (mPlayStarted && mPlayer != null) { mPlayer.seek(0); mPlayer.resume(); } } @Override protected void onPause() { super.onPause(); mPaused = true; if (mPlayStarted && mPlayer != null) { mPlayer.pause(); } } @Override protected void onResume() { super.onResume(); if (mPaused && mPlayStarted && mPlayer != null) { mPlayer.resume(); } mPaused = false; } public void release() { CommonHttpUtil.cancel(CommonHttpConsts.GET_CONFIG); VideoHttpUtil.cancel(VideoHttpConsts.GET_CONCAT_GOODS); VideoHttpUtil.cancel(VideoHttpConsts.SAVE_UPLOAD_VIDEO_INFO); mPlayStarted = false; if (mPlayer != null) { mPlayer.stopPlay(false); mPlayer.setPlayListener(null); } if (mUploadStrategy != null) { mUploadStrategy.cancel(); } if (mMobShareUtil != null) { mMobShareUtil.release(); } mPlayer = null; mUploadStrategy = null; mMobShareUtil = null; } @Override public void onBackPressed() { DialogUitl.showSimpleDialog(mContext, WordUtil.getString(R.string.video_give_up_pub), new DialogUitl.SimpleCallback() { @Override public void onConfirmClick(Dialog dialog, String content) { if (mSaveType == Constants.VIDEO_SAVE_PUB) { if (!TextUtils.isEmpty(mVideoPath)) { File file = new File(mVideoPath); if (file.exists()) { file.delete(); } } if (!TextUtils.isEmpty(mVideoPathWater)) { File file = new File(mVideoPathWater); if (file.exists()) { file.delete(); } } } release(); VideoPublishActivity.super.onBackPressed(); } }); } @Override protected void onDestroy() { release(); super.onDestroy(); } @Override public void onClick(View v) { int i = v.getId(); if (i == R.id.btn_pub) { publishVideo(); } else if (i == R.id.checkbox) { clickCheckBox(); } else if (i == R.id.btn_goods_add) { RouteUtil.searchMallGoods(this, REQ_CODE_GOODS); } else if (i == R.id.btn_video_class) { chooseVideoClass(); } } private void chooseVideoClass() { Intent intent = new Intent(mContext, VideoChooseClassActivity.class); intent.putExtra(Constants.VIDEO_ID, mVideoClassId); startActivityForResult(intent, REQ_CODE_CLASS); } private void clickCheckBox() { if (mCheckBox == null || mLocation == null) { return; } if (mCheckBox.isChecked()) { mLocation.setEnabled(true); mLocation.setText(CommonAppConfig.getInstance().getCity()); } else { mLocation.setEnabled(false); mLocation.setText(WordUtil.getString(R.string.mars)); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if (resultCode == RESULT_OK) { switch (requestCode) { case REQ_CODE_GOODS: mGoodsId = intent.getStringExtra(Constants.MALL_GOODS_ID); mGoodsType = intent.getIntExtra(Constants.LIVE_TYPE, 0); String goodsName = intent.getStringExtra(Constants.MALL_GOODS_NAME); mGoodsName.setText(goodsName); break; case REQ_CODE_CLASS: mVideoClassId = intent.getIntExtra(Constants.VIDEO_ID, 0); if (mVideoClassName != null) { mVideoClassName.setText(intent.getStringExtra(Constants.CLASS_NAME)); } break; } } } /** * 发布视频 */ private void publishVideo() { if (mConfigBean == null || TextUtils.isEmpty(mVideoPath)) { return; } if (mVideoClassId == 0) { ToastUtil.show(R.string.video_choose_class_2); return; } mBtnPub.setEnabled(false); mVideoTitle = mInput.getText().toString().trim(); mLoading = DialogUitl.loadingDialog(mContext, WordUtil.getString(R.string.video_pub_ing)); mLoading.show(); Bitmap bitmap = null; //生成视频封面图 MediaMetadataRetriever mmr = null; try { mmr = new MediaMetadataRetriever(); mmr.setDataSource(mVideoPath); bitmap = mmr.getFrameAtTime(0, MediaMetadataRetriever.OPTION_CLOSEST); } catch (Exception e) { bitmap = null; e.printStackTrace(); } finally { if (mmr != null) { mmr.release(); } } if (bitmap == null) { ToastUtil.show(R.string.video_cover_img_failed); onFailed(); return; } final String coverImagePath = mVideoPath.replace(".mp4", ".jpg"); File imageFile = new File(coverImagePath); FileOutputStream fos = null; try { fos = new FileOutputStream(imageFile); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos); fos.flush(); fos.close(); } catch (Exception e) { imageFile = null; e.printStackTrace(); } finally { if (fos != null) { try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } if (bitmap != null) { bitmap.recycle(); } if (imageFile == null) { ToastUtil.show(R.string.video_cover_img_failed); onFailed(); return; } final File finalImageFile = imageFile; //用鲁班压缩图片 Luban.with(this) .load(finalImageFile) .setFocusAlpha(false) .ignoreBy(8)//8k以下不压缩 .setTargetDir(CommonAppConfig.VIDEO_PATH) .setRenameListener(new OnRenameListener() { @Override public String rename(String filePath) { filePath = filePath.substring(filePath.lastIndexOf("/") + 1); return filePath.replace(".jpg", "_c.jpg"); } }) .setCompressListener(new OnCompressListener() { @Override public void onStart() { } @Override public void onSuccess(File file) { if (!finalImageFile.getAbsolutePath().equals(file.getAbsolutePath()) && finalImageFile.exists()) { finalImageFile.delete(); } uploadVideoFile(file); } @Override public void onError(Throwable e) { uploadVideoFile(finalImageFile); } }).launch(); } private void onFailed() { if (mLoading != null) { mLoading.dismiss(); } if (mBtnPub != null) { mBtnPub.setEnabled(true); } } /** * 上传封面图片 */ private void uploadVideoFile(File imageFile) { if (mConfigBean.getVideoCloudType() == 1) { mUploadStrategy = new VideoUploadQnImpl(mConfigBean); } else { mUploadStrategy = new VideoUploadTxImpl(mConfigBean); } VideoUploadBean videoUploadBean = new VideoUploadBean(new File(mVideoPath), imageFile); if (!TextUtils.isEmpty(mVideoPathWater)) { File waterFile = new File(mVideoPathWater); if (waterFile.exists()) { videoUploadBean.setVideoWaterFile(waterFile); } } mUploadStrategy.upload(videoUploadBean, new VideoUploadCallback() { @Override public void onSuccess(VideoUploadBean bean) { if (mSaveType == Constants.VIDEO_SAVE_PUB) { bean.deleteFile(); } saveUploadVideoInfo(bean); } @Override public void onFailure() { ToastUtil.show(R.string.video_pub_failed); onFailed(); } }); } /** * 把视频上传后的信息保存在服务器 */ private void saveUploadVideoInfo(VideoUploadBean bean) { VideoHttpUtil.saveUploadVideoInfo( mVideoTitle, bean.getResultImageUrl(), bean.getResultVideoUrl(), bean.getResultWaterVideoUrl(), mGoodsId, mMusicId, mCheckBox != null && mCheckBox.isChecked(), mVideoClassId, mGoodsType, new HttpCallback() { @Override public void onSuccess(int code, String msg, String[] info) { if (code == 0) { if (info.length > 0) { if (mConfigBean != null && mConfigBean.getVideoAuditSwitch() == 1) { ToastUtil.show(R.string.video_pub_success_2); } else { ToastUtil.show(R.string.video_pub_success); } if (mAdapter != null) { String shareType = mAdapter.getShareType(); if (shareType != null) { JSONObject obj = JSON.parseObject(info[0]); shareVideoPage(shareType, obj.getString("id"), obj.getString("title"), obj.getString("thumb_s")); } } finish(); } } else { ToastUtil.show(msg); if (mBtnPub != null) { mBtnPub.setEnabled(true); } } } @Override public void onFinish() { if (mLoading != null) { mLoading.dismiss(); } } }); } /** * 分享页面链接 */ public void shareVideoPage(String shareType, String videoId, String videoTitle, String videoImageUrl) { ShareData data = new ShareData(); String shareVideoTitle = mConfigBean.getVideoShareTitle(); if (!TextUtils.isEmpty(shareVideoTitle) && shareVideoTitle.contains("{username}")) { shareVideoTitle = shareVideoTitle.replace("{username}", CommonAppConfig.getInstance().getUserBean().getUserNiceName()); } data.setTitle(shareVideoTitle); if (TextUtils.isEmpty(videoTitle)) { data.setDes(mConfigBean.getVideoShareDes()); } else { data.setDes(videoTitle); } data.setImgUrl(videoImageUrl); String webUrl = HtmlConfig.SHARE_VIDEO + videoId; data.setWebUrl(webUrl); if (mMobShareUtil == null) { mMobShareUtil = new MobShareUtil(); } mMobShareUtil.execute(shareType, data, null); } }这样就实现了短视频源码的功能。 目前直播行业竞争激烈,只有优质的产品,才能利于不败之地,云豹直播让您梦想成真!