最近工作的一个服务,把ogg格式的音频转成mp3格式,这是个http服务, 源ogg音频地址以参数的形式 传进来,解析后我把它取过来,转换后的二进制 mp3文件在http response中返回。
1. http server部份
server 部份用的是 asio 中 的 server3
2. 关于ogg, speex, mp3
speex和ogg都是由Xiph.Org Foundation 贡献的,speex是一种codec, 一种coding(compression) format, ogg是一种container format(media container),并且在xiph.org上这样说到,This(ogg) is our native format and the recommended container for all Xiph codecs.
ogg programming 文档 http://www.xiph.org/ogg/doc/libogg/index.html , decode有几个固定的步骤,上面有介绍, speex 文档案http://speex.org/docs/ 其中有manul和API reference , manul中有programming介绍,但不够细,像SpeexHeader就要查API reference.
以下是其中核心的代码
1 ogg_sync_state oy;
2 ogg_page og;
3 ogg_packet op;
4 ogg_stream_state os;
5 ogg_int64_t page_granule = 0;
6
7 int frames_per_packet = 0;
8 bool eos = false;
9 int packet_count = 0;
10 //int channels;
11
12 SpeexBits bits;
13 void *st = 0;
14 int frame_size;
15
16 string pcmString;
17 short pcmBuffer[320];
18 const int MP3_SIZE = 2000;
19 unsigned char mp3Buffer[MP3_SIZE];
20 int mp3EncodeSize;
21
22
23 /*Init Ogg data struct*/
24 ogg_sync_init(&oy);
25
26 speex_bits_init(&bits);
27
28 lame_t lame = lame_init();
29 lame_set_quality(lame, 5);
30 lame_set_in_samplerate(lame, 8000);
31 lame_set_VBR(lame, vbr_default);
32 lame_set_num_channels(lame, 1);
33 lame_set_mode(lame, MONO);
34 lame_init_params(lame);
35
36 char *data;
37 data = ogg_sync_buffer(&oy, oggString.size());
38
39 memcpy(data, oggString.c_str(), oggString.size());
40
41 ogg_sync_wrote(&oy, oggString.size());
42
43 bool stream_init = false;
44
45 LOG4CPLUS_DEBUG(access_logger, " ogg decode begin");
46
47 while(ogg_sync_pageout(&oy, &og) == 1){
48 if(!stream_init){
49 ogg_stream_init(&os, ogg_page_serialno(&og));
50 stream_init = true;
51 }
52 ogg_stream_pagein(&os, &og);
53 page_granule = ogg_page_granulepos(&og);
54
55 while(!eos && ogg_stream_packetout(&os, &op) == 1){
56
57 // speex header is in first ogg packet
58 if(packet_count == 0){
59 LOG4CPLUS_DEBUG(access_logger,"@@@@@@@Packet header@@@@@@@");
60 // process header
61 SpeexHeader *header;
62 // create a SpeexHeader from a packet
63 header = speex_packet_to_header((char*)op.packet, op.bytes);
64 if(!header){
65 cout << "process header error" << endl;
66 return -1;
67 }
68
69 // cout << "mode = " << header->mode << endl;
70
71 SpeexMode *mode = speex_lib_get_mode(header->mode);
72
73 st = speex_decoder_init(mode);
74
75 if(!st){
76 cout << "init speex decoder error" << endl;
77 free(header);
78 return -1;
79 }
80 speex_decoder_ctl(st, SPEEX_GET_FRAME_SIZE, &frame_size);
81 LOG4CPLUS_DEBUG(access_logger , "frame size = " << frame_size);
82 speex_decoder_ctl(st, SPEEX_SET_SAMPLING_RATE, &header->rate);
83 LOG4CPLUS_DEBUG(access_logger, "rate = " << header->rate);
84 //if not set in header ,0 here
85 frames_per_packet = header->frames_per_packet;
86 LOG4CPLUS_DEBUG(access_logger, "frames_per_packet(if set in header) = " << frames_per_packet);
87
88 free(header);
89 } else {
90 if(op.e_o_s)
91 eos = true;
92
93 speex_bits_read_from(&bits, (char*)op.packet, op.bytes);
94
95 int loop_count = ((frames_per_packet > 0) ? frames_per_packet : bits.nbBits/frame_size) ;
96 LOG4CPLUS_DEBUG(access_logger, "actual loop count in a packet :" << loop_count);
97 for(int i = 0; i < loop_count; ++i){
98 if(speex_decode_int(st, &bits, pcmBuffer) >= 0){
99 mp3EncodeSize = lame_encode_buffer(lame, pcmBuffer, pcmBuffer, frame_size, mp3Buffer, MP3_SIZE);
100 // cout << "mp3 size = " << mp3EncodeSize << endl;
101 mp3String.append((char *)mp3Buffer, mp3EncodeSize);
102 }
103
104 }
105 }
106 ++packet_count;
107 }
108 }
109 mp3EncodeSize = lame_encode_flush(lame, mp3Buffer, MP3_SIZE);
110 // cout << "mp3 flush size" << mp3EncodeSize << endl;
111 if (mp3EncodeSize > 0){
112 mp3String.append((char *)mp3Buffer, mp3EncodeSize);
113 }
114
115 lame_close(lame);
116
117 speex_bits_destroy(&bits);
118 speex_decoder_destroy(st);
语音的转换在一个handler中进行,用libogg库.