### Project Setup and Commands Source: https://github.com/recallatk/roughcut-videocontext/blob/main/CONTRIBUTING.md Commands to clone the repository, install dependencies, and run various development tasks like testing, building, and linting. ```bash git clone https://github.com/Recallatk/roughcut-videocontext.git cd roughcut-videocontext # or your fork npm install npm test # run unit tests npm run build # build dist/ npm run typecheck # run TypeScript checks npm run lint # run ESLint ``` -------------------------------- ### Quick Start Video Composition Source: https://github.com/recallatk/roughcut-videocontext/blob/main/README.md Basic example demonstrating how to set up VideoContext, load two videos, apply a crossfade transition, and play the composition. Ensure the canvas element with id 'canvas' exists in your HTML. ```js import VideoContext from "@videocontext/core"; const canvas = document.getElementById("canvas"); const ctx = new VideoContext(canvas); const video1 = ctx.video("./video1.mp4"); video1.start(0); video1.stop(4); const video2 = ctx.video("./video2.mp4"); video2.start(2); video2.stop(6); const crossFade = ctx.transition(VideoContext.DEFINITIONS.CROSSFADE); crossFade.transition(2, 4, 0.0, 1.0, "mix"); video1.connect(crossFade); video2.connect(crossFade); crossFade.connect(ctx.destination); ctx.play(); ``` -------------------------------- ### Install VideoContext Core Source: https://github.com/recallatk/roughcut-videocontext/blob/main/README.md Install the VideoContext core package using npm. ```sh npm install @videocontext/core ``` -------------------------------- ### Install Dependencies Source: https://github.com/recallatk/roughcut-videocontext/blob/main/README.md Installs project dependencies using npm. ```bash npm install ``` -------------------------------- ### VideoNode Usage Source: https://github.com/recallatk/roughcut-videocontext/blob/main/README.md Create and connect a VideoNode to the destination. Specify the video source file and its start and stop times. ```js const videoNode = ctx.video("./video.mp4"); videoNode.connect(ctx.destination); videoNode.start(0); videoNode.stop(4); ``` -------------------------------- ### AudioNode Usage Source: https://github.com/recallatk/roughcut-videocontext/blob/main/README.md Create and connect an AudioNode to the destination. Specify the audio source file and its start and stop times. ```js const audioNode = ctx.audio("./audio.mp3"); audioNode.connect(ctx.destination); audioNode.start(0); audioNode.stop(4); ``` -------------------------------- ### Define Media Fragment URL Source: https://github.com/recallatk/roughcut-videocontext/blob/main/AdvancedExamples.md Example of a URL formatted to request a specific fragment of a video, including start and end times for playback. ```JavaScript const urlForMediaFragment = "./BigBuckBunny.mp4#t=5,12" ``` -------------------------------- ### Conventional Commit Message Examples Source: https://github.com/recallatk/roughcut-videocontext/blob/main/CONTRIBUTING.md Examples of commit messages following the Conventional Commits specification, indicating the type of change made. ```text feat: add adapter boundary for app integration fix: restore _element guard in _seek chore: remove jsdoc, update serve docs: rewrite contributing guide test: add cache lifecycle integration tests ``` -------------------------------- ### ImageNode Usage Source: https://github.com/recallatk/roughcut-videocontext/blob/main/README.md Create and connect an ImageNode to the destination. Specify the image source file and its start and stop times. ```js const imageNode = ctx.image("./photo.png"); imageNode.connect(ctx.destination); imageNode.start(0); imageNode.stop(4); ``` -------------------------------- ### Custom HLS Source Node Source: https://github.com/recallatk/roughcut-videocontext/blob/main/README.md Example of creating a custom source node for HLS streams by extending VideoContext.NODES.VideoNode. This includes loading the HLS source and managing the HLS.js instance. ```js import Hls from "hls.js"; class HLSNode extends VideoContext.NODES.VideoNode { constructor( src, gl, renderGraph, currentTime, playbackRate, sourceOffset, preloadTime, hlsOptions = {} ) { const video = document.createElement("video"); super(video, gl, renderGraph, currentTime, playbackRate, sourceOffset, preloadTime); this.hls = new Hls(hlsOptions); this.hls.attachMedia(video); this._src = src; this._displayName = "HLSNode"; this._elementType = "hls"; } _load() { if (!this._loadTriggered) { this.hls.loadSource(this._src); } super._load(); } destroy() { if (this.hls) this.hls.destroy(); super.destroy(); } } const hlsNode = ctx.customSourceNode(HLSNode, "https://example.com/stream.m3u8"); hlsNode.start(0); hlsNode.stop(60); hlsNode.connect(ctx.destination); ctx.play(); ``` -------------------------------- ### Control Individual SourceNode Playback Source: https://github.com/recallatk/roughcut-videocontext/blob/main/plan.md Manage the playback of an individual SourceNode. Use `start()` to add to the timeline and `stop()` to signal the end. `clearTimelineState()` resets the node to a 'waiting' state. ```JavaScript videoNode.start(); //start playback now. videoNode.start(10); //start playback in 10 seconds. videoNode.clearTimelineState(); // stops any upcoming start events and sets state to "waiting" videoNode.stop(); //stop playback now. videoNode.stop(10); //stop playback in 10 seconds. videoNode.clearTimelineState(); // stops playback and sets state to "waiting" ``` -------------------------------- ### Custom VideoNode for Media Fragments Source: https://github.com/recallatk/roughcut-videocontext/blob/main/AdvancedExamples.md Extends VideoNode to support media fragments for looping. Parses start and end times from the URL hash and integrates with the VideoContext rendering loop. Note: This implementation relies on private VideoNode APIs and may break in future versions. ```JavaScript import VideoContext from "videocontext"; import parse from "url-parse"; class MediaFragmentVideoNode extends VideoContext.NODES.VideoNode { constructor( src, gl, renderGraph, currentTime, sourceOffset, preloadTime, options ) { /** * In this example we're just supporting part of media fragments * spec, and only when the element is set to `loop`. * * First, parse the start and end loop times from the src url */ const fragment = options.loop && parse(src) .hash.split("&") .filter(str => str.includes("t=")) .map(str => str.split("t=")[1]) .map(values => values.split(",").map(parseFloat)) .map(([start, end]) => ({ start, end }))[0]; /** * We follow the VideoNode implementation pretty closely when * calling `super`. * * The only difference: * - hard code values for playback rate and mediaelement cache * - add our fragement `start` time to the the `sourceOffset` **/ super( src, gl, renderGraph, currentTime, 1 /* playback rate */, sourceOffset + (fragment && fragment.start), preloadTime, undefined /* mediaelement cache */, options ); /** * Next we update add a couple of custom properties to * our class. These will only be accessed within our subclass */ if (fragment) { this._loopStart = fragment.start; this._loopDuration = fragment.end - fragment.start; } this._displayName = "MediaFragmentVideoNode"; } /** * We extend the VideoNode `_update` method to implement our loop. * * * Normal caveat with custom source nodes: you are tying your * code to a private API, be careful when upgrading videocontext! accessed * * Our `_update` signature matches that of `VideoNode._update` * @param {number} currentTime * @param {boolean} triggerTextureUpdate */ _update(currentTime, triggerTextureUpdate = true) { super._update(currentTime, triggerTextureUpdate); /** * if no fragment is set `_update` behaves as before. * otherwise: */ if (this._loopStart) { const loopEndTime = this._startTime + this._loopDuration; if (this._currentTime >= loopEndTime) { // tell the node that it should start again from now this._startTime = loopEndTime; // seek the element back to the start // sourceOffset already has the loop start included this._element.currentTime = this._sourceOffset || 0; } } } } const START_LOOP_TIME = 19; const END_LOOP_TIME = 21; const MEDIA_FRAGMENT_URL = `http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4#t=${START_LOOP_TIME},${END_LOOP_TIME}`; // Setup the video context. const canvas = document.getElementById("canvas"); const ctx = new VideoContext(canvas); const videoNode1 = ctx.customSourceNode( MediaFragmentVideoNode, MEDIA_FRAGMENT_URL, 0, 4, { volume: 0, loop: true } ); videoNode1.startAt(0); videoNode1.connect(ctx.destination); ctx.play(); ``` -------------------------------- ### CanvasNode Usage Source: https://github.com/recallatk/roughcut-videocontext/blob/main/README.md Create and connect a CanvasNode to the destination using an existing HTML canvas element. Specify its start and stop times. ```js const srcCanvas = document.getElementById("input-canvas"); const canvasNode = ctx.canvas(srcCanvas); canvasNode.connect(ctx.destination); canvasNode.start(0); canvasNode.stop(4); ``` -------------------------------- ### Build with Watch Mode Source: https://github.com/recallatk/roughcut-videocontext/blob/main/README.md Builds the project with watch mode enabled for development. ```bash npm run dev ``` -------------------------------- ### Initialize VideoContext Source: https://github.com/recallatk/roughcut-videocontext/blob/main/plan.md Instantiate a new VideoContext object to manage synchronized media playback. ```JavaScript var ctx = new VideoContext() ``` -------------------------------- ### Set Up Video Transitions and Compositing Source: https://github.com/recallatk/roughcut-videocontext/blob/main/plan.md Defines a sequence of video nodes and transitions to create a timeline effect. This includes setting up transition nodes, connecting them to an output, and registering a listener for render updates. Note the use of `transition` and `progress` properties for controlling the animation. ```javascript var v1Node, v2Node, v3Node; //SourcesNodes var v1v2Tranisition, v2v3Transition = ctx.createTransition(VideoContext.Transitions.XFADE); //Tr var outputComposit; v1Node.connect(v1v2Transition, 0); v2Node.connect(v1v2Transition, 1); v1v2Transition.transistion(2,1); v2Node.connect(v2v3Transition, 0); v3Node.connect(v2v3Transition, 1); v2v3Transition.transition(5,1); v1v2Tranisition.connect(outputComposit,0); v2v3Tranisition.connect(outputComposit,1); ctx.registeLisner("render", function(currentTime){ tranistionNode.progres = 0.0 - 1.0; }); tranistion.transition(time, duration, function(progress){ return 0.0-10; }); traniston.progress = 0.5; ``` -------------------------------- ### Connect Inputs by Name or Index Source: https://github.com/recallatk/roughcut-videocontext/blob/main/README.md Demonstrates connecting video nodes to a transition node using either named inputs ('image_a') or by index (1). ```javascript videoNode1.connect(crossfade, "image_a"); // by name videoNode2.connect(crossfade, 1); // by index ``` -------------------------------- ### Initialize VideoContext on Window Load Source: https://github.com/recallatk/roughcut-videocontext/blob/main/test/e2e/html/index.html Initializes the VideoContext when the window loads. Catches and logs any errors during initialization. ```javascript window.onload = function () { try { window.ctx = new VideoContext(canvas); } catch (e) { window.ctxError = e.message || String(e); console.error("VideoContext init failed:", e); } }; ``` -------------------------------- ### Run Unit and Integration Tests Source: https://github.com/recallatk/roughcut-videocontext/blob/main/README.md Executes unit and integration tests for the project. ```bash npm test ``` -------------------------------- ### VideoContext Constructor Options Source: https://github.com/recallatk/roughcut-videocontext/blob/main/README.md Configure VideoContext behavior by passing an options object to the constructor. Options include manual update control, end-on-last-source behavior, video element caching, stall detection, and seek debouncing. ```js const ctx = new VideoContext(canvas, { manualUpdate: false, // drive updates yourself via ctx.update() endOnLastSourceEnd: true, // auto-pause when the last source ends useVideoElementCache: true, // reuse