當(dāng)前位置:首頁 > 芯聞號 > 充電吧
[導(dǎo)讀]V4L2編程?? 2010-09-10 16:26:04|??分類: 默認(rèn)分類 |??標(biāo)簽: |字號大中小?訂閱 ? 轉(zhuǎn)載: 以前做的智能家居的項目用的是Linux2.6.13的核,使用的中星

V4L2編程??

2010-09-10 16:26:04|??分類: 默認(rèn)分類 |??標(biāo)簽: |字號大中小?訂閱

?

轉(zhuǎn)載:

以前做的智能家居的項目用的是Linux2.6.13的核,使用的中星微的攝像頭,移植了spcaview進(jìn)行圖像的獲取,后來用了2.6.29的核,發(fā)現(xiàn)以前移植的spcaview不能用了,后來查了一下,發(fā)現(xiàn)2.6.29核采用了UVC的驅(qū)動(萬能驅(qū)動),采用了V4L2框架,而spcaview是基于V4L1的框架,API接口存在差異。所以需要自己寫圖片獲取的應(yīng)用程序。

?

下面主要是一些資料的總結(jié),并給出一個可以結(jié)果測試的代碼:

?

一.什么是video4linux
Video4linux2(簡稱V4L2),是linux中關(guān)于視頻設(shè)備的內(nèi)核驅(qū)動。在Linux中,視頻設(shè)備是設(shè)備文件,可以像訪問普通文件一樣對其進(jìn)行讀寫,攝像頭在/dev/video0下。

?

二.一般操作流程(視頻設(shè)備):

1.打開設(shè)備文件。 int fd=open(”/dev/video0″,O_RDWR);
2.取得設(shè)備的capability,看看設(shè)備具有什么功能,比如是否具有視頻輸入,或者音頻輸入輸出等。VIDIOC_QUERYCAP,struct v4l2_capability
3.設(shè)置視頻的制式和幀格式,制式包括PAL,NTSC,幀的格式個包括寬度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format
4.向驅(qū)動申請幀緩沖,一般不超過5個。struct v4l2_requestbuffers
5.將申請到的幀緩沖映射到用戶空間,這樣就可以直接操作采集到的幀了,而不必去復(fù)制。mmap
6.將申請到的幀緩沖全部入隊列,以便存放采集到的數(shù)據(jù).VIDIOC_QBUF,struct v4l2_buffer
7.開始視頻的采集。VIDIOC_STREAMON
8.出隊列以取得已采集數(shù)據(jù)的幀緩沖,取得原始采集數(shù)據(jù)。VIDIOC_DQBUF
9.將緩沖重新入隊列尾,這樣可以循環(huán)采集。VIDIOC_QBUF
10.停止視頻的采集。VIDIOC_STREAMOFF
11.關(guān)閉視頻設(shè)備。close(fd);


三、常用的結(jié)構(gòu)體(參見/usr/include/linux/videodev2.h):

struct v4l2_requestbuffers reqbufs;//向驅(qū)動申請幀緩沖的請求,里面包含申請的個數(shù)
struct v4l2_capability cap;//這個設(shè)備的功能,比如是否是視頻輸入設(shè)備
struct v4l2_standard std;//視頻的制式,比如PAL,NTSC
struct v4l2_format fmt;//幀的格式,比如寬度,高度等

struct v4l2_buffer buf;//代表驅(qū)動中的一幀
v4l2_std_id stdid;//視頻制式,例如:V4L2_STD_PAL_B
struct v4l2_queryctrl query;//查詢的控制
struct v4l2_control control;//具體控制的值

?

下面具體說明開發(fā)流程(網(wǎng)上找的)

打開視頻設(shè)備

在V4L2中,視頻設(shè)備被看做一個文件。使用open函數(shù)打開這個設(shè)備:

//用非阻塞模式打開攝像頭設(shè)備

intcameraFd;

cameraFd= open(“/dev/video0″, O_RDWR| O_NONBLOCK, 0);

//如果用阻塞模式打開攝像頭設(shè)備,上述代碼變?yōu)椋?/p>

//cameraFd = open(”/dev/video0″, O_RDWR, 0);

關(guān)于阻塞模式和非阻塞模式

應(yīng)用程序能夠使用阻塞模式或非阻塞模式打開視頻設(shè)備,如果使用非阻塞模式調(diào)用視頻設(shè)備,即使尚未捕獲到信息,驅(qū)動依舊會把緩存(DQBUFF)里的東西返回給應(yīng)用程序。

設(shè)定屬性及采集方式

打開視頻設(shè)備后,可以設(shè)置該視頻設(shè)備的屬性,例如裁剪、縮放等。這一步是可選的。在Linux編程中,一般使用ioctl函數(shù)來對設(shè)備的I/O通道進(jìn)行管理:

extern intioctl(int__fd, unsigned long int__request, …) __THROW;

__fd:設(shè)備的ID,例如剛才用open函數(shù)打開視頻通道后返回的cameraFd;

__request:具體的命令標(biāo)志符。

在進(jìn)行V4L2開發(fā)中,一般會用到以下的命令標(biāo)志符:

1 VIDIOC_REQBUFS:分配內(nèi)存

2 VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的數(shù)據(jù)緩存轉(zhuǎn)換成物理地址

3 VIDIOC_QUERYCAP:查詢驅(qū)動功能

4 VIDIOC_ENUM_FMT:獲取當(dāng)前驅(qū)動支持的視頻格式

5 VIDIOC_S_FMT:設(shè)置當(dāng)前驅(qū)動的頻捕獲格式

6 VIDIOC_G_FMT:讀取當(dāng)前驅(qū)動的頻捕獲格式

7 VIDIOC_TRY_FMT:驗證當(dāng)前驅(qū)動的顯示格式

8 VIDIOC_CROPCAP:查詢驅(qū)動的修剪能力

9 VIDIOC_S_CROP:設(shè)置視頻信號的邊框

10 VIDIOC_G_CROP:讀取視頻信號的邊框

11 VIDIOC_QBUF:把數(shù)據(jù)從緩存中讀取出來

12 VIDIOC_DQBUF:把數(shù)據(jù)放回緩存隊列

13 VIDIOC_STREAMON:開始視頻顯示函數(shù)

14 VIDIOC_STREAMOFF:結(jié)束視頻顯示函數(shù)

15 VIDIOC_QUERYSTD:檢查當(dāng)前視頻設(shè)備支持的標(biāo)準(zhǔn),例如PAL或NTSC。

這些IO調(diào)用,有些是必須的,有些是可選擇的。

檢查當(dāng)前視頻設(shè)備支持的標(biāo)準(zhǔn)

在亞洲,一般使用PAL(720X576)制式的攝像頭,而歐洲一般使用NTSC(720X480),使用VIDIOC_QUERYSTD來檢測:

v4l2_std_id std;

do{

ret= ioctl(fd, VIDIOC_QUERYSTD, &std);

} while(ret== -1 && errno== EAGAIN);

switch(std) {

caseV4L2_STD_NTSC:

//……

caseV4L2_STD_PAL:

//……

}

設(shè)置視頻捕獲格式

當(dāng)檢測完視頻設(shè)備支持的標(biāo)準(zhǔn)后,還需要設(shè)定視頻捕獲格式:

structv4l2_format??? fmt;

memset( &fmt, 0, sizeof(fmt) );

fmt.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;

fmt.fmt.pix.width= 720;

fmt.fmt.pix.height= 576;

fmt.fmt.pix.pixelformat= V4L2_PIX_FMT_YUYV;

fmt.fmt.pix.field= V4L2_FIELD_INTERLACED;

if(ioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {

return-1;

}

v4l2_format結(jié)構(gòu)體定義如下:

structv4l2_format

{

enumv4l2_buf_type type;??? //數(shù)據(jù)流類型,必須永遠(yuǎn)是//V4L2_BUF_TYPE_VIDEO_CAPTURE

union

{

structv4l2_pix_format??? pix;

structv4l2_window??????? win;

structv4l2_vbi_format??? vbi;

__u8??? raw_data[200];

} fmt;

};

structv4l2_pix_format

{

__u32?????????????????? width;???????? //寬,必須是16的倍數(shù)

__u32?????????????????? height;??????? //高,必須是16的倍數(shù)

__u32?????????????????? pixelformat;?? //視頻數(shù)據(jù)存儲類型,例如是//YUV4:2:2還是RGB

enumv4l2_field???????? field;

__u32?????????????????? bytesperline;

__u32?????????????????? sizeimage;

enumv4l2_colorspace??? colorspace;

__u32?????????????????? priv;

};

分配內(nèi)存

接下來可以為視頻捕獲分配內(nèi)存:

structv4l2_requestbuffers? req;

if(ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {

return-1;

}

v4l2_requestbuffers定義如下:

structv4l2_requestbuffers

{

__u32?????????????? count;? //緩存數(shù)量,也就是說在緩存隊列里保持多少張照片

enumv4l2_buf_type? type;?? //數(shù)據(jù)流類型,必須永遠(yuǎn)是V4L2_BUF_TYPE_VIDEO_CAPTURE

enumv4l2_memory??? memory; // V4L2_MEMORY_MMAP或 V4L2_MEMORY_USERPTR

__u32????????????? ?reserved[2];

};

獲取并記錄緩存的物理空間

使用VIDIOC_REQBUFS,我們獲取了req.count個緩存,下一步通過調(diào)用VIDIOC_QUERYBUF命令來獲取這些緩存的地址,然后使用mmap函數(shù)轉(zhuǎn)換成應(yīng)用程序中的絕對地址,最后把這段緩存放入緩存隊列:



typedef structVideoBuffer{

void*start;

size_t? length;

} VideoBuffer;

?

VideoBuffer*????????? buffers= calloc( req.count, sizeof(*buffers) );

structv4l2_buffer??? buf;

?

for(numBufs= 0; numBufs< req.count; numBufs++) {

memset( &buf, 0, sizeof(buf) );

buf.type= V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory= V4L2_MEMORY_MMAP;

buf.index= numBufs;

//讀取緩存

if(ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {

return-1;

}

buffers[numBufs].length= buf.length;

//轉(zhuǎn)換成相對地址

buffers[numBufs].start= mmap(NULL, buf.length,

PROT_READ| PROT_WRITE,

MAP_SHARED,

fd, buf.m.offset);

?

if(buffers[numBufs].start== MAP_FAILED) {

return-1;

}

?

//放入緩存隊列

if(ioctl(fd, VIDIOC_QBUF, &buf) == -1) {

return-1;

}

}

關(guān)于視頻采集方式

操作系統(tǒng)一般把系統(tǒng)使用的內(nèi)存劃分成用戶空間和內(nèi)核空間,分別由應(yīng)用程序管理和操作系統(tǒng)管理。應(yīng)用程序可以直接訪問內(nèi)存的地址,而內(nèi)核空間存放的是供內(nèi)核訪問的代碼和數(shù)據(jù),用戶不能直接訪問。v4l2捕獲的數(shù)據(jù),最初是存放在內(nèi)核空間的,這意味著用戶不能直接訪問該段內(nèi)存,必須通過某些手段來轉(zhuǎn)換地址。

一共有三種視頻采集方式:使用read、write方式;內(nèi)存映射方式和用戶指針模式。

read、write方式:在用戶空間和內(nèi)核空間不斷拷貝數(shù)據(jù),占用了大量用戶內(nèi)存空間,效率不高。

內(nèi)存映射方式:把設(shè)備里的內(nèi)存映射到應(yīng)用程序中的內(nèi)存控件,直接處理設(shè)備內(nèi)存,這是一種有效的方式。上面的mmap函數(shù)就是使用這種方式。

用戶指針模式:內(nèi)存片段由應(yīng)用程序自己分配。這點(diǎn)需要在v4l2_requestbuffers里將memory字段設(shè)置成V4L2_MEMORY_USERPTR。

處理采集數(shù)據(jù)

V4L2有一個數(shù)據(jù)緩存,存放req.count數(shù)量的緩存數(shù)據(jù)。數(shù)據(jù)緩存采用FIFO的方式,當(dāng)應(yīng)用程序調(diào)用緩存數(shù)據(jù)時,緩存隊列將最先采集到的 視頻數(shù)據(jù)緩存送出,并重新采集一張視頻數(shù)據(jù)。這個過程需要用到兩個ioctl命令,VIDIOC_DQBUF和VIDIOC_QBUF:

structv4l2_buffer buf;

memset(&buf,0,sizeof(buf));

buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory=V4L2_MEMORY_MMAP;

buf.index=0;

//讀取緩存

if(ioctl(cameraFd, VIDIOC_DQBUF, &buf) == -1)

{

return-1;

}

//…………視頻處理算法

//重新放入緩存隊列

if(ioctl(cameraFd, VIDIOC_QBUF, &buf) == -1) {

return-1;

}

關(guān)閉視頻設(shè)備

使用close函數(shù)關(guān)閉一個視頻設(shè)備

close(cameraFd)

還需要使用munmap方法。

?

下面是代碼,加了一部分注釋:

//#加了點(diǎn)注釋

?

//#Rockie Cheng

?

?

#include

#include

#include

#include

?

#include ???????????

?

#include ????????????

#include

#include

#include

#include

#include

#include

#include

#include

?

#include ????????

#include

?

#define CLEAR(x) memset (&(x), 0, sizeof (x))

?

struct buffer {

??????? void *????????????????? start;

??????? size_t????????????????? length;

};

?

static char *?????????? dev_name??????? = "/dev/video0";//攝像頭設(shè)備名

static int????????????? fd????????????? = -1;

struct buffer *???????? buffers???????? = NULL;

static unsigned int???? n_buffers?????? = 0;

?

FILE *file_fd;

static unsigned long file_length;

static unsigned char *file_name;

//////////////////////////////////////////////////////

//獲取一幀數(shù)據(jù)

//////////////////////////////////////////////////////

static int read_frame (void)

{

struct v4l2_buffer buf;

unsigned int i;

?

CLEAR (buf);

buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

buf.memory = V4L2_MEMORY_MMAP;

int ff = ioctl (fd, VIDIOC_DQBUF, &buf);

if(ff<0)

printf("failturen"); //出列采集的幀緩沖

?

assert (buf.index < n_buffers);

?? printf ("buf.index dq is %d,n",buf.index);

?

fwrite(buffers[buf.index].start, buffers[buf.index].length, 1, file_fd); //將其寫入文件中

?

ff=ioctl (fd, VIDIOC_QBUF, &buf); //再將其入列

if(ff<0)

printf("failture VIDIOC_QBUFn");

return 1;

}

?

int main (int argc,char ** argv)

{

struct v4l2_capability cap;

struct v4l2_format fmt;

unsigned int i;

enum v4l2_buf_type type;

?

file_fd = fopen("test-mmap.jpg", "w");//圖片文件名

?

fd = open (dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);//打開設(shè)備

?

int ff=ioctl (fd, VIDIOC_QUERYCAP, &cap);//獲取攝像頭參數(shù)

if(ff<0)

printf("failture VIDIOC_QUERYCAPn");

?

?????? struct v4l2_fmtdesc fmt1;

??????? int ret;

?????? memset(&fmt1, 0, sizeof(fmt1));

?????? fmt1.index = 0;

?????? fmt1.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

?????? while ((ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmt1)) == 0)

?????? {

????????????? fmt1.index++;

????????????? printf("{ pixelformat = '%c%c%c%c', description = '%s' }n",

??????????????????????????? fmt1.pixelformat & 0xFF, (fmt1.pixelformat >> 8) & 0xFF,

??????????????????????????? (fmt1.pixelformat >> 16) & 0xFF, (fmt1.pixelformat >> 24) & 0xFF,

??????????????????????????? fmt1.description);

?????????????

?????? }

CLEAR (fmt);

fmt.type??????????????? = V4L2_BUF_TYPE_VIDEO_CAPTURE;

fmt.fmt.pix.width?????? = 640;

fmt.fmt.pix.height????? = 480;

fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;//V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YVU420;//V4L2_PIX_FMT_YUYV;

fmt.fmt.pix.field?????? = V4L2_FIELD_INTERLACED;

ff = ioctl (fd, VIDIOC_S_FMT, &fmt); //設(shè)置圖像格式

if(ff<0)

printf("failture VIDIOC_S_FMTn");

file_length = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height; //計算圖片大小

?

struct v4l2_requestbuffers req;

CLEAR (req);

req.count?????????????? = 1;

req.type??????????????? = V4L2_BUF_TYPE_VIDEO_CAPTURE;

req.memory????????????? = V4L2_MEMORY_MMAP;

?

ioctl (fd, VIDIOC_REQBUFS, &req); //申請緩沖,count是申請的數(shù)量

if(ff<0)

printf("failture VIDIOC_REQBUFSn");

if (req.count < 1)

?? printf("Insufficient buffer memoryn");

?

buffers = calloc (req.count, sizeof (*buffers));//內(nèi)存中建立對應(yīng)空間

?

for (n_buffers = 0; n_buffers < req.count; ++n_buffers)

{

?? struct v4l2_buffer buf;?? //驅(qū)動中的一幀

?? CLEAR (buf);

?? buf.type??????? = V4L2_BUF_TYPE_VIDEO_CAPTURE;

?? buf.memory????? = V4L2_MEMORY_MMAP;

?? buf.index?????? = n_buffers;

?

?? if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buf)) //映射用戶空間

??? printf ("VIDIOC_QUERYBUF errorn");

?

?? buffers[n_buffers].length = buf.length;

?? buffers[n_buffers].start =

?? mmap (NULL /* start anywhere */,??? //通過mmap建立映射關(guān)系

??? buf.length,

??? PROT_READ | PROT_WRITE /* required */,

??? MAP_SHARED /* recommended */,

??? fd, buf.m.offset);

?

?? if (MAP_FAILED == buffers[n_buffers].start)

??? printf ("mmap failedn");

??????? }

?

for (i = 0; i < n_buffers; ++i)

{

?? struct v4l2_buffer buf;

?? CLEAR (buf);

?

?? buf.type??????? = V4L2_BUF_TYPE_VIDEO_CAPTURE;

?? buf.memory????? = V4L2_MEMORY_MMAP;

?? buf.index?????? = i;

?

?? if (-1 == ioctl (fd, VIDIOC_QBUF, &buf))//申請到的緩沖進(jìn)入列隊

??? printf ("VIDIOC_QBUF failedn");

}

???????????????

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

?

if (-1 == ioctl (fd, VIDIOC_STREAMON, &type)) //開始捕捉圖像數(shù)據(jù)

?? printf ("VIDIOC_STREAMON failedn");

?

for (;;) //這一段涉及到異步IO

{

?? fd_set fds;

?? struct timeval tv;

?? int r;

?

?? FD_ZERO (&fds);//將指定的文件描述符集清空

?? FD_SET (fd, &fds);//在文件描述符集合中增加一個新的文件描述符

?

?? /* Timeout. */

?? tv.tv_sec = 2;

?? tv.tv_usec = 0;

?

?? r = select (fd + 1, &fds, NULL, NULL, &tv);//判斷是否可讀(即攝像頭是否準(zhǔn)備好),tv是定時

?

?? if (-1 == r) {

??? if (EINTR == errno)

???? continue;

??? printf ("select errn");

??????????????????????? }

?? if (0 == r) {

??? fprintf (stderr, "select timeoutn");

??? exit (EXIT_FAILURE);

???? ???????????????????}

?

?? if (read_frame ())//如果可讀,執(zhí)行read_frame ()函數(shù),并跳出循環(huán)

?? break;

}

?

unmap:

for (i = 0; i < n_buffers; ++i)

?? if (-1 == munmap (buffers[i].start, buffers[i].length))

??? printf ("munmap error");

type = V4L2_BUF_TYPE_VIDEO_CAPTURE;??

?? ?????if (-1 == ioctl(fd, VIDIOC_STREAMOFF, &type))??

??????????? printf("VIDIOC_STREAMOFF");

close (fd);

fclose (file_fd);

exit (EXIT_SUCCESS);

return 0;

}

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運(yùn)營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