Reactで動画プレイヤーを作成するならreact-playerがおすすめ!
こんにちは、Web エンジニアのまさぴょんです!
今回は、React で動画プレイヤーを作成するなら react-player が簡単に実装できて、おすすめなのでご紹介していきます 🐱
React 動画ライブラリ比較結果 (2024 年 9 月時点)
React の動画プレイヤー・ライブラリで調べて出てきたものを比較してみました 👀
候補は「React 動画ライブラリ」で調べてでてきた次の 5 つです 🙏
- react-player
- mux-player-react
- react-video-js-player
- video-react
- video.js
結論:react-player がよさそう
判断基準は、次のとおりです 👀✨
- 要件を満たす機能を備えていること。
- React 対応のライブラリであること。
- 正式リリースがされており、安定版であること。
- 定期的にメンテナンスがされていること。
- npm trends や GitHub Star などを参照して、使用率や人気がある状態であることが望ましい。
react-player とは?
react-player は、React アプリケーションで動画や音声などのメディアを再生するためのオープンソースの React コンポーネントです。
YouTube、Vimeo、Mux、Twitch、SoundCloud、Facebook など、さまざまなプラットフォームからのコンテンツをサポートしています。
また、シンプルな API と豊富なプロパティを提供しており、メディアプレーヤーのカスタマイズや制御が簡単です 💪🥺🔥
npm や GitHub は、次のとおりです 👀✨
npm: react-player
GitHub: react-player
公式 Doc がない代わりに、GitHub の README.md にて設定できる Option に関する説明が掲載されています 👀✨
React Player デモ環境
react-player のデモで、いろいろな動画タイプや、パラメーターを試すことができます 🙏
react-player で動画プレイヤーを作成する
それでは、実際に react-player で動画プレイヤーを作成していきます。
react-player をインストール
npm install react-player
yarn add react-player
シンプルな Video Player を実装してみる
試しに、シンプルな Video Player を実装してみると、
これぐらいのCode量で、簡単な Video Playerが実装できます👀✨
import ReactPlayer from "react-player";
interface SimpleVideoPlayerProps {
videoUrl: string;
isLoop?: boolean;
isAutoPlay?: boolean;
isControls?: boolean;
}
export const SimpleVideoPlayer = ({
videoUrl,
isLoop,
isAutoPlay,
isControls,
}: SimpleVideoPlayerProps) => {
return (
<ReactPlayer
url={videoUrl}
width={"100%"}
height={"100%"}
playing={isLoop} // 自動再生
loop={isAutoPlay} // ループ再生
controls={isControls} // 動画の操作が可能かどうか
/>
);
};
React Player の Option について
react-player の README には、渡せる Props や Callback 系などの設定値の一覧がまとまっているので、これを見れば何ができるのかは、把握できます🙆♂️
また、Youtube, Vimeo, Mux などの各動画配信サービスの設定プロパティにも対応しているのも、Goodなポイントです🙌
Props
主な Props は、次のとおりです👀✨
Prop | Description | Default |
---|---|---|
url |
再生するビデオまたは曲のURL。array または MediaStream オブジェクトを指定可能。 |
|
playing |
true または false を設定してメディアの再生や一時停止を行う。 |
false |
loop |
true または false を設定してメディアをループ再生する。 |
false |
controls |
ネイティブプレーヤーコントロールを表示するかどうかを設定。Vimeoビデオの場合、コントロールを非表示にするにはビデオ所有者が有効にする必要がある。 | false |
light |
true に設定するとビデオのサムネイルのみが表示され、クリックでフルプレーヤーが読み込まれる。プレビュー画像をオーバーライドするには画像URLを渡す。 |
false |
volume |
プレーヤーの音量を 0 から 1 まで設定。null の場合、全プレーヤーでデフォルトの音量が使用される。 |
null |
muted |
プレーヤーをミュートにする。volume が設定されている場合のみ機能。 |
false |
playbackRate |
プレーヤーの再生速度を設定。YouTube、Wistia、およびファイルパスでのみサポートされている。 | 1 |
width |
プレーヤーの幅を設定。 | 640px |
height |
プレーヤーの高さを設定。 | 360px |
style |
ルート要素に inline styles を追加。 |
{} |
progressInterval |
onProgress コールバックの間隔をミリ秒単位で設定。 |
1000 |
playsinline |
対応している場合に playsinline 属性を適用。 |
false |
pip |
true または false を設定してピクチャー・イン・ピクチャーモードを有効化または無効化する。ファイルURLで再生している場合のみ、特定のブラウザでサポートされている。 |
false |
stopOnUnmount |
pip を使用している場合、stopOnUnmount={false} を使用してReactPlayerがアンマウントされてもピクチャー・イン・ピクチャーモードで再生を続けるようにする。 |
true |
fallback |
遅延読み込みを使用している場合に使用するフォールバック要素またはコンポーネント。 | null |
wrapper |
コンテナ要素として使用する要素またはコンポーネント。 | div |
playIcon |
ライトモードで再生アイコンとして使用する要素またはコンポーネント。 | |
previewTabIndex |
ライトモードで使用するタブインデックスを設定。 | 0 |
config |
各プレーヤーの設定を上書きするオプションを指定。config prop を参照。 |
Callback Props
主な Callback Props は、次のとおりです👀✨
Prop | Description |
---|---|
onReady |
メディアがロードされ再生の準備が完了した際に呼び出される。playing が true に設定されている場合、メディアは即座に再生される。 |
onStart |
メディアの再生が開始されたときに呼び出される。 |
onPlay |
メディアが再生または一時停止後やバッファリング後に再開されたときに呼び出される。 |
onProgress |
played と loaded の進捗状況を割合で提供するコールバック。秒単位で playedSeconds と loadedSeconds も提供される。例: { played: 0.12, playedSeconds: 11.3, loaded: 0.34, loadedSeconds: 16.7 }
|
onDuration |
メディアの長さ(秒単位)を含むコールバック。 |
onPause |
メディアが一時停止されたときに呼び出される。 |
onBuffer |
メディアがバッファリングを開始したときに呼び出される。 |
onBufferEnd |
メディアのバッファリングが完了したときに呼び出される。ファイル、YouTube、Facebookで動作する。 |
onSeek |
seconds パラメータでメディアがシークされたときに呼び出される。 |
onPlaybackRateChange |
プレーヤーの再生速度が変更されたときに呼び出される。YouTube、Vimeo、Wistia、およびファイルパスでサポートされている。 |
onPlaybackQualityChange |
プレーヤーの再生品質が変更されたときに呼び出される。YouTubeでのみサポートされている(有効にした場合)。 |
onEnded |
メディアの再生が終了したときに呼び出される。loop が true に設定されている場合は発火しない。 |
onError |
メディアの再生中にエラーが発生したときに呼び出される。 |
onClickPreview |
ユーザーが light モードのプレビューをクリックしたときに呼び出される。 |
onEnablePIP |
ピクチャー・イン・ピクチャーモードが有効になったときに呼び出される。 |
視聴ログを定期的に送信する Youtube Video Player を実装する
続いて、視聴ログを定期的に送信する Youtube Video Player を実装してみます。
実際に実装した画面は、次のような感じです👀✨
import { Fragment, useState, useRef, useEffect } from "react";
import ReactPlayer from "react-player";
import type { OnProgressProps } from "react-player/base";
// ------------------------ ここから Next.js SSR 対応のコード ------------------------
// // NOTE: Next.js対応
// import dynamic from "next/dynamic";
// import type ReactPlayer from "react-player";
// // NOTE: Next.js での動的インポート対応
// // ReactPlayer を動的にインポート (SSR 無効化)
// const Player = dynamic(() => import("react-player"), { ssr: false });
// ------------------------ ここまで Next.js SSR 対応のコード ------------------------
interface YoutubeVideoPlayerProps {
videoKey: string; // youtube video key
autoPlay?: boolean; // 自動再生フラグ
originUrl?: string; // ルートURL
watchLogIntervalSeconds?: number; // 視聴ログ送信間隔(秒)
}
// TODO: 実際に実装する際は Logic は Custom Hook に切り出す
export const YoutubeVideoPlayer = ({
videoKey,
autoPlay,
originUrl,
watchLogIntervalSeconds,
}: YoutubeVideoPlayerProps) => {
// プレーヤーの参照を保持する Ref
const playerRef = useRef<ReactPlayer>(null);
// 視聴ログ送信間隔の Timer Ref
const intervalRef = useRef<NodeJS.Timeout>();
// 再生中フラグ
const [isPlaying, setIsPlaying] = useState(false);
// progress の最新値を保持するための Ref を追加
const progressRef = useRef<OnProgressProps>({
played: 0,
playedSeconds: 0,
loaded: 0,
loadedSeconds: 0,
});
const handlePlayOn = () => {
setIsPlaying(true);
};
const handlePlayOff = () => {
setIsPlaying(false);
};
// 再生中に定期実行されるコールバック
const handleProgress = (progress: OnProgressProps) => {
console.log("onProgress Called", progress);
// 最新の progress (進捗状況)を Ref に保存, setInterval での参照のため。
progressRef.current = progress;
};
// 視聴ログを送信する関数
const sendVideoWatchLog = () => {
console.log("sendVideoWatchLog Called");
console.log("playerRef", playerRef);
// プレーヤーが存在する場合のみ処理を実行
if (playerRef.current) {
// プレイヤーの再生位置を取得(秒), playedSeconds と同じ値
const currentTime = playerRef.current?.getCurrentTime();
console.log(`再生位置: ${currentTime} 秒`);
console.log("progress", progressRef.current);
const {
played, // 動画の再生済み部分を全体の割合で示した値 (0〜1)
playedSeconds, // 動画の再生済み時間を秒単位で示した値 (秒)
loaded, // 動画の読み込み(バッファリング)済み部分を全体の割合で示した値 (0〜1)
loadedSeconds, // 動画の読み込み済み時間を秒単位で示した値 (秒)
} = progressRef.current;
console.log(
"currentTime === playedSeconds",
currentTime === progressRef.current.playedSeconds
);
// 再生済みのパーセンテージを表示
console.log(`再生位置(%): ${(played * 100).toFixed(2)}%`);
// 再生済みの時間を表示
console.log(`再生位置(秒): ${playedSeconds.toFixed(2)}秒`);
// 読み込み済みのパーセンテージを表示
console.log(`読み込み済み(%): ${(loaded * 100).toFixed(2)}%`);
// 読み込み済みの時間を表示
console.log(`読み込み時間(秒): ${loadedSeconds.toFixed(2)}秒`);
// 送信するデータを作成
const logData = {
currentTime, // 再生位置
played, // 再生済みの割合 (0〜1)
playedSeconds, // 再生済みの時間 (秒)
loaded, // 動画の読み込み済み割合 (0〜1)
loadedSeconds, // 動画の読み込み済み時間 (秒)
timestamp: new Date().toISOString(),
};
// TODO: 視聴ログをサーバーに送信する処理を実装
console.log("視聴ログを送信:", logData);
}
};
// 再生中なら、視聴ログ送信間隔ごとに視聴ログを送信する
useEffect(() => {
if (isPlaying && watchLogIntervalSeconds) {
const interval = watchLogIntervalSeconds * 1000;
// 再生中ならタイマーを開始
intervalRef.current = setInterval(() => {
// 設定された視聴ログ送信間隔ごとに視聴ログを送信する
sendVideoWatchLog();
}, interval);
} else {
// 再生中でないならタイマーをクリア
clearInterval(intervalRef.current);
intervalRef.current = undefined;
}
// クリーンアップ処理
return () => {
clearInterval(intervalRef.current);
};
}, [isPlaying, watchLogIntervalSeconds]);
return (
<Fragment>
<ReactPlayer
ref={playerRef}
url={`https://www.youtube.com/watch?v=${videoKey}`}
width={"100%"}
height={"100%"}
playing={autoPlay} // 自動再生
loop={false} // ループ再生
controls={true} // 動画の操作が可能かどうか
onStart={handlePlayOn} // 再生開始時 Callback Func
onPlay={handlePlayOn} // メディアが一時停止またはバッファリング後に再生を開始または再開する時 Callback Func
onPause={handlePlayOff} // 一時停止時 Callback Func
onEnded={handlePlayOff} // 再生が終了した時 Callback Func
progressInterval={1000} // 進捗状況を取得する間隔(秒), onProgressの実行間隔, Default: 1000(1秒)
onProgress={handleProgress} // 再生中に定期実行される Callback Func
// 各プレーヤーごとの独自設定
config={{
/**
* YouTubeのプレーヤーのパラメータ設定 Docs
* https://developers.google.com/youtube/player_parameters?playerVersion=HTML5&hl=ja
*/
youtube: {
playerVars: {
autoplay: autoPlay ? 1 : 0, // 自動再生
playsinline: 1, // iOS 上の HTML5 プレーヤーで動画をインライン再生する
origin: originUrl, // ルートURL指定
rel: 0, // パラメータの値が 0 に設定されている場合、関連動画は表示されません。
},
},
}}
/>
<p className="flex items-center justify-center mt-5 text-lg">
再生中: {isPlaying ? "Yes" : "No"}
</p>
</Fragment>
);
};
再生中なら、視聴ログ送信間隔ごとにsendVideoWatchLog()
を実行して、視聴ログを送信するようにしています!
擬似ライブ(時間指定の擬似的・生配信)の Video Player を実装する
擬似的なライブ配信動画の Video Player を作成する場合は、次のような設定になります。
import { Fragment, useRef } from "react";
import ReactPlayer from "react-player";
// ------------------------ ここから Next.js SSR 対応のコード ------------------------
// // NOTE: Next.js対応
// import dynamic from "next/dynamic";
// import type ReactPlayer from "react-player";
// // NOTE: Next.js での動的インポート対応
// // ReactPlayer を動的にインポート (SSR 無効化)
// const Player = dynamic(() => import("react-player"), { ssr: false });
// ------------------------ ここまで Next.js SSR 対応のコード ------------------------
interface PseudoLiveStreamingVideoPlayerProps {
videoUrl: string;
}
// TODO: 実際に実装する際は Logic は Custom Hook に切り出す
// 擬似ライブ動画再生用のコンポーネント
export const PseudoLiveStreamingVideoPlayer = ({
videoUrl,
}: PseudoLiveStreamingVideoPlayerProps) => {
// プレーヤーの参照を保持する Ref
const playerRef = useRef<ReactPlayer | null>(null);
// 動画を指定した位置から再生するFunc
const offSetStart = (offSetSeconds: number) => {
console.log("offSetStart Called");
if (playerRef && playerRef.current) {
// 動画の再生位置を変更する (秒指定)
playerRef.current.seekTo(offSetSeconds, "seconds");
}
};
// プレーヤーの準備完了時 Call
const onReady = () => {
console.log("onReady Called");
// TODO: VideoApi.getOffset() を Call して 動画の再生位置(開始時刻) offset を取得する
const offSetSeconds = 12;
offSetStart(offSetSeconds);
};
// シーク(再生位置を変更した)時 Call
const onSeek = () => {
console.log("onSeek Called");
console.log("再生位置の変更を検知");
};
return (
<Fragment>
<ReactPlayer
ref={playerRef}
url={videoUrl}
width={"100%"}
height={"100%"}
playing={true} // 自動再生する
loop={false} // ループ再生はしない
controls={false} // 動画は操作できない
onReady={onReady} // プレーヤーの準備完了時 Call
onSeek={onSeek} // シーク(再生位置を変更した)時 Call
/>
</Fragment>
);
};
実装のポイントは、次のとおりです。
-
onReady()
で、Video Player の準備が整ったら、offSetStart()
を Call する。 -
playerRef.current.seekTo()
で動画の再生位置を設定して、動画を開始する。
まとめ
react-player は、動画プレイヤーを簡単に実装できオプションも豊富なので、おすすめです。
参考・引用
Discussion