1.启动SRS

./objs/srs -c conf/rtc.conf

配置文件:

listen              1935;
max_connections 1000;
daemon off;
srs_log_tank console;

http_server {
enabled on;
listen 8080;
dir ./objs/nginx/html;
}

http_api {
enabled on;
listen 1985;
}
stats {
network 0;
}
rtc_server {
enabled on;
# Listen at udp://8000
listen 8000;
#
# The $CANDIDATE means fetch from env, if not configed, use * as default.
#
# The * means retrieving server IP automatically, from all network interfaces,
# @see https://github.com/ossrs/srs/wiki/v4_CN_RTCWiki#config-candidate 这里需要配置外网IP
candidate 192.168.1.103;
}

推流:

ffmpeg -re -i time.flv -vcodec copy -acodec copy -f flv -y rtmp://192.168.1.103/live/livestream

播放:

http:///192.168.1.103:8080/players/rtc_player.html

2.代码框架

【音视频流媒体】SRS4.0 RTMP转WebRTC_数据

这里主要分为几个部分:

rtmp推流到SRS

RTMP流转为RTC流

RTC客户端和SRS通过HTTP交互SDP信息

RTC客户端通过RTP拉流

RTMP推流到SRS

SrsLiveSource:代表RTMP源

SRS针对推流会创建专门的SrsLiveSource来表示源,本章主要分析一下源的创建和数据流的接受

创建Source

rtmp推流的时候就会创建SrsLiveSource和SrsRtcSource;

SrsRtmpConn::stream_service_cycle()

srs_error_t SrsRtmpConn::stream_service_cycle()
{
srs_error_t err = srs_success;

SrsRequest* req = info->req;

if ((err = rtmp->identify_client(info->res->stream_id, info->type, req->stream, req->duration)) != srs_success) {
return srs_error_wrap(err, "rtmp: identify client");
}

srs_discovery_tc_url(req->tcUrl, req->schema, req->host, req->vhost, req->app, req->stream, req->port, req->param);
req->strip();
srs_trace("client identified, type=%s, vhost=%s, app=%s, stream=%s, param=%s, duration=%dms",
srs_client_type_string(info->type).c_str(), req->vhost.c_str(), req->app.c_str(), req->stream.c_str(), req->param.c_str(), srsu2msi(req->duration));

// discovery vhost, resolve the vhost from config
SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost);
if (parsed_vhost) {
req->vhost = parsed_vhost->arg0();
}

if (req->schema.empty() || req->vhost.empty() || req->port == 0 || req->app.empty()) {
return srs_error_new(ERROR_RTMP_REQ_TCURL, "discovery tcUrl failed, tcUrl=%s, schema=%s, vhost=%s, port=%d, app=%s",
req->tcUrl.c_str(), req->schema.c_str(), req->vhost.c_str(), req->port, req->app.c_str());
}

// check vhost, allow default vhost.
if ((err = check_vhost(true)) != srs_success) {
return srs_error_wrap(err, "check vhost");
}

srs_trace("connected stream, tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%d, app=%s, stream=%s, param=%s, args=%s",
req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(), req->schema.c_str(), req->vhost.c_str(), req->port,
req->app.c_str(), req->stream.c_str(), req->param.c_str(), (req->args? "(obj)":"null"));

// do token traverse before serve it.
// @see https://github.com/ossrs/srs/pull/239
if (true) {
info->edge = _srs_config->get_vhost_is_edge(req->vhost);
bool edge_traverse = _srs_config->get_vhost_edge_token_traverse(req->vhost);
if (info->edge && edge_traverse) {
if ((err = check_edge_token_traverse_auth()) != srs_success) {
return srs_error_wrap(err, "rtmp: check token traverse");
}
}
}

// security check
if ((err = security->check(info->type, ip, req)) != srs_success) {
return srs_error_wrap(err, "rtmp: security check");
}

