GSYVideoPlayer 近期播放能力源码级指南:从 WebVTT 进度预览到 DLNA 投屏
2026/9/13 4:52:28 网站建设 项目流程

GSYVideoPlayer 近期播放能力源码级指南:从 WebVTT 进度预览到 DLNA 投屏

【免费下载链接】GSYVideoPlayerVideo players (IJKplayer, ExoPlayer, MediaPlayer), HTTPS, 16k page size, danmaku (bullet chat) support, external subtitles, support for filters, watermarks, and GIF screenshots, pre-roll and mid-roll ads, multiple simultaneous playback, basic seeking/dragging, volume and brightness adjustment, play-while-cache support项目地址: https://gitcode.com/GitHub_Trending/gs/GSYVideoPlayer

本文基于仓库内的 RECENT_FEATURES_EN.md(与中文版 RECENT_FEATURES.md 互为镜像)整理而成,系统梳理 GSYVideoPlayer 近期在进度条预览、外挂字幕、截图、GL 渲染、清晰度切换、播放器初始化稳定性、Exo 缓存生命周期以及 DLNA/UPnP 投屏上的能力变化。文中每一个能力都会给出 Demo 入口、核心类/API 以及对应源码位置,并附带一份可直接执行的回归清单,方便维护者在二次开发、升级或回归时快速定位入口与验证范围。

能力入口总览

下表汇总了本文涉及的全部近期能力、对应的 Demo 入口、主要实现类与一句话说明。它是后续各章节的导航图:

能力Demo 入口主要类说明
WebVTT 进度条预览打开VIDEOPreViewGSYVideoPlayer通过 WebVTT 缩略图轨道展示预览图,支持独立图片和 sprite 坐标裁剪。
通用外挂字幕自定义EXO支持字幕通用字幕非EXOGSYSubtitleControllerGSYSubtitleViewSRT/WebVTT 由播放器 UI 层统一解析和渲染,可跨 IJK、System、Media3 使用。
完成后保留最后一帧完成保留最后一帧KeepLastFrameVideoDemo 级实现,通过 flag 控制自然播放完成时保留当前渲染画面或回到默认封面态。
截图语义增强滤镜 Demo、MediaCodec Demo 等StandardGSYVideoPlayerGSYRenderView保留视频帧截图,同时新增包含播放器 UI 的组合截图 API。
GLSurfaceView 效果和生命周期滤镜DetailFilterActivityGSYVideoGLView*Render整理 GL render 生命周期,补充滤镜、纹理、多窗口、遮罩等 Demo 场景。
多 URL 清晰度切换稳态优化无缝切换SmartPickVideo保留双 manager 方案,强化切换过程中的位置同步、超时、失败回退和临时 manager 释放。
Exo 自适应清晰度EXO自适应清晰度ExoAdaptiveTrackActivityGSYExo2MediaPlayer使用单个 HLS master playlist 或 DASH MPD,由 Media3 TrackSelector 在同一时间线内自适应或固定 video track。
播放器初始化失败安全处理通用能力GSYVideoBaseManager、各IPlayerManager内核创建或初始化失败时走错误回调和资源清理,避免直接 crash。
Exo 缓存生命周期和 GIF 清理通用能力ExoSourceManagerGifCreateHelper收紧 Exo cache 的打开/释放流程,GIF 生成流程结束或失败时更可靠地清理。
DLNA/UPnP 投屏投屏 DemoCastCapabilityJupnpDlnaProviderJupnpDlnaSessionSampleCastControlVideoCastDemoActivity协议无关 SPI 留在核心,jUPnP 3.0.3 DLNAAVTransport:1实现由可选gsyvideoplayer-cast提供;SetAVTransportURI → Play → Seek保留中途投屏本地进度,并带单机 Loopback Receiver。

这些能力在仓库文档体系中的覆盖范围如下:

  • README_CN.md/README.md:首页能力摘要和近期能力入口。
  • doc/USE.md / doc/USE_EN.md:使用层面的 Demo 入口和核心 API。
  • doc/UPDATE_VERSION.md / doc/UPDATE_VERSION_EN.md:Unreleased 版本变更摘要。
  • doc/ARCHITECTURE.md:播放能力在 UI、Manager、Render、Exo manager 等层级上的设计归属。
  • doc/GSYVIDEO_PLAYER_PROJECT_INFO.md / doc/GSYVIDEO_PLAYER_PROJECT_INFO_EN.md:项目结构说明里的近期能力层级映射。
  • doc/SUBTITLE_CN.md:通用字幕专题说明。
  • doc/KEEP_LAST_FRAME.md / doc/KEEP_LAST_FRAME_EN.md:完成后保留最后一帧专题说明。
  • doc/RECENT_FEATURES.md / doc/RECENT_FEATURES_EN.md:近期能力总览、API 和回归清单。

构建、依赖、SO、发布、解码器、FAQ 类文档没有强行加入这些播放能力说明,因为它们的主题不是 Demo 功能入口或播放架构。

WebVTT 进度条预览

从客户端抽帧到 WebVTT 缩略图轨道

过去的进度条预览方案依赖客户端对原视频批量抽帧,内存与耗时都不理想。现在的方案是:业务侧预生成 WebVTT 缩略图轨道,播放器按拖动进度查表取图。每个 cue(时间区间)可以指向一张独立图片,也可以指向 sprite 雪碧图中的一块区域(#xywh=坐标裁剪):

WEBVTT 00:00:00.000 --> 00:00:05.000 thumbs.jpg#xywh=0,0,160,90

对应的演示播放器是PreViewGSYVideoPlayer(Demo 入口打开VIDEO),核心 API 只有两个:

player.setOpenPreView(true); // 开启进度条预览,默认关闭 player.setPreviewVttUrl("https://example.com/thumbs.vtt"); // 设置 WebVTT 轨道地址

库层解析器与数据模型

库层提供了三个类支撑该能力(位于gsyVideoPlayer-java/src/main/java/com/shuyu/gsyvideoplayer/preview/):

  • GSYVideoPreviewVttParser:WebVTT 解析器,负责把 VTT 文本解析为一组预览帧;
  • GSYVideoPreviewProvider:预览帧提供者接口,按时间点返回对应帧;
  • GSYVideoPreviewFrame:单帧元数据,包含startTimeMs/endTimeMsimageUrl以及可选的cropX/cropY/cropWidth/cropHeight裁剪区域,hasCrop()用于判断是否为 sprite 裁剪帧。

从 GSYVideoPreviewVttParser.java 的实现可以看到几个关键细节:

  • 按行解析-->时间分隔符,兼容hh:mm:ss.mmmmm:ss.mmm两种时间格式(见parseTimeMs);
  • 遇到NOTE注释行自动跳过;
  • 识别#xywh=前缀并拆出x,y,w,h四个整数,解析失败时回退为整图模式(crop 为 -1);
  • 图片地址会基于 VTT 的 baseUrl 做相对路径解析(resolveUrl),因此 VTT 内可以使用相对路径。

GSYVideoPreviewFrame是一个不可变数据类,构造函数同时提供整图模式(GSYVideoPreviewFrame.java)与 sprite 裁剪模式两个入口。

仓库还提供了针对该解析器的单元测试 GSYVideoPreviewVttParserTest.java,覆盖两个典型场景:

  • sprite 场景:同一张160p-00001.jpg通过#xywh=0,0,284,160#xywh=284,0,284,160切分两个时间片,断言getPreviewFrame(1500)命中第二个帧且裁剪坐标正确(cropX=284cropWidth=284);
  • 独立图片场景mm:ss.mmm短时间格式 + 相对图片路径,断言解析后startTimeMs=1000endTimeMs=2500、图片 URL 被正确拼为绝对地址。

Demo 端如何取帧与渲染

PreViewGSYVideoPlayer(app/src/main/java/com/example/gsyvideoplayer/video/PreViewGSYVideoPlayer.java)中,预览流程为:

  1. onStartTrackingTouch用户按住进度条时显示预览容器;
  2. onProgressChanged中把progress换算成time = progress * getDuration() / 100,调用showPreView(time)取帧,并把预览图随手指位置移动(leftMargin 跟随进度);
  3. showPreView通过mPreviewProvider.getPreviewFrame(time)拿到GSYVideoPreviewFrame:sprite 帧用PreviewSpriteTransformation按裁剪坐标变换加载,独立帧直接 Glide 加载并centerCrop到 150x100 dp 的预览尺寸;
  4. onStopTrackingTouch隐藏预览并恢复进度。

VTT 的拉取在单线程ExecutorService中完成(15 秒连接/读取超时),通过自增mPreviewLoadId做请求时效校验——只有最新的加载结果才会生效,过期回调直接丢弃,避免快速切换 URL 时旧结果覆盖新结果。播放器进入全屏时(startWindowFullscreen)会同步透传mOpenPreViewmPreviewVttUrl与已加载的mPreviewProvider,全屏与竖屏共用同一份预览数据。

通用外挂字幕

外挂字幕能力已从 Media3 专属实现调整为UI overlay 能力:SRT/WebVTT 由GSYSubtitleController负责加载、解析,并按播放进度刷新渲染在GSYSubtitleView上。由于字幕渲染在 UI 层完成,它天然可以跨 IJK、System、Media3 等所有内核使用,不再依赖某个播放内核的媒体源合并能力。字幕加载异常时只会清空字幕视图,不会中断主视频播放。

基本用法:

GSYSubtitleSource source = new GSYSubtitleSource.Builder("https://example.com/subtitle.srt") .setMimeType(GSYSubtitleMime.APPLICATION_SUBRIP) .setLanguage("zh") .setLabel("Chinese") .setDefault(true) .build(); videoPlayer.setSubtitleSource(source); videoPlayer.setSubtitleEnabled(true);

播放器基类 GSYVideoControlView.java 中与字幕相关的公开 API 还包括:

  • setSubtitleSources(List<GSYSubtitleSource>):一次设置多个字幕源;
  • selectSubtitle(String id):按 id 切换当前字幕;
  • setSubtitleEnabled(boolean)/isSubtitleEnabled():开关字幕显示;
  • setSubtitleOffsetMs(long)/getSubtitleOffsetMs():字幕时间轴偏移,用于音画不同步校正。

GSYSubtitleController(GSYSubtitleController.java)内部通过update(long positionMs)随播放位置推进刷新当前 cue,并支持字幕样式(GSYSubtitleStyle)与偏移量配置。详细的字幕接入说明见 doc/SUBTITLE_CN.md,仓库同时在 subtitle 测试目录 提供了解析器级单元测试。

完成后保留最后一帧

KeepLastFrameVideo(app/src/main/java/com/example/gsyvideoplayer/video/KeepLastFrameVideo.java)是 Demo 级实现,核心思路是覆写自然播放完成后的 UI 状态切换:在不主动释放 render view 的情况下保留当前渲染画面,而不是立刻回到封面。开关方法为:

keepLastFrameVideo.setKeepLastFrameWhenComplete(true);

从源码看(onAutoCompletion覆写),开启该 flag 后播放完成时:

  1. 记录mTextureViewContainer是否仍持有渲染视图,用于后续判断是否保留画面;
  2. setStateAndUi(CURRENT_STATE_AUTO_COMPLETE)完成 UI 状态迁移;
  3. 释放音频焦点(abandonAudioFocus)、清除FLAG_KEEP_SCREEN_ON、释放网络状态,但不主动销毁渲染 surface,因此最后一帧停留在画面上。

Demo 页面 KeepLastFrameDemoActivity.java 提供了一个开关,可以在"保留最后一帧"与"回到封面"两种行为之间切换验证。

需要特别强调:这不是全局默认行为。在把它下沉到基础组件之前,需要先确认业务侧的封面策略、Surface 释放策略和重播逻辑,详见 doc/KEEP_LAST_FRAME_EN.md 与 doc/KEEP_LAST_FRAME.md。

截图语义增强:视频帧截图与组合截图

保留不变的视频帧截图 API

现有视频帧截图语义保持不变,两个入口均在 StandardGSYVideoPlayer.java 中:

player.taskShotPic(listener); // 异步回调 Bitmap player.saveFrame(file, listener); // 保存到文件

新增的组合截图 API

组合截图用于"把视频画面和播放器 UI 一起截下来"的场景(例如需要包含字幕 overlay、控制栏的截图):

player.taskShotPicWithView(listener); // 视频帧 + 播放器 UI player.saveFrameWithView(file, listener); // 组合截图保存文件

从 StandardGSYVideoPlayer.java 的实现可以看到组合截图的完整链路:

  1. taskShotPicWithView强制切回主线程执行;
  2. 先按播放器宽高创建目标 Bitmap(默认RGB_565high=true时用ARGB_8888);
  3. 调用内部taskShotPic先拿到视频帧
  4. 把视频帧按渲染视图位置绘制到目标 Bitmap 上(resolveRenderRect处理 SurfaceView 与 TextureView 的坐标差异);
  5. 再调用drawPlayerOverlay把播放器 UI(控制栏、字幕等)叠加绘制上去;
  6. 最后通过GSYVideoShotListener.getBitmap回调组合结果,并回收中间 Bitmap。

saveFrameWithView复用同一链路,保存时以FileUtils.saveBitmapToFile的真实写入结果回调success布尔值。

当前修复点

  • SurfaceView 通过 PixelCopy 截图时,所有失败路径都会回调nullsuccess=false,不会静默吞掉错误;
  • TextureView、SurfaceView、GLSurfaceView 的保存回调现在反映真实文件写入结果;
  • 组合截图先取视频帧、再叠加 UI 的绘制顺序,保证字幕 overlay 与控制栏能正确显示在画面上。

GLSurfaceView 效果与生命周期

GL 相关整理集中在DetailFilterActivityGSYVideoGLView*Render系列类上:

  • Demo 内统一切换到GSYVideoType.GLSURFACE渲染类型,退出时恢复进入前的 render type;
  • GSYVideoGLViewBaseRender/GSYVideoGLViewSimpleRender增强了 release 和截图回调,降低 GL 生命周期异常导致的卡住风险;
  • 滤镜Demo 可以切换普通滤镜、纹理水印、多窗口播放、图片穿孔(mask)、背景高斯等场景。

GL 渲染基类位于 gsyVideoPlayer-java 的 render 目录,自定义渲染示例见 app 的 effect 目录(GSYVideoGLViewCustomRender系列),可在此基础上实现自定义 shader 效果。

多 URL 清晰度切换

SmartPickVideo(app/src/main/java/com/example/gsyvideoplayer/video/SmartPickVideo.java)仍然面向多个独立 URL的清晰度切换或下一集切换场景,例如清晰度 URL、下一集 URL。需要明确:这个方案不是 HLS/DASH 标准 ABR

它的核心机制是:使用一个临时 manager预加载目标 URL,等待目标就绪后把当前播放位置同步过去,再提交切换。近期针对切换稳态的优化点包括:

  • 位置不归零:切换时记录最新播放位置,避免跳转到目标后直接回到 0(源码中的updateLastKnownChangePositionisChangeTargetPositionReliable负责记录与校验位置可信度);
  • seek 误差阈值 + 重试 + 超时保护resolveStartChange解析切换目标,retryTmpSeek在 seek 失败时按会话重试,超时后放弃本次切换;
  • 失败回退:切换失败会回退到原播放,并释放临时 manager,不让其长期占用资源(onError分支与临时 manager 释放逻辑);
  • setUp(List<SwitchVideoModel>)支持按源位置切换,applySourcePosition统一落地源位置变更。

如果服务端可以提供 HLS master playlist 或 DASH MPD,应优先使用下一节的 Exo 自适应清晰度方案,而不是多 URL 手动切换。

Exo 自适应清晰度

EXO自适应清晰度Demo(ExoAdaptiveTrackActivity.java)使用一个 HLS master URL 或一个 DASH MPD URL。Exo/Media3 会把同一个 track group 里的多码率 video track 用于 adaptive playback,由 Media3TrackSelector在同一时间线内自动选择或固定指定 track。

Demo 内置测试源:

  • HLS master:https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8
  • DASH MPD:https://dash.akamaized.net/envivio/EnvivioDash3/manifest.mpd

新增的对外 API 由GSYExoVideoManager暴露(GSYExoVideoManager.java),逐层转发到GSYExoPlayerManager再到GSYExo2MediaPlayer

GSYExoVideoManager.instance().getVideoTrackInfoList(); // 列出可选视频轨 GSYExoVideoManager.instance().clearVideoTrackOverride(); // 清除固定选择,恢复自适应 GSYExoVideoManager.instance().setVideoTrackOverride(groupIndex, trackIndex); // 固定到指定 track

底层实现位于 GSYExo2MediaPlayer.java:

  • getVideoTrackInfoList()遍历 Media3Tracks.Group,只保留C.TRACK_TYPE_VIDEO类型的组,为每个 track 生成VideoTrackInfo(包含 groupIndex、trackIndex、format、是否被选中/是否支持/组是否支持自适应);
  • setVideoTrackOverride(groupIndex, trackIndex)校验组类型与 track 可支持性后,构造TrackSelectionOverride并写入TrackSelectionParameters
  • clearVideoTrackOverride()清除 video 类型的所有 override、解除禁用状态,并重置强制最低/最高码率标记。

使用语义:

  • 默认"自动":由 TrackSelector 根据带宽、buffer、设备能力自动选择 track;
  • 选择固定清晰度:使用TrackSelectionOverride固定到某个 video track;
  • 清除 override 后恢复自适应播放。

播放器初始化失败安全处理

播放器初始化加固集中在GSYVideoBaseManager(GSYVideoBaseManager.java)和各内核的IPlayerManager实现中。当 IJK、System、Exo、AliPlayer 创建或初始化失败时,流程会尽量走onError回调和资源释放清理,而不是把异常直接抛到业务层导致 crash。GSYVideoBaseManageronError(IMediaPlayer, int, int)会统一把 what/extra 转发给上层listener().onError,让业务侧有机会进入错误态 UI。

该能力没有单独 Demo 入口,属于全局稳定性兜底。建议回归时覆盖非法 URL、缺失解码能力或故意构造异常初始化路径,确认播放器进入错误态且应用不崩溃。

Exo 缓存生命周期与 GIF 清理

ExoSourceManager(位于 gsyVideoPlayer-exo_player2 的 exo2 源码目录)收紧了 Exo cache 的打开、复用和释放流程,降低 cache 打开/复用/释放过程中的资源残留风险。GifCreateHelper(GifCreateHelper.java)在 GIF 生成结束、失败或取消后会更可靠地推进状态并清理临时资源——从源码看,cancelTask()取消计时任务、clearTmpFiles()清理临时图片、release()统一收尾,覆盖了三条退出路径。

该能力同样没有单独 Demo 入口,回归时应覆盖:Exo cache 播放、退出页面、重新进入播放,以及滤镜 Demo 里的 GIF 生成/失败/取消路径。

DLNA/UPnP 投屏

协议无关 SPI 留在核心,jUPnP 实现由可选模块提供

投屏的协议无关 SPI 保留在gsyVideoPlayer-java(cast 目录);从 13.2.1 起,默认的 jUPnP 3.0.3 DLNAAVTransport:1实现由可选的gsyvideoplayer-cast模块提供(dlna 目录 中的JupnpDlnaProviderJupnpDlnaSession)。SPI 三件套:

  • CastCapability(CastCapability.java):入口门面,通过GSYVideoBaseManager.getCastCapability()获取;
  • CastProvider(CastProvider.java):具体协议 SPI,由 App 注册到CastCapability;默认 DLNA 实现是可选模块中的JupnpDlnaProvider
  • CastSession(CastSession.java):一次投屏会话生命周期:setMediaItem(CastMediaInfo)play/pause/stop/seekTorelease(),状态与进度通过SessionListener回调。

从 GSYVideoBaseManager.java 的注释可以确认:首次访问getCastCapability()不会自动注册任何 provider,App 显式依赖gsyvideoplayer-cast后自行注册JupnpDlnaProvider

中途起播示例

投屏的核心场景是"从本地播放中途转投到大屏并保留进度"。CastMediaInfo构造器(CastMediaInfo.java)除 url/title/mimeType 外还接收durationMsstartPositionMs,其中 mimeType 会写入 DLNA 的 protocolInfo,未知时长传 0 时位置轮询会用GetPositionInfo覆盖。完整流程:

CastCapability cast = GSYVideoManager.instance().getCastCapability(); cast.registerProvider(new JupnpDlnaProvider()); cast.addListener(castListener); // onDeviceListChanged / onSessionStateChanged / onError cast.startDiscovery(context.getApplicationContext()); // 用户选中设备后: long localPositionMs = videoPlayer.getCurrentPositionWhenPlaying(); CastMediaInfo media = new CastMediaInfo(url, title, "video/mp4", /*durationMs*/ 0L, localPositionMs); cast.connect(selectedDevice, new CastProvider.ConnectCallback() { @Override public void onConnected(CastSession session) { session.addListener(sessionListener); session.setMediaItem(media); // SetAVTransportURI → Play → Seek(localPositionMs) } @Override public void onError(Throwable error) { /* 展示连接错误 */ } }); // 页面退出或用户结束投屏:先停止远端,再释放会话与发现服务。 CastSession active = cast.getActiveSession(); if (active != null) active.stop(); cast.disconnect(); cast.stopDiscovery();

Demo 与 Loopback 单机自测

MainActivity提供独立的投屏 Demo入口CastDemoActivity,包含 DLNA 设备选择列表与 Loopback Receiver 开关。演示播放器SampleCastControlVideo在投屏成功后自动塌陷成远端遥控 overlay,本地 surface/audio 释放;断开后按最近一次远端进度恢复本地播放。

单机自测用的 Loopback Receiver(无需真电视)包含以下机制(见 DevReceiverService.java):

  • DevReceiverService运行在独立:dlna进程(foreground service),注册 jUPnPLocalDeviceurn:schemas-upnp-org:device:MediaRenderer:1),并挂载LoopbackAvTransportService+LoopbackRenderingControlService
  • 接收端CastReceiverFloatingWindow以 SYSTEM_ALERT_WINDOW 悬浮窗呈现远端播放画面,CastReceiverPlayer内嵌 IJK 内核;
  • getPositionInfo/getTransportInfo回填真实进度与状态,sender 端 1Hz 轮询看到的进度就是远端真实播放位置;
  • Service ↔ Activity 通过setPackage私有广播同步状态(ACTION_STATE_READY/ACTION_STATE_STOPPED/ACTION_STATE_ERROR):广播用显式包名锁定同 App,无需声明 permission;Android 13+ 已适配RECEIVER_NOT_EXPORTED

依赖开关与版本前提

需要 DLNA 时添加依赖:

io.github.carguo:gsyvideoplayer-cast:13.2.1

该 artifact 统一传递 jUPnP/Jetty,并声明真实的minSdk 26默认播放器不含 Jetty,最低版本保持 API 23(详见 doc/DEPENDENCIES_EN.md / doc/DEPENDENCIES.md)。更多能力目标与测试判据见 doc/CAST_FEATURE_PLAN.md 与 doc/CAST_TEST_PLAYBOOK.md。

回归清单

每次修改上述能力后,至少执行构建与安装:

./gradlew :gsyVideoPlayer-java:testDebugUnitTest :app:assembleDebug adb install -r app/build/outputs/apk/debug/app-debug.apk

真机建议逐项检查:

  • 打开VIDEO:拖动进度条,确认 WebVTT 预览图能出现;
  • 自定义EXO支持字幕通用字幕非EXO:本地 SRT、本地 VTT、网络 SRT 都能切换,失败不影响播放;
  • 完成保留最后一帧:打开/关闭 flag,确认完成后分别停留最后一帧和回到封面;
  • 滤镜:切换滤镜和 GL 场景,确认播放、截图、GIF 不崩;
  • 无缝切换:切换多个 URL,确认不回 0,失败可回退;
  • EXO自适应清晰度:HLS 和 DASH 都能播放,轨道列表能显示,自动/固定清晰度能切换;
  • 播放失败和内核初始化异常:确认进入错误回调,不直接 crash;
  • Exo cache 和 GIF:确认退出、重进、失败路径都能清理资源;
  • 投屏 Demo:可选择 DLNA 设备,投屏后本地塌陷成遥控 overlay;断开后本地在近似远端进度处继续;启用Loopback Receiver可无真电视自测。

