ijsのガラクタ倉庫

ijs01140の作業メモ等

Linux上のDockerで録画鯖を建てる方法をざっくり書く

Linux上のDockerで録画鯖を建てる方法をざっくり書きます。1日でバタバタ書いたので多少の粗さはゆるしてね。

建て方

用意するもの

  • チューナー(いわゆるTS抜き)
    現在だとPLEX製のものが入手しやすい
  • チューナーを接続できるPC x64なデスクトップPC推奨
  • B-CASカード
  • ICカードリーダー(接触式)
    確定申告とかマイナポータルとかの用途向けに売られてるので、入手は容易
  • テレビ用の同軸ケーブル
    古いケーブルや細いケーブルだと、BS放送/CS放送に非対応な場合があるので注意
  • (混合されてる場合のみ)分波器

ドライバ導入(PX-W3PE5, PX-MLT5PE等)

PT3等、KernelにDVBドライバがある場合、この手順は不要。

ドライバのインストール

用意したチューナーに合うドライバを用意しましょう。DKMSを使用したインストール方法だと、Kernel更新のたびにやる必要のあるドライバの再ビルドを自動でやってくれるので、おすすめです。

PX-W3PE5, PX-MLT5PE等の場合、PLEXの公式ドライバではなく、GitHub - nns779/px4_drv: Unofficial Linux driver for PLEX PX4/PX5/PX-MLT series ISDB-T/S receivers (not V4L-DVB)を推奨しています。公式ドライバより安定しており、DKMSも使用可能なためです。

Mirakurun + EPGStation導入

今回の記事では docker-mirakurun-epgstation を使用した手順を説明します。 github.com

pcscdの削除

B-CASカードをDocker上で使うため、pcscdを削除する。

DockerとDocker Composeのインストール

Dockerの公式ドキュメントに、各ディストリビューション毎のインストール方法が記載されています。 ディストリビューションやDockerの更新によって手順が変わる可能性があるので、ここでは公式ドキュメントのURLを紹介するだけに留めておきます。
Dockerのインストール方法はこちら

docs.docker.com

Docker Composeのインストール方法はこちら

docs.docker.com

Dockerイメージのインストール

以下のコマンドを実行すると、MirakurunとEPGStationのDockerイメージをダウンロードし、サンプル設定ファイルを設定し、初回セットアップを実施します。

# MirakurunとEPGStationを導入
curl -sf https://raw.githubusercontent.com/l3tnun/docker-mirakurun-epgstation/v2/setup.sh | sh -s
# これから作業するディレクトリに移動
cd docker-mirakurun-epgstation

chardev版ドライバ特有の手順(PX-W3PE5, PX-MLT5PE等)

PX-W3PE5, PX-MLT5PE等、chardev版ドライバを使用するチューナーの場合、追加の手順が必要になります。

docker-compose.ymlの編集

docker-compose.ymlに以下のような箇所があるので、編集します。

version: '3.7'
services:
    mirakurun:
        image: chinachu/mirakurun
        cap_add:
            - SYS_ADMIN
            - SYS_NICE
        ports:
            - "40772:40772"
            - "9229:9229"
        volumes:
            - ./mirakurun/conf:/app-config
            - ./mirakurun/data:/app-data
        environment:
            TZ: "Asia/Tokyo"
        devices:
            - /dev/bus:/dev/bus
            - /dev/dvb:/dev/dvb
        restart: always
        logging:
            driver: json-file
            options:
                max-file: "1"
                max-size: 10m

まず、devicesの項目をチューナーに合わせて変更し、Mirakurunからチューナーが使えるようにします。
例として、チューナーがPX-W3PE5で、ドライバがpx4_drvの場合を掲載します。PX-W3PE5場合、チューナー数は地上波x2, BS/CSx2のため、デバイスファイルは/dev/px4video0から/dev/px4video4を列挙すればOKです。

        devices:
            - /dev/bus:/dev/bus
            - /dev/dvb:/dev/dvb
# 以下にチューナーを列挙
            - /dev/px4video0:/dev/px4video0
            - /dev/px4video1:/dev/px4video1
            - /dev/px4video2:/dev/px4video2
            - /dev/px4video3:/dev/px4video3

また、volumesの項目を次のように変更し、録画コマンドの導入を可能にします。

        volumes:
            - ./mirakurun/conf:/app-config
            - ./mirakurun/data:/app-data
# 以下を追記
            - ./mirakurun/opt:/opt

録画コマンドの導入

mirakurun/opt/bin/startupにカスタムスタートアップスクリプトを導入し、録画コマンドが導入されていない場合にインストールを行うよう設定します。

mkdir -p mirakurun/opt/bin
vim bin/startup

カスタムスタートアップスクリプトの中身の例は以下の通りです。

#!/bin/bash

if !(type "recpt1" > /dev/null 2>&1); then
  apt-get update
  apt-get install -y --no-install-recommends git autoconf automake

  cd /tmp
  git clone https://github.com/stz2012/recpt1.git
  cd recpt1/recpt1
  ./autogen.sh
  ./configure --prefix /opt
  make
  make install
