2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

リアルタイムで動画に字幕ファイルを合成するhtmlを作ってみた

Last updated at Posted at 2021-08-18

はじめに

動画と字幕をリアルタイムに合成するhtmlを作ったので共有させていただきます。
(webサーバ上のページから再生できるよう改修しました。)
play_movie_with_subtitles.png

サンプル

play_movie_with_subtitles

github

ソース

play_movie_with_subtitles.js
function st_sub(){
    document.getElementById('file').addEventListener('change', handleFileSelect, false);
}

function sleep(second) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve()
        }, second * 1000)
    })
}

//******************************* srt file time style changer ***********************************
function sec_calc(str_mat2){
    i_hh=str_mat2.substring(0,2)
    i_mm=str_mat2.substring(3,5)
    i_ss=str_mat2.substring(6,8)
    i_ms=str_mat2.substring(9,12)
    str_sec=(parseInt(i_hh*3600)+parseInt(i_mm*60)+parseInt(i_ss))+"."+(i_ms)
    int_sec=str_sec
    return int_sec
}

/**********************************/
async function handleFileSelect(ev){
    var reader = new FileReader();
    for (let i = 0; i < ev.target.files.length; i++) {
        let file = ev.target.files[i];
        s_name=file.name
        splt=s_name.split(".")
        if(splt[1]!="srt"){
            v.src = URL.createObjectURL(file);
            await sleep(1)
            v.load()
            await sleep(1)
            v.play()
            reader.onload = function(evt) {
                var subtitles;
                let subtitle = evt.target.result;
                _subtitle(subtitle);
            }
        }else{
            reader.readAsText(file,"Shift_JIS");
        }
    }
};

//**************************** display subtitle **************************************
function _subtitle(text) {
    let Subtitle = text;
    let Pattern = /(\d+)\n([\d:,]+)\s+-{2}\>\s+([\d:,]+)\n([\s\S]*?(?=\n{2}|$))/g;
    let _regExp = new RegExp(Pattern);
    if (typeof (text) != "string") throw "Sorry, Parser accept string only.";
    if (Subtitle === null) return Subtitle;
    let Parse = Subtitle.replace(/\r\n|\r|\n/g, '\n');
    let Matches;
    //*************//
    v.addEventListener("timeupdate", function(){
    document.getElementById("subtitle").style.top = document.getElementById("subtitles_position").value+"%";
    document.getElementById("subtitle").style.fontSize = document.getElementById("fnt_siz").value+"px";
    while ((Matches = Pattern.exec(Parse)) != null) {
        int_start_sec=sec_calc(Matches[2])
        int_end_sec=sec_calc(Matches[3])
        int_v_time=v.currentTime+parseFloat(document.getElementById("delay").value)
        if(int_v_time > parseFloat(int_start_sec)){
        if(int_v_time < parseFloat(int_end_sec)){
            document.getElementById("subtitle").innerHTML = Matches[4].replace('\n','<br />');
        }
        if(int_v_time > parseFloat(int_end_sec)){
            document.getElementById("subtitle").innerHTML = "";
        }
        }
    }
    },false);
}
base.css
body{
    text-align: center;
    background-color:#000000;
  }

  .disp_no{display:none;}
  .inps{background-color: #8f8f8f;text-align:right;}
  .w32{width:32px;}

  #sel_file{width:100%;text-align:right;}

  #v{
    width:100%;
    margin-right: auto;
    margin-left: auto;
      margin-bottom:15px;
  }

  #files_label{
    margin-right:5px;
    color:#000000;
    font-size:12px;
    padding:0px 5px 0px 5px;
    cursor:pointer;
  }

  #subtitle{
    z-index: 3;
    left:50%;
    top:58%;
    line-height:120%;
    height:3em;
    /*display:table-cell;
    vertical-align:bottom;*/
    display:flex;
    flex-direction: column;
    justify-content: flex-end;
    position:absolute;
    text-align:center;
    transform: translate(-50%, -50%);
    color:#cccccc;
    font-size: 30px;
    font-weight: bold;
    margin-right:auto;margin-left:auto;
    text-shadow:3px 0px 1px #111, 0px 3px 1px #111, -3px 0px 1px #111, 0px -3px 1px #111, 3px 3px 1px #111, -3px 3px 1px #111, -3px -3px 1px #111, 3px -3px 1px #111;
    /*   ,4px 0px 0px #ccc, 0px 4px 0px #ccc, -4px 0px 0px #ccc, 0px -4px 0px #ccc, 4px 4px 0px #ccc, -4px 4px 0px #ccc, -4px -4px 0px #ccc, 4px -4px 0px #ccc   */
  }
index.htm
<html>
    <head>
        <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com; connect-src ; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src * blob:; media-src * blob:">
        <link rel="stylesheet" href="css/base.css">
    </head>

    <body onload="st_sub()">

        <video id="v" controls autobuffer></video>
        <div id="subtitle"></div><br />
        <div id="sel_file" style="color:#8f8f8f;">
          <label for="file" id="files_label" class="inps" >
            Select srt file<input type="file" id="file" class="disp_no" name="file[]" multiple />
          </label>
          Sub Pos Top<input type="text" id="subtitles_position" value="58" class="inps w32">%
          Font<input type="text" id="fnt_siz" value="30" class="inps w32">px
          Delay<input type="text" id="delay" value="0" class="inps w32">Sec
        </div>

    </body>

    <footer>
        <script src="js/play_movie_with_subtitles.js"></script>
    </footer>
</html>

使い方

まずブラウザで表示できる動画ファイルと、字幕ファイル(srt形式)を用意し
一つのフォルダにまとめておく必要があります。
1.png
srt形式が不明な場合
srtファイルとは などで検索してみてください。
ちなみに形式は下記のような形式
メモ帳などで編集可能です


1
00:00:43,209 --> 00:00:44,919
やってまいりました今日という日

2
00:00:45,211 --> 00:00:48,882
1967年1月27日
大統領も声明を発表

3
00:00:49,257 --> 00:00:53,261

index.html を準備し 開きます
開くと
このような画面になります。
10.png

右下に Select srt file というボタンがあるのでクリックし
先程用意したフォルダを探します
12.png

字幕ファイルと 動画ファイル の2つともを選択すると
字幕を合成した動画の再生が始まります
13.png

字幕の微調整は
右下の部分で
Sub posが 字幕の縦位置の調整になります。
半角数字の入力で即時反映です
うまい具合の位置でないとシークバーの操作ができなくなります。
21.png

字幕の文字の大きさ調整
半角数字入力即反映
22.png

字幕のタイミング調整
遅くする場合は数値を入力
早くする場合は-(マイナス)付きの数字を入力
どれだけ調整してもタイミングがズレる場合は
動画のバージョン(ディレクターズカット版など)と
字幕のバージョンがあっていない可能性があります。
正しい字幕ファイルを入手するか
メモ帳などでsrtファイルを編集するなどしてみてください。
23.png

なぜ作ったか

動画ファイルも字幕ファイルもあるのに合わせて再生するには
アプリのインストールが必要だとのこと。
そんな大仰なことか?と疑問に思い
そのくらいならhtml+javascriptでできるのでは?と考えたためです。
で、誰かそのくらいならもう書いてるだろうと探したのですが
見当たらなかったため書きました。
コード進行が古風ですがご了承ください。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?