async Task <long> PlayMp4FileAsync(string path, int targetWidth, int targetHeight, double startTimestamp, double endTimestamp, DNNTools.DNNengine dnnEngine, float confidence, bool useTracker, IProgress <ProgressStruct> progress, CancellationToken token, WPFTools.PauseToken pauseToken, bool paceOutput, Dictionary <double, int> frameIndexLookup) { // path - filename/path to Mp4 file // startingAt - point in time to start decoding/playback, given in milliseconds // targetWidth, targetHeight - desired pixel dimension of decoded frames // paceOutput - flag indicating whether to pace the output, using frame // timestamps and framerate, so that playback is at video rate long count = -1; if (File.Exists(path)) { count = await Task.Run <long>(async() => { double timestamp = 0.0f; Stopwatch sw = new Stopwatch(); IntPtr mp4Reader = IntPtr.Zero; try { mp4Reader = Mp4.CreateMp4Reader(path); } catch (Exception ex) { string message = ex.Message; } DNNTools.NonMaximumSuppression nms = new DNNTools.NonMaximumSuppression(); nms.Init(); DNNTools.MultiTracker multiTracker = new DNNTools.MultiTracker(); if (mp4Reader != IntPtr.Zero) { try { long durationMilliseconds; double frameRate; long timestampDelta; long timestampWindow; int height; int width; int sampleCount; // Get the video metadata from the file if (Mp4.GetVideoProperties(mp4Reader, out durationMilliseconds, out frameRate, out width, out height, out sampleCount)) { timestampDelta = (long)(1000.0f / frameRate); timestampWindow = timestampDelta / 2; // Ask the decoder to resample to targetWidth x targetHeight, and assume 24-bit RGB colorspace byte[] frame = new byte[targetWidth *targetHeight * 3]; bool key; ProgressStruct prog; m_frameCount = 0; // move to starting position double actualStart = Mp4.SetTimePositionAbsolute(mp4Reader, startTimestamp); int frameIndex = 0; // get the starting frame index int tempIndex = 0; if (frameIndexLookup != null) { if (frameIndexLookup.TryGetValue(actualStart, out tempIndex)) { frameIndex = tempIndex; } } // create flag used to quit early bool running = true; if (actualStart == -1) // failed to move to start position { running = false; } sw.Start(); while (running) { timestamp = (double)Mp4.GetNextVideoFrame(mp4Reader, frame, out key, targetWidth, targetHeight) / 1000.0; if (timestamp == -0.001) // EOF { running = false; break; } if (token.IsCancellationRequested) { // pause or stop requested running = false; break; } byte[] frameCopy = new byte[targetWidth *targetHeight * 3]; Buffer.BlockCopy(frame, 0, frameCopy, 0, targetWidth *targetHeight * 3); prog = new ProgressStruct(timestamp, durationMilliseconds, frameCopy, frameIndex, key, !running, targetWidth, targetHeight); if (dnnEngine != null) { prog.boxList = dnnEngine.EvalImage(frameCopy, targetWidth, targetHeight, 3, targetWidth, targetHeight, confidence); prog.boxList = nms.Execute(prog.boxList, 0.50f); if (useTracker) { List <DNNTools.BoundingBox> trackedBoxes = multiTracker.Update(frameCopy, targetWidth, targetHeight, prog.boxList); prog.boxList.AddRange(trackedBoxes); prog.boxList = nms.Execute(prog.boxList, 0.50f); } else { multiTracker.ClearTrackers(); } } if (progress != null && prog.data != null) { // if we're in playback mode, pace the frames appropriately if (paceOutput) { while (sw.ElapsedMilliseconds < timestampDelta) { Thread.Sleep(1); } sw.Restart(); } m_frameCount++; // send frame to UI thread progress.Report(prog); } await pauseToken.WaitWhilePausedAsync(); if (timestamp >= endTimestamp) { break; } frameIndex++; } } } catch (Exception ex) { m_errorMsg = ex.Message; timestamp = -2; // indicating an exception occurred } finally { try { Mp4.DestroyMp4Reader(mp4Reader); } catch (Exception ex) { string message = ex.Message; } progress.Report(new ProgressStruct(-0.001, 0, null, m_frameCount, false, true, 0, 0)); // signal that the player stopped (timestamp = -0.001) } } else { timestamp = -3; m_errorMsg = "Could not open file."; } return((long)timestamp); }, token); } // END File.Exists return(count); }
public bool LoadGopIntoCache(int gopIndex, SortedList <int, GopRecord> gopList, SortedList <int, FrameRecord> frameCache) { bool success = true; // TEMP GopRecord gop; if (gopList.TryGetValue(gopIndex, out gop)) { int firstFrameIndexInGop = gop.frameIndex; if (!frameCache.ContainsKey(firstFrameIndexInGop)) { // go get all frames for gop with a timestamp = gop.timestamp // then add them to the m_frameCache -- which means decoding them // and writing the decoded from to a file using the function // WriteFile(string filename, double timestamp, int width, int height, int depth, byte[] frameData) // NEW double actualPosition = Mp4.SetTimePositionAbsolute(m_mp4Reader, gop.timestamp + 0.001); byte[] frame = new byte[m_targetWidth * m_targetHeight * 3]; bool key; int ndx = 0; if (!frameCache.ContainsKey(gop.frameIndex)) { while (true) { double ts = (double)Mp4.GetNextVideoFrame(m_mp4Reader, frame, out key, m_targetWidth, m_targetHeight) / 1000.0; if (ts == -1) // EOF { break; } string filename = BuildFilenameFromTimestamp(ts); int frameIndex = gop.frameIndex + ndx; if (WriteFile(filename, ts, m_targetWidth, m_targetHeight, 3, frame)) { frameCache.Add(frameIndex, new FrameRecord(ts, Path.Combine(m_cacheDirectory, filename))); } else { success = false; break; } ndx++; if (ndx >= gop.numFrames) { break; } } } } } // END TEMP return(success); }