fi

recpt1 -v

次に、このコマンドを実行し、Mirakurunに録画コマンドを導入します。

docker-compose down
docker-compose run --rm -e SETUP=true mirakurun

Mirakurunのチューナー設定を編集

チューナーのデバイスファイルを列挙します。例として、チューナーがPX-W3PE5で、ドライバがpx4_drvの場合を掲載します。これは使ってるチューナーに合わせて、必要に応じて変更してください。

- name: PX_S1
  types:
    - BS
    - CS
  command: recpt1 --device /dev/px4video0 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false
- name: PX_S2
  types:
    - BS
    - CS
  command: recpt1 --device /dev/px4video1 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false
- name: PX_T1
  types:
    - GR
  command: recpt1 --device /dev/px4video2 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false
- name: PX_T2
  types:
    - GR
  command: recpt1 --device /dev/px4video3 <channel> - -
  decoder: arib-b25-stream-test
  isDisabled: false

起動

docker-compose.ymlのあるディレクトリで、sudo docker-compose up -dを実行すると起動できます。

起動後にやること

チャンネルスキャンの実施

テレビ放送を視聴・録画するためにはチャンネル設定が必要です。チャンネル設定を手動でやるのは面倒なため、私はチャンネルスキャンをおすすめしてます。
地上波の場合、以下を実行します

curl -X PUT "http://localhost:40772/api/config/channels/scan"

BSの場合

curl -X 'PUT'   'http://localhost:40772/api/config/channels/scan?dryRun=false&type=BS&useSubCh=true&refresh=true'

CSの場合

curl -X 'PUT'   'http://localhost:40772/api/config/channels/scan?dryRun=false&type=CS&useSubCh=true&refresh=true'

必要なチャンネルスキャンを終えたら、

sudo docker-compose down
sudo docker-compose up -d

で再起動し、チャンネル設定を反映します。

EPGStationを開く

EPGStationはhttp://(サーバーのIPアドレス):8888をブラウザで開くとアクセスできます。サーバーのIPアドレスは、ip addr等を実行すれば調べることができます。

以上で構築手順は終了です。お疲れ様でした。

締め(?)

この記事は、アスタルテ ゆるふわ Advent Calendar 2022 - Adventarの10日目でした。なんとか間に合った……
次の日は白い人さんです。

お行儀の悪い某ウェブアーカイブサービスから身を守るために私が検討していること

はじめに

ウェブアーカイブサービスは、webの公開情報をクロールし記録するサービスである。この記事では、お行儀の悪い某ウェブアーカイブサービスの挙動を紹介し、その記録から逃れる方法について書こうと思う。

ウェブアーカイブサービスの挙動

ウェブアーカイブサービスは、ユーザーの操作または自動収集でデータを取得する。大抵の場合、取得したデータは保存され公開される。また、この種のサービスは性質上、動的に変化するコンテンツの保存には向かない。

比較的お行儀のよいサービスの挙動

お行儀のよいウェブアーカイブサービスは、robots.txtの記述に従ってくれるので記録の拒否が可能である。また、クローラ専用UserAgentを使用しているため、クローラのアクセスを拒否するにはそのUserAgentをブロックすればよい。
また、不本意に記録されてしまった場合でも、削除リクエストを出せば記録を削除してくれるのでけっこう良心的である。

お行儀の悪いサービスの挙動

対して、お行儀の悪い某ウェブアーカイブサービスの挙動は邪悪そのものである。まず、クローラのUserAgent偽装を行い、通常のブラウザからのアクセスに装う。当然、robots.txtの記述は無視される。また、接続がブロックされても複数IPアドレスで試行し、それでも失敗する場合は最終的にTor経由でのアクセスを試みる。これにより、IPアドレスベースでのブロックを難しくしている。
また、削除リクエストを基本的に受け付けない。削除してくれるのは無意味な空白のページなど、ほぼサービス提供の上で都合の悪い記録のみに限られる。

では、お行儀の悪い某ウェブアーカイブサービスから身を守るためには?

先に書いたとおり、UserAgentベースでのブロックは不可能である。そのため、けっこう回りくどい方法で自己防衛に努めるしかない。ここでは、ブロック方法として有用そうなものを2例挙げる。

IPアドレスベースのブロック(本命)

例のウェブアーカイブサービスは、IPアドレス複数で試行するとはいえ、保有可能なIPアドレスは有限である。そのため、内容のない虚無なウェブサイトを用意し、selenium等を用いて自動でそのウェブサイトの記録を試行させ続けることによって、そのサービスが用いるIPアドレスをすべて割ることが可能だと私は考えている。
あとは手に入れたIPアドレスからのアクセスを弾き、Torに関しても問答無用でブロックすれば完璧・・・のはず。

fingerprintを用いてのブロック

