示例#1
0
        /// <summary>
        /// Seeks to a specific timestamp within a recording.
        /// Like <see cref="TrySeekTimestamp(Microseconds64, PlaybackSeekOrigin)"/> but throws exception in case of failure.
        /// </summary>
        /// <remarks><para>
        /// The first call to <see cref="TryGetNextCapture(out Sensor.Capture)"/> after this method
        /// will return the first capture containing an image timestamp greater than or equal to the seek time.
        /// </para><para>
        /// The first call to <see cref="TryGetPreviousCapture(out Sensor.Capture)"/> after this method
        /// will return the firs capture with all image timestamps less than the seek time.
        /// </para><para>
        /// The first call to <see cref="TryGetNextImuSample(out Sensor.ImuSample)"/> after this method
        /// will return the first IMU sample with a timestamp greater than or equal to the seek time.
        /// </para><para>
        /// The first call to <see cref="TryGetPreviousImuSample(out Sensor.ImuSample)"/> after this method
        /// will return the first IMU sample with a timestamp less than the seek time.
        /// </para></remarks>
        /// <param name="offset">The timestamp offset to seek to relative to <paramref name="origin"/>.</param>
        /// <param name="origin">Specifies if the seek operation should be done relative to the beginning or end of the recording.</param>
        /// <exception cref="ObjectDisposedException">This method cannot be called for disposed object.</exception>
        /// <exception cref="PlaybackException">Cannot seek playback to position specified.</exception>
        /// <seealso cref="TrySeekTimestamp(Microseconds64, PlaybackSeekOrigin)"/>
        public void SeekTimestamp(Microseconds64 offset, PlaybackSeekOrigin origin)
        {
            var res = TrySeekTimestamp(offset, origin);

            if (!res)
            {
                throw new PlaybackException($"Cannot seek playback of \"{FilePath}\" to {offset} from {origin}.", FilePath);
            }
        }
        private static Microseconds64 GetEndTimestamp(DeviceConfiguration config, Microseconds64 colorTimestampLast)
        {
            var res = colorTimestampLast;

            if (config.DepthDelayOffColor > 0 && config.DepthMode != DepthMode.Off ||
                config.ColorResolution == ColorResolution.Off)
            {
                res += config.DepthDelayOffColor;
            }
            return(res);
        }
        private static Capture CreateTestCapture(DeviceConfiguration config, Microseconds64 colorTimestamp)
        {
            var capture = new Capture();

            using (var image = CreateTestColorImage(config, colorTimestamp))
                capture.ColorImage = image;
            using (var image = CreateTestIRImage(config, colorTimestamp))
                capture.IRImage = image;
            using (var image = CreateTestDepthImage(config, colorTimestamp))
                capture.DepthImage = image;
            return(capture);
        }
        private static void CheckCapture(Capture capture, DeviceConfiguration config, Microseconds64 colorTimestamp)
        {
            using (var colorImage = capture.ColorImage)
            {
                if (config.ColorResolution == ColorResolution.Off)
                {
                    Assert.IsNull(colorImage);
                }
                else
                {
                    Assert.IsNotNull(colorImage);
                    Assert.AreEqual(config.ColorFormat, colorImage.Format);
                    Assert.AreEqual(config.ColorResolution.WidthPixels(), colorImage.WidthPixels);
                    Assert.AreEqual(config.ColorResolution.HeightPixels(), colorImage.HeightPixels);
                    Assert.AreEqual(colorTimestamp, colorImage.DeviceTimestamp);
                }
            }

            Microseconds64 depthTimestamp = colorTimestamp + config.DepthDelayOffColor;

            using (var depthImage = capture.DepthImage)
            {
                if (!config.DepthMode.HasDepth())
                {
                    Assert.IsNull(depthImage);
                }
                else
                {
                    Assert.IsNotNull(depthImage);
                    Assert.AreEqual(ImageFormat.Depth16, depthImage.Format);
                    Assert.AreEqual(config.DepthMode.WidthPixels(), depthImage.WidthPixels);
                    Assert.AreEqual(config.DepthMode.HeightPixels(), depthImage.HeightPixels);
                    Assert.AreEqual(depthTimestamp, depthImage.DeviceTimestamp);
                }
            }

            using (var irImage = capture.IRImage)
            {
                if (!config.DepthMode.HasPassiveIR())
                {
                    Assert.IsNull(irImage);
                }
                else
                {
                    Assert.IsNotNull(irImage);
                    Assert.AreEqual(ImageFormat.IR16, irImage.Format);
                    Assert.AreEqual(config.DepthMode.WidthPixels(), irImage.WidthPixels);
                    Assert.AreEqual(config.DepthMode.HeightPixels(), irImage.HeightPixels);
                    Assert.AreEqual(depthTimestamp, irImage.DeviceTimestamp);
                }
            }
        }
