最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java實(shí)現(xiàn)提取QSV文件視頻內(nèi)容

 更新時(shí)間:2023年03月29日 10:00:37   作者:OriginalTech  
QSV是一種加密的視頻文件格式。是愛奇藝公司研發(fā)的一種視頻文件格式,這篇文章主要為大家介紹了如何利用Java實(shí)現(xiàn)提取QSV文件視頻內(nèi)容,感興趣的可以了解一下

創(chuàng)建類

第一步新建一個(gè)java類QSV,構(gòu)造函數(shù)傳入需要解析的文件名稱。

public class QSV {
    
    private RandomAccessFile randomAccessFile;
    private String name;

    public QSV(String name) throws FileNotFoundException {
        randomAccessFile = new RandomAccessFile(name, "r");
        this.name = name;
    }

}

通用方法

逐字節(jié)讀取文件

我們需要逐個(gè)字節(jié)讀取文件,這邊就需要jdk自帶的類RandomAccessFile,先定義一個(gè)通用函數(shù),輸出偏移位置和字節(jié)大小獲取字節(jié)數(shù)組。

/**
* @param offset 偏移未知
* @param size   字節(jié)大小
* @return 字節(jié)數(shù)組
* @throws IOException
*/
private byte[] getByteFromFile(int offset, int size) throws IOException {
    //指針移動(dòng)至偏移位置
    randomAccessFile.seek(offset);
    //需要讀取的字節(jié)數(shù)
    byte[] bs = new byte[size];
    randomAccessFile.read(bs);
    return bs;
}

字節(jié)數(shù)組轉(zhuǎn)16進(jìn)制字符串

private static final char[] HEX_VOCABLE = {'0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/**
* 字節(jié)數(shù)組轉(zhuǎn)16進(jìn)制字符串
*
* @param bs
* @return
*/
public String bytesToHex(byte[] bs) {
    StringBuilder sb = new StringBuilder();
    for (byte b : bs) {
        int high = (b >> 4) & 0x0f;
        int low = b & 0x0f;
        sb.append(HEX_VOCABLE[high]);
        sb.append(HEX_VOCABLE[low]);
    }
    return sb.toString();
}

字節(jié)數(shù)組轉(zhuǎn)int

/**
* 字節(jié)數(shù)組轉(zhuǎn)int
*
* @param b
* @return
*/
public int toInt(byte[] b) {
    int res = 0;
    for (int i = 0; i < b.length; i++) {
        res += (b[i] & 0xff) << (i * 8);
    }
    return res;
}

老版本解密算法

老版本解密方法比較簡(jiǎn)單,先看下C的寫法:

// 定義1個(gè)字節(jié)內(nèi)存
typedef uint8_t BYTE;
// 定義4個(gè)字節(jié)內(nèi)存
typedef uint32_t DWORD;
// decryption algorithm for some segments in qsv version 0x1
void decrypt_1(BYTE* buffer, DWORD size) {
    static BYTE dict[] = {0x62, 0x67, 0x70, 0x79};
    for(int i = 0; i < size; ++i) {
        DWORD j = ~i & 0x3;
        buffer[i] ^= dict[j];
    }
}

可以直接用byte數(shù)組和long類型替換c的內(nèi)存定義,這邊沒問題時(shí)因?yàn)闆]有位移算法,后面新版本解密算法會(huì)講到。

private void decrypt_1(byte[] bs, long len) {
    byte[] b = new byte[]{0x62, 0x67, 0x70, 0x79};
    for (int i = 0; i < len; i++) {
        //先取反再與運(yùn)算
        int j = ~i & 0x3;
        //異或運(yùn)算
        bs[i] ^= b[j];
    }
}

新版本解密算法

新版本解密算法比較復(fù)雜,同樣先看下c的寫法:

// 定義1個(gè)字節(jié)內(nèi)存
typedef uint8_t BYTE;
// 定義4個(gè)字節(jié)內(nèi)存
typedef uint32_t DWORD;
// decryption algorithm for some segments in qsv version 0x2
void decrypt_2(BYTE* buffer, DWORD size) {
    DWORD x = 0x62677079;
    for(DWORD i = size - 1; i != 0; --i) {
        x = (x << 1) | (x >> 31);
        x ^= buffer[i];
    }
    for(DWORD i = 1; i < size; ++i) {
        x ^= buffer[i] & 0xFF;
        x = (x >> 1) | (x << 31);
        DWORD j = x % i;
        BYTE tmp = buffer[j];
        buffer[j] = tmp ^ (BYTE)~buffer[i];
        buffer[i] = tmp;
    }
}

這邊注意c的寫法有位移,定義的DWORD占32位內(nèi)存,而java與之對(duì)應(yīng)的int類型也是占32位內(nèi)存,取值范圍是-2^31~2^31-1(即-2147483648~2147483647),如果超出這個(gè)范圍java需要用long,而long占64位內(nèi)存取反和位移都是按64位進(jìn)行的與C的32位取反位移計(jì)算結(jié)果肯定有差異。

比如:2147483648,轉(zhuǎn)換位二進(jìn)制表示:10000000000000000000000000000000,C的寫法由于DWORD占32位內(nèi)存,左移一位后超出32為故就變成00000000000000000000000000000000,而java已經(jīng)超出了int的取值范圍,只能用long定義,由于long占64位,左移一位不會(huì)超出,結(jié)果也就不一樣了。

又比如:9147483648,轉(zhuǎn)為二進(jìn)制表示:1000100001001110111000011000000000,已經(jīng)超出32位,存到DWORD中的只保留右邊的32位,即00100001001110111000011000000000,左移以為變成01000010011101110000110000000000,轉(zhuǎn)為10進(jìn)制為1115098112,而java采用long定義不會(huì)超出64位,左移結(jié)果位10001000010011101110000110000000000,轉(zhuǎn)為十進(jìn)制表示為18294967296,與C的結(jié)果相差較大。那么有辦法解決嗎,答案肯定是有的,我們需要重新寫一個(gè)位移算法。

/**
* 左移
*
* @param value long
* @param i     位移量
* @return long
*/
private long longLeftShift(long value, int i) {
    String binary = format32(value);
    binary = binary.substring(i) + String.format("%0" + i + "d", 0);
    return Long.parseLong(binary, 2);
}

/**
* 右移
*
* @param value long
* @param i     位移量
* @return long
*/
private long longRightShift(long value, int i) {
    String binary = format32(value);
    binary = String.format("%0" + i + "d", 0) + binary.substring(0, binary.length() - i);
    return Long.parseLong(binary, 2);
}

private String format32(long value) {
    String binary = Long.toBinaryString(value);
    if (binary.length() < 32) {
        //補(bǔ)滿32位
        binary = String.format("%0" + (32 - binary.length()) + "d", 0) + binary;
    } else {
        //多余的截取掉
        binary = binary.substring(binary.length() - 32);
    }
    return binary;
}

有了新定義的位移算法,解密算法就可以修改為:

private void decrypt_2(byte[] buffer, int size) {
    long x = 0x62677079;
    for (int i = size - 1; i != 0; --i) {
        x = longLeftShift(x, 1) | longRightShift(x, 31);
        x ^= buffer[i] & 0xFF;
    }
    for (int i = 1; i < size; ++i) {
        x ^= buffer[i] & 0xFF;
        x = longRightShift(x, 1) | longLeftShift(x, 31);
        int j = (int) (x % i);
        byte tmp = buffer[j];
        int a = buffer[i];
        buffer[j] = (byte) (tmp ^ ~a);
        buffer[i] = tmp;
    }
}

獲取QSV頭部信息

上一篇文章講到,頭部信息共90個(gè)字節(jié),signature(10byte)+version(4byte)+vid(16byte)+_unknown1(4byte)+_unknown2(32byte)+_unknown3(4byte)+_unknown4(4byte)+json_offset(8byte)+json_size(4byte)+nb_indices(4byte)

public static void main(String[] args) throws IOException {
    QSV qsv = new QSV("D:\\workspace\\c\\風(fēng)吹半夏第1集-藍(lán)光1080P.qsv");
    qsv.readHead();
    qsv.close();
}

