From: Michael Riedl <michael.riedl@nativewaves.com> To: ffmpeg-devel@ffmpeg.org Subject: [FFmpeg-devel] [PATCH 5/6] libavformat/webrtc_demux: add WebRTC-HTTP egress protocol (WHEP) demuxer Date: Mon, 6 Nov 2023 16:19:33 +0100 Message-ID: <2a21d6de-578c-4426-bbd5-a6f86515d976@nativewaves.com> (raw) Signed-off-by: Michael Riedl <michael.riedl@nativewaves.com> --- configure | 2 + libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/webrtc_demux.c | 231 +++++++++++++++++++++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 libavformat/webrtc_demux.c diff --git a/configure b/configure index 187f16b425d..02c6f7f2c5d 100755 --- a/configure +++ b/configure @@ -3557,6 +3557,8 @@ wav_demuxer_select="riffdec" wav_muxer_select="riffenc" webm_chunk_muxer_select="webm_muxer" webm_dash_manifest_demuxer_select="matroska_demuxer" +whep_demuxer_deps="libdatachannel sdp_demuxer" +whep_demuxer_select="http_protocol" wtv_demuxer_select="mpegts_demuxer riffdec" wtv_muxer_select="mpegts_muxer riffenc" xmv_demuxer_select="riffdec" diff --git a/libavformat/Makefile b/libavformat/Makefile index 329055ccfd9..f790fa8cae4 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -621,6 +621,7 @@ OBJS-$(CONFIG_WEBM_CHUNK_MUXER) += webm_chunk.o OBJS-$(CONFIG_WEBP_MUXER) += webpenc.o OBJS-$(CONFIG_WEBVTT_DEMUXER) += webvttdec.o subtitles.o OBJS-$(CONFIG_WEBVTT_MUXER) += webvttenc.o +OBJS-$(CONFIG_WHEP_DEMUXER) += webrtc.o webrtc_demux.o OBJS-$(CONFIG_WSAUD_DEMUXER) += westwood_aud.o OBJS-$(CONFIG_WSAUD_MUXER) += westwood_audenc.o OBJS-$(CONFIG_WSD_DEMUXER) += wsddec.o rawdec.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index d4b505a5a32..7acb05634c8 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -504,6 +504,7 @@ extern const FFOutputFormat ff_webm_chunk_muxer; extern const FFOutputFormat ff_webp_muxer; extern const AVInputFormat ff_webvtt_demuxer; extern const FFOutputFormat ff_webvtt_muxer; +extern const AVInputFormat ff_whep_demuxer; extern const AVInputFormat ff_wsaud_demuxer; extern const FFOutputFormat ff_wsaud_muxer; extern const AVInputFormat ff_wsd_demuxer; diff --git a/libavformat/webrtc_demux.c b/libavformat/webrtc_demux.c new file mode 100644 index 00000000000..7ef4e3d3ce3 --- /dev/null +++ b/libavformat/webrtc_demux.c @@ -0,0 +1,231 @@ +/* + * WebRTC-HTTP egress protocol (WHEP) demuxer using libdatachannel + * + * Copyright (C) 2023 NativeWaves GmbH <contact@nativewaves.com> + * This work is supported by FFG project 47168763. + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "internal.h" +#include "libavutil/avstring.h" +#include "libavutil/random_seed.h" +#include "version.h" +#include "rtsp.h" +#include "webrtc.h" + +typedef struct WHEPContext { + const AVClass *av_class; + DataChannelContext data_channel; +} WHEPContext; + +static int whep_read_header(AVFormatContext* avctx) +{ + WHEPContext*const ctx = (WHEPContext*const)avctx->priv_data; + int ret, i; + char media_stream_id[37] = { 0 }; + rtcTrackInit track_init; + AVDictionary* options = NULL; + const AVInputFormat* infmt; + AVStream* stream; + FFIOContext sdp_pb; + + webrtc_init_logger(); + ret = webrtc_init_connection(&ctx->data_channel); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to initialize connection\n"); + goto fail; + } + + /* configure audio and video track */ + ret = webrtc_generate_media_stream_id(media_stream_id); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to generate media stream id\n"); + goto fail; + } + ctx->data_channel.tracks = av_mallocz(2 * sizeof(DataChannelTrack)); + ctx->data_channel.nb_tracks = 2; + ctx->data_channel.avctx = avctx; + if (!ctx->data_channel.tracks) { + ret = AVERROR(ENOMEM); + goto fail; + } + for (i=0; i < ctx->data_channel.nb_tracks; i++) { + ctx->data_channel.tracks[i].avctx = avctx; + } + + /* configure video track */ + memset(&track_init, 0, sizeof(rtcTrackInit)); + track_init.direction = RTC_DIRECTION_RECVONLY; + track_init.codec = RTC_CODEC_H264; // TODO: support more codecs once libdatachannel C api supports them + track_init.payloadType = 96; + track_init.ssrc = av_get_random_seed(); + track_init.mid = "0"; + track_init.name = LIBAVFORMAT_IDENT; + track_init.msid = media_stream_id; + track_init.trackId = av_asprintf("%s-video", media_stream_id); + track_init.profile = "profile-level-id=42e01f;packetization-mode=1;level-asymmetry-allowed=1"; + + ctx->data_channel.tracks[0].track_id = rtcAddTrackEx(ctx->data_channel.peer_connection, &track_init); + if (!ctx->data_channel.tracks[0].track_id) { + av_log(avctx, AV_LOG_ERROR, "Failed to add track\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + /* configure audio track */ + memset(&track_init, 0, sizeof(rtcTrackInit)); + track_init.direction = RTC_DIRECTION_RECVONLY; + track_init.codec = RTC_CODEC_OPUS; // TODO: support more codecs once libdatachannel C api supports them + track_init.payloadType = 97; + track_init.ssrc = av_get_random_seed(); + track_init.mid = "1"; + track_init.name = LIBAVFORMAT_IDENT; + track_init.msid = media_stream_id; + track_init.trackId = av_asprintf("%s-audio", media_stream_id); + track_init.profile = "minptime=10;maxaveragebitrate=96000;stereo=1;sprop-stereo=1;useinbandfec=1"; + + ctx->data_channel.tracks[1].track_id = rtcAddTrackEx(ctx->data_channel.peer_connection, &track_init); + if (!ctx->data_channel.tracks[1].track_id) { + av_log(avctx, AV_LOG_ERROR, "Failed to add track\n"); + ret = AVERROR_EXTERNAL; + goto fail; + } + + /* create resource */ + ret = webrtc_create_resource(&ctx->data_channel); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "webrtc_create_resource failed\n"); + goto fail; + } + + /* initialize SDP muxer per track */ + for (int i = 0; i < ctx->data_channel.nb_tracks; i++) { + char sdp_track[SDP_MAX_SIZE] = { 0 }; + ret = rtcGetTrackDescription(ctx->data_channel.tracks[i].track_id, sdp_track, sizeof(sdp_track)); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "rtcGetTrackDescription failed\n"); + goto fail; + } + + ffio_init_read_context(&sdp_pb, (uint8_t*)sdp_track, strlen(sdp_track)); + + infmt = av_find_input_format("sdp"); + if (!infmt) + goto fail; + ctx->data_channel.tracks[i].rtp_ctx = avformat_alloc_context(); + if (!ctx->data_channel.tracks[i].rtp_ctx) { + ret = AVERROR(ENOMEM); + goto fail; + } + ctx->data_channel.tracks[i].rtp_ctx->max_delay = avctx->max_delay; + ctx->data_channel.tracks[i].rtp_ctx->pb = &sdp_pb.pub; + ctx->data_channel.tracks[i].rtp_ctx->interrupt_callback = avctx->interrupt_callback; + + if ((ret = ff_copy_whiteblacklists(ctx->data_channel.tracks[i].rtp_ctx, avctx)) < 0) + goto fail; + + av_dict_set(&options, "sdp_flags", "custom_io", 0); + + ret = avformat_open_input(&ctx->data_channel.tracks[i].rtp_ctx, "temp.sdp", infmt, &options); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "avformat_open_input failed\n"); + goto fail; + } + + ret = webrtc_init_urlcontext(&ctx->data_channel, i); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "webrtc_init_urlcontext failed\n"); + goto fail; + } + ret = ffio_fdopen(&ctx->data_channel.tracks[i].rtp_ctx->pb, ctx->data_channel.tracks[i].rtp_url_context); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "ffio_fdopen failed\n"); + goto fail; + } + + /* copy codec parameters */ + stream = avformat_new_stream(avctx, NULL); + if (!stream) { + ret = AVERROR(ENOMEM); + goto fail; + } + + ret = avcodec_parameters_copy(stream->codecpar, ctx->data_channel.tracks[i].rtp_ctx->streams[0]->codecpar); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "avcodec_parameters_copy failed\n"); + goto fail; + } + stream->time_base = ctx->data_channel.tracks[i].rtp_ctx->streams[0]->time_base; + } + + return 0; + +fail: + webrtc_deinit(&ctx->data_channel); + return ret; +} + +static int whep_read_close(AVFormatContext* avctx) +{ + WHEPContext*const ctx = (WHEPContext*const)avctx->priv_data; + int ret = 0; + + /* close resource */ + ret = webrtc_close_resource(&ctx->data_channel); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "webrtc_close_resource failed\n"); + } + + webrtc_deinit(&ctx->data_channel); + + return ret; +} + +static int whep_read_packet(AVFormatContext* avctx, AVPacket* pkt) +{ + const WHEPContext*const s = (const WHEPContext*const)avctx->priv_data; + const DataChannelTrack*const track = &s->data_channel.tracks[pkt->stream_index]; + pkt->stream_index = 0; + return av_read_frame(track->rtp_ctx, pkt); +} + + +#define OFFSET(x) offsetof(WHEPContext, x) +#define DEC AV_OPT_FLAG_DECODING_PARAM +static const AVOption options[] = { + WEBRTC_OPTIONS(DEC, OFFSET(data_channel)), + { NULL }, +}; + +static const AVClass whep_demuxer_class = { + .class_name = "WHEP demuxer", + .item_name = av_default_item_name, + .option = options, + .version = LIBAVUTIL_VERSION_INT, +}; + +const AVInputFormat ff_whep_demuxer = { + .name = "whep", + .long_name = NULL_IF_CONFIG_SMALL("WebRTC-HTTP egress protocol (WHEP) demuxer"), + .flags = AVFMT_NOFILE | AVFMT_EXPERIMENTAL, + .priv_class = &whep_demuxer_class, + .priv_data_size = sizeof(WHEPContext), + .read_header = whep_read_header, + .read_packet = whep_read_packet, + .read_close = whep_read_close, +}; -- 2.39.2 _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
reply other threads:[~2023-11-06 15:20 UTC|newest] Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=2a21d6de-578c-4426-bbd5-a6f86515d976@nativewaves.com \ --to=michael.riedl@nativewaves.com \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git