当前位置: 首页 > news >正文

Windows软件插件-音视频捕获

下载本插件
音视频捕获就是获取电脑外接的话筒,摄像头,或线路输入的音频和视频。
本插件捕获电脑外接的音频和视频。最多可以同时获取4个视频源和4个音频源。插件可以在win32和MFC程序中使用。

使用方法

首先,加载本“捕获”DLL,获得DLL模块句柄。

	HMODULE hCap = LoadLibrary(L"捕获.dll");//加载捕获模块

获取所有导出函数的地址。

typedef int(__cdecl *MYPROC_Sample)(LONGLONG star, LONGLONG end, BYTE* pB, LONG len);struct CAP_INIT
{MYPROC_Sample VideoSample0 = NULL;//视频样本输出函数MYPROC_Sample VideoSample1 = NULL;MYPROC_Sample VideoSample2 = NULL;MYPROC_Sample VideoSample3 = NULL;MYPROC_Sample AudioSample0 = NULL;//音频样本输出函数MYPROC_Sample AudioSample1 = NULL;MYPROC_Sample AudioSample2 = NULL;MYPROC_Sample AudioSample3 = NULL;
};
struct CAP_VIDEO_INFO
{UINT32 Width=0;//视频宽,单位像素UINT32 Height=0;//视频高,单位像素double nFrames=0;//帧率WCHAR Name[MAX_PATH];//视频捕获设备名称
};
struct CAP_AUDIO_INFO
{UINT32 nCh;//声道数WCHAR Name[MAX_PATH];//音频捕获设备名称
};typedef int(__cdecl *MYPROC_CAP_Init)(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo);
typedef int(__cdecl *MYPROC_VOID)();MYPROC_CAP_Init CAP_Init=NULL;
MYPROC_VOID CAP_ReleaseDevice=NULL;if (hCap){CAP_Init=(MYPROC_CAP_Init)GetProcAddress(hCap, "Init");//“初始化”函数CAP_ReleaseDevice=(MYPROC_VOID)GetProcAddress(hCap, "ReleaseDevice");//“释放所有捕获设备”函数}

定义8个全局函数,以提供给初始化函数;全局函数用于输出视频和音频样本。

int VideoSample0(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流1样本输出函数
{return 1; 
}int VideoSample1(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流2样本输出函数
{return 1; 
}int VideoSample2(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流3样本输出函数
{return 1; 
}int VideoSample3(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//视频流4样本输出函数
{return 1; 
}int AudioSample0(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流1样本输出函数
{return 1; 
}int AudioSample1(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流2样本输出函数
{return 1; 
}int AudioSample2(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流3样本输出函数
{return 1; 
}int AudioSample3(LONGLONG star, LONGLONG end, BYTE* pB, LONG len)//音频流4样本输出函数
{return 1; 
}CAP_INIT CapInit;CapInit.VideoSample0 = &VideoSample0;//为视频,音频源指定输出函数CapInit.VideoSample1 = &VideoSample1;CapInit.VideoSample2 = &VideoSample2;CapInit.VideoSample3 = &VideoSample3;CapInit.AudioSample0 = &AudioSample0;CapInit.AudioSample1 = &AudioSample1;CapInit.AudioSample2 = &AudioSample2;CapInit.AudioSample3 = &AudioSample3;

捕获视频和音频。
CAP_Init函数为每一个捕获设备(接口为IMFMediaSource)创建1个源读取器(接口为IMFSourceReader),并指定输出媒体类型;
为每一个捕获设备创建1个线程,在线程中使用源读取器接口读取样本,反复调用CAP_INIT结构提供的样本输出函数输出视频和音频样本。

		CAP_VIDEO_INFO* pVInfo =  new CAP_VIDEO_INFO[4];//结构数组用于存储视频流,音频流信息CAP_VIDEO_INFO* pAInfo = new CAP_AUDIO_INFO[4];WORD w = CAP_Init(CapInit, pVInfo, pAInfo);//获取捕获设备,创建视频流,音频流VCount = LOBYTE(w); //获取视频流数量ACount = HIBYTE(w);//获取音频流数量	

释放所有获取到的捕获设备。
函数将终止所有捕获线程,并释放所有源读取器接口。

		CAP_ReleaseDevice();

有关本插件的使用示例,可以看后续文章“Windows应用-音视频捕获”。

“捕获”DLL的全部代码

DLL.h

#pragma once
#include <SDKDDKVer.h>#define WIN32_LEAN_AND_MEAN             // 从 Windows 头中排除极少使用的资料
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS      // 某些 CString 构造函数将是显式的
#define _AFX_NO_MFC_CONTROLS_IN_DIALOGS         // 移除对话框中的 MFC 控件支持#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN            // 从 Windows 头中排除极少使用的资料
#endif#include <afx.h>
#include <afxwin.h>         // MFC 核心组件和标准组件
#include <afxext.h>         // MFC 扩展
#ifndef _AFX_NO_OLE_SUPPORT
#include <afxdtctl.h>           // MFC 对 Internet Explorer 4 公共控件的支持
#endif
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h>                     // MFC 对 Windows 公共控件的支持
#endif // _AFX_NO_AFXCMN_SUPPORT// Windows 头文件: 
#include <windows.h>#include "mfidl.h"
#include "mfapi.h"
#pragma comment(lib, "mfuuid")
#pragma comment(lib, "mf")
#pragma comment(lib, "mfplat")#include "mfreadwrite.h"
#pragma comment(lib, "mfreadwrite")#ifndef  SAFE_RELEASE
#define SAFE_RELEASEtemplate <class T> void SafeRelease(T** ppT)
{if (*ppT){(*ppT)->Release();*ppT = NULL;}
}#endif //SAFE_RELEASEtypedef int(__cdecl *MYPROC_Sample)(LONGLONG star, LONGLONG end, BYTE* pB, LONG len);struct CAP_INIT
{MYPROC_Sample VideoSample0 = NULL;//视频样本输出函数MYPROC_Sample VideoSample1 = NULL;MYPROC_Sample VideoSample2 = NULL;MYPROC_Sample VideoSample3 = NULL;MYPROC_Sample AudioSample0 = NULL;//音频样本输出函数MYPROC_Sample AudioSample1 = NULL;MYPROC_Sample AudioSample2 = NULL;MYPROC_Sample AudioSample3 = NULL;
};struct CAP_VIDEO_INFO
{UINT32 Width=0;//视频宽,单位像素UINT32 Height=0;//视频高,单位像素double nFrames=0;//帧率WCHAR Name[MAX_PATH];//视频捕获设备名称
};struct CAP_AUDIO_INFO
{UINT32 nCh;//声道数WCHAR Name[MAX_PATH];//音频捕获设备名称
};class Capture
{
public:Capture();~Capture();CAP_INIT mInit;CAP_VIDEO_INFO mVideoInfo[4];HANDLE hEXIT = NULL;HANDLE hVThread[4];HANDLE hAThread[4];IMFSourceReader* pVideoReader[4];//视频源读取器指针数组IMFSourceReader* pAudioReader[4];//音频源读取器指针数组void GetDevice(int& mVideo, CAP_VIDEO_INFO* VideoInfo, int& mAudio, CAP_AUDIO_INFO* AudioInfo);//获取前4个音频,视频捕获设备void ReleaseDevice();//释放捕获的设备int Init(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo);//初始化捕获设备
};struct PARAM
{Capture* pCapture = NULL;int index;
};

DLL.cpp

#include "DLL.h"Capture::Capture()
{HRESULT hr = MFStartup(MF_VERSION);hEXIT = CreateEvent(NULL, TRUE, FALSE, NULL);//创建“退出”事件。手动重置,初始无信号
}Capture::~Capture()
{CloseHandle(hEXIT);MFShutdown();
}DWORD WINAPI AudioThread(LPVOID lp);
DWORD WINAPI VideoThread(LPVOID lp);void Capture::GetDevice(int& mVideo, CAP_VIDEO_INFO* VideoInfo, int& mAudio, CAP_AUDIO_INFO* AudioInfo)//获取捕获设备
{HRESULT hr;IMFAttributes *pVideoAttributes = NULL;hr = MFCreateAttributes(&pVideoAttributes, 1);hr = pVideoAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID);UINT32 countV;IMFActivate **ppVideoDevices = NULL;hr = MFEnumDeviceSources(pVideoAttributes, &ppVideoDevices, &countV);//枚举视频捕获设备SafeRelease(&pVideoAttributes);if (countV > 4){for (int i = 4; i < (int)countV; i++)//释放4个设备后面的设备{ppVideoDevices[i]->Release();}countV = 4;}mVideo = countV;for (DWORD i = 0; i < countV; i++)//获取所有视频源信息,为视频源创建读取样本线程{hr = S_OK;WCHAR *szFriendlyName = NULL;UINT32 cchName;hr = ppVideoDevices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &szFriendlyName, &cchName); //获取显示名称wmemset(VideoInfo[i].Name, 0, MAX_PATH);CopyMemory(VideoInfo[i].Name, szFriendlyName, 2 * (wcslen(szFriendlyName) + 1));CoTaskMemFree(szFriendlyName);IMFMediaSource *pSource = NULL;if (SUCCEEDED(hr)){hr = ppVideoDevices[i]->ActivateObject(IID_IMFMediaSource, (void**)&pSource);//激活对象}IMFAttributes* pIMFAttributes = NULL;if (SUCCEEDED(hr)){hr = MFCreateAttributes(&pIMFAttributes, 0);//创建空的属性}if (SUCCEEDED(hr)){hr = pIMFAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, (UINT32)1);//使用基于硬件的媒体基础转换}if (SUCCEEDED(hr)){hr = pIMFAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, (UINT32)1);//允许源读取器进行有限的视频处理}hr = MFCreateSourceReaderFromMediaSource(pSource, pIMFAttributes, &pVideoReader[i]);//从媒体源创建源读取器SafeRelease(&pIMFAttributes); SafeRelease(&pSource);IMFMediaType* pVideoMTV = NULL;if (SUCCEEDED(hr)){hr = MFCreateMediaType(&pVideoMTV);//创建空的媒体类型}if (SUCCEEDED(hr)){hr = pVideoMTV->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);//设置主要类型为视频}if (SUCCEEDED(hr)){hr = pVideoMTV->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);//设置子类型RGB32}if (SUCCEEDED(hr)){hr = pVideoReader[i]->SetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, pVideoMTV);//设置源读取器视频输出媒体类型}SafeRelease(&pVideoMTV);IMFMediaType* pMT = NULL;if (SUCCEEDED(hr)){hr = pVideoReader[i]->GetCurrentMediaType(MF_SOURCE_READER_FIRST_VIDEO_STREAM, &pMT);//获取源读取器视频输出媒体类型。此时可以获得完整的输出媒体类型信息}UINT32 Width, Height;if (SUCCEEDED(hr)){hr = MFGetAttributeSize(pMT, MF_MT_FRAME_SIZE, &Width, &Height);//获取视频宽高VideoInfo[i].Width = Width; VideoInfo[i].Height = Height;}UINT32 Numerator, Denominator;if (SUCCEEDED(hr)){hr = MFGetAttributeRatio(pMT, MF_MT_FRAME_RATE, &Numerator, &Denominator);//获取帧率VideoInfo[i].nFrames = (double)Numerator / (double)Denominator;}SafeRelease(&pMT);mVideoInfo[i] = VideoInfo[i];PARAM* pParam = new PARAM();pParam->index = i;pParam->pCapture = this;hVThread[i] = CreateThread(NULL, 0, VideoThread, pParam, 0, NULL);//为视频源创建读取线程}CoTaskMemFree(ppVideoDevices);//释放视频捕获设备接口指针数组使用的内存IMFAttributes *pAudioAttributes = NULL;hr = MFCreateAttributes(&pAudioAttributes, 1);//创建空的属性hr = pAudioAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID);UINT32 countA;IMFActivate **ppAudioDevices = NULL;hr = MFEnumDeviceSources(pAudioAttributes, &ppAudioDevices, &countA);//枚举音频捕获设备SafeRelease(&pAudioAttributes);if (countA > 4){for (int i = 4; i < (int)countA; i++)//释放4个设备后面的设备{ppAudioDevices[i]->Release();}countA = 4;}mAudio = countA;for (DWORD i = 0; i < countA; i++){hr = S_OK;WCHAR *szFriendlyName = NULL;UINT32 cchName;hr = ppAudioDevices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &szFriendlyName, &cchName); //获取捕获设备名称wmemset(AudioInfo[i].Name, 0, MAX_PATH);CopyMemory(AudioInfo[i].Name, szFriendlyName, 2*(wcslen(szFriendlyName) + 1));CoTaskMemFree(szFriendlyName);IMFMediaSource *pSource = NULL;if (SUCCEEDED(hr)){hr = ppAudioDevices[i]->ActivateObject(IID_IMFMediaSource, (void**)&pSource);//激活对象}IMFAttributes* pIMFAttributes = NULL;if (SUCCEEDED(hr)){hr = MFCreateAttributes(&pIMFAttributes, 0);}if (SUCCEEDED(hr)){hr = pIMFAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, (UINT32)1);//使用基于硬件的媒体基础转换}if (SUCCEEDED(hr)){hr = pIMFAttributes->SetUINT32(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING, (UINT32)1);//允许源读取器进行有限的视频处理}if (SUCCEEDED(hr)){hr = MFCreateSourceReaderFromMediaSource(pSource, pIMFAttributes, &pAudioReader[i]);//从媒体源创建源读取器}SafeRelease(&pIMFAttributes);SafeRelease(&pSource);IMFMediaType* pAudioMTA = NULL;if (SUCCEEDED(hr)){hr = MFCreateMediaType(&pAudioMTA);//创建空的媒体类型}if (SUCCEEDED(hr)){hr = pAudioMTA->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio);//设置主要类型音频}if (SUCCEEDED(hr)){hr = pAudioMTA->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM);//设置子类型PCM}if (SUCCEEDED(hr)){hr = pAudioMTA->SetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, (UINT32)16);//设置样本16位}if (SUCCEEDED(hr)){hr = pAudioMTA->SetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, (UINT32)48000);//设置样本采样率48000}if (SUCCEEDED(hr)){hr = pAudioReader[i]->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, pAudioMTA);//设置音频输出媒体类型(此时未提供媒体类型全部信息)}SafeRelease(&pAudioMTA);IMFMediaType* pMT = NULL;if (SUCCEEDED(hr)){hr = pAudioReader[i]->GetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, &pMT);//获取音频输出媒体类型,此时可以得到完整的媒体类型信息}UINT32 nch;if (SUCCEEDED(hr)){hr = pMT->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &nch);//获取声道数AudioInfo[i].nCh = nch;}SafeRelease(&pMT);PARAM* pParam = new PARAM();pParam->index = i;pParam->pCapture = this;hAThread[i] = CreateThread(NULL, 0, AudioThread, pParam, 0, NULL);//为音频源创建读取线程}CoTaskMemFree(ppAudioDevices);//释放音频捕获设备接口指针数组使用的内存
}DWORD WINAPI VideoThread(LPVOID lp)//视频源读取样本线程共用函数
{PARAM* pParam = (PARAM*)lp;Capture* pCapture = pParam->pCapture;int index = pParam->index;HRESULT hr;IMFSourceReader* pIMFSourceReader = pCapture->pVideoReader[index];double dur = (double)10000000 / pCapture->mVideoInfo[index].nFrames;//计算1帧持续时间IMFSample* pMFSample = NULL; DWORD flags;
Agan:hr = pIMFSourceReader->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, NULL, &flags, NULL, &pMFSample);//读取视频样本if (hr == S_OK && pMFSample){LONGLONG Cur, Dur;hr = pMFSample->GetSampleTime(&Cur);//获取显示时间hr = pMFSample->GetSampleDuration(&Dur);//获取持续时间DWORD Lt;hr = pMFSample->GetTotalLength(&Lt);//获取有效长度DWORD count;hr = pMFSample->GetBufferCount(&count);//获取缓冲区数量IMFMediaBuffer* pMFBuffer = NULL;if (count == 1)//如果只有1个缓冲区{hr = pMFSample->GetBufferByIndex(0, &pMFBuffer);}else//如果有多个缓冲区{hr = pMFSample->ConvertToContiguousBuffer(&pMFBuffer);}BYTE* pSB = NULL;hr = pMFBuffer->Lock(&pSB, NULL, NULL);//锁定缓冲区switch (index){case 0://第1个视频源pCapture->mInit.VideoSample0(Cur, Cur + Dur, pSB, (LONG)Lt);//发送样本break;case 1://第2个pCapture->mInit.VideoSample1(Cur, Cur + Dur, pSB, (LONG)Lt);break;case 2:pCapture->mInit.VideoSample2(Cur, Cur + Dur, pSB, (LONG)Lt);break;case 3:pCapture->mInit.VideoSample3(Cur, Cur + Dur, pSB, (LONG)Lt);break;}hr = pMFBuffer->Unlock();//解锁缓冲区SafeRelease(&pMFBuffer); SafeRelease(&pMFSample);//释放接口}DWORD mExit = WaitForSingleObject(pCapture->hEXIT, 0);if (mExit == WAIT_OBJECT_0)//有“退出”信号{delete pParam;return 0;}goto Agan;
}DWORD WINAPI AudioThread(LPVOID lp)//读取音频源样本线程共用函数
{PARAM* pParam = (PARAM*)lp;Capture* pCapture = pParam->pCapture;int index = pParam->index;HRESULT hr;IMFSourceReader* pIMFSourceReader = pCapture->pAudioReader[index];IMFSample* pMFSample = NULL; DWORD flags;
Agan:hr = pIMFSourceReader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, NULL, &flags, NULL, &pMFSample);//读取音频样本if (hr == S_OK && pMFSample){LONGLONG Cur, Dur;hr = pMFSample->GetSampleTime(&Cur);//获取显示时间hr = pMFSample->GetSampleDuration(&Dur);//获取持续时间DWORD Lt;hr = pMFSample->GetTotalLength(&Lt);//获取有效长度DWORD count;hr = pMFSample->GetBufferCount(&count);//获取缓冲区数量IMFMediaBuffer* pMFBuffer = NULL;if (count == 1)//如果只有1个缓冲区{hr = pMFSample->GetBufferByIndex(0, &pMFBuffer);}else//如果有多个缓冲区{hr = pMFSample->ConvertToContiguousBuffer(&pMFBuffer);}BYTE* pSB = NULL;hr = pMFBuffer->Lock(&pSB, NULL, NULL);//锁定缓冲区switch (index){case 0://第1个音频源pCapture->mInit.AudioSample0(Cur, Cur + Dur, pSB, (LONG)Lt);//发送样本break;case 1://第2个pCapture->mInit.AudioSample1(Cur, Cur + Dur, pSB, (LONG)Lt);break;case 2:pCapture->mInit.AudioSample2(Cur, Cur + Dur, pSB, (LONG)Lt);break;case 3:pCapture->mInit.AudioSample3(Cur, Cur + Dur, pSB, (LONG)Lt);break;}hr = pMFBuffer->Unlock();//解锁缓冲区SafeRelease(&pMFBuffer); SafeRelease(&pMFSample);//释放接口}DWORD mExit = WaitForSingleObject(pCapture->hEXIT, 0);if (mExit == WAIT_OBJECT_0)//有“退出”信号{delete pParam;return 0;}goto Agan;
}void Capture::ReleaseDevice()
{SetEvent(hEXIT);//发送“退出”信号WaitForMultipleObjects(4, hVThread, TRUE, INFINITE);//等待所有线程退出WaitForMultipleObjects(4, hAThread, TRUE, INFINITE);for (int i = 0; i < 4; i++)//释放所有视频音频源读取器接口{SafeRelease(&pVideoReader[i]); SafeRelease(&pAudioReader[i]);}
}int Capture::Init(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo)
{mInit = init;ResetEvent(hEXIT);//设置“退出”无信号int mV, mA;GetDevice(mV,VideoInfo, mA,AudioInfo);//获取前4个音频,视频捕获设备return (int)MAKEWORD((BYTE)mA, (BYTE)mV);
}Capture mCapture;#ifdef __cplusplus    // If used by C++ code, 
extern "C" {          // we need to export the C interface
#endif__declspec(dllexport) int __cdecl Init(CAP_INIT init, CAP_VIDEO_INFO*& VideoInfo, CAP_AUDIO_INFO*& AudioInfo)//最多获取前4个视频音频捕获设备{return mCapture.Init(init, VideoInfo, AudioInfo);}__declspec(dllexport) int __cdecl ReleaseDevice()//释放获取到的捕获设备{mCapture.ReleaseDevice();return 0;}#ifdef __cplusplus
}
#endif

相关文章:

  • 【ORB-SLAM3】CreateNewKeyFrame()函数阅读
  • 【时时三省】(C语言基础)字符数组应用举例
  • 深度学习框架---TensorFlow概览
  • C#中Action的用法
  • mac docker弹窗提示Docker 启动没有响应
  • 【docker】--数据卷挂载
  • Redis学习打卡-Day2-缓存更新策略、主动更新策略、缓存穿透、缓存雪崩、缓存击穿
  • 数据科学和机器学习的“看家兵器”——pandas模块 之五
  • 用户安全架构设计
  • 【unity游戏开发——编辑器扩展】使用Selection类获取到选择的对象,并对选中对象进行一些处理
  • 【微信小程序】webp资源上传失败
  • 『已解决』Python virtualenv_ error_ unrecognized arguments_--wheel-bundle
  • Codeforces Round 1023 (Div.2)
  • JAVA学习-练习试用Java实现“语音识别的基础 :如使用MFCC特征提取和简单的分类器”
  • 【AI论文】对抗性后期训练快速文本到音频生成
  • stm32使用freertos时延时时间间隔不对,可能是晶振频率没设置
  • 【Redis】压缩列表
  • Oracle统计信息收集时的锁持有阶段
  • UDP 多点通信
  • 高频交直流电流测量技术:射频PA与MEMS测试的简单解决方案
  • 英德宣布开发射程超2000公里导弹,以防务合作加强安全、促进经济
  • 大环线呼之欲出,“金三角”跑起来了
  • 习近平会见哥伦比亚总统佩特罗
  • 国务院关税税则委:调整对原产于美国的进口商品加征关税措施
  • OpenAI与微软正谈判修改合作条款,以推进未来IPO
  • 为证明我爸是我爸,我将奶奶告上法庭