阿里云RTC为您提供了输入外部音视频流的功能。通过阅读本文,您可以了解输入外部音视频流、视频流数据输出以及外部音频输入播放的方法。
使用场景
使用场景包括但不限于以下:
需要将本地媒体文件(视频/音频)及第三方音视频数据,通过SDK传输到远端播放渲染,可使用音视频外部输入推流实现。
需要在使用音频外部输入的同时,本地播放(耳返)输入内容,可使用外部音频输入播放实现。
需要将通信过程中视频数据保存或输出处理(外部渲染,修改内容)时,可使用视频数据裸数据输出实现。
输入外部视频数据推流
调用SetExternalVideoSource启用外部视频输入推流。
说明目前Windows端不支持texture模式。
应用侧持续调用接PushExternalVideoFrame,向 SDK 投递视频裸数据,进行推流,参数 frame 传入裸数据相关信息,参数 track 指明推流 track 类型,与步骤1 中设置开启的track类型保持一致。
说明投递视频帧数据的频率由应用方控制,依据视频源帧率保持间隔投递,直至输入停止。建议应用侧独立开启线程,进行数据投递,保证数据输入及时性。
目前 Windows 端支持输入 YUV数据(格式I420),需要在参数 frame 的裸数据信息中,指定 pixelFormat 为 RtcEngineVideoI420, frameType 为 RtcEngineVideoFrameRaw。
赋值裸数据信息时,需要数据指针、视频宽高信息、stride 信息等完整传入;旋转角度 rotation 目前暂未支持设置,请保持默认值 0;时间戳取当期时间,单位为毫秒。
数据源输入结束或应用中止外部视频输入,调用接口SetExternalVideoSource关闭外部视频输入。
代码示例:
ding::rtc::RtcEngine *mediaEngine =;
.....
// 1.启用外部视频输入
mediaEngine->SetExternalVideoSource(true, ding::rtc::RtcEngineVideoTrackCamera); // camera track启用外部输入
.....
// 2.独立线程推送外部视频数据
int frameRate = 30; // 30 fps
void *buffer = malloc(bufsize);
do
{
ding::rtc::RtcEngineVideoFrame frm;
memset(&frm, 0, sizeof(frm));
frm.frameType = ding::rtc::RtcEngineVideoFrameRaw;
frm.pixelFormat = ding::rtc::RtcEngineVideoI420;
frm.timestamp = now;
frm.width = // pic width;
frm.height = // pic height;
frm.rotation = 0;
frm.count = count;
frm.stride[0] = // ybuffer stride;
frm.stride[1] = // ubuffer stride;
frm.stride[2] = // vbuffer stride;
frm.data = buffer;
mediaEngine->PushExternalVideoFrame(&frm, ding::rtc::RtcEngineVideoTrackCamera);
// 控制帧数据投递频率
Sleep(1000 / frameRate);
/* 产品代码用控制帧率比较复杂一些,不能用Sleep(frame_duration)方式,
* 因为Sleep非常不准
int64_t now = osal_get_time_ms();
if (now - startTime < frameCount * frameDurationMs) {
// 时间未到,再等一会
osal_sleep_ms(2);
continue;
}
*/
} while (more);
free(buffer);
.....
// 3.停止外部视频输入
mediaEngine->SetExternalVideoSource(false, ding::rtc::RtcEngineVideoTrackCamera);
输入外部音频数据推流
调用接口SetExternalAudioSource启用外部音频输入推流。
应用侧持续调用PushExternalAudioFrame接口,向 SDK 投递音频PCM 数据,参数frame带入音频数据。
说明投递音频裸数据的频率由应用方控制,每次投递数据量不要超过 240ms 的音频数据量,建议每次投递 20ms 的音频数据,保持循环投递直到结束。当输入数据频率过快,SDK 缓存已满暂时无法消费数据时,将丢失输入音频数据。
与视频输入一致,同样建议应用侧单独开启线程,投递音频裸数据,直到输入停止。
数据源输入结束或应用中止外部音频输入,调用接口SetExternalAudioSource关闭外部视音频输入。
代码示例:
ding::rtc::RtcEngine *mediaEngine =;
.....
// 1.启用外部视频输入
mediaEngine->SetExternalAudioSource(true, sample_rate, stereo ? 2 : 1);
.....
// 2.独立线程推送外部音频数据
void *ExtAudioSrc::worker(void *arg)
{
ExtAudioSrc *pthis = (ExtAudioSrc *)arg;
if (!pthis) {
return NULL;
}
osal_thread_set_name("extaudiosrc");
ding::rtc::RtcEngineAudioFrame frm;
memset(&frm, 0, sizeof(frm));
const int bytesPerSample = 2; // short
const int frameMs = 20; // 20 ms per frame
const int bufsize = pthis->sample_rate * frameMs / 1000 * (pthis->stereo ? 2 : 1) * bytesPerSample;
FILE *fp = fopen(pthis->pcm_file_path.c_str(), "rb");
if (fp == NULL) {
return NULL;
}
void *buffer = malloc(bufsize);
int64_t startTime = osal_get_time_ms();
int count = 0;
while (!pthis->quit_thread_flag_) {
// maintain fps
int64_t now = osal_get_time_ms();
if ((now - startTime) * 1000 / frameMs < count * 1000) {
osal_sleep_ms(2);
continue;
}
// read pcm file
int bytes = (int)fread(buffer, 1, bufsize, fp);
if (bytes < bufsize) {
// eof
fseek(fp, 0, SEEK_SET);
bytes = (int)fread(buffer, 1, bufsize, fp);
if (bytes < bufsize) {
break;
}
}
count++;
// callback
frm.type = ding::rtc::RtcEngineAudioFramePcm16;
frm.bytesPerSample = bytesPerSample;
frm.samplesPerSec = pthis->sample_rate;
frm.channels = (pthis->stereo ? 2 : 1);
frm.samples = pthis->sample_rate * frameMs / 1000;
frm.buffer = buffer;
pthis->cb_on_one_frame_(&frm, pthis->cb_arg_);
}
if (buffer)
free(buffer);
if (fp != NULL)
fclose(fp);
return NULL;
}
void ExtAudioSrc::start_sampler(void on_frame(void *frm, void *arg), void *arg)
{
if (sample_thread_ != NULL) {
return;
}
cb_on_one_frame_ = on_frame;
cb_arg_ = arg;
quit_thread_flag_ = false;
osal_thread_create(&sample_thread_, worker, this);
}
.....
ext_audio_src_.start_sampler([](void *frm, void *arg) {
// 从文件读取到一帧数据,通过PushExternalAudioFrame传递给SDK
ding::rtc::RtcEngine *engine = (ding::rtc::RtcEngine *)arg;
engine->PushExternalAudioFrame((ding::rtc::RtcEngineAudioFrame *)frm);
}, GetGlobalContext()->engine_);
// 3. 停止外部音频输入播放
void ExtAudioSrc::stop_sampler()
{
if (sample_thread_ != NULL) {
quit_thread_flag_ = true;
osal_thread_join(sample_thread_, NULL);
sample_thread_ = NULL;
}
}
engine_->SetExternalAudioSource(false, 0, 0);
// stop reading thread
ext_audio_src_.stop_sampler();