關於視頻播放、視頻切片、跨域訪問視頻

關於視頻播放、視頻切片、跨域訪問視頻

前言

  最近在着手部署上線作的一個視頻網站,當咱們部署到雲服務器上後並開始測試視頻觀看併發量,發現了一個很嚴重的問題:帶寬不足。9 或 10 我的同時觀看視頻的時候,就會出現有些用戶加載不了視頻的問題。 
  咱們的雲服務器是 1塊錢的騰訊雲,自己帶寬就很低了,因此確定沒有足夠大的帶寬來支撐這麼大的流量傳輸。這對於一個視頻網站來講,帶寬這個問題是很是致命的。在不能改變帶寬的條件下,因而咱們去找了一些方法來提升性能。html

分析不足

  咱們的視頻網站使用的內置 Flash 播放器來播放視頻,用戶觀看視頻的時候是直接徹底加載整個視頻的(無論你看了多久),從一開始播放就開始加載,而且並不會由於用戶暫停而暫停加載, 它是一直持續加載直到加載徹底的。對於絕大多數用戶來講,他們不必定會把視頻看完,若是是加載一個小視頻,那尚未什麼大問題,但若是是加載一個大視頻的話,這就會浪費的大量的流量,而且加載過程會持續佔用帶寬,使得用戶量多的時候,視頻加載就會出現問題。java

修改第一步

  瞭解到這個問題以後,咱們去看了別人的視頻網站是如何撐起高用戶量的,在視頻播放的時候,咱們發現它們並非一開始就徹底加載視頻的,而是一段段的加載,去搜索以後發現這是一種切片的技術,用於控制流量傳輸。具體的切片的原理可參看 http://www.cnblogs.com/flash3d/archive/2013/11/02/3403109.html。 
  瞭解了切片技術以後,咱們因而就開始在咱們項目中應用切片的技術,咱們使用的是 ffmpeg 來對視頻進行切片。方法就是在程序中調用 ffmpeg 程序,而後調用切片命令對咱們的視頻進行切片,生成 m3u8 文件和 ts 文件,而後使用 flash 播放器播放,可以看到的確可以一段段的加載視頻。 
  修改到這一步,這彷佛解決了咱們的問題,可是新的問題又出現了,咱們發現當咱們對大視頻進行切片的時候,服務器的內存會佔用很大,至於爲何會佔用那個大,咱們猜測多是由於對視頻切片時,ffmpeg 把整個視頻加載到內存,因此致使內存佔用高。當同時對多個視頻進行切片的時候,服務器就炸掉了。因而咱們又尋求新的方法去解決。 
 跨域

修改第二步

  由於沒想到好的方法去解決本地切片內存佔用問題,因而咱們使用了新的途徑去存儲播放視頻,就是使用雲端存儲來存儲視頻,咱們選用的是七牛雲服務器來存儲。它也提供了不一樣語音的 SDK 供開發者參考。 
  使用七牛雲,我在個人 Java Web 項目裏面導進必須的 4 個包,以及編寫了上傳視頻並進行切片預處理的工具類。剛開始使用的時候也遇到許多問題。瀏覽器

問題1:上傳不一樣格式的視頻,有些播放不了

  一開始,咱們對任何格式的視頻都調用同一個切片命令,覺得會生成同一種格式的視頻文件。可是當咱們上傳的 MP4 格式的視頻,切片上傳後,直接在瀏覽器輸入外鏈(文件的訪問連接),此時可以正常播放;可是上傳 avi 或者 flv 格式的視頻,上傳切片後,直接輸入外鏈會變成下載文件。 
  當時一直想不通這個問題的緣由,由於明明都是調用了同一個命令切片,按理來講應該格式是同樣的,可是卻出現不一樣的行爲。後來經過七牛雲的問答平臺尋求解決方案,才發現若是在上傳的時候,沒有使用 saveas 參數對結果另存爲 xxx.m3u8 格式,他仍是任然會以原有的格式去保存。因此基於瀏覽器對不一樣視頻格式的支持,對 mp四、avi、flv等格式的視頻則出現不一樣的效果服務器

問題2: 使用外鏈播放出現跨域拒絕的問題

  在咱們解決完視頻上傳的問題以後,在播放器經過外鏈來播放視頻的時候,發現出現跨域被拒絕的問題 (ERROR:HLSError(code/url/msg)=1tp:Cannot load M3U8: crossdomain access denied:Error #2048),google 了問題,發現原來Flash 播放器在加載跨域視頻時,會先去加載雲端的 corssdomain.xml 文件,而後判斷是否被容許加載。 
   
解決方法:須要在七牛雲端上傳 crossdomian.xml 文件併發

<cross-domain-policy>
<allow-access-from domain="*"/>
<allow-http-request-headers-from domain="*" headers="*"/>
</cross-domain-policy>

Java 七牛雲上傳視頻的工具類

基於七牛雲的 API,寫了一個上傳視頻並切片的接口,可供參考;若有錯 ,可一塊兒討論 
該接口的配置信息採用配置文件 video.properties 來加載,具體的配置文件配置內容爲:cors

access_key=your access key
secert_key=your secert key
bucketname=你的存儲空間
pipeline=你的多媒體處理隊列名
fops=avthumb/m3u8/noDomain/1/vb/500k/t/120(這是切片命令)如果要作其餘處理,請參照七牛雲 SDK
domain=你的七牛雲映射域名
public class QiNiuUtil {
private static String DEFAULT_PROPERTIES = "video.properties";
private static Properties properties = new Properties();
static {
String path = QiNiuUtil.class.getResource("/").toString();
path = path.substring(6, path.length() - 8) + DEFAULT_PROPERTIES;
System.out.println(path);
try {
FileInputStream fileInputStream = new FileInputStream(path);
properties.load(fileInputStream);
System.out.println(properties.toString());
} catch (IOException e) {
System.out.println("配置文件不存在,加載配置文件失敗");
}

}

public static String domian = properties.getProperty("domain");

private static String ACCESS_KEY = properties.getProperty("access_key");
private static String SECRET_KEY = properties.getProperty("secert_key");

// 要上傳的空間
private static String bucketname = properties.getProperty("bucketname");

// 設置切片操做參數
private static String fops =properties.getProperty("fops");

// 設置轉碼的隊列
private static String pipeline = properties.getProperty("pipeline");




//密鑰配置
private static Auth auth = Auth.create(ACCESS_KEY, SECRET_KEY);
//建立上傳對象
private static UploadManager uploadManager = new UploadManager();

//上傳策略中設置persistentOps字段和persistentPipeline字段
public static String getUpToken(String pfops){
return auth.uploadToken(bucketname,null,3600,new StringMap()
.putNotEmpty("persistentOps", pfops)
.putNotEmpty("persistentPipeline", pipeline), true);
}

public static boolean upload(byte[] data, String key) throws IOException{

Response res = null;
try {
// 調用put方法上傳
// 指定文件以 m3u8 格式另存
String urlbase64 = UrlSafeBase64.encodeToString(bucketname + ":" + key + ".m3u8");
res = uploadManager.put(data, key, getUpToken(fops + "|saveas/"+ urlbase64));


//打印返回的信息
System.out.println(res.bodyString());


} catch (QiniuException e) {
Response r = e.response;
// 請求失敗時打印的異常的信息
System.out.println(r.toString());

try {
//響應的文本信息
System.out.println(r.bodyString());
} catch (QiniuException e1) {
//ignore
}
}
return res.isOK(); }}
相關文章
相關標籤/搜索