Heim  >  Artikel  >  Backend-Entwicklung  >  PHP implementiert die Live-Streaming-Funktion

PHP implementiert die Live-Streaming-Funktion

WBOY
WBOYOriginal
2023-06-22 12:34:161199Durchsuche

Mit der kontinuierlichen Entwicklung des Internets sind Live-Übertragungen zu einem unverzichtbaren Bestandteil des täglichen Lebens der Menschen geworden, und die Live-Streaming-Funktion ist der Schlüssel zur Realisierung von Live-Übertragungen. Das Aufkommen von PHP hat ein leistungsstarkes Werkzeug für die Entwicklung von Webanwendungen hervorgebracht, und die Implementierung der Live-Streaming-Funktion bildet da keine Ausnahme. In diesem Artikel wird erläutert, wie Sie mit PHP die Live-Streaming-Funktion implementieren.

1. Verstehen Sie die Grundprinzipien des Live-Streamings

Bevor Sie sich mit der Implementierung der Live-Streaming-Funktion befassen, müssen Sie zunächst die Grundprinzipien des Live-Streamings verstehen. Unter Live-Streaming versteht man den Prozess des Hochladens von Videodaten auf den Server und deren Verteilung an andere Benutzer, wenn Benutzer Live-Übertragungen durchführen. Insbesondere enthält es zwei grundlegende Links: Live-Übertragungs-Upload und Live-Übertragungsverteilung.

Live-Upload bezieht sich auf die Verarbeitung und Speicherung von von Benutzern hochgeladenen Videodaten und deren Transkodierung in Echtzeit, um den Wiedergabeanforderungen verschiedener Endgeräte gerecht zu werden. Unter Live-Verbreitung versteht man die Übertragung verarbeiteter Videodaten an andere Benutzer, damit diese diese ansehen können. Normalerweise ist die Verteilung von Live-Übertragungen in zwei Links unterteilt: Serververteilung und P2P-Verteilung.

2. Verwenden Sie PHP, um das Hochladen von Live-Übertragungen zu implementieren.

In PHP gibt es viele Bibliotheken, die zum Implementieren des Hochladens von Live-Übertragungen verwendet werden können. Zu den häufig verwendeten Bibliotheken gehören FFmpeg, Libav und H264 Streaming Module usw. Diese Bibliotheken basieren alle auf der Sprache C/C++ und die Interoperabilität zwischen PHP und C/C++ kann durch die FFI-Erweiterung von PHP erreicht werden. Der spezifische Implementierungsprozess ist wie folgt:

1. Installieren Sie die FFI-Erweiterung

Um die FFI-Erweiterung in PHP zu verwenden, müssen Sie die Erweiterung zuerst installieren. Sie können die FFI-Erweiterung von der offiziellen Website von PHP herunterladen (https://www.php.net/manual/zh/ffi.installation.php) oder sie mit dem Paketmanager installieren. Fügen Sie nach Abschluss der Installation den folgenden Code zur Datei php.ini hinzu:

extension=ffi.so

2. Verwenden Sie FFmpeg, um die Videokodierung zu implementieren.

Mit FFmpeg müssen Sie die Videodaten in mehrere Frames aufteilen Führen Sie eine Codierungskonvertierung für jeden Frame durch. Der spezifische Implementierungsprozess ist wie folgt:

use FFI;

$ffi = FFI::cdef('
    typedef struct AVCodecParameters AVCodecParameters;
    typedef struct AVCodecContext AVCodecContext;
    typedef struct AVPacket AVPacket;
    typedef struct AVFrame AVFrame;
    typedef struct AVCodec AVCodec;
    typedef struct SwsContext SwsContext;

    AVCodec *avcodec_find_encoder(enum AVCodecID id);
    AVCodecContext *avcodec_alloc_context3(AVCodec *codec);
    int avcodec_parameters_to_context(AVCodecContext *codec_ctx, const AVCodecParameters *par);
    int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);
    int avcodec_send_frame(AVCodecContext *avctx, const AVFrame *frame);
    int avcodec_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);
    AVFrame *av_frame_alloc();
    void av_frame_free(AVFrame **frame);
    SwsContext* sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat, int dstW, int dstH, enum AVPixelFormat dstFormat, int flags, SwsFilter *srcFilter, SwsFilter *dstFilter, const double *param);
    const uint8_t **av_fifo_generic_read(AVFifoBuffer *f, void *dest, int buf_size, void *(*func)(void *, const void *, size_t), void *dest_ctx);
    int av_fifo_size(const AVFifoBuffer *f);
    void av_fifo_reset(AVFifoBuffer *f);
    void av_fifo_free(AVFifoBuffer *f);
    int sws_scale(SwsContext *c, const uint8_t *const srcSlice[], const int srcStride[], int srcSliceY, int srcSliceH, uint8_t *const dst[], const int dstStride[]);
    #define AV_CODEC_ID_H264 AV_CODEC_ID_MPEG4
    #define AV_PIX_FMT_YUV420P AV_PIX_FMT_YUVJ420P
