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(42b)和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; //有效位數為9位,即1+8
long long pcr_high = pcr / 300LL; //有效位數為33位,即8+8+8+8+1
unsigned char pcr_val[6]; // 待寫到ts文件中pcr位置的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文件算出音視頻幀的時間戳)(下式可能發生溢出,demo中有詳細的避免溢出處理)
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; // 3bits
pts_val[1] = (pts >> 22) & 0xff; // 8bits
pts_val[2] = (((pts >> 15) & 0x7f) << 1) | 1; // 7bits
pts_val[3] = (pts >> 7) & 0xff; // 8bits
pts_val[4] = ((pts & 0x7f) << 1) | 1; // 7bits
逆向運算: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 /* test max pcr_val */ 25 //pcr_val[0] = 0xff; 26 //pcr_val[1] = 0xff; 27 //pcr_val[2] = 0xff; 28 //pcr_val[3] = 0xff; 29 //pcr_val[4] = 0xff; 30 //pcr_val[5] = 0x2b; 31 long long PCR_LOW = (unsigned)pcr_val[5] + (((unsigned)pcr_val[4]&0x1)<<8); 32 long long PCR_HIGH = ((unsigned)pcr_val[4]>>7) + ((unsigned)pcr_val[3]<<1) + ((unsigned)pcr_val[2]<<9) + ((unsigned)pcr_val[1]<<17) + ((unsigned long long)pcr_val[0]<<25); 33 long long PCR = PCR_HIGH * 300LL + PCR_LOW; 34 float TMS = PCR / 27000000.0; 35 printf("revert test by 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]); 36 printf(" PCR_LOW=%#llx, PCR_HIGH=%#llx, PCR=%#llx, TMS=%f s\n", PCR_LOW, PCR_HIGH, PCR, TMS); 37 38 printf("---------------------------------------------------------------------------\n"); 39 printf("calculate pts by timeUs!\n"); 40 long long pts = timeUs * 9LL / 100LL; 41 unsigned char pts_val[5]; 42 pts_val[0] = 0x20 | (((pts >> 30) & 7) << 1) | 1; 43 pts_val[1] = (pts >> 22) & 0xff; 44 pts_val[2] = (((pts >> 15) & 0x7f) << 1) | 1; 45 pts_val[3] = (pts >> 7) & 0xff; 46 pts_val[4] = ((pts & 0x7f) << 1) | 1; 47 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]); 48 49 /* test max pts_val */ 50 //pts_val[0] = 0x2f; 51 //pts_val[1] = 0xff; 52 //pts_val[2] = 0xff; 53 //pts_val[3] = 0xff; 54 //pts_val[4] = 0xff; 55 printf("revert test by 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]); 56 long long PTS = ((unsigned)pts_val[4]>>1) | ((unsigned)pts_val[3]<<7) | ((unsigned)pts_val[2]>>1)<<15 | (unsigned)pts_val[1]<<22 | ((unsigned long long)(pts_val[0]&0x0E)>>1)<<30; 57 long long TIMEUS = PTS * 100LL / 9LL; 58 printf(" PTS=%lld, TIMEUS=%lld us\n", PTS, TIMEUS); 59 60 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]); 61 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; 62 long long TIMEUS1 = PTS1 * 100LL / 9LL; 63 printf(" PTS1=%lld, TIMEUS1=%lld us\n", PTS1, TIMEUS1); 64 65 return 0; 66 }
6. 延伸
6.1 ts文件中音視頻幀的最大時間戳是多少?
當最大時,即意味着文件中pcr_val[0-5]或pts_val[0-4]處的二進制位值都為1。
方案1.從pcr_val[0-5]側來計算
由於pcr_low是被300求余的,則其最大值為299=0x12B,那么pcr_va[0-5]數組的值分別為:0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 2B
由此,可以算得TMS最大值為95443.718750 s = 26.5h
利用上面demo,逆向計算結果如下:
方案2.從pts_val[0-4]來計算
由於pts為33bits寬(3+8+7+8+7),則其最大值為:0x1 FF FF FF FF(對應文件中pcr_val[]為:0x2F FF FF FF FF),算得timeUs=pts_max * 100L / 9LL = 95443717666 us = 95443 s = 26.5h
利用上面demo,逆向計算結果如下:
結論:兩個層面,計算結果都一致,即最大的timeUs=95443000000。這意味着,timeUs從0開始,到1天再過2.5h后,就會發生時間戳溢出。