阿里云直播点播

最近一段时间比较忙,没更新。今天项目发版,把这一期做的项目做个总结。今天讲一下直播服务吧。

        目前除了自己开发,市面上的提供直播服务的主要有阿里云,乐视云,还有展视互动。经过前期的综合考虑,还有成本原因,决定采用购买阿里云的直播录播业务。(展示互动好像是目前做的比较好的,但是收费稍贵。)

        因为前期的业务并不是很复杂,将阿里云直播点播的文档看了一遍,很多也很全,但是总感觉有些杂乱。试了下demo,很简单,最后自己动手还是发现了一些坑。

        由于我们的业务不是很复杂,主要还是调阿里云的回调和SDK(SDK是封装了的API),其实后来SDK我们也很少用到,用到的地方是获取录播视频时长。其实SDK是很丰富的,我们业务比较简单,所以用到的少。

        说下工时吧,差不多两星期,一个星期熟悉文档,一个星期开发完成,后期BUG修复。

        现在详细说下主要流程:其实就是一个推流,一个播流的过程。这其中有三个回调:

中心推流回调URL:http://XXX:8082/videoLiveCallBack/pushCallBack 

录制回调 URL: http://XXX:8082/videoLiveCallBack/recordCallBack

视频点播回调URL:http://XXX:8082/videoLiveCallBack/playCallBack 

 

  中心推流回调阿里会监听推流状态,然后返回状态。

  录制回调返回录制状态

  视频点播回调返回点播的相关状态。(例如转码状态)

 

 直接上代码吧:(参考了前辈网上文章,非常感谢同行的无私分享)

第一步:  是后天新增直播,生成推流地址:

 

/**

 * addVideoLive:新增直播

 * 

 * @param dto

 * @return

 * @throws Exception

 * @exception

 * @author Ken

 * @date 2018年5月18日 下午3:36:19

 */

@RequestMapping(value = "/add")

@ResponseBody

public Json add(VideoLiveDto videoLiveDto) throws Exception {

Json j = new Json();

// 拼接直播推流地址

// Example:

// 推流地址(鉴权后的)

// rtmp://video-center.alivecdn.com/AppName/StreamName?vhost=live.i-yuwen.com&auth_key=1529051804-0-0-3339f3cb647bad0fc60882d058f9b5d9

// 播放地址:

// rtmp://live.i-yuwen.com/AppName/StreamName?auth_key=1529054783-0-0-da50433e403d8b5856143af8bb5ea1f9

// http://live.i-yuwen.com/AppName/StreamName.flv?auth_key=1529054783-0-0-899cbdd4f759860b46eb621555692847

// http://live.i-yuwen.com/AppName/StreamName.m3u8?auth_key=1529054783-0-0-f4a347b2e04d15f006d66be488725487

String videoLiveId = UUIDGenerator.getUUID32Bit();

videoLiveDto.setId(videoLiveId);

// 传入自定义参数,即传入应用名称和流名称

String appName = GlobalUtils.getConfig(DataStatus.LIVE_APP_NAME);

 

String streamName = "";

if (videoLiveDto != null && StringUtils.isNotEmpty(videoLiveDto.getTeacherId())) {

streamName = GlobalUtils.getConfig(DataStatus.LIVE_STREAM_NAME) + DataStatus.UNDERLINE + DataStatus.STUDENT + DataStatus.UNDERLINE + videoLiveId;

}

String vhost = GlobalUtils.getConfig(DataStatus.LIVE_VHOST_NAME);

// 时间戳,有效时间

// long time = System.currentTimeMillis() + 1800

// long time = (System.currentTimeMillis() + 30 * 60 * 1000)/1000;

//默认开播时间往后30分钟

long time = (videoLiveDto.getVideoStartTime().getTime() + 30 * 60 * 1000) / 1000;

// 加密key,即直播后台鉴权里面自行设置

String key = GlobalUtils.getConfig(DataStatus.LIVE_KEY_NAME);

 

String strPush = DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + DataStatus.IN_THE_LINE + time + DataStatus.SPLICING_OPERATOR + key;

// 拼接推流地址

// String pushAddr = "rtmp://video-center.alivecdn.com" + DataStatus.BACKSLASH + appName

// + DataStatus.BACKSLASH + streamName + "?vhost=" + vhost;

String pushAddr = "rtmp://video-center.alivecdn.com/" + appName + DataStatus.BACKSLASH + streamName + "?vhost=" + vhost

+ "&auth_key=" + time + DataStatus.SPLICING_OPERATOR + MD5.md5(strPush);

 

// 拼接播放地址

// String playAddr = "rtmp://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH +

// streamName;

// String flvAddr = "rtmp://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName

// + ".flv";

// String m3u8Addr = "rtmp://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH +

// streamName + ".m3u8";

 

// 原画播放地址:

String playMd5 = DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + DataStatus.IN_THE_LINE + time + DataStatus.SPLICING_OPERATOR + key;

String flvMd5 = DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + ".flv-" + time + DataStatus.SPLICING_OPERATOR + key;

String m3u8Md5 = DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + ".m3u8-" + time + DataStatus.SPLICING_OPERATOR + key;

String playAddr = "rtmp://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "?auth_key=" + time + DataStatus.SPLICING_OPERATOR

+ MD5.md5(playMd5);

String flvAddr = "http://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + ".flv?auth_key=" + time + DataStatus.SPLICING_OPERATOR

+ MD5.md5(flvMd5);

String m3u8Addr = "http://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + ".m3u8?auth_key=" + time + DataStatus.SPLICING_OPERATOR

+ MD5.md5(m3u8Md5);

 

logger.info("推流地址pushAddr==>" + pushAddr);

logger.info("原画播放地址playAddr==>" + playAddr);

logger.info("原画播放地址flvAddr==>" + flvAddr);

logger.info("原画播放地址m3u8Addr==>" + m3u8Addr);

// 流畅(LLD):RTMP 格式;FLV 格式

String playLldRtmpMd5 = DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lld-" + time + DataStatus.SPLICING_OPERATOR + key;

String playLldFlvMd5 = DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lld.flv-" + time + DataStatus.SPLICING_OPERATOR + key;

String playLldRtmp = "rtmp://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lld?auth_key=" + time + DataStatus.SPLICING_OPERATOR

+ MD5.md5(playLldRtmpMd5);

String playLldFlv = "http://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lld.flv?auth_key=" + time

+ DataStatus.SPLICING_OPERATOR + MD5.md5(playLldFlvMd5);

System.out.println("流畅播放地址playLldRtmp==" + playLldRtmp);

System.out.println("流畅播放地址playLldFlv==" + playLldFlv);

logger.info("流畅播放地址playLldRtmp==>" + playLldRtmp);

logger.info("流畅播放地址playLldFlv==>" + playLldFlv);

// 标清(LSD):RTMP 格式;FLV 格式

String playLsdRtmpMd5 = DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lsd-" + time + DataStatus.SPLICING_OPERATOR + key;

String playLsdFlvMd5 = DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lsd.flv-" + time + DataStatus.SPLICING_OPERATOR + key;

String playLsdRtmp = "rtmp://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lsd?auth_key=" + time + DataStatus.SPLICING_OPERATOR

+ MD5.md5(playLsdRtmpMd5);

String playLsdFlv = "http://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lsd.flv?auth_key=" + time

+ DataStatus.SPLICING_OPERATOR + MD5.md5(playLsdFlvMd5);

 

// 高清(LHD):RTMP 格式;FLV 格式

String playLhdRtmpMd5 = DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lhd-" + time + DataStatus.SPLICING_OPERATOR + key;

String playLhdFlvMd5 = DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lhd.flv-" + time + DataStatus.SPLICING_OPERATOR + key;

String playLhdRtmp = "rtmp://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lhd?auth_key=" + time + DataStatus.SPLICING_OPERATOR

+ MD5.md5(playLhdRtmpMd5);

String playLhdFlv = "http://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lhd.flv?auth_key=" + time

+ DataStatus.SPLICING_OPERATOR + MD5.md5(playLhdFlvMd5);

// 超清(LUD):RTMP 格式;FLV 格式

String playLudRtmpMd5 = DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lud-" + time + DataStatus.SPLICING_OPERATOR + key;

String playLudFlvMd5 = DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lud.flv-" + time + DataStatus.SPLICING_OPERATOR + key;

String playLudRtmp = "rtmp://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lud?auth_key=" + time + DataStatus.SPLICING_OPERATOR

+ MD5.md5(playLudRtmpMd5);

String playLudFlv = "http://" + vhost + DataStatus.BACKSLASH + appName + DataStatus.BACKSLASH + streamName + "_lud.flv?auth_key=" + time

+ DataStatus.SPLICING_OPERATOR + MD5.md5(playLudFlvMd5);

 

// 保存播放地址到数据库

videoLiveDto.setPushAddr(pushAddr);

 

videoLiveDto.setPlayAddr(playAddr);

videoLiveDto.setFlvAddr(flvAddr);

videoLiveDto.setM3u8Addr(m3u8Addr);

 

videoLiveDto.setPlayLldRtmp(playLldRtmp);

videoLiveDto.setPlayLldFlv(playLldFlv);

videoLiveDto.setPlayLsdRtmp(playLsdRtmp);

videoLiveDto.setPlayLsdFlv(playLsdFlv);

videoLiveDto.setPlayLhdRtmp(playLhdRtmp);

videoLiveDto.setPlayLhdFlv(playLhdFlv);

videoLiveDto.setPlayLudRtmp(playLudRtmp);

videoLiveDto.setPlayLudFlv(playLudFlv);

// 默认未开播

videoLiveDto.setLiveStatus(DataStatus.INT_ONE);

// 直播状态:1:未开播,2:直播中.3:直播结束 要求排序为2:直播中 (对应排序:1) => 1:未开播 (对应排序:2) =>

// 3:直播结束 (对应排序:3)

videoLiveDto.setOrderNo(DataStatus.INT_TWO);

 

// 预约人数

Integer virtualAppointmentNum = videoLiveDto.getVirtualAppointmentNum();

if (videoLiveDto != null && virtualAppointmentNum == null) {

Integer appointmentMin = DataStatus.APPOINTMENT_MIN;

Integer appointmentMax = DataStatus.APPOINTMENT_MAX;

Random r = new Random();

virtualAppointmentNum = r.nextInt(appointmentMax - appointmentMin + DataStatus.INT_ONE) + appointmentMin;

videoLiveDto.setVirtualAppointmentNum(virtualAppointmentNum);

}

videoLiveDto.setAppointmentNum(virtualAppointmentNum);

 

Integer flag = videoLiveService.addVideoLive(videoLiveDto);

if (flag > 0) {

// 若isToAll=0,则该授课老师的学员能看到,其他学员不能(班级暂不限定,按理应对应到班级),默认将该老师下学员加到预约,暂时不能取消预约

if (videoLiveDto != null && videoLiveDto.getIsToAll() != null) {

// 班级ID

Integer cid = videoLiveDto.getCid();

ClassStudentDto classStudentDto = new ClassStudentDto();

classStudentDto.setCid(cid);

List<ClassStudentDto> classStudentList = classStudentService

.findClassStudentListByParam(classStudentDto);

if (CollectionUtils.isNotEmpty(classStudentList)) {

// 加入预约

VideoLiveReservationDto videoLiveReservationDto = new VideoLiveReservationDto();

for (ClassStudentDto cs : classStudentList) {

Integer sid = cs.getSid();

if (sid != 0) {

String studentId = studentService.findStudentIdBySid(sid);

if (StringUtils.isNotEmpty(studentId)) {

videoLiveReservationDto.setStudentId(studentId);

}

}

 

// 预约类型(线上:1;线下:2)

videoLiveReservationDto.setReservationType(DataStatus.INT_TWO);

videoLiveReservationDto.setSid(sid);

videoLiveReservationDto.setVideoLiveId(videoLiveId);

videoLiveReservationDto.setVideoLiveName(videoLiveDto.getTitle());

videoLiveReservationService.addVideoLiveReservation(videoLiveReservationDto);

}

 

}

}

 

j.setSuccess(true);

j.setMsg("添加成功");

j.setObj(videoLiveDto);

} else {

j.setSuccess(false);

j.setMsg("添加失败");

}

return j;

}

 

