Netflixにおける日本語字幕の導入

(Please note that this article is a localized (to Japanese) version of a corresponding tech blog article in the English language)

Netflixでは、2015年9月の日本における配信サービス開始時から日本語字幕を提供しています。 今回のブログでは、日本語字幕提供に至るまでの技術的な取り組みについて説明します。 字幕ソースファイルの仕様、字幕ソースファイルからNetflix配信用字幕への変換モデル、Netflixにおける日本語字幕の納品モデルなどを取り上げます。さらに、W3C字幕規格Timed Text Markup Language 2 (TTML2)導入に向けた対応についても触れます。

2014年の終盤にかけて、Netflixでは2015年9月に予定していた日本での配信開始に向け、技術的な機能実現を進めていました。 当時、日本市場で展開している他社ストリーミングサービスの字幕品質が問題となっていることは十分認識していました。 その上でNetflixの高い品質基準を維持するために、日本における上質な動画配信サービスの慣例となるべく、日本語字幕にとっての”必須”機能をすべて導入する覚悟で準備を始めました。 それらは、下記必要条件への追加条件として導入したものです。

  • 字幕は動画と分けてクライアントに納品 (すなわち、焼き付け字幕は不可)
  • 字幕のソースファイル形式は、将来性を確保するためにもすべてテキスト形式でNetflixに納品

日本語字幕の必須機能

概要

市場調査、日本語言語およびメディア関連の専門家アドバイスをまとめた結果、日本語字幕に関する5つの必須機能が明らかになりました。 これから説明するその5つの機能とは、ルビ、傍点、縦書き、斜体、縦中横 (縦字幕の数字を横書きで表示) です。 これらの機能を実現することは、従来の課題を一層複雑にする大きなチャレンジとなりました。

ルビ

ルビは特定の言葉を説明するためのものです。 たとえば、なじみのない言葉や外来語、スラングの意味を伝えたり、珍しい漢字またはあまり知られていない漢字にふりがなを付けるために使用します。 また、視聴者がコンテンツをより深く理解し楽しめるように、訳文の文化的背景を説明する場合もあります。 ルビ表示は通常、字幕文字よりも小さなフォントサイズを使い、1行のみの字幕、あるいは2行字幕の1行目には、文字の上にルビを振ります。 2行字幕の2行目にルビが存在する場合、文字の下にルビを振ります。ルビは2行字幕の行間には決して配置しません。どちらの行の文字を説明しているのか分かりづらくなるためです。図1に示すルビの例は、”All he ever amounted to was chitlins.”というセリフの字幕に振られたものです。

図1: ルビの例

単語”chitlins”*の訳語にその音訳のルビを振ることで、視聴者はセリフのキーワードと訳語を関連付けることができます。 上述したように、ルビは2行字幕の行間には決して配置しません。 図2に示すのは、2行字幕の正しいルビの振り方です。 万一、3行の字幕を必要とする場合、1行目と2行目は文字の上に、また3行目は文字の下にルビを振ります。

図2: 2行字幕の正しいルビの配置

傍点

傍点は単語や言葉を強調するため、上または下に配置するもので、英文における斜体と同じ役割です。 言外の意味を伝えるのに役立ち、翻訳をより豊かで力強いものにします。 図3に示す傍点の例は、”I need someone to talk to.”というセリフの字幕に振られたものです。

図3: 傍点の例

上図の例の字幕では、傍点は単語”talk”の訳語の文字上に振られています。 単語を強調することで、このシーンで話者が特定の人しか知らない情報の提供者を必要としていることが伝わります。

縦字幕

縦字幕は、主に動画の画面上に表示されている文字との重なりを避けるために使用します。 英語字幕における画面上部への表示に相当するものです。 図4に例を示します。

図4: 画面上のクレジットと縦字幕を同時に表示

縦中横

日本語のタイポグラフィでは、縦書き文字の中に横書きの短い数字やアルファベット文字が含まれることがよくあります。 これを縦中横と呼びます。 縦に並べるのではなく、半角文字を横並びに配置することで読みやすくなり、字幕1行の中により多くの文字を入れることができます。 図5に示す例は、”It’s as if we are still 23 years old”というセリフの字幕です。 この例では、半角数字”23"が縦中横になっています。

