实战Hadoop
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.4 实战:用HDFS存储海量视频数据

2.4.1 应用场景

随着时代的发展,高清视频的应用日益广泛。与此同时,高清视频监控项目规模也在不断扩大,因此,高清视频的存储越来越受到人们的关注。对于视频监控而言,图像清晰度无疑是最关键的特性。图像越清晰,细节便越明显,观看体验越好,各种智能应用业务的准确度也越高。然而,高清的视频数据以G为级别大小也是不可小觑的。与此同时,面对如潮水般涌现的海量视频数据,不仅对存储容量有很高的要求,对读写性能、可靠性等都提出了很高要求。因此,选择什么样的存储系统,往往成为影响视频读写速度的关键。

Hadoop的HDFS具有扩展性强、可靠性高、成本低等优势,为高清视频的存储提供了很大的便利。其中,HDFS“一次写入多次读取”的文件访问模型简化了视频流数据一致性问题,并且保证了高吞吐量的数据访问。另外,HDFS的多机架存放副本的策略使用户不用担心因为某个DataNode的故障而导致的视频文件不完整,确保高清视频实时可用。

2.4.2 设计实现

1.模拟视频流

在缺少摄像头的情况下可以使用VLC播放器模拟出H264的实时视频流:

(1)搭建组播服务器。

第一步:运行VLC程序后选择“媒体→串流”。

第二步:通过“添加”选择需要播放的文件(以wmv文件为例),单击“串流”。

第三步:流输出有三项需要设置,包括来源、目标和选项。来源刚才已指定,单击“下一个”。

第四步:勾选“在本地显示”,选择“RTP/MPEG Transport Stream”输出,单击“添加”。

第五步:如果建立IPv6组播服务器,可以输入组播地址ff15::1,指定端口号“5004”,单击右下角的“下一个”。如果需要建立IPv4组播服务器,则地址栏可输入“239.1.1.1”(239.0.0.0/8为本地管理组播地址)。

第六步:将TTL设置为10,单击左下角“串流”即可发送组播视频,同时在本地播放(视频打开时间较慢,需要等待半分钟左右)。

第七步:使用WireShark抓包查看。

(2)搭建组播客户端。

第一步:运行程序后选择“媒体→打开网络串流”;

第二步:输入URL(rtp://@[ff15::1]:5004),单击“播放”就可以观看组播视频,如果为IPv4组播环境,可输入rtp://239.1.1.1:5004。

注意,测试前请关闭PC防火墙,以免影响组播报文的发送和接收。

2.接收视频流

由于本实战部分的重点为海量视频数据存储,因此不再展示接收视频流的代码,读者可从http://bbs.chinacloud.cn/下载。本处采用的接收方法为:将从摄像头接收到的或通过模拟产生的视频流,以文件的形式存储在本地文件夹内。此外,这个过程不产生任何中间文件。

3.海量视频数据存储

存储海量视频数据的思路为:通过Hadoop提供的API接口,实现将接收到的视频流文件从本地上传到HDFS中。

在此过程中,接收到的视频文件将源源不断地存储到一个指定的本机文件夹中,因此,这个本地文件夹的文件是在动态增加的,此处将这个动态变化的文件夹当做一个“缓冲区”,然后以流的形式将“缓冲区”文件和HDFS进行对接,之后通过调用FSDataOutputStream.write(buffer,0,bytesRead)实现以流的方式将本地文件上传至HDFS上。当本地文件上传成功后,再调用File.delete()批量删除“缓冲区”中已上传文件。此过程将一直延续,直到所有文件都上传至HDFS且清空本地文件夹后才结束。

将接收到的视频流文件上传到HDFS完整代码如下:

    import java.io.File;
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.fs.FSDataInputStream;
    import org.apache.hadoop.fs.FSDataOutputStream;
    import org.apache.hadoop.fs.FileStatus;
    import org.apache.hadoop.fs.FileSystem;
    import org.apache.hadoop.fs.Path;
    public class Read2 {
    public static void main(String [] args)throws Exception{
          Configuration conf=new Configuration();
          FileSystem hdfs=FileSystem.get(conf);
          FileSystem local=FileSystem.getLocal(conf);
      //确定需要上传视频流路径和接受视频流路径
          Path inputDir=new Path("C: \\msys\\1.0\\home\\admin\\ffmpeg\\live.264");
          Path hdfsFile=new Path("/acceptFile/");
          //System.out.println(inputDir.toString());
      //创建HDFS 上“acceptFile”目录,用以接收视频文件
          hdfs.mkdirs(hdfsFile);
          FileStatus[] inputFiles=local.listStatus(inputDir);
          FSDataOutputStream out;
      //通过OutputStream.write( )来将视频文件循环写入HDFS 下的指定目录
          for(int i=0;i<inputFiles.length;i++){
             System.out.println(inputFiles[i].getPath().getName());
             FSDataInputStream in=
              local.open(inputFiles[i].getPath());
          out=hdfs.create(new Path("/acceptFile /"+inputFiles[i].getPath().getName()));
          byte buffer[]=new byte[256];
          int bytesRead=0;
          while((bytesRead=in.read(buffer))>0){
              out.write(buffer,0,bytesRead);
          }
          out.close();
          in.close();
          File file = new File(inputFiles[i].getPath().toString());
          file.delete();
        }
    }
}