/// <summary> /// Processes a NAL. /// </summary> /// <param name="nal">The NAL to be processed.</param> /// <returns>Type of NAL.</returns> private int ProcessNal(Nal nal) { // get the NAL type int nalType = -1; if (nal.Buffer.Length > 4) { byte[] header = new byte[5]; nal.Buffer.CopyTo(0, header, 0, 5); nalType = (header[0] == 0 && header[1] == 0 && header[2] == 0 && header[3] == 1) ? (header[4] & 0x1F) : -1; } //Log.Verbose("NAL: type = {0}, len = {1}", nalType, nal.Buffer.Length); // process the first SPS record we encounter if (nalType == 7 && !isDecoding) { byte[] sps = new byte[nal.Buffer.Length]; nal.Buffer.CopyTo(sps); SpsParser parser = new SpsParser(sps, (int)nal.Buffer.Length); //Log.Verbose("SPS: {0}x{1} @ {2}", parser.width, parser.height, parser.fps); VideoEncodingProperties properties = VideoEncodingProperties.CreateH264(); properties.ProfileId = H264ProfileIds.High; properties.Width = (uint)parser.width; properties.Height = (uint)parser.height; streamSource = new MediaStreamSource(new VideoStreamDescriptor(properties)); streamSource.BufferTime = TimeSpan.Zero; streamSource.CanSeek = false; streamSource.Duration = TimeSpan.Zero; streamSource.SampleRequested += HandleSampleRequested; var action = Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.High, () => { statusTextBlock.Visibility = Visibility.Collapsed; media.SetMediaStreamSource(streamSource); media.Play(); storyboard.Begin(); }); isDecoding = true; } // queue the frame if (nalType > 0 && isDecoding) { if (deferral != null) { request.Sample = MediaStreamSample.CreateFromBuffer(nal.Buffer, new TimeSpan(0)); lock (availableNals) { //Log.Verbose("availableNals.Enqueue"); availableNals.Enqueue(nal); } deferral.Complete(); deferral = null; request = null; //Log.Verbose("Deferral Complete"); } else { //Log.Verbose("usedNals.Enqueue"); lock (usedNals) { usedNals.Enqueue(nal); } } } // return the NAL type return(isDecoding ? nalType : -1); }
/// <summary> /// Reads bytes form the socket and parses them into NALs. /// </summary> /// <returns>The asynchronous task.</returns> private async Task ReadSocketAsync() { Log.Info("+VideoPage.ReadSocketAsync"); Nal nal = availableNals.Dequeue(); int numZeroes = 0; int numReadErrors = 0; bool gotHeader = false; // look for a TCP/IP connection try { // try to connect to the device socket = new StreamSocket(); HostName hostName = new HostName(camera.Address); CancellationTokenSource tokenSource = new CancellationTokenSource(); tokenSource.CancelAfter(settings.ScanTimeout); await socket.ConnectAsync(hostName, camera.Port.ToString()).AsTask(tokenSource.Token); // if we get here, we opened the socket isConnected = true; DataReader reader = new DataReader(socket.InputStream); reader.InputStreamOptions = InputStreamOptions.Partial; while (!isCancelled) { uint len = await reader.LoadAsync(BUFFER_SIZE); //Log.Verbose("numBytes = {0}", len); if (nal == null) { lock (availableNals) { //Log.Verbose("availableNals.Dequeue: {0}", availableNals.Count); nal = (availableNals.Count > 0) ? availableNals.Dequeue() : null; } } // process the input buffer if (nal == null) { if (len > 0) { reader.ReadBuffer(len); } } else if (len > 0) { numReadErrors = 0; for (int i = 0; i < len && nal != null && !isCancelled; i++) { // resize the NAL if necessary if (nal.Stream.Length == nal.Buffer.Capacity) { nal.Resize(nal.Buffer.Capacity + NAL_SIZE_INC); } // add the byte to the NAL byte b = reader.ReadByte(); nal.Stream.WriteByte(b); // look for a header if (b == 0) { numZeroes++; } else { if (b == 1 && numZeroes == 3) { if (nal.Stream.Length > 4) { if (gotHeader) { nal.Buffer.Length = (uint)nal.Stream.Length - 4; int nalType = ProcessNal(nal); if (isCancelled) { break; } if (nalType != -1) { lock (availableNals) { //Log.Verbose("availableNals.Dequeue: {0}", availableNals.Count); nal = (availableNals.Count > 0) ? availableNals.Dequeue() : null; } } } if (nal != null) { nal.Stream.Seek(0, SeekOrigin.Begin); nal.Stream.SetLength(0); nal.Stream.WriteByte(0); nal.Stream.WriteByte(0); nal.Stream.WriteByte(0); nal.Stream.WriteByte(1); } else { //Log.Verbose("null"); } } gotHeader = true; } numZeroes = 0; } } } else { numReadErrors++; if (numReadErrors >= MAX_READ_ERRORS) { DisplayStatusMessage(Res.Error.LostConnection); break; } } } reader.Dispose(); socket.Dispose(); isDecoding = false; isConnected = false; await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => { media.Stop(); Frame.GoBack(); }); } catch (Exception ex) { DisplayStatusMessage(isConnected ? Res.Error.LostConnection : Res.Error.CouldntConnect); Log.Error("EXCEPTION: {0}", ex.ToString()); isConnected = false; return; } Log.Info("-VideoPage.ReadSocketAsync"); }