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;
- }
- }
- }
- }