Search by

xiaosongshu / flv2mp4

2723659854

Pure PHP native media processing library, realize FLV/MP4/HLS mutual conversion , H.264 re-encoding and opus/mp3/aac/wav re-encoding. Built-in live stream relay gateway, file server, multi-protocol push client: RTMP, HTTP-FLV, WebSocket-FLV, no FFmpeg external dependencies.

Package info

github.com/2723659854/flv2mp4

pkg:composer/xiaosongshu/flv2mp4

Statistics

Installs: 120

Dependents: 1

Suggesters: 0

Stars: 1

Open Issues: 0

v1.6.5 2026-09-15 10:16 UTC

README

🇨🇳 中文🇬🇧 English

Introduction

A lightweight pure PHP 8.1+ media processing toolkit with zero external dependencies (no FFmpeg required).
Supports FLV, FMP4, MP4, HLS mutual conversion, live streaming gateway, pushing, pulling, rebroadcasting, as well as H.264 decoding + scaling + re-encoding (Baseline Profile) and Full-format Interconversion of AAC/MP3/Opus/WAV.

📋 Table of Contents

🎯 Core Features

Feature Direction Description
Container conversion FLV ↔ MP4 / FMP4 Generate standard MP4 or fragmented fMP4 (MSE compatible)
HLS slicing FLV → HLS Generate M3U8 + TS segments, compatible with hls.js, VLC, etc.
HLS restoration HLS → FLV Merge HLS segments back into a single FLV file
MP4 ↔ FLV MP4 → FLV / FMP4 → FLV Multi-container interconversion
Live gateway FLV gateway High-performance multi-level forwarding, supports high concurrency
Static file server HTTP file gateway Lightweight file server with directory browsing support
Pushing client FLV / MP4 → RTMP/HTTP-FLV/WS-FLV Push static files as a pseudo-live stream
Pulling client RTMP/HTTP-FLV/WS-FLV → FLV Pull live stream and save as local FLV
Rebroadcasting Multi-protocol input → Multi-protocol output One pull, multiple forwards
H.264 re-encoding Decode → Scale → Encode Baseline Profile, provides core support for multi-bitrate HLS
OPUS→AAC opus→pcm→aac Convert WebRTC Opus audio to AAC-LC
AAC→MP3 aac→pcm→mp3 Convert AAC-LC audio to MP3
opus/aac/mp3/wav format conversion Source audio → PCM → target format audio Supports mutual conversion among Opus/AAC/MP3/WAV audio formats

Environment Requirements

Dependency Description
PHP ≥ 8.1 (CLI mode only)
sockets ext Optional, provides low-level Socket communication. Only required for live streaming and H.264 re-encoding scenarios.
gd ext Optional, used to generate watermarks from PNG/JPG images. If not installed, it automatically falls back to the built-in bitmap font mode.
  • 💡 Only supported in PHP CLI mode. Not supported under Nginx/FPM or web server environments.
  • 💡 No FFmpeg, no third-party binaries required — fully implemented in pure PHP.
  • 💡 Container remuxing (FLV/MP4/HLS conversion) only changes the container format and is very fast.
  • ⚠️ H.264 re-encoding requires proc_open to be enabled. (This module uses multi-process distributed computing and spawns child processes to process frames in parallel.)
  • ⚠️ Opus real-time transcoding (live streaming only) requires proc_open to be enabled. (In real-time scenarios such as WebRTC to RTMP, it launches a standalone background Worker process for audio transcoding. Static file Opus transcoding does not require this function.)
  • ⚠️ H.264 re-encoding is a CPU-intensive task. Performance depends on server configuration. It is not recommended for live real-time transcoding. Enabling JIT acceleration is highly recommended.

🚀 Installation

composer require xiaosongshu/flv2mp4

📚 Quick Start

<?php

declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';

ini_set('memory_limit', '512M');

$file = __DIR__ . '/test.flv';

// 1. FLV → fragmented fMP4 (merged)
\Xiaosongshu\Flv2mp4\Client::runFlv2Fmp4Mixed($file, __DIR__ . '/output_merge');

// 2. FLV → fragmented fMP4 (separate audio/video tracks)
\Xiaosongshu\Flv2mp4\Client::runFlv2Fmp4Separate($file, __DIR__ . '/output_separate');

// 3. FLV → HLS
\Xiaosongshu\Flv2mp4\Client::runFlv2Hls($file, __DIR__ . '/hls');

// 4. HLS → FLV
\Xiaosongshu\Flv2mp4\Client::runHls2Flv(__DIR__ . '/hls/index.m3u8', __DIR__ . '/output.flv');

// 5. MP4 → FLV
\Xiaosongshu\Flv2mp4\Client::runMp42Flv(__DIR__ . '/test.mp4', __DIR__ . '/output.flv');

// 6. FLV → MP4
\Xiaosongshu\Flv2mp4\Client::runFlv2Mp4($file, __DIR__ . '/output.mp4');

// 7. fMP4 → FLV (supports both merged and separate formats)
\Xiaosongshu\Flv2mp4\Client::runFmp42Flv(__DIR__ . '/output_merge/index.m3u8', __DIR__ . '/output.flv');

// 8. MP4 → HLS
\Xiaosongshu\Flv2mp4\Client::runMp42Hls(__DIR__ . "/demo.mp4", __DIR__ . "/mp4_hls");

// 9. HLS → MP4
\Xiaosongshu\Flv2mp4\Client::runHls2Mp4( __DIR__ .'/mp4_hls/demo/index.m3u8', __DIR__.'/hls_2_mp4.mp4');

// 10. MP4 → fMP4
\Xiaosongshu\Flv2mp4\Client::runMp42Fmp4(__DIR__.'/demo.mp4', __DIR__.'/mp4_2_fmp4');

// 11. fMP4 → MP4
\Xiaosongshu\Flv2mp4\Client::runFmp42Mp4(__DIR__.'/mp4_2_fmp4/index.m3u8',__DIR__.'/1234567.mp4');

🌐 Advanced Features

Opus 2 AAC

WebRtcFlvRelay receives WebRTC RTP data, wraps H.264 video into FLV, transcodes Opus audio to AAC‑LC via a pure PHP Worker, and pushes it to a WebSocket‑FLV service for recording or forwarding to RTMP.

<?php

require_once __DIR__ . '/vendor/autoload.php';

use Xiaosongshu\Flv2mp4\Flv\WebRtcFlvRelay;
use Xiaosongshu\Flv2mp4\Opus\OpusWorkerClient;

$clientId = 1;
$streamId = 'stream_001';
$opusWorkerPort = 8330;
$pushUrl = "ws://127.0.0.1:8501/live/{$streamId}";

$relay = new WebRtcFlvRelay(
    $clientId,
    $streamId,
    $pushUrl,
    null,
    null,
    $opusWorkerPort
);
$relay->connect();

// Call these in your WebRTC server's RTP callback:
// $relay->pushRtp($plainRtp, 'video');
// $relay->pushRtp($plainRtp, 'audio');

// Close relay when done; shut down automatically started Workers on process exit.
$relay->finish();
OpusWorkerClient::shutdownOwnedWorkers();

A complete example is available at examples/webrtc.php. Common configuration:

// Each project instance must use a different Worker port.
$opusWorkerPort = 8330;

// Supports RTMP, HTTP‑FLV, and WebSocket‑FLV push URLs.
// The example uses WebSocket‑FLV and replaces placeholder with streamId.
$wsFlvPushUrl = 'ws://127.0.0.1:8501/live/{streamId}';

Run the example:

php webrtc.php

Notes:

  • The relay automatically starts bin/opus-worker.php if no Worker is listening on the port – no manual startup needed.
  • Worker listens only on 127.0.0.1, default port 8330.
  • Auto‑start passes the host project's real vendor/autoload.php via --autoload, working with both local development and Composer‑installed setups.
  • Default output: 48kHz, mono, 64kbps AAC‑LC.
  • One Worker process can manage multiple independent connections, but real‑time transcoding is CPU‑heavy; plan for one live stream per instance.
  • Different project instances on the same machine must use different $opusWorkerPort.
  • On Ctrl+C or process exit, call OpusWorkerClient::shutdownOwnedWorkers() – the example already handles this.
  • PHP must allow proc_open for automatic Worker creation.
  • The Worker queue has bounded back‑pressure; do not simply enlarge the queue to solve performance issues, as it may increase latency and cause A/V desync.
  • WebRTC service requires the xiaosongshu/webrtc package.

FLV Live Gateway

Supports multi‑level proxy deployment for high‑concurrency live stream forwarding. Create flvGateway.php:

<?php
require_once __DIR__ . '/vendor/autoload.php';
$gateway = new \Xiaosongshu\Flv2mp4\Manage\FlvGateway(8080, 'http://127.0.0.1:8501');
$gateway->debug = true;
$gateway->start();

Run:

php flvGateway.php

Static File Gateway

Lightweight HTTP file server with directory browsing toggle. Create fileGateway.php:

<?php
require_once __DIR__ . '/vendor/autoload.php';
$server = new \Xiaosongshu\Flv2mp4\Manage\FileGateway( '0.0.0.0',8100,__DIR__,false);
$server->debug = true;
$server->start();

Run:

php fileGateway.php

Pushing Client

Supports HTTP‑FLV, WS‑FLV, RTMP, with speed control and auto‑reconnect. Create pusher.php:

require_once __DIR__ . '/vendor/autoload.php';
ini_set('memory_limit', '2048M');
$pusher = new \Xiaosongshu\Flv2mp4\Manage\PusherManage(__DIR__."/test.flv", "http://127.0.0.1:8501/live/stream", 1.0, false);
$pusher->start();

Run:

php pusher.php

Pulling Client

Pulls a live stream and saves it as a local FLV file. Create puller.php:

require_once __DIR__ . '/vendor/autoload.php';
ini_set('memory_limit', '2048M');
$puller = new \Xiaosongshu\Flv2mp4\Manage\PullerManage("ws://127.0.0.1:8501/live/stream.flv", __DIR__."/pull_record.flv", 0, false);
$puller->start();

Run:

php puller.php

Rebroadcasting (Forwarding)

Pulls one stream and forwards it to multiple destinations (mixed protocols supported). Create forward.php:

require_once __DIR__ . '/vendor/autoload.php';
ini_set('memory_limit', '2048M');
$forwarder = new \Xiaosongshu\Flv2mp4\Flv\FlvForwardClient("http://127.0.0.1:8501/a/b.flv", ["rtmp://127.0.0.1:1935/c/d","ws://127.0.0.1:8501/c/e"], 0, true);
$forwarder->start();

Run:

php forward.php

🧪 Testing & Playback

Output format Recommended player Sample file
MP4 HTML5 <video> index.html
fMP4 MSE player play_merge.html, mse.html
HLS (TS) hls.js / Safari play.html
FLV flv.js flv.html
FLV (push test) Web push test push.html

🎯 Use Cases

  • Live recording: Save RTMP/FLV streams as fMP4 / HLS in real time.
  • Video playback: On‑demand playback of recorded streams.
  • Stream forwarding: Multi‑level gateways for load balancing and edge acceleration.
  • Offline batch processing: Bulk FLV / MP4 conversion.
  • Pseudo‑live streaming: Push on‑demand files as live streams.
  • Cross‑platform rebroadcasting: One pull, multiple pushes to different platforms.
  • Multi‑bitrate HLS: Pure PHP H.264 re‑encoding to generate adaptive‑bitrate HLS.

🔥 H.264 Decoding + Scaling + Re-encoding

Supports Baseline Profile H.264 decoding, scaling, and re‑encoding, enabling the following capabilities:

Use case Description
Multi‑bitrate HLS Convert a single FLV into multiple resolution HLS streams (adaptive bitrate)
FLV re‑encoding Change resolution/bitrate and output as FLV
MP4 re‑encoding Change resolution/bitrate and output as MP4
Format conversion Re‑encode during FLV ↔ MP4 conversion (not just remuxing)
Watermark overlay Decode YUV → overlay PNG/text watermark → re‑encode output
Image enhancement Apply filters (sharpen, denoise) after decoding → re‑encode
Resolution adaptation Downsample high‑resolution video to multiple output resolutions
Bitrate control Transcode high‑bitrate videos to target bitrate

This is a complete H.264 pixel processing pipeline (decode → process → encode), implemented entirely in PHP without FFmpeg.

FLV2HLS

Example for multi‑bitrate HLS generation:

<?php

require_once __DIR__ . '/vendor/autoload.php';
ini_set('memory_limit', '2048M');
$profiles = [
    '240p' => [
        'width' => 426,
        'height' => 240,
        'bitrate' => 300000, // 300 Kbps video
        'fps' => 24,
        'audioBitrate' => 48000, // 48 Kbps
        'qp' => 30,
        'watermark'=>true,
        'watermark_file'=> __DIR__."/src/Static/watermark_80x16.yuv",
    ]
];
// Enable multi-process acceleration for high-quality re-encoding; not needed for low bitrate.
$generator = new \Xiaosongshu\Flv2mp4\Recode\PurePhpHlsGenerator($profiles, __DIR__ . '/hls/output', true);
$generator->processFlv(__DIR__ . '/input.flv');
echo "Master playlist: hls/output/master.m3u8\n";
echo "All done!\n";

FLV2FLV

Re‑encode a FLV file with new bitrate/resolution:

<?php
require_once __DIR__ . '/vendor/autoload.php';
ini_set('memory_limit', '2048M');

$config = [
    'width' => 320,
    'height' => 180,
    'bitrate' => 150000,
    'fps' => 15,
    'qp' => 30,
    'watermark'=>true,
    'watermark_file'=> __DIR__."/src/Static/watermark_80x16.yuv",
];
// Enable multi-process acceleration for high-quality re-encoding; not needed for low bitrate.
$recoder = new \Xiaosongshu\Flv2mp4\Recode\FlvRecoder($config, true);
$recoder->setMaxFrames(50);
$recoder->processFlv(__DIR__ . '/input.flv', __DIR__.'/output.flv');
echo "FLV re‑encoding done.\n";

MP42MP4

Re‑encode a MP4 file:

<?php
require_once __DIR__ . '/vendor/autoload.php';
ini_set('memory_limit', '2048M');

$config = [
    'width' => 320,
    'height' => 180,
    'bitrate' => 150000,
    'fps' => 15,
    'qp' => 30,
    'watermark'=>true,
    'watermark_file'=> __DIR__."/src/Static/watermark_80x16.yuv",
];
// Enable multi-process acceleration for high-quality re-encoding; not needed for low bitrate.
$recoder = new \Xiaosongshu\Flv2mp4\Recode\Mp4Recoder($config, true);
$recoder->setMaxFrames(50);
$recoder->processMp4(__DIR__ . '/input.mp4', __DIR__ . '/output.mp4');
echo "MP4 re‑encoding done.\n";

Notes for watermarking:

  • Watermark files must be in YUV format, and the filename must include its dimensions (e.g., watermark_{width}x{height}.yuv).
  • The tool parses width and height from the filename automatically (e.g., watermark_80x16.yuv → width 80, height 16).
  • The re‑encoding module exposes a YUV pixel‑level interface, which can be used to implement custom features like subtitles, picture‑in‑picture, video stitching, etc.
  • For detailed H.264 usage, see src/Codec/README.md.

Supported Re‑encoding Features

  • I‑frame decode & encode (100% exact, INF dB)
  • P‑frame decode & encode (Baseline Profile)
  • Intra prediction: 4×4 (9 modes) + 16×16 (4 modes)
  • Inter prediction: P‑frame motion estimation (diamond search optimized)
  • 1/4‑pixel precision (6‑tap filter interpolation)
  • CAVLC entropy coding (Baseline Profile)
  • Resolution scaling (YUV scaling after decode → re‑encode)
  • Bitrate control (via QP parameter)
  • B‑frame support (planned, requires Main Profile with bidirectional prediction)
  • CABAC entropy coding (planned, Main Profile)