// Never allow the empty stream name, for HLS may write to a file with empty name.
// @see https://github.com/ossrs/srs/issues/834
if (req->stream.empty()) {
return srs_error_new(ERROR_RTMP_STREAM_NAME_EMPTY, "rtmp: empty stream");
}

// client is identified, set the timeout to service timeout.
rtmp->set_recv_timeout(SRS_CONSTS_RTMP_TIMEOUT);
rtmp->set_send_timeout(SRS_CONSTS_RTMP_TIMEOUT);

// find a source to serve.
SrsLiveSource* source = NULL;
if ((err = _srs_sources->fetch_or_create(req, server, &source)) != srs_success) {
return srs_error_wrap(err, "rtmp: fetch source");
}
srs_assert(source != NULL);

// update the statistic when source disconveried.
SrsStatistic* stat = SrsStatistic::instance();
if ((err = stat->on_client(_srs_context->get_id().c_str(), req, this, info->type)) != srs_success) {
return srs_error_wrap(err, "rtmp: stat client");
}

bool enabled_cache = _srs_config->get_gop_cache(req->vhost);
srs_trace("source url=%s, ip=%s, cache=%d, is_edge=%d, source_id=%s/%s",
req->get_stream_url().c_str(), ip.c_str(), enabled_cache, info->edge, source->source_id().c_str(), source->pre_source_id().c_str());
source->set_cache(enabled_cache);

switch (info->type) {
case SrsRtmpConnPlay: {
// response connection start play
if ((err = rtmp->start_play(info->res->stream_id)) != srs_success) {
return srs_error_wrap(err, "rtmp: start play");
}
if ((err = http_hooks_on_play()) != srs_success) {
return srs_error_wrap(err, "rtmp: callback on play");
}

err = playing(source);
http_hooks_on_stop();

return err;
}
case SrsRtmpConnFMLEPublish: {
if ((err = rtmp->start_fmle_publish(info->res->stream_id)) != srs_success) {
return srs_error_wrap(err, "rtmp: start FMLE publish");
}

return publishing(source);
}
case SrsRtmpConnHaivisionPublish: {
if ((err = rtmp->start_haivision_publish(info->res->stream_id)) != srs_success) {
return srs_error_wrap(err, "rtmp: start HAIVISION publish");
}

return publishing(source);
}
case SrsRtmpConnFlashPublish: {
if ((err = rtmp->start_flash_publish(info->res->stream_id)) != srs_success) {
return srs_error_wrap(err, "rtmp: start FLASH publish");
}

return publishing(source);
}
default: {
return srs_error_new(ERROR_SYSTEM_CLIENT_INVALID, "rtmp: unknown client type=%d", info->type);
}
}

return err;
}

这里RTMP的业务处理中心,比较重要,推流和播放都在这里处理,同时SrsLiveSource也是在这里创建的。

数据接受

我们在source的音频回调这里打一下断点,看一下调用栈,就可以知道调用顺序

(gdb) bt
#0 SrsLiveSource::on_audio (this=0xfcd7a0, shared_audio=0x1067b80) at src/app/srs_app_source.cpp:2140
#1 0x00000000004f2241 in SrsRtmpConn::process_publish_message (this=0xfcd890, source=0xfcd7a0, msg=0x1067b80) at src/app/srs_app_rtmp_conn.cpp:1055
#2 0x00000000004f20ef in SrsRtmpConn::handle_publish_message (this=0xfcd890, source=0xfcd7a0, msg=0x1067b80) at src/app/srs_app_rtmp_conn.cpp:1034
#3 0x00000000005a0d06 in SrsPublishRecvThread::consume (this=0x10060a0, msg=0x1067b80) at src/app/srs_app_recv_thread.cpp:376
#4 0x000000000059fe7e in SrsRecvThread::do_cycle (this=0x10060c0) at src/app/srs_app_recv_thread.cpp:133
#5 0x000000000059fcea in SrsRecvThread::cycle (this=0x10060c0) at src/app/srs_app_recv_thread.cpp:102
#6 0x000000000051fe11 in SrsFastCoroutine::cycle (this=0x10679c0) at src/app/srs_app_st.cpp:253
#7 0x000000000051fe94 in SrsFastCoroutine::pfn (arg=0x10679c0) at src/app/srs_app_st.cpp:268
#8 0x00000000006346e8 in _st_thread_main () at sched.c:363
#9 0x0000000000634f5b in st_thread_create (start=0x1005daf, arg=0x10679a0, joinable=0, stk_size=16801184) at sched.c:694

