The Video That Only Froze in Safari

Safari မှာမှ ရပ်ရပ်သွားတဲ့ ဗီဒီယို

August 26, 2026 (2mo ago)

This story took place in October–November 2025. Browser autoplay policies are a moving target — Safari's behavior described here was accurate then, but verify against current WebKit behavior before borrowing any of these fixes.

I work as a full-stack engineer on an AI video creation platform — users generate a video with AI, then refine it in a browser-based editor with a timeline, chapters, avatars, and voice narration. A project video is made of chapters, and each chapter has its own canvas where users can layer extra objects on top — text, images, and video clips. The playback engine renders those objects and drives them all off one shared clock.

This bug wasn't about the project video as a whole — it was about those video clips placed inside a chapter.

One day a bug report came in that would eat several weeks of my life, on and off:

"Videos freeze during playback. Only in Safari."

Chrome: fine. Firefox: fine. Safari: a video object would render its first frame and just... sit there. No error dialog, no crash. Sometimes it played, sometimes it froze — and it seemed to depend on where in the video the playback started.

Step 1: Reproduce it properly

"Sometimes it freezes" is not a bug report you can fix. So the first job was turning it into something deterministic.

After a lot of clicking around, the pattern emerged: videos played fine in the chapter where the user had interacted with the page, and froze after chapter transitions. If you pressed play in chapter 1, a video in chapter 1 played. A video in chapter 3? Frozen first frame.

That pattern is a gift, because it points away from "video decoding bug" and toward lifecycle.

Step 2: Hypotheses

I had three:

  1. A decoding/codec issue — Safari is notoriously picky about codecs. But the same file played fine in the first chapter, so the bytes were decodable. Rejected.
  2. A race in our playback clock — maybe we called play() before the element had data. Plausible, but it didn't explain why only later chapters failed.
  3. Safari's autoplay policy — Safari doesn't just require "a user gesture somewhere on the page" like Chrome mostly does. It grants playback permission to specific DOM elements that were activated by a user gesture.

Hypothesis 3 turned out to be the key, and here's the mechanism:

Our editor is a React app. At every chapter transition, React unmounts the old chapter's objects and mounts the new ones. To React this is routine reconciliation. To Safari, it means the <video> element that had permission to play no longer exists. The new element is a stranger. When our clock calls videoElement.play() on it — with no user gesture in the same call stack — Safari rejects the promise, and you get exactly what users saw: a rendered first frame that never moves.

The confirmation was satisfyingly simple: log the play() promise rejection. Chrome resolved it; Safari threw NotAllowedError — but only for elements mounted after the initial gesture.

Safari grants autoplay to the specific DOM element that received the user gesture. After a chapter transition, React mounts a brand-new video element with no permission, so play() rejects with NotAllowedError and the first frame freezes.

Step 3: The fix — and the revert

If Safari blesses elements, the answer is: stop destroying the elements.

I built a VideoElementPool — a small manager that lives outside React, pre-creates a handful of <video> elements up front (while we still have the user's gesture), and hands them out to video objects as chapters mount. Instead of React creating and destroying <video> tags, a component acquires a persistent element, swaps its src, and attaches it to its container. On unmount it releases the element back to the pool.

class VideoElementPool {
  private pool: PoolItem[] = []
 
  acquire(objectId: string): HTMLVideoElement {
    // Reuse the element already assigned to this object, if any
    const existing = this.pool.find((i) => i.currentObjectId === objectId)
    if (existing) return existing.element
 
    // Otherwise hand out an idle, pre-blessed element
    const idle = this.pool.find((i) => !i.inUse)
    idle.inUse = true
    idle.currentObjectId = objectId
    this.reset(idle.element)
    return idle.element
  }
 
  release(objectId: string) { /* mark idle, detach, reset */ }
}

The VideoElementPool lives outside React and never destroys its video elements. React components acquire a pre-blessed element and swap its src on mount, and release it back to the pool on unmount.

I shipped it in late October 2025. It was reverted the same day.

The first version swapped element identity out from under the existing video component, but left the rest of that component's assumptions untouched — its refs, its event listeners, its interaction with the audio engine all still assumed they owned the element's lifecycle. Pooling the DOM node while the component around it believed otherwise broke playback in ways the Safari bug never did.

That revert taught me the real lesson of this bug: the fix wasn't "add a pool." It was change the ownership model. Two days later I landed the second version, which reworked the video component's whole lifecycle around the pool — acquisition on mount, imperative attachment to the container, coordination with the audio engine and the player — instead of bolting a pool onto code that assumed React-managed elements. That one stuck.

Step 4: The bug after the bug

A few weeks later, in late November, a new report: videos occasionally froze in Safari again. Rarer, but real.

This is the hidden tax of object pooling: pooled objects carry state between lives. A recycled <video> element kept whatever muted and loop values its previous user set. And Safari has one more rule: programmatic play() on an unmuted video without a gesture gets rejected — even on a blessed element. So if an element was released while unmuted and later reused, play() silently rejected. Frozen frame, once again.

A pooled video element released while unmuted is later acquired by another object. Calling play() without resetting state gets rejected by Safari. The fix: re-assert muted and loop before every play().

The fix was small but principled: never trust inherited state on a pooled resource — re-assert every property that gates playback, right before playing.

const video = videoRef.current
if (!video) return
 
if (trims?.length) {
  video.currentTime = trims[0].start
}
video.muted = true   // Safari rejects unmuted programmatic play()
video.loop = false   // don't inherit loop state from the element's previous life
video.play()

(The clip's sound wasn't lost, by the way — the mute is only temporary. Once the video is actually playing, our code unmutes it again, and Safari accepts that because the pooled elements were already blessed by the user's click when the pool was warmed up. Narration is separate: it plays through its own audio engine.)

While tracing the loop state I also found an unrelated one-line bug that had been hiding in the loop toggle:

isLoop: o.isLoop ? !o.isLoop : false   // 🐛 always false — the toggle could never turn ON
isLoop: !o.isLoop ? true : false       // ✅ actually toggles

That's the kind of thing you only catch when a hard bug forces you to read adjacent code with full suspicion.

Tradeoffs

The pool is not free:

  • We're fighting React. Element lifecycle is now managed imperatively outside the framework, which is exactly the kind of code that surprises the next engineer. It's documented heavily for that reason.
  • Pool sizing is a guess. We pre-create enough elements for a typical chapter; an LRU fallback handles the pathological case.
  • Stale state is a permanent hazard — the second freeze proved it. The rule "reset everything on acquire and before play" is now load-bearing.

Would I prefer a declarative fix? Absolutely. But Safari's permission model is attached to DOM identity, React's model destroys DOM identity, and no amount of wishing changes either. Sometimes the correct engineering decision is a well-contained ugly bridge between two systems that disagree.

Results

  • Safari video freezes at chapter transitions: gone, confirmed across the reported user scenarios.
  • The follow-up "stale pooled state" freeze: gone, with the reset-before-play rule guarding regressions.
  • One broken loop toggle: fixed as a bonus.

What I took away

  1. "Only in Safari" usually means policy, not parsing. When one browser fails silently, read its permission model before its codec support.
  2. Reproduce until the pattern names the subsystem. "Freezes after chapter transitions" pointed at element lifecycle long before any code did.
  3. A revert is data. My first fix failed because I changed a low-level mechanism without changing the ownership model above it. The second fix succeeded because the revert told me exactly that.
  4. Pooled resources lie about their state. If you recycle objects, re-assert every invariant at the boundary — the bug you prevent will be the silent kind.

ဒီအဖြစ်အပျက်က 2025 အောက်တိုဘာကနေ နိုဝင်ဘာလအတွင်းမှာ ဖြစ်ခဲ့တာပါ။ Browser တွေရဲ့ autoplay စည်းမျဉ်းတွေက ခဏခဏ ပြောင်းတတ်ပါတယ်။ ဒီမှာ ရေးထားတဲ့ Safari ရဲ့ အပြုအမူက အဲဒီတုန်းက မှန်ခဲ့ပေမယ့် ဒီနည်းတွေကို ယူသုံးမယ်ဆိုရင် လက်ရှိ WebKit မှာ အရင် ပြန်စစ်ကြည့်ဖို့ အကြံပြုချင်ပါတယ်။

ကျွန်တော်က AI နဲ့ ဗီဒီယို ဖန်တီးပေးတဲ့ platform တစ်ခုမှာ full-stack engineer အဖြစ် လုပ်နေပါတယ်။ User တွေက AI နဲ့ ဗီဒီယိုကို ထုတ်ပြီးရင် browser ထဲက editor မှာ ဆက်ပြင်ကြတယ်။ အဲဒီ editor မှာ timeline၊ chapter၊ avatar နဲ့ အသံ ရွတ်ပြတဲ့ narration စတာတွေ ပါပါတယ်။ Project video တစ်ခုကို chapter တွေနဲ့ ဖွဲ့ထားပြီး chapter တစ်ခုချင်းစီမှာ ကိုယ်ပိုင် canvas ရှိပါတယ်။ User တွေက အဲဒီ canvas ပေါ်မှာ စာ၊ ပုံ၊ video clip တွေကို ထပ်ထည့်လို့ရတယ်။ ဗီဒီယို playback engine က အဲဒီ object တွေကို ဆွဲပြီး clock တစ်ခုနဲ့ အချိန်ကိုက် ထိန်းပေးပါတယ်။

ဒီ bug က project video တစ်ခုလုံးအကြောင်း မဟုတ်ပါဘူး — chapter ထဲမှာ ထပ်ထည့်ထားတဲ့ အဲဒီ video clip တွေအကြောင်းပါ။ ရပ်ရပ်သွားတာက အဲဒီ chapter ထဲက video clip တေပေါ့။

တစ်နေ့မှာတော့ ကျွန်တော့်ကို အချိန်အတော်အကြာ ခေါင်းစားစေမယ့် bug report တစ်ခု ရောက်လာပါတယ်။

"ဗီဒီယို ကြည့်နေရင်း ရပ်ရပ်သွားတယ်။ Safari မှာပဲ ဖြစ်တယ်။"

Chrome မှာ ပြဿနာမရှိဘူး။ Firefox မှာလည်း ok တယ်။ Safari မှာကျတော့ video က ပထမဆုံး frame ကို ပြပြီး... အဲဒီမှာတင် ငြိမ်သွားရော။ Error လည်း မပြဘူး၊ crash လည်း မဖြစ်ဘူး။ တစ်ခါတစ်လေ ကောင်းကောင်း ပြတယ်၊ တစ်ခါတစ်လေ ရပ်နေတယ်။ ဗီဒီယိုရဲ့ ဘယ်နေရာ ကနေ စဖွင့်သလဲ ဆိုတာနဲ့ ဆိုင်နေပုံရတယ်။

အဆင့် ၁: bug ကို တိတိကျကျ reproduce လုပ်ကြည့်ခြင်း

"တစ်ခါတစ်လေ ရပ်တယ်" ဆိုတာမျိုးကို ပြင်လို့ မရပါဘူး။ ဒါကြောင့် ပထမဆုံး လုပ်ရမှာက ဘယ်အခြေအနေမှာ ဖြစ်တာလဲဆိုတာ သေချာသိပြီး ကိုယ်တိုင် ပြန်ဖြစ်အောင် (reproduce) လုပ်နိုင်ဖို့ပါ။

အကြိမ်ကြိမ် စမ်းကြည့်ပြီးတဲ့အခါ ပုံစံတစ်ခု ပေါ်လာတယ်။ User က page ကို နှိပ်(click)ခဲ့တဲ့ chapter ထဲမှာဆိုရင် ဗီဒီယို ကောင်းကောင်း ပြတယ်။ Chapter ကူးသွားတာနဲ့ ရပ်တော့တာပဲ။ Chapter 1 မှာ play ကို နှိပ်ရင် chapter 1 ထဲက ဗီဒီယိုက ပြတယ်။ Chapter 3 ထဲက ဗီဒီယိုကျတော့ ပထမ frame မှာတင် ရပ်နေတယ်။

ဒီပုံစံကို တွေ့တာ တော်တော် ကံကောင်းတာပါ။ ဘာလို့လဲဆိုတော့ "ဗီဒီယိုကို decode မလုပ်နိုင်လို့" ဆိုတဲ့ဘက်ကို မဟုတ်ဘဲ element တွေ ဘယ်အချိန် ပေါ်လာပြီး ဘယ်အချိန် ပျောက်သွားသလဲ ဆိုတဲ့ lifecycle ဘက်ကို ညွှန်ပြနေလို့ပါ။

အဆင့် ၂: ဖြစ်နိုင်ချေ သုံးခု

ကျွန်တော် စဉ်းစားမိတာ သုံးခုရှိတယ်။

  1. Codec / decode ပြဿနာ — Safari က codec ကိစ္စမှာ ဇီဇာကြောင်တာ လူသိများပါတယ်။ ဒါပေမယ့် ဖိုင်တစ်ခုတည်းကပဲ ပထမ chapter မှာ ကောင်းကောင်း ပြနေတာဆိုတော့ decode လုပ်လို့ ရတယ်ဆိုတာ သေချာတယ်။ ဒါကြောင့် ဒီတစ်ခုကို ပယ်လိုက်တယ်။
  2. Playback clock ထဲက အချိန်ကိုက်မှု (race) ပြဿနာ — element ထဲကို data မရောက်သေးခင် play() ကို ခေါ်မိတာ ဖြစ်နိုင်တယ်။ ဖြစ်တော့ ဖြစ်နိုင်ပေမယ့် ဘာလို့ နောက်ပိုင်း chapter တွေမှာပဲ ဖြစ်ရတာလဲ ဆိုတာကို ဒါက မရှင်းပြနိုင်ဘူး။
  3. Safari ရဲ့ autoplay စည်းမျဉ်း — Chrome မှာဆိုရင် page ပေါ်က ဘယ်နေရာကိုမဆို user တစ်ခါ နှိပ်ထားရင် အများအားဖြင့် လုံလောက်တယ်။ Safari ကတော့ အဲဒီလို မဟုတ်ဘူး။ User နှိပ်လိုက်တဲ့အချိန်မှာ ပါဝင်ခဲ့တဲ့ DOM element တွေကိုပဲ ဗီဒီယိုဖွင့်ခွင့် ပေးတာပါ။

နောက်ဆုံးမှာ တတိယတစ်ခုက အဖြေဖြစ်နေတာ။ ဖြစ်ပုံက ဒီလိုပါ။

Editor က React နဲ့ ရေးထားတာပါ။ Chapter ကူးတိုင်း React က chapter အဟောင်းထဲက object တွေကို ဖြုတ် (unmount) ပြီး အသစ်တွေကို တပ် (mount) ပါတယ်။ React အတွက်တော့ ဒါက ပုံမှန်အလုပ်ပါပဲ။ Safari ဘက်က ကြည့်ရင်တော့ ဖွင့်ခွင့်ရထားတဲ့ <video> element က မရှိတော့ဘူး။ အသစ်ရောက်လာတဲ့ element ကို Safari က မသိဘူး။ ကျွန်တော်တို့ clock က အဲဒီ element အသစ်ပေါ်မှာ videoElement.play() ကို ခေါ်တဲ့အခါ user နှိပ်လိုက်တဲ့ လုပ်ဆောင်ချက်ရဲ့ call stack ထဲမှာ မဟုတ်တော့တဲ့အတွက် Safari က promise ကို reject လုပ်လိုက်တယ်။ ရလဒ်ကတော့ user တွေ မြင်ခဲ့တဲ့အတိုင်းပါပဲ — ပထမ frame ပေါ်လာပြီး ဘယ်တော့မှ ဆက်မရွေ့တော့တာ။

သေချာအောင် စစ်ကြည့်ပုံကလည်း အရမ်းရိုးရှင်းပါတယ်။ play() ကနေ ပြန်လာတဲ့ promise rejection ကို log ထုတ်ကြည့်လိုက်တာပါ။ Chrome မှာ resolve ဖြစ်တယ်။ Safari မှာတော့ NotAllowedError တက်လာတယ် — user ပထမဆုံး နှိပ်ပြီးနောက်ပိုင်းမှ mount ဖြစ်လာတဲ့ element တွေမှာပဲ ဖြစ်တာ။

Safari က user နှိပ်လိုက်တဲ့အချိန်မှာ ပါဝင်ခဲ့တဲ့ DOM element ကိုပဲ autoplay ခွင့်ပေးပါတယ်။ Chapter ကူးပြီးတဲ့အခါ React က ခွင့်ပြုချက်မရှိတဲ့ video element အသစ်ကို တပ်လိုက်တော့ play() က NotAllowedError နဲ့ reject ဖြစ်ပြီး ပထမ frame မှာတင် ရပ်သွားပါတယ်။

အဆင့် ၃: ပြင်ဆင်ချက် — ပြီးတော့ revert ခံရခြင်း

Safari က ခွင့်ပြုချက်ကို element တစ်ခုချင်းစီကို ပေးတာဆိုတော့ အဖြေက ရှင်းပါတယ် — element တွေကို မဖျက်ပစ်နဲ့တော့။

ကျွန်တော် VideoElementPool ဆိုတာကို ရေးလိုက်တယ်။ React ရဲ့ အပြင်ဘက်မှာ သီးသန့်ရှိနေတဲ့ စီမံပေးသူလေးတခုပေါ့။ User နှိပ်လိုက်လို့ ခွင့်ပြုချက် ရှိနေတုန်းမှာပဲ <video> element အနည်းငယ်ကို ကြိုဆောက်ထားပြီး chapter တွေ mount လုပ်တဲ့အခါ video object တွေကို ငှားပေးပါတယ်။ React က <video> tag တွေကို ဆောက်လိုက် ဖျက်လိုက် လုပ်မယ့်အစား component က မပျက်စီးဘဲ ရှိနေတဲ့ element တစ်ခုကို ယူ (acquire) တယ်၊ src ကို လဲတယ်၊ ပြီးတော့ သူ့ container ထဲ ထည့်တယ်။ Unmount တဲ့အခါကျ element ကို pool ထဲ ပြန်အပ် (release) ပါတယ်။

class VideoElementPool {
  private pool: PoolItem[] = []
 
  acquire(objectId: string): HTMLVideoElement {
    // Reuse the element already assigned to this object, if any
    const existing = this.pool.find((i) => i.currentObjectId === objectId)
    if (existing) return existing.element
 
    // Otherwise hand out an idle, pre-blessed element
    const idle = this.pool.find((i) => !i.inUse)
    idle.inUse = true
    idle.currentObjectId = objectId
    this.reset(idle.element)
    return idle.element
  }
 
  release(objectId: string) { /* mark idle, detach, reset */ }
}

VideoElementPool က React ရဲ့ အပြင်ဘက်မှာ ရှိပြီး သူ့ video element တွေကို ဘယ်တော့မှ မဖျက်ပါဘူး။ React component တွေက တပ်တဲ့အခါ ခွင့်ပြုချက်ရပြီးသား element တစ်ခုကို ယူပြီး src ကို လဲတယ်၊ ဖြုတ်တဲ့အခါ pool ထဲ ပြန်အပ်တယ်။

2025 အောက်တိုဘာ လကုန်ပိုင်းမှာ ဒါကို release လိုက်တယ်။ အဲဒီနေ့ချင်းမှာပဲ revert ခံလိုက်ရတယ်။

ပထမ version က ရှိပြီးသား video component ရဲ့ အောက်ကနေ element ကို လဲပေးလိုက်ပေမယ့် component ရဲ့ ကျန်တဲ့ အပိုင်းတွေကို မပြင်ခဲ့ဘူး။ သူ့ ref တွေ၊ event listener တွေ၊ audio engine နဲ့ ချိတ်ထားပုံ အားလုံးက element ကို ကိုယ်တိုင် ပိုင်တယ် လို့ပဲ ယူဆထားဆဲ။ Component က အဲဒီလို ယူဆနေတုန်း DOM node ကို pool ထဲ ထည့်လိုက်တော့ Safari bug ထက်တောင် ပိုဆိုးတဲ့ playback ပြဿနာတွေ ပေါ်လာတယ်။

ဒီ revert က ဒီ bug ရဲ့ တကယ့်သင်ခန်းစာကို ကျွန်တော့်ကို သင်ပေးသွားတယ်။ ပြင်ရမှာက "pool တစ်ခု ထည့်လိုက်ရုံ" မဟုတ်ဘူး။ Element ကို ဘယ်သူ ပိုင်သလဲ (ownership) ဆိုတဲ့ ပုံစံကိုကိုက ပြောင်းရမှာ ပါ။ နှစ်ရက်အကြာမှာ ဒုတိယ version ကို release နိုင်ခဲ့တယ်။ React က element တွေကို စီမံတယ်လို့ ယူဆထားတဲ့ code ပေါ်ကို pool ကို ကပ်ထည့်တာမျိုး မဟုတ်တော့ဘဲ video component ရဲ့ lifecycle တစ်ခုလုံးကို pool ကို အခြေခံပြီး ပြန်ရေးလိုက်တာပါ — mount တဲ့အချိန်မှာ element ယူတာ၊ container ထဲ ကိုယ်တိုင် (imperative) ထည့်တာ၊ audio engine နဲ့ player နဲ့ ညှိတာ။ ဒီတစ်ခါတော့ အဆင်ပြေသွားတယ်။

အဆင့် ၄: bug ပြီးတော့ နောက်ထပ် bug

ရက်သက်တစ်ပတ် အနည်းငယ်ကြာတော့ နိုဝင်ဘာလကုန်ပိုင်းမှာ report အသစ် ထပ်ရောက်လာပြန်တယ် — Safari မှာ ဗီဒီယိုတွေ တစ်ခါတစ်လေ ထပ်ရပ်နေပြန်ပြီတဲ့။ အရင်ထက် ရှားပေမယ့် တကယ် ဖြစ်နေတာ။

ဒါက object pool သုံးရင် ပေးရတဲ့ မမြင်ရတဲ့ အဖိုးအခပါ — pool ထဲက object တွေက အရင်သုံးခဲ့သူ ချန်ထားခဲ့တဲ့ state ကို ဆက်သယ်လာတတ်တယ်။ ပြန်သုံးတဲ့ <video> element က အရင်ယူသုံးခဲ့တဲ့ object သတ်မှတ်ထားခဲ့တဲ့ muted နဲ့ loop တန်ဖိုးတွေကို ဆက်ကိုင်ထားတယ်။ ပြီးတော့ Safari မှာ နောက်ထပ် စည်းမျဉ်းတစ်ခု ရှိသေးတယ် — user မနှိပ်ဘဲ code ကနေ play() ကို ခေါ်တဲ့အခါ အသံ ဖွင့်ထားတဲ့ (unmuted) ဗီဒီယိုဆိုရင် ခွင့်ပြုချက်ရထားတဲ့ element ပေါ်မှာတောင် reject လုပ်ပါတယ်။ ဒါကြောင့် element တစ်ခုက အသံဖွင့်ထားလျက်နဲ့ pool ထဲ ပြန်ရောက်ပြီး နောက်တစ်ခါ ပြန်သုံးခံရတဲ့အခါ play() က ဘာမှ မပြောဘဲ reject ဖြစ်သွားတယ်။ Frame က ရပ်သွားပြန်ရော။

အသံဖွင့်ထားလျက်နဲ့ pool ထဲ ပြန်ရောက်သွားတဲ့ video element ကို နောက်ပိုင်းမှာ တခြား object က ယူသုံးပါတယ်။ State ကို reset မလုပ်ဘဲ play() ကို ခေါ်တော့ Safari က reject လုပ်ပါတယ်။ ပြင်ဆင်ချက်က play() မခေါ်ခင်တိုင်း muted နဲ့ loop ကို ပြန်သတ်မှတ်တာပါ။

ပြင်ဆင်ချက်က သေးပေမယ့် လိုက်နာရမယ့် စည်းကမ်းတစ်ခုပါ — pool ထဲက resource မှာ ကျန်နေတဲ့ state ကို ဘယ်တော့မှ ယုံလို့မရပါဘူး။ ဗီဒီယို ဖွင့်လို့ရ/မရကို ဆုံးဖြတ်တဲ့ property တိုင်းကို play မလုပ်ခင် ချက်ချင်း ပြန်သတ်မှတ်လိုက်ပါတယ်။

const video = videoRef.current
if (!video) return
 
if (trims?.length) {
  video.currentTime = trims[0].start
}
video.muted = true   // Safari rejects unmuted programmatic play()
video.loop = false   // don't inherit loop state from the element's previous life
video.play()

(Video clip ရဲ့ အသံ ပျောက်မသွားပါဘူး — mute လုပ်ထားတာ ခဏပါ။ ဗီဒီယို တကယ်စဖွင့်ပြီးတာနဲ့ code က အသံကို ပြန်ဖွင့်ပေးပါတယ်။ Pool ထဲက element တွေက pool ကို ကြိုဆောက်တုန်းက user နှိပ်လိုက်လို့ Safari ရဲ့ ခွင့်ပြုချက် ရထားပြီးသားမို့ အဲဒီလို ပြန်ဖွင့်တာကို Safari က လက်ခံပါတယ်။ Narration ကတော့ သီးခြားဖြစ်ပြီး သူ့ကိုယ်ပိုင် audio engine ကနေ ဖွင့်တာပါ။)

Loop state ကို လိုက်ရှာရင်းနဲ့ loop ခလုတ်ထဲမှာ ပုန်းနေတဲ့ ဒီကိစ္စနဲ့ မဆိုင်တဲ့ တစ်ကြောင်းတည်း bug တစ်ခုကိုပါ တွေ့လိုက်သေးတယ်။

isLoop: o.isLoop ? !o.isLoop : false   // 🐛 always false — the toggle could never turn ON
isLoop: !o.isLoop ? true : false       // ✅ actually toggles

ခက်တဲ့ bug တစ်ခုကြောင့် ဘေးနားက code တွေကိုပါ သံသယနဲ့ အသေအချာ ဖတ်ရတဲ့အခါမှ တွေ့ရတတ်တဲ့ အမျိုးအစားပေါ့။

အပေးအယူ (tradeoff) တွေ

Pool က အလကား ရတာ မဟုတ်ပါဘူး။

  • React နဲ့ ဆန့်ကျင်ပြီး ရေးနေရတယ်။ Element ရဲ့ lifecycle ကို framework အပြင်ဘက်မှာ ကိုယ်တိုင် စီမံနေရတာဖြစ်လို့ နောက်လာမယ့် engineer ကို အံ့ဩသွားစေမယ့် code မျိုးပါ။ ဒါကြောင့် documentation ကို သေချာ ရေးထားပေးခဲ့ပါတယ်။
  • Pool အရွယ်အစားက ခန့်မှန်းထားတာပါ။ ပုံမှန် chapter တစ်ခုအတွက် လောက်မယ့် element အရေအတွက်ကို ကြိုဆောက်ထားပြီး ပုံမှန်မဟုတ်တဲ့ အခြေအနေတွေအတွက် LRU fallback (အကြာဆုံး မသုံးရသေးတာကို ပြန်ယူသုံးတဲ့နည်း) ထားထားတယ်။
  • ကျန်နေတဲ့ state ဆိုတာ အမြဲ သတိထားရမယ့် အန္တရာယ်ပါ — ဒုတိယ freeze က ဒါကို သက်သေပြသွားတယ်။ "Element ယူတဲ့အချိန် ရော play မလုပ်ခင် ရော အကုန် reset လုပ်" ဆိုတဲ့ စည်းကမ်းက အခု မပါမဖြစ် ဖြစ်နေပြီ။

Declarative ပုံစံနဲ့ ပြင်နိုင်ရင် ပိုကောင်းမလား? ကောင်းတာပေါ့။ ဒါပေမယ့် Safari ရဲ့ ခွင့်ပြုချက်က DOM element တစ်ခုချင်းစီနဲ့ ချိတ်နေတယ်၊ React ကတော့ DOM element တွေကို ဖျက်ပြီး ပြန်ဆောက်တတ်တယ်။ ဘယ်လောက်ပဲ လိုချင်လိုချင် ဒီနှစ်ခုက ပြောင်းမှာ မဟုတ်ဘူး။ တစ်ခါတစ်လေ မှန်ကန်တဲ့ engineering ဆုံးဖြတ်ချက်ဆိုတာ သဘောချင်း မတိုက်ဆိုင်တဲ့ system နှစ်ခုကြားမှာ သိပ်မလှပေမယ့် ဘောင်ခတ်ပြီး သေချာထိန်းထားပေးနိုင်တဲ့ တံတားတစ်ခု ထိုးပေးလိုက်တာပါပဲ။

ရလဒ်

  • Chapter ကူးတိုင်း Safari မှာ ဗီဒီယို ရပ်တာ — မဖြစ်တော့ဘူး။ Report လာခဲ့တဲ့ user အခြေအနေတွေမှာ ပြန်စစ်ပြီး အတည်ပြုပြီးပြီ။
  • နောက်ဆက်တွဲ "pool ထဲက state ကျန်ပြီး ရပ်တာ" — မဖြစ်တော့ဘူး။ Play မလုပ်ခင် reset လုပ်တဲ့ စည်းကမ်းက ပြန်မဖြစ်အောင် ကာကွယ်ထားတယ်။
  • ပျက်နေတဲ့ loop ခလုတ် — အပိုဆုအနေနဲ့ ပြင်ပြီးသွားပြီ။

ကျွန်တော် ရခဲ့တဲ့ သင်ခန်းစာများ

  1. "Safari မှာပဲ ဖြစ်တယ်" ဆိုရင် များသောအားဖြင့် policy ကိစ္စပါ၊ parsing ကိစ္စ မဟုတ်ပါဘူး။ Browser တစ်ခုက ဘာမှမပြောဘဲ အလုပ်မလုပ်တော့ရင် codec support ကို မကြည့်ခင် သူ့ရဲ့ permission model ကို အရင် လေ့လာပါ။
  2. ဘယ်အပိုင်းက ပြဿနာလဲဆိုတာ ပုံစံက ပြောပြလာတဲ့အထိ ပြန်ပြန်စမ်းပါ။ "Chapter ကူးမှ ရပ်တယ်" ဆိုတဲ့ ပုံစံက code တစ်ကြောင်းမှ မကြည့်ရသေးခင်ကတည်းက element lifecycle ကို ညွှန်ပြနေခဲ့တယ်။
  3. Revert ဆိုတာလည်း အချက်အလက်တစ်ခုပါ။ ပထမ fix မအောင်မြင်ခဲ့တာ အောက်ခြေက mechanism ကိုပဲ ပြောင်းပြီး အပေါ်က ownership ပုံစံကို မပြောင်းခဲ့လို့ပါ။ ဒုတိယ fix အောင်မြင်ခဲ့တာက revert က အဲဒါကို တိတိကျကျ ပြောပြခဲ့လို့ပါ။
  4. Pool ထဲက resource တွေက သူ့ state အကြောင်း လိမ်ပြောတတ်တယ်။ Object တွေကို ပြန်သုံးမယ်ဆိုရင် ယူတဲ့အချိန်တိုင်း လိုအပ်တဲ့ အခြေအနေတိုင်းကို ပြန်သတ်မှတ်ပါ — ကာကွယ်လိုက်တဲ့ bug က ဘာမှ မပြောဘဲ တိတ်တိတ်လေး ဖြစ်တတ်တဲ့ အမျိုးအစား ဖြစ်နေမှာမို့လို့ပါ။ ဒီ bug ကို ကျွန်တော်တို့ video element ကို pool ထဲ ပြန်ပီးတော့ release မလုပ်ခင်မှာ reset လုပ်ရင်လဲ ရနိုင််မယ်ထင်ပါတယ်။