(config: VideoEncoderConfig)
| 140 | } |
| 141 | |
| 142 | private static createCodecContext(config: VideoEncoderConfig) { |
| 143 | const inferred = inferCodecFromCodecString(config.codec); |
| 144 | if (!inferred) { |
| 145 | throw new Error('Unsupported codec'); |
| 146 | } |
| 147 | |
| 148 | if (config.alpha === 'keep') { |
| 149 | throw new DOMException('Alpha encoding is not supported.', 'NotSupportedError'); |
| 150 | } |
| 151 | |
| 152 | if (config.bitrateMode === 'quantizer') { |
| 153 | throw new DOMException('Quantizer bitrate mode is not supported.', 'NotSupportedError'); |
| 154 | } |
| 155 | |
| 156 | let codec: Codec | null = null; |
| 157 | if (!canUseHwAccel() || config.hardwareAcceleration === 'prefer-software') { |
| 158 | codec = Codec.findEncoder(inferred); |
| 159 | } else { |
| 160 | codec = getHardwareEncoderCodec(inferred) ?? Codec.findEncoder(inferred); |
| 161 | } |
| 162 | |
| 163 | if (!codec) { |
| 164 | return null; |
| 165 | } |
| 166 | |
| 167 | const codecContext = new CodecContext(); |
| 168 | codecContext.allocContext3(codec); |
| 169 | |
| 170 | let pixelFormat = AV_PIX_FMT_YUV420P; |
| 171 | if (codec.pixelFormats && !codec.pixelFormats.includes(AV_PIX_FMT_YUV420P)) { |
| 172 | pixelFormat = codec.pixelFormats[0]; |
| 173 | } |
| 174 | |
| 175 | codecContext.width = config.width; |
| 176 | codecContext.height = config.height; |
| 177 | codecContext.pixelFormat = pixelFormat; |
| 178 | codecContext.timeBase = new Rational(1, 1e6); |
| 179 | codecContext.gopSize = 60; |
| 180 | codecContext.framerate = new Rational(Math.round(config.framerate ?? 0) || 30, 1); |
| 181 | codecContext.bitRate = BigInt(config.bitrate ?? getDecentVideoBitrate(inferred, config.width, config.height)); |
| 182 | |
| 183 | if (config.bitrateMode === 'constant') { |
| 184 | codecContext.rcMinRate = codecContext.bitRate; |
| 185 | codecContext.rcMaxRate = codecContext.bitRate; |
| 186 | } |
| 187 | |
| 188 | const isRealtime = config.latencyMode === 'realtime'; |
| 189 | |
| 190 | if (codec.name === 'libx264') { |
| 191 | if (isRealtime) { |
| 192 | codecContext.setOption('tune', 'zerolatency'); |
| 193 | codecContext.setOption('preset', 'ultrafast'); |
| 194 | } |
| 195 | } else if (codec.name === 'libx265') { |
| 196 | codecContext.setOption('x265-params', 'log-level=error'); |
| 197 | |
| 198 | if (isRealtime) { |
| 199 | codecContext.setOption('tune', 'zerolatency'); |
no test coverage detected