首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 解析 SWF 檔案屬性:寬高、版本、幀率、幀數、背景色純代碼實現

     編輯:PHP 相關知識

為什麼需要純 PHP 解析 SWF?

在不安裝 ffmpeg、swftools 等外部工具的受限環境(共享主機、Docker 無權限容器)中,直接用 PHP 讀取 SWF 檔頭即可獲得尺寸、版本、幀率、背景色等元資料,零依賴、極低延遲,適合上傳驗證、資產管理系統即時預覽。

SWF 檔案結構要點

  • 簽名: 前 3 字節 FWS(未壓縮)或 CWS(Zlib 壓縮)。
  • 版本: 第 4 字節(單字節)。
  • 檔案長度: 第 5-8 字節(小端序 32 位無號整數)。
  • Frame Size (RECT 結構): 位元級編碼:
    1. 前 5 bits:Nbits(座標位元數,通常 14-31)。
    2. 接著 4 × Nbits bits:Xmin, Xmax, Ymin, Ymax(Twips,1/20 像素)。
    寬度 = (Xmax - Xmin) / 20,高度 = (Ymax - Ymin) / 20。
  • Frame Rate: 16 位定點數(高 8 位整數部,低 8 位小數部),單位 FPS。
  • Frame Count: 16 位無號整數,小端序。
  • 背景色: 非強制標籤,出現在 SetBackgroundColor 標籤(Tag ID 9)中,3 字節 RGB。

完整 FlashParser 類代碼

<?php
class FlashParser {
    // 可選屬性開關
    public $needBackColor = true;
    public $needVersion   = true;
    public $needFrameRate = true;
    public $needFrameCount= true;

    /**
     * 解析 SWF 檔案
     * @param string $filePath
     * @return array ['width'=>, 'height'=>, 'version'=>, 'frameRate'=>, 'frameCount'=>, 'bgColor'=>] 或 ['error'=>檔案路徑]
     */
    public function parse($filePath) {
        if (!file_exists($filePath)) return ['error' => $filePath];

        $fp = fopen($filePath, 'rb');
        $data = fread($fp, filesize($filePath));
        fclose($fp);

        // === 1. 檢測壓縮並解壓 ===
        if ($data[0] === 'C') { // CWS
            $header = substr($data, 0, 8); // 簽名+版本+長度
            $body   = substr($data, 8);
            $body   = @gzuncompress($body);
            if ($body === false) return ['error' => 'Zlib decompress failed'];
            $data = $header . $body;
            $data[0] = 'F'; // 標記為已解壓
        } elseif ($data[0] !== 'F') {
            return ['error' => 'Invalid SWF signature'];
        }

        // === 2. 解析 RECT (Frame Size) ===
        // 位元流讀取輔助
        $bits = '';
        for ($i = 8; $i < strlen($data); $i++) {
            $bits .= str_pad(decbin(ord($data[$i])), 8, '0', STR_PAD_LEFT);
        }
        $pos = 0;
        $nBits = bindec(substr($bits, $pos, 5)); $pos += 5;
        $readCoord = function() use (&$bits, &$pos, $nBits) {
            $val = bindec(substr($bits, $pos, $nBits));
            $pos += $nBits;
            // 有號數轉換(最高位為符號位)
            if ($val >= (1 << ($nBits-1))) $val -= (1 << $nBits);
            return $val;
        };
        $xMin = $readCoord(); $xMax = $readCoord();
        $yMin = $readCoord(); $yMax = $readCoord();
        $width  = ($xMax - $xMin) / 20;
        $height = ($yMax - $yMin) / 20;

        // 位元對齊到字節邊界
        $pos = (int)(($pos + 7) / 8) * 8;
        $bytePos = 8 + ($pos / 8); // 轉回 byte 索引

        $result = ['width' => $width, 'height' => $height];

        // === 3. 版本 ===
        if ($this->needVersion) {
            $result['version'] = ord($data[3]);
        }

        // === 4. Frame Rate & Frame Count ===
        // 緊接在 RECT 之後:Frame Rate (2 bytes) + Frame Count (2 bytes) 小端序
        if ($bytePos + 3 < strlen($data)) {
            if ($this->needFrameRate) {
                $rateInt  = ord($data[$bytePos]);
                $rateFrac = ord($data[$bytePos+1]);
                $result['frameRate'] = $rateInt + $rateFrac / 256;
            }
            if ($this->needFrameCount) {
                $fcLow  = ord($data[$bytePos+2]);
                $fcHigh = ord($data[$bytePos+3]);
                $result['frameCount'] = $fcLow + ($fcHigh << 8);
            }
        }

        // === 5. 背景色(掃描 Tag 9)===
        if ($this->needBackColor) {
            $tagPos = $bytePos + 4; // 跳過 Frame Rate/Count
            while ($tagPos + 1 < strlen($data)) {
                $tagHeader = ord($data[$tagPos]) + (ord($data[$tagPos+1]) << 8);
                $tagType   = $tagHeader >> 6;
                $tagLen    = $tagHeader & 0x3F;
                $tagPos += 2;
                if ($tagLen === 0x3F) { // 長格式
                    if ($tagPos + 3 > strlen($data)) break;
                    $tagLen = ord($data[$tagPos]) + (ord($data[$tagPos+1])<<8) + (ord($data[$tagPos+2])<<16) + (ord($data[$tagPos+3])<<24);
                    $tagPos += 4;
                }
                if ($tagType === 9) { // SetBackgroundColor
                    if ($tagPos + 2 < strlen($data)) {
                        $r = ord($data[$tagPos]);
                        $g = ord($data[$tagPos+1]);
                        $b = ord($data[$tagPos+2]);
                        $result['bgColor'] = sprintf('#%02X%02X%02X', $r, $g, $b);
                    }
                    break;
                }
                $tagPos += $tagLen;
                if ($tagPos > strlen($data)) break;
            }
        }

        return $result;
    }
}

