live: admin live room list + url host fix + app use backend urls

This commit is contained in:
xiao12feng8 2025-12-23 11:39:57 +08:00
parent 1263e00bfd
commit 7094cbccbc
7 changed files with 189 additions and 127 deletions

View File

@ -82,7 +82,7 @@ export function roomBackgroundDeleteApi(id) {
// 房间列表 // 房间列表
export function roomListApi(params) { export function roomListApi(params) {
return request({ return request({
url: '/admin/room/list', url: '/admin/room/live/list',
method: 'get', method: 'get',
params params
}) })

View File

@ -7,16 +7,13 @@
<!-- 搜索表单 --> <!-- 搜索表单 -->
<el-form inline size="small" :model="searchForm" class="mb20 search-form-inline"> <el-form inline size="small" :model="searchForm" class="mb20 search-form-inline">
<el-form-item> <el-form-item>
<el-input v-model="searchForm.ownerNickname" placeholder="请输入主名称" clearable style="width: 150px" /> <el-input v-model="searchForm.streamerName" placeholder="请输入名称" clearable style="width: 150px" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-input v-model="searchForm.roomName" placeholder="请输入房间名称" clearable style="width: 150px" /> <el-input v-model="searchForm.title" placeholder="请输入直播标题" clearable style="width: 150px" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-input v-model="searchForm.ownerPhone" placeholder="请输入房主电话" clearable style="width: 150px" /> <el-input v-model="searchForm.streamKey" placeholder="请输入 streamKey" clearable style="width: 150px" />
</el-form-item>
<el-form-item>
<el-input v-model="searchForm.roomNo" placeholder="请输入房间号" clearable style="width: 150px" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-date-picker v-model="searchForm.startTime" type="date" placeholder="开始时间" style="width: 150px"></el-date-picker> <el-date-picker v-model="searchForm.startTime" type="date" placeholder="开始时间" style="width: 150px"></el-date-picker>
@ -32,85 +29,28 @@
<!-- 表格 --> <!-- 表格 -->
<el-table :data="tableData" v-loading="loading" border style="width: 100%"> <el-table :data="tableData" v-loading="loading" border style="width: 100%">
<el-table-column prop="id" label="房间ID" width="80" align="center" /> <el-table-column prop="id" label="房间ID" width="80" align="center" />
<el-table-column label="房主头像" width="80" align="center"> <el-table-column prop="title" label="标题" min-width="180" />
<el-table-column prop="streamerName" label="主播" width="120" />
<el-table-column prop="streamKey" label="streamKey" min-width="220" />
<el-table-column label="直播状态" width="100" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-image <span>{{ scope.row.isLive ? '直播中' : '未开播' }}</span>
v-if="scope.row.owner_avatar"
:src="scope.row.owner_avatar"
style="width: 50px; height: 50px; border-radius: 50%"
fit="cover"
></el-image>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="owner_nickname" label="房主名称" width="120" /> <el-table-column prop="createTime" label="创建时间" width="160" align="center" />
<el-table-column prop="owner_phone" label="房主手机号" width="120" /> <el-table-column label="播放地址(FLV)" min-width="260">
<el-table-column prop="live_type" label="房间类型" width="100" align="center" />
<el-table-column prop="notice_type" label="房间公告类型" width="120" align="center" />
<el-table-column prop="category_id" label="编号分类ID" width="120" align="center" />
<el-table-column label="房间背景" width="100" align="center">
<template slot-scope="scope"> <template slot-scope="scope">
<el-image <span>{{ scope.row.streamUrls && scope.row.streamUrls.flv }}</span>
v-if="scope.row.background_image"
:src="scope.row.background_image"
:preview-src-list="[scope.row.background_image]"
style="width: 60px; height: 80px; cursor: pointer"
fit="cover"
></el-image>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="房间主页图" width="100" align="center"> <el-table-column label="播放地址(HLS)" min-width="260">
<template slot-scope="scope"> <template slot-scope="scope">
<el-image <span>{{ scope.row.streamUrls && scope.row.streamUrls.hls }}</span>
v-if="scope.row.room_image"
:src="scope.row.room_image"
:preview-src-list="[scope.row.room_image]"
style="width: 60px; height: 60px; cursor: pointer"
fit="cover"
></el-image>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="room_notice" label="房间公告" width="100" align="center" />
<el-table-column prop="room_slogan" label="房间标语" width="100" align="center" />
<el-table-column prop="blacklist_count" label="黑名单数量" width="120" align="center" />
<el-table-column label="房间公告图" width="100" align="center">
<template slot-scope="scope">
<el-image
v-if="scope.row.notice_image"
:src="scope.row.notice_image"
:preview-src-list="[scope.row.notice_image]"
style="width: 60px; height: 60px; cursor: pointer"
fit="cover"
></el-image>
</template>
</el-table-column>
<el-table-column prop="newCount" label="新增数量" width="100" align="center" />
<el-table-column label="房间状态" width="100" align="center">
<template slot-scope="scope">
<span>{{ scope.row.status === 1 ? '正常' : '关闭' }}</span>
</template>
</el-table-column>
<el-table-column prop="viewCount" label="房间浏览人数" width="120" align="center" />
<el-table-column label="推荐状态" width="100" align="center">
<template slot-scope="scope">
<span>{{ scope.row.is_recommended === 1 ? '已推荐' : '未推荐' }}</span>
</template>
</el-table-column>
<el-table-column prop="owner_nickname" label="房间创建人" width="120" />
<el-table-column prop="owner_phone" label="房间创建人账号" width="140" />
<el-table-column prop="create_time" label="创建时间" width="160" align="center" />
<el-table-column label="操作" width="200" align="center" fixed="right"> <el-table-column label="操作" width="200" align="center" fixed="right">
<template slot-scope="scope"> <template slot-scope="scope">
<el-button type="warning" size="small" @click="handleDetail(scope.row)">详情</el-button> <el-button type="warning" size="small" @click="handleDetail(scope.row)">详情</el-button>
<el-button type="warning" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-dropdown @command="handleMore">
<el-button type="warning" size="small">
更多<i class="el-icon-arrow-down el-icon--right"></i>
</el-button>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item :command="{type: 'action', row: scope.row}">操作1</el-dropdown-item>
<el-dropdown-item :command="{type: 'action2', row: scope.row}">操作2</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -133,13 +73,14 @@
<el-dialog title="详情" :visible.sync="detailVisible" width="700px"> <el-dialog title="详情" :visible.sync="detailVisible" width="700px">
<el-descriptions :column="3" border> <el-descriptions :column="3" border>
<el-descriptions-item label="房间ID">{{ detailData.id }}</el-descriptions-item> <el-descriptions-item label="房间ID">{{ detailData.id }}</el-descriptions-item>
<el-descriptions-item label="房间编号">{{ detailData.roomNo }}</el-descriptions-item> <el-descriptions-item label="标题">{{ detailData.title }}</el-descriptions-item>
<el-descriptions-item label="在线人数">{{ detailData.onlineCount || 0 }}</el-descriptions-item> <el-descriptions-item label="主播">{{ detailData.streamerName }}</el-descriptions-item>
<el-descriptions-item label="房间名字">{{ detailData.roomName }}</el-descriptions-item> <el-descriptions-item label="streamKey">{{ detailData.streamKey }}</el-descriptions-item>
<el-descriptions-item label="房间状态">{{ detailData.status }}</el-descriptions-item> <el-descriptions-item label="直播状态">{{ detailData.isLive ? '直播中' : '未开播' }}</el-descriptions-item>
<el-descriptions-item label="房间创建人ID">{{ detailData.creatorId }}</el-descriptions-item> <el-descriptions-item label="创建时间">{{ detailData.createTime }}</el-descriptions-item>
<el-descriptions-item label="房间创建人账号">{{ detailData.creatorAccount }}</el-descriptions-item> <el-descriptions-item label="RTMP">{{ detailData.streamUrls && detailData.streamUrls.rtmp }}</el-descriptions-item>
<el-descriptions-item label="房间创建人手机号">{{ detailData.creatorPhone }}</el-descriptions-item> <el-descriptions-item label="FLV">{{ detailData.streamUrls && detailData.streamUrls.flv }}</el-descriptions-item>
<el-descriptions-item label="HLS">{{ detailData.streamUrls && detailData.streamUrls.hls }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button @click="detailVisible = false">关闭</el-button> <el-button @click="detailVisible = false">关闭</el-button>
@ -171,17 +112,16 @@
</template> </template>
<script> <script>
import { roomListApi, roomDetailApi, roomUpdateApi } from '@/api/room'; import { roomListApi } from '@/api/room';
export default { export default {
name: 'RoomList', name: 'RoomList',
data() { data() {
return { return {
searchForm: { searchForm: {
ownerNickname: '', title: '',
roomName: '', streamerName: '',
ownerPhone: '', streamKey: '',
roomNo: '',
startTime: '', startTime: '',
endTime: '', endTime: '',
page: 1, page: 1,
@ -209,17 +149,15 @@ export default {
const params = { const params = {
page: this.searchForm.page, page: this.searchForm.page,
limit: this.searchForm.limit, limit: this.searchForm.limit,
ownerNickname: this.searchForm.ownerNickname || undefined, title: this.searchForm.title || undefined,
roomName: this.searchForm.roomName || undefined, streamerName: this.searchForm.streamerName || undefined,
ownerPhone: this.searchForm.ownerPhone || undefined, streamKey: this.searchForm.streamKey || undefined,
roomNo: this.searchForm.roomNo || undefined,
startTime: this.searchForm.startTime || undefined, startTime: this.searchForm.startTime || undefined,
endTime: this.searchForm.endTime || undefined, endTime: this.searchForm.endTime || undefined,
}; };
const res = await roomListApi(params); const res = await roomListApi(params);
// CommonResult<CommonPage<T>> res.data CommonPage this.tableData = (res && res.list) || [];
this.tableData = (res.data && res.data.list) || []; this.total = (res && res.total) || 0;
this.total = (res.data && res.data.total) || 0;
} catch (error) { } catch (error) {
this.$message.error(error.message || '获取列表失败'); this.$message.error(error.message || '获取列表失败');
} finally { } finally {
@ -231,22 +169,8 @@ export default {
this.getList(); this.getList();
}, },
async handleDetail(row) { async handleDetail(row) {
try { this.detailData = row || {};
const res = await roomDetailApi(row.id); this.detailVisible = true;
this.detailData = {
id: res.data.id,
roomNo: res.data.roomCode,
onlineCount: res.data.onlineCount,
roomName: res.data.roomTitle,
status: res.data.status === 1 ? '正常' : '关闭',
creatorId: res.data.ownerId,
creatorAccount: res.data.ownerPhone,
creatorPhone: res.data.ownerPhone,
};
this.detailVisible = true;
} catch (error) {
this.$message.error(error.message || '获取详情失败');
}
}, },
handleEdit(row) { handleEdit(row) {
this.editForm = { this.editForm = {
@ -258,15 +182,7 @@ export default {
}, },
async handleSaveEdit() { async handleSaveEdit() {
try { try {
const data = { this.$message.info('当前列表为直播房间数据源,暂不支持在此页面编辑');
id: this.editForm.id,
status: this.editForm.status === '正常' ? 1 : 0,
isRecommended: this.editForm.recommendStatus === '已推荐' ? 1 : 0,
};
await roomUpdateApi(data);
this.$message.success('保存成功');
this.editVisible = false;
this.getList();
} catch (error) { } catch (error) {
this.$message.error(error.message || '保存失败'); this.$message.error(error.message || '保存失败');
} }

View File

@ -1,5 +1,9 @@
package com.zbkj.admin.controller; package com.zbkj.admin.controller;
import cn.hutool.core.util.StrUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zbkj.common.model.live.LiveRoom;
import com.zbkj.common.model.room.Room; import com.zbkj.common.model.room.Room;
import com.zbkj.common.page.CommonPage; import com.zbkj.common.page.CommonPage;
import com.zbkj.common.request.PageParamRequest; import com.zbkj.common.request.PageParamRequest;
@ -7,14 +11,21 @@ import com.zbkj.common.request.RoomSearchRequest;
import com.zbkj.common.request.RoomUpdateRequest; import com.zbkj.common.request.RoomUpdateRequest;
import com.zbkj.common.result.CommonResult; import com.zbkj.common.result.CommonResult;
import com.zbkj.service.service.RoomService; import com.zbkj.service.service.RoomService;
import com.zbkj.service.service.LiveRoomService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;
/** /**
* 房间管理控制器 * 房间管理控制器
*/ */
@ -28,6 +39,18 @@ public class RoomController {
@Autowired @Autowired
private RoomService roomService; private RoomService roomService;
@Autowired
private LiveRoomService liveRoomService;
@Value("${LIVE_PUBLIC_SRS_HOST:}")
private String publicHost;
@Value("${LIVE_PUBLIC_SRS_RTMP_PORT:}")
private String publicRtmpPort;
@Value("${LIVE_PUBLIC_SRS_HTTP_PORT:}")
private String publicHttpPort;
/** /**
* 分页列表 * 分页列表
*/ */
@ -40,6 +63,43 @@ public class RoomController {
return CommonResult.success(page); return CommonResult.success(page);
} }
@ApiOperation(value = "直播房间:分页列表")
@RequestMapping(value = "/live/list", method = RequestMethod.GET)
public CommonResult<CommonPage<LiveRoomAdminResponse>> getLiveList(@RequestParam(required = false) String title,
@RequestParam(required = false) String streamerName,
@RequestParam(required = false) String streamKey,
@ModelAttribute PageParamRequest pageParamRequest,
HttpServletRequest request) {
PageHelper.startPage(pageParamRequest.getPage(), pageParamRequest.getLimit());
com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<LiveRoom> qw = new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<>();
if (StrUtil.isNotBlank(title)) {
qw.like(LiveRoom::getTitle, title);
}
if (StrUtil.isNotBlank(streamerName)) {
qw.like(LiveRoom::getStreamerName, streamerName);
}
if (StrUtil.isNotBlank(streamKey)) {
qw.like(LiveRoom::getStreamKey, streamKey);
}
qw.orderByDesc(LiveRoom::getId);
List<LiveRoom> list = liveRoomService.list(qw);
PageInfo<LiveRoom> originPageInfo = new PageInfo<>(list);
String host = resolveHost(request);
int rtmpPort = parsePort(publicRtmpPort, 25002);
int httpPort = parsePort(publicHttpPort, 25003);
List<LiveRoomAdminResponse> respList = originPageInfo.getList().stream()
.map(r -> toAdminResponse(r, host, rtmpPort, httpPort))
.collect(Collectors.toList());
PageInfo<LiveRoomAdminResponse> respPageInfo = CommonPage.copyPageInfo(originPageInfo, respList);
CommonPage<LiveRoomAdminResponse> page = CommonPage.restPage(respPageInfo);
return CommonResult.success(page);
}
/** /**
* 获取房间详情 * 获取房间详情
*/ */
@ -63,4 +123,71 @@ public class RoomController {
} }
return CommonResult.failed(); return CommonResult.failed();
} }
private LiveRoomAdminResponse toAdminResponse(LiveRoom room, String host, int rtmpPort, int httpPort) {
LiveRoomAdminResponse resp = new LiveRoomAdminResponse();
resp.setId(room.getId());
resp.setTitle(room.getTitle());
resp.setStreamerName(room.getStreamerName());
resp.setStreamKey(room.getStreamKey());
resp.setIsLive(room.getIsLive() != null && room.getIsLive() == 1);
resp.setCreateTime(room.getCreateTime());
LiveRoomStreamUrlsResponse urls = new LiveRoomStreamUrlsResponse();
urls.setRtmp(String.format("rtmp://%s:%d/live/%s", host, rtmpPort, room.getStreamKey()));
urls.setFlv(String.format("http://%s:%d/live/%s.flv", host, httpPort, room.getStreamKey()));
urls.setHls(String.format("http://%s:%d/live/%s.m3u8", host, httpPort, room.getStreamKey()));
resp.setStreamUrls(urls);
return resp;
}
private String resolveHost(HttpServletRequest request) {
if (publicHost != null && !publicHost.trim().isEmpty()) {
return publicHost.trim();
}
String host = request.getHeader("X-Forwarded-Host");
if (host == null || host.trim().isEmpty()) {
host = request.getHeader("Host");
}
if (host == null || host.trim().isEmpty()) {
host = request.getServerName();
}
host = host.trim();
if (host.contains(",")) {
host = host.split(",")[0].trim();
}
if (host.contains(":")) {
host = host.split(":")[0].trim();
}
return host;
}
private int parsePort(String s, int def) {
try {
if (s == null) return def;
String v = s.trim();
if (v.isEmpty()) return def;
return Integer.parseInt(v);
} catch (Exception e) {
return def;
}
}
@Data
public static class LiveRoomAdminResponse {
private Integer id;
private String title;
private String streamerName;
private String streamKey;
private Boolean isLive;
private java.util.Date createTime;
private LiveRoomStreamUrlsResponse streamUrls;
}
@Data
public static class LiveRoomStreamUrlsResponse {
private String rtmp;
private String flv;
private String hls;
}
} }

View File

@ -6,12 +6,13 @@ import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.util.AntPathMatcher; import org.springframework.util.AntPathMatcher;
import org.springframework.util.FileCopyUtils; import org.springframework.util.FileCopyUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import sun.misc.BASE64Decoder;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/** /**
* Swagger 文档 * Swagger 文档
@ -52,7 +53,7 @@ public class SwaggerInterceptor extends HandlerInterceptorAdapter {
public boolean httpBasicAuth(String authorization) throws IOException { public boolean httpBasicAuth(String authorization) throws IOException {
if(check){ if(check){
if (authorization != null && authorization.split(" ").length == 2) { if (authorization != null && authorization.split(" ").length == 2) {
String userAndPass = new String(new BASE64Decoder().decodeBuffer(authorization.split(" ")[1])); String userAndPass = new String(Base64.getDecoder().decode(authorization.split(" ")[1]), StandardCharsets.UTF_8);
String username = userAndPass.split(":").length == 2 ? userAndPass.split(":")[0] : null; String username = userAndPass.split(":").length == 2 ? userAndPass.split(":")[0] : null;
String password = userAndPass.split(":").length == 2 ? userAndPass.split(":")[1] : null; String password = userAndPass.split(":").length == 2 ? userAndPass.split(":")[1] : null;
return this.username.equals(username) && this.password.equals(password); return this.username.equals(username) && this.password.equals(password);

View File

@ -41,7 +41,7 @@ public class LiveRoomController {
@ApiOperation(value = "公开:直播间列表") @ApiOperation(value = "公开:直播间列表")
@GetMapping("/public/rooms") @GetMapping("/public/rooms")
public CommonResult<List<LiveRoomResponse>> publicRooms(HttpServletRequest request) { public CommonResult<List<LiveRoomResponse>> publicRooms(HttpServletRequest request) {
String requestHost = request.getServerName(); String requestHost = resolveHost(request);
return CommonResult.success(liveRoomService.getAll().stream() return CommonResult.success(liveRoomService.getAll().stream()
.map(r -> toResponse(r, requestHost)) .map(r -> toResponse(r, requestHost))
.collect(Collectors.toList())); .collect(Collectors.toList()));
@ -52,7 +52,7 @@ public class LiveRoomController {
public CommonResult<LiveRoomResponse> publicRoom(@PathVariable Integer id, HttpServletRequest request) { public CommonResult<LiveRoomResponse> publicRoom(@PathVariable Integer id, HttpServletRequest request) {
LiveRoom room = liveRoomService.getById(id); LiveRoom room = liveRoomService.getById(id);
if (room == null) return CommonResult.failed("房间不存在"); if (room == null) return CommonResult.failed("房间不存在");
return CommonResult.success(toResponse(room, request.getServerName())); return CommonResult.success(toResponse(room, resolveHost(request)));
} }
@ApiOperation(value = "创建直播间(登录)") @ApiOperation(value = "创建直播间(登录)")
@ -61,7 +61,7 @@ public class LiveRoomController {
Integer uid = frontTokenComponent.getUserId(); Integer uid = frontTokenComponent.getUserId();
if (uid == null) return CommonResult.failed("未登录"); if (uid == null) return CommonResult.failed("未登录");
LiveRoom room = liveRoomService.createRoom(uid, req.getTitle(), req.getStreamerName()); LiveRoom room = liveRoomService.createRoom(uid, req.getTitle(), req.getStreamerName());
return CommonResult.success(toResponse(room, request.getServerName())); return CommonResult.success(toResponse(room, resolveHost(request)));
} }
@ApiOperation(value = "删除直播间(登录)") @ApiOperation(value = "删除直播间(登录)")
@ -98,6 +98,27 @@ public class LiveRoomController {
return resp; return resp;
} }
private String resolveHost(HttpServletRequest request) {
if (publicHost != null && !publicHost.trim().isEmpty()) {
return publicHost.trim();
}
String host = request.getHeader("X-Forwarded-Host");
if (host == null || host.trim().isEmpty()) {
host = request.getHeader("Host");
}
if (host == null || host.trim().isEmpty()) {
host = request.getServerName();
}
host = host.trim();
if (host.contains(",")) {
host = host.split(",")[0].trim();
}
if (host.contains(":")) {
host = host.split(":")[0].trim();
}
return host;
}
private int parsePort(String s, int def) { private int parsePort(String s, int def) {
try { try {
if (s == null) return def; if (s == null) return def;

View File

@ -813,7 +813,7 @@ public class MainActivity extends AppCompatActivity {
String rtmp = room != null && room.getStreamUrls() != null ? room.getStreamUrls().getRtmp() : null; String rtmp = room != null && room.getStreamUrls() != null ? room.getStreamUrls().getRtmp() : null;
// 直接使用服务器返回的RTMP地址 // 直接使用服务器返回的RTMP地址
String rtmpForObs = StreamConfig.rewriteStreamUrl(this, rtmp); String rtmpForObs = rtmp;
// 创建自定义弹窗布局 // 创建自定义弹窗布局
View dialogView = getLayoutInflater().inflate(R.layout.dialog_stream_info, null); View dialogView = getLayoutInflater().inflate(R.layout.dialog_stream_info, null);

View File

@ -383,9 +383,6 @@ public class RoomDetailActivity extends AppCompatActivity {
if (TextUtils.isEmpty(playUrl)) playUrl = fallbackHlsUrl; if (TextUtils.isEmpty(playUrl)) playUrl = fallbackHlsUrl;
} }
playUrl = StreamConfig.rewriteStreamUrl(this, playUrl);
fallbackHlsUrl = StreamConfig.rewriteStreamUrl(this, fallbackHlsUrl);
if (!TextUtils.isEmpty(playUrl)) { if (!TextUtils.isEmpty(playUrl)) {
ensurePlayer(playUrl, fallbackHlsUrl); ensurePlayer(playUrl, fallbackHlsUrl);
} else { } else {