', 'libavcodec.so.58.54.100, libavutil.so.56.55.100, libavformat.so.58.29.100, libswscale.so.5.5.100');

$codec = $ffi->avcodec_find_encoder(AV_CODEC_ID_H264);
$codec_ctx = $ffi->avcodec_alloc_context3($codec);
$options = null;
$width = 640;
$height = 480;
$frame_rate = 25;
$bit_rate = 400000;
$codec_ctx->width = $width;
$codec_ctx->height = $height;
$codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
$codec_ctx->bit_rate = $bit_rate;
$codec_ctx->time_base = FFI::new('AVRational');
$codec_ctx->time_base->num = 1;
$codec_ctx->time_base->den = $frame_rate;
$codec_ctx->gop_size = $frame_rate * 2;
$codec_ctx->max_b_frames = 1;
$codec_ctx->rc_buffer_size = $bit_rate;
$codec_ctx->rc_max_rate = $bit_rate;
$codec_ctx->rc_min_rate = $bit_rate;
$codec_ctx->codec_tag = $codec->id;
$codec_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

if ($codec->id == AV_CODEC_ID_H264) {
    $codec_ctx->profile = FF_PROFILE_H264_BASELINE;
    $codec_ctx->level = 30;
}

if ($ffi->avcodec_open2($codec_ctx, $codec, $options) < 0) {
    throw new Exception('Cannot init encoder');
}

$codec_pkt = $ffi->new('AVPacket[1]');
$frame = $ffi->av_frame_alloc();
$frame->format = AV_PIX_FMT_YUV420P;
$frame->width = $width;
$frame->height = $height;
$ffi->av_frame_get_buffer($frame, 32);

$sws = $ffi->sws_getContext(
    $width, $height, AV_PIX_FMT_RGB24,
    $width, $height, AV_PIX_FMT_YUV420P,
    SWS_FAST_BILINEAR, null, null, null);

$buffer = $ffi->malloc($width * $height * 3);
$i = 0;
while ($i < 120) {
    $i++;
    //$buffer = 获取一帧RGB24像素数据
    $image = 'data://' . base64_encode($buffer);
    $img = imagecreatefromstring(file_get_contents($image));
    $rgb = imagecreatetruecolor($width, $height);
    imagecopyresampled($rgb, $img, 0, 0, 0, 0, $width, $height, $width, $height);
    $rgb_buffer = $ffi->new('uint8_t[?]', $width * $height * 3);
    $p = $ffi->cast('char *', $ffi->addr($rgb_buffer));
    $linesize = $ffi->new('int[3]', [$width * 3, 0, 0]);
    $ffi->av_image_fill_arrays($frame->data, $frame->linesize, $p, AV_PIX_FMT_RGB24, $width, $height, 1);
    $ffi->sws_scale($sws, [$p], $linesize, 0, $height, $frame->data, $frame->linesize);
    $frame->pts = $i;
    $ret = $ffi->avcodec_send_frame($codec_ctx, $frame);
    if ($ret < 0) {
        throw new Exception('Cannot encode video frame');
    }
    while ($ret >= 0) {
        $ret = $ffi->avcodec_receive_packet($codec_ctx, $codec_pkt);
        if ($ret < 0) {
            break;
        }
        //将$codec_pkt->data中的视频数据作为流推送到服务器,代码略
        $ffi->av_packet_unref($codec_pkt);
    }   
}

Der obige Code verwendet FFmpeg, um die erhaltenen RGB24-Pixeldaten zu kodieren und zu konvertieren und die konvertierten Videodaten an den Server zu übertragen. Wenn Sie einen anderen Encoder verwenden möchten, ersetzen Sie einfach AV_CODEC_ID_H264 im obigen Code.

