来自用户输入的动态视频 - 它是如何工作的?

时间:2017-02-06 23:33:48

标签: image video html5-video video-processing

有没有办法按代码生成视频帧的内容?

例如:
我想制作一个程序,它将一些string变量作为输入,然后给出相同文本的输出视频,但现在对文本本身有特殊效果。

在看到Facebook在他们的网站上完成的一些项目后,我想到了这个想法。他们有一个与用户相关的图片,评论,朋友,活动,喜欢等数据库。利用所有这些信息,他们可以制作视频,例如朋友的日子,这是一个与想要发布的用户完全相关的视频。

这些是如何运作的?
我可以使用某种软件吗?有人可以给我一个开始的地方吗?

1 个答案:

答案 0 :(得分:3)

  

我想创建一个程序,它将一些字符串变量作为输入,然后给出相同文本的输出视频,但现在对文本本身有特殊效果。

您可以使用画布渲染文本,并使用新的Media Recorder API记录结果。

当然,效果可以通过各种方式进行,文本内容本身可以用于数据库,或者与机器学习结合使用以构建/获取与文本相关的一些数据。这范围很广,比这里适用的范围更广。

在任何情况下,视频本身的生成如下所示。这是一个简单的例子,您可以输入一些文本,它会被动画并录制为视频。当您单击停止时,将显示实际视频(您可以允许用户下载)。您可以使用其他效果,故事板功能进行扩展,以便您可以录制场景并将其开发以制作整部电影。

请注意,所有动画都会在画布上实时发生。典型的视频以每秒30帧而不是每秒60帧的速度记录,这是画布的典型帧速率。关于平滑度,这也是需要考虑的事项。但是,您可以通过定位30 FPS为您的代码提供更多时间来处理,从而发挥优势。 API假设通过不提供帧速率作为参数来支持步进记录,但我还没有能够完成这项工作。

目前对API的支持存在限制,因为并非所有浏览器都支持它。

实施例

// un-refined code for demo use only
// set up globals, get key elements
var div = document.querySelector("div");
var btn = document.querySelector("button");
var txt = document.querySelector("input");
var c = document.querySelector("canvas");

// make 2d context without alpha channel
var ctx = c.getContext("2d", {alpha: false});
var isRecording = false;

// stream vars
var rec, stream, tref;

// if clicked, lets go
btn.onclick = setup;

function setup() {
  
  // toggle button text/status for demo
  if (isRecording) {
    this.disabled = true;
    this.innerHTML = "Making video...";
    stop();
    return
  }
  else {
    isRecording = true;
    this.innerHTML = "Click to stop & show video";
  }
  
  // Setup canvas for text rendering
  var ct1 = document.createElement("canvas");
  var ct2 = document.createElement("canvas");
  var ctxText1 = ct1.getContext("2d");
  var ctxText2 = ct2.getContext("2d");
  var w, h;
  
  w = ct1.width = ct2.width = c.width;
  h = ct1.height = ct2.height = c.height;
  
  setupCtx(ctxText1, "#333", "#FF9F05");
  setupCtx(ctxText2, "#FF9F05", "#000");
  
  function setupCtx(ctxText, bg, fg) {
    ctxText.textAlign = "center";
    ctxText.textBaseline = "middle";
    ctxText.font = "92px sans-serif";
    ctxText.fillStyle = bg;
    ctxText.fillRect(0, 0, w, h);
    ctxText.fillStyle = fg;
    ctxText.translate(w/2, h/2);
    ctxText.rotate(-0.5);
    ctxText.fillText(txt.value.toUpperCase(), 0, 0);
  }
  
  // populate grid (see Square object below which handles the tiles)
  var cols = 18,rows = 11,
      cellWidth = (c.width / cols)|0,
      cellHeight = (c.height / rows)|0,
      grid = [],
      len = cols * rows, y = 0, x,
      index, hasActive = true;

  for (; y < rows; y++) {
    for (x = 0; x < cols; x++) {
      grid.push(new Square(ctx, x * cellWidth, y * cellHeight, cellWidth, cellHeight, ct1, ct2, 0.01));
    }
  }

  x = 0;

  // start recording canvas to video
  record();
  
  //animation loop (refactor at will)
  function loop() {
    
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.globalAlpha = 1;
    ctx.clearRect(0, 0, w, h);

    // trigger cells
    for (y = 0; y < rows; y++) {
        var gx = (x | 0) - y;
        if (gx >= 0 && gx < cols) {
            index = y * cols + gx;
            grid[index].trigger();
        }
    }

    x += 0.25;

    // update all
    for (var i = 0; i < grid.length; i++) grid[i].update();
    tref = requestAnimationFrame(loop);
  }

  // setup media recorder to record canvas @ 30 FPS (note: canvas = 60 FPS by def.)
  function record() {
    stream = c.captureStream(30);
    rec = new MediaRecorder(stream);
    rec.addEventListener('dataavailable', done);
    rec.start();
    requestAnimationFrame(loop);
  }
}

// stop recorder and trigger dataavailable event
function stop() {
  rec.stop();
  cancelAnimationFrame(tref);  // stop loop as well
}

// finish up, show new shiny video instead of canvas
function done(e) {
  var blob = new Blob([e.data], {"type": "video/webm"});
  var video = document.createElement("video");
 
  document.body.removeChild(c);
  document.body.appendChild(video);
  
  // play, Don
  video.autoplay = video.loop = video.controls = true;
  video.src = URL.createObjectURL(blob);
  video.onplay = function() {
    div.innerHTML = "Playing resulting video below:";
  };
}

// stolen from a previous example I made (CC3.0-attr btw as always)
// this is included for the sole purpose of some animation.
function Square(ctx, x, y, w, h, image, image2, speed) {
  this.ctx = ctx;
  this.x = x;
  this.y = y;
  this.height = h;
  this.width = w;
  this.image = image;
  this.image2 = image2;
  this.first = true;
  this.alpha = 0;          // current alpha for this instance
  this.speed = speed;      // increment for alpha per frame
  this.triggered = false;  // is running
  this.done = false;       // has finished
}

Square.prototype = {
    trigger: function () { // start this rectangle
        this.triggered = true
    },
    update: function () {
        if (this.triggered && !this.done) { // only if active
            this.alpha += this.speed; // update alpha
            if (this.alpha <= 0 || this.alpha >= 1) 
                this.speed = -this.speed;
        }
        var t = Math.sin(Math.PI * this.alpha);
        if (t <= 0) this.first = !this.first;

        this.ctx.fillStyle = this.color; // render this instance
        this.ctx.globalAlpha = Math.max(0, Math.min(1, t));

        var cx = this.x + this.width * 0.5, // center position
            cy = this.y + this.width * 0.5;

        this.ctx.setTransform(t*0.5+0.5, 0, 0, t*0.5+0.5, cx, cy); // scale and translate
        this.ctx.rotate(-Math.PI * (1 - t)); // rotate, 90° <- alpha
        this.ctx.translate(-cx, -cy); // translate back
        this.ctx.drawImage(this.first ? this.image : this.image2, this.x, this.y, this.width, this.height, this.x, this.y, this.width, this.height);
    }
};
body {background: #555;color:#ddd;font:16px sans-serif}
<div>
  <label>Enter text to animate: <input value="U R AWESOME"></label> 
  <button>Click to start animation + recording</button>
</div>
<canvas width=640 height=400></canvas>