You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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" />

问题原因分析

  1. 视频字节获取不完整:Android 12+上,AssetEntity.originBytes方法可能无法正确读取完整的视频文件字节,导致上传的视频文件损坏。
  2. 权限适配不足:Android 13+引入了更细分的媒体权限,原有的READ_EXTERNAL_STORAGE权限在高版本系统中已被替代。
  3. 文件访问兼容性: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 13:22:16