IEnumerator CaptureWebcamToFileCoroutine() { // Open a webcam streamer. The url prefix for this is webcam:// // Optionally a webcam device id can be added (to get a list, use WebCamTexture.devices) string webcamStreamUrl = "webcam://"; Streamer streamer = Streamer.forUrl (webcamStreamUrl); streamer.Connect (webcamStreamUrl); // Set up a remux @ 15fps var vi = new VideoStreamInfo (streamer.videoStreamInfo); vi.framerate = 15; // must be lower than framerate with this approach! AviRemux remux = new AviRemux (); remux.Init (File.OpenWrite (outfile), vi, null); // Do fixed time capture, 10 seconds (150 frames @ 15fps) // The webcam framerate can be lower or higher than this. If it is lower then // a frame is written multiple times, if higher, then some frames are now written. float captureStartTime = Time.realtimeSinceStartup; int realFrameNr, lastRealFrameNr = -1; do { // Read a frame from webcam. It returns a frame number, but we're not using it. byte[] buf; frame = streamer.VideoPosition; int bytesCnt = streamer.ReadVideoFrame (out buf); // Calculate the video frame number that we should be writing. realFrameNr = Mathf.RoundToInt ((Time.realtimeSinceStartup - captureStartTime) * vi.framerate); // If the loop is being executed too seldom compared to vi.framerate, write a warning to console. if (realFrameNr - lastRealFrameNr > 1) { Debug.LogWarning ("Output framerate too high, possibly just skipped " + (realFrameNr - lastRealFrameNr) + " frames"); } // Write as many frames as we need. Normally this is 0 or 1, but can be higher (see the warning a few lines above) while (lastRealFrameNr < realFrameNr) { remux.WriteNextVideoFrame (buf, bytesCnt); lastRealFrameNr ++; } // Give control back to Unity for one frame yield return 1; } while(realFrameNr < 150); // We're done. Close the remux and streamer remux.Shutdown (); streamer.Shutdown (); Debug.Log ("Done capturing"); }
void DropHalfTheFramesRemux() { // In this example we're going one level deeper in the API and work directly // with Demux class. We could use MoviePlayerUtil.Load too, but for remuxing // we don't need Decoders to be instantiated, because we're just copying encoded // frame bytes around. // // Since we're not using decoders, we're not referencing anything from Unity API. // Therefore it's possible to run it in separate thread. RunInBackgroundOrNot (delegate() { // Instantiate a demux for an input stream based on stream type. Stream instream = File.OpenRead (infile); Demux demux = Demux.forSource (instream); demux.Init (instream); // Instantiate a remux for an output stream. Here we have to explicity // instantiate the remux we want, in this case, AviRemux, and set its // properties. Since we're not doing much here, we can use the same // videoStreamInfo and audioStreamInfo for remux as demux. For the video // however we clone the stream info, because we want to change it. Since // we're going to drop every other frame, we also need to lower the // video framerate. Stream outstream = File.OpenWrite (outfile); Remux remux = new AviRemux (); var remuxVideoStreamInfo = new VideoStreamInfo (demux.videoStreamInfo); remuxVideoStreamInfo.framerate /= 2; remux.Init (outstream, remuxVideoStreamInfo, demux.audioStreamInfo); // Just sum buffers and variables needed later byte[] videoBuffer, audioBuffer; int videoBytesRead, audioBytesRead; // Loop until we've processed all the video frames. If we wanted to run this code // in main Unity thread without blocking, then we could wrap it all in a coroutine // and do "yield return 1" inside the loop. do { // Here we're using sequential access to video (and audio) stream. The same could // be achieved with random access, but then only demuxes that can seek in a file // can be used (no streaming from network or webcam). videoBytesRead = demux.ReadVideoFrame (out videoBuffer); if (videoBytesRead > 0) { // Read the exact number of audio samples that are to be played during this frame int samplesPerVideoFrame = (int)(demux.audioStreamInfo.sampleRate / demux.videoStreamInfo.framerate); audioBytesRead = demux.ReadAudioSamples (out audioBuffer, samplesPerVideoFrame); // Only write every second video frame, but all the audio samples. The total stream // lengths will still be the same, because we've set the framerate for remuxed stream // to half of the original. if (demux.VideoPosition % 2 == 1) { remux.WriteNextVideoFrame (videoBuffer, videoBytesRead); } remux.WriteNextAudioSamples (audioBuffer, audioBytesRead); } } while(videoBytesRead > 0); // Close the remux and demux. While it's possible to leave demux just hanging there unclosed and // possibly introducing a memory leak, we have to Close the remux for the output to be playable. // The reason is that AviDemux needs to write all unwritten index chunks and update the avi header // after all frames have been written. remux.Shutdown (); demux.Shutdown (); }); }
/// <summary> /// Starts the processing. /// </summary> void StartProcessing() { movie = new Movie (); movie.sourceStream = File.OpenRead (srcPath); dstStream = File.OpenWrite (dstPath); remux = new AviRemux (); // create and initialize demux for the source data movie.demux = Demux.forSource (movie.sourceStream); movie.demux.Init (movie.sourceStream); // create video stream and decoder too. without a decoder we can't access pixels to compare if (!movie.demux.hasVideo) { throw new MpException ("Remux needs video stream inside an AVI"); } movie.videoDecoder = VideoDecoder.CreateFor (movie.demux.videoStreamInfo); movie.videoDecoder.Init (out framebuffer, movie.demux); // create a remux. this will write into dstStream bool outputHasAudio = movie.demux.hasAudio && !discardAudio; remux.Init (dstStream, movie.demux.videoStreamInfo, outputHasAudio ? movie.demux.audioStreamInfo : null); // create a duplicate finder. most of the options control how the frames are actually compared options.otherStreamsAvailable = outputHasAudio; dupFinder = new DuplicateFrameFinder (movie.videoDecoder, framebuffer, 0, movie.demux.videoStreamInfo.frameCount, options); // if we want a to log the duplicate indexes into a file, then clear the file first if (!string.IsNullOrEmpty (logDuplicatesPath)) { File.WriteAllText (logDuplicatesPath, "# Duplicate frame index for " + srcPath + "\n"); } }