# react-native-dlna-player
**Repository Path**: uking/react-native-dlna-player
## Basic Information
- **Project Name**: react-native-dlna-player
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2025-03-13
- **Last Updated**: 2025-11-26
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# react-native-dlna-player
DLNA视频播放器,支持Android和iOS投屏功能
## 功能特点
- 支持Android设备使用DLNA协议投屏
- 支持iOS设备使用AirPlay投屏
- 支持搜索、连接、断开设备
- 支持播放、暂停、恢复、停止视频
- 支持跳转到指定位置
- 支持获取播放状态
- 支持设置音量和静音
- 高可用、高定制、兼容性好
- 提供完善的错误处理机制
## 安装
```sh
npm install react-native-dlna-player
# 或
yarn add react-native-dlna-player
```
### Expo 支持
从 0.7.0 版本开始,本库支持在 Expo 项目中使用。在 Expo 项目中使用时,请按照以下步骤进行配置:
#### 安装
```sh
expo install react-native-dlna-player
```
#### 配置
在 Expo 项目的 `app.config.js` 或 `app.json` 中添加插件配置:
**app.config.js 示例:**
```js
const { withDlnaPlayer } = require('react-native-dlna-player');
module.exports = ({ config }) => {
// 您的基本配置...
return withDlnaPlayer(config);
};
```
**app.json 示例:**
```json
{
"expo": {
"name": "您的应用名称",
"plugins": [
"react-native-dlna-player"
]
}
}
```
这将自动为您的应用添加所需的权限和配置。
#### 重要说明
- 在 Expo 开发模式下,DLNA/AirPlay 功能可能会受到限制。建议使用 Expo 开发构建(Development Build)或 EAS 构建进行测试。
- 确保 Expo SDK 版本 ≥ 45.0.0。
### Android 配置
在 `android/app/src/main/AndroidManifest.xml` 中添加以下权限:
```xml
```
### iOS 配置
在 `ios/Podfile` 中添加:
```ruby
pod 'react-native-dlna-player', :path => '../node_modules/react-native-dlna-player'
```
然后运行:
```sh
cd ios && pod install
```
## API 说明
本库提供了一套标准的API,用于DLNA设备的发现、连接和控制。所有方法都通过`DlnaPlayer`对象导出。
> **重要说明**:从0.2.0版本开始,我们统一了API设计,推荐使用以下标准API。旧版API仍然可用,但在未来版本中可能会移除。
### 标准API(推荐使用)
```typescript
// 导入库
import DlnaPlayer, { Device, PlaybackStatus, DlnaPlayerEventType } from 'react-native-dlna-player';
// 搜索设备
const searchDevices = async () => {
try {
const devices = await DlnaPlayer.searchDevices();
console.log('发现设备:', devices);
} catch (error) {
console.error('搜索设备失败:', error);
}
};
// 连接设备
const connectToDevice = async (deviceId) => {
try {
const connected = await DlnaPlayer.connectToDevice(deviceId);
console.log('连接状态:', connected ? '已连接' : '连接失败');
} catch (error) {
console.error('连接设备失败:', error);
}
};
// 播放视频
const playVideo = async (url, title) => {
try {
const played = await DlnaPlayer.playVideo(url, title);
console.log('播放状态:', played ? '已开始播放' : '播放失败');
} catch (error) {
console.error('播放视频失败:', error);
}
};
// 暂停、恢复、停止
await DlnaPlayer.pauseVideo();
await DlnaPlayer.resumeVideo();
await DlnaPlayer.stopVideo();
// 跳转到指定位置(秒)
await DlnaPlayer.seekTo(120);
// 获取播放状态
const status = await DlnaPlayer.getPlaybackStatus();
// 设置音量(0-100)
await DlnaPlayer.setVolume(80);
// 获取音量
const volume = await DlnaPlayer.getVolume();
// 设置静音
await DlnaPlayer.setMute(true);
// 检查权限
const permissionResult = await DlnaPlayer.checkPermissions();
// 请求权限
const granted = await DlnaPlayer.requestPermissions();
// 监听事件
const subscription = DlnaPlayer.addEventListener(
DlnaPlayerEventType.DEVICE_FOUND,
(device) => {
console.log('发现新设备:', device);
}
);
// 移除事件监听
DlnaPlayer.removeEventListener(subscription);
```
## 使用方法
```javascript
import DlnaPlayer from 'react-native-dlna-player';
// 搜索设备
const searchDevices = async () => {
try {
const devices = await DlnaPlayer.searchDevices();
console.log('发现设备:', devices);
return devices;
} catch (error) {
console.error('搜索设备失败:', error);
return [];
}
};
// 连接设备
const connectToDevice = async (deviceId) => {
try {
const result = await DlnaPlayer.connectToDevice(deviceId);
console.log('连接设备结果:', result);
return result;
} catch (error) {
console.error('连接设备失败:', error);
return false;
}
};
// 断开设备连接
const disconnectFromDevice = async () => {
try {
const result = await DlnaPlayer.disconnectFromDevice();
console.log('断开设备连接结果:', result);
return result;
} catch (error) {
console.error('断开设备连接失败:', error);
return false;
}
};
// 播放视频
const playVideo = async (url, title, subtitle, coverUrl) => {
try {
const result = await DlnaPlayer.playVideo(url, title, subtitle, coverUrl);
console.log('播放视频结果:', result);
return result;
} catch (error) {
console.error('播放视频失败:', error);
return false;
}
};
// 暂停播放
const pauseVideo = async () => {
try {
const result = await DlnaPlayer.pauseVideo();
console.log('暂停播放结果:', result);
return result;
} catch (error) {
console.error('暂停播放失败:', error);
return false;
}
};
// 恢复播放
const resumeVideo = async () => {
try {
const result = await DlnaPlayer.resumeVideo();
console.log('恢复播放结果:', result);
return result;
} catch (error) {
console.error('恢复播放失败:', error);
return false;
}
};
// 停止播放
const stopVideo = async () => {
try {
const result = await DlnaPlayer.stopVideo();
console.log('停止播放结果:', result);
return result;
} catch (error) {
console.error('停止播放失败:', error);
return false;
}
};
// 跳转到指定位置
const seekTo = async (position) => {
try {
const result = await DlnaPlayer.seekTo(position);
console.log('跳转结果:', result);
return result;
} catch (error) {
console.error('跳转失败:', error);
return false;
}
};
// 获取播放状态
const getPlaybackStatus = async () => {
try {
const status = await DlnaPlayer.getPlaybackStatus();
console.log('播放状态:', status);
return status;
} catch (error) {
console.error('获取播放状态失败:', error);
return null;
}
};
// 设置音量
const setVolume = async (volume) => {
try {
const result = await DlnaPlayer.setVolume(volume);
console.log('设置音量结果:', result);
return result;
} catch (error) {
console.error('设置音量失败:', error);
return false;
}
};
// 获取音量
const getVolume = async () => {
try {
const volume = await DlnaPlayer.getVolume();
console.log('当前音量:', volume);
return volume;
} catch (error) {
console.error('获取音量失败:', error);
return 0;
}
};
// 设置静音
const setMute = async (mute) => {
try {
const result = await DlnaPlayer.setMute(mute);
console.log('设置静音结果:', result);
return result;
} catch (error) {
console.error('设置静音失败:', error);
return false;
}
};
// 获取静音状态
const getMute = async () => {
try {
const mute = await DlnaPlayer.getMute();
console.log('当前静音状态:', mute);
return mute;
} catch (error) {
console.error('获取静音状态失败:', error);
return false;
}
};
```
## 示例
```javascript
import React, { useState, useEffect } from 'react';
import { View, Text, Button, FlatList, StyleSheet } from 'react-native';
import DlnaPlayer from 'react-native-dlna-player';
const DlnaPlayerExample = () => {
const [devices, setDevices] = useState([]);
const [currentDevice, setCurrentDevice] = useState(null);
const [isPlaying, setIsPlaying] = useState(false);
const [playbackStatus, setPlaybackStatus] = useState(null);
// 搜索设备
const handleSearchDevices = async () => {
const foundDevices = await DlnaPlayer.searchDevices();
setDevices(foundDevices);
};
// 连接设备
const handleConnectDevice = async (device) => {
const result = await DlnaPlayer.connectToDevice(device.id);
if (result) {
setCurrentDevice(device);
}
};
// 断开设备连接
const handleDisconnectDevice = async () => {
const result = await DlnaPlayer.disconnectFromDevice();
if (result) {
setCurrentDevice(null);
setIsPlaying(false);
setPlaybackStatus(null);
}
};
// 播放视频
const handlePlayVideo = async () => {
const videoUrl = 'https://example.com/video.mp4';
const result = await DlnaPlayer.playVideo(
videoUrl,
'示例视频',
'这是一个示例视频',
'https://example.com/cover.jpg'
);
if (result) {
setIsPlaying(true);
}
};
// 暂停/恢复播放
const handleTogglePlay = async () => {
if (isPlaying) {
const result = await DlnaPlayer.pauseVideo();
if (result) {
setIsPlaying(false);
}
} else {
const result = await DlnaPlayer.resumeVideo();
if (result) {
setIsPlaying(true);
}
}
};
// 停止播放
const handleStopVideo = async () => {
const result = await DlnaPlayer.stopVideo();
if (result) {
setIsPlaying(false);
}
};
// 获取播放状态
const getStatus = async () => {
if (currentDevice) {
const status = await DlnaPlayer.getPlaybackStatus();
setPlaybackStatus(status);
}
};
// 定期获取播放状态
useEffect(() => {
let interval;
if (currentDevice && isPlaying) {
interval = setInterval(getStatus, 1000);
}
return () => {
if (interval) {
clearInterval(interval);
}
};
}, [currentDevice, isPlaying]);
return (
DLNA播放器示例
设备列表:
item.id}
renderItem={({ item }) => (
{item.name} ({item.type})
)}
ListEmptyComponent={未找到设备}
/>
{currentDevice && (
当前设备: {currentDevice.name}
{playbackStatus && (
播放状态: {playbackStatus.isPlaying ? '播放中' : '已暂停'}
进度: {Math.floor(playbackStatus.position)}秒 / {Math.floor(playbackStatus.duration)}秒
)}
)}
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
padding: 16,
},
title: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 16,
},
sectionTitle: {
fontSize: 16,
fontWeight: 'bold',
marginTop: 16,
marginBottom: 8,
},
deviceItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 8,
borderBottomWidth: 1,
borderBottomColor: '#eee',
},
controlSection: {
marginTop: 24,
padding: 16,
backgroundColor: '#f5f5f5',
borderRadius: 8,
},
playerControls: {
flexDirection: 'row',
justifyContent: 'space-between',
marginTop: 16,
},
statusSection: {
marginTop: 16,
padding: 8,
backgroundColor: '#fff',
borderRadius: 4,
},
});
export default DlnaPlayerExample;
```
## API
### 设备相关
#### `searchDevices(): Promise`
搜索可用的DLNA/AirPlay设备。
返回设备列表,每个设备包含以下属性:
- `id`: 设备ID
- `name`: 设备名称
- `ipAddress`: 设备IP地址
- `type`: 设备类型('dlna' 或 'airplay')
- `model`: 设备型号(可选)
- `manufacturer`: 设备制造商(可选)
#### `connectToDevice(deviceId: string): Promise`
连接到指定设备。
#### `disconnectFromDevice(): Promise`
断开当前设备连接。
### 播放控制
#### `playVideo(url: string, title?: string, subtitle?: string, coverUrl?: string): Promise`
播放视频。
#### `pauseVideo(): Promise`
暂停播放。
#### `resumeVideo(): Promise`
恢复播放。
#### `stopVideo(): Promise`
停止播放。
#### `seekTo(position: number): Promise`
跳转到指定位置(秒)。
#### `getPlaybackStatus(): Promise`
获取播放状态,返回以下属性:
- `isPlaying`: 是否正在播放
- `position`: 当前播放位置(秒)
- `duration`: 视频总时长(秒)
- `isBuffering`: 是否正在缓冲
- `isCompleted`: 是否播放完成
- `isError`: 是否出错
- `errorMessage`: 错误信息(可选)
### 音量控制
#### `setVolume(volume: number): Promise`
设置音量(0-100)。
#### `getVolume(): Promise`
获取当前音量(0-100)。
#### `setMute(mute: boolean): Promise`
设置静音状态。
#### `getMute(): Promise`
获取当前静音状态。
## 错误处理
从0.2.0版本开始,库提供了完善的错误处理机制。所有API方法都会抛出类型化的错误,使开发者可以精确地处理各种错误情况。
### 错误类型
所有错误都是`DlnaError`类型,它继承自JavaScript的`Error`类,并添加了额外的属性:
```typescript
class DlnaError extends Error {
// 错误代码,对应DlnaErrorCode枚举
code: DlnaErrorCode;
// 额外的错误详情
details?: any;
}
```
### 错误代码
```typescript
enum DlnaErrorCode {
// 未知错误
UNKNOWN_ERROR = 'UNKNOWN_ERROR',
// 设备未找到
DEVICE_NOT_FOUND = 'DEVICE_NOT_FOUND',
// 设备连接失败
CONNECTION_FAILED = 'CONNECTION_FAILED',
// 设备断开连接失败
DISCONNECTION_FAILED = 'DISCONNECTION_FAILED',
// 无效参数
INVALID_PARAMETER = 'INVALID_PARAMETER',
// 播放失败
PLAYBACK_FAILED = 'PLAYBACK_FAILED',
// 暂停失败
PAUSE_FAILED = 'PAUSE_FAILED',
// 恢复失败
RESUME_FAILED = 'RESUME_FAILED',
// 停止失败
STOP_FAILED = 'STOP_FAILED',
// 跳转失败
SEEK_FAILED = 'SEEK_FAILED',
// 获取播放状态失败
GET_STATUS_FAILED = 'GET_STATUS_FAILED',
// 音量控制失败
VOLUME_CONTROL_FAILED = 'VOLUME_CONTROL_FAILED',
// 权限错误
PERMISSION_ERROR = 'PERMISSION_ERROR',
// 网络错误
NETWORK_ERROR = 'NETWORK_ERROR',
// 方法未实现
METHOD_NOT_IMPLEMENTED = 'METHOD_NOT_IMPLEMENTED'
}
```
### 错误处理示例
```typescript
import DlnaPlayer, { DlnaError, DlnaErrorCode } from 'react-native-dlna-player';
try {
await DlnaPlayer.searchDevices();
} catch (error) {
if (error instanceof DlnaError) {
// 处理特定类型的错误
switch (error.code) {
case DlnaErrorCode.NETWORK_ERROR:
console.error('网络错误:', error.message);
// 显示网络设置建议
break;
case DlnaErrorCode.PERMISSION_ERROR:
console.error('权限错误:', error.message);
// 引导用户开启权限
break;
default:
console.error('DLNA错误:', error.code, error.message);
}
// 可以访问错误详情
if (error.details) {
console.log('错误详情:', error.details);
}
} else {
// 处理其他类型的错误
console.error('未知错误:', error);
}
}
```
### API错误对照表
下表列出了每个API方法可能抛出的错误类型:
| 方法 | 可能的错误代码 |
| --- | --- |
| `searchDevices()` | `NETWORK_ERROR`, `PERMISSION_ERROR`, `METHOD_NOT_IMPLEMENTED` |
| `connectToDevice(deviceId)` | `DEVICE_NOT_FOUND`, `CONNECTION_FAILED`, `INVALID_PARAMETER` |
| `disconnectFromDevice()` | `DISCONNECTION_FAILED` |
| `playVideo(url, title, subtitle, coverUrl)` | `PLAYBACK_FAILED`, `INVALID_PARAMETER`, `DEVICE_NOT_FOUND` |
| `pauseVideo()` | `PAUSE_FAILED`, `DEVICE_NOT_FOUND` |
| `resumeVideo()` | `RESUME_FAILED`, `DEVICE_NOT_FOUND` |
| `stopVideo()` | `STOP_FAILED`, `DEVICE_NOT_FOUND` |
| `seekTo(position)` | `SEEK_FAILED`, `INVALID_PARAMETER`, `DEVICE_NOT_FOUND` |
| `getPlaybackStatus()` | `GET_STATUS_FAILED`, `DEVICE_NOT_FOUND` |
| `setVolume(volume)` | `VOLUME_CONTROL_FAILED`, `INVALID_PARAMETER`, `DEVICE_NOT_FOUND` |
| `getVolume()` | `VOLUME_CONTROL_FAILED`, `DEVICE_NOT_FOUND` |
| `setMute(mute)` | `VOLUME_CONTROL_FAILED`, `DEVICE_NOT_FOUND` |
| `getMute()` | `VOLUME_CONTROL_FAILED`, `DEVICE_NOT_FOUND` |
| `checkPermissions()` | `PERMISSION_ERROR` |
| `requestPermissions()` | `PERMISSION_ERROR` |
## 许可证
MIT
## 增量搜索和设备缓存功能
新的增量搜索功能允许立即返回缓存的设备,同时在后台继续搜索新设备。当发现新设备时,会通过事件通知应用程序。
```typescript
import { DlnaPlayer, Device } from 'react-native-dlna-player';
import { useEffect, useState } from 'react';
function DeviceSearchExample() {
const [devices, setDevices] = useState([]);
useEffect(() => {
// 添加设备发现监听器
const deviceFoundSubscription = DlnaPlayer.addDeviceFoundListener((device) => {
console.log('发现新设备:', device.name);
// 更新设备列表
setDevices(prevDevices => {
// 检查设备是否已在列表中
const exists = prevDevices.some(d => d.id === device.id);
if (exists) return prevDevices;
// 添加新设备
return [...prevDevices, device];
});
});
// 添加设备丢失监听器
const deviceLostSubscription = DlnaPlayer.addDeviceLostListener((device) => {
console.log('设备丢失:', device.name);
// 从列表中移除设备
setDevices(prevDevices => prevDevices.filter(d => d.id !== device.id));
});
// 开始增量搜索设备
// 立即返回缓存的设备,同时在后台继续搜索
DlnaPlayer.searchDevicesIncremental({
timeout: 15000, // 搜索超时时间(毫秒)
useCached: true, // 使用缓存的设备
clearCache: false // 不清除现有缓存
})
.then(cachedDevices => {
console.log('从缓存中获取到设备:', cachedDevices.length);
// 使用缓存的设备更新UI
setDevices(cachedDevices);
})
.catch(error => {
console.error('设备搜索出错:', error);
});
// 清理函数
return () => {
// 停止设备搜索
DlnaPlayer.stopDeviceSearch();
// 取消监听器
deviceFoundSubscription.remove();
deviceLostSubscription.remove();
};
}, []);
return (
// 在这里渲染设备列表
// ...
);
}
```
### 超时保护
所有网络操作现在都有超时保护,防止操作无响应:
```typescript
try {
// 连接设备,15秒超时
await DlnaPlayer.connectToDevice('device-id');
// 播放视频,15秒超时
await DlnaPlayer.playVideo('http://example.com/video.mp4', '视频标题');
} catch (error) {
if (error.code === 'TIMEOUT') {
console.error('操作超时:', error.message);
} else {
console.error('发生错误:', error);
}
}
```