HTML5 Canvas drawImage из видео, не отображаемого при первом рисовании

У меня есть элемент видео с наложением холста. Затем у меня есть настройка инструмента рисования для рисования поверх видео и кнопка сохранения, которая делает drawImage как из видео, так и из холста, чтобы сохранить скомпилированный кадр. Однако, когда я в первый раз нажимаю «Сохранить», я получаю результат только от холста drawImage, видео не показывает. При последующих сохранениях я получаю правильно наслоенные оба изображения. Я думал, что это может быть проблема с загрузкой видеоизображения, но видео полностью загружается до того, как я нажимаю «Сохранить», и могу даже перемещать кадры, и оно работает правильно при втором сохранении.

Вот код...

<div style="width:960px; height:540px; display:inline-block;">
    <video id="video" src="media/_tmp/AA_017_COMP_v37.mov" width="960" height="540" ></video>
</div>
<canvas id="canvas" width="960" height="540" style="position:absolute; top:40px; left:9px; z-index:100;"></canvas>
<input type="button" value="save" id="btn" size="30" onclick="save()" style="float:left; padding:4px; margin-right:4px;" >
<div id="saved" style="border:1px solid #000; position:absolute; top:626px; left:10px; bottom:40px; width:958px; overflow:auto;">SAVED:</div>


function save() {

    //COMP CANVAS OVER VIDEOFRAME
    var video = document.getElementById("video");
    var currentFrame = Math.floor((<?php echo $mov_frames ?> / video.duration) * video.currentTime);

    var compCanvas = document.createElement('canvas');
    compCanvas.width = video.width;
    compCanvas.height = video.height;
    compContext = compCanvas.getContext('2d');
    compContext.drawImage(video, 0, 0);
    compContext.drawImage(canvas, 0, 0);
    var dataURL = compCanvas.toDataURL();

    $("#saved").append('<div style="width:954px; border-bottom:1px solid #000; padding:2px 2px 0 2px;"><img id="compFrame_'+currentFrame+'" width="180" height="90" src="'+dataURL+'" />Frame: '+currentFrame+'</div>');
}

person Andrew Sinagra    schedule 14.11.2013    source источник
comment
Можете ли вы предоставить jsfiddle, показывающий проблему?   -  person Oriol    schedule 15.11.2013
comment
Вот ссылка jsFiddle не рисовать на холсте... но тем не менее там есть код.   -  person Andrew Sinagra    schedule 15.11.2013
comment
ваш код запускается только после загрузки DOM?   -  person GameAlchemist    schedule 15.11.2013
comment
Функция сохранения находится за пределами $(document).ready, но она вызывается только после полной загрузки страницы.   -  person Andrew Sinagra    schedule 15.11.2013
comment
1) Возможно, это ошибка по одному из-за какой-то проблемы с подкачкой буфера. Можете ли вы быть уверены, что «распечатанное» изображение — это именно то, что вы видели, а не предыдущее? А также идея, которая у вас уже была: 2) как насчет воспроизведения видео на холсте? таким образом, вы будете на 100% уверены в изображениях, которые у вас есть под рукой.   -  person GameAlchemist    schedule 15.11.2013
comment
Видео, которое я на самом деле тестирую, имеет прожиг номера кадра, поэтому я еще раз проверю, сохраняю ли я правильный кадр. Также я подумал о том, чтобы воспроизвести видео на том же холсте, а затем написать прямо на нем, но я потерял бы способность стирать любой рисунок. Я не пробовал запускать его через собственный холст, но я не уверен, почему это будет иначе. В худшем случае, пока я не выясню это, я могу взломать его, чтобы в первый раз, когда он пытался вызвать изображение, он фактически выполнял процесс дважды. Накладные расходы довольно низкие, поэтому, хотя я и ужасно неэлегантен, я не думаю, что пользователь заметит.   -  person Andrew Sinagra    schedule 15.11.2013
comment
Если вы рисуете на другом холсте, вам не нужно удивляться тому, что изображение отображается по сравнению с изображением в буфере, И вы все равно можете сделать очистку на самом верхнем холсте без вопросов. // Для решения "прочитать сначала дважды": да... если оно работает для всех (поддерживаемых) видеоформатов/браузеров/платформ. Я боюсь, что определенный ответ здесь лежит в тестировании....   -  person GameAlchemist    schedule 15.11.2013
comment
Таким образом, решение «прочитать сначала дважды» на самом деле дало те же результаты! Теперь я полностью сбит с толку, почему он не будет читать видео. Похоже, пришло время изучить идею двойного холста.   -  person Andrew Sinagra    schedule 15.11.2013
comment
Кроме того, можете ли вы увидеть, создает ли эта скрипта кадры, как ожидалось (кажется, это работает для меня): jsfiddle .net/AbdiasSoftware/zL8KC/1   -  person    schedule 15.11.2013
comment
Привет Кен .. спасибо за вопрос. Я попробовал другой формат и получил те же результаты. Я попробую другие браузеры и посмотрю, изменится ли что-нибудь. В настоящее время я тестирую Safari на OSX Mavericks. Я попробовал ваш jsFiddle и использовал snaps.appendChild(compCanvas); вместо toDataURL, и это был тот же результат.   -  person Andrew Sinagra    schedule 15.11.2013


