1. 程式人生 > >RTP協議全解析(H264碼流以及PS流荷載)

RTP協議全解析(H264碼流以及PS流荷載)

寫在前面:RTP的解析,網上找了很多資料,但是都不全,所以我力圖整理出一個比較全面的解析,

其中借鑑了很多文章,我都列在了文章最後,在此表示感謝。

網際網路的發展離不開大家的無私奉獻,我決定從我做起,希望大家支援。

1、RTP Header解析

                                                                   

                                                                                    圖1

1)        V:RTP協議的版本號,佔2位,當前協議版本號為2

2)        P:填充標誌,佔1位,如果P=1,則在該報文的尾部填充一個或多個額外的八位組,它們不是有效載荷的一部分。

3)        X:擴充套件標誌,佔1位,如果X=1,則在RTP報頭後跟有一個擴充套件報頭

4)        CC:CSRC計數器,佔4位,指示CSRC 識別符號的個數

5)        M: 標記,佔1位,不同的有效載荷有不同的含義,對於視訊,標記一幀的結束;對於音訊,標記會話的開始。

6)        PT: 有效荷載型別,佔7位,用於說明RTP報文中有效載荷的型別,如GSM音訊、JPEM影象等,在流媒體中大部分是用來區分音訊流和視訊流的,這樣便於客戶端進行解析。

7)        序列號:佔16位,用於標識傳送者所傳送的RTP報文的序列號,每傳送一個報文,序列號增1。這個欄位當下層的承載協議用UDP的時候,網路狀況不好的時候可以用來檢查丟包。同時出現網路抖動的情況可以用來對資料進行重新排序,序列號的初始值是隨機的,同時音訊包和視訊包的sequence是分別記數的。

8)        時戳(Timestamp):佔32位,必須使用90 kHz 時鐘頻率。時戳反映了該RTP報文的第一個八位組的取樣時刻。接收者使用時戳來計算延遲和延遲抖動,並進行同步控制。

9)        同步信源(SSRC)識別符號:佔32位,用於標識同步信源。該識別符號是隨機選擇的,參加同一視訊會議的兩個同步信源不能有相同的SSRC。

10)    特約信源(CSRC)識別符號:每個CSRC識別符號佔32位,可以有0~15個。每個CSRC標識了包含在該RTP報文有效載荷中的所有特約信源。

注:基本的RTP說明並不定義任何頭擴充套件本身,如果遇到X=1,需要特殊處理

取一段碼流如下:

 9b 6b 49 €?....??....A?kI

e1 0f 26 53 02 1a ff06 59 97 1d d2 2e 8c 50 01 ?.&S....Y?.?.?P.

cc 13 ec 52 77 4e e50e 7b fd 16 11 66 27 7c b4 ?.?RwN?.{?..f'|?

f6 e1 29 d5 d6 a4 ef3e 12 d8 fd 6c 97 51 e7 e9 ??)????>.??l?Q??

cfc7 5e c8 a9 51 f6 82 65 d6 48 5a 86 b0 e0 8c ??^??Q??e?HZ????其中,80               是V_P_X_CCe0               是M_PT00 1e          是SequenceNum把前兩位元組換成二進位制如下1000 0000 1110 0000按順序解釋如下:10               是V;0                 是P;0                 是X;0000           是CC;1                 是M;110 0000    是PT;排版不如word看的清晰,大家湊合著看吧。

2、RTP荷載H264碼流


                                                                            圖2

荷載格式定義三個不同的基本荷載結構,接收者可以通過RTP荷載的第一個位元組後5位(如圖2)識別荷載結構。

1)   單個NAL單元包:荷載中只包含一個NAL單元。NAL頭型別域等於原始 NAL單元型別,即在範圍1到23之間

2)   聚合包:本型別用於聚合多個NAL單元到單個RTP荷載中。本包有四種版本,單時間聚合包型別A (STAP-A),單時間聚合包型別B (STAP-B),多時間聚合包型別(MTAP)16位位移(MTAP16), 多時間聚合包型別(MTAP)24位位移(MTAP24)。賦予STAP-A, STAP-B, MTAP16, MTAP24的NAL單元型別號分別是 24,25, 26, 27

3)   分片單元:用於分片單個NAL單元到多個RTP包。現存兩個版本FU-A,FU-B,用NAL單元型別 28,29標識