小结

本文沿 doc/RECENT_FEATURES_EN.md 的脉络,逐项拆解了 GSYVideoPlayer 近期十个播放能力点:WebVTT 进度预览的"VTT 轨道 + sprite 裁剪"新范式、跨内核的 UI 层外挂字幕、保留最后一帧的 Demo 实现、视频帧/组合双层截图 API、GLSurfaceView 生命周期整理、多 URL 切换的位置与超时加固、Exo 自适应清晰度(Media3TrackSelectionOverride)、播放器初始化失败兜底、Exo 缓存与 GIF 资源清理,以及 SPI 与 jUPnP 实现解耦的 DLNA 投屏与 Loopback 自测链路。每个能力都给出了 Demo 入口、核心 API 和源码位置,配合文末回归清单,可直接用于二次开发与版本升级后的稳定性验证。

【免费下载链接】GSYVideoPlayerVideo players (IJKplayer, ExoPlayer, MediaPlayer), HTTPS, 16k page size, danmaku (bullet chat) support, external subtitles, support for filters, watermarks, and GIF screenshots, pre-roll and mid-roll ads, multiple simultaneous playback, basic seeking/dragging, volume and brightness adjustment, play-while-cache support项目地址: https://gitcode.com/GitHub_Trending/gs/GSYVideoPlayer

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询