现在的位置: 首页 > 综合 > 正文

JRTPLIB@Conference DIY视频会议系统 六、G.711编码事例程序

2019年03月21日 ⁄ 综合 ⁄ 共 12616字 ⁄ 字号 评论关闭

      这是JRTPLIB@Conference

列的第六部《G.711编码事例程序》,本系列的主要工作是实现一个基于JRTPLIB的,建立在RTP组播基础上的多媒体视频会议系统。这只是一个实验
系统,用于学习JRTPLIB、RTP、和多媒体相关的编程,不是一个完善的软件工程。而且,我只会在业余的时间出于兴趣写一写。有志同道合的朋友可以通
tinnal@136.com
这个邮箱或博客回复(推荐)和我交流。
      上一部《JRTPLIB@Conference DIY视频会议系统 五、PCM 和G.711编码相关》
      这
一部我们来做个实验,就是把用windows录音机录下来的"PCM 8.000 kHz, 16 位,
单声道"WAV文件转换成为我们要用的8位8000Hz a-law格式PCM。要注意的是录音机默认的方式是PCM 44.100 kHz, 16
位,
立体声,我们不想去进行采样频率的更改,因为这个要进行插值,而且也没必要,因为我们写软件时采样频率我们是可以更改的。所以我们要先把录音另为"PCM
8.000 kHz, 16 位, 单声道"格式。

一、WAV
格式



      

虽然会议系统完成后我们能直接向声卡拿到PCM
数据,但毕竟我们现在拿到手的是WAV
文件,我们要识别这种格式的头文件。下面是一编转自其它网站的《WAV
格式详解》(有一定修改)



1、综述


    WAVE

文件作为多媒体中使用的声波文件格式之一,它是以RIFF
格式为标准的。RIFF
是英文Resource Interchange File Format
的缩写,每个WAVE
文件的头四个字节便是“RIFF”


    WAVE

文件是由若干个Chunk
组成的。按照在文件中的出现位置包括:RIFF WAVE Chunk, Format Chunk, Fact Chunk(
可选), Data Chunk
。具体见下图:

------------------------------------------------
|             RIFF WAVE Chunk                  |
|             ID  = 'RIFF'                     |
|             RiffType = 'WAVE'                |
------------------------------------------------
|             Format Chunk                     |
|             ID = 'fmt '                      |
------------------------------------------------
|             Fact Chunk(optional)             |
|             ID = 'fact'                      |
------------------------------------------------
|             Data Chunk                       |
|             ID = 'data'                      |
------------------------------------------------
           

1   Wav
格式包含Chunk
示例

   
其中除了Fact Chunk
外,其他三个Chunk
是必须的。每个Chunk
有各自的ID
,位于Chunk
最开始位置,作为标示,而且均为4
个字节。并且紧跟在ID
后面的是Chunk
大小(去除ID
Size
所占的字节数后剩下的其他字节数目),4
个字节表示,低字节表示数值低位,高字节表示数值高位。下面具体介绍各个Chunk
内容。


PS



   

所有数值表示均为低字节表示低位,高字节表示高位。

2、具体介绍


RIFF WAVE Chunk
    ==================================
    |       |


所占字节数
具体内容

   |
    ==================================
    | ID    |  4 Bytes |   'RIFF'    |
    ----------------------------------
    | Size  |  4 Bytes |             |
    ----------------------------------
    | Type  |  4 Bytes |   'WAVE'    |
    ----------------------------------
           

2  RIFF WAVE Chunk

   
'FIFF'
作为标示,然后紧跟着为size
字段,该size
是整个wav
文件大小减去ID
Size
所占用的字节数,即FileLen - 8 = Size
。然后是Type
字段,为'WAVE'
,表示是wav
文件。



Format Chunk
    ====================================================================
    |               |  

字节数  |             
具体内容

                |
    ====================================================================
    | ID            |  4 Bytes  |   'fmt '                             |
    --------------------------------------------------------------------
    | Size          |  4 Bytes  |

