You've already forked FrameTour-BE
feat(video): 添加移动端HLS视频流播放列表生成功能
- 实现AppVideoStreamController提供HLS播放列表生成接口 - 添加HlsStreamRequest和HlsStreamResponse数据传输对象 - 实现HlsStreamService服务类处理视频流逻辑 - 支持生成JSON格式和m3u8文件格式的播放列表 - 提供视频片段查询和设备视频HLS播放列表生成功能 - 支持EVENT和VOD两种播放列表类型 - 集成设备存储操作器获取视频文件列表 - 实现播放列表内容构建和视频片段时长计算功能
This commit is contained in:
@@ -0,0 +1,150 @@
|
||||
package com.ycwl.basic.controller.mobile.manage;
|
||||
|
||||
import com.ycwl.basic.annotation.IgnoreToken;
|
||||
import com.ycwl.basic.model.mobile.video.dto.HlsStreamRequest;
|
||||
import com.ycwl.basic.model.mobile.video.dto.HlsStreamResponse;
|
||||
import com.ycwl.basic.service.mobile.HlsStreamService;
|
||||
import com.ycwl.basic.utils.ApiResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* 移动端视频流控制器
|
||||
* 提供HLS视频流播放列表生成功能
|
||||
*
|
||||
* @author Claude Code
|
||||
* @date 2025-12-26
|
||||
*/
|
||||
@Slf4j
|
||||
@RestController
|
||||
@RequestMapping("/api/mobile/video-stream/v1")
|
||||
@RequiredArgsConstructor
|
||||
public class AppVideoStreamController {
|
||||
|
||||
private final HlsStreamService hlsStreamService;
|
||||
|
||||
/**
|
||||
* 生成设备视频的HLS播放列表(JSON格式)
|
||||
* 返回包含m3u8内容和视频片段信息的JSON对象
|
||||
*
|
||||
* @param request HLS流请求参数
|
||||
* @return HLS播放列表响应
|
||||
*/
|
||||
@PostMapping("/hls/playlist")
|
||||
public ApiResponse<HlsStreamResponse> generateHlsPlaylist(@Validated @RequestBody HlsStreamRequest request) {
|
||||
log.info("收到HLS播放列表生成请求: deviceId={}, durationMinutes={}",
|
||||
request.getDeviceId(), request.getDurationMinutes());
|
||||
|
||||
try {
|
||||
HlsStreamResponse response = hlsStreamService.generateHlsPlaylist(request);
|
||||
log.info("HLS播放列表生成成功: deviceId={}, segmentCount={}, totalDuration={}s",
|
||||
response.getDeviceId(), response.getSegmentCount(), response.getTotalDurationSeconds());
|
||||
|
||||
return ApiResponse.success(response);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("生成HLS播放列表失败: deviceId={}", request.getDeviceId(), e);
|
||||
return ApiResponse.buildResponse(500, null, "生成失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成设备视频的HLS播放列表(m3u8文件格式)
|
||||
* 直接返回m3u8文件内容,可被视频播放器直接使用
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param durationMinutes 视频时长(分钟),默认2分钟
|
||||
* @param eventPlaylist 是否为Event播放列表,默认true
|
||||
* @param response HTTP响应对象
|
||||
*/
|
||||
@GetMapping("/hls/playlist.m3u8")
|
||||
@IgnoreToken
|
||||
public void generateHlsPlaylistFile(
|
||||
@RequestParam Long deviceId,
|
||||
@RequestParam(defaultValue = "2") Integer durationMinutes,
|
||||
@RequestParam(defaultValue = "true") Boolean eventPlaylist,
|
||||
HttpServletResponse response) {
|
||||
|
||||
log.info("收到m3u8文件生成请求: deviceId={}, durationMinutes={}",
|
||||
deviceId, durationMinutes);
|
||||
|
||||
try {
|
||||
// 构建请求参数
|
||||
HlsStreamRequest request = new HlsStreamRequest();
|
||||
request.setDeviceId(deviceId);
|
||||
request.setDurationMinutes(durationMinutes);
|
||||
request.setEventPlaylist(eventPlaylist);
|
||||
|
||||
// 生成播放列表
|
||||
HlsStreamResponse hlsResponse = hlsStreamService.generateHlsPlaylist(request);
|
||||
|
||||
log.info("m3u8文件生成成功: deviceId={}, segmentCount={}, totalDuration={}s",
|
||||
deviceId, hlsResponse.getSegmentCount(), hlsResponse.getTotalDurationSeconds());
|
||||
|
||||
// 设置响应头
|
||||
response.setContentType("application/vnd.apple.mpegurl");
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
response.setHeader("Content-Disposition", "inline; filename=\"playlist.m3u8\"");
|
||||
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
||||
response.setHeader("Pragma", "no-cache");
|
||||
response.setHeader("Expires", "0");
|
||||
|
||||
// 写入m3u8内容
|
||||
response.getWriter().write(hlsResponse.getPlaylistContent());
|
||||
response.getWriter().flush();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("生成m3u8文件失败: deviceId={}", deviceId, e);
|
||||
try {
|
||||
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setCharacterEncoding(StandardCharsets.UTF_8.name());
|
||||
response.getWriter().write("{\"code\":500,\"message\":\"生成失败: " + e.getMessage() + "\"}");
|
||||
response.getWriter().flush();
|
||||
} catch (IOException ioException) {
|
||||
log.error("写入错误响应失败", ioException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备最近的视频片段信息
|
||||
* 仅返回视频片段列表,不包含m3u8内容
|
||||
*
|
||||
* @param deviceId 设备ID
|
||||
* @param durationMinutes 视频时长(分钟),默认2分钟
|
||||
* @return 视频片段列表
|
||||
*/
|
||||
@GetMapping("/segments")
|
||||
public ApiResponse<HlsStreamResponse> getVideoSegments(
|
||||
@RequestParam Long deviceId,
|
||||
@RequestParam(defaultValue = "2") Integer durationMinutes) {
|
||||
|
||||
log.info("收到视频片段查询请求: deviceId={}, durationMinutes={}",
|
||||
deviceId, durationMinutes);
|
||||
|
||||
try {
|
||||
HlsStreamRequest request = new HlsStreamRequest();
|
||||
request.setDeviceId(deviceId);
|
||||
request.setDurationMinutes(durationMinutes);
|
||||
request.setEventPlaylist(true);
|
||||
|
||||
HlsStreamResponse response = hlsStreamService.generateHlsPlaylist(request);
|
||||
log.info("视频片段查询成功: deviceId={}, segmentCount={}",
|
||||
deviceId, response.getSegmentCount());
|
||||
|
||||
return ApiResponse.success(response);
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("查询视频片段失败: deviceId={}", deviceId, e);
|
||||
return ApiResponse.buildResponse(500, null, "查询失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user