第二步:  app 的三个回调,由于我们业务是教师端和学生端都要开直播。所以这里用到了策略模式(考虑到阿里云回调只能配一个IP)

package com.ygw.app.controller.v2.aliyun;

 

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

 

import org.apache.commons.lang.BooleanUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

 

import com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.JSONObject;

import com.ygw.app.config.version.ApiVersion;

import com.ygw.app.controller.common.BaseController;

import com.ygw.app.service.IVideoLiveReplayService;

import com.ygw.common.app.dto.VideoLiveReplayDto;

import com.ygw.common.teacher.dto.LiveCourseReplayDto;

import com.ygw.thirdparty.dto.aliyun.CallBackDto;

import com.ygw.thirdparty.service.impl.aliyun.LiveCallBackContext;

import com.ygw.thirdparty.service.teacher.ILiveCourseReplayService;

import com.ygw.utils.AliUtil;

import com.ygw.utils.StringUtils;

import com.ygw.utils.constants.Consts;

import com.ygw.utils.constants.DataStatus;

import com.ygw.utils.constants.VideoCallBackType;

import com.ygw.utils.token.Luna;

 

/**

 * 

 * <p>Title: VideoLiveCallBackController </p>

 * <p>Description: 视频直播点播回调接口控制器(策略模式)</p>

 * <p>Copyright (c) 2018 </p>

 * <p>Company: 上海阳光喔教育科技有限公司</p>

 * @author lb

 * @date 2018年7月19日 下午2:52:44

 * @version 1.0

 * <p>修改人:Administrator</p>

 * <p>修改时间:2018年7月19日 下午2:52:44</p>

 * <p>修改备注:</p>

 */

