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

如何从其他Android设备流式传输音频?本地URL服务器搭建及问题排查

方案一:在Android设备搭建轻量HTTP服务器共享音频文件

这种方式最直接,因为MediaPlayer原生支持HTTP URL数据源,只要把音频文件通过HTTP服务暴露出去,其他设备就能直接用setDataSource("http://[设备IP]:[端口]/[文件名]")播放。

实现步骤:

  1. 添加轻量HTTP服务器依赖(比如NanoHTTPD,体积小无需额外权限)
    在app的build.gradle中添加:

    dependencies {
        implementation 'org.nanohttpd:nanohttpd:2.3.1'
    }
    
  2. 编写自定义HTTP服务器类
    继承NanoHTTPD,处理文件请求:

    public class AudioFileServer extends NanoHTTPD {
        private File audioDir;
    
        public AudioFileServer(int port, File audioDir) {
            super(port);
            this.audioDir = audioDir;
        }
    
        @Override
        public Response serve(IHTTPSession session) {
            String uri = session.getUri();
            // 简化处理,直接根据URI匹配音频文件
            File audioFile = new File(audioDir, uri.substring(1)); // 去掉开头的/
    
            if (audioFile.exists() && audioFile.isFile()) {
                try {
                    return newFixedLengthResponse(Response.Status.OK, "audio/mpeg", new FileInputStream(audioFile), audioFile.length());
                } catch (FileNotFoundException e) {
                    return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "File not found");
                }
            } else {
                return newFixedLengthResponse(Response.Status.NOT_FOUND, "text/plain", "File not found");
            }
        }
    }
    
  3. 启动服务器
    在Activity或Service中初始化并启动:

    // 假设音频文件存在手机的外部存储音乐目录下
    File audioDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC).getAbsolutePath());
    AudioFileServer server = new AudioFileServer(8080, audioDir);
    try {
        server.start();
        Log.d("Server", "HTTP server started at http://" + getLocalIpAddress() + ":8080");
    } catch (IOException e) {
        e.printStackTrace();
    }
    
  4. 获取本地IP地址的工具方法

    private String getLocalIpAddress() {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
                        return inetAddress.getHostAddress();
                    }
                }
            }
        } catch (SocketException ex) {
            ex.printStackTrace();
        }
        return null;
    }
    
  5. 其他设备播放
    拿到服务器设备的IP后,直接在客户端设备调用:

    MediaPlayer mediaPlayer = new MediaPlayer();
    mediaPlayer.setDataSource("http://192.168.1.100:8080/test.mp3"); // 替换成实际IP和文件名
    mediaPlayer.prepareAsync();
    mediaPlayer.setOnPreparedListener(MediaPlayer::start);
    

注意:要申请READ_EXTERNAL_STORAGE(Android 12及以下)或READ_MEDIA_AUDIO(Android 13+)权限,以及INTERNET权限。


方案二:通过Socket实时流式传输音频数据

如果不需要持久化文件共享,而是要实时传输音频(比如实时录音并传输),可以用Socket直接传输音频字节流。但MediaPlayer不能直接用Socket输入流,需要通过管道流做适配:

发送端(音频源设备)

读取音频文件的字节,分块发送到Socket:

// 启动Socket服务端,放在子线程执行
new Thread(() -> {
    try {
        ServerSocket serverSocket = new ServerSocket(8080);
        Socket clientSocket = serverSocket.accept();
        OutputStream outputStream = clientSocket.getOutputStream();

        File audioFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC), "test.mp3");
        FileInputStream fis = new FileInputStream(audioFile);
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = fis.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
            outputStream.flush();
        }

        fis.close();
        outputStream.close();
        clientSocket.close();
        serverSocket.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}).start();

接收端(播放设备)

通过Socket接收字节流,用PipeInputStream和PipeOutputStream桥接,让MediaPlayer可以读取:

// 放在子线程执行,避免阻塞主线程
new Thread(() -> {
    try {
        Socket socket = new Socket("192.168.1.100", 8080); // 替换成发送端IP
        InputStream inputStream = socket.getInputStream();

        PipeInputStream pipeIn = new PipeInputStream();
        PipeOutputStream pipeOut = new PipeOutputStream(pipeIn);

        // 启动子线程把Socket输入流写入管道
        new Thread(() -> {
            byte[] buffer = new byte[4096];
            int bytesRead;
            try {
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    pipeOut.write(buffer, 0, bytesRead);
                    pipeOut.flush();
                }
                pipeOut.close();
                inputStream.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();

        // 用MediaPlayer播放管道流
        MediaPlayer mediaPlayer = new MediaPlayer();
        mediaPlayer.setDataSource(pipeIn);
        mediaPlayer.prepareAsync();
        mediaPlayer.setOnPreparedListener(MediaPlayer::start);
    } catch (IOException e) {
        e.printStackTrace();
    }
}).start();

关于你之前的Socket.io问题

MediaPlayer只支持HTTP、RTSP、本地文件等标准数据源,Socket.io是基于WebSocket的自定义协议,MediaPlayer无法识别这种URL格式,所以直接设置setDataSource("IP:8080/路径")会报错,必须用上述两种适配方式。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 12:52:48