根据近期项目中应用需要,需要将rtp协议承载的 amr(8kHZ)媒体流,转换成pcm格式音频流并以udp协议发送出去。ffmpeg强大的媒体处理功能,再次得到了淋漓尽致的体现,不多说了,直接上代码,记录一下
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <windows.h>
#include <sys/stat.h>
#include <process.h>
#include <time.h>
//_declspec(dllimport) int add(int, int);
//_declspec(dllimport) int sub(int, int);
#pragma comment(lib,"ws2_32.lib")
#define AMR_MAGIC_NUMBER "#!AMR\n"
#define MAX_PATH 256
#define AMR_FRAME_LENGTH 32
#define PCM_BUF_LEN 1024
#define CACHE_PCM_LEN 10240
typedef struct RTP_FIXED_HEADER{
/* byte 0 */
unsigned char csrc_len:4; /* expect 0 */
unsigned char extension:1; /* expect 1 */
unsigned char padding:1; /* expect 0 */
unsigned char version:2; /* expect 2 */
/* byte 1 */
unsigned char payload:7;
unsigned char marker:1; /* expect 1 */
/* bytes 2, 3 */
unsigned short seq_no;
/* bytes 4-7 */
unsigned long timestamp;
/* bytes 8-11 */
unsigned long ssrc; /* stream number is used here. */
} RTP_FIXED_HEADER;
int local_rtp_port;
int local_udp_port;
int local_pcm_port;
int remote_port;
int pcm_sample_rate;
int end_task_flag;
char remote_ip[MAX_PATH]={0};
char local_ip[MAX_PATH]={0};
int audio_header_flag;
int udpsocket_setup_server(int serv_port)
{
const int on = 1; /* for setsockopt() */
const int newon = 512000;
int optlen = 4;
int optlen2 = 4;
int sock = -1;
char set = 1;
WSADATA wsd; //初始化信息
struct sockaddr_in saddr;
if (WSAStartup(MAKEWORD(2,2),&wsd) != 0)
{/*进行WinSocket的初始化, windows 初始化socket网络库,申请2,2的版本,windows socket编程必须先初始化。*/
printf( "WSAStartup Error = %d" , WSAGetLastError() );
return 0;
}
saddr.sin_family = AF_INET;
saddr.sin_port = htons(serv_port);
saddr.sin_addr.s_addr = inet_addr(local_ip);
//setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &set, sizeof(int));
if ((sock = socket (AF_INET, SOCK_DGRAM, 0)) < 0)
{
printf("socket (AF_INET, SOCK_DGRAM, 0)) failed!\n");
return (-1);
}
if (getsockopt (sock, SOL_SOCKET, SO_RCVBUF,(char*) &on, (int *)&optlen) < 0)
{
closesocket(sock);
return (-1);
}
if (setsockopt (sock, SOL_SOCKET, SO_RCVBUF,(char*) &newon, optlen2) < 0)
{
closesocket(sock);
return (-1);
}
if (getsockopt (sock, SOL_SOCKET, SO_RCVBUF,(char*) &on, (int *)&optlen) < 0)
{
closesocket(sock);
return (-1);
}
if (bind (sock, (struct sockaddr *)&saddr, sizeof (saddr)) < 0)
{
printf("bind (sock, (struct sockaddr *)&saddr, sizeof (saddr)) failed!\n");
closesocket(sock);
return (-1);
}
return sock;
}
int udpsocket_read(int sockfd, char *strip, int *serv_port, char *pbuf, int *len)
{
long int msgsize;
struct sockaddr_in saddr;
int saddrlen = sizeof (struct sockaddr_in);
msgsize = recvfrom(sockfd, (char *)pbuf, *len,
0, (struct sockaddr *)&saddr,
(int *)&saddrlen);
if (0 >= msgsize)
{
//printf("msgsize %d \n",msgsize);
return -1;
}
/** If the input param is a pointer then the
value can not be straight away assigned, it needs to be copied -- Rajen **/
strcpy(strip,inet_ntoa(saddr.sin_addr));
/** strip=inet_ntoa(saddr.sin_addr); **/
*serv_port=ntohs(saddr.sin_port);
*len = msgsize; /* return actually recv len */
return 1;
}
int udpsocket_write(int sockfd, char *strip, int serv_port, char *pbuf, int len)
{
struct sockaddr_in saddr;
unsigned long serv_ipaddr;
memset(&saddr, 0, sizeof(struct sockaddr_in));
if ((serv_ipaddr = inet_addr(strip)) == -1)
{
printf("Invalid IP address: %s\n",strip);
return -1;
}
saddr.sin_family = AF_INET;
saddr.sin_port = htons(serv_port);
saddr.sin_addr.s_addr = inet_addr(strip);
if (sendto(sockfd, pbuf, len, 0, (struct sockaddr *)&saddr,
sizeof( struct sockaddr_in)) < 0)
{
return -1;
}
return 1;
}
void udpsocket_close_server(int sock)
{
closesocket(sock);
return ;
}
int write_wav_file(char *file_name, char *p_data, int len)
{
FILE* fpwav = NULL;
static char pcm_header[] =
{ 'R','I','F','F', /* 0x52,0x49,0x46,0x46 RIFF */
0x24,0x00,0x00,0x00, /* 0xff,0xff,0xff,0xff,RIFF_SIZE or 0*/
'W','A','V','E', /* WAVE */
'f','m','t',' ', /* fmt */
0x10,0x00,0x00,0x00, /* FORMAT_SIZE */
0x01,0x00, /* FORMATTAG */
0x01,0x00, /* CHANNELS */
0x44,0xac,0x00,0x00, /* SamplesPerSec */ /* 44100 */
0x88,0x58,0x01,0x00, /* AvgBytesPerSec */ /* 88200 */
0x02,0x00, /* BlockAlign */ /*2 */
0x10,0x00, /* BitsPerSample */ /* 16 */
'd','a','t','a', /*LIST or data*/
0x00,0x00,0x00,0x00};
//printf("in function WirteWavFile len:%d\n", len);
fpwav = fopen(file_name, "rb+");
if (fpwav == NULL)
{
fpwav = fopen(file_name, "ab+");
if (fpwav == NULL)
{
return 0;
}
fwrite(pcm_header,1,sizeof(pcm_header),fpwav);
}
else
{
fclose(fpwav);
fpwav = fopen(file_name, "ab+");
}
fwrite(p_data, 1, len, fpwav);
fclose(fpwav);
return 1;
}
int write_amr_file(char *file_name, unsigned char *p_data, int len)
{
//static S8bit fname[256] = "test.amr";
int bytes = 0;
FILE *fpamr = NULL;
fpamr = fopen(file_name, "rb");
if (fpamr == NULL)
{
fpamr = fopen(file_name, "wb+");
if (fpamr == NULL)
{
return 0;
}
/* write magic number to indicate single channel AMR file storage format */
bytes = fwrite(AMR_MAGIC_NUMBER, sizeof( unsigned char), strlen(AMR_MAGIC_NUMBER), fpamr);
//printf("fpamr is NULL!\n");
//return 0;
}
else
{
fclose(fpamr);
fpamr = fopen(file_name, "ab+");
}
#if 0
if (NULL == fopen(file_name, "r"))
{
fpamr = fopen(file_name, "wb");
if (fpamr == NULL)
{
return 0;
}
/* write magic number to indicate single channel AMR file storage format */
bytes = fwrite(AMR_MAGIC_NUMBER, sizeof(char), strlen(AMR_MAGIC_NUMBER), fpamr);
}
else
{
fpamr = fopen(file_name, "ab");
}
#endif
if (p_data == NULL)
{
return 0;
}
bytes += len;
fwrite(p_data, sizeof( unsigned char), len, fpamr);
fclose(fpamr);
return bytes;
}
int make_final_pcm_stream()
{
int serSocket;
char recvData[CACHE_PCM_LEN]={0};
char cacheData[CACHE_PCM_LEN]={0};
char finalData[PCM_BUF_LEN]={0};
char src_ip[20] = {0};
int src_port = 0;
int rcv_data_len = 0;
int cache_buf_len = 0;
int result = 0;
struct timeval select_time;
struct fd_set sock_set;
serSocket = udpsocket_setup_server(local_pcm_port);
if(serSocket < 0)
{
printf("make_final_pcm_stream serSocket setup fail\n");
return -1;
}
while (1&&!end_task_flag)
{
select_time.tv_sec = 0;
select_time.tv_usec = 10000;
FD_ZERO(&sock_set);
FD_SET(serSocket, &sock_set);
if((result = select(serSocket+1, &sock_set, (fd_set*)NULL, (fd_set*)NULL, &select_time)) > 0)
//if( udpsocket_read(serSocket,src_ip,&src_port,recvData,&rcv_data_len)>0)
{
rcv_data_len = CACHE_PCM_LEN;
memset(recvData, 0, rcv_data_len);
if(udpsocket_read(serSocket,src_ip,&src_port,recvData,&rcv_data_len)<0)
{
continue;
}
if(rcv_data_len==78)
{
continue;
}
memcpy(&cacheData[cache_buf_len],recvData,rcv_data_len);
cache_buf_len+=rcv_data_len;
//write_wav_file("test0.wav",recvData,rcv_data_len);
//printf("rcv_data_len=%d\n",rcv_data_len);
if(cache_buf_len>CACHE_PCM_LEN)
{
cache_buf_len = 0;
}
if(cache_buf_len>PCM_BUF_LEN)
{
memcpy(finalData,cacheData,PCM_BUF_LEN);
cache_buf_len -= PCM_BUF_LEN;
memcpy(cacheData,&cacheData[PCM_BUF_LEN],cache_buf_len);
result = udpsocket_write(serSocket,remote_ip, remote_port, finalData, PCM_BUF_LEN);
if(result<0)
{
printf("make_final_pcm_stream send 1024 byte pcm fail!\n");
continue;
}
//write_wav_file("test.wav",finalData,PCM_BUF_LEN);
}
}
}
return 1;
}
int amr_rtp_parser()
{
int serSocket;
int cnt=0;
int ret=0;
int parse_rtp=1;
char recvData[100]={0};
char parseData[100]={0};
char src_ip[20] = {0};
int src_port = 0;
int rcv_buf_len = 0;
int rtp_header_size = sizeof(RTP_FIXED_HEADER);
int msg_len;
struct timeval select_time;
struct fd_set sock_set;
printf("Listening on port %d\n",local_rtp_port);
serSocket = udpsocket_setup_server( local_rtp_port);
if(serSocket < 0)
{
printf("serSocket setup fail\n");
}
while (1&&!end_task_flag)
{
select_time.tv_sec = 0;
select_time.tv_usec = 5000;
FD_ZERO(&sock_set);
FD_SET(serSocket, &sock_set);
if((ret = select(0, &sock_set, (fd_set*)NULL, (fd_set*)NULL, &select_time)) > 0)
{
msg_len = 100;
memset(recvData, 0, msg_len);
//memset((void*)p_client_mes_req_st,0,sizeof(mes_req_st));
ret = udpsocket_read(serSocket,src_ip,&src_port,recvData,&msg_len);
if(ret<0)
{
continue;
}
if(audio_header_flag==0)
{
//ret = udpsocket_write(serSocket, remote_ip, remote_port, "#!AMR\n", 6);
ret = udpsocket_write(serSocket, local_ip, local_udp_port, "#!AMR\n", 6);
audio_header_flag=1;
}
//printf("recvData[rtp_header_size]=%x\n",recvData[rtp_header_size]);
if(recvData[rtp_header_size]==0xf0)
{
if(0x3c==recvData[rtp_header_size+1])
{
/*frame_cnt=1*/
//printf("frame_cnt=1;");
memcpy(parseData,&recvData[rtp_header_size+1],AMR_FRAME_LENGTH);
ret = udpsocket_write(serSocket,local_ip, local_udp_port, parseData, AMR_FRAME_LENGTH);
//ret = udpsocket_write(serSocket,remote_ip, remote_port, parseData, AMR_FRAME_LENGTH);
if(ret<0)
{
printf("udpsocket_write fail!\n");
}
}
else if(0xbc == recvData[rtp_header_size+1] && 0x3c == recvData[rtp_header_size+2])
{
/*frame_cnt=2*/
memcpy(parseData,&(recvData[rtp_header_size+2]),AMR_FRAME_LENGTH);
//ret = udpsocket_write(serSocket,local_ip, local_udp_port, parseData, AMR_FRAME_LENGTH);
parseData[AMR_FRAME_LENGTH] = 0x3c;
memcpy(&parseData[AMR_FRAME_LENGTH+1],&(recvData[rtp_header_size+2+AMR_FRAME_LENGTH]),AMR_FRAME_LENGTH-1);
//parseData[0]=0x3c;
//memcpy(&parseData[1],&(recvData[rtp_header_size+2+AMR_FRAME_LENGTH]),AMR_FRAME_LENGTH-1);
ret = udpsocket_write(serSocket,local_ip, local_udp_port, parseData, AMR_FRAME_LENGTH*2);
if(ret<0)
{
printf("udpsocket_write fail!\n");
}
//write_amr_file("1.amr",parseData,AMR_FRAME_LENGTH*2);
}
}
}
}
return 1;
}
int GetIniFullPath(char * temppath)
{
char sPath[MAX_PATH]; //临时路径
char path[MAX_PATH]; //可执行文件的全路径,包括可执行文件名
char exepath[MAX_PATH]; //配置文件的路径,包括配置文件文件名
char inipath[MAX_PATH]; //配置文件的路径,包括配置文件文件名
int iPosition;
int i = 0;
__try
{
memset(sPath, 0, sizeof(sPath));
memset(path, 0, sizeof(path));
memset(exepath, 0, sizeof(exepath));
memset(inipath, 0, sizeof(inipath));
GetModuleFileName(NULL, path, MAX_PATH);
sprintf_s(sPath, sizeof(sPath), "%s", path);
memcpy(sPath,path, MAX_PATH );
for (i = sizeof(sPath); i > 0; i--)
{
if (sPath[i - 1] == '\\')
{
iPosition = i;
break;
}
}
memcpy(exepath, sPath, iPosition);
sprintf_s(inipath, sizeof(inipath), "%sconifg.ini", exepath);
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
return 0;
}
memcpy(temppath, inipath, sizeof(inipath));
printf("inipath=%s\n",&inipath);
return 1;
}
void GetIni()
{
char inifilepath[MAX_PATH];
char buffini[80];
memset(inifilepath,0, sizeof(inifilepath));
GetIniFullPath(inifilepath);
__try
{
local_rtp_port = GetPrivateProfileInt("音频转换器信息","LOCAL_RTP_PORT",20001,inifilepath);
local_udp_port = GetPrivateProfileInt("音频转换器信息","LOCAL_UDP_PORT",20002,inifilepath);
remote_port = GetPrivateProfileInt("音频转换器信息","REMOTE_PORT",20003,inifilepath);
pcm_sample_rate = GetPrivateProfileInt("音频转换器信息","PCM_SAMPLE_RATE",44100,inifilepath);
local_pcm_port = local_udp_port + 100;
if (GetPrivateProfileString("音频转换器信息",
"REMOTE_IP",
"\0",
(char*)remote_ip,
MAX_PATH,
(const char *)inifilepath) == 0)
{
printf("GetIni, Error");
}
if (GetPrivateProfileString("音频转换器信息",
"LOCAL_IP",
"\0",
(char*)local_ip,
MAX_PATH,
(const char *)inifilepath) == 0)
{
printf("GetIni, Error");
}
}
__except(EXCEPTION_EXECUTE_HANDLER )
{
printf("GetIni, Error:%d",GetLastError());
}
}
void * ffmpeg_convert_audio(void * arg)
{
char systemStateName[256] = {0};
sprintf_s(systemStateName, sizeof(systemStateName), "ffmpeg -re -i udp://%s:%d -f s16le -acodec pcm_s16le -ar %d -ac 1 udp://%s:%d",
local_ip,local_udp_port,pcm_sample_rate,local_ip,local_pcm_port);
//sprintf_s(systemStateName, sizeof(systemStateName), "ffmpeg -re -i udp://%s:%d -acodec aac -f flv rtmp://%s:1935/live/0",
// local_ip,local_udp_port,local_ip);
printf("systemStateName=%s\n",systemStateName);
system(systemStateName);
}
void *end_task(void *param)
{
getchar();
end_task_flag= 1;
printf("the test will go to end\n");
exit(1);
}
int main()
{
char * pstr = "start thread";
GetIni();
printf("local_ip=%s,local_port=%d\n,remote_port=%d\n,pcm_sample_rate=%d\n,remote_ip=%s\n",
local_ip,local_rtp_port,remote_port,pcm_sample_rate,remote_ip);
//先打开ffmpeg
_beginthread(ffmpeg_convert_audio,0,pstr);
_beginthread(end_task,0,pstr);
//由于项目特殊需求,在每个pcm音频包发出去大小为1024字节。
_beginthread(make_final_pcm_stream,0,pstr);
//去除rtp头,amr音频解析为纯udp流
_beginthread(amr_rtp_parser,0,pstr);
getchar();
return 0;
}