SRS(Simple Realtime Server),Я начинал в аудио- и видеоиндустрии,Кто-то порекомендовал мне библиотеку с открытым исходным кодом.,Хоть я и новичок в аудио сфере,,Но у меня также накопился некоторый опыт использования srs.
В настоящее время мы проводим работу по конвертации между протоколами, а также изучаем и исследуем. Неудивительно, что srs взяли это на изучение и исследование. rtcПротокол преобразуется вrtmp.у меня тоже есть небольшой опыт,Недавно мне довелось вместе участвовать в некоторых мероприятиях.
SRS — это простой и эффективный видеосервер реального времени с открытым исходным кодом (протокол MIT), который поддерживает такие протоколы, как RTMP, WebRTC, HLS, HTTP-FLV, SRT, MPEG-DASH и GB28181. Медиасервер SRS и FFmpeg, OBS, VLC, Используемый совместно с такими клиентами, как WebRTC, для обеспечения возможности получения и распространения потоков, это типичный тип публикации. Модели серверов (push) и подписки (play). SRS поддерживает преобразование аудио- и видеопротоколов, широко используемых в Интернете, таких как RTMP или SRT. Преобразование в такие протоколы, как HLS, HTTP-FLV или WebRTC.
Официальный адрес сайта:SRS
SRSоrtc-to-rtmp:srs:rtc_to_rtmp
srsСкомпилируйте, скачайте и запустите:srs: компиляция и работа исходного кода
WebRTC (Web Real-Time Communications) — это технология связи в реальном времени, которая позволяет сетевым приложениям или сайтам устанавливать двухточечные (Peer-to-Peer) соединения между браузерами без использования посредников для обеспечения потоковой передачи видео и/или. или передачу аудиопотоков или других произвольных данных.
О прямом эфире и конвертации протоколов,Главное — спроектировать уровень передачи мультимедиа.,webrtcИспользование транспортного уровня протокола мультимедиаrtp(Real-time Transport Protocol)。
gitадрес:webrtc
srsоrtmp:srs:webrtc
webrtcтранспортный протокол:WebRTC-обучение Подробное объяснение сетевых протоколов передачи данных в реальном времени (стек протоколов браузера, анализ протокола передачи WebRTC)
RTPВведение протокола:rtp
RTMP(Real Time Messaging Протокол) Протокол передачи сообщений в реальном времени — это протокол потоковой передачи мультимедиа, предложенный Adobe. Он обеспечивает двустороннюю канальную службу сообщений с целью передачи потоков видео, аудио и данных с информацией о времени между терминалами связи. Он передает различные приоритеты. к различным типам сообщений, а затем определить порядок передачи различных сообщений при ограничении пропускной способности сети. RTMP был впервые разработан Adobe на основе Flash. Формат передачи аудио и видео, предложенный проигрывателем. Он чрезвычайно широко использовался, когда флэш-память была популярна в первые дни. В настоящее время от флэш-памяти практически отказались. Однако RTMP как метод инкапсуляции и передачи потокового мультимедиа не был использован. используется, как и ожидалось. На современном этапе популярности прямого вещания RTMP часто используется в качестве протокола потокового мультимедиа для передачи потоков в облако. -- от Подробное объяснение протокола потокового мультимедиа RTMP
оrtmp-urlВведение:srs:rtmp-url
rtmpdump:RTMPDump
Оба используют порт по умолчанию и httpx-static для настройки https-прокси.
./objs/srs -c conf/rtc.conf
#Помнить rtc.conf -> rtc_to_rtmp on;
./objs/signaling
openssl genrsa -out server.key 2048
subj="/C=CN/ST=Beijing/L=Beijing/O=Me/OU=Me/CN=me.org"
openssl req -new -x509 -key server.key -out server.crt -days 365 -subj $subj
sudo ./objs/httpx-static -t 80 -s 443 -r /data/srs/trunk/3rdparty/signaling/www/ -ssk ./objs/server.key -ssc ./objs/server.crt -p http://127.0.0.1:1985/rtc -p http://127.0.0.1:1989/sig
существоватьsrsизоrtc_to_rtmp,На самом деле речь идет в основном о потоке данных,как начать сrtc_serverприезжатьrtmp_server,Как преобразуется формат данных?,Если изrtpприезжатьflv,Есть канал для потока данных,Тогда просто разберитесь,В процессе передачи данных,существовать Какой момент временируководитьиз Преобразование формата данных,Как конвертироватьиз。Прямо сейчаспротоколиз Конверсия разобрана четко。
1. Канал передачи данных
2.Преобразование формата данных
В SRS преобразование протоколов в основном выполняется через мосты для подключения и преобразования.
rtc_server->bridge->live_server(rtmp_server)
Более подробно, live_source привязан к brige, а затем данные rtc_server передаются из birge в live_source и далее в rtmp_server.
rtc_server->live_source(bridge)->live_server(rtmp_server)
С точки зрения данных, это
Принять данные-> Чтение данных -> Анализ данных -> Преобразование формата данных -> Отправка данных
Конвертировать в срс
rtc_server(UDP:8000)_порт прослушивания->rtc_server_получать данные->rtc_server_Анализ данных->rtc_server_Преобразование данных->rtc_to_rtmp_brigre_Отправить данные->live_server->rtmp_client
Основное внимание уделяется этим частям.
srs_main_server.cpp => int main(int argc, char** argv, char** envp) //Инициализируем функцию входа
srs_main_server.cpp => err = do_main(argc, argv, envp) //основная функциязапускать
srs_main_server.cpp => srs_error_t do_main(int argc, char** argv, char** envp) //
srs_main_server.cpp => _srs_config->check_config() //Конфигурация чтения и обнаружения файла конфигурации
srs_main_server.cpp => run_directly_or_daemon() //Охраняется ли процесс и работает ли он в фоновом режиме
srs_main_server.cpp => run_in_thread_pool() // Запустить пул потоков
srs_main_server.cpp => _srs_thread_pool->execute("hybrid", run_hybrid_server, (void*)NULL)) // осуществлять run_hybrid_server
srs_main_server.cpp => _srs_hybrid->register_server(new RtcServerAdapter()) //Регистрируем службу RTC
srs_main_server.cpp => _srs_hybrid->initialize() / server->initialize() //инициализация сервера
srs_main_server.cpp => _srs_hybrid->run() / SrsHybridServer::run() //server run
srs_app_rtc_server.cpp => RtcServerAdapter::run(SrsWaitGroup* wg) //
srs_app_rtc_server.cpp => SrsRtcServer::listen_api() //руководить
srs_app_rtc_conn.cpp => SrsGoApiRtcPublish::serve_http / server_->create_session(ruc, local_sdp, &session) // провести РТК publish создание сеанса
srs_app_rtc_conn.cpp => SrsGoApiRtcPlay::serve_http / server_->create_session(ruc, local_sdp, &session) // провести РТК play создание сеанса
srs_app_rtc_conn.cpp => do_create_session(ruc, local_sdp, session) // создать ртк сессия
srs_app_rtc_conn.cpp => SrsRtcConnection::add_publisher()/ session->add_publisher(ruc, local_sdp) //Добавляем издателя
srs_app_rtc_conn.cpp => SrsRtcConnection::create_publisher(SrsRequest* req, SrsRtcSourceDescription* stream_desc) //rtc создан Publisher
srs_app_rtc_conn.cpp => publisher->initialize(req, stream_desc) //publisher инициализация
srs_app_rtc_conn.cpp => SrsRtcPublishStream::initialize(SrsRequest* r, SrsRtcSourceDescription* stream_desc) //rtc Push-стримингинициализация
srs_app_rtc_conn.cpp => source_->set_bridge(bridge) //Настройки моста
Это код конфигурации моста rtc_to_rtmp:
// Bridge to rtmp
#if defined(SRS_RTC) && defined(SRS_FFMPEG_FIT)
//Прочитайте файл конфигурации, чтобы узнать, включены ли настройки преобразования протокола
bool rtc_to_rtmp = _srs_config->get_rtc_to_rtmp(req_->vhost);
//Если преобразование протокола включено
if (rtc_to_rtmp) {
//Обход, чтобы проверить, есть ли соответствующий liveSource, создайте его, если он не существует
if ((err = _srs_sources->fetch_or_create(r, _srs_hybrid->srs()->instance(), live_source)) != srs_success) {
return srs_error_wrap(err, "create source");
}
// Disable GOP cache for RTC2RTMP bridge, to keep the streams in sync,
// especially for stream merging.
live_source->set_cache(false);
//Создаем новый мост
SrsCompositeBridge* bridge = new SrsCompositeBridge();
//Добавить в to_rtmp_brige
bridge->append(new SrsFrameToRtmpBridge(live_source));
//brige инициализация
if ((err = bridge->initialize(r)) != srs_success) {
srs_freep(bridge);
return srs_error_wrap(err, "create bridge");
}
//Привязываем liveSource к мосту
source_->set_bridge(bridge);
}
#endif
Модель epoll прослушивает порт и считывает данные, что также является традиционным процессом.
srs_app_listener.cpp => srs_error_t SrsUdpMuxListener::cycle() //Функция прослушивания
srs_app_listener.cpp => int nread = skt.recvfrom(SRS_UTIME_NO_TIMEOUT) //Чтение данных
srs_app_listener.cpp => int SrsUdpMuxSocket::recvfrom(srs_utime_t timeout) //Чтение данныхфункция
srs_app_listener.cpp => nread = srs_recvfrom(lfd, buf, nb_buf, (sockaddr*)&from, &fromlen, timeout) //Чтение данныхприезжать buf[SRS_UDP_MAX_PACKET_SIZE] buf[65535]
Анализ данных RTP. Процесс анализа по-прежнему относительно сложен, поскольку в процессе передачи RTP существует множество способов передачи пакетов: один пакет, пакет, состоящий из нескольких отдельных пакетов, фрагментированная передача большого пакета, а также протокол RTP. Это относительно сложно, а также есть отдельная обработка rtcp.
srs_app_listener.cpp => err = handler->on_udp_packet(&skt); //Анализ Функция ввода UDP-данных
srs_app_listener.hpp => ISrsUdpMuxHandler* handler; //Приведенное выше определение обработчика
srs_app_listener.cpp => SrsUdpMuxListener::SrsUdpMuxListener(ISrsUdpMuxHandler* h, std::string i, int p) //назначение обработчика
srs_app_rtc_server.cpp => SrsUdpMuxListener* listener = new SrsUdpMuxListener(this, ip, port); //SrsUdpMuxListener Создание экземпляра, входной параметр обработчика, значение равно this Прямо сейчас SrsRtcServer
srs_app_rtc_server.hpp => class SrsRtcServer : public ISrsUdpMuxHandler, public ISrsFastTimer, public ISrsReloadHandler //При определении SrsRtcServer вы можете видеть, что он наследует публичный класс ISrsUdpMuxHandler,Прямо сейчасon_udp_packet()для virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* skt)
srs_app_rtc_server.hpp => virtual srs_error_t on_udp_packet(SrsUdpMuxSocket* skt) объявлена виртуальная функция on_udp_packet() и реализован в CPP
srs_app_rtc_server.cpp => SrsRtcServer::on_udp_packet(SrsUdpMuxSocket* skt)
srs_app_rtc_server.cpp => char* data = skt->data(); int size = skt->size(); //полученный data = buf[SRS_UDP_MAX_PACKET_SIZE] size= nread
srs_app_rtc_server.cpp => bool is_rtp_or_rtcp = srs_is_rtp_or_rtcp((uint8_t*)data, size); //Оцениваем, так ли это rtp_or_rtcp пакет
srs_app_rtc_server.cpp => bool is_rtcp = srs_is_rtcp((uint8_t*)data, size); //Оцениваем, так ли это rtcp пакет
srs_app_rtc_server.cpp => err = session->udp()->on_rtp(data, size); //в случаеrtpизпакет,руководитьпереписыватьсяизиметь дело с
srs_app_rtc_network.cpp => srs_error_t SrsRtcUdpNetwork::on_rtp(char* data, int nb_data) //Найти rtc_network udp функция on_rtp()
srs_app_rtc_network.cpp => conn_->on_rtp_plaintext(unprotected_buf, nb_unprotected_buf) //руководитьобработка контекста rtp
srs_app_rtc_conn.cpp => publisher->on_rtp_plaintext(data, nb_data) //SrsRtcPublishStream контекстная обработка
srs_app_rtc_conn.cpp => SrsRtcPublishStream::on_rtp_plaintext(char* plaintext, int nb_plaintext)
srs_app_rtc_conn.cpp => err = do_on_rtp_plaintext(pkt, &buf); //SrsRtcPublishStream продолжать дальше обработка контекста rtp
srs_app_rtc_conn.cpp => pkt->decode(buf) //руководитьSrsRtpPacketпакетдекодирование
srs_kernel_rtc_rtp.cpp => srs_error_t SrsRtpHeader::decode(SrsBuffer* buf) //Ключевая функция декодирования rtp
srs_kernel_rtc_rtp.cpp => ssrc = buf->read_4bytes() //Получаем ssrc
srs_kernel_rtc_rtp.cpp => SrsRtcAudioRecvTrack* audio_track = get_audio_track(ssrc) // Создание экземпляра аудио через ssrc
srs_kernel_rtc_rtp.cpp => SrsRtcVideoRecvTrack* video_track = get_video_track(ssrc) // Создание экземпляра видео через ssrc
srs_kernel_rtc_rtp.cpp => audio_track->on_rtp(source, pkt) //руководить Обработка аудиопакетов
srs_kernel_rtc_rtp.cpp => video_track->on_rtp(source, pkt) //руководить Обработка видеопакетов
srs_app_rtc_source.cpp => source->on_rtp(pkt) //SrsRtcSource руководить SrsRtpPacket* pkt Обработка пакетов
srs_app_rtc_source.cpp => frame_builder_->on_rtp(pkt)//SrsRtcFrameBuilder руководить pktОбработка пакетов
srs_app_rtc_source.cpp => SrsRtcFrameBuilder::on_rtp(SrsRtpPacket *pkt)
srs_app_rtc_source.cpp => transcode_audio(pkt) //в случаеаудиопакет,руководить обработкой транскодирования,ПерекодироватьдляAAC
srs_app_rtc_source.cpp => packet_video(pkt) //в случаевидео пакет,руководить Обработка видеопакетов
При обработке аудиопакетов мы в основном выполняем две части работы. opusизменятьдляaac,Другая частьrtpизменятьдляflv,其中изменять换是使用изffmpeg api,Если у вас нет соответствующего понимания, вам все равно нужно посмотреть.
srs_app_rtc_source.cpp => SrsRtcFrameBuilder::transcode_audio(SrsRtpPacket *pkt)//функция ввода
srs_app_rtc_source.cpp => if (is_first_audio_) //Определите, есть лидляпервый кадраудиокадр。руководить Обработка аудиокадра первого кадра
srs_app_rtc_source.cpp => codec_->aac_codec_header(&header, &header_len); //руководитьпервый кадрheaderиметь дело с
srs_app_rtc_source.cpp => SrsCommonMessage out_rtmp //Определяем RTMP сообщение
srs_app_rtc_source.cpp => packet_aac(&out_rtmp, (char *)header, header_len, ts, is_first_audio_) //руководитьrtmpинформацияизформат
srs_app_rtc_source.cpp => SrsSharedPtrMessage msg; //Определение универсально отправляемого пакета сообщений
srs_app_rtc_source.cpp => msg.create(&out_rtmp) //Форматируем пакет общего сообщения в соответствии с аудиопакетом
srs_app_rtc_source.cpp => bridge_->on_frame(&msg)) //Мост руководить сообщением message Обработка пакетов
srs_app_rtc_source.cpp => SrsRtpRawPayload *payload = dynamic_cast<SrsRtpRawPayload*>(pkt->payload()); //Получаем сообщение аудиопакета в pkt
srs_app_rtc_source.cpp => codec_->transcode(&frame, out_pkts) //Используем ffmpeg api руководить транскодированием аудиокадров
srs_app_rtc_source.cpp => std::vector<SrsAudioFrame*>::iterator it = out_pkts.begin(); it != out_pkts.end(); ++it //руководить Перекодировать后аудиокадриметь дело с
srs_app_rtc_source.cpp => SrsCommonMessage out_rtmp //Определяем RTMP сообщение
srs_app_rtc_source.cpp => packet_aac(&out_rtmp, (char *)header, header_len, ts, is_first_audio_) //руководитьrtmpинформацияизформат
srs_app_rtc_source.cpp => SrsSharedPtrMessage msg; //Определение универсально отправляемого пакета сообщений
srs_app_rtc_source.cpp => msg.create(&out_rtmp) //Форматируем пакет общего сообщения в соответствии с аудиопакетом
srs_app_rtc_source.cpp => bridge_->on_frame(&msg)) //Мост руководить сообщением message Обработка пакетов
Этот фрагмент кода srs_error_t SrsRtcFrameBuilder::transcode_audio(SrsRtpPacket *pkt)
более важныйизчасть,дляopusизменятьдляaacизкод ключа。
std::vector<SrsAudioFrame*> out_pkts;
//Получаем аудиопакет rtp
SrsRtpRawPayload *payload = dynamic_cast<SrsRtpRawPayload*>(pkt->payload());
//Определяем переменную SrsAudioFrame
SrsAudioFrame frame;
//Копируем данные аудиокадра
frame.add_sample(payload->payload, payload->nn_payload);
frame.dts = ts;
frame.cts = 0;
//Транскодирование аудиокадра
err = codec_->transcode(&frame, out_pkts);
if (err != srs_success) {
return err;
}
//Функция aac и инкапсулирована в формате flv. Фактически, это в основном обработка заголовка.
void SrsRtcFrameBuilder::packet_aac(SrsCommonMessage* audio, char* data, int len, uint32_t pts, bool is_header)
{
//Добавляем 2 байта тега header
int rtmp_len = len + 2;
audio->header.initialize_audio(rtmp_len, pts, 1);
audio->create_payload(rtmp_len);
SrsBuffer stream(audio->payload, rtmp_len);
//AAC flag Настройки недвижимости
uint8_t aac_flag = (SrsAudioCodecIdAAC << 4) | (SrsAudioSampleRate44100 << 2) | (SrsAudioSampleBits16bit << 1) | SrsAudioChannelsStereo;
//Запись AAC Подробные свойства
stream.write_1bytes(aac_flag);
//Обработка заголовка
if (is_header) {
stream.write_1bytes(0);
} else {
stream.write_1bytes(1);
}
//Запись FLV-данных
stream.write_bytes(data, len);
audio->size = rtmp_len;
}
При обработке видеопакетов это все еще относительно сложно, поскольку однокадровые пакеты данных аудиопакетов относительно велики, некоторые из них представляют собой одиночные пакеты, некоторые из нескольких пакетов, а некоторые являются фрагментированными пакетами, поэтому поток обработки также относительно сложен. Существует также метод извлечения данных чистого потока из rtp и преобразования их в формат данных flv. Вам необходимо иметь соответствующее представление о формате чистого потока, например h264. Если вы хотите передавать чистый поток h264, какой это формат? Какой формат h264 в rtp? Как следует обрабатывать h264 в flv.
srs_app_rtc_source.cpp => SrsRtcFrameBuilder::packet_video(SrsRtpPacket* src)//функция входа
srs_app_rtc_source.cpp => packet_video_key_frame(pkt) //Если это ключевой кадр - руководитьключевой кадриметь дело с
srs_app_rtc_source.cpp => SrsSample* sps = stap_payload ? stap_payload->get_sps() : NULL; //Получаем ключевые кадры sps информация
srs_app_rtc_source.cpp => SrsSample* pps = stap_payload ? stap_payload->get_pps() : NULL; //Получаем ключевые кадры pps информация
srs_app_rtc_source.cpp => avc->mux_sequence_header(string(sps->bytes, sps->size), string(pps->bytes, pps->size), sh)) //Создаем rtmp:flv ключевой кадр sps/pps информационный руководитель
srs_app_rtc_source.cpp => avc->mux_avc2flv(sh, SrsVideoAvcFrameTypeKeyFrame, SrsVideoAvcFrameTraitSequenceHeader, pkt->get_avsync_time(), pkt->get_avsync_time(), &flv, &nb_flv)) != srs_success) // Передача содержимого кадра h264 сырой формат FLV - формат h264
srs_app_rtc_source.cpp => SrsSharedPtrMessage msg; \ msg.create(&rtmp) // Создайте общий пакет сообщений
srs_app_rtc_source.cpp => bridge_->on_frame(&msg) //Моструководитьинформацияиметь дело с
srs_app_rtc_source.cpp => packet_video_rtmp(const uint16_t start, const uint16_t конец)//Обычные видеокадры и ключевой кадр Обычно применимоиз Содержимое кадраиметь дело с
srs_app_rtc_source.cpp => nb_payload //Передача размера пакета,Не забудьте подписатьсяflvформат данныхруководитьвычислитьиз rtp_body_payload + flv_header + flv_tag_header справиться
int nb_payload = 0;
int16_t cnt = srs_rtp_seq_distance(start, end) + 1;
srs_assert(cnt >= 1);
for (uint16_t i = 0; i < (uint16_t)cnt; ++i) {
uint16_t sn = start + i;
uint16_t index = cache_index(sn);
SrsRtpPacket* pkt = cache_video_pkts_[index].pkt;
// fix crash when pkt->payload() if pkt is nullptr;
if (!pkt) continue;
// calculate nalu len
SrsRtpFUAPayload2* fua_payload = dynamic_cast<SrsRtpFUAPayload2*>(pkt->payload());
if (fua_payload && fua_payload->size > 0) {
if (fua_payload->start) {
// Как быть при фрагментированной передаче FUA с размером первого фрагмента и заголовка пакета? Формат 1 байта: Полный размер пакета 4 байта.
nb_payload += 1 + 4;
}
nb_payload += fua_payload->size;
continue;
}
SrsRtpSTAPPayload* stap_payload = dynamic_cast<SrsRtpSTAPPayload*>(pkt->payload());
if (stap_payload) {
for (int j = 0; j < (int)stap_payload->nalus.size(); ++j) {
SrsSample* sample = stap_payload->nalus.at(j);
if (sample->size > 0) {
// Обработка отдельных пакетов относительно проста. Размер размер сумки + Размер пакета данных
nb_payload += 4 + sample->size;
}
}
continue;
}
SrsRtpRawPayload* raw_payload = dynamic_cast<SrsRtpRawPayload*>(pkt->payload());
if (raw_payload && raw_payload->nn_payload > 0) {
//мультиупаковка,в FLV,Каждый подпакет отправляется отдельно,Прямо Сейчас для каждого подпакета требуется + ребенокразмер сумки
nb_payload += 4 + raw_payload->nn_payload;
continue;
}
}
if (0 == nb_payload) {
srs_warn("empty nalu");
return err;
}
//type_codec1 + avc_type + composition time + nalu size + nalu
//Вам также необходимо добавить TAG header размер
nb_payload += 1 + 1 + 3;
2. Объединить rtmp-пакет
SrsCommonMessage rtmp;
SrsRtpPacket* pkt = cache_video_pkts_[cache_index(start)].pkt;
rtmp.header.initialize_video(nb_payload, pkt->get_avsync_time(), 1);
rtmp.create_payload(nb_payload);
rtmp.size = nb_payload;
SrsBuffer payload(rtmp.payload, rtmp.size);
//Запись заголовка
if (pkt->is_keyframe()) {
payload.write_1bytes(0x17); // type(4 bits): key frame; code(4bits): avc
rtp_key_frame_ts_ = -1;
} else {
payload.write_1bytes(0x27); // type(4 bits): inter frame; code(4bits): avc
}
payload.write_1bytes(0x01); // avc_type: nalu
payload.write_1bytes(0x0); // composition time
payload.write_1bytes(0x0);
payload.write_1bytes(0x0);
//aразмер сумки
int nalu_len = 0;
for (uint16_t i = 0; i < (uint16_t)cnt; ++i) {
uint16_t index = cache_index((start + i));
SrsRtpPacket* pkt = cache_video_pkts_[index].pkt;
// fix crash when pkt->payload() if pkt is nullptr;
if (!pkt) continue;
cache_video_pkts_[index].in_use = false;
cache_video_pkts_[index].pkt = NULL;
cache_video_pkts_[index].ts = 0;
cache_video_pkts_[index].rtp_ts = 0;
cache_video_pkts_[index].sn = 0;
SrsRtpFUAPayload2* fua_payload = dynamic_cast<SrsRtpFUAPayload2*>(pkt->payload());
if (fua_payload && fua_payload->size > 0) {
//Обрабатываем первый фрагментированный пакет
if (fua_payload->start) {
//Рассчитываем первый размер фрагментированного пакета сумки
nalu_len = fua_payload->size + 1;
//skip 4 bytes to write nalu_len future
//Пропускаем первые 4 байта. После записи всего пакета вычисляем размер всего пакета и затем записываем.
payload.skip(4);
//Записываем бит формата байта
payload.write_1bytes(fua_payload->nri | fua_payload->nalu_type);
//Копируем фрагментированный пакет
payload.write_bytes(fua_payload->payload, fua_payload->size);
} else {
//накопление промежуточных пакетов
nalu_len += fua_payload->size;
//Копируем средний пакет
payload.write_bytes(fua_payload->payload, fua_payload->size);
//Обрабатываем последний пакет
if (fua_payload->end) {
//write nalu_len back
//Записываем обратно весь размер пакета - Указатель переходит к началу фрагментированного пакета.
payload.skip(-(4 + nalu_len));
//Записываем весь размер пакета
payload.write_4bytes(nalu_len);
//Указатель переходит в конец пакета
payload.skip(nalu_len);
}
}
srs_freep(pkt);
continue;
}
SrsRtpSTAPPayload* stap_payload = dynamic_cast<SrsRtpSTAPPayload*>(pkt->payload());
if (stap_payload) {
for (int j = 0; j < (int)stap_payload->nalus.size(); ++j) {
SrsSample* sample = stap_payload->nalus.at(j);
if (sample->size > 0) {
//В каждом подпакете размер подпакета прописан в заголовке
payload.write_4bytes(sample->size);
//Запись данных подпакета
payload.write_bytes(sample->bytes, sample->size);
}
}
srs_freep(pkt);
continue;
}
SrsRtpRawPayload* raw_payload = dynamic_cast<SrsRtpRawPayload*>(pkt->payload());
if (raw_payload && raw_payload->nn_payload > 0) {
//Записываем размер одного пакета
payload.write_4bytes(raw_payload->nn_payload);
//Запись одного пакета данных
payload.write_bytes(raw_payload->payload, raw_payload->nn_payload);
srs_freep(pkt);
continue;
}
srs_freep(pkt);
}
//Создаем пакет отправки
SrsSharedPtrMessage msg;
if ((err = msg.create(&rtmp)) != srs_success) {
return srs_error_wrap(err, "create message");
}
//Преобразованный пакет обработки контактов моста
if ((err = bridge_->on_frame(&msg)) != srs_success) {
srs_warn("fail to pack video frame");
}
Когда дело доходит до моста, обработка на самом деле относительно проста. Это также последний узел преобразования данных. Он отправляет пакет данных в live_souce и отправляет его соответствующему потребителю.
srs_app_stream_brige.cpp => srs_error_t SrsFrameToRtmpBridge::on_frame(SrsSharedPtrMessage* кадр)//функция ввода
srs_app_stream_brige.cpp => source_->on_frame(frame) // SrsLiveSource руководитьинформация Обработка пакетов
srs_app_source.app => SrsLiveSource::on_frame(SrsSharedPtrMessage* msg) // liveSource информация Обработка пакетов Входфункция
srs_app_source.app => mix_queue->push(msg->copy()) //Помещаем сообщение в очередь mix_queque
srs_app_source.app => SrsSharedPtrMessage* m = mix_queue->pop() //Получаем сообщение из очереди
srs_app_source.app => on_audio_imp(m) //еслиинформацияэто аудио,руководить Аудиоинформацияиметь дело с
srs_app_source.app => on_video_imp(m) //еслиинформация Это видео,руководитьвидеоинформацияиметь дело с
srs_app_source.app => consumer->enqueue(msg, atc, jitter_algorithm) //Потребители потребляют соответствующий аудио- и видеопакет
Отправка данных клиенту не поясняется.
на самом делеSRSизrtc_to_rtmpизпротоколизменять换流程相对复杂一点,Но очень представительный,Я рассказал только некоторые ключевые моменты,и одна или две ключевые функции объяснены подробно.,Но вся связь с процессом все еще относительно завершена. Вам все равно необходимо иметь определенное понимание различных протоколов.,а такжеC++из Некоторые базовые знания лучшеизпонимать。Может быть, я сделаю это позжеSRSиз Другие части будут продолжать давать некоторые пояснения.из,Если позволяют силы и время,Или, может быть, все правыsrsиз О каких модулях вы хотите узнать?изчасть,Вы также можете объяснить это в области комментариев. Если что-то не так,Пожалуйста, объясните прямо в области комментариев.,Будут внесены соответствующие изменения.
Ниже представлена некоторая полезная информация в процессе обучения: