Skip to content

Commit

Permalink
fix: Fix wrong aspect ratio in transmuxed videos (#6170)
Browse files Browse the repository at this point in the history
Fixes #6168
  • Loading branch information
avelad committed Jan 26, 2024
1 parent 08cc34a commit eb1fef8
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 8 deletions.
2 changes: 2 additions & 0 deletions lib/mss/mss_parser.js
Expand Up @@ -661,6 +661,8 @@ shaka.mss.MssParser = class {
videoNalus: videoNalus,
audioConfig: new Uint8Array([]),
videoConfig: new Uint8Array([]),
hSpacing: 0,
vSpacing: 0,
data: null, // Data is not necessary for init segement.
stream: stream,
};
Expand Down
2 changes: 2 additions & 0 deletions lib/transmuxer/aac_transmuxer.js
Expand Up @@ -195,6 +195,8 @@ shaka.transmuxer.AacTransmuxer = class {
videoNalus: [],
audioConfig: new Uint8Array([]),
videoConfig: new Uint8Array([]),
hSpacing: 0,
vSpacing: 0,
data: {
sequenceNumber: this.frameIndex_,
baseMediaDecodeTime: baseMediaDecodeTime,
Expand Down
2 changes: 2 additions & 0 deletions lib/transmuxer/ac3_transmuxer.js
Expand Up @@ -184,6 +184,8 @@ shaka.transmuxer.Ac3Transmuxer = class {
videoNalus: [],
audioConfig: audioConfig,
videoConfig: new Uint8Array([]),
hSpacing: 0,
vSpacing: 0,
data: {
sequenceNumber: this.frameIndex_,
baseMediaDecodeTime: baseMediaDecodeTime,
Expand Down
2 changes: 2 additions & 0 deletions lib/transmuxer/ec3_transmuxer.js
Expand Up @@ -184,6 +184,8 @@ shaka.transmuxer.Ec3Transmuxer = class {
videoNalus: [],
audioConfig: audioConfig,
videoConfig: new Uint8Array([]),
hSpacing: 0,
vSpacing: 0,
data: {
sequenceNumber: this.frameIndex_,
baseMediaDecodeTime: baseMediaDecodeTime,
Expand Down
29 changes: 28 additions & 1 deletion lib/transmuxer/h264.js
Expand Up @@ -20,7 +20,8 @@ shaka.transmuxer.H264 = class {
* describes the properties of upcoming video frames.
*
* @param {!Array.<shaka.extern.VideoNalu>} nalus
* @return {?{height: number, width: number, videoConfig: !Uint8Array}}
* @return {?{height: number, width: number, videoConfig: !Uint8Array,
* hSpacing: number, vSpacing: number}}
*/
static parseInfo(nalus) {
const H264 = shaka.transmuxer.H264;
Expand Down Expand Up @@ -129,6 +130,30 @@ shaka.transmuxer.H264 = class {
frameCropBottomOffset = expGolombDecoder.readUnsignedExpGolomb();
}

let hSpacing = 1;
let vSpacing = 1;

// vui_parameters_present_flag
if (expGolombDecoder.readBoolean()) {
// aspect_ratio_info_present_flag
if (expGolombDecoder.readBoolean()) {
const aspectRatioIdc = expGolombDecoder.readUnsignedByte();
const hSpacingTable = [
1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2,
];
const vSpacingTable = [
1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1,
];
if (aspectRatioIdc > 0 && aspectRatioIdc < 16) {
hSpacing = hSpacingTable[aspectRatioIdc - 1];
vSpacing = vSpacingTable[aspectRatioIdc - 1];
} else if (aspectRatioIdc === 255) {
hSpacing = expGolombDecoder.readBits(16);
vSpacing = expGolombDecoder.readBits(16);
}
}
}

const height = ((2 - frameMbsOnlyFlag) *
(picHeightInMapUnitsMinus1 + 1) * 16) - (frameCropTopOffset * 2) -
(frameCropBottomOffset * 2);
Expand Down Expand Up @@ -165,6 +190,8 @@ shaka.transmuxer.H264 = class {
height,
width,
videoConfig,
hSpacing,
vSpacing,
};
}

Expand Down
30 changes: 25 additions & 5 deletions lib/transmuxer/h265.js
Expand Up @@ -20,7 +20,8 @@ shaka.transmuxer.H265 = class {
* describes the properties of upcoming video frames.
*
* @param {!Array.<shaka.extern.VideoNalu>} nalus
* @return {?{height: number, width: number, videoConfig: !Uint8Array}}
* @return {?{height: number, width: number, videoConfig: !Uint8Array,
* hSpacing: number, vSpacing: number}}
*/
static parseInfo(nalus) {
const H265 = shaka.transmuxer.H265;
Expand Down Expand Up @@ -87,6 +88,8 @@ shaka.transmuxer.H265 = class {
height: spsConfiguration.height,
width: spsConfiguration.width,
videoConfig,
hSpacing: spsConfiguration.sarWidth,
vSpacing: spsConfiguration.sarHeight,
};
}

Expand Down Expand Up @@ -298,6 +301,8 @@ shaka.transmuxer.H265 = class {
}
}
let defaultDisplayWindowFlag = false; // for calc offset
let sarWidth = 1;
let sarHeight = 1;
let minSpatialSegmentationIdc = 0; // for hvcC
gb.readBoolean(); // sps_temporal_mvp_enabled_flag
gb.readBoolean(); // strong_intra_smoothing_enabled_flag
Expand All @@ -306,9 +311,18 @@ shaka.transmuxer.H265 = class {
const aspectRatioInfoPresentFlag = gb.readBoolean();
if (aspectRatioInfoPresentFlag) {
const aspectRatioIdc = gb.readUnsignedByte();
if (aspectRatioIdc === 255) {
gb.readBits(16); // sar_width
gb.readBits(16); // sar_height
const sarWidthTable = [
1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2,
];
const sarHeightTable = [
1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1,
];
if (aspectRatioIdc > 0 && aspectRatioIdc < 16) {
sarWidth = sarWidthTable[aspectRatioIdc - 1];
sarHeight = sarHeightTable[aspectRatioIdc - 1];
} else if (aspectRatioIdc === 255) {
sarWidth = gb.readBits(16);
sarHeight = gb.readBits(16);
}
}
const overscanInfoPresentFlag = gb.readBoolean();
Expand Down Expand Up @@ -459,6 +473,8 @@ shaka.transmuxer.H265 = class {
bitDepthChromaMinus8,
width: codecWidth,
height: codecHeight,
sarWidth: sarWidth,
sarHeight: sarHeight,
};
}

Expand Down Expand Up @@ -773,7 +789,9 @@ shaka.transmuxer.H265.VPSConfiguration;
* bitDepthLumaMinus8: number,
* bitDepthChromaMinus8: number,
* width: number,
* height: number
* height: number,
* sarWidth: number,
* sarHeight: number
* }}
*
* @property {number} generalProfileSpace
Expand All @@ -797,6 +815,8 @@ shaka.transmuxer.H265.VPSConfiguration;
* @property {number} bitDepthChromaMinus8
* @property {number} width
* @property {number} height
* @property {number} sarWidth
* @property {number} sarHeight
*/
shaka.transmuxer.H265.SPSConfiguration;

Expand Down
2 changes: 2 additions & 0 deletions lib/transmuxer/mp3_transmuxer.js
Expand Up @@ -176,6 +176,8 @@ shaka.transmuxer.Mp3Transmuxer = class {
videoNalus: [],
audioConfig: new Uint8Array([]),
videoConfig: new Uint8Array([]),
hSpacing: 0,
vSpacing: 0,
data: {
sequenceNumber: this.frameIndex_,
baseMediaDecodeTime: baseMediaDecodeTime,
Expand Down
12 changes: 12 additions & 0 deletions lib/transmuxer/ts_transmuxer.js
Expand Up @@ -409,6 +409,8 @@ shaka.transmuxer.TsTransmuxer = class {
videoNalus: [],
audioConfig: new Uint8Array([]),
videoConfig: new Uint8Array([]),
hSpacing: 0,
vSpacing: 0,
data: {
sequenceNumber: this.frameIndex_,
baseMediaDecodeTime: baseMediaDecodeTime,
Expand Down Expand Up @@ -500,6 +502,8 @@ shaka.transmuxer.TsTransmuxer = class {
videoNalus: [],
audioConfig: audioConfig,
videoConfig: new Uint8Array([]),
hSpacing: 0,
vSpacing: 0,
data: {
sequenceNumber: this.frameIndex_,
baseMediaDecodeTime: baseMediaDecodeTime,
Expand Down Expand Up @@ -591,6 +595,8 @@ shaka.transmuxer.TsTransmuxer = class {
videoNalus: [],
audioConfig: audioConfig,
videoConfig: new Uint8Array([]),
hSpacing: 0,
vSpacing: 0,
data: {
sequenceNumber: this.frameIndex_,
baseMediaDecodeTime: baseMediaDecodeTime,
Expand Down Expand Up @@ -677,6 +683,8 @@ shaka.transmuxer.TsTransmuxer = class {
videoNalus: [],
audioConfig: new Uint8Array([]),
videoConfig: new Uint8Array([]),
hSpacing: 0,
vSpacing: 0,
data: {
sequenceNumber: this.frameIndex_,
baseMediaDecodeTime: baseMediaDecodeTime,
Expand Down Expand Up @@ -769,6 +777,8 @@ shaka.transmuxer.TsTransmuxer = class {
videoNalus: [],
audioConfig: new Uint8Array([]),
videoConfig: info.videoConfig,
hSpacing: info.hSpacing,
vSpacing: info.vSpacing,
data: {
sequenceNumber: this.frameIndex_,
baseMediaDecodeTime: baseMediaDecodeTime,
Expand Down Expand Up @@ -861,6 +871,8 @@ shaka.transmuxer.TsTransmuxer = class {
videoNalus: [],
audioConfig: new Uint8Array([]),
videoConfig: info.videoConfig,
hSpacing: info.hSpacing,
vSpacing: info.vSpacing,
data: {
sequenceNumber: this.frameIndex_,
baseMediaDecodeTime: baseMediaDecodeTime,
Expand Down
33 changes: 31 additions & 2 deletions lib/util/mp4_generator.js
Expand Up @@ -371,12 +371,13 @@ shaka.util.Mp4Generator = class {
]);

let boxName = 'avc1';
const paspBox = this.pasp_(streamInfo);
let sinfBox = new Uint8Array([]);
if (streamInfo.encrypted) {
sinfBox = this.sinf_(streamInfo);
boxName = 'encv';
}
return Mp4Generator.box(boxName, avc1Bytes, avcCBox, sinfBox);
return Mp4Generator.box(boxName, avc1Bytes, avcCBox, paspBox, sinfBox);
}

/**
Expand Down Expand Up @@ -500,12 +501,34 @@ shaka.util.Mp4Generator = class {
]);

let boxName = 'hvc1';
const paspBox = this.pasp_(streamInfo);
let sinfBox = new Uint8Array([]);
if (streamInfo.encrypted) {
sinfBox = this.sinf_(streamInfo);
boxName = 'encv';
}
return Mp4Generator.box(boxName, hvc1Bytes, hvcCBox, sinfBox);
return Mp4Generator.box(boxName, hvc1Bytes, hvcCBox, paspBox, sinfBox);
}

/**
* Generate a PASP box
*
* @param {shaka.util.Mp4Generator.StreamInfo} streamInfo
* @return {!Uint8Array}
* @private
*/
pasp_(streamInfo) {
if (!streamInfo.hSpacing && !streamInfo.vSpacing) {
return new Uint8Array([]);
}
const Mp4Generator = shaka.util.Mp4Generator;
const hSpacing = streamInfo.hSpacing;
const vSpacing = streamInfo.vSpacing;
const bytes = new Uint8Array([
...this.breakNumberIntoBytes_(hSpacing, 4), // hSpacing
...this.breakNumberIntoBytes_(vSpacing, 4), // vSpacing
]);
return Mp4Generator.box('pasp', bytes);
}

/**
Expand Down Expand Up @@ -1301,6 +1324,8 @@ shaka.util.Mp4Generator.DINF_ = new Uint8Array([]);
* videoNalus: !Array.<string>,
* audioConfig: !Uint8Array,
* videoConfig: !Uint8Array,
* hSpacing: number,
* vSpacing: number,
* data: ?shaka.util.Mp4Generator.Data,
* stream: !shaka.extern.Stream
* }}
Expand All @@ -1325,6 +1350,10 @@ shaka.util.Mp4Generator.DINF_ = new Uint8Array([]);
* The stream's audio config.
* @property {!Uint8Array} videoConfig
* The stream's video config.
* @property {number} hSpacing
* The stream's video horizontal spacing of pixels.
* @property {number} vSpacing
* The stream's video vertial spacing of pixels.
* @property {?shaka.util.Mp4Generator.Data} data
* The stream's data.
* @property {!shaka.extern.Stream} stream
Expand Down

0 comments on commit eb1fef8

Please sign in to comment.