作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Krzysztof Ożóg
Verified Expert in Engineering
16 Years of Experience

Krzysztof是一名熟练的Symfony开发人员,对Symfony 2有很好的了解, Symfony 3, PHP, and OOP coding.

Share

如今,任何web应用的成功和普及都高度依赖于它的性能, flexibility, and ease of use.

Especially in today’s ADHD world, 如果页面加载时间过长,用户很快就会对应用失去耐心. 对于需要支持视频处理的web应用程序(这本身就是计算和I/ o密集型的),这一挑战尤为严峻. 尽管如此,用户的要求越来越高,他们希望自己的视频质量高 and load quickly, even if running on a smartphone or tablet.

用户也在逐渐失去对无法在他们喜欢的浏览器或设备上运行的网络应用的容忍度, 或者不支持加载或导出所需的数据格式. 因此,需要支持的视频格式的多样性也使得将视频支持整合到web应用程序中尤其具有挑战性.

这篇文章描述了我如何有效地利用开源技术和基于云的服务,将视频功能整合到一个基于php的web应用程序中.

使用Wowza和Amazon Elastic Transcoder在PHP中处理在线视频

Use Case

我所在的团队需要开发一个类似youtube的网站, where registered users could upload and share their videos.

该系统需要允许注册用户以各种支持的格式上传视频,然后将其转换为通用格式(MP4)。. 我们还需要生成一组缩略图和图像拼贴,用于在视频播放器中显示视频进度条上的帧.

由于客户端要求阻止我们使用任何可用的CDN或转码api,事情变得更加复杂, so we needed to develop our solution from scratch.

Video Upload

因为上传过程本身并不需要特定于视频(我们只需要一个易于使用的文件上传功能)。, 使用现有的开源解决方案比开发我们自己的解决方案更有意义. We selected jQuery-File-Upload, primarily because it supported two features that were essential in our case; namely, an upload progress bar and chunked uploads.

Chunked uploading 使我们能够允许用户上传几乎任何大小的视频文件(特别是支持高清分辨率的视频文件). With this approach, 该文件在前端被分成多个“块”,对每个数据块(以及每个块的元数据)调用上传操作, such as chunk number and total file size). The complete video file is then reassembled on the back-end. Incidentally, 在元数据中包含块号被证明是特别重要的,因为一些浏览器(比如Mobile Safari)倾向于以随机顺序传输块.

Online Video Processing

视频处理可以像捕捉静止图像一样简单, or can involve more complex operations such as image enhancement, stabilizing the video stream, and so on. In our case, 唯一的视频处理要求是:(a)提取视频编解码器和其他关键元数据;(b)生成一组缩略图和图像拼贴图(用于在视频播放器中显示视频进度条上的帧).

FFmpeg – a widely-used, freely-distributed, 开源库——在满足这些需求方面非常有帮助. FFmpeg provides a complete, cross-platform solution for recording, converting, and streaming audio and video files. It can also be used to convert videos and do simple editing (e.g., trimming, cutting, adding a watermark, etc.).

For our purposes, we were able to use FFmpeg to split the video into ten sections, 然后为每个部分捕获缩略图,以提供所需的功能.

但是,不幸的是,没有针对FFmpeg库的PHP语言绑定. As a result, 从PHP中利用FFmpeg的唯一方法是使用系统命令从命令行调用二进制文件. There are basically two ways to use FFmpeg in PHP:

  • libav. Libav is a free software project, forked from FFmpeg in 2011,生成处理多媒体数据的库和程序. On Ubuntu, for example, this can be installed with the command sudo apt-get install libav-tools. libav commands are compatible with FFmpeg and avconv. PHP needs to have command line access to ffmpeg/avconv to use this programmatically.
  • PHP-FFMpeg. PHP-FFMpeg是一个面向对象的PHP驱动程序,用于FFMpeg二进制文件. It can be accessed by simply executing composer update "php-ffmpeg/php-ffmpeg".

我们使用PHP-FFMpeg,因为它提供了对我们感兴趣的FFmpeg功能的简单访问. For example, the FFProbe 类使您能够接收有关编解码器或特定视频文件长度的信息,如下所示:

$ffprobe = FFMpeg\FFProbe::create();
$ffprobe
    ->format('/path/to/video/mp4') // extracts file informations
    ->get('duration'); 

FFmpeg also makes it easy to save any video frame:

$ffmpeg = FFMpeg\FFMpeg::create();
$video = $ffmpeg->open('video.mpg');
$video
    ->frame(FFMpeg\Coordinate\TimeCode::fromSeconds(10))
    ->save('frame.jpg');

More detailed sample code is available here.

One note of caution: Due to some patent laws, 不是所有的编解码器都可以用FFmpeg处理,有些格式不被正确(或完全)支持. 比如,我记得几年前,我和 .3gp format when support for feature phones was a must.

Queuing

After getting a video’s codecs and other metadata, we push the video to a FIFO (first in first out) conversion queue. 这个队列是使用一个简单的cron脚本实现的,每次运行时选择给定数量的未处理视频,并将它们传递给转换实用程序(示例源代码可用) here).

转换实用程序调用FFMpeg来执行转换,并将每个视频标记为已处理.

We also developed a simple wait time estimation mechanism, which calculates the average time to convert 1 minute of video. Using this average, 我们能够计算并显示用户在视频上传完成后的估计剩余处理时间, based on how many minutes of video remain to be processed.

Video Format Conversion

某些普遍认可的静态图像格式(如JPEG和GIF)已经出现,基本上所有设备和图像处理软件都支持这些格式. While some video formats are more common than others, no such universally supported format has yet emerged for videos.

In our case, 除了需要将各种格式转换为单一的通用格式(MPEG-4)之外,, 我们需要对转换后的视频进行优化,以便流式传输到移动设备.

对于视频格式转换(至少对于我们的短期需求),使用基于云的 Amazon Elastic Transcoder was the best option available. In addition to its general ease-of-use, 转码器服务负责优化和所有编码设置. Fortunately, an AWS SDK for PHP ,这简化了从PHP代码调用服务的过程.

Note: 如果你想快速启动和运行,使用Amazon Elastic Transcoder等基于云的服务是很好的选择. However, 请记住,这个选项对您的客户来说可能会很昂贵, 特别是如果他们的商业模式可能需要大量使用大型视频. 另一个需要考虑的因素是,你不应该假设你的客户的视频或商业模式将与服务条款兼容.

Amazon uses its basic storage and compute elements, S3 (Simple Storage Service) and EC2 (Elastic Compute Cloud) – combined with Auto Scaling and SNS (Simple Notification Service) -提供几乎即时扩展和缩减的能力.

Installation of aws-sdk is simple, since Amazon maintains a Composer-installable version of the package. Simply add ”aws/aws-sdk-php": "2.*" to your composer.json file and do a composer update.

Obviously, 访问Amazon Elastic Transcoder需要一个Amazon帐户, 因此,如果您(或您的客户)还没有这样的设置,您还需要设置.

我们使用Amazon Elastic Transcoder服务时,首先需要将视频文件上传到S3上合适的存储桶. 然后,我们让转码器工作负责解码并生成缩略图, on completion, posts an HTTP request to the specified address. This does require some configuration in the AWS panel, 但它非常简单,亚马逊提供了很好的文档来说明如何做到这一点.

Feel free to make use of our transcoder bundle, which helps simplify integration for Symfony 2. 它包括一个用法描述,并提供了一个控制器,用于快速实现由Amazon发送的通知服务,以收集有关已处理视频的信息. A usage example is available here.

此外,还提供了一个处理Amazon通知的示例控制器 here which also implements confirmation of a subscription address. 服务将首先发布要访问的URL,以确认这是有效的通知接收器. All then that’s really required is to mark the video as processed. 从那时起,我们就可以使用存储在云端的转码视频了.

Streaming

视频流是一种需要高性能的功能:用户对不间断流的期望很高,对延迟的容忍度极低. 由于需要同时实时地将视频流传输到多个客户端,这一挑战往往会加剧.

In our case, 我们需要支持每个用户能够创建他或她自己的视频频道并开始广播. Our solution consisted of three components:

  • Dashboard. 作为流媒体仪表盘的应用程序,提供提供视频服务的能力.
  • Viewer. Video client that consumes and displays a video stream.
  • Streaming Engine. Cloud-based video streaming service.

此外,视频点播(VOD)技术仍在不断发展, 我们面临的另一个问题是摄像头访问不太受支持,只能提供P2P连接. 此外,我们的目标是为多个并发用户提供在线广播. Furthermore, support for the getUserMedia/Stream API (formerly envisioned as the element) is not consistent yet across modern browsers. Based on these factors, 我决定使用Flash技术,因为这是唯一合理的选择. 因此,这两个应用程序(Dashboard和Viewer)都使用 Flex and ActionScript.

For the streaming engine, we used Wowza. Although there are other, non-commercial solutions (such as Red5, which is marketed as essentially a dropin replacement for Wowza), in our case commercial product support was an important factor. Also, at least at the time we were building the system, Wowza提供了更好的文档,这是一个额外的优势. (注意,你可以免费使用Wowza的试用版30天,还有开发者试用版,你可以使用最多180天. But there are some limitations; streaming can only work for two clients and there is limit on the maximum number of connections.)

Wowza Streaming Engine

We used the LiveStream application provided with Wowza. To configure it, leave applications/app_name empty and in conf/app_name copy the Application.xml file from the conf catalogue. Edit the file to configure the section as follows:


  live
  ${com.wowza.wms.context.VHostConfigHome}/content
  ${com.wowza.wms.context.VHostConfigHome}/keys
  
  

The key parameter is live 它定义了这将是来自实时视频馈送的流(例如.g., a camera). 注意,在编辑并保存这个文件之后,您需要重新启动Wowza.

Flash (Flex/ActionScript) Applications

Flash提供了一个完全集成的系统,可以将摄像头和麦克风连接到Wowza流媒体服务器. This is particularly helpful if your experience with ActionScript is limited.

整个应用程序本质上是基于以下对象之间的交互:

  • NetConnection. The NetConnection class creates a two-way connection between a client and a server. The client can be a Flash Player or AIR application. The server can be a web server, Flash Media Server, an application server running Flash Remoting, or the Adobe Stratus service.
  • Camera. The Camera 类用于从客户端系统或设备摄像机捕获视频.
  • Microphone. The Microphone class is used to monitor or capture audio from a microphone.
  • NetStream. The NetStream class opens a one-way streaming channel over a NetConnection.

First, 我们使用NetConnection实例连接到Wowza流服务器,然后附加事件监听器,监听网络连接状态的变化:

nc = new NetConnection();
nc.connect(serverAddress:string);
nc.addEventListener(
    NetStatusEvent.NET_STATUS, // event type
    eNetStatus,     // listener function
    false,          // use capture?
    0,              // priority
    true            // use weak reference?
);

下面是一个极简的事件监听器的例子,它将摄像头和麦克风连接到流媒体服务器:

private function eNetStatus(e:NetStatusEvent):void
{
    switch (e.info.code) {
        case "NetConnection.Connect.Success":
        camera = Camera.getCamera();
        mic = Microphone.getMicrophone();
        ns = new NetStream(nc);               
        ns.publish(streamName, "live");
        ns.attachCamera(camera);
        ns.attachAudio(mic); 
        break;

    case "NetConnection.Connect.Closed":
        // debug trace... display user message
        break;
}

客户端代码非常相似,除了我们只是在用户端显示视频输入. This is done by connecting the stream to Video object, as shown in this simple example:

if(event.info.code == "NetConnection.Connect.Success")
{
    ns = new NetStream(nc);
    ns.client = nsClient;
    ns.addEventListener(NetStatusEvent.NET_STATUS, nsClient.onNetStatus);

    ns.play(streamName);
    video = new Video();
    addChild(video); // this will display video 
    video.attachNetStream(ns); // connect NetStream to video
}

Wrap up

直播和视频将在移动和网络应用中扮演越来越重要的角色. It is therefore important for web developers 熟悉视频转码、处理和流媒体. Numerous tools, libraries, 目前存在将这些功能整合到web应用程序中的服务. 本文展示了我们如何利用和集成这些技术,从而相对轻松地成功创建一个基本的类似youtube的站点.

Hire a Toptal expert on this topic.
Hire Now
Krzysztof Ożóg

Krzysztof Ożóg

Verified Expert in Engineering
16 Years of Experience

Kraków, Poland

Member since November 4, 2014

About the author

Krzysztof是一名熟练的Symfony开发人员,对Symfony 2有很好的了解, Symfony 3, PHP, and OOP coding.

作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

World-class articles, delivered weekly.

By entering your email, you are agreeing to our privacy policy.

World-class articles, delivered weekly.

By entering your email, you are agreeing to our privacy policy.

Toptal Developers

Join the Toptal® community.