Ответы (3)


Поскольку это на OSX с Safari, есть и плохие новости:

Примечание. Видео в качестве источника для метода canvas drawImage() в настоящее время не поддерживается в iOS.

Источник

Если это устаревшая информация, я не знаю, но в любом случае у текущей реализации есть проблемы. Как установлено (в комментариях), проблема вряд ли связана с кодеками, так как это происходит и с другими форматами. fiddle исключает метод toDataURL() в качестве источника, поэтому остается метод drawImage().

Поскольку это работает на другой платформе с предоставленным кодом, и, судя по всему, это не является источником проблемы, поэтому вы здесь рассматриваете очень возможную проблему (ошибку) с браузером Safari на платформе OSX.

В официальной документации, приведенной выше, также говорится, что это не поддерживается, поэтому, вероятно, это очень ранняя реализация, в которой все еще есть некоторые проблемы.

В этом отношении вы мало что можете сделать, кроме как подождать, а также сообщить об этом как о проблеме (я искал сообщение о проблеме, но не смог найти его, поэтому я рекомендую вам сделать это).

person Community    schedule 15.11.2013
comment
Я считаю, что вы правы. Попытка этого в firefox на OSX, а также в chrome и firefox на Win7, кажется, дает правильное поведение. - person Andrew Sinagra; 16.11.2013
comment
На самом деле, вернувшись и прочитав предоставленную вами ссылку на источник, я понял, что iOS конкретно не поддерживается. Mavericks на настольной системе — это не iOS. Однако похоже, что это напрямую связано с Safari, поэтому это все еще может быть ошибкой. - person Andrew Sinagra; 17.11.2013

Недавно я сам столкнулся с той же проблемой и обошел ее, нарисовав видео на «фиктивном» холсте, прежде чем приступить к «настоящей» работе с ним. Это хорошо работает, так как все методы в моем коде, которые пытаются проанализировать видео, ждут, пока на видео не будет запущено событие «canplaythrough». Я оборачиваю обработчик события «canplaythrough» в обещание и разрешаю обещание только тогда, когда получено событие «canplaythrough» И была предпринята попытка нарисовать видео на фиктивном холсте. Это эффективный обходной путь. для ошибки Safari.

Например:

var readyPromise = new Promise(function(resolve) {
    video.addEventListener("canplaythrough", function() {
        var canvas = document.createElement("canvas"),
            context = canvas.getContext("2d");

        context.drawImage(video, 0, 0);

        resolve(video);
    });
});

readyPromise.then(function() {
    // NOW manipulate the video, draw it onto a canvas, etc
});
person Ray Nicholus    schedule 29.05.2014

Я столкнулся с той же проблемой и опробовал решение в последнем комментарии. Это еще не сработало для меня.

Я понял позже и нашел обходной путь:

в моем коде у меня есть другой рисунок вместе с drawImage из видео, например

context.rect(0, 0, 50, 50);
context.fillStyle = 'black';
context.fill();

и с ними второй розыгрыш по-прежнему не будет работать, даже если его перерисовать после события «canplaythrough».

Поэтому я удалил три строки выше и просто сделал второй розыгрыш после события «canplaythrough». Тогда это сработало.

Просто код, который сработал у меня, выглядит так:

context.drawImage(video, 0, 0);
video.addEventListener('canplaythrough', function () {
    context.drawImage(video, 0, 0);
});
person paige    schedule 11.02.2015