@ApiVersion(1.2)

@RestController("VideoLiveBackController-v1.2")

@RequestMapping(value = "/v1.2/videoLiveBack")

public class VideoLiveBackController extends BaseController {

 

@Autowired

private LiveCallBackContext callBackContext;

 

@Autowired

private IVideoLiveReplayService videoLiveReplayService;

 

@Autowired

private ILiveCourseReplayService liveCourseReplayService;

 

@RequestMapping(value = "/pushBack")

@Luna

public void pushCallBack(CallBackDto callBackDto, HttpServletRequest request, HttpServletResponse response)

throws Exception {

logger.info("=======================access  pushBack=======================");

logger.info("pushBack入参: " + callBackDto);

String callBackType = callBackDto.getId().split("_")[1];

String videoLiveId = callBackDto.getId().split("_")[2];

callBackDto.setVideoLiveId(videoLiveId);

switch (callBackType) {

case Consts.STUDENT_CALL_BACK:// 学生端回调

callBackContext.contextPushCallBack(VideoCallBackType.STUDENT.getValue(), callBackDto);

break;

case Consts.TEACHER_CALL_BACK:// 老师端回调

callBackContext.contextPushCallBack(VideoCallBackType.TEACHER.getValue(), callBackDto);

break;

}

logger.info("=======================out  pushBack=======================");

AliUtil.response(request, response, DataStatus.ALIYUN_STATUS_OK, HttpServletResponse.SC_OK);

}

 

@RequestMapping(value = "/recordBack")

@Luna

public void recordCallBack(HttpServletRequest request, HttpServletResponse response) throws Exception {

logger.info("=======================access  recordBack=======================");

// 解析阿里回调服务器的接口数据

String result = AliUtil.GetPostBody(request.getInputStream(),

Integer.parseInt(request.getHeader("content-length")));

JSONObject jsonObject = JSON.parseObject(result);

logger.info("jsonObject: " + jsonObject);

Boolean isFinished = jsonObject.getBoolean("is_finished");

String stream = jsonObject.getString("stream");

if (BooleanUtils.isNotTrue(isFinished) || StringUtils.isEmpty(stream)) {// 没有完成

AliUtil.response(request, response, DataStatus.ALIYUN_STATUS_NOT_OK, HttpServletResponse.SC_BAD_REQUEST);

return;

}

CallBackDto callBackDto = new CallBackDto();

callBackDto.setStreamName(stream);

// 保存录播信息

String callBackType = stream.split("_")[1];

switch (callBackType) {

case Consts.STUDENT_CALL_BACK:// 学生端回调

callBackContext.recordCallBack(VideoCallBackType.STUDENT.getValue(), ObjectCovert(jsonObject, callBackDto));

break;

case Consts.TEACHER_CALL_BACK:// 老师端回调

callBackContext.recordCallBack(VideoCallBackType.TEACHER.getValue(), ObjectCovert(jsonObject, callBackDto));

break;

}

logger.info("=======================out  recordBack=======================");

AliUtil.response(request, response, DataStatus.ALIYUN_STATUS_OK, HttpServletResponse.SC_OK);

}

 

@RequestMapping(value = "/playBack")

@Luna

public void playCallBack(HttpServletRequest request, HttpServletResponse response) throws Exception {

logger.info("=======================access  playBack=======================");

String c_length = request.getHeader("content-length");

int c_i = Integer.parseInt(c_length);

// 解析阿里回调服务器的接口数据

String result = AliUtil.GetPostBody(request.getInputStream(), c_i);

logger.info("playCallBack==" + result);

 

JSONObject jsonObject = JSON.parseObject(result);

VideoLiveReplayDto replayDto = JSON.toJavaObject(jsonObject, VideoLiveReplayDto.class);

String status = jsonObject.getString("Status");

 

/**

 * 五种状态: 1.视频上传完成 EventType String 事件类型,固定为FileUploadComplete

 * 2.单个清晰度转码完成 事件类型,固定为StreamTranscodeComplete 3.视频转码完成

 * 事件类型,固定为TranscodeComplete 4.视频截图完成 事件类型,固定为SnapshotComplete

 * 5.直转点视频录制完成 事件类型,固定为AddLiveRecordVideoComplete 6.直转点录制视频合成开始

 * 事件类型,固定为LiveRecordVideoComposeStart

 */

 

if (!DataStatus.HTTP_STATE_SUCCESS.equals(status)) {

AliUtil.response(request, response, "{\"Status\":\"verdify not ok\"}", HttpServletResponse.SC_BAD_REQUEST);

}

 

String streamName = jsonObject.getString("StreamName");

 

String callBackType = "";

String videoLiveId = "";

if (StringUtils.isNotEmpty(streamName)) {

callBackType = streamName.split("_")[1];

videoLiveId = streamName.split("_")[2];

} else {

// 根据VideoId判断callBackType 状态

String videoId = jsonObject.getString("VideoId");

VideoLiveReplayDto videoLiveReplayDto = videoLiveReplayService.findByVideoId(videoId);

if (videoLiveReplayDto != null) {

callBackType = Consts.STUDENT_CALL_BACK;

}

 

LiveCourseReplayDto liveCourseReplayDto = liveCourseReplayService.findByVideoId(videoId);

if (liveCourseReplayDto != null) {

callBackType = Consts.TEACHER_CALL_BACK;

}

}

switch (callBackType) {

case Consts.STUDENT_CALL_BACK:// 学生端回调

replayDto.setVideoLiveId(videoLiveId);

callBackContext.playCallBack(VideoCallBackType.STUDENT.getValue(), replayDto, jsonObject);

break;

case Consts.TEACHER_CALL_BACK:// 老师端回调

replayDto.setLiveCourseId(videoLiveId);

callBackContext.playCallBack(VideoCallBackType.TEACHER.getValue(), replayDto, jsonObject);

break;

}

 

AliUtil.response(request, response, DataStatus.ALIYUN_STATUS_OK, HttpServletResponse.SC_OK);

}

 

private CallBackDto ObjectCovert(JSONObject jsonObject, CallBackDto callBackDto) {

String videoLiveId = jsonObject.getString("stream").split("_")[2];

// 录制域名

callBackDto.setDomainName(jsonObject.getString("domain"));

// 应用名

callBackDto.setAppName(jsonObject.getString("app"));

// 目标录制文件在用户录制 OSS Bucket 下的路径

callBackDto.setUri(jsonObject.getString("uri"));

callBackDto.setRecordDuration(String.valueOf(jsonObject.getDouble("duration")));

callBackDto.setOssEndpoint(jsonObject.getString("oss_endpoint"));

callBackDto.setOssBucket(jsonObject.getString("oss_bucket"));

callBackDto.setVideoLiveId(videoLiveId);

callBackDto.setLiveCourseId(videoLiveId);

return callBackDto;

}

}

 

 

 

 

 

