2015-09-21 26 views
7

Tôi đang làm việc trên một dự án phía máy khách cho phép người dùng cung cấp tệp video và áp dụng các thao tác cơ bản cho nó. Tôi đang cố gắng trích xuất khung hình từ video một cách đáng tin cậy. Hiện nay tôi có một <video> mà tôi tải video được chọn vào, và sau đó kéo ra mỗi frame như sau:JavaScript: Trích xuất khung hình video đáng tin cậy

  1. Tìm kiếm tới đầu
  2. Tạm dừng video
  3. Vẽ <video> đến một <canvas>
  4. Chụp khung hình từ canvas với .toDataUrl()
  5. Tìm kiếm trước 1/30 giây (1 khung).
  6. Rửa và lặp lại

Đây là một quá trình chứ không phải kém hiệu quả, và đặc biệt hơn, được chứng minh không đáng tin cậy như tôi thường bị mắc kẹt khung. Điều này có vẻ là từ nó không cập nhật phần tử thực tế <video> trước khi nó vẽ lên canvas.

Tôi không muốn tải video gốc lên máy chủ chỉ để chia khung hình và sau đó tải chúng về máy khách.

Bất kỳ đề xuất nào cho cách tốt hơn để thực hiện việc này đều được đánh giá cao. Các chỉ báo trước là tôi cần nó để làm việc với bất kỳ định dạng hỗ trợ trình duyệt (giải mã trong JS không phải là một lựa chọn tuyệt vời).

+0

thay vì tìm kiếm 1/30 về phía trước, bạn nên đính kèm một hàm mục 'timeupdate' sự kiện của video. Nhưng rõ ràng, nếu bạn không cần phải làm điều đó trên phía khách hàng, không sử dụng một trình duyệt cho rằng, ffmpeg hoặc bất kỳ công cụ video sẽ mạnh mẽ hơn cho việc này. – Kaiido

+0

@Kalido Tôi sẽ xử lý nhiều hơn phía máy chủ, nhưng video đến từ máy khách và tôi cần các khung trên máy khách, vì vậy nếu tôi có thể tránh chu kỳ tải lên/tải xuống, tôi sẽ giảm giá nhiều hơn. Ngoài ra, theo như tìm kiếm, tôi đã gặp vấn đề với các thiết bị chậm hơn vì chúng không thể xử lý việc lấy dữ liệu khung đủ nhanh, tuy nhiên, tôi không quen với sự kiện 'timeupdate', vì vậy tôi sẽ xem xét nó . Ngoài ra, bằng cách xác định thời gian tìm kiếm cho mỗi khung hình, tôi có thể kiểm soát tốc độ khung hình nếu cần. –

+0

Ok, vì vậy, bạn có thể làm rõ điểm này (rằng nó dành cho trang web, phía máy khách) trong bản chỉnh sửa cho câu hỏi của bạn, điều đó không rõ ràng chút nào. Ngoài ra, tốc độ khung hình trong trình duyệt hoàn toàn không đổi, vì vậy bạn không nên dựa vào nó. Nhưng tại mỗi khung mới được vẽ, sự kiện thời gian sẽ kích hoạt, vì vậy tôi tin rằng nó sẽ đáng tin cậy hơn. – Kaiido

Trả lời

7

Mostly taken from this great answer bởi GameAlchemist:

Bởi vì trình duyệt không tôn trọng tốc độ khung hình video, nhưng thay vì 'sử dụng một số thủ thuật để làm cho một trận đấu giữa frame-rate của bộ phim và làm mới-rate của màn hình' , giả định của bạn rằng mỗi 30 giây, một khung hình mới sẽ được vẽ là khá không chính xác.
Tuy nhiên, timeupdate event sẽ kích hoạt khi currentTime đã thay đổi và chúng tôi có thể giả định rằng một khung mới đã được vẽ.

Vì vậy, tôi sẽ làm điều đó như sau:

document.querySelector('input').addEventListener('change', extractFrames, false); 
 

 
function extractFrames() { 
 
    var video = document.createElement('video'); 
 
    var array = []; 
 
    var canvas = document.createElement('canvas'); 
 
    var ctx = canvas.getContext('2d'); 
 
    var pro = document.querySelector('#progress'); 
 

 
    function initCanvas(e) { 
 
    canvas.width = this.videoWidth; 
 
    canvas.height = this.videoHeight; 
 
    } 
 

 
    function drawFrame(e) { 
 
    this.pause(); 
 
    ctx.drawImage(this, 0, 0); 
 
    /* 
 
    this will save as a Blob, less memory consumptive than toDataURL 
 
    a polyfill can be found at 
 
    https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toBlob#Polyfill 
 
    */ 
 
    canvas.toBlob(saveFrame, 'image/jpeg'); 
 
    pro.innerHTML = ((this.currentTime/this.duration) * 100).toFixed(2) + ' %'; 
 
    if (this.currentTime < this.duration) { 
 
     this.play(); 
 
    } 
 
    } 
 

 
    function saveFrame(blob) { 
 
    array.push(blob); 
 
    } 
 

 
    function revokeURL(e) { 
 
    URL.revokeObjectURL(this.src); 
 
    } 
 
    
 
    function onend(e) { 
 
    var img; 
 
    // do whatever with the frames 
 
    for (var i = 0; i < array.length; i++) { 
 
     img = new Image(); 
 
     img.onload = revokeURL; 
 
     img.src = URL.createObjectURL(array[i]); 
 
     document.body.appendChild(img); 
 
    } 
 
    // we don't need the video's objectURL anymore 
 
    URL.revokeObjectURL(this.src); 
 
    } 
 
    
 
    video.autoplay = true; 
 
    video.muted = true; 
 

 
    video.addEventListener('loadedmetadata', initCanvas, false); 
 
    video.addEventListener('timeupdate', drawFrame, false); 
 
    video.addEventListener('ended', onend, false); 
 

 
    video.src = URL.createObjectURL(this.files[0]); 
 
}
<input type="file" accept="video/*" /> 
 
<p id="progress"></p>

+1

Giả định của tôi không phải là sẽ có một khung hình mới sau mỗi 30 giây, nhưng nếu tôi nhận được 1 khung hình cứ sau 30 giây, tôi sẽ kết thúc với 30 FPS. Sai lầm tôi đưa ra là giả định rằng các khung hình tôi nhận được sẽ thực sự là khung chính xác và hiện tại. Tôi đánh giá cao sự giúp đỡ.Sự kiện timeupdate rất hữu ích. TY :) –

+0

Phương pháp này không chính xác đáng tin cậy: Nó một lần trả về 4172 khung hình, và sau đó 4573 trên lần chạy thứ hai, trên một video thực sự chỉ có 250 khung hình theo ffmpeg. 10 giây @ 25 khung hình/giây: http://www.w3schools.com/html/mov_bbb.mp4 –

+0

@DineshBolkensteyn, bạn có đọc tiêu đề của câu trả lời này và câu trả lời được liên kết không? Không có phương pháp này không trích xuất đáng tin cậy tất cả các khung hình video mà ae trong tập tin, chỉ là những cái đã được trình duyệt vẽ, mà không tôn trọng tốc độ khung hình của video. – Kaiido

Các vấn đề liên quan