⚠️ Performance note: The H.264 re‑encoding module is pure PHP and is intended for short‑duration videos (≤ 10 seconds) for offline processing or functional verification. For long videos or high‑resolution transcoding, professional tools like FFmpeg are recommended.

Watermark Generator

Provides PHP functions to generate YUV watermark files. Uses GD extension if available, otherwise falls back to built‑in bitmap font.

  • generateFromText() – generates text watermark YUV. Uses GD if available; otherwise falls back to bitmap font (ASCII characters only).
  • generateFromImage() – generates watermark YUV from PNG/JPG images (requires GD extension).

Generate text watermark

<?php
require_once __DIR__ . '/vendor/autoload.php';

use Xiaosongshu\Flv2mp4\Codec\WatermarkUtil;

echo "=== Testing WatermarkUtil ===\n\n";

echo "1. Generate text watermark (xiaosongshu, 80x16)...\n";
$outputFile1 = __DIR__ . '/test_wm_text.yuv';
$start = microtime(true);
$result = WatermarkUtil::generateFromText(
    'xiaosongshu',
    $outputFile1,
    80,
    16,
    [
        'fontSize' => 5, // 1–5, built‑in font; ASCII only
        'fontColor' => [255, 255, 255],
        'bgColor' => [0, 0, 0],
    ]
);
$cost = round(microtime(true) - $start, 3);
if ($result && file_exists($outputFile1)) {
    $size = filesize($outputFile1);
    $expectedSize = 80 * 16 + (80 * 16 >> 1);
    echo "   Success! Size: {$size} bytes (expected: {$expectedSize}) - time: {$cost}s\n";
    if ($size === $expectedSize) {
        echo "   ✅ File size correct\n";
    } else {
        echo "   ❌ File size mismatch\n";
    }
} else {
    echo "   ❌ Generation failed\n";
}

Generate from image

Requires GD extension.

<?php

require_once __DIR__ . '/vendor/autoload.php';

use Xiaosongshu\Flv2mp4\Codec\WatermarkUtil;

echo "=== Testing WatermarkUtil ===\n\n";

echo "1. Generate watermark from image (xiaosongshu, 80x16)...\n";
$outputFile1 = __DIR__ . '/test_wm_copy_80x16.yuv';
$start = microtime(true);
$result = WatermarkUtil::generateFromImage(
    __DIR__."/watermark_80x16.png",
    $outputFile1,
    80,
    16,
);
$cost = round(microtime(true) - $start, 3);
if ($result && file_exists($outputFile1)) {
    $size = filesize($outputFile1);
    $expectedSize = 80 * 16 + (80 * 16 >> 1);
    echo "   Success! Size: {$size} bytes (expected: {$expectedSize}) - time: {$cost}s\n";
    if ($size === $expectedSize) {
        echo "   ✅ File size correct\n";
    } else {
        echo "   ❌ File size mismatch\n";
    }
} else {
    echo "   ❌ Generation failed\n";
}

Performance Test Report

Test Environment

Item Windows Environment Linux Environment (Docker)
Operating System Windows Linux (Docker)
CPU 16 cores (physical) 14 cores (physical)
Memory 15.8 GB (available) 4 GB (available)
Worker processes 8 ME sub‑processes 8 ME sub‑processes
PHP version 8.4.3 (CLI, JIT enabled) 8.1.24 (CLI, OPcache disabled)
OPcache opcache.enable_cli=on, opcache.jit=on, opcache.jit_buffer_size=100M Not enabled
Test clip test.flv, 3.02 s, 720×742, 30 fps Same as left
Output specs output.flv, 360×360, 10 fps Same as left
Encoding settings H.264 Constrained Baseline, AAC 128 kbps Same as left

Cross‑Platform Performance Comparison