図5: 横並びに配置された数字を含む縦字幕

斜体

斜体は他言語におけるイタリック体と同様、ナレーション、画面外のセリフ、および強制字幕に使用します。 ただし、日本語字幕の場合、横字幕と縦字幕では斜体の傾き方向が異なる点が独特です。さらに傾きの角度も一定とは限りません。 図6と図7に例を示します。

図6: 横字幕の斜体
図7: 縦字幕の斜体

日本語字幕のソーシング

エンターテインメント業界での字幕アセットは、主に、構造化テキスト/バイナリファイルまたはレンダリング画像の2形式のどちらかです。 Netflixのコンテンツ取り込みシステム用には、常に前者の形式をお願いしてきました。 それにはいくつかの理由があります。 まず、クライアントによって字幕機能に差があるため、1つのソースから多様なクライアントアセットを作成する必要があります。 また、テキスト形式の字幕ソースファイルは将来性も確保されています。 つまり、新しいデバイス機能が次々と市場に出ても、テキスト形式なら、Netflixが所有する膨大な字幕アセットのバックカタログへ問題なく適用できるのです。 たとえば、HDRデバイスで再生されるHDRコンテンツに字幕を表示する場合、白い文字が最大白色の鏡面ハイライトにならないよう、輝度ゲインを指定することが推奨されます。 テキスト形式の字幕ソースを使用すれば、輝度ゲインをサポートするクライントプロフィールに対応した字幕表示を容易に処理できます。 一方、画像形式の字幕ソースを取り込んだ場合、同様の処理をクライアントアセットに適用するのは非常に困難です。 さらに、解析のための検索性や自然言語処理の観点からも、不透明な画像形式アセットに比べて、はるかに優れています。

テキスト形式の字幕ソースが必須条件であることを前提に、日本語に利用できるオプションを検討した結果、Videotron Lambda (LambdaCap形式とも呼ばれる) を日本語字幕として唯一使用可能なモデルとして選択しました。 これにはいくつかの理由がありますが、 分析した結果、LambdaCap形式には次のような特徴があることがわかったからです:

  • ある程度オープンであるため、Netflix独自のツールやワークフローを構築することができる。
  • 現時点で日本語字幕ツールがサポートする最も一般的な字幕形式である。 これは特に、既存の日本語字幕会社にNetflix向けの字幕作成を作成してもらうために大切な点でした。
  • 既存の日本語字幕の最も一般的なアーカイブ形式である。 LambdaCap形式なら、変換の必要なく既存アセットを取り込めるこの点も重要なポイントの1つでした。
  • 前述の日本語字幕の必須機能をサポートする。
  • 焼き付けに使用する画像ベースの字幕ファイル作成用として、業界で幅広く使われている。 すなわち、徹底的にテストされたものだったのです。

このように、日本でのNetflix配信開始時にはVideotron Lambdaを選択していましたが、長期的には決して優れたオプションではありませんでした。 業界の標準形式ではなく、仕様面において曖昧な点があるためです。 LambdaCap形式は日本語字幕の必須機能をサポートする一方で、TTML1のようなウェブプラットフォーム規格がサポートする基本的な機能の一部が含まれていないことがあります。 そうしたサポート外の機能には、色、フォント情報、さまざまなレイアウトやコンポジションのプリミティブなどが含まれます。 また、Netflixのエコシステムにおける再生デバイスへの納品モデルとしても、LambdaCap形式を使用しないことにしました。 さらにこの時期、タイムテキスト作業グループ (TTWG) がTTML規格の第2バージョンであるTTML2に取り組んでいました。 TTML2の目的の1つは、日本語字幕を主要対象とした多言語字幕をサポートすることでした。 そこで、NetflixでTTML2規格化に向けてTTWGと協力することになり、すでに蓄積した経験に基づいて仕様の完成、および後述する導入作業を行ったのです。 こうして、最終的にはTTML2が、Netflixの字幕処理パイプラインにおける全ソース形式の正準表現になりました。

日本語字幕機能のTTML2へのマッピング

表1は上記の日本語字幕の必須機能と、TTML2が提供する構造間のマッピングを要約したものです。 また、Netflixの日本語字幕カタログ全体における上記機能の使用統計と、Netflixエコシステムの理想モードも示しています。 現時点で未使用の機能や使用頻度が低いものは、今後、より広く使用されるものと予測されます。† 次のセクションでは、特にサポートされている値に関する各機能の詳細を説明します。

表1: 日本語字幕機能とTTML2のスタイル構造間のマッピングの要約

ルビ

tts:ruby

このスタイル属性は、ルビ自体とルビが振られた文字幅の定義を含む、ルビコンテンツの構造的な特徴を指定しています。 tts:rubyに関連付けられた値の範囲は、対応するHTMLマークアップ要素にマッピングされます。 TTMLサンプルで示しているように、”container”マークアップはルビの振られた文字とルビを含み、”base”と”text”はそれぞれ文字とルビをマークアップします。 このサンプルのレンダリングは、図8になるはずです。

<?xml version=”1.0" encoding=”utf-8"?>
<tt xmlns=”http://www.w3.org/ns/ttml" xmlns:ttm=”http://www.w3.org/ns/ttml#metadata" xmlns:ttp=”http://www.w3.org/ns/ttml#parameter" xmlns:tts=”http://www.w3.org/ns/ttml#styling" ttp:frameRate=”30" ttp:frameRateMultiplier=”1000 1001" ttp:version=”2" tts:extent=”1280px 720px” xml:lang=”ja”>
<head>
<styling>
<initial tts:fontSize=”6.0vh” tts:lineHeight=”7.5vh” tts:showBackground=”whenActive” tts:textOutline=”black 0.1em”/>
<style xml:id=”s2" tts:textAlign=”start”/>
<style xml:id=”s3" tts:ruby=”container”/>
<style xml:id=”s4" tts:ruby=”base”/>
<style xml:id=”s5" tts:ruby=”text” tts:rubyPosition=”before”/>
<style xml:id=”s7" tts:textAlign=”center”/>
<style xml:id=”s8" tts:ruby=”text” tts:rubyPosition=”after”/>
</styling>
<layout>
<region xml:id=”横下” tts:displayAlign=”after” tts:extent=”80vw 30vh” tts:position=”center bottom 10vh”/>
</layout>
</head>
<body region=”横下” xml:space=”preserve”>
<div>
<p begin=”00:04:30:13" end=”00:04:32:18" style=”s7"><span style=”s2"><span style=”s3"><span style=”s4">太孫</span><span style=”s5">たいそん</span></span>のペクチョンを連れ<br/><span style=”s3"><span style=”s4">北漢</span><span style=”s8">プッカン</span></span>山に登り</span></p>
</div>
</body>
</tt>
図8: 上記TTMLサンプルに対応するレンダリング

tts:rubyPosition

このスタイル属性は、文字に相対するブロック進行方向の寸法内でのルビの配置を指定しています。 日本語字幕の場合、tts:rubyPosition=”top”とtts:rubyPosition=”bottom”では想定外の行替えに対応できないため、理想的ではないことが分かりました。ルビが2行目にあるときは文字の下に置く必要があるためです。 そのような場合、tts:rubyPosition=”auto”はその名のとおり、自動的に対応してくれます。 次のTTMLサンプルでその例を示しています。 このサンプルのレンダリングを図9で示します。 現在、”auto”の動作は厳密には2行イベントにのみ指定できるものであり、2行イベントの2行目に予測しない改行が起きた場合は適用されません。 NetflixではTTML2で説明されている”outside”の現在の動作が正しいモデルであると考えるため、”auto”を廃止して代わりに”outside”を採用する可能性もあります。

<?xml version="1.0" encoding="UTF-8"?>
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:tt="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:ttp="http://www.w3.org/ns/ttml#parameter" xmlns:tts="http://www.w3.org/ns/ttml#styling" ttp:tickRate="10000000" ttp:version="2" xml:lang="ja">
<head>
<styling>
<initial tts:backgroundColor="transparent" tts:color="white" tts:fontSize="6.000vh"/>
<style xml:id="style0" tts:textAlign="center"/>
<style xml:id="style1" tts:textAlign="start"/>
<style xml:id="style2" tts:ruby="container" tts:rubyPosition="auto"/>
<style xml:id="style3" tts:ruby="base"/>
<style xml:id="style4" tts:ruby="text"/>
<style xml:id="style5" tts:ruby="text"/>
</styling>
<layout>
<region xml:id="region0" tts:displayAlign="after"/>
</layout>
</head>
<body xml:space="preserve">
<div>
<p xml:id="subtitle1" begin="18637368750t" end="18676157500t" region="region0" style="style0"><span style="style1">テソプ<span style="style2"><span style="style3">の所だ</span><span style="style4">カン食ン</span></span>食おう<br/><span style="style2"><span style="style3">江陵</span><span style="style5">カンヌン</span></span>で刺身でも食おう</span></p>
</div>
</body>
</tt>
図9: 上記TTMLサンプルに対応するレンダリング

tts:rubyAlign

このスタイル属性は、ルビコンテナが生成したインラインエリア内のルビの配置を指定しています。 Netflixでの日本語字幕の経験から、tts:rubyAlignの推奨値を”center”にしています。

<?xml version="1.0" encoding="utf-8"?>
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:tt="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:ttp="http://www.w3.org/ns/ttml#parameter" xmlns:tts="http://www.w3.org/ns/ttml#styling" ttp:tickRate="10000000" ttp:version="2" xml:lang="ja">
<head>
<styling>
<initial tts:backgroundColor="transparent" tts:color="white" tts:fontSize="6.000vh" tts:lineHeight="7.500vh" tts:opacity="1.000" tts:showBackground="whenActive" tts:writingMode="lrtb" tts:rubyAlign="center"/>
<style xml:id="style0" tts:textAlign="center"/>
<style xml:id="style1" tts:textAlign="start"/>
<style xml:id="style2" tts:ruby="container"/>
<style xml:id="style3" tts:ruby="base"/>
<style xml:id="style4" tts:ruby="text" tts:rubyPosition="auto"/>
</styling>
<layout>
<region xml:id="region0" tts:displayAlign="after" tts:extent="80.000% 30.000%" tts:position="center bottom 10vh" tts:showBackground="whenActive"/>
</layout>
</head>
<body xml:space="preserve">
<div>
<p xml:id="subtitle1" begin="18637368750t" end="18676157500t" region="region0" style="style0"><span style="style1">テソプ<span style="style2"><span style="style3">の所だ</span><span style="style4">カン食ン</span></span>食おう</span></p>
<p xml:id="subtitle2" begin="18676991666t" end="18717031666t" region="region0" style="style0"><span style="style1">テソプ<span style="style2"><span style="style3">の所だ</span><span style="style4">カン食ンカン食ン</span></span>食おう</span></p>
</div>
</body>
</tt>

次に示すのは上記TTMLサンプルに対応するものです。文字の幅がルビより大きい場合と、その逆の場合の動作の2つの例を表示しています。 どちらの例でも、”の所だ” (ユニコード文字3文字) の文字とルビのアラインメントは”center”です。

例1

この例 (図10) では、ルビの幅が文字の幅よりも小さくレンダリングされています。

図10: レンダリングされたルビの幅が文字の幅よりも小さい

例2

この例 (図11) では、ルビの幅が文字の幅よりも大きくレンダリングされています。 どちらの例も、文字に対してルビは中央揃えになっています。

図11: レンダリングされたルビの幅が文字の幅よりも大きい

tts:rubyReserve

この機能の目的は、文字のみの字幕からルビ付き字幕 (またはその逆) に移動する際に、ブロックの進行方向に沿って文字の配置の時間的整合性を保持することです。 この機能はまた、傍点を使用する際、時間が変わっても文字のアラインメントを保つためにも使用できます。

<?xml version="1.0" encoding="utf-8"?>
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:tt="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:ttp="http://www.w3.org/ns/ttml#parameter" xmlns:tts="http://www.w3.org/ns/ttml#styling" ttp:tickRate="10000000" ttp:version="2" xml:lang="ja">
<head>
<styling>
<initial tts:backgroundColor="transparent" tts:color="white" tts:fontSize="6.000vh" tts:lineHeight="7.500vh" tts:opacity="1.000" tts:showBackground="whenActive" tts:writingMode="lrtb" tts:rubyReserve="auto"/>
<style xml:id="style0" tts:fontShear="16.78842%" tts:textAlign="center"/>
<style xml:id="style1" tts:fontShear="16.78842%" tts:textAlign="start"/>
<style xml:id="style2" tts:fontShear="16.78842%" tts:ruby="container"/>
<style xml:id="style3" tts:fontShear="16.78842%" tts:ruby="base"/>
<style xml:id="style4" tts:fontShear="16.78842%" tts:ruby="text" tts:rubyPosition="auto"/>
</styling>
<layout>
<region xml:id="region0" tts:displayAlign="after" tts:extent="80.000% 30.000%" tts:position="center bottom 10vh" tts:showBackground="whenActive"/>
</layout>
</head>
<body xml:space="preserve">
<div>
<p xml:id="subtitle1" begin="18623187916t" end="18635700416t" region="region0" style="style0"><span style="style1">“行き先は?”</span></p>
<p xml:id="subtitle2" begin="18637368750t" end="18676157500t" region="region0" style="style0"><span style="style1">“テソプの所だ<br/><span style="style2"><span style="style3">江陵</span><span style="style4">カンヌン</span></span>で刺身でも食おう”</span></p>
</div>
</body>
</tt>
図12: 文字の上下相対移動がない2つの字幕のシーケンス

上記TTMLサンプルのレンダリングが図12です。 tts:rubyReserveを有効にした場合、時間が経過しても字幕の相対移動は起こりません。

図13: 文字の上下相対移動がある2つの字幕のシーケンス

tts:rubyReserveを有効にしていない場合、時間が経過すると字幕文字のベースラインに相対移動が起こるため、快適ではない視聴体験が発生してしまいます。 図13に示すのは、1つ目の字幕の文字 (左側) と2つ目の字幕の2行目の文字 (右側) との間に起きている、上下相対移動の例です。

縦書き

標準の縦書き

Netflixでは縦書きモードを指定する際に、tts:writingModeを利用します。 次に縦書き (縦書き用の句読点や記号を含む) の例を示します。以下はTTMLサンプルおよび対応するレンダリング (図14) です。

<?xml version="1.0" encoding="utf-8"?>
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:ttp="http://www.w3.org/ns/ttml#parameter" xmlns:tts="http://www.w3.org/ns/ttml#styling" ttp:frameRate="30" ttp:frameRateMultiplier="1000 1001" ttp:version="2" tts:extent="1280px 720px" xml:lang="ja">
<head>
<styling>
<initial tts:fontSize="6.0vh" tts:lineHeight="7.5vh" tts:showBackground="whenActive" tts:textOutline="black 0.1em"/>
<style xml:id="s1" tts:fontShear="16.78842%" tts:textAlign="center"/>
<style xml:id="s2" tts:textAlign="start"/>
<style xml:id="s3" tts:ruby="container"/>
<style xml:id="s4" tts:ruby="base"/>
<style xml:id="s5" tts:ruby="text" tts:rubyPosition="before"/>
<style xml:id="s8" tts:ruby="text" tts:rubyPosition="after"/>
</styling>
<layout>
<region xml:id="縦左" tts:displayAlign="after" tts:extent="30vh 80vh" tts:position="left 10vw center" tts:writingMode="tbrl"/>
</layout>
</head>
<body xml:space="preserve">
<div>
<p begin="00:05:07:23" end="00:05:09:12" region="縦左" style="s1"><span style="s2"><span style="s3"><span style="s4">党項城</span><span style="s5">タンハンじょう</span></span>…現在の<span style="s3"><span style="s4">京畿道</span><span style="s5">キョンギド</span></span>に<br/>位置する城(<span style="s3"><span style="s4">唐</span><span style="s8">タン</span></span>城)</span></p>
</div>
</body>
</tt>
図14: 上記TTMLサンプルに対応するレンダリング

縦書きのルビ
 
 
上記TTMLサンプルで示したように、縦書きモードにおけるルビのマークアップは横書きモードの場合と変わりはありません。

縦中横

tts:textCombine

縦中横機能を実行するにはtts:textCombineを使用します。 次のTTMLサンプルがその例です。 図15でわかる通り、この機能を使うと字幕の読みやすさが増します。

<?xml version="1.0" encoding="utf-8"?>
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:ttp="http://www.w3.org/ns/ttml#parameter" xmlns:tts="http://www.w3.org/ns/ttml#styling" ttp:frameRate="24" ttp:frameRateMultiplier="1000 1001" ttp:version="2" tts:extent="1280px 720px" xml:lang="ja">
<head>
<styling>
<initial tts:fontSize="6.0vh" tts:lineHeight="7.5vh" tts:showBackground="whenActive" tts:textOutline="black 0.1em"/>
<style xml:id="s1" tts:textCombine="all"/>
<style xml:id="s5" tts:textAlign="center"/>
<style xml:id="s6" tts:textAlign="start"/>
<!-- set up tts:textEmphasis in initial section →
<style xml:id="s10" tts:textEmphasis="dot after"/>
</styling>
<layout>
<region xml:id="横下" tts:displayAlign="after" tts:extent="80vw 30vh" tts:position="center bottom 10vh"/>
<region xml:id="縦右" tts:extent="30vh 80vh" tts:position="right 10vw center" tts:writingMode="tbrl"/>
</layout>
</head>
<body region="横下" xml:space="preserve">
<div>
<!-- illustration of tts:textCombine in vertical writing mode →
<p begin="00:00:37:07" end="00:00:40:00" region="縦右">まるで<span style="s1">23</span>歳のままだわ</p>
<!-- illustration of tts:textEmphasis →
<p begin="00:09:37:14" end="00:09:41:00" style="s5"><span style="s6">もし そうでも<br/><span style="s10">お相手</span>するって</span></p>
</div>
</body>
</tt>
図15: 上記TTMLサンプルに対応するレンダリング

傍点

tts:textEmphasis

傍点をレンダリングするのに使用します。 この機能は前のセクションに記載したTTMLサンプルでも指定されています。 図16は、同サンプルに対応するレンダリングです。

図16: 前のセクションのTTMLサンプルに対応するレンダリング

斜体

tts:fontShear

日本語のタイポグラフィにはイタリック体のフォントはありません。 斜体を表示するには、グリフの幾何変換を実行します。 共通値tts:fontShearは約15度の回転に相当します。 前述したように、斜体の傾き方向は横字幕と縦字幕で異なります。 次のTTMLサンプルのレンダリング図17と図18を確認してください。

<?xml version="1.0" encoding="utf-8"?>
<tt xmlns="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:ttp="http://www.w3.org/ns/ttml#parameter" xmlns:tts="http://www.w3.org/ns/ttml#styling" ttp:frameRate="30" ttp:frameRateMultiplier="1000 1001" ttp:version="2" tts:extent="1280px 720px" xml:lang="ja">
<head>
<styling>
<initial tts:fontSize="6.0vh" tts:lineHeight="7.5vh" tts:showBackground="whenActive" tts:textOutline="black 0.1em"/>
<style xml:id="s1" tts:fontShear="16.78842%" tts:textAlign="center"/>
<style xml:id="s2" tts:textAlign="start"/>
<style xml:id="s3" tts:ruby="container"/>
<style xml:id="s4" tts:ruby="base"/>
<style xml:id="s5" tts:ruby="text" tts:rubyPosition="before"/>
</styling>
<layout>
<region xml:id="横下" tts:displayAlign="after" tts:extent="80vw 30vh" tts:position="center bottom 10vh"/>
<region xml:id="縦左" tts:displayAlign="after" tts:extent="30vh 80vh" tts:position="left 10vw center" tts:writingMode="tbrl"/>
</layout>
</head>
<body region="横下" xml:space="preserve">
<div>
<p begin="00:01:11:14" end="00:01:15:05" style="s1"><span style="s2">天と地に隔たりのない時</span></p>
<p begin="00:05:04:27" end="00:05:06:28" region="縦左" style="s1"><span style="s2"><span style="s3"><span style="s4">黄草嶺</span><span style="s5">ファンチョリョン</span></span>…現在の<span style="s3"><span style="s4">栄光</span><span style="s5">ヨングァン</span></span>郡に<br/>位置する嶺</span></p>
</div>
</body>
</tt>
図17: 上記TTMLサンプルに対応する横字幕のレンダリング
図18: 上記TTMLサンプルの縦字幕レンダリング

字幕の納品

日本語の字幕機能を特定し、ソース形式を選択して、日本語字幕アセットの納品仕様を作成しました。 次の課題はクライアントへの字幕納品でした。 日本語字幕の取り込みはVideotron Lambda形式で行っていましたが、前述の理由でLambdaCap形式はクライアントモデルには適していないと考えるようになりました。 また、テキスト形式の字幕をクライアントへ納品することが望ましい一方で、大多数のNetflix対応デバイスは複雑な日本語字幕機能をサポートしていませんでした。 こうした問題により、日本では画像形式の字幕を使用することにしました。そして複雑な日本語字幕のレンダリング作業のすべてを、Netflixにおけるバックエンドのトランスコード処理で対応することにしたのです。
 
 画像形式の字幕をレンダリングするためには日本語字幕のレンダリングエンジンの導入が必要となり、これが次の課題となりました。 このプロジェクトには、日本語タイポグラフィの専門家でありW3C TTML仕様エディターでもあるグレン・アダムス氏の協力を仰ぎました。 そのコラボレーションの成果が、Netflixによる出資およびSkynavの開発から実現したオープンソースソフトウェアTimed Text Toolkit (TTT) プロジェクトです。 TTTは日本語字幕の必須機能すべてをサポートし、TTML2ファイルの検証とレンダリングを行う完全なツールセットを提供します。 W3C TTML系の派生規格であるIMSC1 (Internet Media Subtitles and Captions: インターネットメディア用字幕とキャプション) は、インターネットベースの字幕とキャプションの納品アプリケーションを対象にしたものです。 IMSC1仕様の開発段階でTTTを完全な基準組み込みとして使用することができたため、TTTはIMSC1仕様の推奨基準となりました。 同様に、TTTはTTML2の完全な組み込みを提供するため、TTML2仕様の実現がさらに可能になります。

図19: 日本語字幕用のNetflix字幕処理パイプライン

TTTを使用し、LambdaCap形式ソースファイルをTTML2正準表現に変換した後、字幕を画像としてレンダリングする字幕処理パイプラインを導入しました。 図19で示している通り、cap2ttモジュールを使用してLambdaCap形式ファイルをTTML2ドキュメントに変換します。 TTML2ドキュメントはttpeによって一連の画像シーケンスに変換されます。 字幕画像の時間シーケンスは、それら画像のタイミングと位置情報を含むアーカイブにパッケージされます。 そして、そのアーカイブがNetflixエコシステムのデバイスに納品されるのです。 また、こうした画像セットを異なる解像度で作成し、多様な画面サイズやデバイスフォームファクターに対応しています。

今後の課題

さまざまな理由から、長期的に望ましい納品システムは画像形式よりもテキスト形式だと言えます。 たとえば、画像形式よりテキスト形式のほうがファイルサイズが小さいため帯域効率がアップすることや、字幕のフォント、フォントサイズ、色をエンドユーザーが柔軟に選択できることなどがその利点です。 Netflixエコシステムの再生デバイスがさらに日本語字幕の機能をサポートできるようになり、TTML2規格が定着すれば、今後はテキスト形式の納品モデルに移行する予定です。 Netflixではこれに向け、さまざまなTTML2機能に対して最上級のサポートを提供し、デバイスに最適な融通性の高い字幕レンダリングエンジンの開発を現在進めています。

— ロヒット・プリ、シリル・コンコラト、デヴィッド・ロンカ、ユミ・ディーター

(*) 「ハウス・オブ・カード 野望の階段」で使われたスラングで、”チトリンズ”とも呼ばれるアメリカ南部のブタの小腸を材料にした料理。 こうした言葉は視聴者にとってなじみがないことが多い。

(†) 表に含まれていないその他のTTML2日本語機能 (tts:rubyOverhangなど) は、Netflixにおける日本語字幕機能のサポートには必要ありません。

Like what you read? Give Netflix Technology Blog a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.