前端分片解析 MP4 视频编码,判断是否为 H.265/HEVC

npm install mp4box@0.5.4
const MP4Box = require('mp4box')

const DEFAULT_CHUNK_SIZE = 1024 * 1024
const DEFAULT_MAX_READ_SIZE = 16 * 1024 * 1024

export interface VideoCodecParseOptions {
    chunkSize?: number
    maxReadSize?: number
}

/**
 * 分片解析 MP4 中的视频轨道编码,解析失败或未解析到编码时返回空数组。
 */
export function parseVideoCodecs(
    file: File,
    options: VideoCodecParseOptions = {},
): Promise<string[]> {
    const chunkSize = options.chunkSize || DEFAULT_CHUNK_SIZE
    const maxReadSize = options.maxReadSize || DEFAULT_MAX_READ_SIZE

    return new Promise(resolve => {
        const mp4File = MP4Box.createFile(false)
        let readSize = 0
        let settled = false

        const finish = (codecs: string[]) => {
            if (settled) {
                return
            }
            settled = true
            resolve(codecs)
        }

        mp4File.onReady = (info: any) => {
            const tracks = info.videoTracks ||
                (info.tracks || []).filter((track: any) => track.video)
            const codecs = tracks
                .map((track: any) => String(track.codec || '').toLowerCase())
                .filter(Boolean)
            finish(codecs)
        }
        mp4File.onError = () => {
            finish([])
        }

        const readChunk = (start: number) => {
            if (settled) {
                return
            }
            if (start >= file.size || readSize >= maxReadSize) {
                finish([])
                return
            }

            const end = Math.min(start + chunkSize, file.size)
            const reader = new FileReader()
            reader.onload = () => {
                const buffer: any = reader.result
                if (!buffer) {
                    finish([])
                    return
                }

                buffer.fileStart = start
                readSize += buffer.byteLength

                let nextStart
                try {
                    nextStart = mp4File.appendBuffer(buffer)
                } catch (error) {
                    finish([])
                    return
                }

                if (!settled) {
                    const offset = typeof nextStart === 'number' && nextStart > start
                        ? nextStart
                        : end
                    readChunk(offset)
                }
            }
            reader.onerror = () => {
                finish([])
            }
            reader.readAsArrayBuffer(file.slice(start, end))
        }

        readChunk(0)
    })
}

使用

const BLOCKED_VIDEO_CODECS = ['hvc1', 'hev1', 'hevc', 'h265', 'h.265']
const codecs = await parseVideoCodecs(file)
let isBlockedCodec = codecs.some(codec => {
                    return BLOCKED_VIDEO_CODECS.some(blockedCodec => {
                        return codec.startsWith(blockedCodec) || codec.includes(blockedCodec)
                    })
                })
Leave a reply

发表评论