SrsRecvThread::do_cycle()

srs_error_t SrsRecvThread::do_cycle()
{
srs_error_t err = srs_success;

while (true) {
if ((err = trd->pull()) != srs_success) {
return srs_error_wrap(err, "recv thread");
}

// When the pumper is interrupted, wait then retry.
if (pumper->interrupted()) {
srs_usleep(timeout);
continue;
}

SrsCommonMessage* msg = NULL;

// Process the received message.
if ((err = rtmp->recv_message(&msg)) == srs_success) {
err = pumper->consume(msg);
}

if (err != srs_success) {
// Interrupt the receive thread for any error.
trd->interrupt();

// Notify the pumper to quit for error.
pumper->interrupt(err);

return srs_error_wrap(err, "recv thread");
}
}

return err;
}

SRS有个专门的协程处理数据接受,主要关注两个rtmp->recv_message(&msg)和pumper->consume(msg)

rtmp->recv_message(&msg):用来接受数据,最终调用SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg)

pumper->consume(msg):用来处理数据

SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg)

srs_error_t SrsProtocol::read_message_payload(SrsChunkStream* chunk, SrsCommonMessage** pmsg)
{
srs_error_t err = srs_success;

// empty message
if (chunk->header.payload_length <= 0) {
srs_trace("get an empty RTMP message(type=%d, size=%d, time=%" PRId64 ", sid=%d)", chunk->header.message_type,
chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id);

*pmsg = chunk->msg;
chunk->msg = NULL;

return err;
}
srs_assert(chunk->header.payload_length > 0);

// the chunk payload size.
int payload_size = chunk->header.payload_length - chunk->msg->size;
payload_size = srs_min(payload_size, in_chunk_size);

// create msg payload if not initialized
if (!chunk->msg->payload) {
chunk->msg->create_payload(chunk->header.payload_length);
}

// read payload to buffer
if ((err = in_buffer->grow(skt, payload_size)) != srs_success) {
return srs_error_wrap(err, "read %d bytes payload", payload_size);
}
memcpy(chunk->msg->payload + chunk->msg->size, in_buffer->read_slice(payload_size), payload_size);
chunk->msg->size += payload_size;

// got entire RTMP message?
if (chunk->header.payload_length == chunk->msg->size) {
*pmsg = chunk->msg;
chunk->msg = NULL;
return err;
}

return err;
}

这里分别拷贝rtmp的header和body给SrsCommonMessage,到这里我们就算拿到rtmp的数据了。

音频的话,rtmp的header的TypeID应该为8,即rtmpt.header.typeid == 0x08。

而body是按照flv的格式组织的,相当于flv的音频tag:

  • 第⼀个字节包含了⾳频数据的参数信息,
  • 第⼆个字节开始为⾳频流数据。

我们抓一个音频的rtmp包分析一下:

【音视频流媒体】SRS4.0 RTMP转WebRTC_webrtc_02

AudioTagHeader格式:

【音视频流媒体】SRS4.0 RTMP转WebRTC_ide_03

处理数据

通过pumper->consume跟踪,发现process_publish_message()用来处理rtmp的message。

srs_error_t SrsRtmpConn::process_publish_message(SrsLiveSource* source, SrsCommonMessage* msg)
{
srs_error_t err = srs_success;

// for edge, directly proxy message to origin.
if (info->edge) {
if ((err = source->on_edge_proxy_publish(msg)) != srs_success) {
return srs_error_wrap(err, "rtmp: proxy publish");
}
return err;
}

// process audio packet 语音包 typeid = 8
if (msg->header.is_audio()) {
if ((err = source->on_audio(msg)) != srs_success) {
return srs_error_wrap(err, "rtmp: consume audio");
}
return err;
}
// process video packet 视频包 typeid = 9
if (msg->header.is_video()) {
if ((err = source->on_video(msg)) != srs_success) {
return srs_error_wrap(err, "rtmp: consume video");
}
return err;
}

// process aggregate packet 统计消息 typeid = 22
if (msg->header.is_aggregate()) {
if ((err = source->on_aggregate(msg)) != srs_success) {
return srs_error_wrap(err, "rtmp: consume aggregate");
}
return err;
}

// process onMetaData 数据消息 typed = 18 | 15
if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) {
SrsPacket* pkt = NULL;
if ((err = rtmp->decode_message(msg, &pkt)) != srs_success) {
return srs_error_wrap(err, "rtmp: decode message");
}
SrsAutoFree(SrsPacket, pkt);

if (dynamic_cast<SrsOnMetaDataPacket*>(pkt)) {
SrsOnMetaDataPacket* metadata = dynamic_cast<SrsOnMetaDataPacket*>(pkt);
if ((err = source->on_meta_data(msg, metadata)) != srs_success) {
return srs_error_wrap(err, "rtmp: consume metadata");
}
return err;
}
return err;
}

return err;
}

这里分别处理语音、视频等各种消息。

RTMP协议转WebRTC

SrsLiveSource:代表RTMP源

SrsRtcSource:代表RTC源

两个协议的转换,就是怎么从SrsLiveSource转到SrsRtcSource,这里需要一个桥梁SrsRtcFromRtmpBridger

【音视频流媒体】SRS4.0 RTMP转WebRTC_音视频_04

创建Source

rtmp推流的时候就会创建SrsLiveSource和SrsRtcSource;

SrsRtmpConn::stream_service_cycle()

srs_error_t SrsRtmpConn::stream_service_cycle()
{
srs_error_t err = srs_success;

SrsRequest* req = info->req;

if ((err = rtmp->identify_client(info->res->stream_id, info->type, req->stream, req->duration)) != srs_success) {
return srs_error_wrap(err, "rtmp: identify client");
}

srs_discovery_tc_url(req->tcUrl, req->schema, req->host, req->vhost, req->app, req->stream, req->port, req->param);
req->strip();
srs_trace("client identified, type=%s, vhost=%s, app=%s, stream=%s, param=%s, duration=%dms",
srs_client_type_string(info->type).c_str(), req->vhost.c_str(), req->app.c_str(), req->stream.c_str(), req->param.c_str(), srsu2msi(req->duration));

// discovery vhost, resolve the vhost from config
SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost);
if (parsed_vhost) {
req->vhost = parsed_vhost->arg0();
}

if (req->schema.empty() || req->vhost.empty() || req->port == 0 || req->app.empty()) {
return srs_error_new(ERROR_RTMP_REQ_TCURL, "discovery tcUrl failed, tcUrl=%s, schema=%s, vhost=%s, port=%d, app=%s",
req->tcUrl.c_str(), req->schema.c_str(), req->vhost.c_str(), req->port, req->app.c_str());
}

// check vhost, allow default vhost.
if ((err = check_vhost(true)) != srs_success) {
return srs_error_wrap(err, "check vhost");
}

srs_trace("connected stream, tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%d, app=%s, stream=%s, param=%s, args=%s",
req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(), req->schema.c_str(), req->vhost.c_str(), req->port,
req->app.c_str(), req->stream.c_str(), req->param.c_str(), (req->args? "(obj)":"null"));

// do token traverse before serve it.
// @see https://github.com/ossrs/srs/pull/239
if (true) {
info->edge = _srs_config->get_vhost_is_edge(req->vhost);
bool edge_traverse = _srs_config->get_vhost_edge_token_traverse(req->vhost);
if (info->edge && edge_traverse) {
if ((err = check_edge_token_traverse_auth()) != srs_success) {
return srs_error_wrap(err, "rtmp: check token traverse");
}
}
}

// security check
if ((err = security->check(info->type, ip, req)) != srs_success) {
return srs_error_wrap(err, "rtmp: security check");
}

// Never allow the empty stream name, for HLS may write to a file with empty name.
// @see https://github.com/ossrs/srs/issues/834
if (req->stream.empty()) {
return srs_error_new(ERROR_RTMP_STREAM_NAME_EMPTY, "rtmp: empty stream");
}

// client is identified, set the timeout to service timeout.
rtmp->set_recv_timeout(SRS_CONSTS_RTMP_TIMEOUT);
rtmp->set_send_timeout(SRS_CONSTS_RTMP_TIMEOUT);

// find a source to serve.
SrsLiveSource* source = NULL;
if ((err = _srs_sources->fetch_or_create(req, server, &source)) != srs_success) {
return srs_error_wrap(err, "rtmp: fetch source");
}
srs_assert(source != NULL);

// update the statistic when source disconveried.
SrsStatistic* stat = SrsStatistic::instance();
if ((err = stat->on_client(_srs_context->get_id().c_str(), req, this, info->type)) != srs_success) {
return srs_error_wrap(err, "rtmp: stat client");
}

bool enabled_cache = _srs_config->get_gop_cache(req->vhost);
srs_trace("source url=%s, ip=%s, cache=%d, is_edge=%d, source_id=%s/%s",
req->get_stream_url().c_str(), ip.c_str(), enabled_cache, info->edge, source->source_id().c_str(), source->pre_source_id().c_str());
source->set_cache(enabled_cache);

switch (info->type) {
case SrsRtmpConnPlay: {
// response connection start play
if ((err = rtmp->start_play(info->res->stream_id)) != srs_success) {
return srs_error_wrap(err, "rtmp: start play");
}
if ((err = http_hooks_on_play()) != srs_success) {
return srs_error_wrap(err, "rtmp: callback on play");
}

err = playing(source);
http_hooks_on_stop();

return err;
}
case SrsRtmpConnFMLEPublish: {
if ((err = rtmp->start_fmle_publish(info->res->stream_id)) != srs_success) {
return srs_error_wrap(err, "rtmp: start FMLE publish");
}

return publishing(source);
}
case SrsRtmpConnHaivisionPublish: {
if ((err = rtmp->start_haivision_publish(info->res->stream_id)) != srs_success) {
return srs_error_wrap(err, "rtmp: start HAIVISION publish");
}

return publishing(source);
}
case SrsRtmpConnFlashPublish: {
if ((err = rtmp->start_flash_publish(info->res->stream_id)) != srs_success) {
return srs_error_wrap(err, "rtmp: start FLASH publish");
}

return publishing(source);
}
default: {
return srs_error_new(ERROR_SYSTEM_CLIENT_INVALID, "rtmp: unknown client type=%d", info->type);
}
}

return err;
}

这里推流的业务处理,是会创建SrsLiveSource,然后我们看看publishing

SrsRtmpConn::publishing(SrsLiveSource* source)

srs_error_t SrsRtmpConn::publishing(SrsLiveSource* source)
{
srs_error_t err = srs_success;

SrsRequest* req = info->req;

if (_srs_config->get_refer_enabled(req->vhost)) {
if ((err = refer->check(req->pageUrl, _srs_config->get_refer_publish(req->vhost))) != srs_success) {
return srs_error_wrap(err, "rtmp: referer check");
}
}

if ((err = http_hooks_on_publish()) != srs_success) {
return srs_error_wrap(err, "rtmp: callback on publish");
}

// TODO: FIXME: Should refine the state of publishing.
if ((err = acquire_publish(source)) == srs_success) {
// use isolate thread to recv,
// @see: https://github.com/ossrs/srs/issues/237
SrsPublishRecvThread rtrd(rtmp, req, srs_netfd_fileno(stfd), 0, this, source, _srs_context->get_id());
err = do_publishing(source, &rtrd);
rtrd.stop();
}

// whatever the acquire publish, always release publish.
// when the acquire error in the midlle-way, the publish state changed,
// but failed, so we must cleanup it.
// @see https://github.com/ossrs/srs/issues/474
// @remark when stream is busy, should never release it.
if (srs_error_code(err) != ERROR_SYSTEM_STREAM_BUSY) {
release_publish(source);
}

http_hooks_on_unpublish();

return err;
}

