<?php
/**
 * SeekQuarry/Yioop --
 * Open Source Pure PHP Search Engine, Crawler, and Indexer
 *
 * Copyright (C) 2009 - 2026  Chris Pollett chris@pollett.org
 *
 * LICENSE:
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * END LICENSE
 *
 * @author Chris Pollett chris@pollett.org
 * @license https://www.gnu.org/licenses/ GPL3
 * @link https://www.seekquarry.com/
 * @copyright 2009 - 2026
 * @filesource
 *
 * This class reads the settings an MP4 or WebM keeps beside an H.264
 * stream. A decoder needs them before it can read a frame.
 */
namespace seekquarry\yioop\library\av_processing;
/**
 * AvcConfig holds the settings an MP4 or a WebM keeps beside an H.264
 * stream, and reads them out of the bytes the container stored.
 */
final class AvcConfig
{
    /**
     * $sequence_settings stores the sequence settings, or nothing where there
     * is none.
     * @var string
     */
    public ?string $sequence_settings = null;
    /**
     * $picture_settings stores the picture settings, or nothing where there is
     * none.
     * @var string
     */
    public ?string $picture_settings = null;
    /**
     * $stream_unit_length_size stores how many bytes each unit's length
     * field takes. An MP4 writes a length in front of every unit of
     * the stream, and a decoder cannot find the next unit without
     * knowing the width of that field.
     * @var int
     */
    public int $stream_unit_length_size = 4;
    /**
     * readSettings reads the settings an MP4 or a WebM keeps beside an H.264
     * stream, and hands back what they say. A container calls this once, when
     * it first meets the track, because a decoder cannot read a frame until it
     * knows how long each unit's length field is and which sequence and picture
     * settings the stream started with. them, from the version byte onward.
     *
     * @param string $payload The settings as the container stored
     * @return self The settings, ready to hand to a decoder.
     */
    public static function readSettings(string $payload): self
    {
        $count_value = new self();
        if (strlen($payload) < 7 || ord($payload[0]) !== 1) {
            return $count_value;
        }
        $count_value->stream_unit_length_size = (ord($payload[4]) & 0x03) + 1;
        $sequence_setting_count = ord($payload[5]) & 0x1F;
        $position = 6;
        $number = strlen($payload);
        for ($i = 0; $i < $sequence_setting_count && $position + 2 <=
            $number; $i++) {
            $length = unpack('n', substr($payload, $position, 2))[1];
            $position += 2;
            if ($i === 0) {
                $count_value->sequence_settings = substr($payload,
                    $position, $length);
            }
            $position += $length;
        }
        if ($position >= $number) {
            return $count_value;
        }
        $picture_setting_count = ord($payload[$position]);
        $position++;
        for ($i = 0; $i < $picture_setting_count && $position + 2 <=
            $number; $i++) {
            $length = unpack('n', substr($payload, $position, 2))[1];
            $position += 2;
            if ($i === 0) {
                $count_value->picture_settings = substr($payload, $position,
                    $length);
            }
            $position += $length;
        }
        return $count_value;
    }
}
