多媒体开发之cgo

     go语言作为近十年来优秀的现代开发语言的代表,由于继承了c语言的简洁和很多现代语言的表达方式,在广泛的应用场景中得到众多爱好者的喜爱,如何将go和c、c++进行联合开发,拓展整个开发生态,不用重复造轮子,掌握cgo可以让你得心应手的在c和go之间传递信息,打通任督二脉。

    go在流媒体传输领域也有很强大的生态和优秀的轮子,比起传统的ffmpeg这种大而全的库,可以选择性的用一些小巧强悍的go语言写的库来替代ffmpeg,比如rtsp拉流,笔者用ffmpeg在android下写了一个推拉流的播放器,但是由于ffmpeg自成体系,在灵活定制方面有一些局限性,于是尝试用go rtsp来代替ffmpeg的rtsp拉流。

    首先我们需要利用cgo的交叉编译特性封装一个可以被c/c++调用的动态库,其中用到了cgo 进行设置和回调函数,并传递了类的指针,从而实现了c++ class的特性,达到了面向对象多路连接的设计目标。以下是go写的动态库源码,由于go的包管理做的特别棒,你可以用很少的代码实现一个多路拉流的应用。感觉比c++爽多了。

 

 

package mainimport ("fmt""sync""time""unsafe""github.com/deepch/vdk/av""github.com/deepch/vdk/codec/h264parser""github.com/deepch/vdk/format/rtsp"
)/*
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#cgo CFLAGS: -I.
void OnSendPacket(void * callclass,unsigned char *data, int len,int mediatype);
typedef void (*CallbackFunc)(unsigned char *,int,int);
*/
import "C"// 导出的回调函数类型
// type
// void SendcallbackFunc(unsigned char* data, int length);
// extern CallbackFunc _callback;
//
//	static void setCallbackWrapper(CallbackFunc callback) {
//	    _callback = callback;
//	}
type CallbackFunc func(*byte, int, int)// 接口定义
type MediaInterface interface {SetCallback(callback CallbackFunc)
}// 结构体实现接口
type MediaImplementation struct {id       intcallback CallbackFunc// C._callbackcallclass unsafe.Pointer // 修改为unsafe.Pointer类型
}var (instances     = make(map[int]*MediaImplementation)instancesLock sync.MutexcallbackLock  sync.Mutex
)func (m *MediaImplementation) SendData(data []byte, length int, mediatype int) {// if C._callback != nil {fmt.Println("callback data len", length, mediatype)// arr := (*C.uchar)(unsafe.Pointer(&data[0]))// C.OnSendPacket(arr, C.int(length), C.int(mediatype))
}func (m *MediaImplementation) CallBackData(data *byte, length int, mediatype int) {goSlice := (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length]m.SendData(goSlice, length, mediatype)
}func (m *MediaImplementation) RtspClientStart(uri string) {// m.rtsp = unsafe.Pointer(&Rtspclient{// 	callback: m.CallBackData,// })// ((*Rtspclient)(m.rtsp)).rtspclient(uri)m.rtspConsumer(uri)
}func (m *MediaImplementation) SetCallback(callback CallbackFunc) {m.callback = callback
}//export CallRtspClientStart
func CallRtspClientStart(impl unsafe.Pointer, data *C.char) {if impl == nil {fmt.Println("Invalid implementation")return}m := (*MediaImplementation)(impl)m.rtspConsumer(C.GoString(data))// m.RtspClientStart(C.GoString(data))
}//export NewMediaImplementation
func NewMediaImplementation() uintptr {instancesLock.Lock()defer instancesLock.Unlock()id := len(instances) + 1instance := &MediaImplementation{id: id,}instances[id] = instancereturn uintptr(unsafe.Pointer(instance))
}//export GetMediaInstanceByID
func GetMediaInstanceByID(id C.int) uintptr { // 修改返回类型为uintptrinstancesLock.Lock()defer instancesLock.Unlock()if instance, ok := instances[int(id)]; ok {return uintptr(unsafe.Pointer(instance))}return uintptr(0) // 返回表示未找到实例
}//export CallSendData
func CallSendData(impl unsafe.Pointer, data *C.uchar, length C.int, mediatype C.int) {if impl == nil {fmt.Println("Invalid implementation")return}goSlice := (*[1 << 30]byte)(unsafe.Pointer(data))[:length:length]m := (*MediaImplementation)(impl)go m.SendData(goSlice, int(length), int(mediatype))
}//export SetCallbackWrapper
func SetCallbackWrapper(impl unsafe.Pointer, callback unsafe.Pointer) {if impl == nil {fmt.Println("Invalid implementation")return}m := (*MediaImplementation)(impl)m.callclass = callbackfmt.Println("SetCallbackWrapper callback", m.callback, callback)// C._callback = callbackFunc// m.SetCallback(callbackFunc)
}func main() {}func (m *MediaImplementation) rtspConsumer(uri string) {annexbNALUStartCode := func() []byte { return []byte{0x00, 0x00, 0x00, 0x01} }//fmt.Println("rtspConsumer starting...")session, err := rtsp.DialTimeout(uri, 10*time.Second)// defer session.Close()if err != nil {fmt.Errorf("rtsp Dial Error: %v", err)return//panic(err)}//session.RtpKeepAliveTimeout = 10 * time.Secondsession.RtpTimeout = 20 * time.Secondsession.RtpKeepAliveTimeout = 5 * time.Secondsession.RtspTimeout = 20 * time.Secondcodecs, err := session.Streams()if err != nil {fmt.Errorf("stream error: %v", err)// continue//panic(err)}for i, t := range codecs {fmt.Printf("Stream", i, "is of type", t.Type().String())}if codecs[0].Type() != av.H264 {//fmt.Println("RTSP feed must begin with a H264 codec")// continue//panic("RTSP feed must begin with a H264 codec")}if len(codecs) != 1 {//fmt.Println("Ignoring all but the first stream.")}// var previousTime time.Durationfor {// select {// case <-rtspsrcch:// default:// if KVMrtsp.BInUse != true {// 	break// }pkt, err := session.ReadPacket()if err != nil {break}// if pkt.Idx != 0 {// 	//audio or other stream, skip it// 	continue// }if codecs[pkt.Idx].Type().IsVideo() {pkt.Data = pkt.Data[4:]// For every key-frame pre-pend the SPS and PPSif pkt.IsKeyFrame {pkt.Data = append(annexbNALUStartCode(), pkt.Data...)pkt.Data = append(codecs[0].(h264parser.CodecData).PPS(), pkt.Data...)pkt.Data = append(annexbNALUStartCode(), pkt.Data...)pkt.Data = append(codecs[0].(h264parser.CodecData).SPS(), pkt.Data...)pkt.Data = append(annexbNALUStartCode(), pkt.Data...)} else {pkt.Data = append(annexbNALUStartCode(), pkt.Data...)}// bufferDuration := pkt.Time - previousTime// previousTime = pkt.Time// m.CallBackData(pkt.Data,len(pkt.Data))fmt.Println("Is Video IsKeyFrame", pkt.IsKeyFrame, codecs[pkt.Idx].Type(), pkt.Time)mediatype := 0switch codecs[pkt.Idx].Type() {case av.H264:mediatype = 1case av.H265:mediatype = 2}length := len(pkt.Data)arr := (*C.uchar)(unsafe.Pointer(&pkt.Data[0]))C.OnSendPacket(m.callclass, arr, C.int(length), C.int(mediatype))// if err = KVMrtsp.Track.WriteSample(media.Sample{Data: pkt.Data, Duration: bufferDuration}); err != nil && err != io.ErrClosedPipe {// 	logger.Errorf("WriteSample error %v", err)// 	break// 	//panic(err)// }} else if codecs[pkt.Idx].Type().IsAudio() {// codecs, err = session.Streams()// if err != nil {// 	fmt.Println("Error getting streams")// 	break// }codec := codecs[pkt.Idx].(av.AudioCodecData)duration, err := codec.PacketDuration(pkt.Data)if err != nil {fmt.Println("Failed to get duration for audio:", err)break}fmt.Println("IS Audio Packet duration:", duration)mediatype := 0switch codecs[pkt.Idx].Type() {case av.AAC:mediatype = 3case av.PCM_MULAW:mediatype = 4case av.PCM_ALAW:mediatype = 5case av.SPEEX:mediatype = 6case av.NELLYMOSER:mediatype = 7case av.PCM:mediatype = 8case av.OPUS:mediatype = 9}length := len(pkt.Data)arr := (*C.uchar)(unsafe.Pointer(&pkt.Data[0]))C.OnSendPacket(m.callclass, arr, C.int(length), C.int(mediatype))// m.CallBackData(pkt.Data,len(pkt.Data))// err = audioTrack.WriteSample(media.Sample{Data: pkt.Data, Duration: duration})// if err != nil {// 	//fmt.Println("Failed to write audio sample", err)// 	break// }}}if err = session.Close(); err != nil {fmt.Errorf("session Close error %v", err)return}// time.Sleep(5 * time.Second)// }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/29495.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P1403 [AHOI2005] 约数研究

题目描述 科学家们在 Samuel 星球上的探险得到了丰富的能源储备&#xff0c;这使得空间站中大型计算机 Samuel II 的长时间运算成为了可能。由于在去年一年的辛苦工作取得了不错的成绩&#xff0c;小联被允许用 Samuel II 进行数学研究。 小联最近在研究和约数有关的问题&…

【redis】redis管道简述

redis管道可以一次性发送多条命令。 命令示例如下&#xff1a; [xxxlocalhost ~]$ echo -e "set k4 99\nincr k4\nget k4" | nc localhost 6379 \OK :100 $3 100下面先简述一下这条命令的组成&#xff0c;再简述一下管道的常用场景和注意事项。 首先&#xff0c;|是…

IDEA 搭建Android 开发环境

项目实战 废话不多说开始创建先第一个 Android 项目 步骤一 FILE → New → Project 步骤二-选择 Android 项目模板 选那个安卓机器人,如果没有这个选项,需要升级IDEA版本或者安装安卓插件 选择*Basic Activity* Next-下一步 步骤三-项目初始化 名称、包名、安装位置自行调整…

双电源并用问题与解决方案

双电源并用问题 曾经有客户在电源模块应用过程中出现过这样的应用场景&#xff0c;如下图1所示。客户使用两路电源给后端电路进行供电&#xff0c;要求在不断电的情况下切换输入电源&#xff0c;此过程中发现后端电路会出现损坏。对各个节点波形进行分析后发现&#xff0c;在给…

【C++】STL之list容器的模拟实现

个人主页&#xff1a;&#x1f35d;在肯德基吃麻辣烫 分享一句喜欢的话&#xff1a;热烈的火焰&#xff0c;冰封在最沉默的火山深处。 文章目录 前言一、list的三个类的关系分析图vector和list的区别1.节点的成员变量以及构造函数2.list的迭代器 二、list的增删查改工作2.1inse…

MFC第十六天 CFileDialog、CEdit简介、(线程)进程的启动,以及Notepad的开发(托盘技术-->菜单功能)

文章目录 CCommonDialogCFileDialogCEdit托盘技术进程的启动附录1:启动线程方式附录2:MFC对话框的退出过程 CCommonDialog 通用对话框 CCommonDialog 这些对话框类封装 Windows 公共对话框。 它们提供了易于使用的复杂对话框实现。 CFileDialog 提供用于打开或保存文件的标准对…

C#在工业自动化领域的应用前景如何?

在2021年&#xff0c;C#与工业自动化已经开始结合&#xff0c;并且这种趋势有望在未来继续发展。C#是一种功能强大的编程语言&#xff0c;其面向对象的特性、跨平台支持以及丰富的类库和工具&#xff0c;使其成为在工业自动化领域应用的有力工具。 我这里刚好有嵌入式、单片机…

Nautlius Chain主网正式上线,模块Layer3时代正式开启

Nautilus Chain 是在 Vitalik Buterin 提出 Layer3 理念后&#xff0c; 对 Layer3 领域的全新探索。作为行业内首个模块化 Layer3 链&#xff0c;我们正在对 Layer3 架构进行早期的定义&#xff0c;并有望进一步打破公链赛道未来长期的发展格局。 在今年年初&#xff0c;经过我…

Spring Cloud 之 Gateway 网关

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

springboot第30集:springboot集合问题

Logstash Logstash 是开源的服务器端数据处理管道&#xff0c;能够同时从多个来源采集数据、格式化数据&#xff0c;然后将数据发送到es进行存储。 ElasticSearch Elasticsearch 是基于JSON的分布式搜索和分析引擎&#xff0c;是利用倒排索引实现的全文索引。 KibanaKibana 能够…

<C语言> 自定义类型

1.结构体 结构体是一种用户自定义的数据类型&#xff0c;允许将不同类型的数据项组合在一起&#xff0c;形成一个更大的数据结构。结构体可以包含多个成员变量&#xff0c;每个成员变量可以是不同的数据类型&#xff0c;如整数、字符、浮点数等&#xff0c;甚至可以包含其他结构…

能源监测系统:实时监控+数据可视化

能源监测系统是应用物联网技术&#xff0c;对水、电、气、热等能源进行实时监测的系统&#xff0c;能够对各种设备数据进行智能化标准化的管理&#xff0c;从而建立起统一的管理优化平台&#xff0c;是积极响应国家节能降耗政策的典型模范&#xff0c;也是企业建设节能型工厂的…