简介:这是一套面向Java中高级开发者与移动开发学习者的短视频APP双端实战源码,聚焦原生Android(Java)与iOS(Objective-C)协同开发及音视频核心能力训练,解决跨平台短视频应用从UI架构、媒体编解码到前后端联调的全链路实践难题。资源共1127个文件,含258个Java业务逻辑与Activity/Fragment代码、317个XML布局与资源定义、417个PNG/GIF图标与界面素材,以及39个SO库和8个AAR(如LiteAVSDK_UGC_4.4.3774.aar、ti-sdk.aar)等关键音视频依赖组件,包体63.2MB,结构完整、模块清晰,便于按Android/iOS/Server分层研读。已有776人学习下载,可直接运行调试,深入理解MediaCodec与AVFoundation音视频处理差异、RecyclerView流式UI实现、ThinkPHP后端API设计及伪静态路由配置,是少有的覆盖双端+音视频+服务端三位一体的Java系短视频项目范本。
1. 这不是“写个播放器就完事”的Java项目,而是一套可落地的短视频双端音视频链路闭环
你手头拿到的这个“原生Java高仿短视频APP双端源码”,表面看是Android+iOS的界面复刻,但真正值钱的是它把从拍摄→编码→上传→转码→分发→播放→渲染→互动这一整条短视频生产消费链路,用原生Java(Android端)和Objective-C(iOS端)完整跑通了。它不依赖Flutter或React Native这类跨端框架,所有音视频关键路径都直触系统API——比如Android端用MediaCodec硬编硬解配合SurfaceTexture做实时美颜预览,iOS端用AVCaptureSession + Metal纹理绑定实现帧级滤镜叠加。这意味着:如果你正在面试音视频方向的Android开发岗,这份代码里VideoRecordActivity.java中MediaCodec.createEncoderByType("video/avc")后的configure()参数组合、ByteBuffer流转逻辑、MediaMuxer写入时机,就是面试官最可能深挖的八股文现场;如果你刚学完Java基础正愁没实战项目,它又提供了完整的MVC分层结构、Retrofit+OkHttp网络栈、Room本地缓存、以及ThinkPHP后端配套SQL脚本——不是玩具Demo,而是能真机跑通、上传视频、拉取feed流、点赞评论的最小可行产品(MVP)。适合两类人:想补足音视频硬核能力的中级Android工程师,以及需要Java全栈闭环训练的应届生或转行者。
2. Android端音视频链路:从MediaCodec硬编码到LiteAVSDK集成的实操拆解
2.1 为什么不用MediaRecorder?硬编码才是短视频低延迟的关键
短视频APP对首帧时间、编码质量、功耗控制要求极高。MediaRecorder虽简单,但无法干预YUV数据流、无法动态调节码率、无法插入自定义滤镜。本项目在com.tencent.liteav.demo.shortvideo.record包下,采用MediaCodec硬编码方案,核心在于三点:
- 输入Surface绑定Camera预览:通过
mCamera.setPreviewTexture(mSurfaceTexture)将相机原始帧直接送入GPU,避免CPU拷贝; - 编码器配置参数精准控制:
MediaFormat.KEY_BIT_RATE设为800000(800kbps),KEY_FRAME_RATE为30,KEY_I_FRAME_INTERVAL为2秒——这决定了GOP结构,直接影响CDN分片和首屏加载; - ByteBuffer双缓冲机制防卡顿:
dequeueInputBuffer()获取空闲buffer后,用mInputBuffers[index].put(data)写入YUV420SP数据,再queueInputBuffer()提交;同时dequeueOutputBuffer()循环读取编码后的H.264 NALU,写入MediaMuxer。
提示:
fileSnapshots.bin和localClassSetAnalysis.bin等Gradle构建缓存文件,说明项目已启用android.enableJetifier=true和android.useAndroidX=true,确保第三方SDK(如LiteAVSDK)兼容AndroidX。若编译报错NoClassDefFoundError,优先检查gradle.properties中这两项是否开启。
2.2 LiteAVSDK_UGC_4.4.3774.aar的接入与关键配置
项目根目录下的LiteAVSDK_UGC_4.4.3774.aar是腾讯云LiteAV SDK的UGC(User Generated Content)版本,专为短视频录制、编辑、上传设计。它并非简单替换MediaCodec,而是封装了更上层的能力:
- 美颜与滤镜:通过
TXVideoEditConstants.VIDEO_BEAUTY_SMOOTH等常量控制磨皮强度,底层调用OpenGL ES shader; - 变速录制:
TXVideoRecordListener.onRecordProgress(long millisecond, long totalMillisecond)回调中,millisecond实际是按变速比例缩放的时间戳; - 封面截取:
TXVideoRecordListener.onRecordComplete(TXRecordCommon.RecordResult result)返回的result.coverPath即首帧截图路径。
接入步骤需严格遵循:
- 将
LiteAVSDK_UGC_4.4.3774.aar放入app/libs/目录; - 在
app/build.gradle中添加:
repositories { flatDir { dirs 'libs' } } dependencies { implementation(name: 'LiteAVSDK_UGC_4.4.3774', ext: 'aar') // 必须添加support库兼容 implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.10.0' }- 初始化SDK(必须在Application.onCreate()中):
TXLiveBase.getInstance().setLicence(this, "your_license_url", "your_license_key"); TXLiveBase.getInstance().setConsoleEnabled(true); // 开启日志便于调试注意:
your_license_url需替换为腾讯云控制台生成的License地址,否则录制时会弹出水印。ti-sdk.aar是配套的AI能力SDK(如人脸检测),若需美颜需同时引入。
2.3 Gradle构建优化:如何让gradlew.bat稳定编译含Native库的项目
gradlew.bat是Windows平台Gradle Wrapper启动脚本,本项目因LiteAVSDK含ARMv7/ARM64/x86_64多架构so库,常出现UnsatisfiedLinkError。关键修复点:
- NDK版本锁定:在
app/build.gradle的android.ndkVersion指定"23.1.7779620"(与LiteAVSDK 4.4.3774兼容); - ABI过滤精简:避免打包无用架构,减少APK体积:
android { defaultConfig { ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' // 移除x86/x86_64 } } }- ProGuard规则补充:LiteAVSDK需保留特定类,否则混淆后崩溃:
-keep class com.tencent.** { *; } -keep class tx.** { *; } -dontwarn com.tencent.**执行gradlew.bat assembleDebug前,建议先运行gradlew.bat clean清除taskArtifacts.bin等旧缓存,避免增量编译错误。
3. 双端协同与服务端对接:ThinkPHP后端如何支撑短视频业务流
3.1 ThinkPHP 5.6的RESTful API设计与伪静态路由
后端采用ThinkPHP 5.6框架,其route/route.php中定义了短视频核心接口:
// 视频上传 Route::post('api/upload', 'api.Upload/upload'); // 获取Feed流(带分页) Route::get('api/feed/:page', 'api.Feed/index'); // 用户点赞 Route::post('api/like', 'api.Like/toggle');伪静态设置通过.htaccess实现URL重写:
RewriteEngine on RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php?route=$1 [QSA,PT,L]这使得https://api.example.com/api/feed/1被重写为index.php?route=api/feed/1,ThinkPHP自动解析路由并调用对应控制器。SEO友好性提升的同时,也规避了?r=api/feed&page=1这类暴露参数的URL。
3.2 MySQL 5.5数据库表结构与短视频业务建模
thinkphp.sql脚本创建了6张核心表,其中video_info和user_action是高频操作对象:
| 表名 | 关键字段 | 业务含义 |
|---|---|---|
video_info | id,user_id,cover_url,video_url,duration,width,height,status | 存储视频元信息,status=1表示审核通过可展示 |
user_action | id,user_id,video_id,action_type(1=like,2=comment),created_time | 记录用户行为,支持按video_id聚合统计点赞数 |
user_info | id,nickname,avatar,follow_count,follower_count | 用户基础信息,follow_count需在关注时触发UPDATE |
查询Feed流的SQL(api/Feed.php中):
SELECT v.*, u.nickname, u.avatar FROM video_info v LEFT JOIN user_info u ON v.user_id = u.id WHERE v.status = 1 ORDER BY v.created_time DESC LIMIT :offset, 20注意:
LIMIT :offset, 20中的:offset由ThinkPHP的input('page')计算得出(offset = (page-1)*20),避免深分页性能问题。若数据量超百万,需改用游标分页(WHERE created_time < ? ORDER BY created_time DESC LIMIT 20)。
3.3 Android端Retrofit网络请求与Token鉴权实践
客户端使用Retrofit 2.9.0对接ThinkPHP后端,关键配置在ApiService.java:
public interface ApiService { @POST("api/upload") Call<UploadResponse> uploadVideo(@Body RequestBody file, @Header("Authorization") String token); @GET("api/feed/{page}") Call<FeedResponse> getFeed(@Path("page") int page, @Header("Authorization") String token); }Token通过登录接口获取并持久化:
// 登录成功后保存token SharedPreferences sp = getSharedPreferences("config", MODE_PRIVATE); sp.edit().putString("token", response.getToken()).apply(); // 构建Retrofit时添加拦截器 OkHttpClient client = new OkHttpClient.Builder() .addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { Request original = chain.request(); String token = sp.getString("token", ""); Request request = original.newBuilder() .header("Authorization", "Bearer " + token) .build(); return chain.proceed(request); } }) .build();后端ThinkPHP通过app/common.php中的checkToken()函数验证JWT:
function checkToken() { $auth = request()->header('authorization'); if (!$auth || strpos($auth, 'Bearer ') !== 0) { return json(['code'=>401, 'msg'=>'Unauthorized']); } $token = substr($auth, 7); // 使用firebase/php-jwt验证签名 try { $decoded = JWT::decode($token, config('jwt.secret'), ['HS256']); return $decoded->uid; // 返回用户ID供后续查询 } catch (Exception $e) { return json(['code'=>401, 'msg'=>'Invalid token']); } }4. iOS端Objective-C与AVFoundation的音视频处理要点
4.1 AVCaptureSession配置:如何平衡预览流畅度与录制画质
iOS端在TCRecordViewController.m中构建采集链路,关键参数组合决定体验:
- 预览分辨率:
session.sessionPreset = AVCaptureSessionPresetPhoto(最高12MP),但实际预览用AVCaptureVideoDataOutput的minFrameDuration控制帧率; - 录制分辨率:
AVCaptureMovieFileOutput的maxRecordedDuration设为60秒,minFreeDiskSpaceLimit设为1073741824(1GB),防存储不足中断; - 前后置切换:调用
[self switchCamera]时,需先[self.session beginConfiguration],再[device lockForConfiguration:nil],最后[self.session commitConfiguration],避免AVCaptureSessionIsRunning状态冲突。
提示:
AVCaptureVideoDataOutput的setSampleBufferDelegate:self queue:dispatch_get_main_queue()]中,delegate方法captureOutput:didOutputSampleBuffer:fromConnection:接收CMSampleBufferRef,需用VTCreateCGImageFromCVPixelBuffer()转为UIImage做实时美颜,但此操作在主线程会卡UI,正确做法是创建独立串行队列:dispatch_queue_t processQueue = dispatch_queue_create("video.process", DISPATCH_QUEUE_SERIAL);。
4.2 AVFoundation与Metal纹理绑定实现零拷贝滤镜
项目未使用Core Image(CPU渲染慢),而是通过MTLTexture与CVMetalTextureCacheCreateTextureFromImage()实现GPU加速:
// 创建Metal纹理缓存 NSError *error; self.textureCache = CVMetalTextureCacheCreate(kCFAllocatorDefault, NULL, self.device, NULL, &error); // 在sampleBufferDelegate中 CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); CVMetalTextureRef textureRef = NULL; CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, self.textureCache, pixelBuffer, NULL, MTLPixelFormatBGRA8Unorm, width, height, 0, &textureRef); // 绑定到Metal渲染管线 id<MTLTexture> texture = CVMetalTextureGetTexture(textureRef); [renderEncoder setTexture:texture atIndex:0]; [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangleStrip vertexStart:0 vertexCount:4];此方案比CIImage快3倍以上,且支持自定义Metal shader(如shaders.metal中的beauty_kernel)。
5. 音视频参数调优与常见故障定位技巧
5.1 编码参数黄金组合表:平衡画质、体积与兼容性
| 参数 | 推荐值 | 影响说明 | 典型故障现象 |
|---|---|---|---|
bitrate | 800kbps (720p) / 1200kbps (1080p) | 码率过低导致马赛克,过高增加CDN成本 | 播放卡顿、首屏超时 |
profile | MediaCodecInfo.CodecProfileLevel.AVCProfileHigh | High Profile支持B帧,压缩率高但部分低端机解码失败 | 华为Mate 20播放黑屏 |
keyInterval | 2秒(即每2秒一个I帧) | I帧过多增大体积,过少影响seek精度 | 快进跳转延迟 > 1s |
rotation | 根据设备orientation动态设MediaFormat.KEY_ROTATION | 不设会导致横屏视频竖播 | 视频画面逆时针旋转90° |
验证编码参数是否生效:用ffprobe -v quiet -show_entries stream=width,height,bit_rate,codec_name,profile -of default video.mp4检查输出。
5.2 腾讯云LiteAVSDK日志分析法:三步定位上传失败
当TXVideoRecordListener.onRecordComplete()返回result.retCode != 0时:
- 开启SDK日志:
TXLiveBase.getInstance().setConsoleEnabled(true),Logcat过滤LiteAV; - 关键错误码速查:
1001:License无效 → 检查setLicence()参数;1002:存储空间不足 →result.desc含"no space",提示用户清理;1003:网络超时 → 检查TXVideoPublishConfig.timeout(默认15s),弱网环境调至30s;
- 抓包确认上传地址:在
TXVideoPublishConfig.publishUrl处加断点,确认URL是否含有效sign参数(腾讯云STS临时凭证)。
注意:
fileHashes.bin记录Gradle构建时的文件哈希,若修改LiteAVSDK_UGC_4.4.3774.aar后未更新此文件,可能导致Duplicate class冲突,此时需手动删除该文件并clean project。
5.3 MySQL慢查询优化:Feed流接口响应超时的根因解决
当api/feed/1响应时间>2s时,执行SHOW PROCESSLIST发现大量Sending data状态:
- 索引缺失:
EXPLAIN SELECT * FROM video_info WHERE status=1 ORDER BY created_time DESC LIMIT 20显示type=ALL(全表扫描); - 修复方案:为
status和created_time建联合索引:
ALTER TABLE video_info ADD INDEX idx_status_time (status, created_time);- 验证效果:
EXPLAIN结果中type=range,rows从10万降至200,QPS从50提升至300。
若仍慢,检查innodb_buffer_pool_size是否足够(建议设为物理内存60%),避免频繁磁盘IO。
本文还有配套的精品资源,点击获取