SRS: подробное объяснение webrtc_to_rtmp
SRS: подробное объяснение webrtc_to_rtmp

SRS: подробное объяснение webrtc_to_rtmp

Предисловие

SRS(Simple Realtime Server),Я начинал в аудио- и видеоиндустрии,Кто-то порекомендовал мне библиотеку с открытым исходным кодом.,Хоть я и новичок в аудио сфере,,Но у меня также накопился некоторый опыт использования srs.

В настоящее время мы проводим работу по конвертации между протоколами, а также изучаем и исследуем. Неудивительно, что srs взяли это на изучение и исследование. rtcПротокол преобразуется вrtmp.у меня тоже есть небольшой опыт,Недавно мне довелось вместе участвовать в некоторых мероприятиях.

Введение

srs

Язык кода:wiki
копировать
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

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

RTMP(Real Time Messaging Протокол) Протокол передачи сообщений в реальном времени — это протокол потоковой передачи мультимедиа, предложенный Adobe. Он обеспечивает двустороннюю канальную службу сообщений с целью передачи потоков видео, аудио и данных с информацией о времени между терминалами связи. Он передает различные приоритеты. к различным типам сообщений, а затем определить порядок передачи различных сообщений при ограничении пропускной способности сети. RTMP был впервые разработан Adobe на основе Flash. Формат передачи аудио и видео, предложенный проигрывателем. Он чрезвычайно широко использовался, когда флэш-память была популярна в первые дни. В настоящее время от флэш-памяти практически отказались. Однако RTMP как метод инкапсуляции и передачи потокового мультимедиа не был использован. используется, как и ожидалось. На современном этапе популярности прямого вещания RTMP часто используется в качестве протокола потокового мультимедиа для передачи потоков в облако. -- от Подробное объяснение протокола потокового мультимедиа RTMP

оrtmp-urlВведение:srs:rtmp-url

rtmpdump:RTMPDump

анализ процесса

запускать

Оба используют порт по умолчанию и httpx-static для настройки https-прокси.

  1. srs - запускать
Язык кода:shell
копировать
./objs/srs -c conf/rtc.conf
#Помнить rtc.conf -> rtc_to_rtmp on;
  1. signaling
Язык кода:shell
копировать
./objs/signaling
  1. httpx-static
Язык кода:shell
копировать
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,Есть канал для потока данных,Тогда просто разберитесь,В процессе передачи данных,существовать Какой момент временируководитьиз Преобразование формата данных,Как конвертироватьиз。Прямо сейчаспротоколиз Конверсия разобрана четко。

Язык кода:shell
копировать
1. Канал передачи данных
2.Преобразование формата данных

В SRS преобразование протоколов в основном выполняется через мосты для подключения и преобразования.

Язык кода:wiki
копировать
rtc_server->bridge->live_server(rtmp_server)

Более подробно, live_source привязан к brige, а затем данные rtc_server передаются из birge в live_source и далее в rtmp_server.

Язык кода:wiki
копировать
rtc_server->live_source(bridge)->live_server(rtmp_server)

С точки зрения данных, это

Язык кода:wiki
копировать
Принять данные-> Чтение данных -> Анализ данных -> Преобразование формата данных -> Отправка данных

Конвертировать в срс

Язык кода:wiki
копировать
rtc_server(UDP:8000)_порт прослушивания->rtc_server_получать данные->rtc_server_Анализ данных->rtc_server_Преобразование данных->rtc_to_rtmp_brigre_Отправить данные->live_server->rtmp_client

Основное внимание уделяется этим частям.

Мост rtp_to_rtmp установлен

Язык кода:shell
копировать
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:

Язык кода:c
копировать
    // 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

Чтение данных UDP

Модель epoll прослушивает порт и считывает данные, что также является традиционным процессом.

Язык кода:txt
копировать
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]

Анализ UDP-данных

Анализ данных RTP. Процесс анализа по-прежнему относительно сложен, поскольку в процессе передачи RTP существует множество способов передачи пакетов: один пакет, пакет, состоящий из нескольких отдельных пакетов, фрагментированная передача большого пакета, а также протокол RTP. Это относительно сложно, а также есть отдельная обработка rtcp.

Язык кода:txt
копировать
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,Если у вас нет соответствующего понимания, вам все равно нужно посмотреть.

Язык кода:txt
копировать
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изкод ключа。

Язык кода:c
копировать
    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. Фактически, это в основном обработка заголовка.

Язык кода:c
копировать
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.

Язык кода:shell
копировать
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 справиться
  1. Вычислить размер nb_payload
Язык кода:c
копировать
    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-пакет

Язык кода:c
копировать
   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 и отправляет его соответствующему потребителю.

Язык кода:txt
копировать
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из О каких модулях вы хотите узнать?изчасть,Вы также можете объяснить это в области комментариев. Если что-то не так,Пожалуйста, объясните прямо в области комментариев.,Будут внесены соответствующие изменения.

приложение

Ниже представлена ​​некоторая полезная информация в процессе обучения:

boy illustration
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода