CrafyVideoJS is a lightweight, hardware-accelerated (GPU) client-side JavaScript library for manipulating, decoding, and encoding MP4 H.264 (AVC) videos directly in the browser. By leveraging the browser's VideoEncoder
and VideoDecoder
APIs combined with mp4box.js, this library provides fast and efficient video processing without requiring server-side support.
- βοΈ Cut videos: Trim a video to a specific start and end time.
- βοΈ Adjust bitrate: Modify the output video's bitrate.
- π₯ Change resolution: Resize videos while maintaining aspect ratio.
- βΉοΈ Video info extraction: Retrieve metadata and structural information.
- π οΈ Decode and encode: Perform decoding and encoding of both video and audio tracks.
Requirement: mp4box.js library.
You can use the file hosted in this repository:
<script src="MP4Box_minified.js"></script>
Simply include the library in your project:
<script src="CrafyVideoJS.js"></script>
For production environments, use the minified version:
<script src="CrafyVideoJS_minified.js"></script>
Note: It only works in modern browsers that support VideoEncoder and VideoDecoder.
Check out the online demo to see CrafyVideoJS in action!
var CrafyVideoJS_instance = new CrafyVideoJS();
// Input video must be MP4 H.264 (avc) or MP4 H.265 (hvc)
function onInputFileChange(file) {
var reader = new FileReader();
reader.onload = function () {
CrafyVideoJS_instance.onProgress = function (progressData) {
console.log("ETA in ms:", progressData['video']['eta']);
};
CrafyVideoJS_instance.onResult = function (result) {
console.log("Link to output video:", URL.createObjectURL(new Blob([result['video_array_buffer']], { type: "video/mp4" })));
};
CrafyVideoJS_instance.onError = function (error) {
console.error("Error:", error);
};
// Video compression example, setting max bitrate and max resolution (720p):
CrafyVideoJS_instance.processVideo({
file: this.result,
max_video_bitrate: 1_500_000,
max_video_resolution: 1280
});
};
reader.readAsArrayBuffer(file);
}
This event is fired every time a sample is decoded or encoded.
{
"video": {
"decoding": {
"index": 11,
"count": 1000,
"percentage": 1,
"percentage_original": 1.1,
"eta": 11.5236
},
"encoding": {
"index": 11,
"count": 1000,
"percentage": 1,
"percentage_original": 1.1,
"eta": 9.5235
},
"eta": 12.123456
},
"audio": {
"decoding": {
"index": 11,
"count": 1000,
"percentage": 1,
"percentage_original": 1.1
},
"encoding": {
"index": 11,
"count": 1000,
"percentage": 1,
"percentage_original": 1.1
}
}
}
"count"
is the total number of samples."index"
is the index of the last sample processed."percentage"
is rounded."eta"
is in miliseconds.
This event is triggered when the encoding of the output video finishes successfully.
Key | Type | Description |
---|---|---|
video_array_buffer |
ArrayBuffer |
Output video as an ArrayBuffer . |
reencoding_time_seconds |
float |
Total processing time in seconds. |
reencoding_video_frames |
int |
Total number of encoded video samples. |
reencoding_video_fps |
int |
Average number of encoded video samples per second. |
reencoding_audio_frames |
int |
Total number of encoded audio samples. |
reencoding_audio_fps |
int |
Average number of encoded audio samples per second. |
This event is triggered when a fatal error occurs, that is, processing stops completely due to a problem.
Parameter | Type | Description |
---|---|---|
error |
error |
Error info |
This is the main function to load a video as an ArrayBuffer
, decode it, apply transformations, and re-encode it.
Parameter | Type | Default | Description |
---|---|---|---|
file |
ArrayBuffer |
Required | Input video file as an ArrayBuffer . |
start_timestamp |
int |
false |
(Optional) Start trimming the video from this timestamp (in microseconds). |
end_timestamp |
int |
false |
(Optional) Trim the video up to this timestamp (in microseconds). |
max_video_bitrate |
int |
false |
(Optional) Set the maximum output video bitrate (in bits). If the bitrate of the input video is lower, it will not be modified. |
max_video_resolution |
int |
false |
(Optional) Defines the longest edge of the output video in pixels (example: 1920 for 1080p, 1280 for 720p, etc). If the video resolution is lower, it will not be modified. |
queue_max_size |
int |
10 |
(Optional) Maximum number of video frames in the processing queue. (A larger number will increase parallel graphics calculations and provide a slight speed improvement, but may cause a bottleneck. A value greater than 15 is not recommended.) |
redimension_system |
string |
'bitmap' |
(Optional) Video resizing method: "webgl" or "bitmap" . ("bitmap" is the recommended option). reference |
encoder_latencyMode |
string |
'quality' |
(Optional) Encoder latency mode: "quality" or "realtime" . reference |
video_info_read_max_time |
int |
60000 |
(Optional) Maximum time (in milliseconds) to retrieve input video information. If this time limit is exceeded, an error is returned. |
redimension_resizeQuality |
string |
'low' |
(Optional) Resize quality: "pixelated" , "low" , "medium" , or "high" . reference |
max_input_video_size |
int |
false |
(Optional) Maximum input video file size in bytes. If the input video exceeds this size, an error is returned. |
max_input_video_samplesCount |
int |
false |
(Optional) Maximum number of video samples in the input. If the input video exceeds this number of samples, an error is returned. Number of samples = Duration in seconds * FPS |
preprocess_video_info_function |
function |
false |
(Optional) Async function to preprocess the input video metadata and dynamically modify the method parameters. |
audio_queue_max_size |
int |
20 |
(Optional) Maximum number of audio frames in the processing queue. (A larger number will increase parallel graphics calculations and provide a slight speed improvement, but may cause a bottleneck. A value greater than 30 is not recommended.) |
Allowed formats and codecs:
- MP4
- Video codec:
- H.264 (AVC)
- H.265 (HVC)
- Audio codec:
- mp4a
- Opus
- Video codec:
Fixed maximum.
CrafyVideoJS_instance.processVideo({
max_video_bitrate: 1_500_000 // Fixed int.
});
Input video bitrate percentage.
CrafyVideoJS_instance.processVideo({
max_video_bitrate: {
"type": "percentage_of_original",
"percentage": 50
}
});
Fixed maximum based on closest resolution to output video resolution.
CrafyVideoJS_instance.processVideo({
max_video_bitrate: {
"type": "max_by_resolution",
"data": {
// (Total resolution = Video width * Video height): Max bitrate
2073600: 5_000_000, // 1080p, 5mbps
921600: 1_000_000, // 720p, 1mbps
307200: 300_000 // 480p, 300kbps
}
}
});
Percentage of input video bitrate based on closest resolution to output video resolution.
CrafyVideoJS_instance.processVideo({
max_video_bitrate: {
"type": "percentage_of_original_by_resolution",
"data": {
// (Total resolution = Video width * Video height): Percentage of input video bitrate
2073600: 50, // 1080p, 50%
921600: 40, // 720p, 40%
307200: 30 // 480p, 30%
}
}
});
Percentage of input video bitrate based on closest resolution to output video resolution, combined with a fixed maximum.
CrafyVideoJS_instance.processVideo({
max_video_bitrate: {
"type": "percentage_and_max_of_original_by_resolution",
"data": {
// (Total resolution = Video width * Video height): {percentage, max}
2073600: {
"percentage": 50,
"max": 5_000_000
},
921600: {
"percentage": 40,
"max": 1_000_000
},
307200: {
"percentage": 30,
"max": 300_000
}
}
}
});
Fixed maximum.
CrafyVideoJS_instance.processVideo({
max_video_resolution: 1920 // 1080p
});
Fixed maximum based on closest number of samples to number of samples from the input video.
CrafyVideoJS_instance.processVideo({
max_video_resolution: {
"type": "by_near_samplesCount",
"data": {
// (Number of samples = Video seconds duration * Video FPS): Max resolution
1200: 1920, // 1080p
30000: 1280 // 720p
}
}
});
Fixed maximum based on a maximum number of samples from the input video.
CrafyVideoJS_instance.processVideo({
max_video_resolution: {
"type": "by_max_samplesCount",
"data": {
// (Number of samples = Video seconds duration * Video FPS): Max resolution
1200: 1920,
30000: 1280,
0: 640 // 0 is used when the number of samples in the input video is greater than all the maximums set.
}
}
});
If set, this function will be called after reading the input video data and will wait for your response to continue.
The function will receive the input video information as a parameter, and must respond with an object that has the same structure as the one used as options in CrafyVideoJS.processVideo(options)
.
This feature allows fine-grained control over configuration parameters for video processing based on input video information.
Example:
CrafyVideoJS_instance.processVideo({
preprocess_video_info_function: async function (video_info) {
// Process video_info (mp4box file).
return {
redimension_resizeQuality: "high"
};
}
});
Feature | CrafyVideoJS | FFmpeg WASM |
---|---|---|
Performance | Very fast (hardware-accelerated via GPU). | Slow (WebAssembly is CPU-only). |
Browser Compatibility | Modern browsers supporting VideoEncoder and VideoDecoder (no Firefox support yet). |
Works in most browsers, including Firefox. |
Formats/Codecs | Video input: MP4 (H.264/AVC), MP4 (H.265/HVC). Video output: MP4 (H.264/AVC) only. | Wide range of formats and codecs. |
Use Case | Ideal for MP4 H.264-focused applications requiring efficiency. | Flexible for various video processing tasks. |
Note: CrafyVideoJS plans to support additional codecs and formats in future releases.
We welcome contributions to improve CrafyVideoJS! Here's how you can help:
- Major Changes: Fork the repository, create a branch, implement your changes, and submit a pull request.
- Bug Reports & Feature Requests: Open an issue with as much detail as possible, including error messages and steps to reproduce (if applicable).
- Small Fixes: Feel free to directly submit issues or pull requests for minor changes.
CrafyVideoJS is licensed under the MIT License. See the LICENSE
file for more details.
Happy coding! π
Made with π by Crafy Holding.