示例#5
0
        /// <summary>Writes data for the custom track to file.</summary>
        /// <param name="deviceTimestamp">
        /// The timestamp in microseconds for the custom track data. This timestamp should be in the same time domain as the
        /// device timestamp used for recording.
        /// </param>
        /// <param name="customData">The buffer of custom track data. Not <see langword="null"/>.</param>
        /// <remarks>
        /// Custom track data must be written in increasing order of timestamp, and the file's header must already be written.
        /// When writing custom track data at the same time as captures or IMU data, the custom data should be within 1 second of
        /// the most recently written timestamp.
        /// </remarks>
        /// <exception cref="ArgumentNullException"><paramref name="customData"/> is <see langword="null"/>.</exception>
        /// <exception cref="RecordingException">Some error during recording to file. See logs for details.</exception>
        /// <exception cref="ObjectDisposedException">This method cannot be called for disposed <see cref="Recorder"/>.</exception>
        public void WriteData(Microseconds64 deviceTimestamp, byte[] customData)
        {
            if (customData == null)
            {
                throw new ArgumentNullException(nameof(customData));
            }

            var customDataLength = Helpers.Int32ToUIntPtr(customData.Length);
            var res = NativeApi.RecordWriteCustomTrackData(RecorderHandle, trackNameAsBytes, deviceTimestamp, customData, customDataLength);

            if (res != NativeCallResults.Result.Succeeded)
            {
                throw new RecordingException(recorder.FilePath);
            }
        }
        private static Image CreateTestDepthImage(DeviceConfiguration config, Microseconds64 colorTimestamp)
        {
            if (!config.DepthMode.HasDepth())
            {
                return(null);
            }

            var width  = config.DepthMode.WidthPixels();
            var height = config.DepthMode.HeightPixels();
            var buffer = new short[width * height];

            var image = Image.CreateFromArray(buffer, ImageFormat.Depth16, width, height);

            image.DeviceTimestamp = colorTimestamp + config.DepthDelayOffColor;

            return(image);
        }
示例#7
0
            protected override void BackgroundLoop()
            {
                try
                {
                    var forward      = true;
                    var sw           = Stopwatch.StartNew();
                    var frameCounter = 0L;

                    while (!playback.IsDisposed)
                    {
                        while (doNotPlayFasterThanOriginalFps && !playback.IsDisposed)
                        {
                            var expectedTime = Microseconds64.FromSeconds((double)frameCounter / frameRateHz);
                            var elapsedTime  = (Microseconds64)sw.Elapsed;
                            if (elapsedTime >= expectedTime)
                            {
                                break;
                            }
                            var sleepInterval = new Microseconds64((expectedTime.ValueUsec - elapsedTime.ValueUsec) / 2);
                            Thread.Sleep(sleepInterval);
                        }

                        var res = forward
                            ? playback.TryGetNextCapture(out var capture)
                            : playback.TryGetPreviousCapture(out capture);
                        if (!res)
                        {
                            forward = !forward;
                            continue;
                        }

                        frameCounter++;
                        using (capture)
                        {
                            CaptureReady?.Invoke(this, new CaptureReadyEventArgs(capture));
                        }
                    }
                }
                catch (ObjectDisposedException)
                { }
                catch (Exception exc)
                {
                    Failed?.Invoke(this, new FailedEventArgs(exc));
                }
            }
        public void TestAttachments()
        {
            var mkvPath = GenerateTempMkvFilePath();

            var config = new DeviceConfiguration
            {
                CameraFps       = FrameRate.Fifteen,
                ColorFormat     = ImageFormat.ColorNV12,
                ColorResolution = ColorResolution.R720p,
                DepthMode       = DepthMode.NarrowView2x2Binned,
            };

            var deviceTimestamp0 = Microseconds64.FromMilliseconds(1.0);
            var deviceTimestamp1 = Microseconds64.FromMilliseconds(7.7);

            var attachment1Name = "\ud182\ud0b5\ud181\ud182 \ud0ba\ud0be\ud0b4\ud0b8\ud180\ud0be\ud0b2\ud0ba\ud0b8";
            var attachment1Data = new byte[] { 1, 255, 0, 8, 7, 254, 128, 3, 127, 65, 179 };

            var attachment2Name = "another_attachment.file";
            var attachment2Data = new byte[] { 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 220, 230, 240, 250 };

            using (var recorder = new Recorder(mkvPath, null, config))
            {
                recorder.AddAttachment(attachment1Name, attachment1Data);
                recorder.AddAttachment(attachment2Name, attachment2Data);

                recorder.WriteHeader();

                WriteTestCaptures(recorder, deviceTimestamp0, deviceTimestamp1);
            }

            using (var playback = new Playback(mkvPath))
            {
                Assert.IsTrue(playback.TryGetAttachment(attachment1Name, out var data1));
                AssertAreEqual(attachment1Data, data1);

                Assert.IsTrue(playback.TryGetAttachment(attachment2Name, out var data2));
                AssertAreEqual(attachment2Data, data2);

                Assert.IsFalse(playback.TryGetAttachment("some_unknown_attachment_name", out var tmp));
                Assert.IsNull(tmp);
            }

            File.Delete(mkvPath);
        }
        private static Image CreateTestColorImage(DeviceConfiguration config, Microseconds64 colorTimestamp)
        {
            if (config.ColorResolution == ColorResolution.Off)
            {
                return(null);
            }

            var width  = config.ColorResolution.WidthPixels();
            var height = config.ColorResolution.HeightPixels();
            var stride = config.ColorFormat.StrideBytes(width);
            var buffer = new byte[config.ColorFormat.ImageSizeBytes(stride, height)];

            var image = Image.CreateFromArray(buffer, config.ColorFormat, width, height);

            image.DeviceTimestamp = colorTimestamp;

            return(image);
        }
示例#10
0
 public static extern NativeCallResults.Result PlaybackSeekTimestamp(
     NativeHandles.PlaybackHandle playbackHandle,
     Microseconds64 offset,
     PlaybackSeekOrigin origin);
示例#11
0
 /// <summary>
 /// Seeks to a specific timestamp within a recording.
 /// Like <see cref="SeekTimestamp(Microseconds64, PlaybackSeekOrigin)"/> but returns <see langword="false"/> in case of failure.
 /// </summary>
 /// <param name="offset">The timestamp offset to seek to relative to <paramref name="origin"/>.</param>
 /// <param name="origin">Specifies if the seek operation should be done relative to the beginning or end of the recording.</param>
 /// <returns>
 /// <see langword="true"/> if the seek operation was successful, or <see langword="false"/>
 /// if an error occurred. The current seek position is left unchanged if <see langword="false"/> is returned.
 /// </returns>
 /// <remarks><para>
 /// The first call to <see cref="TryGetNextCapture(out Sensor.Capture)"/> after this method
 /// will return the first capture containing an image timestamp greater than or equal to the seek time.
 /// </para><para>
 /// The first call to <see cref="TryGetPreviousCapture(out Sensor.Capture)"/> after this method
 /// will return the firs capture with all image timestamps less than the seek time.
 /// </para><para>
 /// The first call to <see cref="TryGetNextImuSample(out Sensor.ImuSample)"/> after this method
 /// will return the first IMU sample with a timestamp greater than or equal to the seek time.
 /// </para><para>
 /// The first call to <see cref="TryGetPreviousImuSample(out Sensor.ImuSample)"/> after this method
 /// will return the first IMU sample with a timestamp less than the seek time.
 /// </para></remarks>
 /// <exception cref="ObjectDisposedException">This method cannot be called for disposed object.</exception>
 /// <seealso cref="SeekTimestamp(Microseconds64, PlaybackSeekOrigin)"/>
 public bool TrySeekTimestamp(Microseconds64 offset, PlaybackSeekOrigin origin)
 => NativeApi.PlaybackSeekTimestamp(handle.ValueNotDisposed, offset, origin) == NativeCallResults.Result.Succeeded;
示例#12
0
        private void ImageExtractLoop()
        {
            Stopwatch sw = new Stopwatch();
            int       frameCountProduced = 0;

            sw.Start();
            // bool isFrist = true;
            // TimeSpan initialDiff = new TimeSpan(0,0,0,0,0);
            while (true)
            {
                Capture capture = null;
                bool    isSucceeded;
                try
                {
                    isSucceeded = camera.TryGetCapture(out capture, K4AdotNet.Timeout.FromSeconds(0.1));
                }
                catch
                {
                    isSucceeded = false;
                    capture?.Dispose();
                }

                if (isSucceeded)
                {
                    frameCountProduced++;

                    //var deviceTime = capture.ColorImage.DeviceTimestamp.ToTimeSpan();
                    //var systemTime = capture.ColorImage.SystemTimestamp.ToTimeSpan();
                    //var diff = deviceTime - systemTime + initialDiff;

                    //if (isFrist) {
                    //    isFrist = false;
                    //    initialDiff = -diff;
                    //}

                    // Debug.WriteLine("{0,12} \"deviceTime - systemTime\" {1,10}", field.ToString(), diff.TotalMilliseconds.ToString());
                    capture.ColorImage.DeviceTimestamp = Microseconds64.FromMilliseconds(capture.ColorImage.SystemTimestamp.TotalMilliseconds);

                    if (Globals.getInstance().isRecording)
                    {
                        if (threadVideoRecording == null)
                        {
                            threadVideoRecording          = new Thread(() => VideoRecordingLoop());
                            threadVideoRecording.Priority = ThreadPriority.BelowNormal;
                            threadVideoRecording.Start();
                        }

                        mutVideoRecord.WaitOne();
                        qVideoBufferToRecord.Enqueue(capture);
                        mutVideoRecord.ReleaseMutex();
                    }
                    else
                    {
                        mutVideoDisplay.WaitOne();
                        qVideoBufferToDisplay.Enqueue(capture);
                        mutVideoDisplay.ReleaseMutex();
                    }
                }
                else
                {
                    Thread.Sleep(30);
                }

                if (sw.Elapsed > TimeSpan.FromSeconds(2))
                {
                    fpsProduced        = (double)frameCountProduced / sw.Elapsed.TotalSeconds;
                    frameCountProduced = 0;
                    sw.Restart();
                }

                if (isDisposing)
                {
                    break;
                }
            }
        }
