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(); } } }