webrtc浏览器端直播推流示例代码

代码语言:html

所属分类:多媒体

代码描述:webrtc浏览器端直播推流示例代码,直播服务器可以安装mediamtx,地址https://github.com/bluenviron/mediamtx

代码标签: webrtc 浏览器 直播 推流 示例 代码

下面为部分代码预览,完整代码请点击下载或在bfwstudio webide中打开

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<style>
html, body {
	margin: 0;
	padding: 0;
	height: 100%;
	overflow: hidden;
}
body {
    display: flex;
    flex-direction: column;
}
#video {
	height: 100%;
	background: black;
    flex-grow: 1;
    min-height: 0;
}
#controls {
    height: 200px;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
#device {
    flex-direction: column;
}
#device > div {
    margin: 10px 0;
    display: flex;
    gap: 20px;
    justify-content: center;
}
select {
    width: 200px;
}
</style>
</head>
<body>

<video id="video" muted controls autoplay playsinline></video>
<div id="controls">
    <div id="initializing" style="display: block;">
        initializing
    </div>
    <div id="device" style="display: none;">
        <div id="device_line">
            video device:
            <select id="video_device">
                <option value="none">none</option>
            </select>

            audio device:
            <select id="audio_device">
                <option value="none">none</option>
            </select>
        </div>
        <div id="codec_line">
            video codec:
            <select id="video_codec">
            </select>

            audio codec:
            <select id="audio_codec">
            </select>
        </div>
        <div id="bitrate_line">
            video bitrate (kbps):
            <input id="video_bitrate" type="text" value="10000" />

            audio bitrate (kbps):
            <input id="audio_bitrate" type="text" value="32" />

            <div>
                <input id="audio_voice" type="checkbox" checked>
                <label for="audio_voice">optimize for voice</label>
            </div>
        </div>
        <div id="submit_line">
            <button id="publish_confirm">publish</button>
        </div>
    </div>
    <div id="transmitting" style="display: none;">
        publishing
    </div>
    <div id="error" style="display: none;">
        <span id="error-message"></span>
    </div>
</div>

<script>

const INITIALIZING = 0;
const DEVICE = 1;
const TRANSMITTING = 2;
const ERROR = 3;

let state = INITIALIZING;
let errorMessage = '';

const render = () => {
    for (const el of ['initializing', 'device', 'transmitting', 'error']) {
        document.getElementById(el).style.display = 'none';
    }

    switch (state) {
    case DEVICE:
        document.getElementById('device').style.display = 'flex';
        break;

    case TRANSMITTING:
        document.getElementById('transmitting').style.display = 'flex';
        break;

    case ERROR:
        document.getElementById('error').style.display = 'flex';
        document.getElementById('error-message').innerHTML = 'error: ' + errorMessage;
        break;
    }
};

const restartPause = 2000;

const unquoteCredential = (v) => (
    JSON.parse(`"${v}"`)
);

const linkToIceServers = (links) => (
    (links !== null) ? links.split(', ').map((link) => {
        const m = link.match(/^<(.+?)>; rel="ice-server"(; username="(.*?)"; credential="(.*?)"; credential-type="password")?/i);
        const ret = {
            urls: [m[1]],
        };

        if (m[3] !== undefined) {
            ret.username = unquoteCredential(m[3]);
            ret.credential = unquoteCredential(m[4]);
            ret.credentialType = "password";
        }

        return ret;
    }) : []
);

const parseOffer = (offer) => {
    const ret = {
        iceUfrag: '',
        icePwd: '',
        medias: [],
    };

    for (const line of offer.split('\r\n')) {
        if (line.startsWith('m=')) {
            ret.medias.push(line.slice('m='.length));
        } else if (ret.iceUfrag === '' && line.startsWith('a=ice-ufrag:')) {
            ret.iceUfrag = line.slice('a=ice-ufrag:'.length);
        } else if (ret.icePwd === '' && line.startsWith('a=ice-pwd:')) {
            ret.icePwd = line.slice('a=ice-pwd:'.length);
        }
    }

    return ret;
};

const generateSdpFragment = (offerData, candidates) => {
    const candidatesByMedia = {};
    for (const candidate of candidates) {
        const mid = candidate.sdpMLineIndex;
        if (candidatesByMedia[mid] === undefined) {
            candidatesByMedia[mid] = [];
        }
        candidatesByMedia[mid].push(candidate);
    }

    let frag = 'a=ice-ufrag:' + offerData.iceUfrag + '\r\n'
        + 'a=ice-pwd:' + offerData.icePwd + '\r\n';

    let mid = 0;

    for (const media of offerData.medias) {
        if (candidatesByMedia[mid] !== undefined) {
            frag += 'm=' + media + '\r\n'
                + 'a=mid:' + mid + '\r\n';

            for (const candidate of candidatesByMedia[mid]) {
                frag += 'a=' + candidate.candidate + '\r\n';
            }
        }
        mid++;
    }

    return frag;
};

const setCodec = (section, codec) => {
    const lines = section.split('\r\n');
    const lines2 = [];
    const payloadFormats = [];

    for (const line of lines) {
        if (!line.startsWith('a=rtpmap:')) {
            lines2.push(line);
        } else {
            if (line.toLowerCase().includes(codec)) {
                payloadFormats.push(line.slice('a=rtpmap:'.length).split(' ')[0]);
                lines2.push(line);
            }
        }
    }

    const lines3 = [];

    for (const line of lines2) {
        if (line.startsWith('a=fmtp:')) {
            if (payloadFormat.........完整代码请登录后点击上方下载按钮下载查看

网友评论0