这里先看看acquire_publish处理

srs_error_t SrsRtmpConn::acquire_publish(SrsLiveSource* source)
{
srs_error_t err = srs_success;

SrsRequest* req = info->req;

// Check whether RTMP stream is busy.
if (!source->can_publish(info->edge)) {
return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "rtmp: stream %s is busy", req->get_stream_url().c_str());
}

// Check whether RTC stream is busy.
#ifdef SRS_RTC
SrsRtcSource *rtc = NULL;
bool rtc_server_enabled = _srs_config->get_rtc_server_enabled();
bool rtc_enabled = _srs_config->get_rtc_enabled(req->vhost);
if (rtc_server_enabled && rtc_enabled && !info->edge) {
if ((err = _srs_rtc_sources->fetch_or_create(req, &rtc)) != srs_success) {
return srs_error_wrap(err, "create source");
}

if (!rtc->can_publish()) {
return srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "rtc stream %s busy", req->get_stream_url().c_str());
}
}
#endif

// Bridge to RTC streaming.
#if defined(SRS_RTC) && defined(SRS_FFMPEG_FIT)
if (rtc) {
SrsRtcFromRtmpBridger *bridger = new SrsRtcFromRtmpBridger(rtc);
if ((err = bridger->initialize(req)) != srs_success) {
srs_freep(bridger);
return srs_error_wrap(err, "bridger init");
}

source->set_bridger(bridger);
}
#endif

// Start publisher now.
if (info->edge) {
return source->on_edge_start_publish();
} else {
return source->on_publish();
}
}

可以看出,如果配置了RTC,就会创建SrsRtcSource和SrsRtcFromRtmpBridger。

获取RTMP数据

SrsRtmpConn::process_publish_message(SrsLiveSource* source, SrsCommonMessage* msg)

srs_error_t SrsRtmpConn::process_publish_message(SrsLiveSource* source, SrsCommonMessage* msg)
{
srs_error_t err = srs_success;

// for edge, directly proxy message to origin.
if (info->edge) {
if ((err = source->on_edge_proxy_publish(msg)) != srs_success) {
return srs_error_wrap(err, "rtmp: proxy publish");
}
return err;
}

// process audio packet
if (msg->header.is_audio()) {
if ((err = source->on_audio(msg)) != srs_success) {
return srs_error_wrap(err, "rtmp: consume audio");
}
return err;
}
// process video packet
if (msg->header.is_video()) {
if ((err = source->on_video(msg)) != srs_success) {
return srs_error_wrap(err, "rtmp: consume video");
}
return err;
}

// process aggregate packet
if (msg->header.is_aggregate()) {
if ((err = source->on_aggregate(msg)) != srs_success) {
return srs_error_wrap(err, "rtmp: consume aggregate");
}
return err;
}

// process onMetaData
if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) {
SrsPacket* pkt = NULL;
if ((err = rtmp->decode_message(msg, &pkt)) != srs_success) {
return srs_error_wrap(err, "rtmp: decode message");
}
SrsAutoFree(SrsPacket, pkt);

if (dynamic_cast<SrsOnMetaDataPacket*>(pkt)) {
SrsOnMetaDataPacket* metadata = dynamic_cast<SrsOnMetaDataPacket*>(pkt);
if ((err = source->on_meta_data(msg, metadata)) != srs_success) {
return srs_error_wrap(err, "rtmp: consume metadata");
}
return err;
}
return err;
}

return err;
}

这里处理收到的源数据,下面分析一下音频的处理

srs_error_t SrsLiveSource::on_audio(SrsCommonMessage* shared_audio)
{
srs_error_t err = srs_success;

// monotically increase detect.
if (!mix_correct && is_monotonically_increase) {
if (last_packet_time > 0 && shared_audio->header.timestamp < last_packet_time) {
is_monotonically_increase = false;
srs_warn("AUDIO: stream not monotonically increase, please open mix_correct.");
}
}
last_packet_time = shared_audio->header.timestamp;

// convert shared_audio to msg, user should not use shared_audio again.
// the payload is transfer to msg, and set to NULL in shared_audio.
SrsSharedPtrMessage msg;
if ((err = msg.create(shared_audio)) != srs_success) {
return srs_error_wrap(err, "create message");
}

// directly process the audio message.
if (!mix_correct) {
return on_audio_imp(&msg);
}

// insert msg to the queue.
mix_queue->push(msg.copy());

// fetch someone from mix queue.
SrsSharedPtrMessage* m = mix_queue->pop();
if (!m) {
return err;
}

// consume the monotonically increase message.
if (m->is_audio()) {
err = on_audio_imp(m);
} else {
err = on_video_imp(m);
}
srs_freep(m);

return err;
}

SrsLiveSource::on_audio_imp(SrsSharedPtrMessage* msg)

srs_error_t SrsLiveSource::on_audio_imp(SrsSharedPtrMessage* msg)
{
srs_error_t err = srs_success;

bool is_aac_sequence_header = SrsFlvAudio::sh(msg->payload, msg->size);
bool is_sequence_header = is_aac_sequence_header;

// whether consumer should drop for the duplicated sequence header.
bool drop_for_reduce = false;
if (is_sequence_header && meta->previous_ash() && _srs_config->get_reduce_sequence_header(req->vhost)) {
if (meta->previous_ash()->size == msg->size) {
drop_for_reduce = srs_bytes_equals(meta->previous_ash()->payload, msg->payload, msg->size);
srs_warn("drop for reduce sh audio, size=%d", msg->size);
}
}

// Copy to hub to all utilities.
if ((err = hub->on_audio(msg)) != srs_success) {
return srs_error_wrap(err, "consume audio");
}

// For bridger to consume the message.
if (bridger_ && (err = bridger_->on_audio(msg)) != srs_success) {
return srs_error_wrap(err, "bridger consume audio");
}

// copy to all consumer
if (!drop_for_reduce) {
for (int i = 0; i < (int)consumers.size(); i++) {
SrsLiveConsumer* consumer = consumers.at(i);
if ((err = consumer->enqueue(msg, atc, jitter_algorithm)) != srs_success) {
return srs_error_wrap(err, "consume message");
}
}
}

// cache the sequence header of aac, or first packet of mp3.
// for example, the mp3 is used for hls to write the "right" audio codec.
// TODO: FIXME: to refine the stream info system.
if (is_aac_sequence_header || !meta->ash()) {
if ((err = meta->update_ash(msg)) != srs_success) {
return srs_error_wrap(err, "meta consume audio");
}
}

// when sequence header, donot push to gop cache and adjust the timestamp.
if (is_sequence_header) {
return err;
}

// cache the last gop packets
if ((err = gop_cache->cache(msg)) != srs_success) {
return srs_error_wrap(err, "gop cache consume audio");
}

// if atc, update the sequence header to abs time.
if (atc) {
if (meta->ash()) {
meta->ash()->timestamp = msg->timestamp;
}
if (meta->data()) {
meta->data()->timestamp = msg->timestamp;
}
}

return err;
}

这里把数据转发给SrsRtcFromRtmpBridger,上面都比较简单,下面着重分析一下SrsRtcFromRtmpBridger收到数据以后的语音转码

转码:aac转opus