3. Verwenden Sie PHP, um die Verteilung von Live-Übertragungen zu implementieren.

Es gibt viele Möglichkeiten, die Verteilung von Live-Übertragungen in PHP zu implementieren, einschließlich Serververteilung und P2P-Verteilung. Unter Serververteilung versteht man das Streamen der verarbeiteten Videodaten an den Server, und der Zwischenserver kann das Video transkodieren und verteilen. Unter P2P-Verteilung versteht man die direkte Verteilung von Videodaten an andere Benutzer über das UDP-Protokoll.

Das Folgende ist der Code zum Implementieren der Serververteilung über das Ratchet-Framework:

use RatchetServerIoServer;
use RatchetWebSocketWsServer;
use AppVideoServer;

require dirname(__DIR__) . '/vendor/autoload.php';

$server = IoServer::factory(new WsServer(new VideoServer()), 8080);
$server->run();
use RatchetConnectionInterface;
use RatchetMessageComponentInterface;
use ReactEventLoopFactory;
use ReactEventLoopLoopInterface;
use ReactStreamReadableResourceStream;

class VideoServer implements MessageComponentInterface {
    protected $clients;
    /** @var LoopInterface $loop */
    protected $loop;
    protected $sourceUrl;
    protected $sourceRenderer;

    public function __construct() {
        $this->clients = new SplObjectStorage();
        $this->loop = Factory::create();
        $this->sourceUrl = '';
        $this->sourceRenderer = null;
    }

    public function onOpen(ConnectionInterface $conn) {
        $this->clients->attach($conn);
        $conn->on('close', function() use ($conn) {
            $this->clients->detach($conn);
            if ($this->clients->count() === 0 && $this->sourceRenderer !== null) {
                $this->sourceRenderer->close();
                $this->sourceRenderer = null;
            }
        });

        if ($this->sourceRenderer === null) {
            $this->loop->futureTick(function() use ($conn) {
                $conn->send('Waiting for source video stream...');
            });
            return;
        }

        $resource = new ReadableResourceStream($this->sourceRenderer->stdout, $this->loop);
        $resource->on('data', function ($chunk) use ($conn) {
            foreach ($this->clients as $client) {
                if ($client !== $conn) {
                    $client->send($chunk);
                }
            }
        });
    }
    
    public function onMessage(ConnectionInterface $from, $msg) {
        if ($this->sourceRenderer === null) {
            $this->sourceUrl = trim($msg);
            $this->sourceRenderer = new ReactChildProcessProcess('ffmpeg -i ' . escapeshellarg($this->sourceUrl) . ' -c:v libx264 -preset superfast -tune zerolatency -b:v 400k -pix_fmt yuv420p -r 30 -f mpegts -');
            $this->sourceRenderer->start($this->loop);
        }
    }

    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
        if ($this->clients->count() === 0 && $this->sourceRenderer !== null) {
            $this->sourceRenderer->close();
            $this->sourceRenderer = null;
        }
    }

    public function onError(ConnectionInterface $conn, Exception $e) {}
}

Der obige Code implementiert den WebSocket-Server über das Ratchet-Framework. Wenn ein Benutzer eine Verbindung zum Server herstellt, startet der Server einen Unterprozess, um FFmpeg zur Verarbeitung des Videostreams auszuführen, und leitet den verarbeiteten Videostream über WebSocket an den Benutzer weiter. Wenn mehrere Benutzer mit dem Server verbunden sind, verteilt der Server den verarbeiteten Videostream in Echtzeit an jeden Benutzer.

4. Zusammenfassung

In diesem Artikel wird erläutert, wie Sie PHP zur Implementierung der Live-Streaming-Funktion verwenden. Zuerst werden die Videodaten durch Aufrufen von FFmpeg codiert, und dann werden die codierten Videodaten über den Stream an den Server übertragen. Schließlich wird der Server über das Ratchet-Framework verteilt und der verarbeitete Videostream wird in Echtzeit an den Benutzer verteilt . Diese Technologien sind für die Erzielung effizienter Live-Streaming-Funktionen von entscheidender Bedeutung und können Entwicklern dabei helfen, die Entwicklung von Live-Streaming-Anwendungen problemlos abzuschließen.

Das obige ist der detaillierte Inhalt vonPHP implementiert die Live-Streaming-Funktion. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn