HLS Playback Optimizer with P2P(视频协议优化HLS篇)

Optimize HLS playback on web pages with P2P assistance

// ==UserScript==
// @name         HLS Playback Optimizer with P2P(视频协议优化HLS篇)
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  Optimize HLS playback on web pages with P2P assistance
// @author       KiwiFruit
// @match        *://*/*
// @grant        none
// @license MIT
// ==/UserScript==

/* global Hls, SimplePeer */

(function() {
    'use strict';

    function loadExternalScript(url, callback) {
        var script = document.createElement('script');
        script.src = url;
        script.onload = callback;
        document.head.appendChild(script);
    }

    loadExternalScript('https://unpkg.com/hls.js@latest/dist/hls.min.js', function() {
        console.log('hls.js loaded successfully.');
        // 在这里可以使用 hls 对象
    });

    loadExternalScript('https://unpkg.com/simple-peer@latest/dist/simple-peer.min.js', function() {
        console.log('simple-peer loaded successfully.');
        // 在这里可以使用 simplePeer 对象
    });
    function getSegmentData(frag) {
        const segmentUrl = frag.url;
        return fetch(segmentUrl)
            .then(response => response.arrayBuffer())
            .then(data => {
                // Convert ArrayBuffer to Blob or other data format as needed
                const blob = new Blob([data]);
                return blob;
            })
            .catch(error => {
                console.error('Failed to get segment data:', error);
                // Handle error, e.g., by skipping the segment
            });
    }

    function optimizeHLSPlaybackWithP2P(videoElement) {
        const hls = new Hls();
        hls.attachMedia(videoElement);
        hls.loadSource('http://example.com/path/to/your/playlist.m3u8');

        const peer = new SimplePeer({
            initiator: location.hash === '#init',
            trickle: false,
            wrtc: {}
        });

        let peers = [];

        peer.on('signal', (data) => {
            console.log('Signal:', data);
            if (location.hash !== '#init') {
                location.hash = JSON.stringify(data);
            } else {
                document.getElementById('join').onclick = () => {
                    peer.signal(JSON.parse(location.hash.substr(1)));
                };
            }
        });

        peer.on('connect', () => {
            console.log('Connected to peer');
            peers.push(peer);
        });

        peer.on('data', (data) => {
            console.log('Received data from peer:', data);
            // Handle the received segment and append it to the buffer
        });

        let bufferedSegments = [];
        let currentBufferLength = 0;

        hls.on(Hls.Events.BUFFER_APPENDING, (event, data) => {
            if (!bufferedSegments.includes(data.frag.sn)) {
                bufferedSegments.push(data.frag.sn);
                currentBufferLength += data.frag.duration;
            }
        });

        hls.on(Hls.Events.FRAG_BUFFERED, (event, data) => {
            while (currentBufferLength > 90) { // Adjust this threshold as needed
                const oldestSegment = bufferedSegments.shift();
                hls.remove(oldestSegment);
                currentBufferLength -= oldestSegment.duration;
            }
            const segmentData = getSegmentData(data.frag);
            peers.forEach(p => p.send(segmentData));
        });

        function preloadNextSegments() {
            const networkSpeed = hls.network.speed; // Hypothetical property, adjust according to your implementation
            const segmentsToPreload = Math.max(2, Math.floor(networkSpeed / 5));
            for (let i = 0; i < segmentsToPreload; i++) {
                hls.nextLoadLevel = i;
                hls.startLoad();
            }
        }

        setInterval(preloadNextSegments, 5000);
    }

    const videoElements = document.querySelectorAll('video');
    videoElements.forEach(videoElement => {
        if (videoElement.src.endsWith('.m3u8')) {
            optimizeHLSPlaybackWithP2P(videoElement);
        } else if (videoElement.querySelector('source[src$=".m3u8"]')) {
            optimizeHLSPlaybackWithP2P(videoElement.querySelector('source[src$=".m3u8"]').parentNode);
        }
    });
})();