summaryrefslogtreecommitdiff
path: root/libardrone/paveparser.py
diff options
context:
space:
mode:
Diffstat (limited to 'libardrone/paveparser.py')
-rw-r--r--libardrone/paveparser.py159
1 files changed, 159 insertions, 0 deletions
diff --git a/libardrone/paveparser.py b/libardrone/paveparser.py
new file mode 100644
index 0000000..5e6fc08
--- /dev/null
+++ b/libardrone/paveparser.py
@@ -0,0 +1,159 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2013 Adrian Taylor
+# Inspired by equivalent node.js code by Felix Geisendörfer
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+# THE SOFTWARE.
+
+import struct
+"""
+The AR Drone 2.0 allows a tcp client to receive H264 (MPEG4.10 AVC) video
+from the drone. However, the frames are wrapped by Parrot Video
+Encapsulation (PaVE), which this class parses.
+"""
+
+"""
+Usage: Pass in an output file object into the constructor, then call write on this.
+"""
+class PaVEParser(object):
+
+ HEADER_SIZE_SHORT = 64; # sometimes header is longer
+
+ def __init__(self, outfileobject):
+ self.buffer = ""
+ self.state = self.handle_header
+ self.outfileobject = outfileobject
+ self.misaligned_frames = 0
+ self.payloads = 0
+ self.drop_old_frames = True
+ self.align_on_iframe = True
+
+ if self.drop_old_frames:
+ self.state = self.handle_header_drop_frames
+
+ def write(self, data):
+ self.buffer += data
+ while True:
+ made_progress = self.state()
+ if not made_progress:
+ return
+
+ def handle_header(self):
+ if self.fewer_remaining_than(self.HEADER_SIZE_SHORT):
+ return False
+
+ (signature, version, video_codec, header_size, self.payload_size, encoded_stream_width,
+ encoded_stream_height, display_width, display_height, frame_number, timestamp, total_chunks,
+ chunk_index, frame_type, control, stream_byte_position_lw, stream_byte_position_uw,
+ stream_id, total_slices, slice_index, header1_size, header2_size,
+ reserved2, advertised_size, reserved3) = struct.unpack("<4sBBHIHHHHIIBBBBIIHBBBB2sI12s",
+ self.buffer[0:self.HEADER_SIZE_SHORT])
+
+ if signature != "PaVE":
+ self.state = self.handle_misalignment
+ return True
+ self.buffer = self.buffer[header_size:]
+ self.state = self.handle_payload
+ return True
+
+ def handle_header_drop_frames(self):
+
+ eligible_index = self.buffer.find('PaVE')
+
+ if (eligible_index < 0):
+ return False
+ self.buffer = self.buffer[eligible_index:]
+
+ if self.fewer_remaining_than(self.HEADER_SIZE_SHORT):
+ return False
+
+ eligible_index = 0
+ current_index = eligible_index
+
+ while current_index != -1 and len(self.buffer[current_index:]) > self.HEADER_SIZE_SHORT:
+ (signature, version, video_codec, header_size, payload_size, encoded_stream_width,
+ encoded_stream_height, display_width, display_height, frame_number, timestamp, total_chunks,
+ chunk_index, frame_type, control, stream_byte_position_lw, stream_byte_position_uw,
+ stream_id, total_slices, slice_index, header1_size,
+ header2_size, reserved2, advertised_size,
+ reserved3) = struct.unpack("<4sBBHIHHHHIIBBBBIIHBBBB2sI12s",
+ self.buffer[current_index:current_index + self.HEADER_SIZE_SHORT])
+
+ if (frame_type != 3 or current_index == 0):
+ eligible_index = current_index
+ self.payload_size = payload_size
+
+ offset = self.buffer[current_index + 1:].find('PaVE') + 1
+ if (offset == 0):
+ break
+
+ current_index += offset
+
+ self.buffer = self.buffer[eligible_index + header_size:]
+ self.state = self.handle_payload
+ return True
+
+
+ def handle_misalignment(self):
+ """Sometimes we start of in the middle of frame - look for the PaVE header."""
+ IFrame = False
+ if self.align_on_iframe:
+ while (not IFrame):
+ index = self.buffer.find('PaVE')
+ if index == -1:
+ return False
+
+ self.buffer = self.buffer[index:]
+
+ if self.fewer_remaining_than(self.HEADER_SIZE_SHORT):
+ return False
+
+ (signature, version, video_codec, header_size, self.payload_size, encoded_stream_width,
+ encoded_stream_height, display_width, display_height, frame_number, timestamp, total_chunks,
+ chunk_index, frame_type, control, stream_byte_position_lw, stream_byte_position_uw,
+ stream_id, total_slices, slice_index, header1_size, header2_size, reserved2, advertised_size,
+ reserved3) = struct.unpack("<4sBBHIHHHHIIBBBBIIHBBBB2sI12s", self.buffer[0:self.HEADER_SIZE_SHORT])
+
+ IFrame = (frame_type == 1 or frame_type == 2)
+ if not IFrame:
+ self.buffer = self.buffer[header_size:]
+ else:
+ index = self.buffer.find('PaVE')
+ if index == -1:
+ return False
+ self.buffer = self.buffer[index:]
+
+ self.misaligned_frames += 1
+ self.state = self.handle_header
+
+ return True
+
+ def handle_payload(self):
+ if self.fewer_remaining_than(self.payload_size):
+ return False
+ self.state = self.handle_header
+ if self.drop_old_frames:
+ self.state = self.handle_header_drop_frames
+
+ self.outfileobject.write(self.buffer[0:self.payload_size])
+ self.buffer = self.buffer[self.payload_size:]
+ self.payloads += 1
+ return True
+
+ def fewer_remaining_than(self, desired_size):
+ return len(self.buffer) < desired_size