常用的打包時的分包規則是:如果小於MTU採用單個NAL單元包,如果大於MTU就採用FUs分片方式。
因為常用的打包方式就是單個NAL包和FU-A方式,所以我們只解析這兩種。

2.1、單個NAL單元包


                                                     圖3

        定義在此的NAL單元包必須只包含一個。這意味聚合包和分片單元不可以用在單個NAL 單元包中。並且RTP序號必須符合NAL單元的解碼順序。NAL單元的第一位元組和RTP荷載頭第一個位元組重合。如圖3。

        打包H264碼流時,只需在幀前面加上12位元組的RTP頭即可。

2.2、分片單元(FU-A)


                                       圖4

分片只定義於單個NAL單元不用於任何聚合包。NAL單元的一個分片由整數個連續NAL單元位元組組成。每個NAL單元位元組必須正好是該NAL單元一個分片的一部分。相同NAL單元的分片必須使用遞增的RTP序號連續順序傳送(第一和最後分片之間沒有其他的RTP包)。相似,NAL單元必須按照RTP順序號的順序裝配。

   當一個NAL單元被分片運送在分片單元(FUs)中時,被引用為分片NAL單元。STAPs,MTAPs不可以被分片。 FUs不可以巢狀。 即, 一個FU 不可以包含另一個FU。運送FU的RTP時戳被設定成分片NAL單元的NALU時刻。

   圖 4 表示FU-A的RTP荷載格式。FU-A由1位元組的分片單元指示(如圖5),1位元組的分片單元頭(如圖6),和分片單元荷載組成。

S: 1 bit 當設定成1,開始位指示分片NAL單元的開始。當跟隨的FU荷載不是分片NAL單元荷載的開始,開始位設為0。

E: 1 bit 當設定成1, 結束位指示分片NAL單元的結束,即, 荷載的最後位元組也是分片NAL單元的最後一個位元組。當跟隨的 FU荷載不是分片NAL單元的最後分片,結束位設定為0。

R: 1 bit 保留位必須設定為0,接收者必須忽略該位

打包時,原始的NAL頭的前三位為FU indicator的前三位,原始的NAL頭的後五位為FU header的後五位。
取一段碼流分析如下:

00 0a 7f ca 94 05 3b7f 3e 7f fe 14 2b 27 26 f8 ...??.;.>.?.+'&?

89 88 dd 85 62 e1 6dfc 33 01 38 1a 10 35 f2 14 ????b?m?3.8..5?.

84 6e 21 24 8f 72 62f0 51 7e 10 5f 0d 42 71 12 ?n!$?rb?Q~._.Bq.

17 65 62 a1 f1 44 dc df 4b 4a 38 aa 96 b7 dd 24 .eb??D??KJ8????$前12位元組是RTP Header7c是FU indicator85是FU HeaderFU indicator(0x7C)和FU Header(0x85)換成二進位制如下0111 1100 1000 0101按順序解析如下:0                            是F11                          是NRI11100                    是FU Type,這裡是28,即FU-A1                            是S,Start,說明是分片的第一包0                            是E,End,如果是分片的最後一包,設定為1,這裡不是0                            是R,Remain,保留位,總是000101                    是NAl Type,這裡是5,說明是關鍵幀(不知道為什麼是關鍵幀請自行谷歌)

打包時,FUindicator的F、NRI是NAL Header中的F、NRI,Type是28;FU Header的S、E、R分別按照分片起始位置設定,Type是NAL Header中的Type。

解包時,取FU indicator的前三位和FU Header的後五位,即0110 0101(0x65)為NAL型別。

3、RTP荷載PS流

        針對H264 做如下PS 封裝:每個IDR NALU 前一般都會包含SPS、PPS 等NALU,因此將SPS、PPS、IDR 的NALU 封裝為一個PS 包,包括ps 頭,然後加上PS system header,PS system map,PES header+h264 raw data。所以一個IDR NALU PS 包由外到內順序是:PSheader| PS system header | PS system Map | PES header | h264 raw data。對於其它非關鍵幀的PS 包,就簡單多了,直接加上PS頭和PES 頭就可以了。順序為:PS header | PES header | h264raw data。以上是對只有視訊video 的情況,如果要把音訊Audio也打包進PS 封裝,也可以。當有音訊資料時,將資料加上PES header 放到視訊PES 後就可以了。順序如下:PS 包=PS頭|PES(video)|PES(audio),再用RTP 封裝傳送就可以了。

        GB28181 對RTP 傳輸的資料負載型別有規定(參考GB28181 附錄B),負載型別中96-127

        RFC2250 建議96 表示PS 封裝,建議97 為MPEG-4,建議98 為H264

        即我們接收到的RTP 包首先需要判斷負載型別,若負載型別為96,則採用PS 解複用,將音視訊分開解碼。若負載型別為98,直接按照H264 的解碼型別解碼。

        注:此方法不一定準確,取決於打包格式是否標準

PS 包中的流型別(stream type)的取值如下:

1)        MPEG-4 視訊流: 0x10;

2)        H.264 視訊流: 0x1B;

3)        SVAC 視訊流: 0x80;

4)        G.711 音訊流: 0x90;

5)        G.722.1 音訊流: 0x92;

6)        G.723.1 音訊流: 0x93;

7)        G.729 音訊流: 0x99;

8)       SVAC音訊流: 0x9B。

3.1、PS包頭

                                                 圖7

1)        Pack start code:包起始碼欄位,值為0x000001BA的位串,用來標誌一個包的開始。

2)        System clock reference base,system clock reference extenstion:系統時鐘參考欄位。

3)        Pack stuffing length :包填充長度欄位,3 位整數,規定該欄位後填充位元組的個數

7e ff 3e fb 44 01 00 5f 6b f8 00 00 01 e0 14 53 ~.>?D.._k?...?.S

80 80 05 2f bf cf bed1 1c 42 56 7b 13 58 0a 1e €€./????.BV{.X..

08 b1 4f 33 69 35 0453 6d 33 a8 04 15 58 d9 21 .?O3i5.Sm3?..X?!

9741 b9 f1 75 3d 94 2b 1f bc 0b b2 b4 97 bf 93 ?A??u=?+.?.?????

前12位是RTP Header,這裡不再贅述;

000001ba是包頭起始碼;

接下來的9位包括了SCR,SCRE,MUXRate,具體看圖7

最後一位是保留位(0xf8),定義了是否有擴充套件,二進位制如下

1111 1000

前5位跳過,後3位指示了擴充套件長度,這裡是0.

3.2、系統標題


                                                           圖8Systemheader當且僅當pack是第一個資料包時才存在,即PS包頭之後就是系統標題。取值0x000001BB的位串,指出系統標題的開始,暫時不需要處理,讀取Header Length直接跳過即可。

3.3、節目對映流

Systemheader當且僅當pack是第一個資料包時才存在,即系統標題之後就是節目流對映。取值0x000001BC的位串,指出節目流對映的開始,暫時不需要處理,讀取Header Length直接跳過即可。前5位元組的結構同系統標題,見圖8。

取一段碼流分析系統標題和節目對映流

01 bb 00 0c 80 cc f5 04 e1 7f e0 e0 e8 c0 c0 20  .?..€??.?.?????

00 00 01 bc 00 1e e1 ff00 00 00 18 1b e0 00 0c ...?..?......?..

2a 0a 7f ff 00 00 0708 1f fe a0 5a 90 c0 00 00  *........??Z??..

00 00 00 0000 00 01 e0 7f e0 80 80 0521 6a 75  .......?.?€€.!ju

前14個位元組是PS包頭(注意,沒有擴充套件);

接下來的00 00 01 bb是系統標題起始碼;

接下來的00 0c說明了系統標題的長度(不包括起始碼和長度位元組本身);

接下來的12個位元組是系統標題的具體內容,這裡不做解析;

繼續看到00 00 01 bc,這是節目對映流起始碼;

緊接著的00 1e同樣代表長度;

跳過e1 ff,基本沒用;

接下來是00 18,代表基本流長度,說明了後面還有24個位元組;

接下來的1b,意思是H264編碼格式;

下一個位元組e0,意思是視訊流;

接下里00 0c,同樣代表接下的長度12個位元組;

跳過這12個位元組,看到90,這是G.711音訊格式;

下一個位元組是c0,代表音訊流;

接下來的00 00同樣代表長度,這裡是0;

接下來4個位元組是CRC,迴圈冗餘校驗。

到這裡節目對映流解析完畢。(好累奮鬥)。

好戲還在後頭呢。生氣

3.4、PES分組頭部


                                                         圖9

別被這麼長的圖嚇到,其實原理相同,但是,你必須處理其中的每一位。

1)        Packet start code prefix:值為0x000001的位串,它和後面的stream id 構成了標識分組開始的分組起始碼,用來標誌一個包的開始。

2)        Stream id:在節目流中,它規定了基本流的號碼和型別。0x(C0~DF)指音訊,0x(E0~EF)為視訊

3)        PES packet length:16 位欄位,指出了PES 分組中跟在該欄位後的位元組數目。值為0 表示PES 分組長度要麼沒有規定要麼沒有限制。這種情況只允許出現在有效負載包含來源於傳輸流分組中某個視訊基本流的位元組的PES 分組中。

4)        PTS_DTS:2 位欄位。當值為'10'時,PTS 欄位應出現在PES 分組標題中;當值為'11'時,PTS 欄位和DTS 欄位都應出現在PES 分組標題中;當值為'00'時,PTS 欄位和DTS 欄位都不出現在PES分組標題中。值'01'是不允許的。

5)        ESCR:1位。置'1'時表示ESCR 基礎和擴充套件欄位出現在PES 分組標題中;值為'0'表示沒有ESCR 欄位。

6)        ESrate:1 位。置'1'時表示ES rate 欄位出現在PES 分組標題中;值為'0'表示沒有ES rate 欄位。

7)        DSMtrick mode:1 位。置'1'時表示有8 位特技方式欄位;值為'0'表示沒有該欄位。

8)        Additionalinfo:1 位。附加版權資訊標誌欄位。置'1'時表示有附加拷貝資訊欄位;值為'0'表示沒有該欄位。

9)        CRC:1 位。置'1'時表示CRC 欄位出現在PES 分組標題中;值為'0'表示沒有該欄位。

10)    Extensionflag:1 位標誌。置'1'時表示PES 分組標題中有擴充套件欄位;值為'0'表示沒有該欄位。

PES header data length: 8 位。PES 標題資料長度欄位。指出包含在PES 分組標題中的可選欄位和任何填充位元組所佔用的總位元組數。該欄位之前的位元組指出了有無可選欄位。

老規矩,上碼流:

00 00 01 e0 21 33 80 80 05 2b 5f df 5c 95 71 84 ...?!3€€.+_?\?q?

aa e4 e9 e9 ec 40 cc17 e0 68 7b 23 f6 89 df 90 [email protected]?.?h{#????

a9d4 be 74 b9 67 ad 34 6d f0 92 0d 5a 48 dd 13 ???t?g?4m??.ZH?.00 00 01是起始碼;

e0是視訊流;

21 33 是幀長度;

接下來的兩個80 80見下面的二進位制解析;

下一個位元組05指出了可選欄位的長度,前一位元組指出了有無可選欄位;

接下來的5位元組是PTS;

第7、8位元組的二進位制如下:

1000 0000 1000 0000

按順序解析:

第7個位元組:

10                         是標誌位,必須是10;

00                         是加擾控制欄位,‘00’表示沒有加密,剩下的01,10,11由使用者自定義;

0                           是優先順序,1為高,0為低;

0                           是資料對齊指示欄位;

0                           是版權欄位;

0                           是原始或拷貝欄位。置'1'時表示相關PES分組有效負載的內容是原始的;'0'表示內容是一份拷貝;

第8個位元組:

10                         是PTS_DTS欄位,這裡是10,表示有PTS,沒有DTS;

0                           是ESCR標誌欄位,這裡為0,表示沒有該段;

0                           是ES速率標誌欄位,,這裡為0,表示沒有該段;

0                           是DSM特技方式標誌欄位,,這裡為0,表示沒有該段;

0                           是附加版權資訊標誌欄位,,這裡為0,表示沒有該段;

0                           是PESCRC標誌欄位,,這裡為0,表示沒有該段;

0                           是PES擴充套件標誌欄位,,這裡為0,表示沒有該段;

本段碼流只有PTS,貼一下解析函式

  1. unsigned long parse_time_stamp (const unsigned char *p)  
  2. {  
  3.     unsigned long b;  
  4.     //共33位,溢位後從0開始
  5.     unsigned long val;  
  6.     //第1個位元組的第5、6、7位
  7.     b = *p++;  
  8.     val = (b & 0x0e) << 29;  
  9.     //第2個位元組的8位和第3個位元組的前7位
  10.     b = (*(p++)) << 8;  
  11.     b += *(p++);  
  12.     val += ((b & 0xfffe) << 14);  
  13.     //第4個位元組的8位和第5個位元組的前7位
  14.     b = (*(p++)) << 8;  
  15.     b += *(p++);  
  16.     val += ((b & 0xfffe) >> 1);  
  17.     return val;  
  18. }  

其他欄位可參考協議解析

ps:

遇到00 00 01 bd的,這個是私有流的標識

ps:

另外,有的hk攝像頭回調然後解讀出來的原始h.264碼流,有的一包裡只有分界符資料(nal_unit_type=9)或補充增強資訊單元(nal_unit_type=6),如果直接送入解碼器,有可能會出現問題,這裡的處理方式要麼丟棄這兩個部分,要麼和之後的資料合起來,再送入解碼器裡,如有遇到的朋友可以交流一下:)

寫在後面:

第一次發原創,在這裡感謝  @cmengwei  的無私幫助,提供了很多幫助,非常感謝。

文件我都放在了我的資源裡面,有1個下載積分,大家不要吝嗇,絕對值得!

《RTP Payload Format for H.264 Video》

《MPEG2-2(13818中文版)》

RTP荷載H264的程式碼參考:

RTP荷載PS流的程式碼參考:

請不要跟我要原始碼,參考我提供的這些,你足以寫出一個可以正常執行的程式。

授人以魚不如授人以漁。


其他參考:

相關推薦

RTP協議解析H264PS

寫在前面:RTP的解析,網上找了很多資料,但是都不全,所以我力圖整理出一個比較全面的解析, 其中借鑑了很多文章,我都列在了文章最後,在此表示感謝。 網際網路的發展離不開大家的無私奉獻,我決定從我做起,希望大家支援。 1、RTP Header解析         

媒體開發: RTP協議解析H264PS

1、RTP Header解析                                                                                                                                      

RTP協議解析H264以及PS荷載

寫在前面:RTP的解析,網上找了很多資料,但是都不全,所以我力圖整理出一個比較全面的解析,其中借鑑了很多文章,我都列在了文章最後,在此表示感謝。網際網路的發展離不開大家的無私奉獻,我決定從我做起,希望大家支援。1、RTP Header解析                  

RTP協議解析H264提取

一、 h264基礎概念SODB: 資料位元串-->最原始的編碼資料RBSP: 原始位元組序列載荷-->在SODB的後面填加了結尾位元(RBSP trailing bits 一個bit“1”)若干位元“0”,以便位元組對齊。EBSP: 擴充套件位元組序列載荷– >在RBS

PHP 常量、PHP 變量解析局變量、變量的8種數據類型等

ret each 回收 操作系統 js xml name static bject 單獨 常量特點 常量一旦被定義就無法更改或撤銷定義。 常量名不需要開頭的$ 與變量不同,常量貫穿整個腳本是自動全局的。 作用域不影響對常量的訪問 常量值只能是字符串或數字 設置 PHP

I3C 總線協議詳細解析第一章

同時 ble 如果 數據 結構 一中 替換 erro 移動設備 目前隨著手機等移動設備包含的sensor越來越多,傳統應用在sensor上的I2C/SPI接口的局限性也越來越明顯,典型的缺陷如下: 1、sensor等設備的增加,對控制總線的速度和功耗提出

DoTwe幸運28平臺搭建下載en解析入門篇

陌生 搭建 今天 入門 tween 幸運 開發人員 方式 開發 DoTween,Itw幸運28平臺搭建下載【征途源碼論壇zhengtuwl.com】聯系方式:QQ:2747044651幸運28平臺搭建下載een,這些名字作為一個Unity開發人員聽起來並不陌生,它們在動畫方

vue源全方位深入解析分享

渲染 未來 font mode 更新 實例 認識 講解 gen 第1章 準備工作介紹了 Flow、Vue.js 的源碼目錄設計、Vue.js 的源碼構建方式,以及從入口開始分析了 Vue.js 的初始化過程。1-1 課程簡介1-2 準備工作1-3 認識 Flow-文檔1-4

Android圖片載入框架最解析,玩轉Glide的回撥與監聽筆記

參考原文:Android圖片載入框架最全解析(四),玩轉Glide的回撥與監聽 回撥的原始碼實現 的Target物件傳入到GenericRequest當中,而Glide在圖片載入完成之後又會回撥GenericRequest的onResourceReady()方法,onReso

Android圖片載入框架最解析,Glide強大的圖片變換功能筆記

參考原文:Android圖片載入框架最全解析(五),Glide強大的圖片變換功能 一個問題 百度這張logo圖片的尺寸只有540258畫素,但是我的手機的解析度卻是10801920畫素,而我們將ImageView的寬高設定的都是wrap_content,那麼圖片的寬度應該只有

Android圖片載入框架最解析,深入探究Glide的快取機制(筆記)

原文地址:Android圖片載入框架最全解析(三),深入探究Glide的快取機制 筆記: 1.Glide快取簡介 2.快取Key EngineKey 重寫了equals()和hashCode()方法,保證只有傳入EngineKey的所有引數都相同的情況下才認為是

Android圖片載入框架最解析,實現帶進度的Glide圖片載入功能筆記

參考原文:Android圖片載入框架最全解析(七),實現帶進度的Glide圖片載入功能 擴充套件目標 對Glide進行功能擴充套件,使其支援監聽圖片下載進度的功能 開始 dependencies { compile 'com.github.bumptech.glid

Android圖片載入框架最解析,探究Glide的自定義模組功能(筆記)

參考原文:Android圖片載入框架最全解析(六),探究Glide的自定義模組功能 自定義模組的基本用法 自定義模組功能可以將更改Glide配置,替換Glide元件等操作獨立出來,使得我們能輕鬆地對Glide的各種配置進行自定義,並且又和Glide的圖片載入邏輯沒有任何交集,

設計模式23種解析5分鐘徹底瞭解23種設計模式

一、設計模式的分類 總體來說設計模式分為三大類: 建立型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。 結構型模式,共七種:介面卡模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。 行為型模式,共十一種:策略模式、模板

Android圖片載入框架最解析,Glide強大的圖片變換功能

                       本文同步發表於我的微信公眾號,掃一掃文章底部的二維碼或在微信搜尋 郭霖 即可關注,每天都有文章更新。大家好,又到了學習Glide的時間了。前段時間由於專案開發緊張,再加上後來又生病了,所以停更了一個月,不過現在終於又可以恢復正常更新了。今天是這個系列的第五篇文章,

第X屆智慧車攝像頭組程式碼解析 ------前言

作者:Sumjess   一、為什麼題目叫第X屆? 因為每年規則都在變,而不變的有很多東西,那什麼在變呢?車模?元素?還不止這些,而我寫這系列部落格的原因是著重寫那些不變的東西,包括PID啊,影象處理啊,等等。這裡我會以一個成熟程式碼作為介質來講透,因為我是第十三屆選手,所

第X屆智慧車攝像頭組程式碼解析------電機PID初始化

作者:Sumjess   本次部落格內容: 該初始化函式下有以下語句: 一、ftm_pwm_init();  ---  電機初始化: 直接呼叫山外的庫函式,該函式內部並無修改。     ftm_pwm

第X屆智慧車攝像頭組程式碼解析------按鍵等其他初始化配置

作者:Sumjess   本次部落格內容: 該初始化函式下有以下語句: 一、init_control_circle();  ---  初始化PIT1和 PIT2:        PIT

第X屆智慧車攝像頭組程式碼解析------初始化配置

作者:Sumjess   本次部落格內容:   該初始化函式下有以下語句: 一、key_init(KEY_MAX);  ---  按鍵初始化: 用在山外的lcd上,直接呼叫山外的庫函式,該函式內部並無修改,截圖如下

第X屆智慧車攝像頭組程式碼解析------補充新建工程

作者:Sumjess   有人可能會問怎麼會突然新添一個新建工程啊~這裡想解釋下為什麼不直接用山外的例程直接開始寫程式碼。因為我個人曾經遇到一個問題,程式下不進去,板子、jlink均無問題,換一個例程下載卻能下進去,我把我自己的程式碼移植到這個例程中後,就又沒有問題了,可是好景