Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ReactHooksでvideoを乗りこなす

narirou
November 02, 2019

 ReactHooksでvideoを乗りこなす

「映像プレーヤー」を作成したことはあるでしょうか?
作成したことはなくても、おそらく日々様々なプレーヤーを利用していると思います。
HTML5で定義されるHTML Video Elementからは、メディア状態・エラー・DRM複合処理・広告再生・読み込み、などブラウザごとに多種多様なイベントが発生します。プレーヤーはこのイベントに加え、ユーザーから入力される複雑な操作も併せて処理する必要があります。UIや内部要件を実装するには、このイベント制御を適切にハンドルし、のりこなすことが必要不可欠です。
今回のセッションでは、複雑な処理をReactHooksを用いてHTML Video elementを制御する手法を、現在映像サービス「GYAO!」でプロダクションで使用されているプレーヤーを事例に、フロントエンドの観点からご紹介します。

narirou

November 02, 2019
Tweet

More Decks by narirou

Other Decks in Programming

Transcript

  1. MASANARI HAMADA ͳΓΖ͏ @narirow ͓࢓ࣄ Yahoo! JAPAN 2015೥৽ଔೖࣾ GYAO! WebͷϑϩϯτΤϯυΞʔΩςΫνϟ࡮৽

    GYAO! ಈըϓϨʔϠʔ࡮৽ GYAO! iOS SwiftԽ / ϦϑΝΫλϦϯά౳ σβΠϯγεςϜ / JAMStack / WEBඪ४ ౳ʹڵຯ͕͋Γ·͢ ࣥච WEB+DB PRESS ͷϑϩϯτΤϯυ࿈ࡌهࣄͱͯ͠Storybookɺ HeadlessCMSͷهࣄΛࣥච
  2. <video controls autoplay loop> <source src="/media/sample-movie.webm" type="video/webm"> <source src="/media/sample-movie.mp4" type="video/mp4">

    ͋ͳͨͷϒϥ΢β͸ಈը࠶ੜͰ͖·ͤΜ </video> ୯७ͳ࠶ੜ͸ͨͬͨ͜Ε͚ͩɻϒϥ΢β͸ݡͯ͘ɺΦϓγϣϯͷtype ଐੑ͔ΒϑΝΠϧΛμ΢ϯϩʔυͯ͠࠶ੜ͢Δ͔Λ൑அ͠·͢ɻ ଐੑʹΑͬͯॳظ஋΍ಈ࡞ΛࢦఆͰ͖ɺΠϯλʔϑΣʔε͔ΒԻྔɺ όοϑΝϦϯάɺ࠶ੜҐஔͳͲΛ੍ޚͰ͖·͢ɻ
  3. ࠶ੜʹؔ܎͢Δٕज़ ετϦʔϛϯά࠶ੜ MediaSourceExtension HLS (HTTP Live Streaming) MPEG-DASH ίϯςϯπอޢ Encrypted

    Media Extensions DRMϥΠηϯε ࣈນ WEB VTT ಈը޿ࠂ VAST / VMAP (IAB) ࠶ੜʹؔ࿈͢Δٕज़͸ɺ௿ϨΠϠʔΛؚΈɺଟ༷ͳ෼໺ʹΘͨΓ·͢ɻ
  4. ࠶ੜͷΠϕϯτ (HTML Media Events) ❖ loadedmetadata…. ❖ loadeddata……….. ❖ canplay…………….

    ❖ canplaythrough….. ❖ waiting……………. ❖ etc…. ಈըͷϝλσʔλ͕ಡΈࠐ·Εͨ࣌ ಈըͷॳظϑϨʔϜ͕ಡΈࠐ·Εͨ࣌ ࠶ੜ͕Մೳͱͳͬͨ࣌ όοϑΝϦϯάΛߟྀͯ͠࠶ੜ͕Մೳͱͳͬͨ࣌ ࠶ੜʹඞཁͳσʔλ͕ෆ଍͠࠶ੜ͕ఀࢭͨ࣌͠ ͜ΕΒͷٕज़Λ΋ͬͯ಺෦ͰߦΘΕͨॲཧ͸ɺө૾࠶ੜʹؔ͢ΔΠϕ ϯτͱͯ͠ɺදࣔॲཧܥʹ௨஌͞Ε·͢ɻ
  5. ࠶ੜٕज़ ө૾ঢ়ଶ ෳ਺ͷ࠶ੜΠϕϯτ Video Element ঢ়ଶ (STORE) มߋ௨஌ (ACTION) Player

    UI ࠓճ͸ɺReactͰө૾(video)ͷঢ়ଶΛద੾ʹѻ͏ͨΊʹɺVideo ElementΛঢ়ଶͱΞΫγϣϯʹ෼཭͠ɺͦͷ৘ใͷΈࢀর͢ΔΑ͏ʹม ߋ͠·ͨ͠ɻ
  6. ࠶ੜٕज़ ө૾ঢ়ଶ ෳ਺ͷ࠶ੜΠϕϯτ Video Element ঢ়ଶ (STORE) มߋ௨஌ (ACTION) Player

    UI ͜ͷ࣌ɺSTORE(useContext)ͱͯ͠ѻ͏ঢ়ଶʹ͸ɺUIʹඞཁ ࠷খݶͳ஋ʹߜΓ·͢ɻ
  7. // ࠶ੜऴྃ useEffect(() => { const onEnded = () =>

    { setEnded(true); }; player.on('ended', onEnded); return () => { player.off('ended', onEnded); }; }, [player]); // ϦϓϨΠ useEffect(() => { if (!ended) return () => {}; const onReplay = () => { setSessionId(generateSessionId()); setEnded(false); player.off('playing', onReplay); }; player.on('playing', onReplay); return () => { player.off('playing', onReplay); }; }, [ended, player]);
  8. ࠶ੜٕज़ ө૾ঢ়ଶ ෳ਺ͷ࠶ੜΠϕϯτ Video Element ঢ়ଶ (STORE) มߋ௨஌ (ACTION) Player

    UI ࠶ϨϯμϦϯά ࠶ϨϯμϦϯά ࠶ϨϯμϦϯά ❌ Context͸ศརͰ͕͢ɺ͢΂ͯͷ৘ใΛ΋ͨͤͯ͠·͏ͱɺ ߋ৽ස౓ͷߴ͍ঢ়ଶʹҾ͖ͮΒΕɺProvidor഑ԼͷίϯϙʔωϯτͰ࠶ ϨϯμϦϯά͕ൃੜ͠·͢ɻ
  9. interface InitialState = { currentTime: number //- ݱࡏͷ࠶ੜҐஔ(࣌ؒ) media: Media

    //- ө૾ͷϝλ৘ใ } ݱࡏͷ࣌ؒ͸ө૾͕࠶ੜ͞ΕΔͨͼʹසൟͳߋ৽͕૸Γ·͕͢ɺ ө૾ͷϝλσʔλ͸ॳճͷΈσʔλΛऔಘͯ͠ɺ࠶ಡΈࠐΈ͕૸ Βͳ͍ݶΓ͸มԽ͠ͳ͍஋Ͱ͢ɻ ྫ͑͹ɺҎԼͷΑ͏ͳঢ়ଶΛ΋͍ͬͯͨ৔߹
  10. ঢ়ଶ (STORE) MediaContext (ө૾ϝλ) media diration PlaybackContext (ө૾ͷ࠶ੜ৘ใ) currentTime, paused,

    ended, seeking, waiting, buffer PlaybackSettingContext (ө૾ͷ࠶ੜ৘ใ) muted, playbackRate, videoQuality, isFullscreen ߋ৽ස౓ ߴ ߋ৽ස౓ ௿ ෼ղ
  11. MediaContext (ө૾ϝλ) PlaybackContext (ө૾ͷ࠶ੜ৘ใ) PlaybackSettingContext (ө૾ͷ࠶ੜ৘ใ) ߋ৽ස౓ ߴ ߋ৽ස౓ ௿

    ස౓ͱ໾ׂ͝ͱʹ෼ׂͨ͜͠ͱʹΑΓɺ ContextʹΑΔ࠶ϨϯμϦϯάίετΛ཈͑Δɻ γʔΫόʔɺ࣌ؒදࣔɺόοϑΝදࣔ λΠτϧදࣔɺαϜωΠϧ ઃఆ
  12. 2: useMemoΛڬΉ ෛՙͷߴ͍ίϯϙʔωϯτʹͷΈඞཁͳ৘ใΛ౉͠ϝϞԽ͠·͢ɻ ਌ͷϨϯμϦϯά͸࣮ߦ͞Ε·͕͢ɺuseMemoͷೖྗ͕ಉ͡৔߹ ʹɺࢠίϯϙʔωϯτ͸ϨϯμϦϯά͞Εͳ͘ͳΓ·͢ɻ function MediaDitails () { const

    { media } = useContext(PlayerContext); return <MediaInfomation theme={theme} /> } const MediaInfomation = useMemo(media => { // ԼهʹϨϯμϦϯάίετͷߴ͍ॲཧΛهड़ return <MediaInfomation title={media.id} />; }, [media]);
  13. export const useUserAttention = attentionDistractedIntervalMillis => { // - ΠϕϯτϦεφͷొ࿥(লུ)…

    const userAttention = useMemo(() => (isMouseHovered && isMouseMoving) || isTouchFocused, []); return { userAttention, containerElementRef, }; }; useUserAttension Ϣʔβʔ͕ϓϨʔϠʔʹ஫ҙΛҾ͍͍ͯΔ͔
  14. export const ControlContainer = () => { const { seeking,

    paused } = useContext(PlaybackContext); const { userAttention, initialAttention } = useContext(PlayerFrameContext); const className = classNames('gyp-control-container', { 'is-active': userAttention || initialAttention || seeking || paused, }); return h` <div className=${className}> ... </div> `; }; ControlContainer (ίϯτϩʔϧόʔཁૉ) ෼ׂ͞ΕͨContextܦ༝ͰΧελϜϑοΫ͔Βͷ஋Λऔಘ ෳࡶͳॲཧͰ͕͢ɺͳͥίϯτϩʔϧόʔ͕දࣔ͞ΕΔ͔ ໌ࣔతʹهड़Ͱ͖Δ
  15. ΑΓਂ͘஌Δʹ͸ (ࢀߟจݙ) The Video Embed element https://developer.mozilla.org/ja/docs/Web/HTML/Element/video ಈը | Web

    | Google Developers https://developers.google.com/web/fundamentals/media/video?hl=ja Preventing rerenders with React.memo and useContext hook https://github.com/facebook/react/issues/15156 Building Your Own Hooks https://reactjs.org/docs/hooks-custom.html#extracting-a-custom-hook HTML Media Events https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Media_events