電子オーディオおよびビデオプレーヤーに基づいて、

序文

電子ベースのオーディオおよびビデオプレーヤーのデスクトップアプリケーションに従事することを気まぐれにので、私は、自分の仕事の思想に従事するためにいくつかの時間前に、フロントエンドエンジニアです。開発の数ヶ月後、最終的に我々は、ほとんどの機能を実現し、私は一度に前期の仕事、および開発のさまざまなを要約したい甌穴に会った、希望はあなたが電子に従事したいデスクトップソフトウェア用に開発することができます友人の少し助けて。

ビデオやオーディオプレーヤーのデスクトップアプリケーションの理由を行うことを選択

最初に私は、WebサイトやWebアプリケーションの種類になる予定だったが、どのような種類のものが一般的であるため、フロントエンドエンジニア、ウェブサイトやWebアプリケーションと何として、私の場所を引き付けることができ、ステレオタイプなので、私が欲しかったですビデオやオーディオプレーヤーを作るために電子の使用は、別の何かを指摘します。そこに多くのオンラインビデオプレーヤーがありますが、それらのプレーヤーは基本的にCで書かれており、プレイヤーのうち、電子使用していませんが。

技術の選択

私が使用する主な技術は、DPlayer電子、ノード、VUE、エクスプレス、HTML5関連技術です。電子は、主に環境のオーディオおよびシステムリソース(コール・エクスプローラ、ブラウザなど)やデスクトップアプリケーションにパッケージ化へのAPIアクセスを提供するために必要なビデオプレーヤーを構築するために使用されます。実際には、はっきり電子は、ブラウザとサーバのバックエンドの組み合わせに相当するが、電子は、伝統的なブラウザの境界を壊し、基盤となるシステムリソースAPIを呼び出して提供する、などのカメラ、マイク、として、開発者がシステムリソースを使用することができます。同時に、開発者は、電子内のノードモジュールを呼び出すというように、背景とを構築することができます。電子プロセスの2種類があり、一方が他方のメインプロセスで、レンダリング処理です。一つだけのマスタープロセスは、それらのウィンドウを管理し、基本的なシステムリソースを呼び出すための責任があります。レンダリングプロセスは、ページのレンダリングを担当し、複数持つことができます。ファイルに関連するオーディオおよびビデオプレーヤーが操作を読んでいるので二つのモジュールが不可欠であるように、ノードは、主に、FSおよびこれら二つのモジュールのパスを使用していました。Vueがインターフェイスを構築するための責任があります。Expressは、アプリケーションでのミニ背景を構築するために使用され、フォームにビデオストリームを読み出すための責任があり、その後、フロントエンドのインターフェイスに戻ります。ドラッグのHTML5主な用途と技術のAPI、フルスクリーンAPI、通知メッセージ通知をドロップします。DPlayerは、全体のオーディオおよびビデオプレーヤーのコアコンポーネントであるオーディオとビデオを再生するための責任があります。

