mirror of
https://github.com/arcodange-org/mediabunny.git
synced 2026-09-29 03:43:50 +02:00
Clean up cropping logic
This commit is contained in:
+30
-42
@@ -47,7 +47,7 @@ import {
|
||||
} from './misc';
|
||||
import { Output, TrackType } from './output';
|
||||
import { Mp4OutputFormat } from './output-format';
|
||||
import { AudioSample, VideoSample } from './sample';
|
||||
import { AudioSample, clampCropRectangle, validateCropRectangle, VideoSample } from './sample';
|
||||
import { MetadataTags, validateMetadataTags } from './tags';
|
||||
import { NullTarget } from './target';
|
||||
|
||||
@@ -106,22 +106,6 @@ export type ConversionOptions = {
|
||||
export type ConversionVideoOptions = {
|
||||
/** If `true`, all video tracks will be discarded and will not be present in the output. */
|
||||
discard?: boolean;
|
||||
/**
|
||||
* Specifies a rectangular region of the input video to crop to, in the coordinate
|
||||
* system of the original, unrotated frame. Parts of the crop rectangle that extend
|
||||
* beyond the frame will be filled with black. Cropping is performed before rotation
|
||||
* and resizing.
|
||||
*/
|
||||
crop?: {
|
||||
/** The distance in pixels from the left edge of the source frame to the left edge of the crop rectangle. */
|
||||
left: number;
|
||||
/** The distance in pixels from the top edge of the source frame to the top edge of the crop rectangle. */
|
||||
top: number;
|
||||
/** The width in pixels of the crop rectangle. */
|
||||
width: number;
|
||||
/** The height in pixels of the crop rectangle. */
|
||||
height: number;
|
||||
};
|
||||
/**
|
||||
* The desired width of the output video in pixels, defaulting to the video's natural display width. If height
|
||||
* is not set, it will be deduced automatically based on aspect ratio.
|
||||
@@ -142,10 +126,24 @@ export type ConversionVideoOptions = {
|
||||
*/
|
||||
fit?: 'fill' | 'contain' | 'cover';
|
||||
/**
|
||||
* The angle in degrees to rotate the input video by, clockwise. Rotation is applied before resizing. This
|
||||
* rotation is _in addition to_ the natural rotation of the input video as specified in input file's metadata.
|
||||
* The angle in degrees to rotate the input video by, clockwise. Rotation is applied before cropping and resizing.
|
||||
* This rotation is _in addition to_ the natural rotation of the input video as specified in input file's metadata.
|
||||
*/
|
||||
rotate?: Rotation;
|
||||
/**
|
||||
* Specifies the rectangular region of the input video to crop to. The crop region will automatically be clamped to
|
||||
* the dimensions of the input video track. Cropping is performed after rotation but before resizing.
|
||||
*/
|
||||
crop?: {
|
||||
/** The distance in pixels from the left edge of the source frame to the left edge of the crop rectangle. */
|
||||
left: number;
|
||||
/** The distance in pixels from the top edge of the source frame to the top edge of the crop rectangle. */
|
||||
top: number;
|
||||
/** The width in pixels of the crop rectangle. */
|
||||
width: number;
|
||||
/** The height in pixels of the crop rectangle. */
|
||||
height: number;
|
||||
};
|
||||
/**
|
||||
* The desired frame rate of the output video, in hertz. If not specified, the original input frame rate will
|
||||
* be used (which may be variable).
|
||||
@@ -189,24 +187,6 @@ const validateVideoOptions = (videoOptions: ConversionVideoOptions | undefined)
|
||||
if (videoOptions?.forceTranscode !== undefined && typeof videoOptions.forceTranscode !== 'boolean') {
|
||||
throw new TypeError('options.video.forceTranscode, when provided, must be a boolean.');
|
||||
}
|
||||
if (videoOptions?.crop !== undefined) {
|
||||
if (typeof videoOptions.crop !== 'object') {
|
||||
throw new TypeError('options.video.crop, when provided, must be an object.');
|
||||
}
|
||||
const { left, top, width, height } = videoOptions.crop;
|
||||
if (!Number.isInteger(left)) {
|
||||
throw new TypeError('options.video.crop.left must be an integer.');
|
||||
}
|
||||
if (!Number.isInteger(top)) {
|
||||
throw new TypeError('options.video.crop.top must be an integer.');
|
||||
}
|
||||
if (!Number.isInteger(width) || width <= 0) {
|
||||
throw new TypeError('options.video.crop.width must be a positive integer.');
|
||||
}
|
||||
if (!Number.isInteger(height) || height <= 0) {
|
||||
throw new TypeError('options.video.crop.height must be a positive integer.');
|
||||
}
|
||||
}
|
||||
if (videoOptions?.codec !== undefined && !VIDEO_CODECS.includes(videoOptions.codec)) {
|
||||
throw new TypeError(
|
||||
`options.video.codec, when provided, must be one of: ${VIDEO_CODECS.join(', ')}.`,
|
||||
@@ -247,6 +227,9 @@ const validateVideoOptions = (videoOptions: ConversionVideoOptions | undefined)
|
||||
if (videoOptions?.rotate !== undefined && ![0, 90, 180, 270].includes(videoOptions.rotate)) {
|
||||
throw new TypeError('options.video.rotate, when provided, must be 0, 90, 180 or 270.');
|
||||
}
|
||||
if (videoOptions?.crop !== undefined) {
|
||||
validateCropRectangle(videoOptions.crop);
|
||||
}
|
||||
if (
|
||||
videoOptions?.frameRate !== undefined
|
||||
&& (!Number.isFinite(videoOptions.frameRate) || videoOptions.frameRate <= 0)
|
||||
@@ -635,13 +618,18 @@ export class Conversion {
|
||||
const totalRotation = normalizeRotation(track.rotation + (trackOptions.rotate ?? 0));
|
||||
const outputSupportsRotation = this.output.format.supportsVideoRotationMetadata;
|
||||
|
||||
const [rotatedWidth, rotatedHeight] = totalRotation % 180 === 0
|
||||
? [track.codedWidth, track.codedHeight]
|
||||
: [track.codedHeight, track.codedWidth];
|
||||
|
||||
const crop = trackOptions.crop;
|
||||
const [croppedWidth, croppedHeight] = crop
|
||||
if (crop) {
|
||||
clampCropRectangle(crop, rotatedWidth, rotatedHeight);
|
||||
}
|
||||
|
||||
const [originalWidth, originalHeight] = crop
|
||||
? [crop.width, crop.height]
|
||||
: [track.codedWidth, track.codedHeight];
|
||||
const [originalWidth, originalHeight] = totalRotation % 180 === 0
|
||||
? [croppedWidth, croppedHeight]
|
||||
: [croppedHeight, croppedWidth];
|
||||
: [rotatedWidth, rotatedHeight];
|
||||
|
||||
let width = originalWidth;
|
||||
let height = originalHeight;
|
||||
|
||||
Reference in New Issue
Block a user