public void readHead() throws IOException {
    //signature:10byte
    byte[] signature = getByteFromFile(0x0, 0xA);
    //version:4byte
    byte[] version = getByteFromFile(0xA, 0x4);
    //vid:16byte
    byte[] vid = getByteFromFile(0xE, 0x10);
    //_unknown1:4byte
    byte[] _unknown1 = getByteFromFile(0x1E, 0x4);
    //_unknown2:32byte
    byte[] _unknown2 = getByteFromFile(0x22, 0x20);
    //_unknown3:4byte
    byte[] _unknown3 = getByteFromFile(0x42, 0x4);
    //_unknown4:4byte
    byte[] _unknown4 = getByteFromFile(0x46, 0x4);
    //json_offset:8byte
    byte[] json_offset = getByteFromFile(0x4A, 0x8);
    //json_size:4byte
    byte[] json_size = getByteFromFile(0x52, 0x4);
    //nb_indices:4byte
    byte[] nb_indices = getByteFromFile(0x56, 0x4);
    System.out.println(String.format("signature:%s\nversion:%s\nvid:%s\n_unknown1:%s\n_unknown2:%s\n_unknown3:%s\n_unknown4:%s\njson_offset:0x%s\njson_size:0x%s\nnb_indices:0x%s",
            new String(signature), toInt(version), bytesToHex(vid), bytesToHex(_unknown1), bytesToHex(_unknown2), bytesToHex(_unknown3),
            bytesToHex(_unknown4), Integer.toHexString(toInt(json_offset)), Integer.toHexString(toInt(json_size)), Integer.toHexString(toInt(nb_indices))));
}

運(yùn)行結(jié)果:

signature:QIYI VIDEO
version:2
vid:A90EA47D4333331BB85F331C1130504C
_unknown1:01000000
_unknown2:0000000000000000000000000000000000000000000000000000000000000000
_unknown3:01000000
_unknown4:01000000
json_offset:0x167
json_size:0x1d81
nb_indices:0x7

獲取json數(shù)據(jù)

獲取json數(shù)據(jù)需要先獲取頭部信息記錄的json的偏移位置和字節(jié)大小,獲取到的字節(jié)數(shù)組再通過老版本解密算法解密即可。

public static void main(String[] args) throws IOException {
    QSV qsv = new QSV("D:\\workspace\\c\\風(fēng)吹半夏第1集-藍(lán)光1080P.qsv");
    qsv.readJson();
    qsv.close();
}

public void readJson() throws IOException {
    //json_offset:8byte
    byte[] json_offset = getByteFromFile(0x4A, 0x8);
    //json_size:4byte
    byte[] json_size = getByteFromFile(0x52, 0x4);
    //json
    byte[] bs = getByteFromFile(toInt(json_offset), toInt(json_size));
    decrypt_1(bs, bs.length);
    System.out.println(String.format("json_offset:0x%s\njson_size:0x%s", Integer.toHexString(toInt(json_offset)), Integer.toHexString(toInt(json_size))));
    System.out.println(new String(bs, StandardCharsets.UTF_8));
}

運(yùn)行結(jié)果:

json_offset:0x167
json_size:0x1d81
QYVI   {"qsv_info":{"ad":{},"aid":"3644740799867701","bid":600,"dr":-1,"independentaudio":true,"m3u8":"","pano":{"type":1},"qsvinfo_version":2,"sdv":"","st":"","thdt":1,"tht":0,"title":"","title_tail_info":{"bt":99,"bts":-1,"et":2621,"ete":-1,"fe":1,"le":0},"tvid":"3185901936393500","vd":{"seg":{"duration":["376140","365508","370300","371477","368311","368308","554319"],"rid":["a8c47c2906b88ae47e8503c96dae0494","4c8578cecbdfa3137a61b7e2bd1c68dc","4bc76f9b4d4ce995f40b42c467d206f8","8ed13ccd56aa2367f5eaabee12043d89","247b4612415c5b452f5965894a967e19","309400bef063cfd6baf19feb0d4a73a9","9771b1042cabfc9828767a3d0350f655"],"size":["75624801","84329329","90135782","82093900","91974933","75298884","111399115"]},"time":{"et":"2621000","st":"99000"}},"vi":"{\"writer\":\"\",\"authors\":\"\",\"upOrder\":36,\"rewardAllowed\":0,\"fl\":[],\"allowEditVVIqiyi\":0,\"isPopup\":1,\"payMark\":0,\"an\":\"風(fēng)吹半夏\",\"pvu\":\"\",\"subType\":1,\"rewardMessage\":\"\",\"ipLimit\":1,\"pubTime\":\"1673306651000\",\"mainActorRoles\":[],\"up\":\"2023-01-17 20:20:02\",\"un\":\"\",\"qiyiProduced\":1,\"platforms\":[],\"vn\":\"風(fēng)吹半夏第1集\",\"plc\":{\"4\":{\"downAld\":1,\"coa\":1},\"40\":{\"downAld\":1,\"coa\":1},\"10\":{\"downAld\":1,\"coa\":1},\"5\":{\"downAld\":1,\"coa\":1},\"41\":{\"downAld\":1,\"coa\":1},\"22\":{\"downAld\":1,\"coa\":1},\"32\":{\"downAld\":1,\"coa\":1},\"11\":{\"downAld\":1,\"coa\":1},\"12\":{\"downAld\":1,\"coa\":1},\"7\":{\"downAld\":1,\"coa\":1},\"13\":{\"downAld\":1,\"coa\":1},\"14\":{\"downAld\":1,\"coa\":1},\"91\":{\"downAld\":1,\"coa\":1},\"34\":{\"downAld\":1,\"coa\":1},\"9\":{\"downAld\":1,\"coa\":1},\"6\":{\"downAld\":1,\"coa\":1},\"16\":{\"downAld\":1,\"coa\":1},\"17\":{\"downAld\":1,\"coa\":1},\"27\":{\"downAld\":1,\"coa\":1},\"28\":{\"downAld\":1,\"coa\":1},\"18\":{\"downAld\":1,\"coa\":1},\"29\":{\"downAld\":1,\"coa\":1},\"1\":{\"downAld\":1,\"coa\":1},\"50\":{\"downAld\":1,\"coa\":1},\"31\":{\"downAld\":1,\"coa\":1},\"15\":{\"downAld\":1,\"coa\":1},\"2\":{\"downAld\":1,\"coa\":1},\"3\":{\"downAld\":1,\"coa\":1},\"21\":{\"downAld\":1,\"coa\":1},\"30\":{\"downAld\":1,\"coa\":1},\"19\":{\"downAld\":1,\"coa\":1},\"8\":{\"downAld\":1,\"coa\":1},\"20\":{\"downAld\":1,\"coa\":1}},\"sm\":0,\"st\":200,\"startTime\":99,\"showChannelId\":2,\"vu\":\"http:\\/\\/www.iqiyi.com\\/v_uvxuws1kkw.html\",\"povu\":\"\",\"ppsuploadid\":0,\"qiyiPlayStrategy\":\"非會(huì)員每日20:00轉(zhuǎn)免1集\",\"ar\":\"內(nèi)地\",\"cc\":0,\"videoQipuId\":3185901936393500,\"albumQipuId\":3644740799867701,\"pano\":{\"type\":1},\"subt\":\"許半夏幫童驍騎驅(qū)霉運(yùn)\",\"endTime\":2621,\"cpa\":1,\"userVideocount\":0,\"es\":36,\"plg\":2774,\"stl\":{\"d\":\"http:\\/\\/meta.video.iqiyi.com\",\"stl\":[]},\"subKey\":\"3644740799867701\",\"tvFocuse\":\"趙麗穎歐豪攜手創(chuàng)業(yè)\",\"qtId\":0,\"ty\":0,\"ppsInfo\":{\"shortTitle\":\"風(fēng)吹半夏第1集\",\"name\":\"風(fēng)吹半夏第1集\"},\"sid\":0,\"etm\":\"\",\"keyword\":\"風(fēng)吹半夏\",\"pturl\":\"\",\"a\":\"\",\"isTopChart\":0,\"idl\":0,\"albumAlias\":\"許半夏幫童驍騎驅(qū)霉運(yùn)\",\"stm\":\"\",\"pd\":1,\"tags\":[],\"coa\":0,\"c\":2,\"producers\":\"\",\"nurl\":\"\",\"tvEname\":\"wild bloom\",\"uid\":\"0\",\"asubt\":\"\",\"d\":\"傅東育|毛溦\(chéng)",\"vid\":\"a90ea47d4333331bb85f331c1130504c\",\"dts\":\"20230117202002\",\"editorInfo\":\"\",\"tvSeason\":0,\"ntvd\":3185901936393500,\"vpic\":\"http:\\/\\/pic6.iqiyipic.com\\/image\\/20221127\\/e2\\/52\\/v_170312139_m_601_m1.jpg\",\"vType\":0,\"cType\":1,\"payMarkUrl\":\"\",\"apic\":\"http:\\/\\/pic8.iqiyipic.com\\/image\\/20230104\\/6c\\/00\\/a_100498846_m_601_m26.jpg\",\"shortTitle\":\"風(fēng)吹半夏第1集\",\"tvid\":3185901936393500,\"aid\":3644740799867701,\"au\":\"http:\\/\\/www.iqiyi.com\\/a_yq6d69gtw9.html\",\"tvPhase\":0,\"ifs\":0,\"is\":0,\"tpl\":[],\"actors\":[],\"isVip\":\"\",\"votes\":[],\"previewImageUrl\":\"http:\\/\\/preimage3.iqiyipic.com\\/preimage\\/20230110\\/6a\\/a6\\/v_170312139_m_611_m6.jpg\",\"e\":\"\",\"cnPremTime\":0,\"s\":\"\",\"ma\":\"趙麗穎|歐豪|李光潔|劉威|柯藍(lán)|任重|馮嘉怡|孫千|黃澄澄|是安|宋熹|王西|黃義威|王勁松|劉威葳|尤靖茹|林鵬(大陸男演員)|郝平|淮文|顏世魁|薛淑杰|劉碩|丁冠中|豆藝?yán)寇鐘吁|楊德民|傅宜箴|朱超藝|黃子琪|榮飛|田玲|周小鑌|孔琳|方曉莉|陳創(chuàng)|韓朔|高遠(yuǎn)\",\"supName\":\"\",\"sc\":0,\"categoryKeywords\":\"電視劇,3644740799867701,0,http:\\/\\/list.iqiyi.com\\/www\\/2\\/------------------.html 自制,12791537149997,2,http:\\/\\/list.iqiyi.com\\/www\\/2\\/-12791537149997-----------------.html 浙江衛(wèi)視,20291787825994,0,http:\\/\\/list.iqiyi.com\\/www\\/2\\/------------------.html 家庭,23757144288056,2,http:\\/\\/list.iqiyi.com\\/www\\/2\\/-23757144288056-----------------.html 言情,23789230921965,2,http:\\/\\/list.iqiyi.com\\/www\\/2\\/-23789230921965-----------------.html 勵(lì)志,29717236536467,2,http:\\/\\/list.iqiyi.com\\/www\\/2\\/-29717236536467-----------------.html 商戰(zhàn),32284167249109,2,http:\\/\\/list.iqiyi.com\\/www\\/2\\/-32284167249109-----------------.html 劇情,37979544767779,2,http:\\/\\/list.iqiyi.com\\/www\\/2\\/-37979544767779-----------------.html 都市,80141381722890,2,http:\\/\\/list.iqiyi.com\\/www\\/2\\/-80141381722890-----------------.html 內(nèi)地,80526421329786,0,http:\\/\\/list.iqiyi.com\\/www\\/2\\/------------------.html 愛情,87328787718281,2,http:\\/\\/list.iqiyi.com\\/www\\/2\\/-87328787718281-----------------.html\",\"onlineStatus\":1,\"mdown\":0,\"bmsg\":{\"t\":\"20230117202002\",\"f\":\"web\",\"mid\":\"\",\"sp\":\"26173601,\"},\"nvid\":\"8ec7f862076f40ee17c908e2bbc3fae2\",\"flpps\":[],\"supType\":\"\",\"lg\":2,\"is3D\":0,\"bossStatus\":0,\"isDisplayCircle\":0,\"actorRoles\":[],\"exclusive\":1,\"circle\":{\"type\":0,\"id\":0},\"commentAllowed\":1,\"tg\":\"忠犬 大女主 自制 工業(yè)強(qiáng)國(guó) 毒舌 重組家庭 渣男 浙江衛(wèi)視 家庭 言情 霸道總裁 御姐 勵(lì)志 個(gè)人成長(zhǎng) 虐戀 雙強(qiáng) 普通話 商戰(zhàn) 硬漢 劇情 出軌 兄弟反目 女強(qiáng)人 爽劇 初戀 青年奮斗 熱血 女性勵(lì)志 群像 個(gè)人奮斗 接地氣 女青年 大佬 原生家庭 豬隊(duì)友 都市 內(nèi)地 組團(tuán) 愛情\",\"info\":\"1996年的秋天,陽(yáng)光炫目。許半夏開著一輛桑塔納,接著剛出獄的童驍騎駛往鷺州出差。一路上童驍騎默默無(wú)言,二人之間氣氛尷尬。到了酒店后,許半夏的指示很清楚:洗澡,看電視,睡覺,第二天一早,她來(lái)接童驍騎一起出差。童驍騎點(diǎn)點(diǎn)頭,在許半夏離開后,他開始洗澡,記憶也回到了五年前……\\n1991年的一個(gè)雨夜,童驍騎因?yàn)榧敝o媽媽籌醫(yī)藥費(fèi),撬了一堆下水井蓋,蹬著三輪車?yán)搅嗽S半夏和小陳的廢品站。小陳認(rèn)定童驍騎就是個(gè)騙子,不愿幫忙,但善良的半夏相信童驍騎,給了他錢,并且三個(gè)人一大早一起把井蓋都送了回去。童驍騎為表感激,愿意替二人做事。許半夏提到當(dāng)?shù)劁搹S有很多廢了的下腳料,他們可以去收,但缺一輛大車。于是司機(jī)班出身的童驍騎真的把廠里的東風(fēng)偷偷開了出來(lái),三人搖搖晃晃進(jìn)了鋼廠,掙到了人生的第一桶金,但童驍騎也因?yàn)樗阶杂密嚤粡S里開除了?;氐綇U品站的童驍騎心灰意冷,許半夏卻安慰道正好,今后他們?nèi)齻€(gè)人就一塊兒干,而這些錢就是他們的第一筆合伙資金。\",\"supId\":0,\"issueTime\":0,\"dtype\":3,\"producer\":\"\",\"presentor\":[],\"followerCount\":0}","vid":"a90ea47d4333331bb85f331c1130504c","videotype":0}}s

提取視頻信息

獲取視頻信息,首先要先提取索引信息,根據(jù)索引信息中的偏移位置和字節(jié)大小獲取,獲取到的字節(jié)數(shù)組前1024個(gè)字節(jié)需要解密,根據(jù)頭部信息中的version判斷,如果是1則用老版本算法解密,如果是2則用新版本算法解密。

一個(gè)QSV文件包含多段視頻數(shù)據(jù),已發(fā)現(xiàn)的視頻格式有flv(舊版客戶端)、mpeg-ts(新版客戶端)。可以通過前三個(gè)字節(jié)判斷,如果前三個(gè)字節(jié)轉(zhuǎn)字符串為FLV則為FLV格式。

public static void main(String[] args) throws IOException {
    QSV qsv = new QSV("D:\\workspace\\c\\風(fēng)吹半夏第1集-藍(lán)光1080P.qsv");
    qsv.readIndex("D:\\workspace\\c");
    qsv.close();
}

public void readIndex(String outPath) throws IOException {
    //nb_indices
    byte[] nb_indices = getByteFromFile(0x56, 0x4);
    //索引
    //_unknown_flag
    byte[] _unknown_flag = getByteFromFile(0x5A, toInt(nb_indices) + 7 >> 3);
    System.out.println(String.format("nb_indices:0x%s\n_unknown_flag:%s", Integer.toHexString(toInt(nb_indices)), bytesToHex(_unknown_flag)));
    //version:4byte
    byte[] version = getByteFromFile(0xA, 0x4);
    //索引體
    byte[] indices = getByteFromFile(0x5A + (toInt(nb_indices) + 7 >> 3), toInt(nb_indices) * 0x1C);
    for (int i = 0; i < toInt(nb_indices); i++) {
        byte[] indice = new byte[0x1C];
        System.arraycopy(indices, 0x1C * i, indice, 0, 0x1C);
        if (toInt(version) == 1) {
            decrypt_1(indice, indice.length);
        } else if (toInt(version) == 2) {
            decrypt_2(indice, indice.length);
        }
        //索引體
        //_codetable
        byte[] _codetable = new byte[0x10];
        System.arraycopy(indice, 0, _codetable, 0, 0x10);
        //segment_offset
        byte[] segment_offset = new byte[0x8];
        System.arraycopy(indice, 0x10, segment_offset, 0, 0x8);
        //segment_size
        byte[] segment_size = new byte[0x4];
        System.arraycopy(indice, 0x18, segment_size, 0, 0x4);
        System.out.println(String.format("_codetable:%s,segment_offset:0x%s,segment_size:0x%s", bytesToHex(_codetable), Integer.toHexString(toInt(segment_offset)), Integer.toHexString(toInt(segment_size))));
        exportVideo(i + 1, segment_offset, segment_size, outPath);
    }
}

private void exportVideo(int fileindex, byte[] segment_offset, byte[] segment_size, String outPath) throws IOException {
    byte[] file = getByteFromFile(toInt(segment_offset), toInt(segment_size));
    //前1024字節(jié)加密
    decrypt_2(file, 1024);
    byte[] head = new byte[0x3];
    System.arraycopy(file, 0, head, 0, 0x3);
    //System.out.println(new String(head));
    String outname = name.substring(name.lastIndexOf(File.separator) + 1);
    String outFile = outPath + File.separator + outname.substring(0, outname.lastIndexOf(".")) + "-第" + fileindex + "段." + new String(head).toLowerCase();
    RandomAccessFile write = new RandomAccessFile(outFile, "rw");
    System.out.println("export:" + outFile);
    write.write(file);
    write.close();
}

運(yùn)行結(jié)果:

nb_indices:0x7
_unknown_flag:7F
_codetable:A8C47C2906B88AE47E8503C96DAE0494,segment_offset:0x1ee8,segment_size:0x481f161
export:D:\workspace\c\風(fēng)吹半夏第1集-藍(lán)光1080P-第1段.flv
_codetable:4C8578CECBDFA3137A61B7E2BD1C68DC,segment_offset:0x4821049,segment_size:0x506c371
export:D:\workspace\c\風(fēng)吹半夏第1集-藍(lán)光1080P-第2段.flv
_codetable:4BC76F9B4D4CE995F40B42C467D206F8,segment_offset:0x988d3ba,segment_size:0x55f5ce6
export:D:\workspace\c\風(fēng)吹半夏第1集-藍(lán)光1080P-第3段.flv
_codetable:8ED13CCD56AA2367F5EAABEE12043D89,segment_offset:0xee830a0,segment_size:0x4e4a74c
export:D:\workspace\c\風(fēng)吹半夏第1集-藍(lán)光1080P-第4段.flv
_codetable:247B4612415C5B452F5965894A967E19,segment_offset:0x13ccd7ec,segment_size:0x57b6d15
export:D:\workspace\c\風(fēng)吹半夏第1集-藍(lán)光1080P-第5段.flv
_codetable:309400BEF063CFD6BAF19FEB0D4A73A9,segment_offset:0x19484501,segment_size:0x47cf844
export:D:\workspace\c\風(fēng)吹半夏第1集-藍(lán)光1080P-第6段.flv
_codetable:9771B1042CABFC9828767A3D0350F655,segment_offset:0x1dc53d45,segment_size:0x6a3d0cb
export:D:\workspace\c\風(fēng)吹半夏第1集-藍(lán)光1080P-第7段.flv

參考資料

本文參考開源項(xiàng)目qsv2flv,項(xiàng)目地址:https://github.com/btnkij/qsv2flv.git

該項(xiàng)目采用c語(yǔ)言和Qt開發(fā),本文作者根據(jù)原理采用Java進(jìn)行了翻寫,僅供學(xué)習(xí)和參考。如果要用于實(shí)際應(yīng)用,請(qǐng)使用原項(xiàng)目提供的工具,可以將qsv文件轉(zhuǎn)換為flvmp4格式。

到此這篇關(guān)于Java實(shí)現(xiàn)提取QSV文件視頻內(nèi)容的文章就介紹到這了,更多相關(guān)Java提取QSV文件視頻內(nèi)容內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • IDEA SpringBoot 項(xiàng)目配置Swagger2的詳細(xì)教程

    IDEA SpringBoot 項(xiàng)目配置Swagger2的詳細(xì)教程

    這篇文章主要介紹了IDEA SpringBoot 項(xiàng)目配置Swagger2的詳細(xì)教程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • 解決idea每次打開新的項(xiàng)目都需要重新配置maven問題

    解決idea每次打開新的項(xiàng)目都需要重新配置maven問題

    這篇文章主要介紹了解決idea每次打開新的項(xiàng)目都需要重新配置maven問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • SpringBoot中實(shí)現(xiàn)數(shù)據(jù)字典的示例代碼

    SpringBoot中實(shí)現(xiàn)數(shù)據(jù)字典的示例代碼

    這篇文章主要介紹了SpringBoot中實(shí)現(xiàn)數(shù)據(jù)字典的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-09-09
  • springboot實(shí)現(xiàn)注冊(cè)加密與登錄解密功能(demo)

    springboot實(shí)現(xiàn)注冊(cè)加密與登錄解密功能(demo)

    這篇文章主要介紹了springboot實(shí)現(xiàn)注冊(cè)的加密與登錄的解密功能,本文通過demo實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-02-02
  • Java判斷主機(jī)是否能ping通代碼實(shí)例

    Java判斷主機(jī)是否能ping通代碼實(shí)例

    這篇文章主要介紹了Java判斷主機(jī)是否能ping通代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • 在java中 利用匿名內(nèi)部類進(jìn)行較簡(jiǎn)潔的雙括弧初始化的方法

    在java中 利用匿名內(nèi)部類進(jìn)行較簡(jiǎn)潔的雙括弧初始化的方法

    本篇文章小編將為大家介紹,關(guān)于在java中 利用匿名內(nèi)部類進(jìn)行較簡(jiǎn)潔的雙括弧初始化的方法,有需要的朋友可以參考一下
    2013-04-04
  • springBoot  創(chuàng)建定時(shí)任務(wù)過程詳解

    springBoot 創(chuàng)建定時(shí)任務(wù)過程詳解

    這篇文章主要介紹了springBoot 創(chuàng)建定時(shí)任務(wù)過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-10-10
  • Java處理日期時(shí)間的方法匯總

    Java處理日期時(shí)間的方法匯總

    這篇文章主要給大家介紹了利用Java中的Calendar 類處理日期時(shí)間的方法匯總,其中包括取日期的每部分、取當(dāng)月的第一天或最后一天、求兩個(gè)日期之間相隔的天數(shù)以及一年前的日期等等的示例代碼,有需要的朋友們可以直接參考借鑒,下面來(lái)一起看看吧。
    2016-12-12
  • java、python、JavaScript以及jquery循環(huán)語(yǔ)句的區(qū)別

    java、python、JavaScript以及jquery循環(huán)語(yǔ)句的區(qū)別

    本篇文章主要介紹java、python、JavaScript以及jquery的循環(huán)語(yǔ)句的區(qū)別,這里整理了它們循環(huán)語(yǔ)句語(yǔ)法跟示例,以便大家閱讀,更好的區(qū)分它們的不同
    2016-07-07
  • Java自定義映射resultMap定義及用法

    Java自定義映射resultMap定義及用法

    MyBatis的每一個(gè)查詢映射的返回類型都是ResultMap,當(dāng)我們提供返回類型屬性是resultType時(shí),MyBatis會(huì)自動(dòng)給我們把對(duì)應(yīng)值賦給resultType所指定對(duì)象的屬性,當(dāng)我們提供返回類型是resultMap時(shí),將數(shù)據(jù)庫(kù)中列數(shù)據(jù)復(fù)制到對(duì)象的相應(yīng)屬性上,可以用于復(fù)制查詢,兩者不能同時(shí)用
    2022-11-11

最新評(píng)論

阿鲁科尔沁旗| 宜春市| 罗平县| 临猗县| 乌苏市| 阿拉善左旗| 花莲市| 沐川县| 马边| 库伦旗| 门源| 清河县| 南充市| 临武县| 通城县| 汉沽区| 龙里县| 安陆市| 额济纳旗| 巴南区| 平原县| 乐亭县| 瑞丽市| 平乐县| 巍山| 威信县| 浦县| 扎鲁特旗| 囊谦县| 凭祥市| 涞源县| 淮安市| 嘉黎县| 渭南市| 罗源县| 资源县| 册亨县| 金平| 车致| 江都市| 绥化市|