機能が達成されました

  • ビデオ再生には:今、そのようなMP4、WebMの、MKV、AVI、WMV、FLV、RMVB、などなど、ほとんどのビデオフォーマットをサポートし、フォローアップは、より多くのビデオフォーマットを追加します
  • オーディオの再生には:今、などMP3、など、ほとんどのオーディオフォーマットをサポートし、フォローアップは、より多くのオーディオフォーマットを追加します
  • スキン:この機能は、他のソフトウェアの皮に似ていますが、ユーザーが自分の好みに合わせてさまざまなテーマのスキンを選択することができます
  • 歴史:オーディオおよびビデオプレーヤーが自動的にその時点で、ユーザは、オーディオやビデオの再生など、ビデオやオーディオを再生されています記録します
  • メモリー機能:ユーザーがアプリケーションを、再び開いたユーザは、皮膚のテーマを変更したオーディオおよびビデオプレーヤーアプリケーションされて閉じた後、オーディオおよびビデオプレーヤーが自動的に、皮膚のテーマを置き換えるために、このようなユーザーとしての修正構成及び動作を、保存されます。ビデオやオーディオの加速操作のユーザーが記憶され、ビデオやオーディオをユーザーがクリックするには、再びユーザの操作を返します。
  • 再生モード:5つの主要な再生モードは、シングルプレイヤー、洋楽シングルループ、プレーの順番、ランダム再生、それぞれがあります
  • ソートモード:ソートモードの5種類があり、すなわち、ランダムな順序で、降順の時間をソートでデフォルトの並べ替え、名前の並べ替え
  • 主な機能:アプリケーション・インタフェースは常にトップのまま
  • 加減速機能:オーディオおよびビデオ再生の加速または減速
  • ドラッグ&ファイルやフォルダをドロップ:ユーザーは、オーディオおよびビデオプレーヤーにファイルやフォルダをドラッグ&ドロップすることができ、アプリケーションがファイルを除外しますが再生できません
  • フルスクリーン機能:アプリケーションのフルスクリーン機能は、ここではHTML5フルスクリーンAPIを使用していない、提供フルスクリーンAPIの電子の使用であり、
  • メニュー機能を右クリックします。機能は、すでにフォローアップを実施し実現していない、右クリックメニューのほとんどを達成しています

達成するために、オーディオとビデオの再生

オーディオとビデオの再生が実現しています。MP4、WebMの、OGG、だけでなく、現在主流のビデオフォーマットのAVI、MKV、WMVおよび他のビデオフォーマット:最初に私が直接提供HTML5タグを考えていたが、この偉大なラベルの制限、それが唯一の3つのビデオフォーマットをサポートしています。それから私はMP4をトランスコードしていない人たちの考える、WebMの、OGGビデオフォーマットが、電子が行くffmepgするために、このツールのパッケージをパッケージ化するつもりはないので、これはそれぞれを必要とするとき、トランスコードするffmepg必要がありますオーディオおよびビデオプレーヤーの使用。手動ffmepg環境をインストールして設定する必要がありますが、このアプローチは明らかに受け入れられません。トランスコード処理に時間がかかりますが、一度、いくつかのGビデオに会った人たちは、少なくとも、トランスコードには数分かかるし、その後、それはユーザーにとって非常に貧弱なユーザーエクスペリエンスで、ユーザーのアクションに応答します。私は、の使用を映像を読み出す置くために必要な、フロントエンドインターフェースを表現するための要求を受信したときに、ミニチュア特急電子サーバを構築するために使用することを選択したことが電子環境であるとして、ストリームとしてフロントエンドに返されますそれはlocalhostですので、あなたはすぐにネイティブプレイヤーの経験に近づいて、ユーザのアクションに応答することができます。

コード:

let pathSrc = req.query.video;
let stat = fs.statSync(pathSrc);
let fileSize = stat.size;
let range = req.headers.range;
if (range) {
    //有range头才使用206状态码
    let parts = range.replace(/bytes=/, "").split("-");
    let start = parseInt(parts[0], 10);
    let end = parts[1] ? parseInt(parts[1], 10) : start + 999999;

    // end 在最后取值为 fileSize - 1 
    end = end > fileSize - 1 ? fileSize - 1 : end;

    let chunksize = (end - start) + 1;
    let file = fs.createReadStream(pathSrc, {
        start,
        end
    });
    let head = {
        'Content-Range': `bytes ${start}-${end}/${fileSize}`,
        'Accept-Ranges': 'bytes',
        'Content-Length': chunksize,
        'Content-Type': 'video/mp4',
    };
    res.writeHead(206, head);
    file.pipe(res);
} else {
    let head = {
        'Content-Length': fileSize,
        'Content-Type': 'video/mp4',
    };
    res.writeHead(200, head);
    fs.createReadStream(pathSrc).pipe(res);
}


達成するために、メニューを右クリックし、