数值为16
18
18
则最后又附加信息

     |
    --------------------------------------------------------------------  ----
    | FormatTag     |  2 Bytes  |

编码方式,一般为

0x0001               |     |
    --------------------------------------------------------------------     |
    | Channels      |  2 Bytes  |

声道数目,1--
单声道;2--
双声道

       |     |
    --------------------------------------------------------------------     |
    | SamplesPerSec |  4 Bytes  |

采样频率

                             |     |
    --------------------------------------------------------------------     |
    | AvgBytesPerSec|  4 Bytes  |

每秒所需字节数

                       |     |===> WAVE_FORMAT
    --------------------------------------------------------------------     |
    | BlockAlign    |  2 Bytes  |

数据块对齐单位(
每个采样需要的字节数

) |     |
    --------------------------------------------------------------------     |
    | BitsPerSample |  2 Bytes  |

每个采样需要的bit

                  |     |
    --------------------------------------------------------------------     |
    |               |  2 Bytes  |

附加信息(可选,通过Size
来判断有无)

|     |
    --------------------------------------------------------------------  ----
                           

3  Format Chunk

   
'fmt '
作为标示。一般情况下Size
16
,此时最后附加信息没有;如果为18
,则最后多了2
个字节的附加信息。主要由一些软件制成的wav
格式中含有该2
个字节的附加信息。




Fact Chunk
    ==================================
    |       |


所占字节数
具体内容

   |
    ==================================
    | ID    |  4 Bytes |   'fact'    |
    ----------------------------------
    | Size  |  4 Bytes |  

数值为

4   |
    ----------------------------------
    | data  |  4 Bytes |             |
    ----------------------------------
           

4  Fact Chunk

    Fact Chunk
是可选字段,一般当wav
文件由某些软件转化而成,则包含该Chunk



 

Data Chunk
    ==================================
    |       |

所占字节数
具体内容

   |
    ==================================
    | ID    |  4 Bytes |   'data'    |
    ----------------------------------
    | Size  |  4 Bytes |             |
    ----------------------------------
    | data  |          |             |
    ----------------------------------
            

5 Data Chunk

    Data Chunk
是真正保存wav
数据的地方,以'data'
作为该Chunk
的标示。然后是数据的大小。紧接着就是wav
数据。根据Format Chunk
中的声道数以及采样bit
数,wav
数据的bit
位置可以分成以下几种形式:


    ---------------------------------------------------------------------
    |  


单声道  |   
取样1    |   
取样2    |   
取样3    |   
取样

4    |
    |           |--------------------------------------------------------
    |  8bit

量化 |   
声道0    |   
声道0    |   
声道0    |   
声道

0    |
    ---------------------------------------------------------------------
    |  

双声道  |         
取样1            |          
取样

2           |
    |           |--------------------------------------------------------
    |  8bit

量化
声道0(
)  | 
声道1(
)  | 
声道0(
)  | 
声道1(

)  |
    ---------------------------------------------------------------------
    |           |         

取样1            |          
取样

2           |
    |  

单声道

  |--------------------------------------------------------
    | 16bit

量化 |   
声道0    | 
声道0      |   
声道0    | 
声道

0      |
    |           | (

低位字节)  | (
高位字节)  | (
低位字节)  | (
高位字节

)  |
    ---------------------------------------------------------------------
    |           |                        

取样

1                         |
    |  

双声道

  |--------------------------------------------------------
    | 16bit

量化
声道0(
)  | 
声道0(
)  | 
声道1(
)  | 
声道1(

)  |
    |           | (

低位字节)  | (
高位字节)  | (
低位字节)  | (
高位字节

)  |
    ---------------------------------------------------------------------
                        

6 wav
数据bit
位置安排方式

3、小结


   

因此,根据上述结构定义以及格式介绍,很容易编写相应的wav
格式解析代码。这里具体的代码就不给出了。 


二、代码的实现
 

      根据上面的格式规定,我们把它写成一头文件wav.h

 1
#ifndef _WAV_H_

 2

#define
 _WAV_H_


 3


 4

#include 

"
types.h
"


 5


 6

#pragma pack(

1
)

 7


 8

struct
 RIFF_HEADER

 9


{

10

    U8        szRiffID[

4
];  
//
 'R','I','F','F'


11

    U32        dwRiffSize;

12

    U8        szRiffFormat[

4
]; 
//
 'W','A','V','E'


13

}

;

14


15

struct
 WAVE_FORMAT

16


{

17

    U16        wFormatTag;

18

    U16        wChannels;

19

    U32        dwSamplesPerSec;

20

    U32        dwAvgBytesPerSec;

21

    U16        wBlockAlign;

22

    U16        wBitsPerSample;

23

    U16        pack;        

//
附加信息


24

}

;

25

struct
 FMT_BLOCK

26


{

27

    U8        szFmtID[

4
]; 
//
 'f','m','t',' '


28

    U32        dwFmtSize;

29

    

struct
    WAVE_FORMAT wavFormat;

30

}


;

31


32

struct
 FACT_BLOCK

33


{

34

    U8        szFactID[

4
]; 
//
 'f','a','c','t'


35

    U32        dwFactSize;

36

}


;

37


38

struct
 DATA_BLOCK

39


{

40

    U8        szDataID[

4
]; 
//
 'd','a','t','a'


41

    U32        dwDataSize;

42

}


;

43


44


45

#endif

      因为这是个简单的程序,我没有去规划,相就的WAV解码过程我放到main.c的main函数里做了,这是不应该的,请原谅

  1

/*
******************************************************

  2

*    这是配合我的博客《JRTPLIB@Conference DIY视频会议系统》

  3

*    而写的一个阶段性实验。

  4

*    作者:冯富秋 tinnal

  5

*    邮箱:tinnal@163.com

  6

*    博客:www.cnitblog.com/tinnal/

  7

*    目期:2009-01-03

  8

*    版本:1.00

  9

********************************************************

*/



 10


 11

#include 

"
stdio.h
"


 12

#include 

"
string.h
"


 13

#include 

"
types.h
"


 14

#include 

"
g711.h
"


 15

#include 

"
wav.h
"


 16


 17

struct
 RIFF_HEADER    riff_header;

 18

struct
 FMT_BLOCK    fmt_block;

 19

char
   fack_block_buffer[
20
];        
//
20 should be enough


 20

struct
 FACT_BLOCK    fact_block;

 21

struct
 DATA_BLOCK    data_block;

 22


 23

int
 main(
int
 argc, 
char
 
**
argv)

 24


{

 25

    FILE 

*
wav_in;

 26

    FILE 

*
wav_out;

 27

    U32 i;

 28

    U8    has_fact_block 

=
0
;

 29


 30

    unsigned 

char
 pcm_bytes[
2
];

 31

    

short
 pcm;

 32

    unsigned 

char
 a_law;    

 33


 34

    

long
 file_pos;

 35


 36

    

if
(argc 
!=
 
3
 )

 37

    


{

 38

        printf(

"
Usage:/n/t%s <intput file> <output file>/n
"
, argv[
0
]);

 39

        exit(

-
1
);

 40

    }




 41


 42

    wav_in 

=
 fopen(argv[
1
],
"
rb
"
);

 43

    

if
(wav_in 
==
 NULL)

 44

    


{

 45

        printf(

"
Can't open input file %s/n
"
, argv[
1
]);

 46

        

return
 (
-
1
);

 47

    }




 48


 49

    wav_out 

=
 fopen(argv[
2
], 
"
wb
"
);

 50

    

if
( wav_out 
==
 NULL)

 51

    


{

 52

        printf(

"
Can't open output file %s/n
"
,argv[
2
]);

 53

        fclose(wav_in);

 54

        

return
(
-
1
);

 55

    }




 56

    

 57

    file_pos 

=
 ftell(wav_in);

 58


 59

    

//
Read RIFF_HEADER


 60

    fread(
&
riff_header, 
sizeof
(
struct
 RIFF_HEADER), 
1
, wav_in);

 61

    

if
(    memcmp(riff_header.szRiffID, 
"
RIFF
"

4

!=
 
0
 
||


 62

        memcmp(riff_header.szRiffFormat, 

"
WAVE
"

4

!=
 
0
 )

 63

    


{

 64

        printf(

"
No a vaild wave file!/n
"
);

 65

        fclose(wav_in);

 66

        fclose(wav_out);

 67

        

return
(
-
1
);

 68

    }




 69

    file_pos 

=
 ftell(wav_in);

 70


 71

    

//
Read FMT_BLOCK


 72

    fread(
&
fmt_block, 
sizeof
(
struct
 FMT_BLOCK), 
1
, wav_in);

 73

    

if
(    memcmp(fmt_block.szFmtID, 
"
fmt 
"

4

!=
0
 
||


 74

        fmt_block.dwFmtSize 

!=
 
18
 
||


 75

        fmt_block.wavFormat.wFormatTag 

!=
 
0x1
 
||


 76

        fmt_block.wavFormat.wChannels 

!=
 
0x1
 
||


 77

        fmt_block.wavFormat.dwSamplesPerSec 

!=
 
8000
 
||


 78

        fmt_block.wavFormat.wBitsPerSample 

!=
 
16
)

 79

    


{

 80

        printf(

"
Sorry this is only test program,/n
"


 81

            

"
we only support follow format,/n
"


 82

            

"
/t 1. Format:        linear PCM /n
"


 83

            

"
/t 2. Samples Rate:  8000 KHz /n
"


 84

            

"
/t 3. Channels:      one channel /n
"


 85

            

"
/t 4. BitsPerSample: 16 /n
"
);

 86

        fclose(wav_in);

 87

        fclose(wav_out);

 88

        

return
(
-
1
);

 89

    }




 90

    

 91

    file_pos 

=
 ftell(wav_in);

 92


 93

    

//
Try to read FACT_BLOCK


 94

    file_pos 
=
 ftell(wav_in);

 95

    fread(

&
fact_block, 
sizeof
(
struct
 FACT_BLOCK), 
1
, wav_in);

 96

    

if
( memcmp(fact_block.szFactID, 
"
fact
"

4

==
 
0
 )

 97

    


{    

 98

        has_fact_block 

=
1
;

 99

        fread(

&
fack_block_buffer, fact_block.dwFactSize, 
1
, wav_in);

100

    }




101

    

else


102

        fseek(wav_in, file_pos, SEEK_SET);

103

    

104

    fread(

&
data_block, 
sizeof
(
struct
 DATA_BLOCK), 
1
, wav_in);

105

    

if
 (memcmp(data_block.szDataID, 
"
data
"

4

!=
 
0
)

106

    


{

107

        printf(

"
OOh what error?/n
"
);

108

        fclose(wav_in);

109

        fclose(wav_out);

110

        

return
(
-
1
);

111

    }




112


113

    

//
Change the wave header to write


114

    riff_header.dwRiffSize                    
-=
    data_block.dwDataSize
/
2
 ;

115

    

116

    fmt_block.wavFormat.wFormatTag            

=
    
0x06
;

117

    fmt_block.wavFormat.wChannels            

=
    
0x01
;

118

    fmt_block.wavFormat.dwSamplesPerSec        

=
    
8000
;

119

    fmt_block.wavFormat.dwAvgBytesPerSec    

=
    
8000
;

120

    fmt_block.wavFormat.wBlockAlign            

=
    
0x01
;

121

    fmt_block.wavFormat.wBitsPerSample        

=
    
0x08
;

122


123

    data_block.dwDataSize                    

-=
    data_block.dwDataSize
/
2
 ;

124


125

    

//
Write wave file header


126

    fwrite(
&
riff_header, 
sizeof
(
struct
 RIFF_HEADER), 
1
, wav_out);

127

    fwrite(

&
fmt_block, 
sizeof
(
struct
 FMT_BLOCK), 
1
, wav_out);

128

    

if
(has_fact_block 
==
 
1


129

    


{

130

        fwrite(

&
fact_block, 
sizeof
(
struct
 FACT_BLOCK), 
1
, wav_out);

131

        fwrite(

&
fack_block_buffer, fact_block.dwFactSize, 
1
, wav_out);

132

    }




133

    fwrite(

&
data_block, 
sizeof
(
struct
 DATA_BLOCK), 
1
, wav_out);

134


135

    

//
Convert pcm data to a-low data  and  write wav file.


136

    
for
(i 
=
0
; i
<
 data_block.dwDataSize; i
++
)

137

    


{

138

        pcm_bytes[

0

=
 (U8) fgetc(wav_in);

139

        pcm_bytes[

1

=
 (U8) fgetc(wav_in);

140

        pcm 

=
 
*
(
short
 
*
)
&
pcm_bytes;

141


142

        a_law     

=
 ALawEncode((
int
)pcm);

143

//
        a_law     = linear2alaw((int)pcm);


144

        fputc(a_law, wav_out);

145

    }




146

    fclose(wav_in);

147

    fclose(wav_out);

148

    

149

    printf(

"
Finish!/n
"
);

150

    

return
 
0
;

151

}




152

      整个文件基本都是在为WAV文件格式服务而非我们的核心工作--G.711编码。唉~,我也不想。这里在面进行G.711编码的就是
ALawEncode函数。这个函数定义在g711.c里件里,这个文件函数一些我认为比较有用的函数。我们这是只把ALawEncode这个函数拿出
来。 

 1
//

省略的代码



 2

unsigned 
char
 ALawEncode(
int
 pcm16)

 3


{

 4

    

int
 p 
=
 pcm16;

 5

    unsigned a;  

//
 A-law value we are forming


 6

    
if
(p
<
0
)

 7

    


{

 8

        

//
 -ve value

 9

        

//
 Note, ones compliment is used here as this keeps encoding symetrical

10

        

//
 and equal spaced around zero cross-over, (it also matches the standard).


11

        p 
=
 
~
p;

12

        a 

=
 
0x00

//
 sign = 0


13

    }



14

    

else


15

    


{

16

        

//
 +ve value


17

        a 
=
 
0x80

//
 sign = 1


18

    }



19

    

20

    

//
 Calculate segment and interval numbers


21

    p 
>>=
 
4
;

22

    

if
(p
>=
0x20
)

23

    


{

24

        

if
(p
>=
0x100
)

25

        


{

26

            p 

>>=
 
4
;

27

            a 

+=
 
0x40
;

28

        }




29

        

if
(p
>=
0x40
)

30

        


{

31

            p 

>>=
 
2
;

32

            a 

+=
 
0x20
;

33

        }




34

        

if
(p
>=
0x20
)

35

        


{

36

            p 

>>=
 
1
;

37

            a 

+=
 
0x10
;

38

        }




39

    }




40

    

//
 a&0x70 now holds segment value and 'p' the interval number


41

    

42

    a 

+=
 p;  
//
 a now equal to encoded A-law value


43

    

44

    

return
 a
^
0x55
;    
//
 A-law has alternate bits inverted for transmission


45

}



46

//

省略的代码

47

      哈哈,前一部说了这么多,其实G711编码也只是很简单的
。当然,不然VOIP怎么把它变的每个软件的必要品。

      完整的程序可以从下面的链接下载:PCM2ALaw.ra

抱歉!评论已关闭.