打造m3u8視頻(流視頻)下載解密合併器(kotlin)

来源:https://www.cnblogs.com/stars-one/archive/2020/01/15/12198572.html
-Advertisement-
Play Games

本文是對我原創工具 "m3u8視頻下載合併器" 關鍵代碼解析及軟體實現的思路的講解,想要工具的請跳轉鏈接 1.思路說明 思路挺簡單,具體步驟如下: 1. 下載m3u8文件 2. 解析m3u8文件獲得ts文件列表 3. 根據文件列表批量下載ts文件 4. 進行ts的解密操作(如果沒有加密則跳過此步驟) ...


本文是對我原創工具m3u8視頻下載合併器關鍵代碼解析及軟體實現的思路的講解,想要工具的請跳轉鏈接

1.思路說明

思路挺簡單,具體步驟如下:

  1. 下載m3u8文件
  2. 解析m3u8文件獲得ts文件列表
  3. 根據文件列表批量下載ts文件
  4. 進行ts的解密操作(如果沒有加密則跳過此步驟)
  5. 將解密後的文件或未加密的ts文件按照m3u8中的列表順序進行合併,得到mp4文件

可以把Kotlin看作為Java語言的增強版,Java中的知識Kotlin也是通用的

本文涉及到知識如下

  • String字元串的處理
  • IO流,讀文件進行讀寫
  • 網路編程
  • AES解密(其實我也不是很懂)

2.m3u8格式介紹

可能這個格式大家不是很瞭解,其實現在大家看的大多數線上視頻,都是使用了m3u8文件來實現線上視頻播放的。

M3U8 是 Unicode 版本的 M3U,用 UTF-8 編碼。"M3U" 和 "M3U8" 文件都是蘋果公司使用的 HTTP Live Streaming(HLS) 協議格式的基礎,這種協議格式可以在 iPhone 和 Macbook 等設備播放。

簡單地來說,m3u8就是一個播放列表,裡面保存這多個短視頻的地址,之後伺服器從此文件中按照順序依次下載ts文件併進行播放。

ts文件也可以看做為mp4文件,可以直接拿QQ影音等軟體打開,但這隻限於未加密的ts文件

可能有些小伙伴會發現, 有些ts文件直接打開軟體會提示不支持解析此文件,這其實就是因為ts文件已經被加密了。

我們可以以文本的方式打開m3u8的文件,內容如下:

#EXTM3U
#EXT-X-TARGETDURATION:10

#EXTINF:9.009,
http://media.example.com/first.ts
#EXTINF:9.009,
http://media.example.com/second.ts
#EXTINF:3.003,
http://media.example.com/third.ts
...

上面的是未加密的m3u8文件內容,我們來看看加密的m3u8文件:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0

#EXT-X-KEY:METHOD=AES-128,URI="key.key"
#EXTINF:10.000000,
00000.ts
#EXTINF:10.000000,
00001.ts
#EXTINF:10.000000,
00002.ts
#EXTINF:10.000000
...

PS:想要瞭解m3u8格式更多的資料,請查看我底下的參考鏈接

這裡提一下獲取m3u8文件的方式,可以通過瀏覽器F12進入調試模式,之後找到m3u8的網址資源,或者是通過貓抓(Chrome插件)獲取鏈接,貓抓插件安裝請自行百度

3.解析m3u8文件獲取信息

由上面我們大概瞭解到了m3u8文件裡面的內容,我們將m3u8文件下載到本地之後,可以得到兩個信息,key文件地址(如果採用了加密的話)和全部的ts文件地址

#EXTM3U
#EXT-X-TARGETDURATION:10

#EXTINF:9.009,
http://media.example.com/first.ts
#EXTINF:9.009,
http://media.example.com/second.ts
#EXTINF:3.003,
http://media.example.com/third.ts
...

上面的這個是沒有採用加密的,而且,ts文件都是給出了具體的網址,這是極為理想的情況,但是市面上大部分不會採用這樣的,一般都是像下麵的這種格式:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:10
#EXT-X-MEDIA-SEQUENCE:0

#EXT-X-KEY:METHOD=AES-128,URI="key.key",IV=0x12345(可能有)
#EXTINF:10.000000,
00000.ts
#EXTINF:10.000000,
00001.ts
#EXTINF:10.000000,
00002.ts
#EXTINF:10.000000
...

上面的m3u8文件採用了加密,而且ts文件都是只有編號,沒有網址,而且key文件也是非常的簡短,根本就不是一個地址,這種情況我們就得進行字元串的拼接處理。

一般的網站,會將m3u8文件、key文件(有加密的話)、ts文件都是放在同一路徑

比如說現在有個m3u8的地址為www.xxx.com/2020/1/14/m3u8.m3u8,使用了加密,所以它的key文件為www.xxx.com/2020/1/14/key.key,ts文件為www.xxx.com/2020/1/14/0000.ts

上面只是個簡單的例子,具體的網站還得具體分析,可以使用抓包進行分析。

現在來對上面的m3u8文件進行簡單地分析吧:

採用了AES-128進行了加密,key的地址為key.key,偏移量IV為12345,有些是沒有使用偏移量,則可以使用0來代替

我們通過解析m3u8文件,首先是獲得key文件和所有ts文件的地址,然後進行下載即可

通用的下載代碼(下載m3u8文件、key文件、ts文件):

/**
 * 下載文件到本地
 * @param url 網址
 * @param file 文件
 */
private fun downloadFile(url: String, file: File) {
    val conn = URL(url).openConnection()
    conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)")
    val bytes = conn.getInputStream().readBytes()
    file.writeBytes(bytes)
}

4.ts文件下載優化

ts文件過多,如果只開啟一個單線程進行下載,下載太慢了,所以,可以採用多線程進行下載

這裡的話,由於之前解析可以獲得一個ts文件地址的列表,把此列表分為幾份列表,每份列表開啟一個子線程來進行下載,這樣便可以保證任務的併發性,提高了下載速度。

這裡,稍微有點複雜,因為要把列表劃分成幾份列表,我大概是這樣分的:

首先,計算出列表可以平均分為幾份,每份列表的數目,之後再將剩下的列表分為一份,但是,使用迴圈的話不是很好寫,所以就先把第一個列表和最後一個列表分好,之後來個迴圈,將中間的平分完。

/**
 * 下載ts文件
 * @param threadCount 線程數(預設開啟5個線程下載,速度較快,100M寬頻測試速度有17M/s)
 */
fun downloadTsFile(threadCount: Int = 5) {
    val countDownLatch = CountDownLatch(threadCount)
    //每份列表的數目
    val step = tsUrls.size / threadCount
    //最後列表的數目(剩下的)
    val yu = tsUrls.size % threadCount
    //第一份列表
    thread {
        val firstList = tsUrls.take(step)
        downloadTsList(firstList)
        countDownLatch.countDown()
    }
    //最後一份列表
    thread {
        val lastList = tsUrls.takeLast(step + yu)
        downloadTsList(lastList)
        countDownLatch.countDown()
    }
    //中間的平分
    for (i in 1..threadCount - 2) {
        val list = tsUrls.subList(i * step, (i + 1) * step + 1)
        thread {
            downloadTsList(list)
            countDownLatch.countDown()
        }
    }
    countDownLatch.await()
    println("所有ts文件下載完畢")
}

上面的使用了CountDownLatch類的對象進行線程的控制,只有當所有線程完成之後,此方法才算結束

5.ts文件解密

先上代碼,之後再細講:

//1.獲得key和iv的字元串
val keyString = "2e9515db8fe8358bc8fcf6ae601a00be"
val ivString = "d0817f83115d911241fe8ba17673f120"

//2.獲得key和iv的bytes數組
val keyBytes = decodeHex(keyString)
val ivBytes = decodeHex(ivString)

//3.key數組轉為SecretKeySpec對象,iv數組轉為IvParameterSpec
val algorithm = "AES"
val skey = SecretKeySpec(keyBytes, algorithm)
val iv = IvParameterSpec(ivBytes)

//4. 初始化cipher
val transformation = "AES/CBC/PKCS5Padding"
val cipher = Cipher.getInstance(transformation)
cipher.init(Cipher.DECRYPT_MODE,skey,iv)

//5. 解密,
val tsFile = File("Q:\\m3u8破解\\2273\\440.ts")
val result = cipher.doFinal(tsFile.readBytes())
val newFile = File("Q:\\m3u8破解\\2273\\440_s.ts")

//6.寫入文件
BufferedOutputStream(FileOutputStream(newFile)).write(result)

key文件本質是一個16位元組文件,我們可以通過winhex等軟體查看裡面的內容

不過,查看出來之後的內容,我們還得進行轉換,因為是字元串,所以得調用decodeHex方法,將字元串轉為bytes數組

所以,直接使用代碼查看更為方便,Kotlin中可以直接讀取bytes(如果使用Java的話,推薦使用common-io的第三方jar包),如:

val keyFile = File("Q:\\test\key.key")
//獲得bytes數組
val bytes = keyFile.readBytes()

PS:對了,如果m3u8文件中沒有使用到IV偏移量,直接使用0即可(要保證bytes數組的長度為16),如果使用了IV的話,要使用decodeHex方法轉為bytes數組

val ivBytes = if (ivString.isBlank()) byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) else decodeHex(ivString)
 /**
 * 將字元串轉為16進位並返回位元組數組
 */
private fun decodeHex(input: String): ByteArray {
    val data = input.toCharArray()
    val len = data.size
    if (len and 0x01 != 0) {
        try {
            throw Exception("Odd number of characters.")
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }
    val out = ByteArray(len shr 1)

    try {
        var i = 0
        var j = 0
        while (j < len) {
            var f = toDigit(data[j], j) shl 4
            j++
            f = f or toDigit(data[j], j)
            j++
            out[i] = (f and 0xFF).toByte()
            i++
        }
    } catch (e: Exception) {
        e.printStackTrace()
    }

    return out
}

@Throws(Exception::class)
private fun toDigit(ch: Char, index: Int): Int {
    val digit = Character.digit(ch, 16)
    if (digit == -1) {
        throw Exception("Illegal hexadecimal character $ch at index $index")
    }
    return digit
}

有了key文件和IV偏移量的bytes,我們就可以往下走了,下麵的代碼其實都沒有什麼好說明的,明眼人估計一看就懂了,這裡就不多說了

需要註意的是,因為解密之後,我們還需要把所有已經解密好的ts文件按照順序合併成一個mp4文件,所以,註意解密後數據的名字。

建議在保存原來編號的基礎上,加上寫簡短的字母,之後,就可以通過contains方法進行判斷是否文件名是否符合條件

6.ts文件合併

合併的話,使用IO流,按照順序依次把流追加到末尾即可

參考

m3u8 文件格式詳解
關於m3u8格式的視頻文件ts轉mp4下載和key加密問題
aes 256 32位key和32位iv
加密ts解密


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 第一種,使用reversed 函數,reversed返回的結果是一個反轉的迭代器,我們需要對其進行 list 轉換 第二種,使用sorted函數,sorted是排序函數,它是對一個列表進行排序後生成一個新的list列表,而sort則是在原來的列表上直接進行排序。 其中,reverse是排序規則,Tr ...
  • 一、print()函數概述 print() 方法用於列印輸出,是python中最常見的一個函數。 該函數的語法如下: 參數的具體含義如下: objects 表示輸出的對象。輸出多個對象時,需要用 , (逗號)分隔。 sep 用來間隔多個對象。 end 用來設定以什麼結尾。預設值是換行符 \n,我們可 ...
  • typing模塊的作用 自python3.5開始,PEP484為python引入了類型註解(type hints) 類型檢查,防止運行時出現參數和返回值類型、變數類型不符合。 作為開發文檔附加說明,方便使用者調用時傳入和返回參數類型。 該模塊加入後並不會影響程式的運行,不會報正式的錯誤,只有提醒py ...
  • 2020年1月15號慕課網,騰訊課堂,龍果學院,更新資料整理 -買家看 微雲:鏈接:https://share.weiyun.com/5x8hmp6 密碼:202015 百度網盤:鏈接:https://pan.baidu.com/s/1voabjevl1BhoL7ul7yDskg 提取碼:a2z4 ...
  • java學習經驗總結 單鏈表的建立與結點的增刪 在該鏈表結點有data數據,並且還有cpu,分給cpu隨機的時間片,根據時間片大小進行結點data的排序 鏈表結點的建立 鏈表的構建過程以及添加節點、刪除節點 ...
  • 一、什麼是GPIO GPIO全稱為General Purpose Input Output,中文理解為通用輸入輸出埠。它指的是編程可控制的引腳,即可以控制引腳是作為輸入來用,還是輸出功能,又或者是交給片上外設使用(復用)。 二、GPIO結構框圖 理解GPIO硬體電路的實現,有助於編程的理解。下圖是 ...
  • 字的存儲 在上一篇中說到一個16位寄存器可以存放一個字(16位)或者一個位元組(8位),當存放一個位元組的時候只需要一個記憶體單元(記憶體單元是以位元組為單位的,8位),而存放一個字需要兩個記憶體單元,這樣存放一個字就需要兩個連續的記憶體單元,這個16位的字, 高位存放在高地址,低位存放在低地址 。 |記憶體地址| ...
  • CodeForces - 209C Trails and Glades 傳送門 題目大意:n個點,m條邊。從一號點出發,需要遍歷所有有邊相連的所有點最後要到一號點。(1 ≤ n ≤ 106; 0 ≤ m ≤ 106) 解法:跑出連通塊個數和每個連通塊所包含的度數為奇數的點,對於包含2個以上奇度頂點的 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...