示例#13
0
 private static void WriteTestCapture(Recorder recorder, Microseconds64 colorTimestamp)
 {
     using (var capture = CreateTestCapture(recorder.DeviceConfiguration, colorTimestamp))
         recorder.WriteCapture(capture);
 }
示例#14
0
        public void TestCustomSubtitleTracks()
        {
            var mkvPath = GenerateTempMkvFilePath();

            var config = new DeviceConfiguration
            {
                CameraFps       = FrameRate.Five,
                ColorFormat     = ImageFormat.ColorNV12,
                ColorResolution = ColorResolution.R720p,
                DepthMode       = DepthMode.Off,
            };

            var deviceTimestamp0 = Microseconds64.FromMilliseconds(0);
            var deviceTimestamp1 = Microseconds64.FromMilliseconds(20);

            var track1Name         = "TEST_TRACK_NAME";
            var track1CodecId      = "S_CUSTOM_SUBTITLE_CODEC";
            var track1CodecContext = new byte[] { 0, 1, 2, 3 };
            var track1Settings     = new RecordSubtitleSettings {
                HighFreqData = false
            };

            var track2Name         = "ANOTHER_CUSTOM_TRACK";
            var track2CodecId      = "S_XYZ_CODEC";
            var track2CodecContext = new byte[] { 255, 254, 253 };
            var track2Settings     = new RecordSubtitleSettings {
                HighFreqData = true
            };

            using (var recorder = new Recorder(mkvPath, null, config))
            {
                var track1 = recorder.CustomTracks.AddCustomSubtitleTrack(track1Name, track1CodecId, track1CodecContext, track1Settings);
                Assert.AreEqual(track1Name, track1.Name);
                Assert.AreEqual(track1CodecId, track1.CodecId);
                Assert.AreSame(track1CodecContext, track1.CodecContext);

                var track2 = recorder.CustomTracks.AddCustomSubtitleTrack(track2Name, track2CodecId, track2CodecContext, track2Settings);
                Assert.AreEqual(track2Name, track2.Name);
                Assert.AreEqual(track2CodecId, track2.CodecId);
                Assert.AreSame(track2CodecContext, track2.CodecContext);

                recorder.WriteHeader();

                WriteTestCaptures(recorder, deviceTimestamp0, deviceTimestamp1);

                track1.WriteData(deviceTimestamp0, new byte[] { 10 });
                track1.WriteData(deviceTimestamp0 + 200, new byte[] { 12 });
                track1.WriteData(deviceTimestamp0 + 400, new byte[] { 14 });
                track1.WriteData(deviceTimestamp0 + 600, new byte[] { 16 });
                track1.WriteData(deviceTimestamp0 + 800, new byte[] { 18 });
                track1.WriteData(deviceTimestamp1, new byte[] { 20 });
                track1.WriteData(deviceTimestamp1 + 200, new byte[] { 22 });
                track1.WriteData(deviceTimestamp1 + 400, new byte[] { 24 });
                track1.WriteData(deviceTimestamp1 + 600, new byte[] { 26 });
                track1.WriteData(deviceTimestamp1 + 800, new byte[] { 28 });

                track2.WriteData(deviceTimestamp0, new byte[] { 2, 0 });
                track2.WriteData(deviceTimestamp1, new byte[] { 2, 1 });
            }

            using (var playback = new Playback(mkvPath))
            {
                var builtInTrackCount = GetBuiltInTrackCount(config);
                var trackCount        = builtInTrackCount + 2;
                Assert.AreEqual(trackCount, playback.Tracks.Count);
                Assert.AreEqual(builtInTrackCount, playback.Tracks.Count(t => t.IsBuiltIn));
                Assert.AreEqual(2, playback.Tracks.Count(t => !t.IsBuiltIn));

                Assert.IsTrue(playback.Tracks.Exists(track1Name));
                Assert.IsTrue(playback.Tracks.Exists(track2Name));
                Assert.IsFalse(playback.Tracks.Exists("SOME_UNKNOWN_TRACK_NAME"));

                var track1 = playback.Tracks[track1Name];
                Assert.IsNotNull(track1);
                Assert.AreEqual(track1Name, track1.Name);
                Assert.AreEqual(track1CodecId, track1.CodecId);
                AssertAreEqual(track1CodecContext, track1.CodecContext);
                Assert.IsFalse(track1.IsBuiltIn);

                var track2 = playback.Tracks[track2Name];
                Assert.IsNotNull(track2);
                Assert.AreEqual(track2Name, track2.Name);
                Assert.AreEqual(track2CodecId, track2.CodecId);
                AssertAreEqual(track2CodecContext, track2.CodecContext);
                Assert.IsFalse(track2.IsBuiltIn);

                Assert.IsNull(playback.Tracks["SOME_UNKNOWN_TRACK_NAME"]);

                // Reading of track 1

                Assert.IsFalse(track1.TryGetPreviousDataBlock(out var dataBlock));
                Assert.IsNull(dataBlock);

                // forward
                for (var i = 0; i < 10; i += 2)
                {
                    Assert.IsTrue(track1.TryGetNextDataBlock(out dataBlock));
                    Assert.IsNotNull(dataBlock);
                    Assert.AreEqual(deviceTimestamp0.ValueUsec + i * 100, dataBlock.DeviceTimestamp.ValueUsec);
                    Assert.AreEqual(1, dataBlock.SizeBytes);
                    Assert.AreEqual(10 + i, Marshal.ReadByte(dataBlock.Buffer));
                    dataBlock.Dispose();
                }

                for (var i = 0; i < 10; i += 2)
                {
                    Assert.IsTrue(track1.TryGetNextDataBlock(out dataBlock));
                    Assert.IsNotNull(dataBlock);
                    Assert.AreEqual(deviceTimestamp1.ValueUsec + i * 100, dataBlock.DeviceTimestamp.ValueUsec);
                    Assert.AreEqual(1, dataBlock.SizeBytes);
                    Assert.AreEqual(20 + i, Marshal.ReadByte(dataBlock.Buffer));
                    dataBlock.Dispose();
                }

                // eof
                Assert.IsFalse(track1.TryGetNextDataBlock(out dataBlock));
                Assert.IsNull(dataBlock);

                // Reading of track 2

                Assert.IsFalse(track2.TryGetPreviousDataBlock(out dataBlock));
                Assert.IsNull(dataBlock);

                // forward
                Assert.IsTrue(track2.TryGetNextDataBlock(out dataBlock));
                Assert.IsNotNull(dataBlock);
                Assert.AreEqual(deviceTimestamp0, dataBlock.DeviceTimestamp);
                Assert.AreEqual(2, dataBlock.SizeBytes);
                Assert.AreEqual(2, Marshal.ReadByte(dataBlock.Buffer, 0));
                Assert.AreEqual(0, Marshal.ReadByte(dataBlock.Buffer, 1));
                dataBlock.Dispose();

                Assert.IsTrue(track2.TryGetNextDataBlock(out dataBlock));
                Assert.IsNotNull(dataBlock);
                Assert.AreEqual(deviceTimestamp1, dataBlock.DeviceTimestamp);
                Assert.AreEqual(2, dataBlock.SizeBytes);
                Assert.AreEqual(2, Marshal.ReadByte(dataBlock.Buffer, 0));
                Assert.AreEqual(1, Marshal.ReadByte(dataBlock.Buffer, 1));
                dataBlock.Dispose();

                // eof
                Assert.IsFalse(track2.TryGetNextDataBlock(out dataBlock));
                Assert.IsNull(dataBlock);

                // backward
                Assert.IsTrue(track2.TryGetPreviousDataBlock(out dataBlock));
                Assert.IsNotNull(dataBlock);
                Assert.AreEqual(deviceTimestamp1, dataBlock.DeviceTimestamp);
                Assert.AreEqual(2, dataBlock.SizeBytes);
                Assert.AreEqual(2, Marshal.ReadByte(dataBlock.Buffer, 0));
                Assert.AreEqual(1, Marshal.ReadByte(dataBlock.Buffer, 1));
                dataBlock.Dispose();

                Assert.IsTrue(track2.TryGetPreviousDataBlock(out dataBlock));
                Assert.IsNotNull(dataBlock);
                Assert.AreEqual(deviceTimestamp0, dataBlock.DeviceTimestamp);
                Assert.AreEqual(2, dataBlock.SizeBytes);
                Assert.AreEqual(2, Marshal.ReadByte(dataBlock.Buffer, 0));
                Assert.AreEqual(0, Marshal.ReadByte(dataBlock.Buffer, 1));
                dataBlock.Dispose();

                // eof
                Assert.IsFalse(track2.TryGetPreviousDataBlock(out dataBlock));
                Assert.IsNull(dataBlock);
            }

            File.Delete(mkvPath);
        }
