commit cca1d056c9724b373a75d169909761f1cf8fe51d Author: luocai Date: Thu Jan 18 22:22:26 2024 +0800 first commit diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..f3960e2 --- /dev/null +++ b/.clang-format @@ -0,0 +1,17 @@ +BasedOnStyle: LLVM + +ObjCBlockIndentWidth: 4 +IndentWidth: 4 +TabWidth: 4 +AccessModifierOffset: -4 +ColumnLimit: 120 + +#模板声明后换行 +AlwaysBreakTemplateDeclarations: true + +# 是否允许短if单行 If true, if (a) return; 可以放到同一行 +AllowShortIfStatementsOnASingleLine: true + +#短句 while (true) continue; 能被放到单行。 +AllowShortLoopsOnASingleLine: true +AllowShortFunctionsOnASingleLine: false \ No newline at end of file diff --git a/AudioDecoder.cpp b/AudioDecoder.cpp new file mode 100644 index 0000000..e5deca2 --- /dev/null +++ b/AudioDecoder.cpp @@ -0,0 +1,10 @@ +#include "AudioDecoder.h" +extern "C" { +#include "libavcodec/avcodec.h" +} + +AudioDecoder::AudioDecoder(const AVCodec *codec) { +} + +void AudioDecoder::push(const std::shared_ptr &packet) { +} diff --git a/AudioDecoder.h b/AudioDecoder.h new file mode 100644 index 0000000..7119df9 --- /dev/null +++ b/AudioDecoder.h @@ -0,0 +1,14 @@ +#ifndef __AUDIODECODER_H__ +#define __AUDIODECODER_H__ + +#include + +struct AVCodec; +struct AVPacket; + +class AudioDecoder { +public: + AudioDecoder(const AVCodec *codec); + void push(const std::shared_ptr &packet); +}; +#endif // __AUDIODECODER_H__ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..11c7003 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,64 @@ +cmake_minimum_required(VERSION 3.27) + +project(FFmpegPlayer) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(Projects_ROOT E:/Projects) +set(Libraries_ROOT ${Projects_ROOT}/Libraries) + +set(BOOST_ROOT ${Libraries_ROOT}/boost_1_84_0_msvc2022_64bit) +set(Boost_INCLUDE_DIR ${BOOST_ROOT}/include/boost-1_84) +option(Boost_USE_STATIC_LIBS OFF) +add_compile_definitions( + BOOST_USE_WINAPI_VERSION=BOOST_WINAPI_VERSION_WIN10 +) + +find_package(Boost REQUIRED COMPONENTS json) + +set(OpenSSL_ROOT D:/Qt/Tools/OpenSSLv3/Win_x64) +set(OPENSSL_INCLUDE_DIR ${OpenSSL_ROOT}/include) +set(OpenSSL_LIBRARY_DIRS ${OpenSSL_ROOT}/lib) +set(OpenSSL_LIBRARIES libssl libcrypto) + + +set(FFMPEG_ROOT ${Libraries_ROOT}/ffmpeg-6.0-full_build-shared) +set(FFMPEG_INCLUDE_DIR ${FFMPEG_ROOT}/include) +set(FFMPEG_LIBRARY_DIRS ${FFMPEG_ROOT}/lib) +set(FFMPEG_LIBRARIES avcodec avformat avutil) + +find_package(Qt6 REQUIRED COMPONENTS Widgets) +qt_standard_project_setup() + + +set(LibDataChannel_DIR E:/Projects/Libraries/libdatachannel-0.19.5_msvc2022_64bit_debug/lib/cmake/LibDataChannel) +find_package(LibDataChannel) + +add_subdirectory(E:/Projects/Kylin/Universal Universal) +add_subdirectory(E:/Projects/Kylin/HttpProxy HttpProxy) + +add_executable(FFmpegPlayer main.cpp + Demuxer.h Demuxer.cpp + AudioDecoder.h AudioDecoder.cpp + VideoDecoder.h VideoDecoder.cpp + WebRTCPublisher.h WebRTCPublisher.cpp +) + +target_include_directories(FFmpegPlayer + PRIVATE ${FFMPEG_INCLUDE_DIR} +) + +target_link_directories(FFmpegPlayer + PRIVATE ${FFMPEG_LIBRARY_DIRS} + PRIVATE ${OpenSSL_LIBRARY_DIRS} +) + +target_link_libraries(FFmpegPlayer + PRIVATE Qt6::Widgets + PRIVATE Universal + PRIVATE HttpProxy + PRIVATE ${FFMPEG_LIBRARIES} + PRIVATE ${OpenSSL_LIBRARIES} + PRIVATE LibDataChannel::LibDataChannel +) diff --git a/CMakeLists.txt.user b/CMakeLists.txt.user new file mode 100644 index 0000000..fc7ae3b --- /dev/null +++ b/CMakeLists.txt.user @@ -0,0 +1,419 @@ + + + + + + EnvironmentId + {11f4a1e9-12b5-4bf9-bf26-e33024189f36} + + + ProjectExplorer.Project.ActiveTarget + 0 + + + ProjectExplorer.Project.EditorSettings + + true + false + true + + Cpp + + CppGlobal + + + + QmlJS + + QmlJSGlobal + + + 2 + UTF-8 + false + 4 + false + 80 + true + true + 1 + 0 + false + true + false + 0 + true + true + 0 + 8 + true + false + 0 + true + true + true + *.md, *.MD, Makefile + false + true + true + + + + ProjectExplorer.Project.PluginSettings + + + true + false + true + true + true + true + + + 0 + true + + true + true + Builtin.DefaultTidyAndClazy + 3 + true + + + + true + + + + + ProjectExplorer.Project.Target.0 + + Desktop + Desktop Qt 6.6.1 MSVC2019 64bit + Desktop Qt 6.6.1 MSVC2019 64bit + qt.qt6.661.win64_msvc2019_64_kit + 0 + 0 + 0 + + Debug + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=Debug +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + 0 + E:\Projects\FFmpegPlayer-Desktop_Qt_6_6_1_MSVC2019_64bit-Debug + + + + + all + + false + + true + 构建 + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + 构建 + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Debug + CMakeProjectManager.CMakeBuildConfiguration + + + Release + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=Release +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + E:\Projects\FFmpegPlayer-Desktop_Qt_6_6_1_MSVC2019_64bit-Release + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + E:\Projects\FFmpegPlayer-Desktop_Qt_6_6_1_MSVC2019_64bit-RelWithDebInfo + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Release with Debug Information + CMakeProjectManager.CMakeBuildConfiguration + + + RelWithDebInfo + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + 0 + E:\Projects\FFmpegPlayer-Desktop_Qt_6_6_1_MSVC2019_64bit-Profile + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Profile + CMakeProjectManager.CMakeBuildConfiguration + + + MinSizeRel + 2 + false + + -DCMAKE_GENERATOR:STRING=Ninja +-DCMAKE_BUILD_TYPE:STRING=MinSizeRel +-DCMAKE_PROJECT_INCLUDE_BEFORE:FILEPATH=%{BuildConfig:BuildDirectory:NativeFilePath}/.qtc/package-manager/auto-setup.cmake +-DQT_QMAKE_EXECUTABLE:FILEPATH=%{Qt:qmakeExecutable} +-DCMAKE_PREFIX_PATH:PATH=%{Qt:QT_INSTALL_PREFIX} +-DCMAKE_C_COMPILER:FILEPATH=%{Compiler:Executable:C} +-DCMAKE_CXX_COMPILER:FILEPATH=%{Compiler:Executable:Cxx} +-DCMAKE_CXX_FLAGS_INIT:STRING=%{Qt:QML_DEBUG_FLAG} + E:\Projects\FFmpegPlayer-Desktop_Qt_6_6_1_MSVC2019_64bit-MinSizeRel + + + + + all + + false + + true + CMakeProjectManager.MakeStep + + 1 + 构建 + 构建 + ProjectExplorer.BuildSteps.Build + + + + + + clean + + false + + true + CMakeProjectManager.MakeStep + + 1 + 清除 + 清除 + ProjectExplorer.BuildSteps.Clean + + 2 + false + + false + + Minimum Size Release + CMakeProjectManager.CMakeBuildConfiguration + + 5 + + + 0 + 部署 + 部署 + ProjectExplorer.BuildSteps.Deploy + + 1 + + false + ProjectExplorer.DefaultDeployConfiguration + + 1 + + true + true + 0 + true + + 2 + + false + FFmpegPlayer + CMakeProjectManager.CMakeRunConfiguration.FFmpegPlayer + FFmpegPlayer + false + true + true + true + E:/Projects/FFmpegPlayer-Desktop_Qt_6_6_1_MSVC2019_64bit-Debug + + 1 + + + + ProjectExplorer.Project.TargetCount + 1 + + + ProjectExplorer.Project.Updater.FileVersion + 22 + + + Version + 22 + + diff --git a/Demuxer.cpp b/Demuxer.cpp new file mode 100644 index 0000000..826639b --- /dev/null +++ b/Demuxer.cpp @@ -0,0 +1,66 @@ +#include "Demuxer.h" +#include "BoostLog.h" +extern "C" { +#include "libavformat/avformat.h" +} + +static_assert(AVMEDIA_TYPE_VIDEO == Stream::Video, "enum value must same..."); + +Demuxer::Demuxer() { + m_context = avformat_alloc_context(); +} + +Demuxer::~Demuxer() { + m_exit = true; + if (m_thread.joinable()) m_thread.join(); +} + +void Demuxer::open(const std::string &path) { + avformat_open_input(&m_context, path.c_str(), nullptr, nullptr); + av_dump_format(m_context, 0, path.c_str(), 0); + const AVCodec *videoCodec = nullptr, *audioCodec = nullptr; + m_streams[Stream::Video] = av_find_best_stream(m_context, AVMEDIA_TYPE_VIDEO, -1, -1, &videoCodec, 0); + m_streams[Stream::Audio] = av_find_best_stream(m_context, AVMEDIA_TYPE_AUDIO, -1, -1, &audioCodec, 0); + LOG(info) << "video stream index: " << m_streams[Stream::Video] + << ", audio stream index: " << m_streams[Stream::Audio]; + + m_codecInformations[Stream::Video] = {videoCodec, m_context->streams[Stream::Video]->codecpar}; + m_codecInformations[Stream::Audio] = + std::pair(audioCodec, m_context->streams[Stream::Audio]->codecpar); +} + +Demuxer::CodecInformation Demuxer::codecInformation(Stream type) const { + return m_codecInformations[type]; +} + +void Demuxer::setStreamPacketCallback(Stream type, const Callback &callback) { + m_callback[type] = callback; +} + +void Demuxer::start() { + m_exit = false; + m_thread = std::thread(&Demuxer::run, this); +} + +void Demuxer::run() { + auto packet = av_packet_alloc(); + while (!m_exit) { + int ret = av_read_frame(m_context, packet); + if (ret == AVERROR_EOF) { + LOG(info) << "end of file."; + break; + } else if (ret < 0) { + char buffer[AV_ERROR_MAX_STRING_SIZE] = {0}; + av_strerror(ret, buffer, sizeof(buffer)); + LOG(error) << "read failed: " << ret << ", " << buffer; + break; + } + + std::shared_ptr out(av_packet_alloc(), [](AVPacket *packet) { av_packet_free(&packet); }); + av_packet_ref(out.get(), packet); + if (m_callback[packet->stream_index]) m_callback[packet->stream_index](out); + // LOG(info) << ". " << packet->stream_index; + av_packet_unref(packet); + } + av_packet_free(&packet); +} diff --git a/Demuxer.h b/Demuxer.h new file mode 100644 index 0000000..a92fa36 --- /dev/null +++ b/Demuxer.h @@ -0,0 +1,42 @@ +#ifndef __DEMUXER_H__ +#define __DEMUXER_H__ + +#include +#include +#include + +struct AVFormatContext; +struct AVCodec; +struct AVCodecParameters; +struct AVPacket; + +enum Stream : uint32_t { + Video, + Audio, + Size, +}; + +class Demuxer { +public: + using CodecInformation = std::pair; + using Callback = std::function &)>; + Demuxer(); + ~Demuxer(); + void open(const std::string &path); + CodecInformation codecInformation(Stream type) const; + void setStreamPacketCallback(Stream type, const Callback &callback); + void start(); + +protected: + void run(); + +private: + bool m_exit; + std::thread m_thread; + AVFormatContext *m_context; + int m_streams[Stream::Size]; + CodecInformation m_codecInformations[Stream::Size]; + Callback m_callback[Stream::Size]; +}; + +#endif // __DEMUXER_H__ \ No newline at end of file diff --git a/VideoDecoder.cpp b/VideoDecoder.cpp new file mode 100644 index 0000000..5233b6a --- /dev/null +++ b/VideoDecoder.cpp @@ -0,0 +1,60 @@ +#include "VideoDecoder.h" +#include "BoostLog.h" +extern "C" { +#include "libavcodec/avcodec.h" +} + +VideoDecoder::VideoDecoder(const AVCodec *codec, const AVCodecParameters *parameters) { + m_context = avcodec_alloc_context3(codec); + avcodec_parameters_to_context(m_context, parameters); + avcodec_open2(m_context, codec, nullptr); +} + +void VideoDecoder::start() { + m_exit = false; + m_thread = std::thread(&VideoDecoder::run, this); +} + +void VideoDecoder::push(const std::shared_ptr &packet) { + std::lock_guard locker(m_mutex); + LOG(info) << "dts: " << packet->dts << ", pts: " << packet->pts; + m_packets.push(packet); +} + +void VideoDecoder::run() { + using namespace std::chrono_literals; + auto frame = av_frame_alloc(); + while (!m_exit) { + m_mutex.lock(); + if (m_packets.empty()) { + m_mutex.unlock(); + std::this_thread::sleep_for(5ms); + continue; + } + auto packet = m_packets.front(); + m_packets.pop(); + m_mutex.unlock(); + + auto ret = avcodec_send_packet(m_context, packet.get()); + if (ret < 0) { + char buffer[AV_ERROR_MAX_STRING_SIZE] = {0}; + av_strerror(ret, buffer, sizeof(buffer)); + LOG(error) << "read failed: " << ret << ", " << buffer; + } + while (true) { + ret = avcodec_receive_frame(m_context, frame); + if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN)) { + break; + } else if (ret < 0) { + char buffer[AV_ERROR_MAX_STRING_SIZE] = {0}; + av_strerror(ret, buffer, sizeof(buffer)); + LOG(error) << "read failed: " << ret << ", " << buffer; + break; + } + LOG(info) << "width: " << frame->width << ", height: " << frame->height << ", pts: " << frame->pts + << ", pkt_dts:" << frame->pkt_dts; + av_frame_unref(frame); + } + } + av_frame_free(&frame); +} diff --git a/VideoDecoder.h b/VideoDecoder.h new file mode 100644 index 0000000..fffe6c1 --- /dev/null +++ b/VideoDecoder.h @@ -0,0 +1,29 @@ +#ifndef __VIDEODECODER_H__ +#define __VIDEODECODER_H__ + +#include +#include +#include + +struct AVPacket; +struct AVCodec; +struct AVCodecContext; +struct AVCodecParameters; + +class VideoDecoder { +public: + VideoDecoder(const AVCodec *codec, const AVCodecParameters *parameters); + void start(); + void push(const std::shared_ptr &packet); + +protected: + void run(); + +private: + bool m_exit; + std::thread m_thread; + AVCodecContext *m_context; + std::mutex m_mutex; + std::queue> m_packets; +}; +#endif // __VIDEODECODER_H__ \ No newline at end of file diff --git a/WebRTCPublisher.cpp b/WebRTCPublisher.cpp new file mode 100644 index 0000000..461b9e2 --- /dev/null +++ b/WebRTCPublisher.cpp @@ -0,0 +1,60 @@ +#include "WebRTCPublisher.h" +#include "BoostLog.h" +#include "NetworkUtility.h" +#include +#include + +WebRTCPublisher::WebRTCPublisher(boost::asio::io_context &ioContext) : m_ioContext(ioContext) { + + m_configuration.iceServers.emplace_back("mystunserver.org:3478"); + m_peer = std::make_shared(m_configuration); + + m_peer->onLocalDescription([&, this](rtc::Description sdp) { + LOG(info) << "Send the SDP to the remote peer: " << std::string(sdp); + const auto serverIp = "172.16.7.16"; + const auto serverPort = "443"; + const auto streamUrl = "/live/livestream"; + std::ostringstream oss; + oss << "https://" << serverIp << ":" << serverPort << "/rtc/v1/publish/"; + + boost::json::object requestJson; + requestJson["api"] = oss.str(); + requestJson["streamurl"] = streamUrl; + requestJson["sdp"] = std::string(sdp); + auto request = boost::json::serialize(requestJson); + LOG(info) << "body: " << request; + boost::system::error_code error; + Http::Client https(m_ioContext, Http::SSL); + https.loadRootCertificates(error); + auto reply = https.post(serverIp, serverPort, "/rtc/v1/publish/", request, error); + LOG(info) << "reply " << reply; + + // Send the SDP to the remote peer + // MY_SEND_DESCRIPTION_TO_REMOTE(std::string(sdp)); + }); + + m_peer->onLocalCandidate([](rtc::Candidate candidate) { + // Send the candidate to the remote peer + // MY_SEND_CANDIDATE_TO_REMOTE(candidate.candidate(), candidate.mid()); + }); + + // MY_ON_RECV_DESCRIPTION_FROM_REMOTE([&pc](std::string sdp) { pc.setRemoteDescription(rtc::Description(sdp)); }); + + // MY_ON_RECV_CANDIDATE_FROM_REMOTE( + // [&pc](std::string candidate, std::string mid) { pc.addRemoteCandidate(rtc::Candidate(candidate, mid)); }); + + m_peer->onStateChange([](rtc::PeerConnection::State state) { LOG(info) << "State: " << (int)state; }); + + m_peer->onGatheringStateChange( + [](rtc::PeerConnection::GatheringState state) { LOG(info) << "Gathering state: " << (int)state; }); + + m_dataChannel = m_peer->createDataChannel("test"); + + m_dataChannel->onOpen([]() { LOG(info) << "Open" << std::endl; }); + + m_dataChannel->onMessage([](std::variant message) { + if (std::holds_alternative(message)) { + LOG(info) << "Received: " << get(message) << std::endl; + } + }); +} diff --git a/WebRTCPublisher.h b/WebRTCPublisher.h new file mode 100644 index 0000000..6c7515d --- /dev/null +++ b/WebRTCPublisher.h @@ -0,0 +1,18 @@ +#ifndef WEBRTCPUBLISHER_H +#define WEBRTCPUBLISHER_H + +#include "rtc/rtc.hpp" +#include + +class WebRTCPublisher { +public: + WebRTCPublisher(boost::asio::io_context &ioContext); + +private: + boost::asio::io_context &m_ioContext; + rtc::Configuration m_configuration; + std::shared_ptr m_peer; + std::shared_ptr m_dataChannel; +}; + +#endif // WEBRTCPUBLISHER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..69ff317 --- /dev/null +++ b/main.cpp @@ -0,0 +1,35 @@ +#include "AudioDecoder.h" +#include "Demuxer.h" +#include "IoContext.h" +#include "VideoDecoder.h" +#include "WebRTCPublisher.h" +#include +#include +#include + +int main(int argc, char *argv[]) { + QApplication app(argc, argv); + auto ioContext = Amass::Singleton::instance(); + std::cout << "hello world." << std::endl; + constexpr auto path = "E:/Documents/juren-30s.mp4"; + Demuxer demuxer; + demuxer.open(path); + + // auto [audioCodec, audioCodecParameter] = demuxer.codecInformation(Stream::Audio); + // auto [videoCodec, videoCodecParameter] = demuxer.codecInformation(Stream::Video); + // VideoDecoder videoDecoder(videoCodec, videoCodecParameter); + // // AudioDecoder audioDecoder(audioCodec, audioCodecParameter); + + // demuxer.setStreamPacketCallback( + // Stream::Video, [&videoDecoder](const std::shared_ptr &packet) { videoDecoder.push(packet); }); + // // demuxer.setStreamPacketCallback(Stream::Audio, + // // std::bind(&AudioDecoder::push, &audioDecoder, std::placeholders::_1)); + // demuxer.start(); + // videoDecoder.start(); + + QWidget w; + // w.show(); + WebRTCPublisher rtc(*ioContext->ioContext()); + ioContext->run(); + return app.exec(); +}