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

Flutter使用just_audio播放Google Drive音频链接报错求助

解决just_audio播放Google Drive音频的错误问题

问题说明

尝试通过just_audio插件直接播放存储在Google Drive中的音频文件,传入分享链接后持续出现播放错误。

相关代码

// ignore_for_file: library_private_types_in_public_api, avoid_print

import 'package:connectivity/connectivity.dart';
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import 'package:fluttertoast/fluttertoast.dart';

class HymnTune extends StatefulWidget {
  final String hymnMusicPath;
  const HymnTune({super.key, required this.hymnMusicPath});

  @override
  _HymnTuneState createState() => _HymnTuneState();
}

class _HymnTuneState extends State<HymnTune> with SingleTickerProviderStateMixin {
  late AnimationController iconController;
  bool isPlaying = false;
  double iconSize = 30.0;
  final AudioPlayer hymnTunePlayer = AudioPlayer();

  @override
  void initState() {
    super.initState();
    iconController = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 100),
    );
    loadMusic();
  }

  Future<void> loadMusic() async {
    if (widget.hymnMusicPath.isNotEmpty) {
      try {
        await hymnTunePlayer.setAudioSource(AudioSource.uri(Uri.parse(widget.hymnMusicPath)));
      } catch (e) {
        Fluttertoast.showToast(
          msg: "Error loading audio source: $e",
          textColor: Colors.white,
          backgroundColor: Colors.black45,
          toastLength: Toast.LENGTH_LONG,
          gravity: ToastGravity.BOTTOM,
        );
      }
    }
  }

  Future<bool> checkInternetConnection() async {
    var connectivityResult = await Connectivity().checkConnectivity();
    return connectivityResult != ConnectivityResult.none;
  }

  void showNoInternetToast() {
    Fluttertoast.showToast(
      msg: 'No Internet Connection',
      textColor: Colors.white,
      backgroundColor: Colors.black45,
      toastLength: Toast.LENGTH_SHORT,
      gravity: ToastGravity.BOTTOM,
    );
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () => animateIcon(),
      child: AnimatedIcon(
        icon: AnimatedIcons.play_pause,
        progress: iconController,
        size: iconSize,
      ),
    );
  }

  void animateIcon() async {
    if (widget.hymnMusicPath.isNotEmpty) {
      bool isConnected = await checkInternetConnection();

      if (!isConnected) {
        showNoInternetToast();
        return;
      }

      setState(() {
        isPlaying = !isPlaying;
      });

      if (isPlaying) {
        await iconController.forward();
        await hymnTunePlayer.play();
      } else {
        await iconController.reverse();
        await hymnTunePlayer.pause();
      }
    }
  }

  @override
  void dispose() {
    hymnTunePlayer.dispose();
    iconController.dispose();
    super.dispose();
  }
}

错误日志

E/ExoPlayerImplInternal( 9612):       at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:549)
E/ExoPlayerImplInternal( 9612):       at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:105)
E/ExoPlayerImplInternal( 9612):       at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:26)
E/ExoPlayerImplInternal( 9612):       at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:559)
E/ExoPlayerImplInternal( 9612):       at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:376)
E/ExoPlayerImplInternal( 9612):       ... 7 more
E/ExoPlayerImplInternal( 9612):   Caused by: java.net.SocketException: socket is closed
E/ExoPlayerImplInternal( 9612):       at com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream.read(ConscryptFileDescriptorSocket.java:551)
E/ExoPlayerImplInternal( 9612):       at com.android.okhttp.okio.Okio$2.read(Okio.java:136)
E/ExoPlayerImplInternal( 9612):       at com.android.okhttp.okio.AsyncTimeout$2.read(AsyncTimeout.java:211)
E/ExoPlayerImplInternal( 9612):       ... 21 more
E/AudioPlayer( 9612): TYPE_SOURCE: java.net.SocketTimeoutException: timeout
I/ExoPlayerImpl( 9612): Release 59b3258 [ExoPlayerLib/2.18.7] [Infinix-X626B, Infinix X626B, INFINIX MOBILITY LIMITED, 28] [goog.exo.core, goog.exo.exoplayer, goog.exo.decoder, goog.exo.datasource]
I/ExoPlayerImpl( 9612): Init a576746 [ExoPlayerLib/2.18.7] [Infinix-X626B, Infinix X626B, INFINIX MOBILITY LIMITED, 28]
D/ViewRootImplToast: hardware acceleration = true , fakeHwAccelerated = false, sRendererDisabled = false, forceHwAccelerated = false, sSystemRendererDisabled = false
D/Surface ( 9612): Surface::allocateBuffers(this=0x72d948e000)
D/OpenSSLLib( 9612): OpensslErr:Module:30(110:); file:external/boringssl/src/crypto/fipsmodule/cipher/cipher.c ;Line:563;Function:EVP_CIPHER_CTX_set_key_length
W/RenderThread( 9612): type=1400 audit(0.0:1891): avc: denied { search } for name="battery" dev="sysfs" ino=6960 scontext=u:r:untrusted_app:s0:c118,c256,c512,c768 tcontext=u:object_r:sysfs_batteryinfo:s0 tclass=dir permissive=0
D/Surface ( 9612): Surface::connect(this=0x72d948e000,api=1)
D/Surface ( 9612): Surface::disconnect(this=0x72d948e000,api=1)
D/View    ( 9612): [Warning] assignParent to null: this = android.widget.FrameLayout{212b1d2 V.E...... ......ID 0,0-525,73}

解决方案

  • 修正Google Drive链接格式:默认的Google Drive分享链接(如https://drive.google.com/file/d/[FILE_ID]/view)无法直接被音频播放器识别,需要转换为可直接访问的资源链接。将链接替换为:https://drive.google.com/uc?export=open&id=[FILE_ID],其中[FILE_ID]是原链接中/d/和/view之间的字符串。同时确保音频文件的分享权限设置为「任何有链接的人都可以查看」。
  • 处理超时问题:错误日志中出现SocketTimeoutException,可通过配置just_audio的数据源参数延长超时时间。修改loadMusic方法,自定义HttpDataSource:
    await hymnTunePlayer.setAudioSource(
      AudioSource.uri(
        Uri.parse(widget.hymnMusicPath),
        headers: {},
      ),
      dataSourceResolver: (Uri uri) async {
        return HttpDataSource(
          uri,
          connectTimeout: const Duration(seconds: 10),
          readTimeout: const Duration(seconds: 10),
        );
      },
    );
    
  • 优化音频加载逻辑:避免在initState中过早加载音频,可改为在用户点击播放按钮时再加载,防止链接过期或网络波动导致的加载失败。修改animateIcon方法,在播放前检查音频是否已加载,未加载则重新加载:
    void animateIcon() async {
      if (widget.hymnMusicPath.isNotEmpty) {
        bool isConnected = await checkInternetConnection();
        if (!isConnected) {
          showNoInternetToast();
          return;
        }
    
        setState(() {
          isPlaying = !isPlaying;
        });
    
        if (isPlaying) {
          if (hymnTunePlayer.processingState == ProcessingState.idle) {
            await loadMusic();
          }
          await iconController.forward();
          await hymnTunePlayer.play();
        } else {
          await iconController.reverse();
          await hymnTunePlayer.pause();
        }
      }
    }
    
  • 验证链接可用性:在浏览器中打开转换后的链接,确认能直接播放或下载音频,排除链接本身的访问问题。

内容的提问来源于stack exchange,提问作者Igwe Ugochukwu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 01:25:55