/**

 * <p>Title: LiveCallBackContext </p>

 * <p>Description: 直播回调容器类</p>

 * <p>Copyright (c) 2018 </p>

 * <p>Company: 上海阳光喔教育科技有限公司</p>

 * @author lb

 * @date 2018年7月19日 下午1:10:04

 * @version 1.0

 * <p>修改人:Administrator</p>

 * <p>修改时间:2018年7月19日 下午1:10:04</p>

 * <p>修改备注:</p>

 */

@Component

public class LiveCallBackContext {

 

@Autowired

private final Map<String, LiveCallBackStrategy> contextStrategy = new ConcurrentHashMap<>();

 

public Map<String, LiveCallBackStrategy> getContextStrategy() {

return contextStrategy;

}

 

@Autowired

public void setContextStrategy(Map<String, LiveCallBackStrategy> contextStrategy) {

this.contextStrategy.clear();

contextStrategy.forEach((k, v) -> this.contextStrategy.put(k, v));

}

 

/**

 * 直播推流回调策略方法

 */

public void contextPushCallBack(String type, CallBackDto callBackDto) throws Exception {

this.contextStrategy.get(type).pushCallBack(callBackDto);

}

 

/**

 * 直播录制回调策略方法

 */

public void recordCallBack(String type, CallBackDto callBackDto) throws Exception {

this.contextStrategy.get(type).recordCallBack(callBackDto);

}

 

/**     

 * playCallBack:点播回调

 * @param replayDto

 * @exception

 * @author lb

 * @date 2018年7月19日 下午6:21:06  

 */

public void playCallBack(String type, VideoLiveReplayDto replayDto, JSONObject jsonObject) throws Exception {

this.contextStrategy.get(type).playCallBack(replayDto, jsonObject);

}

 

}

 

 

 

package com.ygw.thirdparty.service.impl.aliyun;

 

 

/**

 * <p>Title: StudentLiveCallBackStrategy </p>

 * <p>Description: 类描述</p>

 * <p>Copyright (c) 2018 </p>

 * <p>Company: 上海阳光喔教育科技有限公司</p>

 * @author Administrator

 * @date 2018年7月19日 下午1:08:55

 * @version 1.0

 * <p>修改人:Administrator</p>

 * <p>修改时间:2018年7月19日 下午1:08:55</p>

 * <p>修改备注:</p>

 */

@Service

public class StudentLiveCallBackStrategy implements LiveCallBackStrategy {

 

protected Logger logger = Logger.getLogger(StudentLiveCallBackStrategy.class);

 

@Autowired

private IVideoLiveService videoLiveService;

@Autowired

private IVideoLiveReplayService videoLiveReplayService;

 

@Override

public void pushCallBack(CallBackDto callBackDto) throws Exception {

// 直播状态:1:未开播,2:直播中.3:直播结束

int liveStatus = DataStatus.INT_ONE;

if (DataStatus.PUBLISH.equals(callBackDto.getAction())) {

liveStatus = DataStatus.INT_TWO;

callBackDto.setOrderNo(DataStatus.INT_ONE);

} else if (DataStatus.PUBLISH_DONE.equals(callBackDto.getAction())) {

liveStatus = DataStatus.INT_THREE;

callBackDto.setOrderNo(DataStatus.INT_THREE);

// 直播结束时间

Long now = System.currentTimeMillis();

callBackDto.setVideoEndTime(new Date(now));

}

callBackDto.setLiveStatus(liveStatus);

VideoLiveDto videoLiveDto = new VideoLiveDto();

BeanUtils.copyProperties(callBackDto, videoLiveDto);

videoLiveDto.setId(callBackDto.getVideoLiveId());// 放入直播id

videoLiveService.editVideoLive(videoLiveDto);

}

 

@Override

public void recordCallBack(CallBackDto callBackDto) throws Exception {

VideoLiveDto videoLiveDto = videoLiveService.findById(callBackDto.getVideoLiveId());

if (ObjectUtils.isEmpty(videoLiveDto)) {

return;

}

VideoLiveReplayDto replayDto = BeanUtils.createBeanByTarget(videoLiveDto, VideoLiveReplayDto.class);

BeanUtils.copyProperties(callBackDto, replayDto);

videoLiveReplayService.addVideoLiveReplay(replayDto);

}

 

@Override

public void playCallBack(VideoLiveReplayDto replayDto,JSONObject jsonObject) throws Exception {

logger.info("replayDto = " + replayDto);

switch (replayDto.getEventType()) {

// 第一种情况,直转点视频录制完成

case DataStatus.ADD_LIVE_RECORD_VIDEO_COMPLETE:

VideoLiveReplayDto dto = new VideoLiveReplayDto();

dto.setVideoLiveId(replayDto.getVideoLiveId());

List<VideoLiveReplayDto> videoLiveReplayList = videoLiveReplayService

.findVideoLiveReplayListByParam(dto);

if (CollectionUtils.isNotEmpty(videoLiveReplayList)) {

VideoLiveReplayDto videoLiveReplayDto = videoLiveReplayList.get(0);

videoLiveReplayDto.setEventType(DataStatus.ADD_LIVE_RECORD_VIDEO_COMPLETE);

videoLiveReplayDto.setVideoType(DataStatus.INT_FOUR);

videoLiveReplayDto.setVideoId(replayDto.getVideoId());

videoLiveReplayService.editVideoLiveReplay(videoLiveReplayDto);

}

break;

 

// 第二种情况,直播录制转点播成功

case DataStatus.TRANSCODE_COMPLETE:

String videoId = jsonObject.getString("VideoId");

JSONArray streamInfos = jsonObject.getJSONArray("StreamInfos");

logger.info("videoId==" + videoId);

logger.info("streamInfos==" + streamInfos);

VideoLiveReplayDto dtos = new VideoLiveReplayDto();

dtos.setVideoId(videoId);

List<VideoLiveReplayDto> videoLiveReplayLists = videoLiveReplayService

.findVideoLiveReplayListByParam(dtos);

logger.info("videoLiveReplayList==" + videoLiveReplayLists);

if (CollectionUtils.isNotEmpty(videoLiveReplayLists)) {

VideoLiveReplayDto videoLiveReplayDto = videoLiveReplayLists.get(0);

videoLiveReplayDto.setEventType(DataStatus.TRANSCODE_COMPLETE);

videoLiveReplayDto.setVideoType(DataStatus.INT_FIVE);

 

if (streamInfos != null && streamInfos.size() > 0) {

for (int i = 0; i < streamInfos.size(); i++) {

String definition = streamInfos.getJSONObject(i)

.getString("Definition");

String fileUrl = streamInfos.getJSONObject(i)

.getString("FileUrl");

String format = streamInfos.getJSONObject(i)

.getString("Format");

if (DataStatus.DEFINITION_LD.equals(definition) && DataStatus.FORMAT_MP4.equals(format)) {

videoLiveReplayDto.setMp4Ld(fileUrl);

} else if (DataStatus.DEFINITION_LD.equals(definition) && DataStatus.FORMAT_M3U8.equals(format)) {

videoLiveReplayDto.setM3u8Ld(fileUrl);

} else if (DataStatus.DEFINITION_SD.equals(definition) && DataStatus.FORMAT_MP4.equals(format)) {

videoLiveReplayDto.setMp4Sd(fileUrl);

} else if (DataStatus.DEFINITION_SD.equals(definition) && DataStatus.FORMAT_M3U8.equals(format)) {

videoLiveReplayDto.setM3u8Sd(fileUrl);

} else if (DataStatus.DEFINITION_HD.equals(definition) && DataStatus.FORMAT_MP4.equals(format)) {

videoLiveReplayDto.setMp4Hd(fileUrl);

} else if (DataStatus.DEFINITION_HD.equals(definition) && DataStatus.FORMAT_M3U8.equals(format)) {

videoLiveReplayDto.setM3u8Hd(fileUrl);

}

}

videoLiveReplayService.editVideoLiveReplay(videoLiveReplayDto);

}

break;

}

}

}

 

 

这样就简单实现了阿里云的直播服务,可以在后台新建直播直播吃鸡啦。

上面第二个何地三个回调会有点小坑。第三个回调会调 很多次转码,调的时候需要注意阿里返回参数。

    下面简单说下调阿里云SDK,这个看起来比较麻烦,里面方法很多,找到你需要的调用即可,下面举个例子(获取通知在线人数,这个结果大概有三秒钟的延迟):

 

IClientProfile profile = DefaultProfile.getProfile(DataStatus.ALIYUN_ZONE, DataStatus.ALIYUN_ACCESSKEY, DataStatus.ALIYUN_ACCESSSECRET);

        IAcsClient client = new DefaultAcsClient(profile);

DescribeLiveStreamOnlineUserNumRequest describeLiveStreamOnlineUserNumRequest = new DescribeLiveStreamOnlineUserNumRequest();

describeLiveStreamOnlineUserNumRequest.setDomainName(DataStatus.ALIYUN_DO_MAIN);

describeLiveStreamOnlineUserNumRequest.setAppName(DataStatus.ALIYUN_APP_NAME);

String streamName = DataStatus.YGWO_STR + DataStatus.UNDERLINE + DataStatus.TEACHER + DataStatus.UNDERLINE + dto.getId();

describeLiveStreamOnlineUserNumRequest.setStreamName(streamName);

try {

DescribeLiveStreamOnlineUserNumResponse response = client.getAcsResponse(describeLiveStreamOnlineUserNumRequest);

dto.setTotalUserNumber(response.getTotalUserNumber());

} catch (ClientException e) {

e.printStackTrace();

}

 

 

 

最后有什么问题可以咨询我,邮箱:jamhihi@126.com        QQ:841973440

感谢同行的无私分享,让我们一起学习,共同进步。

最后说下,有啥问题可以在阿里云提工单,一般都能解决,就是反馈有点慢。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值