freeswitch的大部分媒体逻辑在switch_ivr_*.c中实现,但是这些是功能逻辑,最后会调用switch_core_io.c中的读写帧函数进行io操作。本章不分析功能,只对读写帧这些底层IO的API进行分析。
- switch_core_session_read_frame
读写帧代码比较长,这里对关键流程进行注解。
对一些参数进行判断
- if (!switch_core_codec_ready(session->read_codec)) {
从端点读帧
- if (session->endpoint_interface->io_routines->read_frame) {
- switch_mutex_unlock(session->read_codec->mutex);
- switch_mutex_unlock(session->codec_read_mutex);
- if ((status = session->endpoint_interface->io_routines->read_frame(session, frame, flags, stream_id)) == SWITCH_STATUS_SUCCESS) {
- for (ptr = session->event_hooks.read_frame; ptr; ptr = ptr->next) {
- if ((status = ptr->read_frame(session, frame, flags, stream_id)) != SWITCH_STATUS_SUCCESS) {
- break;
- }
- }
- }
调用端点的io接口去读,io层不具体去读,而是调用端点的接口,说明它支持很多种读法,如果是mod_sofia,从网络读RTP,在mod_portaudio则从本地声卡读。另外,除了端点可以读,还可以从事件回调接口读,session->event_hooks.read_frame。
传给监控媒体流
- if (bp->ready) {
- if (switch_test_flag(bp, SMBF_TAP_NATIVE_READ)) {
- if ((*frame)->codec && (*frame)->codec->implementation &&
- (*frame)->codec->implementation->encoded_bytes_per_packet &&
- (*frame)->datalen != (*frame)->codec->implementation->encoded_bytes_per_packet) {
- switch_set_flag((*frame), SFF_CNG);
- break;
- }
- if (bp->callback) {
- bp->native_read_frame = *frame;
- ok = bp->callback(bp, bp->user_data, SWITCH_ABC_TYPE_TAP_NATIVE_READ);
- bp->native_read_frame = NULL;
- }
- }
- }
freeswitch支持通过bug这种机制进行监控媒体流,这里会把读到的帧传给监控bug。
解码帧
- status = switch_core_codec_decode(codec,
- session->read_codec,
- read_frame->data,
- read_frame->datalen,
- session->read_impl.actual_samples_per_second,
- session->raw_read_frame.data, &session->raw_read_frame.datalen, &session->raw_read_frame.rate,
- &read_frame->flags);
从网络读到的RTP包是编码的,这里如果code不一样,或者直接播放,要进行解码。
重采样
- case SWITCH_STATUS_RESAMPLE:
- if (!session->read_resampler) {
- switch_mutex_lock(session->resample_mutex);
- status = switch_resample_create(&session->read_resampler,
- read_frame->codec->implementation->actual_samples_per_second,
- session->read_impl.actual_samples_per_second,
- session->read_impl.decoded_bytes_per_packet, SWITCH_RESAMPLE_QUALITY,
- session->read_impl.number_of_channels);
除了支持转码,还支持重采样。
再编码
- status = switch_core_codec_encode(session->read_codec,
- enc_frame->codec,
- enc_frame->data,
- enc_frame->datalen,
- session->read_impl.actual_samples_per_second,
- session->enc_read_frame.data, &session->enc_read_frame.datalen, &session->enc_read_frame.rate, &flag);
所谓转码,就是两端协商使用的codec不一样,A的媒体数据,freeswitch先解码成中间格式L16,再编码成B支持的codec。
- switch_core_session_write_frame
快速发
- if (switch_test_flag(frame, SFF_PROXY_PACKET) || pass_cng) {
- /* Fast PASS! */
- switch_mutex_lock(session->codec_write_mutex);
- status = perform_write(session, frame, flag, stream_id);
- switch_mutex_unlock(session->codec_write_mutex);
- return status;
- }
有些情况,比如这个是代理包标志还是啥,就要调用perform_write直接发。
转码
- if (frame->codec) {
- session->raw_write_frame.datalen = session->raw_write_frame.buflen;
- frame->codec->cur_frame = frame;
- session->write_codec->cur_frame = frame;
- status = switch_core_codec_decode(frame->codec,
- session->write_codec,
- frame->data,
- frame->datalen,
- session->write_impl.actual_samples_per_second,
- session->raw_write_frame.data, &session->raw_write_frame.datalen, &session->raw_write_frame.rate, &frame->flags);
重采样
- switch (status) {
- case SWITCH_STATUS_RESAMPLE:
- resample++;
- write_frame = &session->raw_write_frame;
- write_frame->rate = frame->codec->implementation->actual_samples_per_second;
- if (!session->write_resampler) {
- switch_mutex_lock(session->resample_mutex);
- status = switch_resample_create(&session->write_resampler,
- frame->codec->implementation->actual_samples_per_second,
- session->write_impl.actual_samples_per_second,
- session->write_impl.decoded_bytes_per_packet, SWITCH_RESAMPLE_QUALITY, session->write_impl.number_of_channels);
- switch_resample_process(session->write_resampler, data, write_frame->datalen / 2 / session->write_resampler->channels);
再编码
- status = switch_core_codec_encode(session->write_codec,
- frame->codec,
- enc_frame->data,
- enc_frame->datalen,
- session->write_impl.actual_samples_per_second,
- session->enc_write_frame.data, &session->enc_write_frame.datalen, &session->enc_write_frame.rate, &flag);
都处理完后,最后调用perform_write把数据发出去。
- done:
- if (ptime_mismatch || resample) {
- write_frame->timestamp = 0;
- }
- if (do_write) {
- status = perform_write(session, write_frame, flags, stream_id);
- }
perform_write实际调用端点的IO函数。
- if (session->endpoint_interface->io_routines->write_frame) {
- if ((status = session->endpoint_interface->io_routines->write_frame(session, frame, flags, stream_id)) == SWITCH_STATUS_SUCCESS) {
- for (ptr = session->event_hooks.write_frame; ptr; ptr = ptr->next) {
- if ((status = ptr->write_frame(session, frame, flags, stream_id)) != SWITCH_STATUS_SUCCESS) {
- break;
- }
- }
- }
- }