精华内容
下载资源
问答
  • 实现局域网语音聊天工具的雏形方案
    千次阅读
    2007-10-25 09:51:00

     功能:同一局域网的两台PC可实现全双工语音聊天

    实现环境:Windows XP + VS2005 + C++

    设计步骤:

    一,初始化工作:

        1,检查音频输入输出设备工作是否正常

        2,建立TCP侦听套接字

    二, 建立连接

        既是server , 又是client,

    三, 通话过程

        1,两块接收对方数据内存A1,A2, 接收对方发过来的音频数据, A1满后,用A2接收,将A1拷贝到播放内存上播放. 如此循环…
        2,两块接收本地音频输入的内存C1,C2, 工作模式跟A1,A2一样, 区别在于一块接收满后先在本地播放,再发送过去
        3,两块播放内存B1, B2, 工作模式跟A1,A2 一样

    四, 断开.

     

    有这些问题亟待解决:

    1,如何获取音频输入数据?

    2,如何将音频数据写入程序定义的变量中?

    3,如何将音频数据发送出去?

    4,程序如何根据麦克风的状态工作?即说话时才采集数据,不说话时不采集.

    更多相关内容
  • VB编写的局域网语音聊天工具,支持点对点语音聊天,可以当做对讲机使用。
  • 安卓局域网语音聊天

    2021-03-16 15:35:39
    安卓局域网语音聊天源码是一套关于局域网聊天的安卓项目源码。实现了语音对讲、文字对讲和文件发送功能。但是小编周围没有什么可用的无线网就没有搭建环境真正的去实验一下。模拟器还是自己玩自己的,不在一个局域网...
  • windows 下底层函数的多媒体编程,实现局域网语音通信,播放流畅。实现是用windows api底层函数编写比如WaveInStart等该类函数写成。 使用过程中一方作为客户端,一方作为服务端进行连接通信。局域网可以实现,...
  • 聊天工具是基于局域网内的基于DIRECTX的实时语音传输工具,编写工具是C# vs2008。
  • 局域网多人语音聊天

    2013-12-16 17:54:26
    服务器端,客户端打包好!内有说明,设置简单!亲自测试可用,觉得声音小的可以在设置里面把输出音量拉到大,麦克风灵敏开到大!!
  • 一个简单的基于lan的聊天程序,允许您创建一个聊天网络,如果您的朋友具有网络名称和密码(可选),则可以加入。 在网络中,您可以轻松聊天和共享文件。 简单易用-尽情享受吧!
  • 局域网视频语音聊天工具 1.0┊实现局域网语音视频及传送┊简体中文绿色免费版
  • C# 局域网语音通讯(Winform)

    热门讨论 2016-08-22 13:39:13
    该资源为国外开源的一款局域网C#语音通讯工具,简单方便(使用VS2010打开)。本人已经运行测试可以行。使用Winform开发,可以使用笔记本麦克风与喇叭一起使用(保证在同一网段下)。
  • 本项目源码是一套关于局域网聊天的安卓项目源码。实现了语音对讲、文字对讲和文件发送功能。但是小编周围没有什么可用的无线网就没有搭建环境真正的去实验一下。模拟器还是自己玩自己的,不在一个局域网。也没法子...
  • 局域网视频聊天软件,,可文字聊天,语音聊天,视频聊天,文件传输
  • 局域网聊天通讯工具

    2018-01-06 16:43:54
    飞秋 飞Q 飞鸽 聊天 通讯 局域网 可以在相同网络中使用 办公方便快捷 直接聊天 语音 视频 传输文件等
  • delphi局域网聊天工具

    2017-05-09 13:33:56
    自带语音提示上线功能。局域网聊天,无服务端,简洁易懂。
  • 飞秋(FeiQ)是一款局域网聊天传送文件的即时通讯软件,它参考了飞鸽传书(IPMSG)和QQ, 完全兼容飞鸽传书(IPMSG)协议,具有局域网传送方便,速度快,操作简单的优点,同时具有QQ中的一些功能,是飞鸽的完善代替者。...
  • 本资源是本人前面发过的一个局域网聊天工具的升级版。主要在界面上进行了美化,并添加了语音聊天的功能。具体功能有: 1.采用了全新的界面风格(新增) 2.实现了基本文字聊天功能 3.实现了基本文件传送功能 4.实现了...
  • Python 实现局域网视频聊天工具

    千次阅读 多人点赞 2020-05-25 23:59:13
    引言 做了下实验楼的关于Python 实现局域网视频聊天工具

    引言

    做了下实验楼的关于Python 实现局域网视频聊天工具 ,感觉还不错,作为练习opencv和socket的小脚本入门了。

    1. 内容简介

    • 本实验实现简易的视频通信工具
    • 在视频通信的基础上加入语音
    • 用户可以选择通信的质量,即画质、停顿等参数
    • 支持IPv6

    2. 实验知识点

    本课程项目完成过程中将学习:

    • Python 基于 OpenCV 对摄像头信息的捕获和压缩
    • Python 关于 线程 和 socket 通信的一些基础技巧
    • Python 基于 PyAudio 对语音信息的捕获和压缩

    其中将重点介绍 socket 传输过程中对数据的压缩和处理。

    3.实验环境

    • python 3.5
    • opencv-python 3.4.1.15
    • numpy 1.14.5
    • PyAudio 0.2.11

    实验步骤

    环境搭建

    首先关于实验所需要用到的包,opencv-python和PyAudio并不是太好装,我简单讲一下我的安装步骤。

    因为我自己电脑Windows下的是anaconda,关于opencv的python第三方包如果是在conda环境下查找,会发现找不到,那么使用pip安装同样会出现四次retry,然后安装失败。所以建议最好创建一个python的虚拟环境,便于直接install,而不需要找什么本地whl文件,或者换源等等,比较麻烦。
    在这里插入图片描述

    另外就是PyAudio==0.2.11,这个包是已经很久没有更新过了,但这并不意味着它适配不了新的python环境,已经编译不了了。它的安装百度到的答案,或者说pypi推荐的安装方式都是pip install PyAudio == 0.2.11,但就最新的python3.6到3.7版本来讲,我是没有安装成功的,原因应该都是报这个错吧:

     error: Microsoft Visual C++ 14.0 is required. Get it with "Build Tools for Visual Studio": 
    

    在这里插入图片描述
    于是继续百度,发现推荐的都是去找PyAudio-0.2.11-cp36-cp36m-win_amd64.whl文件,然后我也去找了,但可能是我python是3.7的原因,我并没有安装成功。报错为:

    >>> pip install D:\Downloads\PyAudio-0.2.11-cp36-cp36m-win_amd64.whl
    
    ERROR: PyAudio-0.2.11-cp36-cp36m-win_amd64.whl is not a supported wheel on this platform.
    

    这里卡了很久,再次发现国内博客都是抄来抄去的,中间还有其它事情,直到第二天搭梯子谷歌了一下,终于找到了答案:

    pip install pipwin	# 安装当前pip环境下的所有必要依赖的包
    
    pipwin install pyaudio	# 成功安装
    

    在这里插入图片描述
    当然还有一种方式,是去GitHub将包的源码拷贝到本地重命名,不过我不喜欢这么做。

    https://github.com/intxcc/pyaudio_portaudio/releases

    代码实现

    先为双方的通信设计 Server 类和 Client类,两个类均继承 threading.Thread并实现双向的C / S连接,只需要分别实现 init、__del__和run方法,之后对象调用.start()方法即可在独立线程中执行run方法中的内容。首先Client类需要存储远端的IP地址和端口,而Server类需要存储本地服务器监听的端口号。用户还应当可以指定通信双方使用的协议版本,即基于IPv4 还是IPv6 的TCP连接。因此Server类的初始化需要传入两个参数(端口、版本),Client类的初始化需要三个参数(远端IP、端口、版本)。新建文件vchat.py,在其中定义基础的两个类,另外捕获视频流的任务应当由Client类完成,一起完善为:

    from socket import *
    import threading
    class Video_Server(threading.Thread):
        def __init__(self, port, version) :
            threading.Thread.__init__(self)
            self.setDaemon(True)	# 设置守护线程,必须在t.start()之前设置
            self.ADDR = ('', port)
            if version == 4:	# ipv4版本,client要与之对应
                self.sock = socket(AF_INET ,SOCK_STREAM)	# 创建TCP连接,ipv4版本
            else:
                self.sock = socket(AF_INET6 ,SOCK_STREAM)	# 创建TCP连接,ipv6版本
        def __del__(self):
            self.sock.close()
            # TODO
        def run(self):
            print("server starts...")
            self.sock.bind(self.ADDR)
            self.sock.listen(1)		# 操作系统可以挂起的最大连接数,如果同一时间的连接数超过1,拒绝其他的连接
            conn, addr = self.sock.accept()	# 接收客户端的请求,且获取新socket对象和客户端信息
            print("remote client success connected...")
            # TODO
    
    class Video_Client(threading.Thread):
        def __init__(self ,ip, port, version):
            threading.Thread.__init__(self)
            self.setDaemon(True)
            self.ADDR = (ip, port)
            if version == 4:
                self.sock = socket(AF_INET, SOCK_STREAM)
            else:
                self.sock = socket(AF_INET6, SOCK_STREAM)
            self.cap = cv2.VideoCapture(0)	# 创建一个成员变量cap
        def __del__(self) :
            self.sock.close()
            self.cap.release()
        def run(self):
            print("client starts...")
            while True:
                try:
                    self.sock.connect(self.ADDR)
                    break
                except:
                    time.sleep(3)
                    continue
            print("client connected...")
            while self.cap.isOpened():	# 如果连接上了,进入循环读相应的文件,这里也可以try一下,没有连接上接收异常,这样的语法不好排错
                ret, frame = self.cap.read()	# 读
    

    已经捕获到数据,接下来要发送字节流。首先我们继续编写Client,为其添加发送数据功能的实现。这里只改动了run方法。在捕获到帧后,我们使用pickle.dumps方法对其打包,并用sock.sendall方法发送。注意发送过程中我们用struct.pack方法为每批数据加了一个头,用于接收方确认接受数据的长度。

        def run(self):
            while True:
                try:
                    self.sock.connect(self.ADDR)
                    break
                except:
                    time.sleep(3)
                    continue
            print("client connected...")
            while self.cap.isOpened():
                ret, frame = self.cap.read()	
                data = pickle.dumps(frame)
                try:
                	"""
    				将len(data)等参数的值进行一层包装,包装的方法由fmt指定。被包装的参数必须严格符合fmt。最后返回一个包装后的字符串。
    				"""
                    self.sock.sendall(struct.pack("L", len(data)) + data)
                except:
                    break
    

    下面编写Server,在服务器端连接成功后,应当创建一个窗口用于显示接收到的视频。因为连接不一定创建成功,因此cv.destroyAllWindows()被放在一个try…catch块中防止出现错误。在接收数据过程中,我们使用payload_size记录当前从缓冲区读入的数据长度,这个长度通过struct.calcsize(‘L’)来读取。使用该变量的意义在于缓冲区中读出的数据可能不足一个帧,也可能由多个帧构成。为了准确提取每一帧,我们用payload_size区分帧的边界。在从缓冲区读出的数据流长度超过payload_size时,剩余部分和下一次读出的数据流合并,不足payload_size时将合并下一次读取的数据流到当前帧中。在接收完完整的一帧后,显示在创建的窗口中。同时我们为窗口创建一个键盘响应,当按下Esc 或 q键时退出程序。

    另外当用户指定使用低画质通信,我们应当对原始数据做变换,最简单的方式即将捕获的每一帧按比例缩放,同时降低传输的帧速,在代码中体现为resize,该函数的第二个参数为缩放中心,后两个参数为缩放比例,并且根据用户指定的等级,不再传输捕获的每一帧,而是间隔几帧传输一帧。为了防止用户指定的画质过差,代码中限制了最坏情况下的缩放比例为0.3,最大帧间隔为3。此外,我们在发送每一帧的数据前使用zlib.compress对其压缩,尽量降低带宽负担。

    所以完整代码为:

    class Video_Server(threading.Thread):
        def __init__(self, port, version) :
            threading.Thread.__init__(self)
            self.setDaemon(True)
            self.ADDR = ('', port)
            if version == 4:
                self.sock = socket(AF_INET ,SOCK_STREAM)
            else:
                self.sock = socket(AF_INET6 ,SOCK_STREAM)
        def __del__(self):
            self.sock.close()
            try:
                cv2.destroyAllWindows()
            except:
                pass
        def run(self):
            print("VIDEO server starts...")
            self.sock.bind(self.ADDR)
            self.sock.listen(1)
            conn, addr = self.sock.accept()
            print("remote VIDEO client success connected...")
            data = "".encode("utf-8")
            payload_size = struct.calcsize("L")		# 结果为4
            cv2.namedWindow('Remote', cv2.WINDOW_NORMAL)
            while True:
                while len(data) < payload_size:
                    data += conn.recv(81920)
                packed_size = data[:payload_size]
                data = data[payload_size:]
                msg_size = struct.unpack("L", packed_size)[0]
                while len(data) < msg_size:
                    data += conn.recv(81920)
                zframe_data = data[:msg_size]
                data = data[msg_size:]
                frame_data = zlib.decompress(zframe_data)
                frame = pickle.loads(frame_data)
                cv2.imshow('Remote', frame)
                if cv2.waitKey(1) & 0xFF == 27:
                    break
    
    
    class Video_Client(threading.Thread):
        def __init__(self ,ip, port, level, version):
            threading.Thread.__init__(self)
            self.setDaemon(True)
            self.ADDR = (ip, port)
            if level <= 3:
                self.interval = level
            else:
                self.interval = 3
            self.fx = 1 / (self.interval + 1)
            if self.fx < 0.3:	# 限制最大帧间隔为3帧
                self.fx = 0.3
            if version == 4:
                self.sock = socket(AF_INET, SOCK_STREAM)
            else:
                self.sock = socket(AF_INET6, SOCK_STREAM)
            self.cap = cv2.VideoCapture(0)
        def __del__(self) :
            self.sock.close()
            self.cap.release()
        def run(self):
            print("VIDEO client starts...")
            while True:
                try:
                    self.sock.connect(self.ADDR)
                    break
                except:
                    time.sleep(3)
                    continue
            print("VIDEO client connected...")
            while self.cap.isOpened():
                ret, frame = self.cap.read()
                sframe = cv2.resize(frame, (0,0), fx=self.fx, fy=self.fx)
                data = pickle.dumps(sframe)
                zdata = zlib.compress(data, zlib.Z_BEST_COMPRESSION)
                try:
                    self.sock.sendall(struct.pack("L", len(zdata)) + zdata)
                except:
                    break
                for i in range(self.interval):
                    self.cap.read()
    

    里面有些代码上面的大段应该解释得比较清楚,如果还有不明白的,可以看作者的推荐,还有我找到的一些资料链接:

    struct.pack()、struct.unpack()和struct.calcsize()

    Python 网络编程

    加入音频的捕获和传输

    在完成视频通信的基础上,整体框架对于音频通信可以直接挪用,只需要修改其中捕获视频/音频的代码和服务器解码播放的部分。这里我们使用 PyAudio 库处理音频,在 Linux 下你也可以选择 sounddevice。关于sounddevice这里不做过多介绍,可以在这里看到它最新版本的文档。将vchat.py复制一份,重命名为achat.py,简单修改几处,最终音频捕获、传输的完整代码如下。将上面代码中的Server和Client分别加上Video和Audio前缀以区分,同时显示给用户的print输出语句也做了一定修改,对于视频加上VIDEO前缀,音频加上AUDIO前缀。

    from socket import *
    import threading
    import pyaudio
    import wave
    import sys
    import zlib
    import struct
    import pickle
    import time
    import numpy as np
    
    CHUNK = 1024
    FORMAT = pyaudio.paInt16    # 格式
    CHANNELS = 2    # 输入/输出通道数
    RATE = 44100    # 音频数据的采样频率
    RECORD_SECONDS = 0.5    # 记录秒
    
    class Audio_Server(threading.Thread):
        def __init__(self, port, version) :
            threading.Thread.__init__(self)
            self.setDaemon(True)
            self.ADDR = ('', port)
            if version == 4:
                self.sock = socket(AF_INET ,SOCK_STREAM)
            else:
                self.sock = socket(AF_INET6 ,SOCK_STREAM)
            self.p = pyaudio.PyAudio()  # 实例化PyAudio,并于下面设置portaudio参数
            self.stream = None
        def __del__(self):
            self.sock.close()   # 关闭套接字
            if self.stream is not None:
                self.stream.stop_stream()   # 暂停播放 / 录制
                self.stream.close()     # 终止流
            self.p.terminate()      # 终止会话
        def run(self):
            print("Video server starts...")
            self.sock.bind(self.ADDR)
            self.sock.listen(1)
            conn, addr = self.sock.accept()
            print("remote Video client success connected...")
            data = "".encode("utf-8")
            payload_size = struct.calcsize("L")     # 返回对应于格式字符串fmt的结构,L为4
            self.stream = self.p.open(format=FORMAT,
                                      channels=CHANNELS,
                                      rate=RATE,
                                      output=True,
                                      frames_per_buffer = CHUNK
                                      )
            while True:
                while len(data) < payload_size:
                    data += conn.recv(81920)
                packed_size = data[:payload_size]
                data = data[payload_size:]
                msg_size = struct.unpack("L", packed_size)[0]
                while len(data) < msg_size:
                    data += conn.recv(81920)
                frame_data = data[:msg_size]
                data = data[msg_size:]
                frames = pickle.loads(frame_data)
                for frame in frames:
                    self.stream.write(frame, CHUNK)
    
    class Audio_Client(threading.Thread):
        def __init__(self ,ip, port, version):
            threading.Thread.__init__(self)
            self.setDaemon(True)
            self.ADDR = (ip, port)
            if version == 4:
                self.sock = socket(AF_INET, SOCK_STREAM)
            else:
                self.sock = socket(AF_INET6, SOCK_STREAM)
            self.p = pyaudio.PyAudio()
            self.stream = None
            print("AUDIO client starts...")
        def __del__(self) :
            self.sock.close()
            if self.stream is not None:
                self.stream.stop_stream()
                self.stream.close()
            self.p.terminate()
        def run(self):
            while True:
                try:
                    self.sock.connect(self.ADDR)
                    break
                except:
                    time.sleep(3)
                    continue
            print("AUDIO client connected...")
            self.stream = self.p.open(format=FORMAT, 
                                 channels=CHANNELS,
                                 rate=RATE,
                                 input=True,
                                 frames_per_buffer=CHUNK)
            while self.stream.is_active():
                frames = []
                for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
                    data = self.stream.read(CHUNK)
                    frames.append(data)
                senddata = pickle.dumps(frames)
                try:
                    self.sock.sendall(struct.pack("L", len(senddata)) + senddata)
                except:
                    break
    

    Play and Record Sound with Python

    PyAudio Documentation¶

    以上为参考文档,下面就需要编写启动这两个文件的主函数了。

    编写程序入口 main.py

    为了提供用户参数解析,代码使用了argparse。你可能对此前几个类中初始化方法的self.setDaemon(True)有疑惑。这个方法的调用使每个线程在主线程结束之后自动退出,保证程序不会出现崩溃且无法销毁的情况。在main.py中,我们通过每隔1s做一次线程的保活检查,如果视频/音频中出现阻塞/故障,主线程会终止。

    import sys
    import time
    import argparse
    from vchat import Video_Server, Video_Client
    from achat import Audio_Server, Audio_Client
    
    parser = argparse.ArgumentParser()
    
    parser.add_argument('--host', type=str, default='127.0.0.1')
    parser.add_argument('--port', type=int, default=10087)
    parser.add_argument('--level', type=int, default=1)
    parser.add_argument('-v', '--version', type=int, default=4)
    
    args = parser.parse_args()
    
    IP = args.host
    PORT = args.port
    VERSION = args.version
    LEVEL = args.level
    
    if __name__ == '__main__':
        vclient = Video_Client(IP, PORT, LEVEL, VERSION)
        vserver = Video_Server(PORT, VERSION)
        aclient = Audio_Client(IP, PORT+1, VERSION)
        aserver = Audio_Server(PORT+1, VERSION)
        vclient.start()
        aclient.start()
        time.sleep(1)    # make delay to start server
        vserver.start()
        aserver.start()
        while True:
            time.sleep(1)
            if not vserver.isAlive() or not vclient.isAlive():
                print("Video connection lost...")
                sys.exit(0)
            if not aserver.isAlive() or not aclient.isAlive():
                print("Audio connection lost...")
                sys.exit(0)
    

    关于argparse模块,我记得在之前的一篇文章中提到过,现在基本都忘了。。。链接为:

    Python利用argparse模块图片转字符文件

    如果有不清楚,可以进去看看我对参数画了思维导图。

    总结

    因为环境都没有提供摄像头,如果需运行,要修改一下代码,让程序从一个本地视频文件读取,模拟摄像头的访问。将Video_Client中self.cap = cv2.VideoCapture(0)改为self.cap = cv2.VideoCapture(‘xxx.mp4’),即从本地视频xxx.mp4中读取。我这里拿了一个B站的MAD来试验,另外,当程序启动时,电脑接收到的声音,因为自己与自己远程,都会有回声。

    那么我在Windows环境下运行为:

    在这里插入图片描述

    展开全文
  • Python实现简易局域网视频聊天工具 ICanDo_It 2016-11-15 18:27:25 6571 收藏 8 分类专栏: Linux基础 文章标签: 算法 版权 基于实验楼限免课程: Python实现简易局域网视频聊天工具 试验操作系统为Ubuntu14.04,...

    Python实现简易局域网视频聊天工具

    ICanDo_It 2016-11-15 18:27:25 6571 收藏 8
    分类专栏: Linux基础 文章标签: 算法
    版权

         基于实验楼限免课程:
    

    Python实现简易局域网视频聊天工具

         试验操作系统为Ubuntu14.04,OpenCV版本为2.4.13.1,源代码可以在https://github.com/Forec/lan-ichat上获取。软件初衷是为了在IPV6协议网络中节省聊天流量。
    
         内容简介:
    
                   1.实现简易视频通信工具,并能实现语音通话 2.通信质量 ,如画质、停顿等参数可以调节  3.支持IPV6
    
         知识点:
    
                   1.Python基于Opencv对摄像头信息的捕获和压缩
    
                   2.Python关于线程和socket通信的基础技巧(数据的压缩处理为重点)
    
                   3.Python基于Pyaudio对语音信息的捕获和压缩
    

    期间遇到的问题:

         1.执行wgethttps://labfile.oss.aliyuncs.com/courses/672/2.4.13-binary.tar.gz操作时,遇到证书问题
    
         2.进入到python后回不去了(小白笨笨哒),http://blog.csdn.net/langzi7758521/article/details/51163009在这里找到了解法
    

    即quit()/exit()/ctrl+d退出。。之前一直尝试cd …/cd ~无果

         3.import cv2时出现问题:failed to initialize libdc1394,百度后http://stackoverflow.com/questions/12689304/ctypes-error-libdc1394-error-failed-to-initialize-libdc1394中找到解法。
    
         4.最后仍是无法查看CV2的版本,原因是无法打出更长的下划线。。无语无奈
    

    无法下载链接中的文件,于是通过实验楼中的火狐浏览器直接下载后,复制到Opencv文件夹下,进行下一步操作成功。

    过程理解:

         1.更新实验楼中一些基本库
    
         2.新建OpenCV文件夹,下载作者已编译好的包,完成环境配置的准备工作
    

    Python实现简易局域网视频聊天工具

    一、课程介绍

    1.课程来源

    课程使用的操作系统为 Ubuntu 14.04,OpenCV版本为OpenCV 2.4.13.1,你可以在这里查看该版本 OpenCV 的文档。

    你可以在我的 Github 上找到 Windows 系统和 Linux 系统对应的源代码,此教程对应的版本是 v0.2。目前我正在开发的版本是 v0.3,新版本将允许使用不同IP协议的主机通信,并且范围不再局限于局域网内。这个工具最初是为了通过IPv6节省聊天工具使用的流量而开发的。

    2.内容简介

    课程实验实现简易的视频通信工具
    在视频通信的基础上加入语音
    用户可以选择通信的质量,即画质、停顿等参数
    支持IPv6
    

    3.课程知识点

    本课程项目完成过程中将学习:

    Python 基于 OpenCV 对摄像头信息的捕获和压缩
    Python 关于 线程 和 socket 通信的一些基础技巧
    Python 基于 PyAudio 对语音信息的捕获和压缩
    

    其中将重点介绍 socket 传输过程中对数据的压缩和处理。

    二、实验环境

    本实验需要先在实验平台安装 OpenCV ,需下载依赖的库、源代码并编译安装。安装过程建议按照教程给出的步骤,或者你可以参考官方文档中 Linux 环境下的安装步骤,但 有些选项需要变更。安装过程所需时间会比较长,这期间你可以先阅读接下来的教程,在大致了解代码原理后再亲自编写尝试。
    我提供了一个编译好的2.4.13-binary.tar.gz包,你可以通过下面的命令下载并安装,节省了编译的时间,但仍需要安装依赖库的过程,整个环境配置过程大约20~30分钟,根据当前机器速度决定。
    

    · $ sudo apt-get update

    · $ sudo apt-get install build-essentiallibgtk2.0-dev libjpeg-dev libtiff5-dev libjasper-dev libopenexr-dev cmakepython-dev python-numpy python-tk libtbb-dev libeigen2-dev yasm libfaac-devlibopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-devlibxvidcore-dev libx264-dev libqt4-dev libqt4-opengl-dev sphinx-commontexlive-latex-extra libv4l-dev libdc1394-22-dev libavcodec-dev libavformat-devlibswscale-dev python-pyaudio

    · $ cd ~

    · $ mkdir OpenCV && cd OpenCV

    · $ wgethttps://labfile.oss.aliyuncs.com/courses/672/2.4.13-binary.tar.gz

    · $ tar -zxvf 2.4.13-binary.tar.gz

    · $ cdopencv-2.4.13

    · $ cd build

    · $ sudo make install

    如果你想体验编译的整个过程,我也提供了一个一键安装的脚本文件,你可以通过下面的命令尝试。这个过程会非常漫长,期间可能还需要你做一定的交互确认工作。
    

    · $ cd ~

    · $ sudo apt-get update

    · $ wgethttps://labfile.oss.aliyuncs.com/courses/672/opencv.sh

    · $ sudo chmod 777 opencv.sh

    · $ ./opencv.sh

    如果你觉得有必要亲自尝试一下安装的每一步,可以按照下面的命令逐条输入执行,在实验楼的环境中大概需要两个小时。
    

    · $ sudo apt-get update

    · $ sudo apt-get install build-essentiallibgtk2.0-dev libjpeg-dev libtiff5-dev libjasper-dev libopenexr-dev cmakepython-dev python-numpy python-tk libtbb-dev libeigen2-dev yasm libfaac-devlibopencore-amrnb-dev libopencore-amrwb-dev libtheora-dev libvorbis-dev libxvidcore-devlibx264-dev libqt4-dev libqt4-opengl-dev sphinx-common texlive-latex-extralibv4l-dev libdc1394-22-dev libavcodec-dev libavformat-dev libswscale-devpython-pyaudio

    · $ wgethttps://github.com/Itseez/opencv/archive/2.4.13.zip

    · $ unzip 2.4.13.zip

    · $ cd 2.4.13

    · $ mkdir release && cd release

    · $ cmake -D WITH_TBB=ON -DBUILD_NEW_PYTHON_SUPPORT=ON -D WITH_V4L=ON -D INSTALL_C_EXAMPLES=ON -DINSTALL_PYTHON_EXAMPLES=ON -D BUILD_EXAMPLES=ON -D WITH_QT=ON -D WITH_GTK=ON -DWITH_OPENGL=ON …

    · $ sudo make

    · $ sudo make install

    · $ sudo gedit/etc/ld.so.conf.d/opencv.conf

    · $ 输入 /usr/local/lib,按 Ctrl + X 退出,退出时询问是否保存,按 Y 确认。

    · $ sudo ldconfig -v

    · $ sudo gedit /etc/bash.bashrc

    · $ 在文件末尾加入

    · $ PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig

    · export PKG_CONFIG_PATH

    · 按 Ctrl + X 退出,按 Y 确认保存。

    检验配置是否成功。
    

    · $ python

    · >>> import cv2

    · >>> cv2.version

    · ‘2.4.13’

    三、实验原理

    实验实现了简易的视频通信工具,基于 OpenCV 和 PyAudio,使用 TCP 协议通信,通信双方建立双向 CS 连接,双方均维护一个客户端和一个服务器端。在捕获视频信息后,根据用户指定的参数对画面做压缩并传输。

    四、实验步骤

    通过以下命令可下载项目源码,作为参照对比完成下面详细步骤的学习。

    wgethttps://labfile.oss.aliyuncs.com/courses/672/ichat.zip

    unzip ichat.zip

    1.实现双向 C/S 连接

    先为双方的通信设计 Server 类和 Client类,两个类均继承threading.Thread,只需要分别实现 init、__del__和run方法,之后对象调用.start()方法即可在独立线程中执行run方法中的内容。首先Client类需要存储远端的IP地址和端口,而Server类需要存储本地服务器监听的端口号。用户还应当可以指定通信双方使用的协议版本,即基于IPv4 还是IPv6 的TCP连接。因此Server类的初始化需要传入两个参数(端口、版本),Client类的初始化需要三个参数(远端IP、端口、版本)。新建文件vchat.py,在其中定义基础的两个类如下。

    from socket import *

    import threading

    classVideo_Server(threading.Thread):

    def__init__(self, port, version) :

        threading.Thread.__init__(self)
    
        self.setDaemon(True)
    
        self.ADDR = ('', port)
    
        if version == 4:
    
            self.sock = socket(AF_INET,SOCK_STREAM)
    
        else:
    
            self.sock = socket(AF_INET6,SOCK_STREAM)
    

    def__del__(self):

        self.sock.close()
    
        # TODO
    

    defrun(self):

        print("server starts...")
    
        self.sock.bind(self.ADDR)
    
        self.sock.listen(1)
    
        conn, addr = self.sock.accept()
    
        print("remote client success connected...")
    
        # TODO
    

    classVideo_Client(threading.Thread):

    def__init__(self ,ip, port, version):

        threading.Thread.__init__(self)
    
        self.setDaemon(True)
    
        self.ADDR = (ip, port)
    
        if version == 4:
    
            self.sock = socket(AF_INET,SOCK_STREAM)
    
        else:
    
            self.sock = socket(AF_INET6,SOCK_STREAM)
    

    def__del__(self) :

        self.sock.close()
    
        # TODO
    

    defrun(self):

        print("client starts...")
    
        whileTrue:
    
            try:
    
                self.sock.connect(self.ADDR)
    
                break
    
            except:
    
                time.sleep(3)
    
                continue
    
        print("client connected...")
    
        # TODO
    

    2.实现摄像头数据流捕获

    OpenCV 为Python 提供的接口非常简单并且易于理解。捕获视频流的任务应当由Client类完成,下面完善Client的run函数。在下面的代码中,我们为类添加了一个成员变量cap,它用来捕获默认摄像头的输出。

    classVideo_Client(threading.Thread):

    def__init__(self ,ip, port, version):

        threading.Thread.__init__(self)
    
        self.setDaemon(True)
    
        self.ADDR = (ip, port)
    
        if version == 4:
    
            self.sock = socket(AF_INET,SOCK_STREAM)
    
        else:
    
            self.sock = socket(AF_INET6,SOCK_STREAM)
    
        self.cap = cv2.VideoCapture(0)
    

    def__del__(self) :

        self.sock.close()
    
        self.cap.release()
    

    defrun(self):

        print("client starts...")
    
        whileTrue:
    
            try:
    
                self.sock.connect(self.ADDR)
    
                break
    
            except:
    
                time.sleep(3)
    
                continue
    
        print("client connected...")
    
        whileself.cap.isOpened():
    
            ret, frame = self.cap.read()
    
        # TODO
    

    3.发送捕获到的数据到服务器

    已经捕获到数据,接下来要发送字节流。首先我们继续编写Client,为其添加发送数据功能的实现。这里只改动了run方法。在捕获到帧后,我们使用pickle.dumps方法对其打包,并用sock.sendall方法发送。注意发送过程中我们用struct.pack方法为每批数据加了一个头,用于接收方确认接受数据的长度。

    defrun(self):

        whileTrue:
    
            try:
    
                self.sock.connect(self.ADDR)
    
                break
    
            except:
    
                time.sleep(3)
    
                continue
    
        print("client connected...")
    
        whileself.cap.isOpened():
    
            ret, frame = self.cap.read()
    
            data = pickle.dumps(frame)
    
            try:
    
                self.sock.sendall(struct.pack("L", len(data))+ data)
    
            except:
    
                break
    

    下面编写Server,在服务器端连接成功后,应当创建一个窗口用于显示接收到的视频。因为连接不一定创建成功,因此cv.destroyAllWindows()被放在一个try…catch块中防止出现错误。在接收数据过程中,我们使用payload_size记录当前从缓冲区读入的数据长度,这个长度通过struct.calcsize(‘L’)来读取。使用该变量的意义在于缓冲区中读出的数据可能不足一个帧,也可能由多个帧构成。为了准确提取每一帧,我们用payload_size区分帧的边界。在从缓冲区读出的数据流长度超过payload_size时,剩余部分和下一次读出的数据流合并,不足payload_size时将合并下一次读取的数据流到当前帧中。在接收完完整的一帧后,显示在创建的窗口中。同时我们为窗口创建一个键盘响应,当按下Esc 或 q键时退出程序。

    classVideo_Server(threading.Thread):

    def__init__(self, port, version) :

        threading.Thread.__init__(self)
    
        self.setDaemon(True)
    
        self.ADDR = ('', port)
    
        if version == 4:
    
            self.sock = socket(AF_INET,SOCK_STREAM)
    
        else:
    
            self.sock = socket(AF_INET6,SOCK_STREAM)
    

    def__del__(self):

        self.sock.close()
    
        try:
    
            cv2.destroyAllWindows()
    
        except:
    
            pass
    

    defrun(self):

        print("server starts...")
    
        self.sock.bind(self.ADDR)
    
        self.sock.listen(1)
    
        conn, addr = self.sock.accept()
    
        print("remote client successconnected...")
    
        data = "".encode("utf-8")
    
        payload_size = struct.calcsize("L")
    
        cv2.namedWindow('Remote', cv2.WINDOW_NORMAL)
    
        whileTrue:
    
            while len(data)< payload_size:
    
                data += conn.recv(81920)
    
            packed_size = data[:payload_size]
    
            data = data[payload_size:]
    
            msg_size = struct.unpack("L", packed_size)[0]
    
            while len(data)< msg_size:
    
                data += conn.recv(81920)
    
            zframe_data = data[:msg_size]
    
            data = data[msg_size:]
    
            frame_data =zlib.decompress(zframe_data)
    
            frame = pickle.loads(frame_data)
    
            cv2.imshow('Remote', frame)
    
            if cv2.waitKey(1) & 0xFF == 27:
    
                break
    

    4.视频缩放和数据压缩

    现在的服务器和客户端已经可以运行,你可以在代码中创建一个Client类实例和一个Server类实例,并将IP地址设为127.0.0.1,端口设为任意合法的(0-65535)且不冲突的值,版本设为IPv4。执行代码等同于自己和自己通信。如果网络状况不好,你也许会发现自己和自己的通信也有卡顿现象。为了使画面质量、延迟能够和现实网络状况相匹配,我们需要允许用户指定通信中画面的质量,同时我们的代码应当本身具有压缩数据的能力,以尽可能利用带宽。

    当用户指定使用低画质通信,我们应当对原始数据做变换,最简单的方式即将捕获的每一帧按比例缩放,同时降低传输的帧速,在代码中体现为resize,该函数的第二个参数为缩放中心,后两个参数为缩放比例,并且根据用户指定的等级,不再传输捕获的每一帧,而是间隔几帧传输一帧。为了防止用户指定的画质过差,代码中限制了最坏情况下的缩放比例为0.3,最大帧间隔为3。此外,我们在发送每一帧的数据前使用zlib.compress对其压缩,尽量降低带宽负担。

    classVideo_Client(threading.Thread):

    def__init__(self ,ip, port, level, version):

        threading.Thread.__init__(self)
    
        self.setDaemon(True)
    
        self.ADDR = (ip, port)
    
        if level <= 3:
    
            self.interval = level
    
        else:
    
            self.interval = 3
    
        self.fx = 1 /(self.interval + 1)
    
        if self.fx <0.3:
    
            self.fx = 0.3
    
        if version == 4:
    
            self.sock = socket(AF_INET,SOCK_STREAM)
    
        else:
    
            self.sock = socket(AF_INET6,SOCK_STREAM)
    
        self.cap = cv2.VideoCapture(0)
    

    def__del__(self) :

        self.sock.close()
    
        self.cap.release()
    

    defrun(self):

        print("VEDIO client starts...")
    
        whileTrue:
    
            try:
    
                self.sock.connect(self.ADDR)
    
                break
    
            except:
    
                time.sleep(3)
    
                continue
    
        print("VEDIO client connected...")
    
        whileself.cap.isOpened():
    
            ret, frame = self.cap.read()
    
            sframe = cv2.resize(frame, (0,0),fx=self.fx, fy=self.fx)
    
            data = pickle.dumps(sframe)
    
            zdata = zlib.compress(data,zlib.Z_BEST_COMPRESSION)
    
            try:
    
                self.sock.sendall(struct.pack("L", len(zdata))+ zdata)
    
            except:
    
                break
    
            for i inrange(self.interval):
    
                self.cap.read()
    

    服务器端最终代码如下,增加了对接收到数据的解压缩处理。

    classVideo_Server(threading.Thread):

    def__init__(self, port, version) :

        threading.Thread.__init__(self)
    
        self.setDaemon(True)
    
        self.ADDR = ('', port)
    
        if version == 4:
    
            self.sock = socket(AF_INET,SOCK_STREAM)
    
        else:
    
            self.sock = socket(AF_INET6,SOCK_STREAM)
    

    def__del__(self):

        self.sock.close()
    
        try:
    
            cv2.destroyAllWindows()
    
        except:
    
            pass
    

    defrun(self):

        print("VEDIO server starts...")
    
        self.sock.bind(self.ADDR)
    
        self.sock.listen(1)
    
        conn, addr = self.sock.accept()
    
        print("remote VEDIO client successconnected...")
    
        data = "".encode("utf-8")
    
        payload_size = struct.calcsize("L")
    
        cv2.namedWindow('Remote',cv2.WINDOW_NORMAL)
    
        whileTrue:
    
            while len(data)< payload_size:
    
                data += conn.recv(81920)
    
            packed_size = data[:payload_size]
    
            data = data[payload_size:]
    
            msg_size = struct.unpack("L",packed_size)[0]
    
            while len(data)< msg_size:
    
                data += conn.recv(81920)
    
            zframe_data = data[:msg_size]
    
            data = data[msg_size:]
    
            frame_data =zlib.decompress(zframe_data)
    
            frame = pickle.loads(frame_data)
    
            cv2.imshow('Remote', frame)
    
            if cv2.waitKey(1) & 0xFF == 27:
    
                break
    

    5.加入音频的捕获和传输

    在完成视频通信的基础上,整体框架对于音频通信可以直接挪用,只需要修改其中捕获视频/音频的代码和服务器解码播放的部分。这里我们使用 PyAudio 库处理音频,在 Linux 下你也可以选择 sounddevice。关于sounddevice这里不做过多介绍,你可以在这里看到它最新版本的文档。将vchat.py复制一份,重命名为achat.py,简单修改几处,最终音频捕获、传输的完整代码如下。我将上面代码中的Server和Client分别加上Video和Audio前缀以区分,同时显示给用户的print输出语句也做了一定修改,对于视频加上VIDEO前缀,音频加上AUDIO前缀。如果你对代码中使用到的 PyAudio 提供的库函数有所疑问,可以在这里找到相关的入门文档及示例。

    classAudio_Server(threading.Thread):

    def__init__(self, port, version) :

        threading.Thread.__init__(self)
    
        self.setDaemon(True)
    
        self.ADDR = ('', port)
    
        if version == 4:
    
            self.sock = socket(AF_INET,SOCK_STREAM)
    
        else:
    
            self.sock = socket(AF_INET6,SOCK_STREAM)
    
        self.p = pyaudio.PyAudio()
    
        self.stream = None
    

    def__del__(self):

        self.sock.close()
    
        if self.stream isnotNone:
    
            self.stream.stop_stream()
    
            self.stream.close()
    
        self.p.terminate()
    

    defrun(self):

        print("AUDIO server starts...")
    
        self.sock.bind(self.ADDR)
    
        self.sock.listen(1)
    
        conn, addr = self.sock.accept()
    
        print("remote AUDIO client success connected...")
    
        data = "".encode("utf-8")
    
        payload_size = struct.calcsize("L")
    
        self.stream =self.p.open(format=FORMAT,
    
                                 channels=CHANNELS,
    
                                  rate=RATE,
    
                                  output=True,
    
                                 frames_per_buffer = CHUNK
    
                                  )
    
        whileTrue:
    
            while len(data)< payload_size:
    
                data += conn.recv(81920)
    
            packed_size = data[:payload_size]
    
            data = data[payload_size:]
    
            msg_size = struct.unpack("L",packed_size)[0]
    
            while len(data)< msg_size:
    
                data += conn.recv(81920)
    
            frame_data = data[:msg_size]
    
            data = data[msg_size:]
    
            frames = pickle.loads(frame_data)
    
            for frame in frames:
    
                self.stream.write(frame, CHUNK)
    

    classAudio_Client(threading.Thread):

    def__init__(self ,ip, port, version):

        threading.Thread.__init__(self)
    
        self.setDaemon(True)
    
        self.ADDR = (ip, port)
    
        if version == 4:
    
            self.sock = socket(AF_INET,SOCK_STREAM)
    
        else:
    
            self.sock = socket(AF_INET6,SOCK_STREAM)
    
        self.p = pyaudio.PyAudio()
    
        self.stream = None
    

    def__del__(self) :

        self.sock.close()
    
        if self.stream isnotNone:
    
            self.stream.stop_stream()
    
            self.stream.close()
    
        self.p.terminate()
    

    defrun(self):

       print("AUDIO client starts...")
    
        whileTrue:
    
            try:
    
                self.sock.connect(self.ADDR)
    
                break
    
            except:
    
                time.sleep(3)
    
                continue
    
        print("AUDIO client connected...")
    
        self.stream =self.p.open(format=FORMAT,
    
                             channels=CHANNELS,
    
                             rate=RATE,
    
                             input=True,
    
                            frames_per_buffer=CHUNK)
    
        whileself.stream.is_active():
    
            frames = []
    
            for i in range(0, int(RATE /CHUNK * RECORD_SECONDS)):
    
                data = self.stream.read(CHUNK)
    
                frames.append(data)
    
            senddata = pickle.dumps(frames)
    
           try:
    
                self.sock.sendall(struct.pack("L",len(senddata)) + senddata)
    
            except:
    
                break
    

    6.编写程序入口 main.py

    为了提供用户参数解析,代码使用了argparse。你可能对此前几个类中初始化方法的self.setDaemon(True)有疑惑。这个方法的调用使每个线程在主线程结束之后自动退出,保证程序不会出现崩溃且无法销毁的情况。在main.py中,我们通过每隔1s做一次线程的保活检查,如果视频/音频中出现阻塞/故障,主线程会终止。

    import sys

    import time

    import argparse

    from vchat importVideo_Server, Video_Client

    from achat importAudio_Server, Audio_Client

    parser = argparse.ArgumentParser()

    parser.add_argument(’–host’, type=str,default=‘127.0.0.1’)

    parser.add_argument(’–port’, type=int,default=10087)

    parser.add_argument(’–level’, type=int,default=1)

    parser.add_argument(’-v’, ‘–version’, type=int,default=4)

    args = parser.parse_args()

    IP = args.host

    PORT = args.port

    VERSION = args.version

    LEVEL = args.level

    if name == ‘main’:

    vclient = Video_Client(IP, PORT, LEVEL, VERSION)

    vserver = Video_Server(PORT, VERSION)

    aclient = Audio_Client(IP, PORT+1, VERSION)

    aserver = Audio_Server(PORT+1, VERSION)

    vclient.start()

    aclient.start()

    time.sleep(1) # make delay to start server

    vserver.start()

    aserver.start()

    whileTrue:

        time.sleep(1)
    
        ifnotvserver.isAlive() ornotvclient.isAlive():
    
            print("Video connection lost...")
    
            sys.exit(0)
    
        ifnotaserver.isAlive() ornotaclient.isAlive():
    
            print("Audio connection lost...")
    
            sys.exit(0)
    

    7.运行情况

    因为实验楼的环境没有提供摄像头,因此我们需要修改一下代码,让程序从一个本地视频文件读取,模拟摄像头的访问。将Video_Client中self.cap =cv2.VideoCapture(0)改为self.cap =cv2.VideoCapture(‘test.mp4’),即从本地视频test.mp4中读取。在修改完你的代码后,你可以通过以下命令下载test.mp4(该视频文件是周杰伦《浪漫手机》的MV),并检验代码。

    $ wgethttp://labfile.oss.aliyuncs.com/courses/671/test.mp4

    $ python2 main.py

    和上面命令一样,在本机可以通过 python2 main.py 来实验本机和本机的视频聊天,如果你有条件在同一局域网内的两台机器上实验,则可以将程序部署在两台机器上,并相互连接观察效果。下面两张图为本机上实验截图,有些情况下 PyAudio 可能会提示一些警告,你可以忽视它的提示。用户也可以指定level参数,level越高,画质越差,level为0 为原始画面,在我们的main.py中默认level为1。

    通过在某高校校园网内验证,程序可以保证长时间顺畅通话,偶尔会出现网络质量较差导致的短暂卡顿,不影响实际视频通话效果。

    五、代码获取

    你可以在我的 Github仓库 中获取到完整的代码,里面提供了Windows 版本和 Linux版本的配置、运行方案。如果你有建议或想法,欢迎提 PR 沟通。

    展开全文
  • 修正了之前发布的GLSpeaker的几个bug,将其中的exe文件覆盖之前的同名文件即可。 如果你还未下载GLSpeaker软件,可通过以下地址下载: ...
  • Discord 是一个非常受欢迎的文字和语音聊天程序。虽然开始时主要面向游戏玩家,但它几乎获得了所有人的了广泛青睐。Discord 不仅仅是一个很好的聊天客户端。当你安装它时,你还可以获得其强大的服务端功能,强力而...

    Discord 是一个非常受欢迎的文字和语音聊天程序。虽然开始时主要面向游戏玩家,但它几乎获得了所有人的了广泛青睐。

    Discord 不仅仅是一个很好的聊天客户端。当你安装它时,你还可以获得其强大的服务端功能,强力而自足。游戏玩家和非玩家都可以在几分钟内开启自己的私人聊天服务,这使 Discord 成为团队、公会和各种社区的明显选择。

    Linux 用户经常被游戏世界遗忘。但 Discord 并不是这样。它的开发人员也在 Linux 下积极构建并维护其流行聊天平台。Ubuntu 用户甚至拥有更好的待遇,Discord 捆绑在方便的 Debian/Ubuntu .deb 包中。

    获取并安装软件包

    有几种方法在 Ubuntu 下获取 Discord。你可以都看一下,并选择一个你喜欢的。

    依赖

    在 Discord 的下载页面上没有什么关于依赖的内容,但是在 Ubuntu 上安装之前,你需要安装一些依赖。没什么大不了的。打开你的终端,并输入以下命令以获得所需内容:

    sudoapt install libgconf-2-4libappindicator1

    图形化安装

    图形化安装会耗时长一点,但是这对 Linux 新手而言更简单。

    c0faf85405c3f321edab887b5f7ee753.png

    Download Discord for Linux

    首先进入 Discord 的网站。该网站应该能自动检测到你正在运行的 Linux,并在页面正中建议正确的包。

    如果没有,只需向下滚动一下。页面的下一部分会显示其他可用的下载,而 Linux 包就在那里。

    找到 Linux 的下载链接后,请检查是否选择了 “deb”,然后单击 “Download” 按钮。

    84218f018413f5f9c5ae8c1ec7c9128f.png

    Ubuntu prompt to install Discord

    浏览器会询问你想要用 Ubuntu 软件安装器打开或者下载文件。你可以选择任意一个,但是自动用软件安装器打开会更快。

    33af55901ede3c75794ceec0ce0edfbf.png

    Ubuntu Software Center Discord Inst all

    下载很快,所以安装程序会立即打开,并允许你安装新下载的软件包。这是一个非常简单的窗口,没有漂亮的图标或很多描述性的文本,所以不要犹豫。这是正常的。单击 “Install” 开始安装。

    安装过程不会耗费太长时间。之后,Discord 就可以使用了。

    命令行安装

    “懒惰”的 Linux 熟手并不在意花哨的 GUI 工具。如果你是这个阵营的人,那么你有一个更直接的命令行选项。

    首先,打开一个终端并进入你的下载目录。在那里可以使用 wget 直接下载 .deb 包。

    cd~/Downloads

    wget-O discord-0.0.1.debhttps://discordapp.com/api/download?platform=linux&format=deb

    下载完成后,你可以使用 dpkg 直接安装 .deb 软件包。运行下面的命令:

    sudodpkg-i discord-0.0.1.deb

    测试一下

    5477decad9777a2b5eee1ced9a203eb5.png

    Discord icon in Unity

    现在你可以打开软件启动器并搜索 Discord。单击图标运行。

    eb2c20d382e000f57785420a8c70baeb.png

    Login to Discord on Ubuntu

    首次启动,根据你需求,创建一个帐户或者登录。

    12392c4673ba9ca2225c202c36577b5d.png

    Discord running on Ubuntu Linux

    登录后,你就进入 Discord 了。它会提供一些介绍教程和建议。你可以直接略过并开始尝试。欢迎进入你新的 Linux 聊天体验!

    本文由 LCTT 原创编译,Linux中国 荣誉推出

    0b1331709591d260c1c78e86d0c51c18.png

    展开全文
  • 本软件为绿色软件,不用安装,复制即用。支持文本信息、视频信息、语音信息、文件传送。...使用语音聊天时,程序会有1秒左右的延迟时间,并且,为了不产生回音,最好使用耳机进行语音聊天,或者将音响音量开小。
  • 《布谷鸟2012》企业通讯软件/企业通讯工具/局域网聊天软件/局域网通讯软件/局域网通讯工具/局域网即时通讯软件/局域网即时通讯工具。  布谷鸟具有通讯聊天、群聊、传送文件、离线文件、远程协助、录音留言、消息...
  • 具体功能有: 1.采用了全新的界面风格 2.实现了基本文字聊天功能 3.实现了基本文件传送功能 4.实现了发送窗口抖动的功能 5.实现了语音聊天的功能 注:main函数在MainFrame类
  • 局域网视频语音聊天工具 V1.0 局域网聊天
  • 毕业设计制作的局域网语音聊天软件的毕业论文、答辩ppt一并上传了,不得已,赚点分,有需要的朋友下吧。论文。答辩成绩为“良好”。目录结构如下: 目录 局域网聊天工具设计 1 ——文本聊天、文件传输及用户管理 1 ...
  • 飞鸽传书是一款面向企业办公的即时通讯软件,基于TCP/IP模式。 企业员工可在企业内部或外部通过飞鸽传书进行通讯,支持消息发送,文件传输,语音视频等。为企业提供安全,稳定的即时通讯解决方案。
  • 本帖最后由 FlyHorsejun 于 2012-3-22 10:18 编辑牺牲了一个周末的时间,做了一个局域网内的聊天小软件,可以进行文字聊天与图片传输。主要讲述一下在制作过程中遇到的一些问题以及一些技巧,希望对VC驿站的朋友们...
  • 这是局域网语音聊天的服务器,支持用户的注册、登陆和注销等操作,维护一个用户信息数据库。
  • 点对点的局域网聊天软件,实现了文件传输和语音聊天

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,817
精华内容 1,126
关键字:

局域网语音聊天工具