feat: support media playbackRate (#1000)

Co-authored-by: wangfukang <wangfukang@kuaishou.com>
This commit is contained in:
fukang wang
2026-04-01 12:00:00 +08:00
committed by GitHub
parent 503d78f941
commit 5fdc817e06
7 changed files with 20 additions and 1 deletions

View File

@@ -133,6 +133,8 @@ export function diff(
oldMediaElement.volume = newMediaRRElement.volume; oldMediaElement.volume = newMediaRRElement.volume;
if (newMediaRRElement.currentTime !== undefined) if (newMediaRRElement.currentTime !== undefined)
oldMediaElement.currentTime = newMediaRRElement.currentTime; oldMediaElement.currentTime = newMediaRRElement.currentTime;
if (newMediaRRElement.playbackRate !== undefined)
oldMediaElement.playbackRate = newMediaRRElement.playbackRate;
break; break;
} }
case 'CANVAS': case 'CANVAS':

View File

@@ -580,6 +580,7 @@ export function BaseRRMediaElementImpl<
public volume?: number; public volume?: number;
public paused?: boolean; public paused?: boolean;
public muted?: boolean; public muted?: boolean;
public playbackRate?: number;
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
attachShadow(_init: ShadowRootInit): IRRElement { attachShadow(_init: ShadowRootInit): IRRElement {
throw new Error( throw new Error(

View File

@@ -217,6 +217,7 @@ describe('diff algorithm for rrdom', () => {
expect(element.currentTime).toEqual(0); expect(element.currentTime).toEqual(0);
expect(element.muted).toEqual(false); expect(element.muted).toEqual(false);
expect(element.paused).toEqual(true); expect(element.paused).toEqual(true);
expect(element.playbackRate).toEqual(1);
const rrDocument = new RRDocument(); const rrDocument = new RRDocument();
const rrMedia = rrDocument.createElement(tagName) as RRMediaElement; const rrMedia = rrDocument.createElement(tagName) as RRMediaElement;
@@ -224,12 +225,14 @@ describe('diff algorithm for rrdom', () => {
rrMedia.currentTime = 100; rrMedia.currentTime = 100;
rrMedia.muted = true; rrMedia.muted = true;
rrMedia.paused = false; rrMedia.paused = false;
rrMedia.playbackRate = 0.5;
diff(element, rrMedia, replayer); diff(element, rrMedia, replayer);
expect(element.volume).toEqual(0.5); expect(element.volume).toEqual(0.5);
expect(element.currentTime).toEqual(100); expect(element.currentTime).toEqual(100);
expect(element.muted).toEqual(true); expect(element.muted).toEqual(true);
expect(element.paused).toEqual(false); expect(element.paused).toEqual(false);
expect(element.playbackRate).toEqual(0.5);
rrMedia.paused = true; rrMedia.paused = true;
diff(element, rrMedia, replayer); diff(element, rrMedia, replayer);

View File

@@ -894,6 +894,7 @@ describe('Basic RRDocument implementation', () => {
expect(node.volume).toBeUndefined(); expect(node.volume).toBeUndefined();
expect(node.paused).toBeUndefined(); expect(node.paused).toBeUndefined();
expect(node.muted).toBeUndefined(); expect(node.muted).toBeUndefined();
expect(node.playbackRate).toBeUndefined();
expect(node.play).toBeDefined(); expect(node.play).toBeDefined();
expect(node.pause).toBeDefined(); expect(node.pause).toBeDefined();
expect(node.toString()).toEqual('VIDEO '); expect(node.toString()).toEqual('VIDEO ');

View File

@@ -703,13 +703,19 @@ function initMediaInteractionObserver({
) { ) {
return; return;
} }
const { currentTime, volume, muted } = target as HTMLMediaElement; const {
currentTime,
volume,
muted,
playbackRate,
} = target as HTMLMediaElement;
mediaInteractionCb({ mediaInteractionCb({
type, type,
id: mirror.getId(target as Node), id: mirror.getId(target as Node),
currentTime, currentTime,
volume, volume,
muted, muted,
playbackRate,
}); });
}, sampling.media || 500); }, sampling.media || 500);
const handlers = [ const handlers = [
@@ -717,6 +723,7 @@ function initMediaInteractionObserver({
on('pause', handler(MediaInteractions.Pause)), on('pause', handler(MediaInteractions.Pause)),
on('seeked', handler(MediaInteractions.Seeked)), on('seeked', handler(MediaInteractions.Seeked)),
on('volumechange', handler(MediaInteractions.VolumeChange)), on('volumechange', handler(MediaInteractions.VolumeChange)),
on('ratechange', handler(MediaInteractions.RateChange)),
]; ];
return () => { return () => {
handlers.forEach((h) => h()); handlers.forEach((h) => h());

View File

@@ -1188,6 +1188,9 @@ export class Replayer {
// unexpeted behavior // unexpeted behavior
void mediaEl.play(); void mediaEl.play();
} }
if (d.type === MediaInteractions.RateChange) {
mediaEl.playbackRate = d.playbackRate;
}
} catch (error) { } catch (error) {
if (this.config.showWarning) { if (this.config.showWarning) {
console.warn( console.warn(

View File

@@ -611,6 +611,7 @@ export const enum MediaInteractions {
Pause, Pause,
Seeked, Seeked,
VolumeChange, VolumeChange,
RateChange,
} }
export type mediaInteractionParam = { export type mediaInteractionParam = {
@@ -619,6 +620,7 @@ export type mediaInteractionParam = {
currentTime?: number; currentTime?: number;
volume?: number; volume?: number;
muted?: boolean; muted?: boolean;
playbackRate?: number;
}; };
export type mediaInteractionCallback = (p: mediaInteractionParam) => void; export type mediaInteractionCallback = (p: mediaInteractionParam) => void;