示例#15
0
        public void TestCustomVideoTracks()
        {
            var mkvPath = GenerateTempMkvFilePath();

            var config = new DeviceConfiguration
            {
                CameraFps       = FrameRate.Five,
                ColorFormat     = ImageFormat.ColorNV12,
                ColorResolution = ColorResolution.R720p,
                DepthMode       = DepthMode.NarrowView2x2Binned,
            };

            var deviceTimestamp0 = Microseconds64.FromMilliseconds(0);
            var deviceTimestamp1 = Microseconds64.FromMilliseconds(20);

            var track1Name         = "TEST_TRACK_NAME";
            var track1CodecId      = "V_CUSTOM_VIDEO_CODEC";
            var track1CodecContext = new byte[] { 255, 254, 253, 0, 1, 2, 3, 4, 8, 16, 32, 64, 128 };
            var track1Settings     = new RecordVideoSettings {
                Width = 2, Height = 4, FrameRate = 5
            };

            var track2Name         = "ANOTHER_CUSTOM_TRACK";
            var track2CodecId      = "V_XYZ_CODEC";
            var track2CodecContext = new byte[] { 20, 19, 8, 27, 22, 29, 59 };
            var track2Settings     = new RecordVideoSettings {
                Width = 20, Height = 40, FrameRate = 4
            };

            using (var recorder = new Recorder(mkvPath, null, config))
            {
                var track1 = recorder.CustomTracks.AddVideoTrack(track1Name, track1CodecId, track1CodecContext, track1Settings);
                Assert.AreEqual(track1Name, track1.Name);
                Assert.AreEqual(track1CodecId, track1.CodecId);
                Assert.AreSame(track1CodecContext, track1.CodecContext);

                var track2 = recorder.CustomTracks.AddVideoTrack(track2Name, track2CodecId, track2CodecContext, track2Settings);
                Assert.AreEqual(track2Name, track2.Name);
                Assert.AreEqual(track2CodecId, track2.CodecId);
                Assert.AreSame(track2CodecContext, track2.CodecContext);

                recorder.WriteHeader();

                WriteTestCaptures(recorder, deviceTimestamp0, deviceTimestamp1);

                track1.WriteData(deviceTimestamp0, new byte[] { 1, 0 });
                track1.WriteData(deviceTimestamp1, new byte[] { 1, 1 });

                track2.WriteData(deviceTimestamp0, new byte[] { 2, 0 });
                track2.WriteData(deviceTimestamp1, new byte[] { 2, 1 });
            }

            using (var playback = new Playback(mkvPath))
            {
                var builtInTrackCount = GetBuiltInTrackCount(config);
                var trackCount        = builtInTrackCount + 2;
                Assert.AreEqual(trackCount, playback.Tracks.Count);
                Assert.AreEqual(builtInTrackCount, playback.Tracks.Count(t => t.IsBuiltIn));
                Assert.AreEqual(2, playback.Tracks.Count(t => !t.IsBuiltIn));

                Assert.IsTrue(playback.Tracks.Exists(track1Name));
                Assert.IsTrue(playback.Tracks.Exists(track2Name));
                Assert.IsFalse(playback.Tracks.Exists("SOME_UNKNOWN_TRACK_NAME"));

                var track1 = playback.Tracks[track1Name];
                Assert.IsNotNull(track1);
                Assert.AreEqual(track1Name, track1.Name);
                Assert.AreEqual(track1CodecId, track1.CodecId);
                AssertAreEqual(track1CodecContext, track1.CodecContext);
                Assert.AreEqual(track1Settings, track1.VideoSettings);
                Assert.IsFalse(track1.IsBuiltIn);

                var track2 = playback.Tracks[track2Name];
                Assert.IsNotNull(track2);
                Assert.AreEqual(track2Name, track2.Name);
                Assert.AreEqual(track2CodecId, track2.CodecId);
                AssertAreEqual(track2CodecContext, track2.CodecContext);
                Assert.AreEqual(track2Settings, track2.VideoSettings);
                Assert.IsFalse(track2.IsBuiltIn);

                Assert.IsNull(playback.Tracks["SOME_UNKNOWN_TRACK_NAME"]);

                // Reading of track 1

                Assert.IsFalse(track1.TryGetPreviousDataBlock(out var dataBlock));
                Assert.IsNull(dataBlock);

                // forward
                Assert.IsTrue(track1.TryGetNextDataBlock(out dataBlock));
                Assert.IsNotNull(dataBlock);
                Assert.AreEqual(deviceTimestamp0, dataBlock.DeviceTimestamp);
                Assert.AreEqual(2, dataBlock.SizeBytes);
                Assert.AreEqual(1, Marshal.ReadByte(dataBlock.Buffer, 0));
                Assert.AreEqual(0, Marshal.ReadByte(dataBlock.Buffer, 1));
                dataBlock.Dispose();

                Assert.IsTrue(track1.TryGetNextDataBlock(out dataBlock));
                Assert.IsNotNull(dataBlock);
                Assert.AreEqual(deviceTimestamp1, dataBlock.DeviceTimestamp);
                Assert.AreEqual(2, dataBlock.SizeBytes);
                Assert.AreEqual(1, Marshal.ReadByte(dataBlock.Buffer, 0));
                Assert.AreEqual(1, Marshal.ReadByte(dataBlock.Buffer, 1));
                dataBlock.Dispose();

                // eof
                Assert.IsFalse(track1.TryGetNextDataBlock(out dataBlock));
                Assert.IsNull(dataBlock);

                // backward
                Assert.IsTrue(track1.TryGetPreviousDataBlock(out dataBlock));
                Assert.IsNotNull(dataBlock);
                Assert.AreEqual(deviceTimestamp1, dataBlock.DeviceTimestamp);
                Assert.AreEqual(2, dataBlock.SizeBytes);
                Assert.AreEqual(1, Marshal.ReadByte(dataBlock.Buffer, 0));
                Assert.AreEqual(1, Marshal.ReadByte(dataBlock.Buffer, 1));
                dataBlock.Dispose();

                Assert.IsTrue(track1.TryGetPreviousDataBlock(out dataBlock));
                Assert.IsNotNull(dataBlock);
                Assert.AreEqual(deviceTimestamp0, dataBlock.DeviceTimestamp);
                Assert.AreEqual(2, dataBlock.SizeBytes);
                Assert.AreEqual(1, Marshal.ReadByte(dataBlock.Buffer, 0));
                Assert.AreEqual(0, Marshal.ReadByte(dataBlock.Buffer, 1));
                dataBlock.Dispose();

                // eof
                Assert.IsFalse(track1.TryGetPreviousDataBlock(out dataBlock));
                Assert.IsNull(dataBlock);

                // Reading of track 2

                Assert.IsFalse(track2.TryGetPreviousDataBlock(out dataBlock));
                Assert.IsNull(dataBlock);

                // forward
                Assert.IsTrue(track2.TryGetNextDataBlock(out dataBlock));
                Assert.IsNotNull(dataBlock);
                Assert.AreEqual(deviceTimestamp0, dataBlock.DeviceTimestamp);
                Assert.AreEqual(2, dataBlock.SizeBytes);
                Assert.AreEqual(2, Marshal.ReadByte(dataBlock.Buffer, 0));
                Assert.AreEqual(0, Marshal.ReadByte(dataBlock.Buffer, 1));
                dataBlock.Dispose();

                Assert.IsTrue(track2.TryGetNextDataBlock(out dataBlock));
                Assert.IsNotNull(dataBlock);
                Assert.AreEqual(deviceTimestamp1, dataBlock.DeviceTimestamp);
                Assert.AreEqual(2, dataBlock.SizeBytes);
                Assert.AreEqual(2, Marshal.ReadByte(dataBlock.Buffer, 0));
                Assert.AreEqual(1, Marshal.ReadByte(dataBlock.Buffer, 1));
                dataBlock.Dispose();

                // eof
                Assert.IsFalse(track2.TryGetNextDataBlock(out dataBlock));
                Assert.IsNull(dataBlock);

                // backward
                Assert.IsTrue(track2.TryGetPreviousDataBlock(out dataBlock));
                Assert.IsNotNull(dataBlock);
                Assert.AreEqual(deviceTimestamp1, dataBlock.DeviceTimestamp);
                Assert.AreEqual(2, dataBlock.SizeBytes);
                Assert.AreEqual(2, Marshal.ReadByte(dataBlock.Buffer, 0));
                Assert.AreEqual(1, Marshal.ReadByte(dataBlock.Buffer, 1));
                dataBlock.Dispose();

                Assert.IsTrue(track2.TryGetPreviousDataBlock(out dataBlock));
                Assert.IsNotNull(dataBlock);
                Assert.AreEqual(deviceTimestamp0, dataBlock.DeviceTimestamp);
                Assert.AreEqual(2, dataBlock.SizeBytes);
                Assert.AreEqual(2, Marshal.ReadByte(dataBlock.Buffer, 0));
                Assert.AreEqual(0, Marshal.ReadByte(dataBlock.Buffer, 1));
                dataBlock.Dispose();

                // eof
                Assert.IsFalse(track2.TryGetPreviousDataBlock(out dataBlock));
                Assert.IsNull(dataBlock);
            }

            File.Delete(mkvPath);
        }
