Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

expo-dlna-player

firefly0621104MIT0.7.0TypeScript support: included

A React Native/Expo module for DLNA/AirPlay media casting to smart TVs and streaming devices

react-native, expo, expo-dlna-player, ExpoDlnaPlayer, dlna, upnp, airplay, miracast, cast, screencast, streaming, media, video, tv, smart-tv

readme

expo-dlna-player

一个用于React Native和Expo应用的DLNA/AirPlay/Miracast投屏播放组件,支持发现和连接媒体设备进行多媒体投屏播放。

功能特点

  • 支持DLNA、AirPlay和Miracast设备的发现和连接
  • 多媒体投屏播放(视频/音频/图片)
  • 全面的播放控制(播放、暂停、恢复、停止、进度控制)
  • 高级媒体控制(播放速率调整、静音、缓冲状态监控)
  • 音量控制和状态监控
  • 专为iOS优化的AirPlay原生体验
  • Android平台的Miracast屏幕镜像支持
  • 完整的事件系统,实时反馈设备和播放状态
  • 跨平台支持(iOS和Android)

API文档

安装(Expo托管项目)

对于托管Expo项目,请按照最新稳定版本的API文档中的安装说明进行操作。如果您点击链接但没有可用的文档,那么此库尚不能在托管项目中使用 — 它可能会在即将发布的Expo SDK版本中包含。

安装(裸React Native项目)

对于裸React Native项目,您必须确保已经安装并配置了expo

添加依赖包

npm install expo-dlna-player
# 或者
yarn add expo-dlna-player

Android配置

android/app/src/main/AndroidManifest.xml中添加以下权限:

<!-- DLNA/UPnP相关权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />

<!-- Miracast相关权限(如需使用) -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

iOS配置

运行npx pod-install安装原生依赖。

Info.plist中添加以下服务描述:

<key>NSLocalNetworkUsageDescription</key>
<string>需要使用本地网络来发现并连接到DLNA/AirPlay设备</string>
<key>NSBonjourServices</key>
<array>
    <string>_airplay._tcp</string>
    <string>_raop._tcp</string>
</array>

基本用法

import ExpoDlnaPlayer, { 
  DeviceInfo, 
  PlaybackStatus,
  startDiscovery,
  connectToDevice,
  play,
  pause,
  resume,
  stop,
  seek,
  setVolume,
  setPlaybackRate,
  setMuted,
  getBufferingStatus
} from 'expo-dlna-player';
import { useState, useEffect } from 'react';

function App() {
  const [devices, setDevices] = useState([]);
  const [playbackStatus, setPlaybackStatus] = useState(null);

  // 开始搜索设备
  const startDeviceDiscovery = async () => {
    try {
      // 可以直接使用模块方法
      await ExpoDlnaPlayer.startDiscovery();
      // 或使用辅助函数
      // await startDiscovery();
    } catch (error) {
      console.error("搜索设备失败:", error);
    }
  };

  // 设备发现事件监听
  useEffect(() => {
    // 注册设备发现监听器
    const deviceFoundSubscription = ExpoDlnaPlayer.addListener('onDeviceFound', (device) => {
      console.log('发现设备:', device.name, device.type);
      setDevices(prev => [...prev.filter(d => d.id !== device.id), device]);
    });

    // 注册播放状态监听器
    const playbackSubscription = ExpoDlnaPlayer.addListener('onPlaybackStatusChanged', (status) => {
      setPlaybackStatus(status);
    });

    // 开始搜索
    startDeviceDiscovery();

    // 清理
    return () => {
      deviceFoundSubscription.remove();
      playbackSubscription.remove();
      ExpoDlnaPlayer.stopDiscovery();
    };
  }, []);

  // 连接设备并播放媒体
  const playMedia = async (deviceId) => {
    try {
      // 连接到设备
      const connected = await connectToDevice(deviceId);
      if (connected) {
        // 播放媒体
        await play(
          "https://example.com/video.mp4", 
          "测试视频", 
          "video/mp4"
        );

        // 两秒后调整播放速率
        setTimeout(async () => {
          await setPlaybackRate(1.5);
          console.log('播放速率已设置为1.5倍');
        }, 2000);
      }
    } catch (error) {
      console.error("播放失败:", error);
    }
  };

  // 播放控制示例
  const controlPlayback = async () => {
    if (!playbackStatus) return;

    if (playbackStatus.isPlaying) {
      await pause();
    } else {
      await resume();
    }
  };

  // 跳转到30秒位置
  const seekToPosition = async () => {
    await seek(30);
  };

  return (
    // 渲染UI...
  );
}

