]> git.nihav.org Git - nihav.git/blobdiff - nihav-commonfmt/src/demuxers/mov.rs
avimux: do not record palette change chunks in OpenDML index
[nihav.git] / nihav-commonfmt / src / demuxers / mov.rs
index 25e65d5e4409b8f38cc50401ddfa97af77f5dce6..a1c3669750156334f431cbec40b142c3e3e7763e 100644 (file)
@@ -11,6 +11,13 @@ macro_rules! mktag {
     });
 }
 
+#[derive(Clone,Copy,Debug,PartialEq)]
+enum DemuxMode {
+    Normal,
+    MacBin,
+    ResFork,
+}
+
 trait Skip64 {
     fn skip64(&mut self, size: u64) -> ByteIOResult<()>;
 }
@@ -573,6 +580,7 @@ fn parse_audio_edata(br: &mut ByteReader, start_pos: u64, size: u64) -> DemuxerR
     }
 }
 
+#[allow(clippy::neg_cmp_op_on_partial_ord)]
 fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult<u64> {
     const KNOWN_STSD_SIZE: u64 = 24;
     validate!(size >= KNOWN_STSD_SIZE);
@@ -625,12 +633,12 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
                 match depth & 0x1F {
                     2 => {
                         let mut pal = [0; 1024];
-                        (&mut pal[..4 * 4]).copy_from_slice(&MOV_DEFAULT_PAL_2BIT);
+                        pal[..4 * 4].copy_from_slice(&MOV_DEFAULT_PAL_2BIT);
                         track.pal = Some(Arc::new(pal));
                     },
                     4 => {
                         let mut pal = [0; 1024];
-                        (&mut pal[..16 * 4]).copy_from_slice(&MOV_DEFAULT_PAL_4BIT);
+                        pal[..16 * 4].copy_from_slice(&MOV_DEFAULT_PAL_4BIT);
                         track.pal = Some(Arc::new(pal));
                     },
                     8 => {
@@ -722,7 +730,7 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
             if &fcc == b"raw " && sample_size == 8 {
                 soniton.signed = false;
             }
-            let block_align = 1;
+            let mut block_align = 1;
             match sver {
                 1 => {
                     let samples_per_packet  = br.read_u32be()?;
@@ -731,7 +739,8 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
                     let _bytes_per_sample   = br.read_u32be()?;
                     track.bsize = bytes_per_frame as usize;
                     track.frame_samples = samples_per_packet as usize;
-                    track.tb_num = samples_per_packet;
+                    track.tb_num = samples_per_packet.max(1);
+                    block_align = bytes_per_frame as usize;
                 },
                 2 => {
                                               br.read_u32be()?; // some size
@@ -754,7 +763,9 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
                     track.bsize = (sample_size / 8) as usize;
                 },
             };
-            track.tb_den = sample_rate;
+            if track.tb_den <= 1 {
+                track.tb_den = sample_rate;
+            }
             track.raw_audio = matches!(&fcc,
                     b"NONE" | b"raw " | b"twos" | b"sowt" |
                     b"in24" | b"in32" | b"fl32" | b"fl64" |
@@ -816,7 +827,7 @@ fn read_stss(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
     validate!(version == 0);
     let _flags              = br.read_u24be()?;
     let entries             = br.read_u32be()? as usize;
-    validate!(entries < ((std::u32::MAX >> 2) - 8) as usize);
+    validate!(entries < ((u32::MAX >> 2) - 8) as usize);
     validate!((entries * 4 + 8) as u64 == size);
     track.keyframes = Vec::with_capacity(entries);
     let mut last_sample_no = 0;
@@ -834,7 +845,7 @@ fn read_stsc(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
     validate!(version == 0);
     let _flags              = br.read_u24be()?;
     let entries             = br.read_u32be()? as usize;
-    validate!(entries < ((std::u32::MAX / 12) - 8) as usize);
+    validate!(entries < ((u32::MAX / 12) - 8) as usize);
     validate!((entries * 12 + 8) as u64 == size);
     track.sample_map = Vec::with_capacity(entries);
     let mut last_sample_no = 0;
@@ -863,10 +874,23 @@ fn read_stsz(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
     } else {
         let entries             = br.read_u32be()? as usize;
         validate!((entries * 4 + 12) as u64 == size);
-        track.chunk_sizes = Vec::with_capacity(entries);
-        for _ in 0..entries {
-            let sample_size     = br.read_u32be()?;
-            track.chunk_sizes.push(sample_size);
+        if entries < 1024 {
+            track.chunk_sizes = Vec::with_capacity(entries);
+            for _ in 0..entries {
+                let sample_size     = br.read_u32be()?;
+                track.chunk_sizes.push(sample_size);
+            }
+        } else {
+            // reading whole array at once and converting it later is much faster
+            track.chunk_sizes = vec![0; entries];
+            unsafe {
+                let ptr = track.chunk_sizes.as_mut_ptr() as *mut u8;
+                let dbuf = std::slice::from_raw_parts_mut(ptr, entries * 4);
+                br.read_buf(dbuf)?;
+            }
+            for el in track.chunk_sizes.iter_mut() {
+                *el = u32::from_be(*el);
+            }
         }
         Ok(size)
     }
@@ -878,10 +902,24 @@ fn read_stco(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
     let _flags              = br.read_u24be()?;
     let entries             = br.read_u32be()? as usize;
     validate!((entries * 4 + 8) as u64 == size);
-    track.chunk_offsets = Vec::with_capacity(entries);
-    for _i in 0..entries {
-        let sample_offset   = br.read_u32be()?;
-        track.chunk_offsets.push(u64::from(sample_offset));
+    if entries < 1024 {
+        track.chunk_offsets = Vec::with_capacity(entries);
+        for _i in 0..entries {
+            let sample_offset   = br.read_u32be()?;
+            track.chunk_offsets.push(u64::from(sample_offset));
+        }
+    } else {
+        // reading whole array at once and converting it later is much faster
+        track.chunk_offsets = vec![0; entries];
+        let mut tmp = vec![0u32; entries];
+        unsafe {
+            let data = tmp.as_mut_ptr();
+            let ptr = std::slice::from_raw_parts_mut(data as *mut u8, entries * 4);
+            br.read_buf(ptr)?;
+        }
+        for (dst, &src) in track.chunk_offsets.iter_mut().zip(tmp.iter()) {
+            *dst = u64::from(u32::from_be(src));
+        }
     }
     Ok(size)
 }
@@ -899,10 +937,24 @@ fn read_ctts(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
     match version {
         0 | 1 => {
             validate!(size == (entries as u64) * 8 + 8);
-            for _ in 0..entries {
-                let samp_count  = br.read_u32be()?;
-                let samp_offset = br.read_u32be()?;
-                track.ctts_map.add(samp_count, samp_offset / track.tb_div);
+            if entries < 1024 {
+                for _ in 0..entries {
+                    let samp_count  = br.read_u32be()?;
+                    let samp_offset = br.read_u32be()?;
+                    track.ctts_map.add(samp_count, samp_offset / track.tb_div);
+                }
+            } else {
+                // reading whole array at once and converting it later is much faster
+                track.ctts_map.array.resize(entries, RLEPair(0, 0));
+                unsafe {
+                    let data = track.ctts_map.array.as_mut_ptr();
+                    let ptr = std::slice::from_raw_parts_mut(data as *mut u8, entries * 8);
+                    br.read_buf(ptr)?;
+                }
+                for RLEPair(count, offset) in track.ctts_map.array.iter_mut() {
+                    *count = u32::from_be(*count);
+                    *offset = u32::from_be(*offset) / track.tb_div;
+                }
             }
         },
         _ => unreachable!(),
@@ -1015,7 +1067,7 @@ fn read_trun(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult
         track.ctts_map.reserve(sample_count);
     }
 
-    if track.chunk_offsets.len() < (std::u32::MAX as usize) {
+    if track.chunk_offsets.len() < (u32::MAX as usize) {
         track.keyframes.push((track.chunk_offsets.len() + 1) as u32);
     }
     for _ in 0..sample_count {
@@ -1059,7 +1111,7 @@ struct MOVDemuxer<'a> {
 
     print_chunks:   bool,
 
-    macbinary:      bool,
+    demux_mode:     DemuxMode,
 }
 
 struct Track {
@@ -1146,9 +1198,13 @@ impl TimeSearcher {
     }
 }
 
+#[repr(C)]
+#[derive(Clone,Copy,Default)]
+struct RLEPair<T>(u32, T);
+
 #[derive(Default)]
 struct RLESearcher<T> {
-    array:      Vec<(u32, T)>,
+    array:      Vec<RLEPair<T>>,
     idx:        usize,
     start:      u64,
     next:       u64,
@@ -1164,7 +1220,7 @@ impl<T:Default+Copy> RLESearcher<T> {
         self.array.reserve(size);
     }
     fn add(&mut self, len: u32, val: T) {
-        self.array.push((len, val));
+        self.array.push(RLEPair(len, val));
     }
     fn reset(&mut self) {
         self.start = 0;
@@ -1274,7 +1330,7 @@ impl Track {
         for kf_time in self.keyframes.iter() {
             let pts = tsearch.map_time(*kf_time - 1, &self.time_to_sample);
             let time = NATimeInfo::ts_to_time(pts, 1000, self.tb_num, self.tb_den);
-            seek_index.add_entry(self.track_no as u32, SeekEntry { time, pts: u64::from(*kf_time - 1), pos: 0 });
+            seek_index.add_entry(self.track_no, SeekEntry { time, pts: u64::from(*kf_time - 1), pos: 0 });
         }
     }
     fn calculate_chunk_size(&self, nsamp: usize) -> usize {
@@ -1406,8 +1462,7 @@ impl Track {
             self.bsize
         }
     }
-    #[allow(clippy::collapsible_if)]
-    fn seek(&mut self, pts: u64, tpoint: NATimePoint) -> DemuxerResult<()> {
+    fn seek(&mut self, pts: u64, tpoint: NATimePoint) -> DemuxerResult<u64> {
         self.cur_sample = pts as usize;
         self.samples_left = 0;
         self.cur_ts = None;
@@ -1434,14 +1489,12 @@ impl Track {
                             }
                             self.raw_apos = apos;
                             apos += (cur_samps / self.frame_samples) as u64;
-                            if apos > exp_pts {
-                                if cur_samps == self.frame_samples || apos > exp_pts + 1 {
-                                    if self.cur_chunk >= self.chunk_offsets.len() {
-                                        return Err(DemuxerError::SeekError);
-                                    }
-                                    self.last_offset = self.chunk_offsets[self.cur_chunk];
-                                    break;
+                            if apos > exp_pts && (cur_samps == self.frame_samples || apos > exp_pts + 1) {
+                                if self.cur_chunk >= self.chunk_offsets.len() {
+                                    return Err(DemuxerError::SeekError);
                                 }
+                                self.last_offset = self.chunk_offsets[self.cur_chunk];
+                                break;
                             }
                             self.cur_chunk += 1;
                         }
@@ -1477,7 +1530,9 @@ impl Track {
                         self.samples_left = csamp - self.cur_sample;
                         self.cur_chunk += 1;
                     }
-                } else if self.chunk_offsets.len() == self.chunk_sizes.len() {
+                } else if self.chunk_offsets.len() == self.chunk_sizes.len() && self.duration != 0 {
+                    let new_sample = (self.chunk_sizes.len() as u64 * exp_pts / u64::from(self.duration)) as usize;
+                    self.cur_sample = new_sample;
                     self.cur_chunk = self.cur_sample;
                 } else {
                     if !self.time_to_sample.is_empty() {
@@ -1575,7 +1630,9 @@ impl Track {
             self.samples_left = csamp + cur_samps - self.cur_sample;
             self.cur_chunk += 1;
         }
-        Ok(())
+        let cur_pts = self.timesearch.map_time(self.cur_sample as u32, &self.time_to_sample);
+        let cur_time = NATimeInfo::ts_to_time(cur_pts, 1000, self.tb_num, self.tb_den);
+        Ok(cur_time)
     }
 }
 
@@ -1600,56 +1657,77 @@ fn process_packet(src: &mut ByteReader, strmgr: &StreamManager, track: &mut Trac
 
 impl<'a> DemuxCore<'a> for MOVDemuxer<'a> {
     fn open(&mut self, strmgr: &mut StreamManager, seek_index: &mut SeekIndex) -> DemuxerResult<()> {
-        if !self.macbinary {
-            self.read_root(strmgr)?;
-        } else {
-            let ver                     = self.src.read_byte()?;
-            validate!(ver == 0);
-                                          self.src.read_skip(64)?;
-            let tag                     = self.src.read_tag()?;
-            validate!(&tag == b"MooV");
-                                          self.src.read_skip(14)?;
-            let data_length             = self.src.read_u32be()?;
-            validate!(data_length > 8);
-            let rsrc_length             = self.src.read_u32be()?;
-            validate!(rsrc_length > 0);
-                                          self.src.read_skip(31)?;
-            let ver                     = self.src.read_byte()?;
-            validate!(ver == 0x81);
-            let ver                     = self.src.read_byte()?;
-            validate!(ver == 0x81);
-            //xxx: maybe check header CRC
-
-            let rsrc_start = 0x80 + ((data_length + 0x7F) & !0x7F);
-                                          self.src.seek(SeekFrom::Start(rsrc_start.into()))?;
-            let rsrc_off                = self.src.read_u32be()?;
-            let rsrc_map_off            = self.src.read_u32be()?;
-            let rsrc_size               = self.src.read_u32be()?;
-            let _rsrc_map_size          = self.src.read_u32be()?;
-            validate!(rsrc_off >= 0x10);
-            validate!(rsrc_map_off >= rsrc_off + rsrc_size);
-                                          self.src.seek(SeekFrom::Current(i64::from(rsrc_off - 16)))?;
-            // I'm too lazy to parse resource map, so let's just iterate over resources for movie header
-            let end_pos = u64::from(rsrc_start + rsrc_off + rsrc_size);
-            let mut peek_buf = [0u8; 8];
-            while self.src.tell() < end_pos {
-                let cur_size            = self.src.read_u32be()?;
-                validate!(self.src.tell() + u64::from(cur_size) <= end_pos);
-                if cur_size > 8 {
-                    let rsize           = self.src.peek_u32be()?;
-                    if rsize == cur_size {
-                                          self.src.peek_buf(&mut peek_buf)?;
-                        if &peek_buf[4..] == b"moov" {
-                                          self.src.read_skip(8)?;
-                            self.read_moov(strmgr, rsize.into())?;
-                            self.mdat_pos = 8;
-                            break;
+        let mut data_start = 0;
+        match self.demux_mode {
+            DemuxMode::Normal => self.read_root(strmgr)?,
+            DemuxMode::MacBin => {
+                let ver                     = self.src.read_byte()?;
+                validate!(ver == 0);
+                                              self.src.read_skip(64)?;
+                let tag                     = self.src.read_tag()?;
+                validate!(&tag == b"MooV");
+                                              self.src.read_skip(14)?;
+                let data_length             = self.src.read_u32be()?;
+                validate!(data_length > 8);
+                let rsrc_length             = self.src.read_u32be()?;
+                validate!(rsrc_length > 0);
+                                              self.src.read_skip(31)?;
+                let ver                     = self.src.read_byte()?;
+                validate!(ver == 0x81);
+                let ver                     = self.src.read_byte()?;
+                validate!(ver == 0x81);
+                //xxx: maybe check header CRC
+
+                let rsrc_start = 0x80 + ((data_length + 0x7F) & !0x7F);
+                                              self.src.seek(SeekFrom::Start(rsrc_start.into()))?;
+                let rsrc_off                = self.src.read_u32be()?;
+                let rsrc_map_off            = self.src.read_u32be()?;
+                let rsrc_size               = self.src.read_u32be()?;
+                let _rsrc_map_size          = self.src.read_u32be()?;
+                validate!(rsrc_off >= 0x10);
+                validate!(rsrc_map_off >= rsrc_off + rsrc_size);
+                                              self.src.seek(SeekFrom::Current(i64::from(rsrc_off - 16)))?;
+                // I'm too lazy to parse resource map, so let's just iterate over resources for movie header
+                let end_pos = u64::from(rsrc_start + rsrc_off + rsrc_size);
+                let mut peek_buf = [0u8; 8];
+                while self.src.tell() < end_pos {
+                    let cur_size            = self.src.read_u32be()?;
+                    validate!(self.src.tell() + u64::from(cur_size) <= end_pos);
+                    if cur_size > 8 {
+                        let rsize           = self.src.peek_u32be()?;
+                        if rsize == cur_size {
+                                              self.src.peek_buf(&mut peek_buf)?;
+                            if &peek_buf[4..] == b"moov" {
+                                              self.src.read_skip(8)?;
+                                self.read_moov(strmgr, rsize.into())?;
+                                self.mdat_pos = 8;
+                                break;
+                            }
                         }
                     }
+                                              self.src.read_skip(cur_size as usize)?;
                 }
-                                          self.src.read_skip(cur_size as usize)?;
-            }
-        }
+            },
+            DemuxMode::ResFork => {
+                let res_data_offset         = self.src.read_u32be()?;
+                let res_map_offset          = self.src.read_u32be()?;
+                let res_map_data_len        = self.src.read_u32be()?;
+                let res_map_length          = self.src.read_u32be()?;
+                validate!(res_data_offset == 0x100 && res_map_length > 16);
+                                              self.src.seek(SeekFrom::Start(u64::from(res_map_offset)))?;
+                let res_data_offset2        = self.src.read_u32be()?;
+                let res_map_offset2         = self.src.read_u32be()?;
+                let res_map_data_len2       = self.src.read_u32be()?;
+                let res_map_length2         = self.src.read_u32be()?;
+                validate!(res_data_offset == res_data_offset2
+                    && res_map_offset == res_map_offset2
+                    && res_map_data_len == res_map_data_len2
+                    && res_map_length == res_map_length2);
+                                              self.src.read_skip(res_map_length as usize - 16)?;
+                data_start = self.src.tell();
+                self.read_root(strmgr)?;
+            },
+        };
         validate!(self.mdat_pos > 0);
         validate!(!self.tracks.is_empty());
         for track in self.tracks.iter_mut() {
@@ -1660,13 +1738,24 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> {
                 track.track_str_id = str_id;
             }
         }
-        if self.macbinary {
-            // patch data offsets
-            for track in self.tracks.iter_mut() {
-                for offset in track.chunk_offsets.iter_mut() {
-                    *offset += 0x80;
+        match self.demux_mode {
+            DemuxMode::MacBin => {
+                // patch data offsets
+                for track in self.tracks.iter_mut() {
+                    for offset in track.chunk_offsets.iter_mut() {
+                        *offset += 0x80;
+                    }
                 }
-            }
+            },
+            DemuxMode::ResFork => {
+                // patch data offsets
+                for track in self.tracks.iter_mut() {
+                    for offset in track.chunk_offsets.iter_mut() {
+                        *offset += data_start;
+                    }
+                }
+            },
+            _ => {},
         }
         for track in self.tracks.iter() {
             track.fill_seek_index(seek_index);
@@ -1681,7 +1770,7 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> {
             return Err(DemuxerError::EOF);
         }
         let mut has_all_time = true;
-        let mut min_ts = std::u64::MAX;
+        let mut min_ts = u64::MAX;
         for trk in self.tracks.iter() {
             if let Some(ts) = trk.cur_ts {
                 min_ts = min_ts.min(ts);
@@ -1741,14 +1830,36 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> {
         let seek_info = ret.unwrap();
         let tbn = self.tracks[seek_info.str_id as usize].tb_num;
         let tbd = self.tracks[seek_info.str_id as usize].tb_den;
+        let mut vpts = None;
+        let mut apts = None;
         for track in self.tracks.iter_mut() {
             let cur_pts = if track.track_id == seek_info.str_id {
                     seek_info.pts
                 } else {
                     seek_info.pts * u64::from(tbn) * u64::from(track.tb_den) / (u64::from(tbd) * u64::from(track.tb_num))
                 };
-            track.seek(cur_pts, time)?;
+            let actual_time = track.seek(cur_pts, time)?;
+            match track.stream_type {
+                StreamType::Video => vpts = Some(actual_time),
+                StreamType::Audio => apts = Some(actual_time),
+                _ => {},
+            };
         }
+        /* For audio+video stream case when the post-seek actual times differ
+           by more than half a second try to seek audio to a closer position
+           to video.
+        */
+        if let (true, Some(vtime), Some(atime)) = (self.tracks.len() == 2, vpts, apts) {
+            if vtime.max(atime) - vtime.min(atime) > 500 && atime != 0 {
+                for track in self.tracks.iter_mut() {
+                    if track.stream_type == StreamType::Audio {
+                        let new_pts = NATimeInfo::time_to_ts(vtime, 1000, track.tb_num, track.tb_den);
+                        track.seek(new_pts, NATimePoint::Milliseconds(vtime))?;
+                    }
+                }
+            }
+        }
+
         Ok(())
     }
     fn get_duration(&self) -> u64 {
@@ -1796,12 +1907,15 @@ impl<'a> NAOptionHandler for MOVDemuxer<'a> {
 
 impl<'a> MOVDemuxer<'a> {
     fn new(io: &'a mut ByteReader<'a>) -> Self {
-        Self::new_common(io, false)
+        Self::new_common(io, DemuxMode::Normal)
     }
     fn new_macbinary(io: &'a mut ByteReader<'a>) -> Self {
-        Self::new_common(io, true)
+        Self::new_common(io, DemuxMode::MacBin)
     }
-    fn new_common(io: &'a mut ByteReader<'a>, macbinary: bool) -> Self {
+    fn new_resfork(io: &'a mut ByteReader<'a>) -> Self {
+        Self::new_common(io, DemuxMode::ResFork)
+    }
+    fn new_common(io: &'a mut ByteReader<'a>, demux_mode: DemuxMode,) -> Self {
         MOVDemuxer {
             src:            io,
             depth:          0,
@@ -1817,7 +1931,7 @@ impl<'a> MOVDemuxer<'a> {
 
             print_chunks:   false,
 
-            macbinary,
+            demux_mode,
         }
     }
     fn read_root(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<()> {
@@ -1869,6 +1983,15 @@ impl DemuxerCreator for MacBinaryMOVDemuxerCreator {
     fn get_name(&self) -> &'static str { "mov-macbin" }
 }
 
+pub struct MacResForkMOVDemuxerCreator { }
+
+impl DemuxerCreator for MacResForkMOVDemuxerCreator {
+    fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
+        Box::new(MOVDemuxer::new_resfork(br))
+    }
+    fn get_name(&self) -> &'static str { "mov-resfork" }
+}
+
 const MOV_DEFAULT_PAL_2BIT: [u8; 4 * 4] = [
     0x93, 0x65, 0x5E, 0x00,
     0xFF, 0xFF, 0xFF, 0x00,
@@ -2222,4 +2345,26 @@ mod test {
             println!("Got {}", pkt);
         }
     }
+
+    #[test]
+    fn test_resfork_demux() {
+        // sample from The Wonders of Electricity: An Adventure in Safety
+        let mut file = File::open("assets/QT/car.mov").unwrap();
+        let mut fr = FileReader::new_read(&mut file);
+        let mut br = ByteReader::new(&mut fr);
+        let mut dmx = MOVDemuxer::new_resfork(&mut br);
+        let mut sm = StreamManager::new();
+        let mut si = SeekIndex::new();
+        dmx.open(&mut sm, &mut si).unwrap();
+
+        loop {
+            let pktres = dmx.get_frame(&mut sm);
+            if let Err(e) = pktres {
+                if e == DemuxerError::EOF { break; }
+                panic!("error");
+            }
+            let pkt = pktres.unwrap();
+            println!("Got {}", pkt);
+        }
+    }
 }