Android 12+ 中VideoPlayer无法播放Firebase视频但iOS正常
Android 12+ 无法播放Firebase Storage上传的视频(iOS正常)
问题背景
通过PhotoManager获取List<AssetEntity>媒体资源,将其转换为字节后上传至Firebase Storage。iOS端使用VideoPlayer可正常播放上传后的视频,但Android 12+设备无法正常加载播放。
相关代码
上传媒体方法
Future<String?> uploadMedia({ required List<AssetEntity> assets, required String profileId, required String albumId, }) async { await firebaseErrorHandler(() async { Reference imagesPath = s.ref("$albumId/Images"); Reference videoPath = s.ref("$albumId/Videos"); Reference thumbnailPath = s.ref("$albumId/Thumbnails"); CollectionReference docRef = mediaCollection(albumId); List<Future<String>> futures = assets.map((item) async { final bool isVideo = item.type == AssetType.video; final Uint8List? bytes = await item.originBytes; late Reference mediaRef; switch (item.type) { case AssetType.image: mediaRef = imagesPath.child( '${item.createDateTime.toIso8601String()}.jpg', ); break; case AssetType.video: mediaRef = videoPath.child( '${item.createDateTime.toIso8601String()}.mp4', ); break; default: throw Exception("Unsupported asset type."); } late String downloadUrl; if (bytes != null) { UploadTask task = mediaRef.putData( bytes, SettableMetadata( contentType: isVideo ? 'video/mp4' : 'image/jpeg', ), ); TaskSnapshot snap = await task; downloadUrl = await snap.ref.getDownloadURL(); } else { throw Exception("${item.id} is invalid."); } final Uint8List? thumbnailBytes = await item.thumbnailData; Reference thumbnailRef = thumbnailPath.child( item.createDateTime.toIso8601String(), ); String thumbnailUrl = ''; if (isVideo) { if (thumbnailBytes != null) { UploadTask thumbTask = thumbnailRef.putData( thumbnailBytes, SettableMetadata(contentType: 'image/jpeg'), ); TaskSnapshot thumbSnap = await thumbTask; thumbnailUrl = await thumbSnap.ref.getDownloadURL(); } else { UploadTask thumbTask = thumbnailRef.putData( customThumbnail, SettableMetadata(contentType: 'image/jpeg'), ); TaskSnapshot thumbSnap = await thumbTask; thumbnailUrl = await thumbSnap.ref.getDownloadURL(); } } late GeoPoint? geoPoint; if (item.latitude != null || item.longitude != null) { geoPoint = GeoPoint(item.latitude!, item.longitude!); } await docRef.add(Media( docId: docRef.id, parentAlbumId: albumId, downloadUrl: downloadUrl, profileId: profileId, dateTime: DateTime.now(), mediaType: isVideo ? 'video' : 'image', exifDateTime: item.createDateTime, exifLocation: geoPoint, thumbnailUrl: thumbnailUrl, ).toMap()); return isVideo ? thumbnailUrl : downloadUrl; }).toList(); List<String> uploadedUrls = await Future.wait(futures); if (uploadedUrls.isNotEmpty) { await albumCollection().doc(albumId).update({ 'thumbnail': uploadedUrls.last, 'dateUpdated': DateTime.now(), }); } }).then((err) { debugPrint(err); if (err != null) return err; }); }
AndroidManifest.xml权限配置
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
问题原因分析
- 视频字节获取不完整:Android 12+上,
AssetEntity.originBytes方法可能无法正确读取完整的视频文件字节,导致上传的视频文件损坏。 - 权限适配不足:Android 13+引入了更细分的媒体权限,原有的
READ_EXTERNAL_STORAGE权限在高版本系统中已被替代。 - 文件访问兼容性:Android 10+的分区存储机制,未启用
requestLegacyExternalStorage可能导致文件读取异常。
解决方案
1. 替换视频字节获取方式
将视频的字节获取从originBytes改为通过本地文件读取,避免字节截断问题:
// 替换原有的bytes获取逻辑 Uint8List? bytes; if (isVideo) { final File? videoFile = await item.file; if (videoFile != null) { bytes = await videoFile.readAsBytes(); } } else { bytes = await item.originBytes; }
2. 更新Android权限配置
适配Android 13+的媒体权限,并添加分区存储兼容配置:
<uses-permission android:name="android.permission.INTERNET"/> <!-- 适配Android 12及以下 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- 适配Android 13+ --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" android:minSdkVersion="33" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" android:minSdkVersion="33" /> <application ... android:requestLegacyExternalStorage="true"> <!-- 其他配置 --> </application>
3. 验证上传视频的完整性
上传完成后,将Firebase Storage中的视频下载到Android本地,用系统播放器测试是否能正常播放,确认是上传过程还是播放环节的问题。
4. 检查VideoPlayer配置
确保Android项目中VideoPlayer的依赖配置正确,同时在布局文件中启用硬件加速(默认已开启,可显式声明):
<androidx.constraintlayout.widget.ConstraintLayout ... android:hardwareAccelerated="true"> <!-- VideoPlayer视图 --> </androidx.constraintlayout.widget.ConstraintLayout>
内容的提问来源于stack exchange,提问作者Spencer
相关产品推荐
相关产品推荐

