我目前正在开发一个项目,尝试将视频从 Tello 无人机流式传输到使用 React Native 和 Expo 构建的移动应用程序,我在手机上为我的应用程序完成了 EAS 构建。我正在使用react-native-vlc-media-player库来处理视频流。
这是我的 DroneScreen.js 文件中的相关代码:
import React, { useEffect, useState, useRef } from "react";
import {
Button,
StyleSheet,
View,
Dimensions,
Image,
TouchableOpacity,
ScrollView,
Animated,
ActivityIndicator,
} from "react-native";
import { useFocusEffect } from "@react-navigation/native";
import dgram from "react-native-udp";
import KText from "../components/KText";
import { VLCPlayer } from "react-native-vlc-media-player";
import _ from "lodash";
import Icon from "react-native-vector-icons/FontAwesome5";
const screenWidth = Dimensions.get("window").width;
export default function DroneScreen() {
const [isConnected, setIsConnected] = useState(false);
const [isStreaming, setIsStreaming] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [isInitialScreen, setIsInitialScreen] = useState(true);
const [fadeAnim] = useState(new Animated.Value(0));
const clientRef = useRef(null);
const serverRef = useRef(null);
const videoServerRef = useRef(null);
const playerRef = useRef(null);
useFocusEffect(
React.useCallback(() => {
fadeAnim.setValue(0);
Animated.timing(fadeAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
}, [])
);
useEffect(() => {
clientRef.current = dgram.createSocket("udp4");
serverRef.current = dgram.createSocket("udp4");
videoServerRef.current = dgram.createSocket("udp4");
try {
clientRef.current.bind(8889);
serverRef.current.bind(8890);
videoServerRef.current.bind(11111);
} catch (err) {
console.error("Failed to bind socket", err);
return;
}
let timeoutId = null;
let intervalId = null;
let state = null;
let rinfo = null;
serverRef.current.on("message", (msg, rinfo) => {
state = parseState(msg.toString());
rinfo = rinfo;
setIsConnected(true);
});
intervalId = setInterval(() => {
if (state && rinfo) {
console.log(`server got: ${state} from ${rinfo.address}:${rinfo.port}`);
}
}, 15000);
videoServerRef.current.on("message", (msg, rinfo) => {
setIsStreaming(true);
});
timeoutId = setTimeout(() => {
setIsConnected(false);
}, 5000);
return () => {
clientRef.current.close();
serverRef.current.close();
videoServerRef.current.close();
clearTimeout(timeoutId);
clearInterval(intervalId);
};
}, []);
function parseState(state) {
return state
.split(";")
.map((x) => x.split(":"))
.reduce((data, [key, value]) => {
data[key] = value;
return data;
}, {});
}
const sendCommand = (command) => {
clientRef.current.send(
command,
0,
command.length,
8889,
"192.168.10.1",
(err) => {
if (err) {
console.error("Failed to send command", err);
setIsConnected(false);
} else {
console.log("Command sent: " + command);
}
}
);
};
const connectAndStartStreaming = () => {
setIsLoading(true);
sendCommand("command");
setTimeout(() => {
sendCommand("streamon");
setIsInitialScreen(false);
setIsLoading(false);
}, 5000);
};
return (
<ScrollView contentContainerStyle={styles.container}>
{isInitialScreen || !isConnected ? (
<View style={styles.innerContainer}>
<Animated.Image
source={require("../assets/tello-img.png")}
style={{
width: screenWidth * 1.3,
resizeMode: "contain",
position: "absolute",
opacity: fadeAnim,
}}
/>
<View style={styles.greyContainer}>
<TouchableOpacity
style={[
styles.connectButton,
{ bottom: 120, flexDirection: "row" },
]}
onPress={connectAndStartStreaming}
disabled={isLoading}
>
{isLoading ? (
<ActivityIndicator size="small" color="#fff" />
) : (
<>
<Icon name="link" size={20} color="#fff" />
<KText style={styles.connectButtonText}>Connect</KText>
</>
)}
</TouchableOpacity>
</View>
</View>
) : (
<>
{isStreaming &&
(console.log("isStreaming:", isStreaming),
(
<VLCPlayer
style={{ width: "100%", height: "100%" }}
source={{ uri: "udp://@0.0.0.0:11111" }}
autoplay={false}
initOptions={["--network-caching=150", "--rtsp-tcp"]}
onError={(e) => {
console.log("onError", e);
setIsConnected(false);
setIsStreaming(false);
}}
onOpen={(e) => {
console.log("onOpen", e);
if (
playerRef.current &&
typeof playerRef.current.play === "function"
) {
playerRef.current.play();
}
}}
onBuffering={(e) => {
console.log("onBuffering", e);
if (
e.bufferRate > 0 &&
playerRef.current &&
typeof playerRef.current.play === "function"
) {
playerRef.current.play();
}
}}
onPlaying={(e) => console.log("onPlaying", e)}
ref={playerRef}
/>
))}
<View style={styles.innerContainer}>
<TouchableOpacity
style={styles.buttonContainer}
onPress={() => sendCommand("takeoff")}
>
<KText style={styles.buttonText}>Take Off</KText>
</TouchableOpacity>
<TouchableOpacity
style={styles.buttonContainer}
onPress={() => sendCommand("land")}
>
<KText style={styles.buttonText}>Land</KText>
</TouchableOpacity>
<TouchableOpacity
style={styles.buttonContainer}
onPress={() => sendCommand("streamoff")}
>
<KText style={styles.buttonText}>Off Stream</KText>
</TouchableOpacity>
</View>
</>
)}
</ScrollView>
);
}
....
我面临的问题是我没有在我的移动应用程序上接收来自无人机的视频流。我已确认我的手机已通过 WiFi 连接到无人机,并且无人机已配置为将视频流发送到端口 11111。我能够连接并发送“起飞”等有效命令。 isStreaming 状态变量设置为 true,这表明“streamon”命令正在工作。
我尝试了各种解决方案,包括将套接字绑定到端口 11111,但这似乎不起作用,而且大多数在线解决方案都是针对 Python 定制的。我还检查了控制台日志中是否有任何错误或警告,但似乎没有与此问题相关的内容。我手机上的官方 Tello 应用程序可以显示视频。
如果您能提供有关如何解决此问题的帮助或建议,我将不胜感激。谢谢!
你找到解决办法了吗?