Hello Tello Pilot!
Join our DJI Tello community & remove this banner.
Sign up

Recording / storing video stream using python?

nodecaf

Member
Joined
Apr 7, 2019
Messages
24
Reaction score
28
Location
Melbourne, Australia
I've been playing around with the python examples on github (dji-sdk/Tello-Python) , but one thing I can't seem to figure out, is how to store the video stream to a file and play back later. I have no issues watching the stream live using the provided h264decoder library, but how can I store the stream?

I tried to add a binary write call in the video socket loop like, e.g. :

def _receive_video_thread(self):
"""
Listens for video streaming (raw h264) from the Tello.
Runs as a thread, sets self.frame to the most recent frame Tello captured.
"""
packet_data = ""
while True:
try:
res_string, ip = self.socket_video.recvfrom(2048)
packet_data += res_string
write to a file here
# end of frame
if len(res_string) != 1460:
for frame in self._h264_decode(packet_data):
self.frame = frame
packet_data = ""


My understanding is that it is already a h264 stream so I would have expected that it could just go into a file and then be piped back into ffmpeg or similar too for post processing?

Any help would be much appreciated!

Fly Safe - NoDecaf
 
I was able to figure out how to save video to a recorded file using my Windows laptop, Thonny, Editor to run the Python code, and the ffmpeg-python wrapper pip package from kkroening. None of these have to be used exclusively to do it. Just wanted you to know my setup. Here is the code:

import threading
import socket
import time
import ffmpeg

host = ' '
port = 9000


# Create a UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

tello_address = ('192.168.10.1', 8889)

sock.bind((host, port))

#Function that sends commands to Tello
def send(message):
try:
sock.sendto(message.encode(), tello_address)
print("Sending message: " + message)
except Exception as e:
print("Error sending: " + str(e)

#Function that receives command conformation from Tello. Runs in thread
def recv():
while True:
try:
data, server = sock.recvfrom(1518)
print(data.decode(encoding="utf-8"))
except Exception:
print ('\nExit . . .\n')
break

#Function that receives and saves Tello's video feed into .mp4 using ffmpeg-python
def videoRecv():
try:
(
ffmpeg
.input('udp://0.0.0.0:11111', t=30) #t is the amount of time video will be saved. 30 seconds. Can increase or decrease amount of time.
.output('drone.mp4') #Saves to same directory that I am running code from. Can specify directory with a path.
.run()
)
except Exception as e:
print("Error receiving video: " + str(e))

#This thread listens for command confirmation from Tello
recvThread = threading.Thread(target=recv)
recvThread.daemon = True
recvThread.start()

print("Mission initiated")

send("command")
time.sleep(3)

#This thread runs receiving video feed.
receive_video_thread = threading.Thread(target=videoRecv)
receive_video_thread.daemon = True
receive_video_thread.start()

send("streamon")
time.sleep(5)

send("takeoff")
time.sleep(6)

send("up " + str(75))
time.sleep(10)

send("forward " + str(75))
time.sleep(10)

send("back " + str(100))
time.sleep(10)

send("down " + str(75))
time.sleep(10)

send("land")
time.sleep(4)

send("streamoff")
time.sleep(10)

print("Mission completed successfully!")

sock.close()

There are some issues with this code, like if a video with the same name is already in the save directory them it's won't overwrite the save file. I would have to code in something to account for that. I've just been deleting the video from directory every time I test it.

Maybe you can help me though. My ultimate goal was to get to this to work on an Android phone using Pydroid 3. In Pydroid 3 app on the mobile phone I can install the ffmpeg-python but when I run the same code with an adjustment to the saving directory:

.output('/storage/emulated/0/DroneView.mp4')

I keep getting a no such file or folder error. Haven't been able to figure out a solution. From all that I read permissions should let me save a .mp4 to the internal storage. I am also not sure if this error is thrown because it is having issues reading the incoming video stream or if it is having issues saving the output file. Any help would be great.

Oh and as a side note some of the navigational commands were not getting ran on the Tello which is why I increase the sleep time to 10. Seems like when I give the Tello enough time to run a command the less likely a command will be skipped. Any help with that would be great as well.
 
  • Like
Reactions: nodecaf
Thanks @ZoomD for that writeup, there are some interesting bits in there. I'm really resource restrained in my project, but I could probably insert a sequence tag into the stream before storing each frame, and then write each package back into ffmpeg when I transfer it to my laptop. Probably not the cleanest way, but could work. At least thanks for tipping me off about ffmpeg-python library!

Unfortunately I've got zero experience working on mobile/android. It might sound a bit basic, but my understanding is that android contains each program. Have you verified that Pydroid has been allowed access to the storage? On my phone it is under settings->apps&notifications->appinfo->Yourapp->permissions , where you have to allow it access to e.g. storage.

I know I saw an example somewhere where they dealt with the command queuing issue you describe, I'll see if I can find it again.

Fly Safe - NoDecaf
 
In the end I found this post How to process raw UDP packets so that they can be decoded by a decoder filter in a directshow source filter that explained that for a UDP stream you'll have to at least insert a header of 0x000001 before writing each frame, so by modifying the tello_video (dji-sdk/Tello-Python) example doing the following:

~ First open a fd in the __init__ function ~

dateobj=datetime.datetime.now() #requires "import datetime"
#put timestamp in the filename
outfile='video\\out_'+time.strftime("%Y%m%d_%H%M%S")+".mpeg"
self.file=open(outfile,"ab") #open in ab (add binary)


~ in the _receive_video_thread function ~
while True:
try:
res_string, ip = self.socket_video.recvfrom(2048)
packet_data += res_string
# end of frame
if len(res_string) != 1460:
self.file.write("\x00\x00\x01"+packet_data) #this will write a header before each frame
for frame in self._h264_decode(packet_data):
self.frame = frame
packet_data = ""


I'm now able to playback with ffplay, and do conversions to other format (mp4). I will have to give it fps, but that's ok.

@ZoomD , if nothing else, perhaps you can use the filename creation lines?

Fly Safe - NoDecaf
 
I was able to figure out how to save video to a recorded file using my Windows laptop, Thonny, Editor to run the Python code, and the ffmpeg-python wrapper pip package from kkroening. None of these have to be used exclusively to do it. Just wanted you to know my setup. Here is the code:

import threading
import socket
import time
import ffmpeg

host = ' '
port = 9000


# Create a UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

tello_address = ('192.168.10.1', 8889)

sock.bind((host, port))

#Function that sends commands to Tello
def send(message):
try:
sock.sendto(message.encode(), tello_address)
print("Sending message: " + message)
except Exception as e:
print("Error sending: " + str(e)

#Function that receives command conformation from Tello. Runs in thread
def recv():
while True:
try:
data, server = sock.recvfrom(1518)
print(data.decode(encoding="utf-8"))
except Exception:
print ('\nExit . . .\n')
break

#Function that receives and saves Tello's video feed into .mp4 using ffmpeg-python
def videoRecv():
try:
(
ffmpeg
.input('udp://0.0.0.0:11111', t=30) #t is the amount of time video will be saved. 30 seconds. Can increase or decrease amount of time.
.output('drone.mp4') #Saves to same directory that I am running code from. Can specify directory with a path.
.run()
)
except Exception as e:
print("Error receiving video: " + str(e))

#This thread listens for command confirmation from Tello
recvThread = threading.Thread(target=recv)
recvThread.daemon = True
recvThread.start()

print("Mission initiated")

send("command")
time.sleep(3)

#This thread runs receiving video feed.
receive_video_thread = threading.Thread(target=videoRecv)
receive_video_thread.daemon = True
receive_video_thread.start()

send("streamon")
time.sleep(5)

send("takeoff")
time.sleep(6)

send("up " + str(75))
time.sleep(10)

send("forward " + str(75))
time.sleep(10)

send("back " + str(100))
time.sleep(10)

send("down " + str(75))
time.sleep(10)

send("land")
time.sleep(4)

send("streamoff")
time.sleep(10)

print("Mission completed successfully!")

sock.close()

There are some issues with this code, like if a video with the same name is already in the save directory them it's won't overwrite the save file. I would have to code in something to account for that. I've just been deleting the video from directory every time I test it.

Maybe you can help me though. My ultimate goal was to get to this to work on an Android phone using Pydroid 3. In Pydroid 3 app on the mobile phone I can install the ffmpeg-python but when I run the same code with an adjustment to the saving directory:

.output('/storage/emulated/0/DroneView.mp4')

I keep getting a no such file or folder error. Haven't been able to figure out a solution. From all that I read permissions should let me save a .mp4 to the internal storage. I am also not sure if this error is thrown because it is having issues reading the incoming video stream or if it is having issues saving the output file. Any help would be great.

Oh and as a side note some of the navigational commands were not getting ran on the Tello which is why I increase the sleep time to 10. Seems like when I give the Tello enough time to run a command the less likely a command will be skipped. Any help with that would be great as well.
I tried running your code, however, I keep getting "Error receiving video: [WinError 2] The system cannot find the file specified." I suspect the problem is the 'udp://0.0.0.0:11111'. Any ideas on how to resolve??
 

New Posts

Members online

No members online now.

Forum statistics

Threads
5,690
Messages
39,934
Members
17,023
Latest member
Repiv

New Posts