
简介这是一套面向Java与移动开发学习者的原生高仿短视频APP双端源码覆盖Android与iOS两端适合希望深入理解短视频应用完整实现、提升音视频处理与跨平台开发能力的中高级开发者。压缩包共1127个文件约63.2MB以258个java源码、317个xml布局、417个png图片资源为主另含39个so库、23个jar包及gradle构建脚本完整呈现工程结构与依赖配置。项目涉及Android SDK、Activity与Fragment、MediaCodec、MediaPlayer、OpenGL ES、FFmpeg、AVFoundation、Objective-C、PHP 5.6、MySQL 5.5与ThinkPHP框架并包含伪静态URL重写等后端实践。已有776人学习下载。通过研读源码读者可掌握双端UI搭建、音视频编解码与播放、服务端接口交互及MVC设计模式是贯通移动端与后端全流程开发的实战参考。1. 从一份带 LiteAVSDK_UGC 的 Java 双端源码说起短视频 App 的源码包我拆过不少大多数要么只有 Android 端要么 iOS 端是空壳要么音视频模块直接调个系统播放器糊弄过去。这份「原生 Java 高仿短视频 APP 双端源码」不太一样——解压后第一眼看到的是LiteAVSDK_UGC_4.4.3774.aar和ti-sdk.aar两个体积不小的依赖包加上gradlew.bat和一堆 Gradle 构建缓存文件fileSnapshots.bin、localClassSetAnalysis.bin、fileHashes.bin、taskArtifacts.bin这些说明它至少是一个真正跑过构建、编译出过 APK 的工程不是随手丢几个 Java 文件凑数的。它解决的核心问题是让你在一个完整可编译的双端工程里看到短视频从拍摄、编辑、上传到播放的整条链路是怎么用 JavaAndroid 端和 Objective-CiOS 端串起来的后端还配了 PHP 5.6 MySQL 5.5 ThinkPHP 的接口层。适合谁适合已经会写 Android 界面、但没碰过音视频采集与推流编码的 Java 开发者也适合想对照双端实现来理解 SDK 封装思路的移动端工程师。下面按「资源是什么 → 怎么跑起来 → 音视频链路怎么读 → 坑在哪 → 进阶怎么改」的顺序拆。2. 工程结构与双端技术栈先搞清楚每个模块归谁管2.1 Android 端Java Android SDK LiteAVSDKAndroid 端是这份源码里最厚的一块。UI 层用 XML 布局配合 Activity 和 Fragment列表页大概率是 RecyclerView 加自定义 Adapter这在短视频类 App 里是标配。业务逻辑用 Java 写网络请求、数据解析、状态管理都在这一层。真正跟音视频打交道的是LiteAVSDK_UGC_4.4.3774.aar——腾讯云短视频 SDK 的 UGC 版本负责拍摄、剪辑、拼接、特效、推流这一整套。ti-sdk.aar通常是配套的滤镜或特效资源包。你需要在 Android Studio 里打开工程确认build.gradle里的compileSdkVersion、minSdkVersion跟本机 SDK 匹配。常见做法是先把 Gradle 版本对齐再同步依赖。如果LiteAVSDK_UGC的 aar 没有正确被flatDir或libs目录引用编译会直接报找不到TXUGCRecord之类的类。2.2 iOS 端Objective-C AVFoundation FFmpegiOS 端用 Objective-C 写架构是 MVCUI 走 UIKit。音视频处理主要靠 AVFoundation——采集用AVCaptureSession播放用AVPlayer编辑用AVAssetExportSession。项目正文里提到可能用到 FFmpeg 做跨平台编解码这在需要自定义封装格式或做特殊裁剪时会用到。如果你只熟悉 Swift读 OC 代码会有点别扭但语法障碍不大重点看AVCaptureVideoDataOutput的回调和CMSampleBufferRef的处理流程。2.3 后端PHP 5.6 MySQL 5.5 ThinkPHP后端不是摆设。用户注册登录、视频列表拉取、点赞评论、视频上传后的转码回调都走 PHP 接口。ThinkPHP 提供 MVC 结构MySQL 存用户表和视频元数据。伪静态设置URL 重写是为了让接口路径看起来像静态页对 SEO 友好但对你调试来说关键是找到route.php或.htaccess里的重写规则确认 API 前缀是什么。提示PHP 5.6 和 MySQL 5.5 都是老版本本地搭建建议用 phpStudy 或 Docker 拉对应镜像别用 PHP 8 去跑否则 ThinkPHP 的旧版本会报一堆废弃函数错误。3. 把工程跑起来Android 端编译与后端接口对接3.1 Android 端 Gradle 同步与 aar 依赖配置先把工程导入 Android Studio别急着点 Run。第一步是检查app/build.gradle和项目根目录的build.gradle。LiteAVSDK 的 aar 通常放在app/libs/下需要在build.gradle里声明flatDir仓库。// app/build.gradle repositories { flatDir { dirs libs // 指向 aar 所在目录 } } dependencies { implementation fileTree(dir: libs, include: [*.jar]) implementation(name: LiteAVSDK_UGC_4.4.3774, ext: aar) // 短视频 SDK implementation(name: ti-sdk, ext: aar) // 特效资源包 implementation com.android.support:recyclerview-v7:28.0.0 // 列表控件 implementation com.squareup.okhttp3:okhttp:3.12.0 // 网络请求 }这段配置的逻辑是flatDir让 Gradle 去libs目录找 aarimplementation(name:..., ext: aar)精确引用。参数上LiteAVSDK_UGC_4.4.3774这个版本号必须跟文件名完全一致少一个数字都会报Failed to resolve。OkHttp 版本别升太高3.x 跟老项目的 support 库兼容性更好。同步成功后如果报Manifest merger failed多半是 aar 里的minSdkVersion跟主工程冲突。在AndroidManifest.xml的application标签里加tools:replaceandroid:theme或tools:nodereplace能压过去。3.2 后端接口地址替换与伪静态规则Android 端和 iOS 端的网络层都会有一个BASE_URL或API_HOST常量。搜http://或https://找到它换成你本地后端的地址。ThinkPHP 的入口文件一般是public/index.php伪静态规则写在.htaccessApache或 nginx 配置里。# nginx 伪静态配置示例 location / { if (!-e $request_filename) { rewrite ^(.*)$ /index.php?s$1 last; } }这段规则的作用是当请求的文件不存在时把路径重写到index.php并带上s参数ThinkPHP 再根据s参数路由到对应控制器。参数$1是捕获的完整路径。改完后用 Postman 请求http://你的IP/index.php/api/video/list能返回 JSON 就说明后端通了。3.3 数据库导入与初始配置MySQL 里新建一个库字符集选utf8mb4导入源码包里附带的.sql文件。然后改application/database.phpThinkPHP 3.2 是Conf/config.php里的数据库账号密码。常见坑是 SQL 文件里用了utf8而你的库是utf8mb4导入时中文会乱码统一成utf8mb4就行。4. 音视频链路拆解从拍摄到播放的 Java 实现4.1 拍摄与编辑LiteAVSDK 的 TXUGCRecord 调用Android 端的拍摄页面核心是TXUGCRecord类。它封装了摄像头采集、美颜、滤镜、分段录制。你不需要直接碰 MediaCodecSDK 内部已经处理了硬编硬解。典型调用流程是初始化 → 设置录制参数 → 开始录制 → 分段录制 → 停止并生成视频文件。// 拍摄页面初始化 TXUGCRecord mRecord TXUGCRecord.getInstance(context); TXUGCRecord.SimpleRecordParams params new TXUGCRecord.SimpleRecordParams(); params.mRecordVideoWidth 720; // 录制宽度 params.mRecordVideoHeight 1280; // 录制高度 params.mVideoBitrate 2000; // 码率 kbps params.mVideoFps 20; // 帧率 mRecord.setRecordParams(params); // 开始录制 mRecord.startRecord(outputPath, new TXUGCRecord.ITXRecordListener() { Override public void onRecordProgress(long milliSecond) { // 更新进度条 } Override public void onRecordComplete(TXUGCRecord.RecordResult result) { // result.videoPath 是生成的 mp4 路径 } });参数说明mRecordVideoWidth和mRecordVideoHeight决定输出分辨率720×1280 是竖屏短视频的常见选择mVideoBitrate2000kbps 在 720p 下画质和体积比较平衡调高到 4000 文件会明显变大mVideoFps20 帧够用30 帧更流畅但编码压力大。startRecord的outputPath要指向 App 私有目录别写死到/sdcard/Android 10 以后会因权限问题失败。4.2 播放与渲染MediaPlayer 与 OpenGL ES 的分工列表页的短视频播放简单场景用MediaPlayerSurfaceView就够了。但这份源码里出现了 OpenGL ES 的痕迹说明它可能在视频帧上做了滤镜或水印渲染。MediaPlayer负责解码和音频输出OpenGL ES 负责把视频帧画到GLSurfaceView上再做二次处理。// MediaPlayer 基础播放 MediaPlayer mediaPlayer new MediaPlayer(); mediaPlayer.setDataSource(videoUrl); mediaPlayer.setSurface(surface); // 绑定 SurfaceView 的 Surface mediaPlayer.setLooping(true); // 短视频循环播放 mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(mp - mp.start());setLooping(true)是短视频列表的标配用户滑到某个视频时自动循环。prepareAsync异步准备避免主线程卡顿。如果要用 OpenGL ES 做滤镜就得把MediaPlayer的输出接给SurfaceTexture再在 GL 线程里用 shader 渲染。这块代码量不小建议先跑通基础播放再去看滤镜部分。4.3 上传与转码分片上传与后端回调视频拍完后要上传到服务器。源码里大概率用了分片上传——把 mp4 切成若干块逐块 POST 到后端最后合并。后端 PHP 收到文件后调 FFmpeg 转码成统一格式再写数据库。你在UploadActivity或类似类里能找到分片逻辑。// 分片上传伪代码 int chunkSize 1024 * 1024; // 1MB 一片 File file new File(videoPath); long fileSize file.length(); int chunkCount (int) Math.ceil((double) fileSize / chunkSize); for (int i 0; i chunkCount; i) { byte[] chunk readChunk(file, i * chunkSize, chunkSize); // POST chunk 到 /api/upload/chunk带 fileId 和 chunkIndex } // 全部完成后调 /api/upload/merge 合并chunkSize设 1MB 是移动网络下的稳妥值太大容易超时太小请求次数多。每个分片要带唯一fileId和chunkIndex后端按序合并。合并完成后后端返回视频 URL客户端再刷新列表。5. 避坑与排查双端源码跑不通时先看这几条5.1 编译报错Failed to resolve: LiteAVSDK_UGC现象Gradle sync 失败提示找不到 LiteAVSDK 相关类。原因aar 文件没放在libs目录或者flatDir没配或者文件名跟implementation里的名字不一致。解决确认app/libs/下有LiteAVSDK_UGC_4.4.3774.aarbuild.gradle里flatDir { dirs libs }和implementation(name: LiteAVSDK_UGC_4.4.3774, ext: aar)都写对名字大小写敏感。5.2 录制时黑屏或报Camera permission denied现象进入拍摄页黑屏Logcat 报权限拒绝。原因Android 6.0 以上需要动态申请CAMERA和RECORD_AUDIO权限源码里可能只写了 Manifest 没写运行时申请。解决在拍摄 Activity 的onCreate里加ActivityCompat.requestPermissions或者手动去系统设置里给 App 开权限。另外模拟器摄像头支持差用真机测。5.3 后端接口返回 404 或module not exists现象App 请求登录接口返回 404或者 ThinkPHP 报模块不存在。原因伪静态没生效或者BASE_URL里少了index.php。解决先直接用http://IP/index.php/api/user/login测通了再配伪静态。nginx 的rewrite规则要放在location /里Apache 要确认AllowOverride All已开。5.4 视频上传后播放花屏或音画不同步现象上传成功但播放时画面花屏、声音对不上。原因分片合并时顺序错了或者转码参数跟源文件不匹配。解决检查后端合并逻辑是否按chunkIndex排序FFmpeg 转码命令加-vsync cfr强制恒定帧率音频用-acodec aac -ar 44100统一采样率。5.5 iOS 端AVCaptureSession启动崩溃现象iOS 端一进拍摄页就崩控制台报AVCaptureSession startRunning相关。原因startRunning是阻塞操作不能在主线程调。解决放到dispatch_queue的串行队列里执行UI 更新再切回主线程。另外NSCameraUsageDescription和NSMicrophoneUsageDescription必须写进Info.plist否则系统直接拒绝。6. 进阶改造把 LiteAVSDK 换成自研 MediaCodec 链路的思路跑通之后很多人会想能不能不依赖 LiteAVSDK自己用 MediaCodec OpenGL ES 搭一套可以但工作量不小。核心是把 SDK 帮你做的三件事拆开采集、编码、封装。采集用Camera2ImageReader拿到Image后转成SurfaceTexture编码用MediaCodec的createInputSurface模式把 GL 渲染的输出直接喂给编码器封装用MediaMuxer把 H.264 和 AAC 写成 mp4。这套链路的好处是可控坏处是兼容性坑多——不同芯片的 MediaCodec 支持格式不一样得做降级。// MediaCodec 编码器创建H.264 MediaFormat format MediaFormat.createVideoFormat(video/avc, 720, 1280); format.setInteger(MediaFormat.KEY_BIT_RATE, 2000000); format.setInteger(MediaFormat.KEY_FRAME_RATE, 20); format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); // 关键帧间隔 1 秒 MediaCodec encoder MediaCodec.createEncoderByType(video/avc); encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); Surface inputSurface encoder.createInputSurface(); // 给 OpenGL 渲染 encoder.start();KEY_COLOR_FORMAT设成COLOR_FormatSurface才能用 Surface 输入模式这是硬编的关键。KEY_I_FRAME_INTERVAL设 1 秒方便拖动预览。createInputSurface返回的 Surface 给 GL 线程当渲染目标GL 画完一帧编码器就自动取走一帧。验证方法录一段 10 秒视频用ffprobe看编码格式和帧率是否跟设置一致。如果花屏多半是 GL 的eglSwapBuffers时机不对如果音画不同步检查音频和视频的时间戳是否都用了System.nanoTime()做基准。我自己的习惯是每次改完编码参数先用 720p 录 5 秒ffprobe确认无误再上 1080p。从那以后我每次动音视频参数都强制走一遍「短录 ffprobe 校验」省得后面批量转码时才发现问题。希望帮到你。本文还有配套的精品资源点击获取