示例#16
0
        public void TestBasicScenario()
        {
            var mkvPath = GenerateTempMkvFilePath();

            var config = new DeviceConfiguration
            {
                CameraFps                 = FrameRate.Thirty,
                ColorFormat               = ImageFormat.ColorNV12,
                ColorResolution           = ColorResolution.R720p,
                DepthMode                 = DepthMode.NarrowView2x2Binned,
                SubordinateDelayOffMaster = Microseconds32.FromSeconds(0.00001),
                WiredSyncMode             = WiredSyncMode.Subordinate,
                DepthDelayOffColor        = Microseconds32.FromMilliseconds(-0.005),
            };

            var deviceTimestamp0 = Microseconds64.FromMilliseconds(1.0);
            var deviceTimestamp1 = Microseconds64.FromMilliseconds(4.4);
            var deviceTimestamp2 = Microseconds64.FromMilliseconds(7.7);

            using (var recorder = new Recorder(mkvPath, null, config))
            {
                Assert.IsFalse(recorder.IsDisposed);
                Assert.AreEqual(mkvPath, recorder.FilePath);
                Assert.AreEqual(0, recorder.CustomTracks.Count);

                recorder.WriteHeader();

                WriteTestCaptures(recorder, deviceTimestamp0, deviceTimestamp1, deviceTimestamp2);
            }

            using (var playback = new Playback(mkvPath))
            {
                Assert.IsFalse(playback.IsDisposed);
                Assert.AreEqual(mkvPath, playback.FilePath);

                var trackCount = GetBuiltInTrackCount(config);
                Assert.AreEqual(trackCount, playback.Tracks.Count);
                Assert.AreEqual(trackCount, playback.Tracks.Count(t => t.IsBuiltIn));
                Assert.AreEqual(0, playback.Tracks.Count(t => !t.IsBuiltIn));

                playback.GetRecordConfiguration(out var recordConfig);
                Assert.AreEqual(config.CameraFps, recordConfig.CameraFps);
                Assert.AreEqual(config.ColorFormat, recordConfig.ColorFormat);
                Assert.AreEqual(config.ColorResolution, recordConfig.ColorResolution);
                Assert.AreEqual(config.ColorResolution != ColorResolution.Off, recordConfig.ColorTrackEnabled);
                Assert.AreEqual(config.DepthDelayOffColor, recordConfig.DepthDelayOffColor);
                Assert.AreEqual(config.DepthMode, recordConfig.DepthMode);
                Assert.AreEqual(config.DepthMode.HasDepth(), recordConfig.DepthTrackEnabled);
                Assert.IsFalse(recordConfig.ImuTrackEnabled);
                Assert.AreEqual(config.DepthMode.HasPassiveIR(), recordConfig.IRTrackEnabled);
                var startTimestamp = GetStartTimestamp(config, deviceTimestamp0);
                Assert.AreEqual(startTimestamp.ValueUsec, recordConfig.StartTimeOffset.ValueUsec);
                Assert.AreEqual(config.SubordinateDelayOffMaster, recordConfig.SubordinateDelayOffMaster);
                Assert.AreEqual(config.WiredSyncMode, recordConfig.WiredSyncMode);

                var length       = playback.RecordLength;
                var endTimestamp = GetEndTimestamp(config, deviceTimestamp2);
                Assert.AreEqual(endTimestamp.ValueUsec - startTimestamp.ValueUsec, length.ValueUsec);

                // Try play backward from start
                Assert.IsFalse(playback.TryGetPreviousCapture(out var capture));
                Assert.IsNull(capture);

                // Play forward
                Assert.IsTrue(playback.TryGetNextCapture(out capture));
                Assert.IsNotNull(capture);
                CheckCapture(capture, config, deviceTimestamp0);
                capture.Dispose();

                Assert.IsTrue(playback.TryGetNextCapture(out capture));
                Assert.IsNotNull(capture);
                CheckCapture(capture, config, deviceTimestamp1);
                capture.Dispose();

                Assert.IsTrue(playback.TryGetNextCapture(out capture));
                Assert.IsNotNull(capture);
                CheckCapture(capture, config, deviceTimestamp2);
                capture.Dispose();

                // EoF
                Assert.IsFalse(playback.TryGetNextCapture(out capture));
                Assert.IsNull(capture);

                // Play backward
                Assert.IsTrue(playback.TryGetPreviousCapture(out capture));
                Assert.IsNotNull(capture);
                CheckCapture(capture, config, deviceTimestamp2);
                capture.Dispose();

                Assert.IsTrue(playback.TryGetPreviousCapture(out capture));
                Assert.IsNotNull(capture);
                CheckCapture(capture, config, deviceTimestamp1);
                capture.Dispose();

                Assert.IsTrue(playback.TryGetPreviousCapture(out capture));
                Assert.IsNotNull(capture);
                CheckCapture(capture, config, deviceTimestamp0);
                capture.Dispose();

                // EoF
                Assert.IsFalse(playback.TryGetPreviousCapture(out capture));
                Assert.IsNull(capture);

                // Seek to end
                Assert.IsTrue(playback.TrySeekTimestamp(Microseconds64.Zero, PlaybackSeekOrigin.End));
                Assert.IsFalse(playback.TryGetNextCapture(out capture));
                Assert.IsNull(capture);

                Assert.IsTrue(playback.TryGetPreviousCapture(out capture));
                Assert.IsNotNull(capture);
                CheckCapture(capture, config, deviceTimestamp2);
                capture.Dispose();

                // Seek to start
                Assert.IsTrue(playback.TrySeekTimestamp(Microseconds64.Zero, PlaybackSeekOrigin.Begin));
                Assert.IsFalse(playback.TryGetPreviousCapture(out capture));
                Assert.IsNull(capture);

                Assert.IsTrue(playback.TryGetNextCapture(out capture));
                Assert.IsNotNull(capture);
                CheckCapture(capture, config, deviceTimestamp0);
                capture.Dispose();

                // Seek to device timestamp
                var ts = GetStartTimestamp(config, deviceTimestamp1);
                Assert.IsTrue(playback.TrySeekTimestamp(ts, PlaybackSeekOrigin.DeviceTime));

                Assert.IsTrue(playback.TryGetNextCapture(out capture));
                Assert.IsNotNull(capture);
                CheckCapture(capture, config, deviceTimestamp1);
                capture.Dispose();
            }

            File.Delete(mkvPath);
        }