私は練習を始めた右のメニューは、動的DOMによって生成されたイベントを聞いて、右、その後、ページに挿入されています。しかし、このアプローチは現実的ではありません。得られたポップアップメニューは、ユーザの位置の近傍に表示する必要がマウスをクリックするので、ユーザがアプリケーションの途中であってもよい場合、マウスが表示され、それはそうで左上隅、右上隅、であってもよいです。それはDOM、右クリックメニューを使用して生成されるため、ドキュメントの範囲を超えてレンダリングすることができない、そうでない場合はスクロールバーが存在します。ときに、ユーザーのマウス位置インターフェース境界多数の結果を得るために、一連の計算を通過する必要がある、マウスの位置の上、または左上隅などの下に表示されますので、中の電子、右クリックメニューを計算する必要がありますこのような複雑な計算がページカトンを引き起こす可能性があるため、レンダリングプロセスは、明らかに現実的ではありません。だから私は、同じことがまた、DOM操作の量を減らしているが、右クリックメニューの電子メニューモジュールによって生成されることは黒と白で、レンダリング処理の負担を軽減、メインプロセスにポップアップメニューを生成し、電子メニューモジュールに戻っていました、スタイルが期待される結果と一致していなくてもよいです。しかし、2種類の右クリックメニューによって生成されたトレードオフは、電子メニューモジュールを使用すると、コンテキストメニューが最良の選択肢であるが生成されます。

コード

 let contextMenuTemplate = [
        {
          label: "打开",
          submenu: [
            {
              label: "打开文件",
              click() {
              }
            }
          ]
        },
        {
          type: "separator"
        },
        {
          label: "窗口置顶",
          submenu: [
            {
              label: this.isAlwaysOnTop ? "     从不" : "√   从不",
              click: () => {
                this.setAlwaysOnTop(false);
              }
            },
            {
              label: this.isAlwaysOnTop ? "√   始终" : "     始终",
              click: () => {
                this.setAlwaysOnTop(true);
              }
            }
          ]
        },
        {
          type: "separator"
        },
        {
          label: "播放列表"
        },
        {
          label: "播放顺序",
          submenu: [
            {
              label: this.playMode == 1 ? "√ 单个播放" : "   单个播放",
              click: () => {
                this.setPlayMode(1);
              }
            },
            {
              label: this.playMode == 2 ? "√ 单个循环" : "   单个循环",
              click: () => {
                this.setPlayMode(2);
              }
            },
            {
              label: this.playMode == 3 ? "√ 循环列表" : "   循环列表",
              click: () => {
                this.setPlayMode(3);
              }
            },
            {
              label: this.playMode == 4 ? "√ 顺序播放" : "   顺序播放",
              click: () => {
                this.setPlayMode(4);
              }
            },
            {
              label: this.playMode == 5 ? "√ 随机播放" : "   随机播放",
              click: () => {
                this.setPlayMode(5);
              }
            }
          ]
        },
        {
          type: "separator"
        },
        {
          label: "声音",
          submenu: [
            {
              label: this.volumePercent == 0.1?"√ 10%":"   10%",
              click:()=>{
                let inWidth = 0.1*62
                this.setInWidth(inWidth)
              }
            },
            {
              label: this.volumePercent == 0.2?"√ 20%":"   20%",
              click:()=>{
                let inWidth = 0.2*62
                this.setInWidth(inWidth)
              }
            },
            {
              label: this.volumePercent == 0.3?"√ 30%":"   30%",
              click:()=>{
                let inWidth = 0.3*62
                this.setInWidth(inWidth)
              }
            },
            {
              label: this.volumePercent == 0.4?"√ 40%":"   40%",
              click:()=>{
                let inWidth = 0.4*62
                this.setInWidth(inWidth)
              }
            },
            {
              label: this.volumePercent == 0.5?"√ 50%":"   50%",
              click:()=>{
                let inWidth = 0.5*62
                this.setInWidth(inWidth)
              }
            },
            {
              label: this.volumePercent == 0.6?"√ 60%":"   60%",
              click:()=>{
                let inWidth = 0.6*62
                this.setInWidth(inWidth)
              }
            },
            {
              label: this.volumePercent == 0.7?"√ 70%":"   70%",
              click:()=>{
                let inWidth = 0.7*62
                this.setInWidth(inWidth)
              }
            },
            {
              label: this.volumePercent == 0.8?"√ 80%":"   80%",
              click:()=>{
                let inWidth = 0.8*62
                this.setInWidth(inWidth)
              }
            },
            {
              label: this.volumePercent == 0.9?"√ 90%":"   90%",
              click:()=>{
                let inWidth = 0.9*62
                this.setInWidth(inWidth)
              }
            },
            {
              label: this.volumePercent == 1?"√ 100%":"   100%",
              click:()=>{
                let inWidth = 1*62
                this.setInWidth(inWidth)
              }
            },
            {
              label: (this.volumePercent != 0.1&&this.volumePercent != 0.2&&this.volumePercent != 0.3&&this.volumePercent != 0.4&&this.volumePercent != 0.5&&this.volumePercent != 0.6&&this.volumePercent != 0.7&&this.volumePercent != 0.8&&this.volumePercent != 0.9&&this.volumePercent != 1&&this.volumePercent != 0)?`√ 其他(${Math.round(this.volumePercent*100)}%)`:"   其他",
            },
            {
              label: this.volumePercent == 0?"√ 静音":"   静音",
              click:()=>{
                let inWidth = 0
                this.setInWidth(inWidth)
              }
            }
          ]
        },
        {
          type: "separator"
        },
        {
          label: "设置"
        }
      ];
      if (this.currentVideo) {
        let addMenu = [
          {
            label: this.isPlaying ? "暂停" : "播放",
            click: () => {
              this.setPlaying(!this.isPlaying);
            }
          },
          {
            type: "separator"
          }
        ];
        contextMenuTemplate.unshift(...addMenu);

        contextMenuTemplate.splice(4, 0, {
          label: this.isFullScreen ? "退出全屏" : "全屏",
          click: () => {
            this.setFullScreen(!this.isFullScreen);
          }
        });

        contextMenuTemplate.push({
          label:'文件信息',
          click:()=>{
            this.videoInfo = this.currentVideo
            this.isShowInfo = true
          }
        })
      }
      let m = Menu.buildFromTemplate(contextMenuTemplate);
      Menu.setApplicationMenu(m);
      m.popup({ window: remote.getCurrentWindow() });

概要

なぜ達成するので、オーディオとビデオの再生、および右クリックメニュー言いますか?これら2つの関数は、私は、この機能は特に、オーディオおよびビデオ再生機能の最大数を、書き直したので、私はまた、パフォーマンスの問題を再生するさまざまな方法をテストするために、遊びの異なる方法をテストするためにデモをたくさん書いて、彼らは最終的に構築することにしましたこのミニサーバー方法。その他の機能は、他の特徴は、共通のコードを共有し、詳細どこを説明するためには何も特別なニーズにもコードを書くための結合度を減らすために、パッケージに注意を払っていない、サブモジュール、サブ機能。問題が開始されていない場合に開始するために、私は、これらの場所に気付かなかった、より多くの後ろにコードを書く、私は、コードをリファクタリング仕上げに多くの時間を費やすことを可能にする、変更するにはどこか分かりません。

レンダリング

図1の影響

図2の影響

エフェクト3

図4の影響

図5エフェクト

図6の影響

図7の影響

:インストールパッケージのダウンロードhttp://zhifa.daiqee.com/player%20Setup%200.0.1.exeを

最后如果大家觉得我这个音视频播放器还可以的话,欢迎去我的github:https://github.com/c10342/player 给个star

おすすめ

転載: blog.csdn.net/vgub158/article/details/91490185