阿里云RTC为您提供了输入外部音视频流的功能。通过阅读本文,您可以了解输入外部音视频流、视频流数据输出以及外部音频输入播放的方法。
使用限制
支持Windows SDK 1.16.2及以上版本。
使用场景
使用场景包括但不限于以下:
- 需要将本地媒体文件(视频/音频)及第三方音视频数据,通过SDK传输到远端播放渲染,可使用音视频外部输入推流实现。
- 需要在使用音频外部输入的同时,本地播放(耳返)输入内容,可使用外部音频输入播放实现。
- 需要将通信过程中视频数据保存或输出处理(外部渲染,修改内容)时,可使用视频数据裸数据输出实现。
输入外部视频数据推流
代码示例:
AliRtcEngine *pEngine = AliRtcEngine::sharedInstance(this, "");
.....
//1.启用外部视频输入
pEngine->setExternalVideoSource(true, false, AliRtcVideoSourceCamera); //camera track启用外部输入
bPushExternalVideo = true;
//2.配置开始推流
pEngine->configLocalCameraPublish(true);
pEngine->publish();
.....
//3.独立线程推送外部视频数据
int frameRate = 30; // 30 fps
do
{
size_t frameLength = videoWidth * videoHeight * 3 / 2;
void* cacheBuf = (void*)malloc(frameLength);
/*
从外部数据源拷贝推送视频数据到cacheBuf
*/
AliRtcVideoDataSample sample;
sample.data = (unsigned char*)cacheBuf;
sample.format = AliRtcVideoFormatI420;
sample.width = videoWidth;
sample.height = videoHeight;
sample.strideY = videoWidth;
sample.strideU = videoWidth / 2;
sample.strideV = videoWidth / 2;
sample.dataLen = frameLength;
sample.rotation = 0;
pEngine->pushExternalVideoFrame(&sample, AliRtcVideoSourceCamera);
//控制帧数据投递频率
Sleep(1000 / frameRate);
} while (bPushExternalVideo);
.....
//4.停止外部视频输入
pEngine->setExternalVideoSource(false, false, AliRtcVideoSourceCamera);
bPushExternalVideo = false;
视频裸数据输出
代码示例:
AliRtcEngine *pEngine = AliRtcEngine::sharedInstance(this, "");
.....
//1. 注册视频裸数据输出
pEngine->registerVideoSampleObserver();
//开始预览/推流及订阅其他用户视频
.....
//2. 接收裸数据回调
void onCaptureVideoSample(AliRtcVideoSource videoSource, AliRtcVideoDataSample *videoSample)
{
//处理本地采集数据回调
}
void onRemoteVideoSample(const AliRtc::String &uid, AliRtcVideoSource videoSource, AliRtcVideoDataSample *videoSample)
{
//处理远端数据回调
}
//3.停止视频裸数据输出
pEngine->unRegisterVideoSampleObserver();
输入外部音频数据推流
代码示例:
AliRtcEngine *pEngine = AliRtcEngine::sharedInstance(this, "");
.....
//1. 启用外部音频输入推流
unsigned int mIAudioRate = 44100; //44.1k
unsigned int mIPlayMonoAudio = 1; //单声道
pEngine->setExternalAudioSource(true, mIAudioRate, mIPlayMonoAudio);; //启用音频输入推流
mPushExternalAudio = true;
//2.独立线程推送外部音频数据
unsigned int time_interval = 20; //20 ms
size_t byte_per_sample = 16 / 8; // Signed 16-bit
size_t data_size = mIAudioRate * mIPlayMonoAudio * byte_per_sample / (1000 / time_interval);
unsigned int* data = (unsigned int*)malloc(data_size);
if (data == nullptr)
{
fclose(fin);
PostMessage(WM_COMMAND, MAKEWPARAM(IDC_BUTTON_PLAY_AUDIO, BN_CLICKED), 0);
return -1;
}
do
{
size_t read_size = 0;
if ((read_size = fread(data, 1, data_size, fin)) != data_size) {
fseek(fin, 0, SEEK_SET);
read_size = fread(data, 1, data_size, fin);
}
if (read_size > 0)
{
std::chrono::milliseconds now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
int ret = mpEngine->pushExternalAudioFrameRawData(data, read_size, now.count());
if (ret < 0)
{
PostMessage(WM_COMMAND, MAKEWPARAM(IDC_BUTTON_PLAY_AUDIO, BN_CLICKED), 0);
break;
}
//此处保证远端播放音与本地端播放音同步
while (ret == ERR_AUDIO_BUFFER_FULL && mPushExternalAudio)
{
Sleep(time_interval); //sleep 20 ms
if (!mPushExternalAudio) break;
ret = mpEngine->pushExternalAudioFrameRawData(data, read_size, now.count());
if (ret < 0)
{
PostMessage(WM_COMMAND, MAKEWPARAM(IDC_BUTTON_PLAY_AUDIO, BN_CLICKED), 0);
break;
}
}
}
//Sleep(time_interval); //sleep 20 ms
} while (mPushExternalAudio);
.....
//4.停止外部音频输入播放
mpEngine->setExternalAudioSource(false, 0, 0);
mPushExternalAudio = false;
外部音频输入播放
代码示例:
AliRtcEngine *pEngine = AliRtcEngine::sharedInstance(this, "");
.....
//1. 启用外部音频输入播放
unsigned int sampleRate = 44100; //44.1k
unsigned int channelsPerFrame = 1; //单声道
pEngine->setExteranlAudioRender(true, sampleRate, channelsPerFrame); //启用音频输入播放
bRenderExternalAudio = true;
//2.独立线程推送外部音频数据
unsigned int audioDataInterval = 20; //20 ms
size_t bytePerSample = 16 / 8; // Signed 16-bit
size_t dataSize = sampleRate * channelsPerFrame * bytePerSample / (1000 / audioDataInterval);
unsigned int* data = (unsigned int*)malloc(dataSize);
do
{
/*
从外部数据源拷贝推送音频数据到data
*/
std::chrono::milliseconds now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
int ret = mpEngine->pushExternalAudioRenderRawData(data, dataSize, sampleRate, channelsPerFrame, now.count());
if (ret < 0)
{
//发生错误,中断播放
break;
}
//返回结果不为0,需要进行错误判断,检查是否缓冲区满
//返回结果如果为0,投递成功,继续读取并投递数据,无需间隔
while (ret == ERR_AUDIO_BUFFER_FULL && bRenderExternalAudio)
{
Sleep(audioDataInterval); //缓冲区满,间隔一个数据长度后再次重试,直到成功
if (!bRenderExternalAudio) break;
ret = mpEngine->pushExternalAudioRenderRawData(data, read_size, sampleRate, channelsPerFrame, now.count());
if (ret < 0)
{
//发生错误,中断推送
break;
}
}
} while (bRenderExternalAudio);
.....
//4.停止外部音频输入播放
mpEngine->setExteranlAudioRender(false, 0, 0);
bRenderExternalAudio = false;