一般のブラウザとウェブアーカイブサービスのクローラを区別できるかもしれない方法として、fingerprintも挙げられる。fingerprintは閲覧環境ごとの挙動の違いを利用して、アクセス元の環境を割り出す技術である。これを使えばUserAgent偽装を見破れる・・・かもしれないが私は使ったことがないので、こういう方法があるよとの紹介に留めておく。
また、fingerprintはその性質上、閲覧者のプライバシーを侵害する恐れがあるので、運用には細心の注意が必要になる。

ijs01140.com1周年を記念して、私のFediverse歴を振り返る

思えば、私がijs01140.comをmastodon用に取得してから、1年が過ぎようとしている。時の流れがとてつもなく速く感じる。私はその1年で、メインのLinuxGentooに入れ替えたり、新たに中古サーバー機を2台買ったり、MisskeyやMastodonMinecraftのサーバーを建てたり。この記事では、私がいかにしてMastodonに流れ着き、自鯖を建て、Fediverseの住人となっていったかを振り返ってみる。

あいじすのFediverse歴を振り返る

Mastodonとの出会い

2017年4月ごろ?に、Mastodonが大きな話題となった。あの頃に周りの人たちが次々とMastodon始めていくなかで、私は新たなものに手を出す気力は当時なく、しばらくするとMastodonについて忘れてしまった。 翌年になって、Twitterの知り合いの投稿でMastodonについて思い出した。新年になったし、(今は亡き)ニコフレでも登録してみるかーと思って、しばらく二コフレを使っていた。これが私の初めてのMastodon生活だった。ただ、雰囲気に飽きたり、夜中の性的な生々しい話がつらくなったりして、1週間も経たないうちにやめてしまった。

ここで、私のMastodon生活には大きなブランクが空いた。当時の私は、もうMastodonやらないだろうと思っていた。

Mastodonへの定着

2018年4月、知り合いの紹介でmstdn.maud.io(通称:末代鯖)を知った。試しに登録してみたら、私の興味と近い話をしている人が多く、どんどんのめり込んでいった。気がつくと、Twitterが放置気味になり、徐々にMastodonがメインになっていった。

mstdn.maud.io

Mastodon自鯖へのあこがれ

mstdn.maud.ioでは、自分専用のMastodonサーバーを建てて独立する人がたまにいて、6月ごろから自分でMastodonを建てたいと思い始めた。しかし、私は当時、ArchLinuxをやっと使い始めた程度で、サーバーに関しては全く知識がなかった。(今もあるとは言い難いけどね!) Dockerを利用すると簡単に建つよと知人から聞き、MastodonGitHubに記載されている手順に従って建てようとしてみるが、失敗。全く知識がないので、一旦諦める。 鯖缶工場で質問してみることも考えたが、当時鯖缶工場でトラブルがあった話をタイミング悪く聞いてしまい、人に建て方を聞くのが怖くなって放置してしまった。
ただ、Mastodon自鯖へのあこがれは捨てきれず、「年内にMastodon自鯖を建てる」と宣言した。

Misskeyを知る

2018年8月、TwitterがStreaming APIを削除すると宣言し、ツイ廃たちを絶望に追いやった頃(のハズ)であり、それに伴って移住先候補としてMastodonが再び話題になった。そして、私のTwitter上の知り合いは、Misskey開発者の界隈と距離が近い人が多くいて、Misskeyへの集団移住を目撃した。ActivityPubを採用しつつも、Mastodonとは全く違う方向性で、Misskeyは私の興味をひいた。

mstdn.ijs01140.com誕生

2018年12月初旬、私は偶然、joinmastodon.orgに最新の構築手順が書かれていることを知った。

https://docs.joinmastodon.org/administration/installation/docs.joinmastodon.org

試しにその手順通りに、キーボードと液晶の壊れたノートパソコンで建ててみて成功、数日後に自鯖を建てることを決意した。2018年12月末、ドメインを買い、LinodeVPSも借りて構築し、ついにmstdn.ijs01140.comが建つ。確かクリスマス前だったと思う。ここで「年内にMastodon自鯖を建てる」宣言はギリギリだが達成された。

https://mstdn.ijs01140.com/web/accounts/1mstdn.ijs01140.com

翌年には、リレーサーバーへと接続した。連合にいろいろなサーバーの投稿が流れてきておもしろかったが、あっという間に容量を食いつぶし・・・・

初めてのサーバーでのトラブル

2019年に入ったある日のこと、突然mstdn.ijs01140.comがエラーを返してアクセスできなくなる。私はログの辿り方等を知らず、かなり苦戦した。半日に渡る格闘の末、ディスク容量不足によるものと判明。tootctl media removeを実行し、事なきを得た。これを機に、リレー接続を解除し、ストレージ問題の不可逆的解決までその接続を凍結した。 その後も不注意でディスク容量不足を何度かやってしまい、バックアップの削除や、ときには旧バージョンのKernelやRubyの削除でしのいだ。これは後日ConoHaオブジェクトストレージの採用で解決され、それに伴いリレー接続も再開した。

アスタルテとの出会い

2019年2月、偶然私は、魔改造インスタンスとしてのアスタルテを知った。運良く新規登録が通った。雰囲気が好きだったり、いろいろな意味でヤバい鯖缶が好きだったりして、第二の居場所としてアスタルテに定着した。実は自鯖より投稿多い日もけっこうあるのはナイショダヨ・・・

kirishima.cloud

Misskey自鯖の誕生

2019年3月初旬、私はなんとなくMisskey鯖を建てた。mi.ijs01140.cfの誕生である。メインのアカウントとして既にmstdn.ijs01140.comが存在し、こちらもおひとり鯖にしてしまうとモチベーションが続かないと思われたため、個人的に信用できる人のみ招待する方針で開始した。新たにVPSを借りると費用がかさむため、既存の録画鯖(某ブログの手順丸コピですでに2017年1月ごろに建てている)と同居させることにした。

録画鯖、故障。

さて、先述の録画鯖であるが、なんの変哲もない中古デスクトップPCであり、実家で動かしていた(これはNHK対策のため)。2018年5月初頭に自分用のデスクトップPCを自作した際、グラボの購入を後回しにするため、録画鯖からグラボを抜き出し、それ以来録画鯖にはジャンクのとても古いATI製グラボが刺さっていた。グリスがだめになっていたためかこれのファンが常時高回転状態であり、帰省のたびに私の睡眠を妨害していた。 私は私自身の睡眠を守るため、その忌まわしきグラボを分解・清掃し、グリスも塗り直し、元通りに組みこんだ。そしたら、見事に録画鯖が故障してしまい、それから数ヶ月、mi.ijs01140.cfも休止することとなる。

実は安い中古サーバー機

その後、私は中古サーバー機が数千円でそれなりの性能のものが買えることを知る。自宅にサーバー機を置くことは私の憧れであったため、数日後にヤフオクXeon E5搭載中古サーバー機を落札した。そして、録画鯖機能とともにmi.ijs01140.cfをそのサーバー機に移転し、mi.ijs01140.cfは復活を遂げることになる。

そしてサーバーは増殖する

mst.ijs01140.cf誕生

2019年6月、私は新たにMastodon鯖を建てた。こちらは承認制で、誰でも登録可にしている。建てたのは、アスタルテの他においくら丼の影響が大きいかも。ただ、将来的に改造することを見越して、アスタルテではなくglitch-socを採用してみた。Mastodon本家でないのは、ローカル限定機能が欲しかったからだったり。

サーバー機が増えたよ!やったね!

メモリ容量に限界が生じたため、新たに中古サーバー機を買い足した。RAMを新たに調達するより、こちらのほうが実は安かったので・・・・ こちらは思い切ってOSにGentooを採用してみた。

mc.ijs01140.cf誕生

2019年10月、マイクラ鯖を建ててみた。1.14.4採用で、いくつかプラグインも導入してみた。

Mastodon立てたよ

12/23深夜3時頃、友人宅で自鯖立てたよ。やったね!3時間位でサクッと立ったよ!
ということで、末代から自鯖にアカウント移住しますというお知らせでした。これからもよろしくね。

mstdn.ijs01140.com

そういえば、しばらく記事書いてなかったね

そういえば、ijsは最近いろいろと忙しくて、ブログの記事かけてなかったなと今更ながらに思った。
なので、まあ暇を見つけてちょっとずつまた書いていきたいなと。

次回予告的な何か

たぶん、そのうちこんな記事書くと思うのでよろしく!

  • Ryzen2700Xでパソコン組んだ話
  • ニコニコチャンネルのアニメ最新話(無料)を自動DLするスクリプト書いた話
  • 4Kモニターに振り回される話
  • その他の小ネタ(BIOSの設定項目分かりづらい話とか、USBスピーカーの話とかまあいろいろ)

EDCB録画後PowerShellで自動エンコする(解説)

この記事は書きかけなので、後で大幅に追記する可能性が高いので注意
あと、この記事は解説が古いです

はじめに

この記事では、ijsがEDCBで使っているPowerShellスクリプト(PostRecEnd.ps1)についての概要を解説する。
このスクリプトでは、録画後のTSを自動エンコしてGoogleフォトに高画質モードで無限保存する。

スクリプト解説

スクリプト全体を見たい方は、以下の記事を参照してください。
ijs01140.hatenablog.com

EDCBから環境変数受け取るのに必要なやつ

# _EDCBX_DIRECT_

必要な関数

関数は先に書かないと動作しない。今回はTweetする部分だけ関数化している。
この関数は、Tweetする文をtxtに保存してTweetRubyスクリプトに渡している。

PostRecEnd.ps1側の記述
#===================Tweet用の関数=====================
#関数は使う前に書かないといけないみたいですね(白目)
function Tweet ($tw) {
    Write-Output "${tw}" | Out-File -LiteralPath 'C:\tools\script\tweet_utf8.txt' -Encoding UTF8
    ruby 'C:\tools\script\tweet_utf8.rb' 'C:\tools\script\tweet_utf8.txt'
    Write-Output "ツイート完了`:${tw}"
}
TweetRubyスクリプトの記述
#!/usr/bin/env ruby
require "twitter"
require "csv"

client = Twitter::REST::Client.new do |config|
  config.consumer_key        = '(ここにconsumer_keyを入力)'
  config.consumer_secret     = '(ここにconsumer_secretを入力)'
  config.access_token        = '(ここにaccess_tokenを入力)'
  config.access_token_secret = '(ここにaccess_token_secretを入力)'
end

talk = CSV.read( ARGV[0] )
client.update( talk[rand(talk.length - 1)][0] )

ログ出力設定

これで、エラー時に原因を特定しやすくなる

#====================ログ出力設定====================
Start-Transcript -LiteralPath "${env:FilePath}.log"

夜間電力を利用する(非推奨)

ただ単に夜間電力の時間帯になるまで待つというもの。処理が詰まる恐れがあるので非推奨。 ijsは現に使っていない。

#==============ピークシフト機能(大嘘)===============
#時間を取得し、7以上且つ22未満(昼)の場合22時まで待つ
#もちろん言わずもがな夜間料金のため
$nowhour=Get-Date -Format 'H'
if ($($nowhour -ge 7) -and $($nowhour -lt 22)) {
    #待ち時間の計算
    $waittime = 22 - $(Get-Date -Format 'HH') * 3600 - $(Get-Date -Format 'mm') * 60 - $(Get-Date -Format 'ss')
    start-sleep $waittime
}

変数・環境変数を設定

必要な変数や環境変数をここで設定する。ffmpegのパスやGoogleフォトへのアップロード用のフォルダのパス等はここで設定。

#====================環境変数設定====================
#ファイル名をタイトルバーに表示
$myHost = (Get-Host).UI.RawUI
$myHost.WindowTitle = "${env:FileName}.tsをエンコード中..."
#ffmpeg.exeがあるフォルダのパス
$FFFolderPATH='C:\tools\ffmpeg'
#一時的にmp4を吐き出すフォルダのパス
$MP4FolderPATH='D:\temp\mp4'
#backup and sync用フォルダのパス
$BASFolderPATH='D:\temp\gsync\tv'
#backup and sync用フォルダのパス
$treatedTSPATH='D:\treatedTS'
#10GB以上用フォルダのパス
$OtherFolderPATH='D:\temp\sizeover'
#Tweet用証明書の環境変数を設定
Set-Item env:SSL_CERT_FILE -Value 'C:\tools\script\cacert.pem'

エンコード時のビデオフィルタを設定

変数$VFILTERにビデオフィルタを順番に足していく

インタレース解除フィルタを追加

ffmpegでのインタレース解除フィルタでよく使われるもの(ijsの偏見)として、bwdifyadifが挙げられる。
yadifのほうが処理速度的には優れているが、品質はbwdifのほうが上である。パソコンの処理速度や求める品質を考慮して、どちらかお好きな方を。
FFmpeg Filters Documentation 10.12 bwdif
FFmpeg Filters Documentation 10.193 yadif
ijsの場合はbwdifを使っている。

#==========インタレース解除フィルタを追加==============
$VFILTER='bwdif=0:-1:1'
Write-Output 'インタレース解除を行います'
24fps化フィルタを追加

アニメや映画は、24fpsで制作されている場合が多い。しかし、テレビ放送は30fps(インタレース形式なので実質60fps)なので、フレームのダブリが生じてカクカク感が出てしまう。(鈍感なijsにはわからないけど)
そこで、アニメをエンコしている人たちなら誰しもが聞いたことのあるだろう、24fps化を行う。
・・・と言いたいところだが、アニメや映画でも24fps制作でない場合もあり、そこを自動判定するのはキツイものがある。

現にijsは即座に諦め、24fps制作であるとの情報を得られた某自称クソアニメだけを24fps化している。 ポプテピピックを検出する方法は簡単で、EDCBから出力される(TSファイル名).program.txtを読み込み、「ポプテピピック」という文字列が含まれるか判定するだけである。

#==============24fps化フィルタを追加==================
#ただのネタ(実用性の問題を解決できなかったため)
$decimatechk = $(Get-Content -LiteralPath "${env:FilePath}.program.txt" | Select-String -SimpleMatch 'ポプテピピック' -quiet)
if ($decimatechk -eq $True) {
    #$VFILTER="$VFILTER,decimate"
    $VFILTER += ',decimate'
    Write-Output 'ポプテピピックが検出されたので、24fps化します'
}
自動ロゴ消し

放送局をServiceNameから判定し、それぞれの放送局用に用意した局ロゴ画像を用いてロゴ消しする。
一応、ロゴ消した部分にフィルタ適用することも考えて、フィルタデータの入ったtxtを検知してビデオフィルタに自動で追加する機能もつけている。(が、まだ使っていない)
この部分は詳しく解説したいが、記事が見づらくなるのであえて概要に留める。詳しい解説については後日、単独で記事にまとめる予定。

#====================ロゴ消し設定====================
#ロゴ消しのため、放送局を判別する
#まだ画像用意できてない場合、エラーの原因になるので(それはそう)、用意できるまでコメントアウトする
$LogoNA=0
switch ($env:ServiceName) {
#BS
    "NHKBS1" {$BROADCASTER='nhkbs1'} #ロゴ画像の精度が怪しいので要チェック
    "NHKBSプレミアム" {$BROADCASTER='nhkbsp'}
    "BS日テレ" {$BROADCASTER='bsntv'}
    #"BS朝日1" {$BROADCASTER='bsasahi'}
    "BS-TBS" {$BROADCASTER='bstbs'}
    "BSジャパン" {$BROADCASTER='bsjapan'} #ロゴ画像の精度が怪しいので要チェック
    "BSフジ・181" {$BROADCASTER='bsfuji'} #ロゴ画像の精度、おそらく大丈夫だが要チェック
    "BS11イレブン" {$BROADCASTER='bs11'}
    #"BS12トゥエルビ" {$BROADCASTER='bs12'}
#地デジ
    "NHK総合1・福岡" {$BROADCASTER='nhk'} #ロゴ画像の精度、おそらく大丈夫、なはず
    "NHKEテレ1福岡" {$BROADCASTER='etv'}
    "FBS福岡放送1" {$BROADCASTER='fbs'}
    "KBCテレビ" {$BROADCASTER='kbc'}
    #"RKB毎日放送" {$BROADCASTER='rkb'}
    "TVQ九州放送1" {$BROADCASTER='tvq'}
    "テレビ西日本1" {$BROADCASTER='tnc'}
#その他
    default {$LogoNA = 1}
}
if ($LogoNA -eq 0) {
    #局別ロゴ消し用画像を使用し、removelogoフィルタを適用する
    $VFILTER += ",removelogo=${BROADCASTER}.png"
    Write-Output "放送局ロゴ消しに${BROADCASTER}.pngが使用されます"
    #局別フィルタ用txtファイルが存在する場合、読み込んでフィルタ適用
    if (Test-Path "C:\TV\logo\${BROADCASTER}.txt") {
        $logofilter = $(Get-Content -LiteralPath "C:\TV\logo\${BROADCASTER}.txt")
        $VFILTER += $logofilter
        Write-Output "ロゴ修正フィルタ適用:${logofilter}"
    }
} else {
    Write-Output "ロゴ消しは、いたしません"
}
デブロック・デノイズフィルタを追加

せっかく自分でエンコするのだから、品質改善ができるならしたいでしょ?ijsはしたい。ということでこのフィルタ適用で!
あと、ロゴ消し後の不自然な部分を目立たなくなるよう矯正するという目的もある。

#==========デブロック・デノイズフィルタを追加=========
$VFILTER += ',pp=ac'
Write-Output 'デブロック・デノイズフィルタが適用されます'
リサイズフィルタを追加

ffproveに録画ファイルをぶちこんで、吐き出されてきたログに1920x1080が含まれるか判定し、含まれてなかったら720pにリサイズする。
正直、ここの部分はもっとスマートに書けるはずなのだが、面倒なのでさっさと諦めてわかりやすい方法を使う。

#==============リサイズフィルタを追加================
#生TSのサイズが1920x1080か1440x1080か調べる
#.NET Freamworkのオブジェクト取得する方式はスマートに書けるけどスマートに書けなくてめんどくさいからこれまたボツね
Start-Process -FilePath "${FFFolderPATH}\ffprobe.exe" -ArgumentList "`"${env:FilePath}`"" -RedirectStandardError 'D:\temp\temp.txt' -Wait
$fhdchk = $(Get-Content -LiteralPath 'D:\temp\temp.txt' | Select-String -SimpleMatch '1920x1080' -quiet)
if ($fhdchk -ne $True) {
    $VFILTER += ',scale=1280:720:flags=lanczos+accurate_rnd'
    Write-Output '解像度が1440x1080なので、720pにリサイズされます'
} else {
    Write-Output '解像度が1920x1080なので、そのまま1080pでエンコします'
}
3次元デノイズフィルタを追加

これも上記のデブロック・デノイズフィルタと目的は同じで、品質改善が目的である。
このフィルタは処理速度向上を目的に、リサイズ後に適用する。

#==========3次元デノイズフィルタを追加=========
$VFILTER += ',hqdn3d=3.000'
Write-Output '3次元デノイズフィルタが適用されます'

ffmpegのビデオオプションを設定

ビデオフィルタは指定し終えたので、ビデオオプションを指定する。エンコード時のエンコーダやプリセットやCRF値はあえて変数化し、将来的に条件ごとに変更できるようにしている。
※ijsの使っているPCはQSVが使えないので、ソフトエンコを採用している※

#==============ffmpegのビデオオプションを設定==============
$ENCPRESET = 'medium'
$ENCFORMAT = 'libx265'
$ENCCRF = 24
$VideoOption = "-vf $VFILTER -crf $ENCCRF -c:v $ENCFORMAT -preset:v $ENCPRESET -g 300 -bf 16 -refs 16 -b_strategy 1 -pix_fmt yuv420p"

オーディオオプションを設定

ビデオオプションを設定ときたら、次はオーディオオプションを設定するのだなと大半の人は察したはずだ。そう、もちろん設定する。
ffmpegを知っている方なら、音声形式ならエンコード前も後も変わらないから-c:a copyでいいだろ!と思われるかもしれないが実際はそうはいかない。それをやってしまうと高確率で音ズレが訪れで阿鼻叫喚まっしぐらであるし、デュアルモノ音声の番組だと、片方のスピーカーからは日本語もう片方からは英語と、語学教材状態になってしまう。(また、厳密に言えば、同じAACでもMPEG-2とH.265では微妙に異なっているらしい)
それを防ぐためには、再エンコは免れない。どうせ音声の再エンコなんてすぐ終わるのだからそのくらい我慢しよう。

基本部分

エンコ後の音声形式をAACに指定する

$AudioOption = '-c:a aac' #エンコ後の音声形式をAACに設定(-c:a copyはあまりよろしくないので非採用)
デュアルモノ判定

テレビ放送で厄介なのが、デュアルモノ音声である。デュアルモノ音声とは、読んで時のごとく、言語ごとに片方ずつモノラル音声が割り当てられている2言語放送のことを言う。
この対策をしないと、先述した通り、片方のスピーカーからは日本語、もう片方からは英語が流れてくる、まさに語学教材状態になってしまう。
それを避けるために、デュアルモノ音声を判別する。
方法は、EDCBから出力される(TSファイル名).program.txtを読み込み、デュアルモノという文字列が含まれるか判定するというもの。含まれれば、それぞれの音声に128kbpsを割り当て、分離する。含まれていなければ、そのまま256kbpsにする。

$dualmonochk = $(Get-Content -LiteralPath "${env:FilePath}.program.txt" | Select-String -SimpleMatch 'デュアルモノ' -quiet)
if ($dualmonochk -eq $True) {
    Write-Output 'デュアルモノ音声が検出されました'
    $AudioOption += ' -b:a 128k -filter_complex channelsplit'
} else {
    $AudioOption += ' -b:a 256k'
}

ffmpegエンコード

いよいよ、お待ちかねのエンコードである。長かった。

注意点
  • ロゴ消し処理はカレントディレクトリにある画像しか使えないので、ロゴ消し用画像のあるフォルダをカレントディレクトリに設定する
  • 開始してすぐにエンコに失敗するとファイルが出力されなかったりファイルが0Bだったりするので、10回まではループして復旧を試みる
  • ffmpegはlogを全て標準エラー出力に出力するので、標準エラー出力をテキストに保存する
  • Start-ProcessのRedirectStandardErrはファイルパスでなぜかワイルドカード判定するので、一旦無難なファイル名で出力して後ほどリネーム・移動してやる
#====================エンコード====================
#エンコ開始時刻を取得
#ロゴ画像データがあるフォルダにカレントディレクトリを移動
Set-Location 'C:\TV\logo'
#ループ処理用
$CNT = 0
do {
    #10回までループし,それでもダメなら諦めて無限ループを回避
    $CNT = $CNT+1
    if ($CNT -gt 10) {
        #エンコに失敗したならtsとmp4を10GB以上用フォルダに移動
        Move-Item -LiteralPath "${env:FilePath}" "${OtherFolderPATH}" -force
        Move-Item -LiteralPath "${MP4FolderPATH}\${env:FileName}.mp4" "${OtherFolderPATH}" -force
        #予期せぬエラーが発生したことをツイートで警告
        Tweet "${env:FileName}.tsのエンコードを10回トライしてもダメだったよぉ。10GB以上用フォルダに退避させておいたから早く助けてあげて!! #ijsrec_err"
        Write-Output "${env:FileName}.tsのエンコードは異常終了しました。設定を見直しましょう。"
        exit 1
    }
    Write-Output "エンコード${CNT}回目"

    #録画の開始終了でビジーなので負荷を減らすために10秒待つ
    Start-Sleep -s 10

    #エンコ
    $FFOPTION="-analyzeduration 30M -probesize 100M -loglevel warning -y -fflags +discardcorrupt -i `"${env:FilePath}`" ${VideoOption} ${AudioOption} -map 0:p:${env:SID10}:0 -map 0:p:${env:SID10}:1 `"${MP4FolderPATH}\${env:FileName}.mp4`""
    Write-Output "FFOPTION`:${FFOPTION}"
    Start-Process "${FFFolderPATH}\ffmpeg.exe" -ArgumentList "${FFOPTION}" -RedirectStandardError 'D:\temp\postrecend_tmp.txt' -Wait #RedirectStandardErrorはなぜかワイルドカードパス扱いされるのでその問題を回避

    #====================mp4ファイルサイズ判別====================
    #エンコ後ファイルのサイズを環境変数"MP4SIZE"に指定
    $MP4SIZE = $(Get-ChildItem -LiteralPath "${MP4FolderPATH}\${env:FileName}.mp4").Length 
} while ($MP4SIZE -eq 0) #ファイルサイズが0バイトなら失敗とみなしループさせ復旧を試みる
エンコード時間とfps(処理速度)を取得

せっかくなら、処理速度を知りたいという気持ちからつけてみた機能。
ffmpegのlogから「encoded」の含まれる行を抜き出し、空白で区切り、目的の数値を取り出す。

#エンコード時間とfpsをffmpegのログから取り出す
#$EncodeTime = $(Get-Content -LiteralPath "${TSFolder}\${TSName}\logo.txt" | Select-String -Pattern 'encoded' -SimpleMatch | ForEach-Object { $($_ -split)[4] })
$EncodeTimeLine = $(Get-Content -LiteralPath 'D:\temp\postrecend_tmp.txt' | Select-String -Pattern 'encoded' -SimpleMatch) #encodedの含まれる行を抜き出し、変数EncodeTimeLineに格納
#エンコード時間
$EncodeTime = $(-split $EncodeTimeLine)[4] #変数EncodeTimeLineを空白文字で区切り、5つ目を取り出す
$EncodeTimeTrim = $($EncodeTime.substring(0,${EncodeTime}.length-1)) #最後から1文字(s)を除去
#fps
$EncodeFps = $(-split $EncodeTimeLine)[5] #変数EncodeTimeLineを空白文字で区切り、5つ目を取り出す
$EncodeFpsTrim = $($EncodeFps.substring(1,${EncodeFps}.length-1)) #最初から1文字(括弧)を除去
#エンコード時間とfpsを含め、エンコード完了をTweetする
Tweet "${env:FileName}.mp4エンコード完了。エンコードに要した時間は${EncodeTimeTrim}秒(${EncodeFpsTrim}fps)です。"

ファイルのアップロード

いよいよ、最終段階である。Googleフォトに高画質モードでうpする。
※ここでは、Googleフォトへのアップロード用のフォルダを用意し、Backup and Syncの設定まで済ませた前提で進める
Googleフォトは10GB制限があるので、
10GB以内の場合、エンコ後mp4をアップロード用のフォルダに移動し、関連ファイルをまとめて処理済みフォルダに移動、
10GB超過の場合、当該録画関連ファイルをまとめて処理失敗フォルダに移動する。

#10GB以下ならbackup and sync用フォルダ,大きいなら10GB以上用フォルダへ(10GB以上はうp出来ない)
#====================ファイル移動====================
if ($MP4SIZE -le 10GB) {
    Write-Output 'エンコ後10GB以内に収まったよ!やったね!'
    <#
    エンコしたファイルが10GB以下なら
    ・mp4をbackup and sync用フォルダに移動
    ・生TSやその関連ファイルを処理済みフォルダに移動
    #>
    Move-Item -LiteralPath "${MP4FolderPATH}\${env:FileName}.mp4" "${BASFolderPATH}\mp4" -force
    Move-Item -LiteralPath "${env:FilePath}" "${treatedTSPATH}" -force
    Move-Item -LiteralPath "${env:FilePath}.program.txt" "${treatedTSPATH}" -force
    Move-Item -LiteralPath "${env:FilePath}.err" "${treatedTSPATH}" -force
    Move-Item -LiteralPath 'D:\temp\postrecend_tmp.txt' "${treatedTSPATH}\${env:FileName}.ffmpeg.txt" -force
    #処理が正常終了したことをツイートで報告
    Tweet "${env:FileName}.mp4アップロード準備完了。エンコーダは${ENCFORMAT}、ファイルサイズは約$([math]::round(${MP4SIZE}/1MB, 2))MBです。"
} else {
    Write-Output '残念ながら、サイズオーバーです。エンコ設定を見直しましょう。'
    #エンコしたファイルが10GBより大きいならmp4とtsとその関連ファイルを10GB以上用フォルダに移動
    Move-Item -LiteralPath "${env:FilePath}" "${OtherFolderPATH}" -force
    Move-Item -LiteralPath "${MP4FolderPATH}\${env:FileName}.mp4" "${OtherFolderPATH}" -force
    Move-Item -LiteralPath "${env:FilePath}.program.txt" "${OtherFolderPATH}" -force
    Move-Item -LiteralPath "${env:FilePath}.err" "${OtherFolderPATH}" -force
    Move-Item -LiteralPath 'D:\temp\postrecend_tmp.txt' "${treatedTSPATH}\${env:FileName}.ffmpeg.txt" -force
    #10GBより大きいので手動でうpする必要があることをツイートで報告
    Tweet "${env:FileName}.mp4は残念ながら、サイズオーバーです。エンコ設定を見直しましょう。 #ijsrec_err"
}
#処理終了をTweetで知らせる
Tweet "${env:FileName}.mp4の処理が終了しました"
Write-Output '処理終了おつかれさん'
exit

エンコスクリプト全体

※必ず自己責任で使うこと※

ijs01140.hatenablog.com