zhangpeihao / gortmp

Implement RTMP protocol by golang
MIT License
523 stars 162 forks source link

defines.go文件中GetTimestamp()函数的问题 #27

Open programmerZhou opened 7 years ago

programmerZhou commented 7 years ago
  1. 下面rtmp_specification_1.0.pdf文件中关于timestamp的描述。

Because timestamps are 32 bits long, they roll over every 49 days, 17 hours, 2 minutes and 47.296 seconds. Because streams are allowed to run continuously, potentially for years on end, an RTMP application SHOULD use serial number arithmetic [RFC1982] when processing timestamps, and SHOULD be capable of handling wraparound. For example, an application assumes that all adjacent timestamps are within 2^31 - 1 milliseconds of each other, so 10000 comes after 4000000000, and 3000000000 comes before 4000000000.

上面的描述说明了的timestamp是32的无符号整数所表示的毫秒,由于该值最大只能表示不到50天的时间戳,所以需要对时间戳做环绕式处理。

  1. defines.go文件中GetTimestamp的实现
466 // Get timestamp
467 func GetTimestamp() uint32 {
468     //return uint32(0)
469     return uint32(time.Now().UnixNano()/int64(1000000)) % MAX_TIMESTAMP
470 }

该函数取系统当前时间(毫秒)作为时间戳,然后与 MAX_TIMESTAMP取模,这样也确实实现的环绕处理, 但是我看到MAX_TIMESTAMP的定义如下:

278     MAX_TIMESTAMP                       = uint32(2000000000)  

正确的定义该值应该是32位无符号整数的最大值+1, 即0x100000000,defines.go文件对该值的定义的显然不对。

  1. defines.go文件的第469行有一种更简单的改写方式, 如下:
469     return uint32(time.Now().UnixNano()/int64(1000000))

超过32位无符号整数的部分会被自然溢出,我们强制转换得到溢出后剩下的部分,即实现了环绕,也相当于对0x100000000做取模操作。

hy05190134 commented 7 years ago

@programmerZhou 你的意思不取余反而是正确的?

programmerZhou commented 7 years ago

@hy05190134 强转让其溢出即是取模。

return uint32(time.Now().UnixNano()/int64(1000000))
return uint32(time.Now().UnixNano()/int64(1000000) % 0x100000000)

上面这两行等价。

类似的写法还有

// 对一个数除2 
a := x / 2 
// 也可写成 
a = x >> 1

上面的问题主要是MAX_TIMESTAMP定义的不对,应该是0x100000000, 时间戳是环绕的,达到最大值之后,下一个值为0,即0xffffffff的下一个值为0。

注:位运算一般效率更高。

zhangpeihao commented 7 years ago

这个需要实测,spec里面写的有些不准确,所以设置了一个最大支持的时间范围。