API参考

设备发现

  • startDiscovery() - 开始搜索媒体设备
  • stopDiscovery() - 停止搜索
  • getDevices() - 获取已发现的设备列表

连接与控制

  • connectToDevice(deviceId) - 连接到指定设备
  • disconnectFromDevice() - 断开当前连接
  • isConnected() - 检查是否已连接
  • getConnectedDevice() - 获取当前连接的设备

基本媒体控制

  • play(url, title, mimeType) - 播放媒体
  • pause() - 暂停播放
  • resume() - 恢复播放
  • stop() - 停止播放
  • seek(position) - 定位到指定位置(单位:秒)
  • setVolume(volume) - 设置音量(范围:0-100)
  • getPlaybackStatus() - 获取当前播放状态

高级媒体控制

  • setPlaybackRate(rate) - 设置播放速率(范围:0.5-2.0)
  • setMuted(muted) - 设置静音状态
  • getBufferingStatus() - 获取媒体缓冲状态

Android特定功能 (Miracast)

  • startProjection(deviceId, mode) - 开始投屏(模式:"SCREEN_MIRRORING"或"VIDEO_ONLY")
  • stopProjection() - 停止投屏
  • isProjectionSupported() - 检查设备是否支持投屏

事件

可以通过ExpoDlnaPlayer.addListener方法订阅以下事件:

事件名 描述 参数
onDeviceFound 发现新设备 DeviceInfo
onDeviceDisappeared 设备离线 deviceId: string
onConnectionChanged 连接状态变化 { deviceId: string, connected: boolean }
onPlaybackStatusChanged 播放状态更新 PlaybackStatus
onError 发生错误 { code: string, message: string, deviceId?: string }
onPermissionStatus 权限状态变更 { permission: string, status: string }

PlaybackStatus 对象

播放状态对象包含以下属性:

{
  isPlaying: boolean;      // 是否正在播放
  duration: number;        // 总时长(秒)
  position: number;        // 当前位置(秒)
  volume: number;          // 音量(0-100)
  rate?: number;           // 播放速率
  isMuted?: boolean;       // 是否静音
  isBuffering?: boolean;   // 是否正在缓冲
  isCompleted?: boolean;   // 是否播放完成
  error?: string;          // 错误信息
}

使用提示

AirPlay使用方式

对于iOS设备上的AirPlay功能,需要注意以下几点:

  1. 使用AirPlayButton组件(推荐方式): 由于iOS的限制,不能通过代码直接触发AirPlay选择器。必须使用AirPlayButton组件在UI中提供一个用户可以点击的元素:

    import { AirPlayButton } from 'expo-dlna-player';
    
    // 在您的渲染函数中
    return (
      <View>
        {/* 其他UI元素 */}
        <AirPlayButton style={styles.airplayButton} />
      </View>
    );

    用户点击此按钮时,iOS将显示系统AirPlay设备选择器。这是苹果推荐的方式,符合iOS的设计规范。

  2. 使用发现和连接API(适用于DLNA和其他设备)

    // 通过设备发现和连接API
    await startDiscovery();
    // 获取设备列表并在UI中展示
    const devices = await getDevices();
    // 用户选择设备后...
    await connectToDevice(deviceId);

虽然showAirPlayPicker()方法在API中存在,但由于iOS的限制,它在大多数情况下无法按预期工作。请始终使用AirPlayButton组件提供用户交互界面。

AirPlay使用方式

对于iOS设备上的AirPlay功能,您必须使用AirPlayButton组件:

  1. 使用AirPlayButton组件: 为了符合Apple的应用商店指南,必须使用AirPlayButton组件在UI中提供用户可点击的元素:

    import { AirPlayButton } from 'expo-dlna-player';
    
    // 在您的渲染函数中
    return (
      <View>
        {/* 其他UI元素 */}
        <AirPlayButton style={styles.airplayButton} />
      </View>
    );

    用户点击此按钮时,iOS将显示系统AirPlay设备选择器。这是苹果唯一允许的方式,完全符合iOS的设计规范。

DLNA设备最佳实践

DLNA设备需要确保拥有有效的控制URL:

// 确保DLNA设备有控制URL
const devices = await getDevices();
const dlnaDevices = devices.filter(d => 
  d.type === 'dlna' && d.controlURL
);

完整示例

查看example目录获取完整的示例应用。

贡献

欢迎贡献!请参考贡献指南中描述的准则。