Output Format Windows Time Linux (Docker) Time Performance Gain
FLV Re‑encoding 28 s 23 s ↓ 17.9%
MP4 Re‑encoding 29 s 24 s ↓ 17.2%
HLS (mpegts + m3u8) 37 s 31 s ↓ 16.2%

Linux environment (without OPcache) is still about 5–6 seconds faster than Windows (with JIT enabled).

Encoding/Decoding for AAC-MP3-OPUS-WAV

# aac-lc → mp3
\Xiaosongshu\Flv2mp4\Client::runAac2Mp3( __DIR__ . '/input.aac',__DIR__ . '/aac2mp3.mp3');
# aac-lc → wav
\Xiaosongshu\Flv2mp4\Client::runAac2Wav(__DIR__ . '/input.aac',__DIR__ . '/aac2wav.wav');
# wav → aac-lc
\Xiaosongshu\Flv2mp4\Client::runWav2Aac(__DIR__ . '/input.wav',__DIR__ . '/wav2aac.aac');
# opus → wav
\Xiaosongshu\Flv2mp4\Client::runOpus2Wav(__DIR__ . '/input.opus',__DIR__ . '/opus2wav.wav');
# mp3 → wav
\Xiaosongshu\Flv2mp4\Client::runMp32Wav(__DIR__ . '/input.mp3',__DIR__ . '/mp32wav.wav');
# wav → mp3
\Xiaosongshu\Flv2mp4\Client::runWav2Mp3(__DIR__ . '/input.wav',__DIR__ . '/wav2mp3.mp3');
# opus → mp3
\Xiaosongshu\Flv2mp4\Client::runOpus2Mp3(__DIR__ . '/input.opus',__DIR__ . '/opus2mp3.mp3');
# opus → aac-lc
\Xiaosongshu\Flv2mp4\Client::runOpus2Aac(__DIR__ . '/input.opus',__DIR__ . '/opus2aac.aac');
# mp3 → aac-lc
\Xiaosongshu\Flv2mp4\Client::runMp32Aac(__DIR__ . '/input.mp3',__DIR__ . '/mp32aac.aac');
# mp3 → opus
\Xiaosongshu\Flv2mp4\Client::runMp32Opus(__DIR__ . '/input.mp3', __DIR__ . '/mp32opus.opus');
# wav → opus
\Xiaosongshu\Flv2mp4\Client::runWav2Opus(__DIR__ . '/input.wav', __DIR__ . '/wav2opus.opus');
# aac-lc → opus 
\Xiaosongshu\Flv2mp4\Client::runAac2Opus(__DIR__ . '/input.aac', __DIR__ . '/aac2opus.opus');

🔧 Technical Notes

  • 100% pure PHP 8.1+, no FFmpeg dependency.
  • Originally built to serve xiaosongshu/rtmp_server.
  • Recommended static analysis: PHPStan Level 8.
  • H.264 re‑encoding uses distributed multi‑process architecture; disable distributed mode if running on a single‑core machine.

Open Source License & Disclaimer

  • Open Source License: This project is released under the Apache License 2.0, which permits free use, modification, and distribution (including for commercial purposes). The code is provided "AS IS", without any express or implied warranties. The author shall not be held liable for any damages arising from the use of this software.
  • Patent Risk Notice: This project contains pure PHP implementations of patent-protected audio/video codecs, including H.264, AAC-LC, and MP3. The above open source license grants only a copyright license and does not include any patent license.
  • Usage Restrictions & Transfer of Liability: The above codec implementations are intended solely for learning, research, testing, and personal non-commercial use. If you use them for any commercial product distribution or commercial operation, you must obtain the appropriate patent licenses from the relevant patent holders (e.g., Via Licensing, MPEG LA, Fraunhofer IIS) at your own expense and assume all patent infringement risks. The author of this project shall not be liable for any patent infringement liabilities arising therefrom.
  • Final Interpretation: By using this project, you are deemed to have read, understood, and agreed to all terms of this disclaimer.

📧 Contact