socket TCP 从0实现音频传输 ALSA 播放
RTP标准是采用 UDP 发送,有不少现成的开源库,但不在本文讨论的范围内。
UDP 用户数据报,不提供流程,安全传输的功能,但速度快,能提供多播,广播,没有序列号 SEQ ,有 MTU 限制,1500。
TCP 传输控制协议,提供流控,SEQ ,重传功能,没有数据长度限制,可以发几 M 。
但在使用中还是有很多地方需要注意,否则声音不好听,断断续续或是延时严重。
虽然 TCP 在使用上没有 MTU 限制,但是在真实的2个PC 之音使用 TCP 发送数据,也是被切片的,每次包不能超过 1448 字节,本机对本机,数据包没有 MTU 限制,因为走的是 LOOPBACK ,装个 wireshark 一看就懂了。
为什么是 1448 ,MTU 是1500 减去 包头 20 TCP 头 20 时间戳 12 。
ALSA 的播放有2种打开模式,阻塞 非阻塞 snd_pcm_nonblock(playback_handle, 1);
发送接收流控问题,TCP 发送的数据如果大于接收的速度,就会被缓存起来,一直到接收完成以后。
下面只贴出关键代码,来看这一个问题。
send.c 通过 读取本地的一个 wav 的文件,直接读取 一块内容直接发送到本地端口。
这里隐藏了,2个问题
1,用 TCP 来发送,RTP是使用 UDP 发送,而 UDP 不提供 SEQ ,也就是说,接收到的数据顺序会乱,所以,RTP 在使用 UDP 承载时都会自己加一个头信息
自行维护,SEQ ,时间戳,数据校验
2,UDP 是一发就很快返回,不需要等待接收方的 ACK ,所以用 TCP 发送会慢一点。
play.c
struct sockaddr_in client_addr;
socklen_t client_addr_len;
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len); if( > client_fd)
{
printf("client connect error");
continue;
}
printf("client connect addr:%s \n", inet_ntoa(client_addr.sin_addr));
while()
{
int read_len;
int ret;
unsigned char buffer[];
read_len = recv(client_fd, buffer, , );
if( >= read_len) break;
ret = snd_pcm_writei(playback_handle, buffer, read_len/);
if(-EPIPE == ret)
{
snd_pcm_prepare(playback_handle);
}
printf("read_len:%d \n", read_len);
}
close(client_fd);
send.c
audio_p = audio_buf;
send_size = ;
while((audio_p - audio_buf) <= stat.st_size)
{
if(- != client_fd) send(client_fd, audio_p, send_size, );
audio_p += send_size;
}
以上2个程序,可以运行,也可以播放WAV ,存在的问题是,send 一下子把所有数据都发过去了,当按 ctrl+c 退出时,play 还有数据,还能播放一段时间。
理想的情况是,就像打电话一样,挂断后马上停止播放。
send 中是直接发送的 WAV 中的 PCM 数据,所以要添加合适的 delay 来模拟真实的音频直播。
delay 时长的计算:4096*1000000/(44100*16*2/8)=23219 ,每次发送的长度是 4096 ,采样率44k 2通道 16位,算出来,休眠时长是 23219us 。
如果直接在 while 发送 数据包时直接添加 usleep(23219) ,就会发现,播放出来非常难听,这是因为 TCP 发送还是需要时间的,这也是RTP 用 UDP 的原因,UDP 发送非常快。
所以,这里需要另开一个线程,专门做 发送 TCP 数据包的功能。
同理,play 的里面也需要把 TCP 接收的单独放到线程中。
原理讲明白了,代码就不贴了。
delay 时长的另一种计算方法:
ffmpeg -i baby.wav
Input #, wav, from 'baby.wav':
Metadata:
artist : Justin Bieber
date :
genre : Dance
title : Baby
album : My World 2.0
track :
encoder : Lavf58.20.100
Duration: ::34.21, bitrate: kb/s
Stream #:: Audio: pcm_s16le ([][][][] / 0x0001), Hz, stereo, s16, kb/s ls -l
-rw-r--r-- root root 1月 : baby.wav (*+)*= ms ________ = _____
x */=23.1974894086877 ms
通过文件大小和播放时长计算,4096长度的需要播放多少时间,这里为了简单没有去掉 WAV 文件头。
使用 getimeofday 来统计播放需要的时长
printf("chunk_size:%ld \n", chunk_size);
audio_p = audio_buf;
while((audio_p - audio_buf) <= stat.st_size)
{
struct timeval t1, t2;
gettimeofday(&t1, NULL);
int ret = snd_pcm_writei(playback_handle, audio_p, chunk_size);
if(-EPIPE == ret)
{
snd_pcm_prepare(playback_handle);
}
gettimeofday(&t2, NULL);
printf("ms:%ld\n", (t2.tv_sec - t1.tv_sec)* + (t2.tv_usec-t1.tv_usec)/);
audio_p += chunk_size * ; //16位 双声道
}
打出来的结果全是 21 20 。如果 chunk_size 设为 1024 那么打出来的结果很混乱 41 21 0 20 。因为 alsa 的 period_size 不正好是4096 ,所以播放用时不同。
socket TCP 从0实现音频传输 ALSA 播放的更多相关文章
- 分享一个分布式消息总线,基于.NET Socket Tcp的发布-订阅框架,附代码下载
一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已经完成,在通常的情况下,开发人中都是在使用者B所使用 ...
- 基于.NET Socket Tcp的发布-订阅框架
基于.NET Socket Tcp的发布-订阅框架 一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已 ...
- C++写Socket——TCP篇(0)建立连接及双方传输数据
满山的红叶--飘落之时-- 最近接触了点关于用C++写socket的东西,这里总结下. 这里主要是关于TCP的,TCP的特点什么的相关介绍在我另一篇博文里,所以这里直接动手吧. 我们先在windows ...
- 标准C实现基于TCP/IP协议的文件传输
上学期集成程序设计的课堂作业,对于理解TCP/IP实现还是挺有帮助的. TCP/IP编程实现远程文件传输在LUNIX中一般都采用套接字(socket)系统调用. 采用客户/服务器模式,其程序编写步骤如 ...
- http与https与socket tcp/IP与UDP 协议等
网络由下往上分为: 物理层-- 数据链路层-- 网络层-- IP协议 传输层-- ...
- python socket+tcp三次握手四次撒手学习+wireshark抓包
Python代码: server: #!/usr/bin/python # -*- coding: UTF-8 -*- # 文件名:server.py import socket # 导入 socke ...
- 初步探究java中程序退出、GC垃圾回收时,socket tcp连接的行为
初步探究java中程序退出.GC垃圾回收时,socket tcp连接的行为 今天在项目开发中需要用到socket tcp连接相关(作为tcp客户端),在思考中发觉需要理清socket主动.被动关闭时发 ...
- 标准C语言实现基于TCP/IP协议的文件传输
TCP/IP编程实现远程文件传输在LUNIX中一般都采用套接字(socket)系统调用. 采用客户/服务器模式,其程序编写步骤如下: 1.Socket系统调用 为了进行网络I/O,服务器和客户机两 ...
- 27.Socket,TCP,UDP,HTTP基本通信原理
Socket,TCP,UDP,HTTP基本通信原理(摘自百度): TCP.UDP,HTTP 底层通信都是通过 socket 套接字实现 网络上不同的计算机,也可以通信,那么就得使用网络套接字(sock ...
随机推荐
- python后端面试第八部分:制作简历和如何面试--长期维护
############### 就业指导 ################ 这里面有套路,你懂了这个套路,你会找到更好的工作,你会更快的找到工作, ,如何制作简历,五颗星 ,如何投递简历 ...
- jmeter后置处理器之Json提取器详解
此提取器用于提取请求返回结果中的某个值或者某一组值,用法比正则表达式要简单,标准写法为$.key,其中key为返回结果map中的一个键,如果是多层则继续用.key进行即可,如果遇到key的value值 ...
- ES6下的Function.bind方法
在JavaScript的使用中,this的指向问题始终是一个难点.不同的调用方式,会使this指向不同的对象.而使用call,apply,bind等方式,可改变this的指向,完成一些令人惊叹的黑魔法 ...
- OSCACHE介绍
Cache是一种用于提高系统响应速度.改善系统运行性能的技术.尤其是在Web应用中,通过缓存页面的输出结果,可以很显著的改善系统运行性能.本文中作者给大家介绍一个实现J2EE框架中Web应用层缓存功能 ...
- Mercurial 小结
基本操作 # 拉取 并 更新 pull pull && hg update # 撤销上一个命令(不能重复运行) hg rollback # 恢复到指定的 changeset hg st ...
- Python---6条件判断与循环
条件判断 计算机之所以能做很多自动化的任务,因为它可以自己做条件判断. 比如,输入用户年龄,根据年龄打印不同的内容,在Python程序中,用if语句实现: age = 20 if age >= ...
- e代驾推出新产品“e代喝”,能否实现前者的社交梦?
近日,关于e代驾推出e代喝的新闻不断出现在各大媒体的新闻报道之中,看似好像是替人排扰解难的征服酒局的又一利器.但事实真的如此吗?首先要弄清楚的,是目前e代驾在行业中的处境.作为代驾行业的先驱者,e代驾 ...
- Oops 的栈信息分析
MTK MT55 F3600 平台 现象:播放MP4文件不断快退或者快进系统重启. 关键log: Kernel panic - not syncing: x_msg_q_receive(): not ...
- c#版阿里云oss上传——基于layui、pupload的oss 后端web端直传的vue组件
废话前言 去年,做项目用到oss上传,度娘上逛了一圈写了个前后端代码结合的c#版oss上传. 今年又要用到oss上传时发现很难复用,复用改动的范围太大,显然这个轮子不合格.于是想了下,花了一天的时间( ...
- 视频描述(Video Captioning)近年重要论文总结
视频描述 顾名思义视频描述是计算机对视频生成一段描述,如图所示,这张图片选取了一段视频的两帧,针对它的描述是"A man is doing stunts on his bike", ...