GOLANG简单类型定义,在协议解析的妙用
来源:互联网 发布:win10 mac地址怎么查 编辑:程序博客网 时间:2024/06/08 18:59
原文:https://gocn.io/article/322
在协议解析中,经常需要用到转换不同的含义,比如声音的采样率,在FLV中定义和AAC中定义是不同的。在FLV中只有4中采样率5512, 11025, 22050, 44100
。而在AAC中有16种采样率96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350
(还有4个是保留的)。也就是说,1在FLV中标识11025Hz,而在AAC中表示的是88200Hz。如何实现这个转换呢?
C++当然先得定义枚举:
enum SrsAudioSampleRate{ SrsAudioSampleRate5512 = 0, SrsAudioSampleRate11025, SrsAudioSampleRate22050, SrsAudioSampleRate44100, SrsAudioSampleRateForbidden,};
C++当然是用函数了:
SrsAudioSampleRate aac_to_flv(int v) { if (v >= 0 && v <=5) { return SrsAudioSampleRate44100; } else if (v >=6 && v <= 8) { return SrsAudioSampleRate22050; } else if (v >= 9 && v <= 11) { return SrsAudioSampleRate11025; } else if (v == 12) { return SrsAudioSampleRate5512; } else { return SrsAudioSampleRateForbidden; }}
看起来还是挺简单的。慢着,还有的时候需要打印出采样率来,所以还得搞个函数:
string srs_audio_sample_rate2str(SrsAudioSampleRate v){ switch (v) { case SrsAudioSampleRate5512: return "5512"; case SrsAudioSampleRate11025: return "11025"; case SrsAudioSampleRate22050: return "22050"; case SrsAudioSampleRate44100: return "44100"; default: return "Forbidden"; }}
拿到一个AAC的采样率,然后转换成FLV的,并打印出来,是这么使用的:
// 从文件或者流中读取出AAC的采样率的值。int samplingFrequencyIndex = ...;// 转换成FLV的采样率。SrsAudioSampleRate sampleRate = aac_to_flv(samplingFrequencyIndex);// 转换成字符串格式。string sSampleRate = srs_audio_sample_rate2str(sampleRate);// 打印采样率。printf("SampleRate=%d/%sHz\n", sampleRate, sSampleRate);
有什么麻烦的呢?
- 函数和类型之间没有关系,每次使用的时候都得去翻手册啊翻手册。
- 如果定义成一个struct,那转换的时候又太麻烦了。
还能不能愉快的玩耍呢?用GOLANG吧!先看用法:
var sampleRate AudioSamplingRatesampleRate.From(samplingFrequencyIndex)fmt.Printf("SampleRate=%d/%v\n", sampleRate, sampleRate)
就是这么简单(此处应该有掌声)~
其实实现起来也非常自然:
type AudioSamplingRate uint8const ( AudioSamplingRate5kHz AudioSamplingRate = iota // 0 = 5.5 kHz AudioSamplingRate11kHz // 1 = 11 kHz AudioSamplingRate22kHz // 2 = 22 kHz AudioSamplingRate44kHz // 3 = 44 kHz AudioSamplingRateForbidden)func (v AudioSamplingRate) String() string { switch v { case AudioSamplingRate5kHz: return "5.5kHz" case AudioSamplingRate11kHz: return "11kHz" case AudioSamplingRate22kHz: return "22kHz" case AudioSamplingRate44kHz: return "44kHz" default: return "Forbidden" }}func (v *AudioSamplingRate) From(a int) { switch a { case 0, 1, 2, 3, 4, 5: *v = AudioSamplingRate44kHz case 6, 7, 8: *v = AudioSamplingRate22kHz case 9, 10, 11: *v = AudioSamplingRate11kHz case 12: *v = AudioSamplingRate5kHz default: *v = AudioSamplingRateForbidden }}
Remark: 代码参考commit.
有几个地方非常不同:
- 虽然GOLANG只是在uint8上面加了函数,但是使用起来方便很多了,以前在C++中用这两个枚举,每次都要跳到枚举的定义来看对应的函数是什么。
- GOLANG的switch比较强大,可以case好几个值,和C++的if有点想,但是GOLANG的case更直观,知道这几个值会被转换成另外的值,而if读起来像是将一个范围的值转换,不好懂。
- GOLANG的枚举使用const实现,也可以带类型,而且有个iota很强大,特别是在定义那些移位的枚举时就很好用。
好吧,这只是几个小的改进,虽然用起来很方便。来看看在AMF0中基本类型的妙用,AMF0是一种传输格式,和JSON很像,不过JSON是文本的,而AMF0是字节的,都是用来在网络中传输对象的。因此,AMF0定义了几个基本的类型:String, Number, Boolean, Object,其中Object的属性定义为String的属性名和值,值可以是其他的类型。
先看看C++的实现,首先定义一个AMF0Any对象,可以转换成具体的String或者Object等对象:
class SrsAmf0Any { // 提供转换的函数,获取实际的值。 virtual std::string to_str(); virtual bool to_boolean(); virtual double to_number(); virtual SrsAmf0Object* to_object(); // 当然还得提供判断的函数,得知道是什么类型才能转。 virtual bool is_string(); virtual bool is_boolean(); virtual bool is_number(); virtual bool is_object(); // 提供创建基本类型的函数。 static SrsAmf0Any* str(const char* value = NULL); static SrsAmf0Any* boolean(bool value = false); static SrsAmf0Any* number(double value = 0.0); static SrsAmf0Object* object();};
在实现时,String和Number等基本类型可以隐藏起来(在cpp中实现):
namespace _srs_internal { class SrsAmf0String : public SrsAmf0Any { public: std::string value; // 当然它必须实现编码和解码的函数。 virtual int total_size(); virtual int read(SrsBuffer* stream); virtual int write(SrsBuffer* stream); };}
AMF0Object当然得暴露出来的:
class SrsAmf0Object : public SrsAmf0Any {public: virtual int total_size(); virtual int read(SrsBuffer* stream); virtual int write(SrsBuffer* stream); // 提供设置和读取属性的方法。 virtual void set(std::string key, SrsAmf0Any* value); virtual SrsAmf0Any* get_property(std::string name);};
用起来是这样:
// 设置Object的属性,并发送给服务器。SrsConnectAppPacket* pkt = NULL;pkt->command_object->set("app", SrsAmf0Any::str(app.c_str()));pkt->command_object->set("tcUrl", SrsAmf0Any::str(tcUrl.c_str()));// 读取服务器的响应,取出服务器的IP等信息。SrsConnectAppResPacket* pkt = NULL;SrsAmf0Any* data = pkt->info->get_property("data");if (si && data && data->is_object()) { SrsAmf0Object* obj = data->to_objet(); SrsAmf0Any* prop = obj->get_property("srs_server_ip"); if (prop && prop->is_string()) { printf("Server IP: %s\n", prop->to_str().c_str()); } prop = obj->get_property("srs_pid"); if (prop && prop->is_number()) { printf("Server PID: %d\n, prop->to_number()); }}
看起来巨繁琐吧?快用GOLANG,如果换成GOLANG,可以用基本类型定义AMF0的基本类型,这样使用起来是这样:
pkt := or.NewConnectAppPacket()pkt.CommandObject.Set("tcUrl", amf0.NewString(tcUrl))pkt.CommandObject.Set("app", amf0.NewString(app))var res *or.ConnectAppResPacketif data, ok := res.Args.Get("data").(*amf0.Object); ok { if data, ok := data.Get("srs_server_ip").(*amf0.String); ok { fmt.Printf("Server IP: %s\n", string(*data)) } if data, ok := data.Get("srs_pid").(*amf0.Number); ok { fmt.Printf("Server PID: %d\n, int(*data)) }}
区别在于:
- C++由于不能在基本类型上定义方法,导致必须创建struct或者class类型,有比较繁琐的类型转换和判断。
- GOLANG的类型判断,提供了ok的方式,一句话就能把类型转换弄好,而且接口和实现struct的对象可以重用变量名。
- 不必加很多类型判断,没有多余的变量,干净利索,需要维护的信息比较少。
爱生活,爱够浪(此处可以响起掌声了)~
- GOLANG简单类型定义,在协议解析的妙用
- Golang unsafe的妙用
- goLang slice 类型的简单应用
- 宏定义的妙用
- 宏定义的妙用
- 宏定义的妙用
- about协议的妙用
- golang的xml解析
- KVC在定义Model类中的妙用
- KVC在定义Model类中的妙用
- KVC在定义Model类中的妙用
- golang定义错误的方式
- TypeScript 类型映射的妙用:
- golang简单的抓取
- [Golang]妙用channel
- [Golang]妙用channel(2)
- goLang中的基础类型简单应用
- golang-redis之string类型简单操作
- 2017.5.11工作随笔
- 黄聪:C# 写Excel 代码
- GRUB中硬盘和分区编号,UUID
- intellij cannot resolve method问题解决
- Android获取CPU使用率
- GOLANG简单类型定义,在协议解析的妙用
- 在MQTT服务器mosquitto上使用SSL/TSL
- 简单的生产者消费者例子
- POJ3256 Cow Picnic DFS搜索
- 分布式锁的几种实现方式
- [Language]Python中错误与异常
- 面向对象的三大特性之多态
- iOS
- 安卓开发:数字文字选择器