У меня есть следующая функция, которая может создавать миниатюры из видео:
async function getThumbnailForVideo(videoUrl) {
const video = document.createElement("video");
const canvas = document.createElement("canvas");
video.style.display = "none";
canvas.style.display = "none";
// Trigger video load
await new Promise((resolve, reject) => {
video.addEventListener("loadedmetadata", () => {
video.width = video.videoWidth;
video.height = video.videoHeight;
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
// Seek the video to 25%
video.currentTime = video.duration * 0.25;
});
video.addEventListener("seeked", () => resolve());
video.src = videoUrl;
});
// Draw the thumbnail
canvas
.getContext("2d")
.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
const imageUrl = canvas.toDataURL("image/png");
return imageUrl;
}
В сочетании с URL.createObjectURL
я могу создать эскиз из выбранного пользователем видеофайла. Я создал следующий тестовый проект на StackBlitz для тестирования: Редактор приложений Предварительный просмотр приложения
Хотя кажется, что это нормально работает для Chrome и Safari, похоже, что Firefox не уважает EXIF-информацию видео и поэтому рисует ее неправильно.
В документации MDN для CanvasRenderingContext2D.drawImage прямо указано, что :
drawImage()
будет игнорировать все метаданные EXIF в изображениях, включая Ориентацию. Вы должны сами определить Ориентацию и использовать rotate (), чтобы исправить это.
Modernizr намекает на решение в своем определение функции exiforientation, если я смогу прочитать данные вращения из файла, так что мне нужно только выполнить дополнительные преобразования в Firefox .
Мне любопытно, есть ли более идемпотентное решение для рисования изображения из HTMLVideoElement
во всех браузерах?
1 ответ
Таким образом, оказывается, что тест Modernizr exiforientation
проверяет только то, учитывает ли элемент img
данные EXIF изображения, но не то, правильно ли отображается то же изображение, нарисованное на холсте.
Вместо этого я решил создать свой собственный тест, нарисовав известное видео на холсте и протестировав его. Я создал видео, используя ffmpeg
:
ffmpeg -filter_complex \
"color=color=#ffffff:duration=1us:size=4x4[bg]; \
color=color=#ff0000:duration=1us:size=2x2[r]; \
color=color=#00ff00:duration=1us:size=2x2[g]; \
color=color=#0000ff:duration=1us:size=2x2[b]; \
[bg][r]overlay=x=2:y=0:format=rgb:alpha=premultiplied[bg+r]; \
[bg+r][g]overlay=x=0:y=2:format=rgb:alpha=premultiplied[bg+r+g]; \
[bg+r+g][b]overlay=x=2:y=2:format=rgb:alpha=premultiplied[bg+r+g+b]" \
-map "[bg+r+g+b]" \
-y wrgb-0.mp4
ffmpeg -i wrgb-0.mp4 -c copy -metadata:s:v:0 rotate=180 -y wrgb-180.mp4
Используя одну и ту же демонстрацию, я вижу, что Chrome и Firefox создают разные превью видео на холст.
Затем мне просто нужна была функция, которая, учитывая массив значений RGBA с холста, выдавала бы узор на холсте:
function getColourPattern(rgbaData) {
let pattern = "";
for (let i = 0; i < rgbaData.length; i += 4) {
const r = rgbaData[i] / 255;
const g = rgbaData[i + 1] / 255;
const b = rgbaData[i + 2] / 255;
const w = (r + g + b) / 3;
if (w > 0.9) {
pattern += "w";
continue;
}
switch (Math.max(r, g, b)) {
case r:
pattern += "r";
break;
case g:
pattern += "g";
break;
case b:
pattern += "b";
break;
}
}
return pattern;
}
Это возвращает bbggbbggrrwwrrww
в Chrome и Safari и wwrrwwrrggbbggbb
в Firefox (с отключенным отпечатком холста)
Затем я использовал basenc --base64 wrgb-180.mp4 -w 0
, чтобы получить представление видео в формате base64, чтобы я мог встроить его в одну тестовую функцию:
export async function canvasUsesEXIF() {
const videoUrl = `data:video/mp4;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAAAvxtZGF0AAACrgYF//+q3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE1OSByMjk5OSAyOTY0OTRhIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAyMCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTEgcmVmPTMgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MzoweDExMyBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MSBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRzPTEgbG9va2FoZWFkX3RocmVhZHM9MSBzbGljZWRfdGhyZWFkcz0wIG5yPTAgZGVjaW1hdGU9MSBpbnRlcmxhY2VkPTAgYmx1cmF5X2NvbXBhdD0wIGNvbnN0cmFpbmVkX2ludHJhPTAgYmZyYW1lcz0zIGJfcHlyYW1pZD0yIGJfYWRhcHQ9MSBiX2JpYXM9MCBkaXJlY3Q9MSB3ZWlnaHRiPTEgb3Blbl9nb3A9MCB3ZWlnaHRwPTIga2V5aW50PTI1MCBrZXlpbnRfbWluPTI1IHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAAPmWIhAAt/9pbuD7Z/gvI3kF2QzYeJnVbANgW8XnGVlnoDJNW7zJawMem6POfQ3cvmVl9l7mrZDdjuR26xB2/AAADAm1vb3YAAABsbXZoZAAAAAAAAAAAAAAAAAAAA+gAAAAoAAEAAAEAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAIsdHJhawAAAFx0a2hkAAAAAwAAAAAAAAAAAAAAAQAAAAAAAAAoAAAAAAAAAAAAAAAAAAAAAP//AAAAAAAAAAAAAAAAAAD//wAAAAAAAAAAAAAAAAAAQAAAAAAEAAAABAAAAAAAJGVkdHMAAAAcZWxzdAAAAAAAAAABAAAAKAAAAAAAAQAAAAABpG1kaWEAAAAgbWRoZAAAAAAAAAAAAAAAAAAAMgAAAAIAVcQAAAAAAC1oZGxyAAAAAAAAAAB2aWRlAAAAAAAAAAAAAAAAVmlkZW9IYW5kbGVyAAAAAU9taW5mAAAAFHZtaGQAAAABAAAAAAAAAAAAAAAkZGluZgAAABxkcmVmAAAAAAAAAAEAAAAMdXJsIAAAAAEAAAEPc3RibAAAAKtzdHNkAAAAAAAAAAEAAACbYXZjMQAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAEAAQASAAAAEgAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABj//wAAADVhdmNDAWQACv/hABhnZAAKrNlfnnwEQAAAAwBAAAAMg8SJZYABAAZo6+PLIsD9+PgAAAAAEHBhc3AAAAABAAAAAQAAABhzdHRzAAAAAAAAAAEAAAABAAACAAAAABxzdHNjAAAAAAAAAAEAAAABAAAAAQAAAAEAAAAUc3RzegAAAAAAAAL0AAAAAQAAABRzdGNvAAAAAAAAAAEAAAAwAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1OC40NS4xMDA=`;
const video = document.createElement("video");
const canvas = document.createElement("canvas");
video.style.display = "none";
canvas.style.display = "none";
await new Promise((resolve, reject) => {
video.addEventListener("canplay", () => {
video.width = video.videoWidth;
video.height = video.videoHeight;
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
video.currentTime = 0;
});
video.addEventListener("seeked", () => resolve());
video.src = videoUrl;
});
const context = canvas.getContext("2d");
context.drawImage(video, 0, 0, video.videoWidth, video.videoHeight);
const { data } = context.getImageData(0, 0, 4, 4);
return getColourPattern(data) === "bbggbbggrrwwrrww";
}
Теперь, предполагая, что у вас есть метаданные поворота видео, вы сможете проверить, нужно ли вам вручную повернуть его на холсте 🤓
Изменить 1 :
Это должно избавить Firefox в Windows от выдачи ошибки NS_ERROR_NOT_AVAILABLE
.
9c9
< video.addEventListener("loadedmetadata", () => {
---
> video.addEventListener("canplay", () => {
Похожие вопросы
Новые вопросы
javascript
По вопросам программирования на ECMAScript (JavaScript/JS) и его различных диалектах/реализациях (кроме ActionScript). Имейте в виду, что JavaScript — это НЕ то же самое, что Java! Включите все ярлыки, относящиеся к вашему вопросу; например, [node.js], [jQuery], [JSON], [ReactJS], [angular], [ember.js], [vue.js], [typescript], [svelte] и т. д.
loadedmetadata
наcanplay
.