// === 使用範例 ===
$parser = new FlashParser();
$info = $parser->parse('/path/to/movie.swf');
if (isset($info['error'])) {
    echo "解析失敗:{$info['error']}";
} else {
    echo "寬度:{$info['width']}px
"; echo "高度:{$info['height']}px
"; echo "版本:{$info['version']}
"; echo "幀率:{$info['frameRate']} FPS
"; echo "幀數:{$info['frameCount']}
"; echo "背景色:{$info['bgColor']}
"; }

關鍵實作細節補充

  • 位元流讀取: 將整個檔案轉為二進位字串,以 bit 為單位游標讀取,避免複雜的位移運算,碼簡單且不易出錯。
  • 有號座標處理: RECT 座標為有號整數,需檢查最高位進行負數還原。
  • Twips 單位換算: 1 Twip = 1/20 像素,寬高皆需除以 20。
  • Frame Rate 定點數: 高字節整數部 + 低字節小數部/256。
  • 標籤掃描: SWF 為標籤流結構,背景色非必要,需逐標籤掃描 Tag ID 9,支援短/長長度格式。
  • 記憶體考量: 整檔讀入記憶體,僅適合 < 10MB 檔案;大檔案請改用流式讀取或外部工具。

現代化替代方案

方案優點缺點
getID3()支援超多格式、成熟穩定、Composer 安裝體積大、類庫依賴
FFmpeg/ffprobe最權威、支援影音流、元資料完整需安裝二進制、執行開銷大
swftools (swfdump)專精 SWF、可導出矢量/動作腳本停止維護、編譯麻煩
本純 PHP 類零依賴、單檔、嵌入專案極簡功能僅限基本屬性、大檔記憶體壓力

總結

純 PHP 解析 SWF 檔頭是零依賴環境下的最佳選擇。本 FlashParser 類完整實現簽名檢測、Zlib 解壓、RECT 位元解碼、定點數幀率、標籤掃描背景色,代碼清晰、可直接嵌入專案。若需更豐富元資料(音訊、腳本、字體),請改用 getID3 或 ffprobe。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved