ts流中的pcr與pts計算與逆運算

  mpeg2ts文件格式中有pcr和pts的概念,其程式碼含義如下:

  PCR(Program Clock Reference)——指示系統時鐘本身的瞬時值的時間標籤稱為節目參考時鐘標籤(PCR)。

  PTS(Presentation Time Stamp)——指示音影片顯示時間的時間戳稱為顯示時間戳(PTS)。

  二者的更具體含義可以網上查找資料,本博文的重點不再於此。本博文主題為:利用編碼幀bitstream所攜帶的時間戳,如何換算出ts文件中的pcr和pts值。

1. 一段典型的音影片ts數據包:

  AAC: 47 41 E1 3F 07 10 00 F9 F2 B6 FE B3 00 00 01 C0 01 6A 84 80 05 21 07 CF CA DB 。。。(bitstream)。。。

  AVC: 47 41 E2 3D 07 10 00 F9 F2 B6 FE B3 00 00 01 E0 98 65 84 80 05 21 07 CF CA DB 。。。(bitstream)。。。

  紅色六個位元組為pcr值(188Bytes的TS數據包的第7個位元組),粉紅色四個半位元組為pts值(頭的尾部,後面直接跟編碼數據)。

2. pcr及pts演算法介紹(pcr_val(48b)和pts[32..30..0](33b))

  兩段典型的示例如下:

  時間戳為00:06:04.013(timsUs=364013000)的一幀數據,其pcr的值:00 F9 F2 A9 7E 0D ,其pts[32…0]值為:21 07 CF CA A5
  時間戳為00:06:04.034(timsUs=364034000)的一幀數據,其pcr的值:00 F9 F6 59 FE CF ,其pts[32…0]值為:21 07 CF D9 69

  其中,編碼模組送給封裝模組(Android中為MPEG2TSWriter)的編碼幀所攜帶的時間戳並不是「00:06:04.013」形式,而是一個long long型的整數,單位為微秒,例如364013000(代表364.013秒),換算成人容易識別的格式(hh:mm:ss)為:00:06:04.013。

  注意,ts在封裝時,標準規定了pcr值和pts各個位如何排布(參考下面的pcr_val和pts_val數組,即符合spec規定)。

3. timeUs和pcr之間轉換

  正向運算:timeUs -> pcr (用於封裝)

    float tmp = timeUs / 1000000 + (timeUs % 1000000) / 1000 * 0.001; //timeUs以us為單位,tmp以s為單位,即換算成xy.z格式(小數點放在倒數第六個位元組前),xy為秒值,z為毫秒值
    long long pcr = (long long)(tmp * 27000000.0);
    long long pcr_low = pcr % 300LL;

    long long pcr_high = pcr / 300LL;

    unsigned char pcr_val[6]; //待寫到ts文件中的6個位元組值

    pcr_val[0] = pcr_high>>25;
    pcr_val[1] = pcr_high>>17;
    pcr_val[2] = pcr_high>>9;
    pcr_val[3] = pcr_high>>1;
    pcr_val[4] = pcr_high<<7 | pcr_low>>8 | 0x7e;
    pcr_val[5] = pcr_low;

  逆向運算:pcr -> timeUs (用於解封裝時獲取時間戳,由已知ts文件算出音影片幀的時間戳)

    long long PCR_LOW = pcr_val[5] + ((pcr_val[4]&0x1)<<8);
    long long PCR_HIGH = (pcr_val[4]>>7) + (pcr_val[3]<<1) + (pcr_val[2]<<9) + (pcr_val[1]<<17) + (pcr_val[0]<<25);
    long long PCR = PCR_HIGH * 300 + PCR_LOW;
    float TMS = PCR / 27000000.0;

4. timeUs和pts之間的轉換

  正向運算:timeUs -> pts

    long long pts = timeUs * 9LL / 100LL;
    unsigned char pts_val[5];
    pts_val[0] = 0x20 | (((pts >> 30) & 7) << 1) | 1;
    pts_val[1] = (pts >> 22) & 0xff;
    pts_val[2] = (((pts >> 15) & 0x7f) << 1) | 1;
    pts_val[3] = (pts >> 7) & 0xff;
    pts_val[4] = ((pts & 0x7f) << 1) | 1;

  逆向運算:pts -> timeUs

    long long PTS = (pts_val[4]>>1) | (pts_val[3]<<7) | (pts_val[2]>>1)<<15 | pts_val[1]<<22 | (pts_val[0]>>1)<<30;
    long long TIMEUS = PTS * 100LL / 9LL;

5. 完整demo

 1 #include <stdio.h>
 2 
 3 int main(void)
 4 {
 5     long long timeUs = 364034000;
 6 
 7     float tm = timeUs / 1000000 + (timeUs % 1000000) / 1000 * 0.001;
 8     long long pcr = (long long)(tm * 27000000.0);
 9     long long pcr_low = pcr % 300LL;
10     long long pcr_high = pcr / 300LL;
11 
12     unsigned char pcr_val[6];
13     pcr_val[0] = pcr_high>>25;
14     pcr_val[1] = pcr_high>>17;
15     pcr_val[2] = pcr_high>>9;
16     pcr_val[3] = pcr_high>>1;
17     pcr_val[4] = pcr_high<<7 | pcr_low>>8 | 0x7e;
18     pcr_val[5] = pcr_low;
19     printf("calculate pcr by timeUs!\n");
20     printf("    timeUs=%lld us, tm=%f s\n", timeUs, tm);
21     printf("    pcr=%llx, pcr_low=%#llx, pcr_high=%#llx\n", pcr, pcr_low, pcr_high);
22     printf("    pcr_val[0-5]: %#x, %#x, %#x, %#x, %#x, %#x\n", pcr_val[0], pcr_val[1], pcr_val[2], pcr_val[3], pcr_val[4], pcr_val[5]);
23 
24     long long PCR_LOW = pcr_val[5] + ((pcr_val[4]&0x1)<<8);
25     long long PCR_HIGH = (pcr_val[4]>>7) + (pcr_val[3]<<1) + (pcr_val[2]<<9) + (pcr_val[1]<<17) + (pcr_val[0]<<25);
26     long long PCR = PCR_HIGH * 300 + PCR_LOW;
27     float TMS = PCR / 27000000.0;
28     printf("revert test by pcr_val[0-5]!\n");
29     printf("    PCR_LOW=%#llx, PCR_HIGH=%#llx, PCR=%#llx, TMS=%f s\n", PCR_LOW, PCR_HIGH, PCR, TMS);
30 
31     printf("---------------------------------------------------------------------------\n");
32     printf("calculate pts by timeUs!\n");
33     long long pts = timeUs * 9LL / 100LL;
34     unsigned char pts_val[5];
35     pts_val[0] = 0x20 | (((pts >> 30) & 7) << 1) | 1;
36     pts_val[1] = (pts >> 22) & 0xff;
37     pts_val[2] = (((pts >> 15) & 0x7f) << 1) | 1;
38     pts_val[3] = (pts >> 7) & 0xff;
39     pts_val[4] = ((pts & 0x7f) << 1) | 1;
40     printf("    pts_val[0-4]: %#x, %#x, %#x, %#x, %#x\n", pts_val[0], pts_val[1], pts_val[2], pts_val[3], pts_val[4]);
41 
42     printf("revert test by pts_val[0-4]!\n");
43     long long PTS = (pts_val[4]>>1) | (pts_val[3]<<7) | (pts_val[2]>>1)<<15 | pts_val[1]<<22 | (pts_val[0]>>1)<<30;
44     long long TIMEUS = PTS * 100LL / 9LL;
45     printf("    PTS=%lld, TIMEUS=%lld\n", PTS, TIMEUS);
46 
47     long long PTS_VAL = (1LL * pts_val[0]<<32) + (1LL * pts_val[1]<<24) + (1LL * pts_val[2]<<16) + (1LL * pts_val[3]<<8) + (1LL * pts_val[4]);
48     long long PTS1 = ((PTS_VAL>>1)&0x7f) | ((PTS_VAL>>8)&0xff)<<7 | ((PTS_VAL>>(16+1))&0x7f)<<15 | ((PTS_VAL>>24)&0xff)<<22 | ((PTS_VAL>>(32+1))&7)<<30;
49     long long TIMEUS1 = PTS1 * 100LL / 9LL;
50     printf("    PTS1=%lld, TIMEUS1=%lld\n", PTS1, TIMEUS1);
51 
52     return 0;
53 }

View Code

  運行結果: