2024,江端さんの技術メモ

仮想RTSPカメラの作り方と使い方のメモ

2024/11/21
江端
この仮想RTSPカメラは、クライアント(受信側)の接続を止めて、再度接続しようとするとクライアント(受信側)に動画が表示されなくなる、という欠点が発覚しております(2024/11/26) 
をお勧めします。
https://wp.kobore.net/2024/12/06/18206/

1. 背景と要件

1.1. 背景

  1. 地車間実験において複数カメラを準備する為、複数の仮想のRTSPカメラが必要となったため、これに対応する

1.2. 仮想RTSPカメラの要件

  1. 1台のWindowsPCにて最大4台程度の仮想RTSPカメラが実現できること
  2. 仮想カメラは指定したmp4ファイルの映像を配送する
  3. 上記のmp4ファイルは繰り返し再生できることが望ましい
  4. 仮想RTSPカメラは、通常のRTSPカメラと同様にrtsp://127.0.0.1:8554/testという形式で指定できること
  5. できるだけ簡易かつ汎用的なコマンドで実現できることが望ましい。

1.3. 事前検討

  1. 仮想RTSPカメラの実現方法としては、(1)ffmpeg, (2)Gstreamer, (3)VLC(VideoLAN Client), (4)C言語(Gstreamerライブラリ)が挙げられている。
  2. 検討の結果、mp4ファイルを繰り返し再生できる方法は指定されているが、試したところ実際に動いたのはVLCのみだったの、VLCで実現することにした。
  3. vlcのバージョンは、3.0.20以上であること(3.0.14では稼動しないことを確認済)。

2. 説明用の設定

  • 仮想RTSPカメラを設定するWindows10パソコンのIPアドレスを、便宜的に、192.168.0.3として取り扱うこととする。
  • 仮想RTSPカメラを再生するWindows10パソコンのIPアドレスを、便宜的に、192.168.0.25として取り扱うこととする。

3. VLCによる仮想カメラの作り方

3.1. 事前準備: 送信側(192.168.0.3)のファイアウォール設定設定

本節の設定は、設定しなくても動くこともあるので、動かなくなった場合のみに対応する。

送信側のVLCは、RTSP制御パケット(TCP:8554)とRTPデータストリーム(UDPポート)を送信する。これらがブロックされないようにする。

3.1.1. 受信規則の設定(クライアントからのRTSP制御パケットを許可)を許可)

  1. 「Windowsセキュリティ」懼「ファイアウォールとネットワーク保護」懼「詳細設定」を開きます。
  2. 左側の「受信規則」を右クリックして「新しい規則」を選択。
  3. **「ポート」**を選択して「次へ」。
  4. TCPを選択し、「特定のローカルポート」に8554を入力して「次へ」。
  5. 「接続を許可する」を選択し「次へ」。
  6. プロファイルを選択(プライベートにチェックを入れる)し「次へ」。
  7. 名前を設定(例: VLC RTSP TCP)して「完了」をクリック。

3.1.2. 送信規則の設定(RTP/RTCPデータを送信)を送信)

  1. 同様に「送信規則」を右クリックして「新しい規則」を選択。
  2. **「ポート」**を選択して「次へ」。
  3. UDPを選択し、「特定のローカルポート」に5000-5500を入力して「次へ」。
  4. 「接続を許可する」を選択し「次へ」。
  5. プロファイルを選択(プライベートにチェックを入れる)し「次へ」。
  6. 名前を設定(例: VLC RTP UDP)して「完了」をクリック。

3.2. 事前準備: 受信側(192.168.0.25)のファイアウォール設定設定

受信側が、Windows10でない場合は、本節は無視して下さい。

受信側のGStreamerは、RTSP制御パケット(TCP:8554)とRTPデータストリーム(UDPポート)を受信します。これらを許可します。

3.2.1. 受信規則の設定(RTSPとRTPストリームの受信を許可)を許可)

  1. 「Windowsセキュリティ」懼「ファイアウォールとネットワーク保護」懼「詳細設定」を開きます。
  2. 左側の「受信規則」を右クリックして「新しい規則」を選択。
  3. **「ポート」**を選択して「次へ」。
  4. TCPを選択し、「特定のローカルポート」に8554を入力して「次へ」。
  5. 「接続を許可する」を選択し「次へ」。
  6. プロファイルを選択(プライベートにチェックを入れる)し「次へ」。
  7. 名前を設定(例: GStreamer RTSP TCP)して「完了」をクリック。

3.2.1.1. RTPストリームのポート設定設定**

  1. 再度「受信規則」を右クリックして「新しい規則」を選択。
  2. **「ポート」**を選択して「次へ」。
  3. UDPを選択し、「特定のローカルポート」に5000-5500を入力して「次へ」。
  4. 「接続を許可する」を選択し「次へ」。
  5. プロファイルを選択(プライベートにチェックを入れる)し「次へ」。
  6. 名前を設定(例: GStreamer RTP UDP)して「完了」をクリック。

3.2.2. アプリケーション許可(必要に応じて)応じて)

GStreamerがファイアウォールでブロックされている場合、gst-launch-1.0の実行ファイルを許可します。

  1. 「Windows Defender ファイアウォールを介したアプリまたは機能を許可する」をクリック。
  2. 「別のアプリを許可する」をクリックし、GStreamerの実行ファイル(例: C:\msys64\mingw64\bin\gst-launch-1.0.exe)を指定して追加。
  3. 「プライベート」にチェックを入れて「OK」をクリック。

3.3. 仮想RTSPカメラの起動方法

以下のコマンド(例示)で起動する。

$ "C:\Program Files\VideoLAN\VLC\vlc.exe" -vvv "0326_JP.mp4" --sout="#rtp{sdp=rtsp://0.0.0.0:8554/test}" --loop

コマンドの内容は、以下の通りである。

  • "C:\Program Files\VideoLAN\VLC\vlc.exe": vlc.exeを起動するコマンドをフルパスで指定。
  • vvv: デバッグ情報を詳細に出力する。問題発生時のトラブルシューティングに役立つ。
  • "0326_JP.mp4": 再生するmp4のファイル名(このファイル名は例示であるので、使用時のファイル名を使用する)
  • rtsp://0.0.0.0:8554/test: RTSPアドレス名。0.0.0.0は特殊なアドレスで、「すべてのネットワークインターフェース」を意味する。サーバーがリッスンする対象が、特定のIPアドレスではなく、すべての有効なインターフェースで接続を受け付ける設定である。8554は、ポート番号である。RTSPのデフォルトポート番号は554であるが、アプリケーションによって異なるポート番号が指定されることがある。この例では8554を使用している。/testは、ストリームのパス名を示す。サーバー内でストリームを識別するための名前である。
  • loop: ファイルの繰り返し再生を指定する。

なお、上記のコマンドを投入すると、以下の黒い画面が立ち上がり、コマンドは直ちにリターンする。矢印は再生している位置を示している。

このコマンドを停止するには、この画面を右上の『・』を押下する。

3.4. 仮想RTSPカメラのフレームレート/画像サイズの変更方法

VLCを使って仮想RTSPカメラを実現した際に、送信するフレームレートや画像サイズ(解像度)を変更することは可能である。以下の方法で設定を変更する。

$ "C:\Program Files\VideoLAN\VLC\vlc.exe" -vvv "0326_JP.mp4" --sout="#transcode{vcodec=h264,fps=15,width=640,height=360}:rtp{sdp=rtsp://0.0.0.0:8554/test}" --loop

  • fps=15:
    • 送信するフレームレートを15fpsに設定する。
    • 必要に応じて、他の値(例: 3060)に変更可能。
  • width=640,height=360:
    • 解像度を640x360に設定しています。
    • 必要に応じて、他の解像度(例: 1920x1080640x480)に変更可能。

3.5. 複数の仮想RTSPカメラの起動方法

今回のケースでは、1台のPCで複数のカメラを実現するので、IPアドレスを固定として、異なるポート番号を使って、複数のカメラを実現することとする。

具体的には、以下のポート番号(8554, 8555, 8556)を変えて、それぞれコマンドを押下することで、同じmp4ファイルで3台のカメラが実現できる(もちろん、mp4ファイルを変えても良い)。

$ "C:\Program Files\VideoLAN\VLC\vlc.exe" -vvv "0326_JP.mp4" --sout="#rtp{sdp=rtsp://0.0.0.0:8554/test}" --loop

$ "C:\Program Files\VideoLAN\VLC\vlc.exe" -vvv "0326_JP.mp4" --sout="#rtp{sdp=rtsp://0.0.0.0:8555/test}" --loop

$ "C:\Program Files\VideoLAN\VLC\vlc.exe" -vvv "0326_JP.mp4" --sout="#rtp{sdp=rtsp://0.0.0.0:8556/test}" --loop

4. 仮想RTSPカメラの起動確認方法

4.1. VLCを使った起動確認方法

簡易な確認方法として、画像受信もVLCを使用する方法を提示する。

受信側Windows10(192.168.0.25)のVLCを立ち上げて、以下の操作を行う。

ネットワークURLに、rtsp://192.168.0.3:8554/testと入力する。

これで映像が表示されれば成功である。

4.2. GStreamerを使った起動確認方法

以下のコマンドを投入して下さい。

$ gst-launch-1.0 -v rtspsrc location=rtsp://192.168.0.3:8554/test latency=0 ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! glimagesink

(Windows10の場合は、autovideosinkでは動かないことが多い)

4.3. 注意点

仮想RTSPカメラはmp4ファイルを繰り返し再生するが、ファイル終了時にVLC(および殆どのクライアント)は、映像が停止したものと判断して再生を終了する。

現時点で、この対応方法は不明である。

以上

2024,江端さんの技術メモ

fastapiによるプロセスのスレッド停止に関する原因究明のメモ

1. 問題の背景と経緯

1.1. 問題の内容

  1. fastapiからC言語で作成したプログラムを起動すると、Cプログラムの中にあるスレッドが途中で停止するという問題が発生している。

  2. Cプログラム をfastapiプログラムから起動したの場合、udpマルチキャストの受信を⾏うスレッドが、数回〜10数回⽬で停⽌することが発覚。

1.2. 事前確認事項

  1. fastapiプログラムから Cプログラムを起動せず、コマンド(手入力)で起動した場合、この問題は発⽣しないことは確認済。

  2. また、1のCプログラム起動だけでなく、複数のCプログラムを起動した場合においても、この問題が発生しないことも確認済み。

  3. 以上より、この問題は、Cプログラム本体ではなくて、それを起動するfastapi(fastapiプログラム)側に問題があると推認された。

  4. この問題は、以下のfastapiプログラムのコードの差し替えによって解決することが判明している。

問題が発生するケースでのfastapiプログラムのコマンド発生プロセスのコード

(1)
pid = await run_command_and_get_pid(command)

(2)
task = asyncio.create_task(run_command_async(command))
process = await task
pid = process.pid

# なお、上記(1)(2)は、実質的には同じ処理である
問題が発生しないケースでのfastapiプログラムのコマンド発生プロセスのコード

with open(os.devnull, 'w') as devnull:
process = subprocess.Popen(shlex.split(command), stdout=devnull,
stderr=devnull)
pid = process.pid

2. 問題点の差分の明確化

この問題の発生/不発生は、以下の2点に集約できることが確認された。

  1. asyncio.create_task と await を併用した非同期処理
  2. 上記を使用しない、subprocess.Popen を使用したプロセス処理

3. fastapiプログラムの制御対象である、Cプログラムの特徴

Cプログラムは、映像転送を行うメインルーチンと、SRT転送の状況を常時把握するスレッド、自分と自分以外のCプログラムのプロセスをリアルタイムでカウントするスレッドと、稼動中のCプログラムに通信を行うためのスレッド(現在未使用)からなる、映像転送&プロトコル変換プログラムである。

alt text

1Mbps以上の映像転送を行いながら、映像転送環境を常に把握し続ける必要のある、リアルタイム性能が要求される高負荷の制御プログラムである。

4. 検討

問題の原因を分析するためには、いくつかの可能性を考慮する必要がある。特に、asyncio.create_task や await を使用した非同期処理と、subprocess.Popen を使用したプロセス管理の違いが影響している可能性がある。この点を中心に考察する。

4.1. asyncio.create_task や await を使用した非同期処理において、排除できる可能性

4.1.1. 非同期処理の影響の可能性

asyncio.create_task と await を使用した場合、非同期処理によってプロセスの状態が意図しない形で処理されている可能性があるが、今回はこれには該当していない。プロセス自体が稼動していることは確認している。したがって、Python のガベージコレクションやスケジューリングの問題によって、起動したプロセスが適切に管理されないケースは考慮しなくてよい。

4.1.2. タスクが解放された可能性

ログファイルを用いて確認した結果、asyncio.create_task を使用して作成されたタスクが適切に監視されず終了した形跡はないので、この件について考慮しなくてよい。

4.1.3. await によるブロックの影響の可能性

非同期関数内で await を使用してプロセスの終了を待っていると、FastAPI のリクエストが別の処理と競合し、プロセスの動作に影響を与える可能性はあるが、今回はCプログラムが1個の場合でも発生しているので、これも考慮する必要はない。

4.2. asyncio.create_task や await を使用した非同期処理において、排除できない可能性

4.2.1. イベントループのスケジューリングとリソースの競合

asyncio を使用すると、イベントループによってすべての非同期タスクがスケジュールされる。しかし、他のタスクやリクエスト処理が増えると、イベントループ全体の負荷が高まり、処理が遅延する可能性がある。

つまり、fastapiプログラムが、Cプログラムを含めた全体のタスク管理を行うため、Cプログラムの自由な実行が制限されるという問題が発生しうる。

この状況では、特定のタスク(今回の場合はsend_bitrate_or_count_processes のスレッド)への割り当てが遅れることで、スレッドが停止したかのような挙動が発生することがありえる。

4.2.2. プロセス管理の不安定さ

非同期タスクでは、プロセス管理がイベントループの中で行われるため、イベントループ自体の負荷やスケジュールの影響を受けやすくなる。

つまり、リアルタイム性の高い高タスクのCプログラムの実行が、fastapiプログラムの動作を遅らせ、その結果、fastapiプログラムの管理下にあるCプログラムの実行が制限されるという悪循環を発生させると考えうる。

4.2.3. プロセスのモニタリングの不安定さ

非同期処理では、プロセスの監視 (monitor_process_status) を含むさまざまなタスクが並行して実行される。この際、イベントループがプロセスの管理や状態確認の処理を後回しにすることで、プロセスの実行状態が適切に反映されず、スレッドが停止しているかのような挙動になったと考えうる。

4.3. subprocess.PopenによってCプログラムのスレッド問題が解決した理由理由

4.3.1. subprocess.Popen との違い

subprocess.Popen を使用して起動する場合は、Python の非同期処理機構に依存されることなく(fastapiプログラムの管理に組込まれないため)、独立してプロセスが管理される。このため、プロセスの状態が安定して保たれたと考える。

これは事実上、手入力でコマンドを押下する処理と同じことをやっており、前述の「コマンド(手入力)で起動した場合、この問題は発生しないことが確認済」という内容と整合が取れている。

5. 結論

今回のプログラムにおける問題の原因は、非同期タスクがイベントループのスケジューリングに依存している点に起因していると考えられる。

具体的には、非同期処理 (asyncio.create_task + await) のリソース競合やスケジューリングのタイミングによって、プロセスの管理や監視が適切に行われず、スレッドが途中で停止しているように見えたと考えられる。

以上より、subprocess.Popenの利用によってCプログラムのスレッド問題が解決したことは、合理的に説明ができる

以上

6. 付録

6.1. 「非同期タスクでのコマンド実行 (asyncio.create_task + await)」の意義(なんのために、このような仕組みが用意されているのか)

非同期タスク (asyncio.create_task + await) の意義は、主に以下の3つの観点に集約される

6.1.1. 高いスループットと効率的なリソース使用

非同期タスクは、待機中のリソース(たとえば、ネットワークI/OやファイルI/Oなど)を効率的に使用しながら、他のタスクの実行を並行して進めることを可能にする。これにより、CPUの使用率を高め、多くのリクエストやタスクを同時に処理することができる。例えば、クライアントからのHTTPリクエストに対する応答待ちの間に、他のタスクの処理を進めることができるため、高いスループットが実現される

6.1.2. ブロッキングの回避とレスポンスの向上

従来の同期的な実装では、一つの処理が完了するまで他の処理を待たせる必要がある(ブロッキング)。これに対して、非同期処理では、あるタスクがI/O待機などで停止している間にも、他のタスクが進行するため、アプリケーション全体のレスポンスが向上する。具体的には、ネットワークのレスポンスを待っている間に他の処理を進行させることで、CPUのアイドル時間を減らし、待機時間のロスを最小化する。

6.1.3. 大量の同時接続やリクエストの処理に強い

非同期タスクの仕組みは、大量の接続やリクエストを効率的に処理するために設計されている。特に、Webアプリケーションやマイクロサービスでは、多くのクライアントからの接続を管理し、レスポンスを返す必要がある。例えば、FastAPI などの非同期対応Webフレームワークでは、同時に多数のリクエストを処理し、レスポンスを返すことが求められる。このとき、asyncio.create_task と await を利用することで、各リクエストを非同期タスクとして並列に処理できる。

6.2. Cプログラムは、映像転送(中継)とプロトコル変換を行う典型的な制御プログラムで、リアルタイムのパフォーマンスが要求されるプログラムである。このようなプログラムの起動にfastapiを用いるのは正しい選択だったか?

FastAPIを使用してリアルタイムパフォーマンスが要求される制御プログラム(Cプログラム)を起動することは、設計上の選択としていくつかの利点と課題が存在すると考えた。

6.2.1. FastAPIを使用する利点

6.2.1.1. 軽量で高速な非同期フレームワーク:

FastAPIはPythonの非同期処理を活かして、高速かつ効率的にリクエストを処理できる。これにより、複数のリクエストを並列に処理することが可能であり、APIベースのコントロールが求められる場面では有効である。

6.2.1.2. シンプルで明快なAPI構築:

RESTful APIを簡単に構築できるため、外部からのコントロールやモニタリングが要求される制御プログラムにとっては適切である。また、外部から簡単にステータス確認やプロセスの制御が可能になる

6.2.1.3. スケーラビリティの確保:

FastAPIは、UvicornやGunicornといったASGIサーバーと組み合わせることで、スケーラブルな構成をとることができる。リクエストの多い状況や複数の制御要求が同時に発生するような環境で柔軟に対応できる。

6.2.2. FastAPIを制御プログラムに用いる際の課題と考慮点

一方で、リアルタイムのパフォーマンスを要求される制御プログラムにおいて、FastAPIの非同期タスクやイベントループの特性が以下のような問題を引き起こす可能性がある。

6.2.2.1. イベントループの負荷と競合:

FastAPIは非同期フレームワークであるため、複数のタスクが同時に実行されることを前提としている。しかし、リアルタイム制御を要するプログラム(映像転送やプロトコル変換など)の場合、イベントループの負荷が高まると、タスクの遅延やリソース競合が発生しやすくなる。

6.2.2.2. リアルタイム性能の不安定さ:

映像の中継やプロトコル変換を行うプログラムでは、タイミングやデータ転送の精度が重要である。FastAPIを介したプロセスの管理やコマンドの実行は、イベントループに依存するため、Pythonのインタープリタの遅延やスレッド管理の問題がリアルタイム性能を損なうリスクがある。

6.3. (付録の)結論

プロセスの制御や起動部分をFastAPIから分離する本件の方式は、fastapi + 制御プログラム の併用方式としては最適である、と考える。

2024,江端さんの技術メモ

頻繁に忘れれるので、メモ(バージョンによってコロコロ替わるので、これは一例)

sudo nano /etc/dhcpcd.conf
interface eth0
static ip_address=192.168.0.200/24
static routers=192.168.0.1
static domain_name_servers=8.8.8.8 8.8.4.4

======
別のケースとしては、
sudo nano /etc/netplan/99_config.yaml

 

network:
  version: 2
  renderer: networkd
  ethernets:
    eth0:
      dhcp4: false
      addresses:
        - 192.168.101.30/24
      routes:
        - { to: default, via: 192.168.101.1 }
      nameservers:
        addresses:
          - 8.8.8.8
          - 8.8.4.4
    enx207bd2222d29:
      dhcp4: false
      addresses:
        - 192.168.0.15/24

 

2024,江端さんの技術メモ

古いラズパイの再活用方法

 

1. 背景と目的

実験システム用にTCが2台必要となったので、2019年の実証実験で使っていたラズパイを改造してTCにすることにした。

2. 今回のターゲットとしたラズパイとその構成

ディストリビューションはRaspbianである。

Linux raspberrypi 4.19.66-v7+ #1253 SMP Thu Aug 15 11:49:46 BST 2019 armv7l GNU/Linux

ラズパイにはイーサポートが1つしかないので、2つ目のポートは、USB-Ethを使う。

Cable Matters USB LAN変換アダプター 有線LANアダプター USB イーサネットアダプタ USB2.0 to RJ45

ディストリビューションはRaspbianである。

3. 古いラズパイの再構築

3.1. 状況の把握

古いラズパイを再利用するために、sudo apt-get updateをすると、

E: Failed to fetch http://raspbian.raspberrypi.org/raspbian/pool/main/c/cyrus-sasl2/libsasl2-modules_2.1.27~101-g0780600+dfsg-3+deb9u1_armhf.deb 404 Not Found [IP: 2a00:1098:0:80:1000:75:0:3 80]

E: Failed to fetch http://raspbian.raspberrypi.org/raspbian/pool/main/libv/libvpx/libvpx4_1.6.1-3+deb9u2_armhf.deb 404 Not Found [IP: 2a00:1098:0:80:1000:75:0:3 80]

という表示がでてくる。

3.2 リポジトリの更新更新

この場合リポジトリのURLが古い可能性があるので。/etc/apt/sources.listまたは/etc/apt/sources.list.d/raspi.listを編集し、最新のリポジトリURLを使用していることを確認する。

sudo nano /etc/apt/sources.list.d/raspi.list

deb行を以下のように変更する:

deb http://archive.raspbian.org/raspbian/ buster main contrib non-free rpi

3.3 パッケージリストを更新更新

リポジトリURLを変更した後、以下のコマンドを実行してパッケージリストを更新する。

アップグレード手順の例:

sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade
sudo apt-get autoremove

必要に応じて、リリースをアップグレード:

sudo sed -i 's/stretch/buster/g' /etc/apt/sources.list
sudo sed -i 's/stretch/buster/g' /etc/apt/sources.list.d/raspi.list
sudo apt-get update
sudo apt-get upgrade

以上

2024,江端さんの技術メモ

これまで、動画の一部を切り取る、という作業をしてきましたが、有償ソフトであり、他の人には紹介できない状況でした。

しかし、先日、「OBS Studio」というフリーのソフトを教えてもらい、使ってみました。

ちょっとハマったところがあったので、自分の為にメモを残しておきます(私が分かりそうな部分は、ザックリ省略します)

Step 1 OBS起動

Step 2 「ウインドウキャプチャ」

で、ここからハマった

画面の切出しが分からない。ChatGPTに聞いたところ、『Altキーを押しながらドラッグで切り出せる』と言われたんだけど、
「Altキーを押しながらドラッグ」すると、画面全体を動かし、サイズも変えることはできますが、画面の部分を指定することができません。

で、なんどか質問を繰り返して

プレビュー画面の赤枠の「端」Altキーを押した状態でドラッグすると、その部分を切り取れます。

で分かりました。

Step 3 で、この後は、「録画開始」で切りとった部分だけが録画できるようになります。

今回は、比較的軽い「ハマり」で助かりました。

重要なのは「端」です。この一語がないだけで、ハマってしまうのが、この(IT)界隈の面倒くささです。

2024,江端さんの技術メモ

Windowsのmsys2でもmsys64でもUbuntu22.04でもdockerでも、VMAF機能尽きのffmpegが上手くいかなくて困っています。

g:\home\ebata\vmafに

FROM ubuntu:22.04

# 必要なパッケージのインストール
RUN apt-get update && apt-get install -y \
    wget build-essential yasm pkg-config libtool autoconf automake cmake git \
    libx264-dev libx265-dev meson ninja-build nasm python3-pip && \
    apt-get clean

# VMAFのビルドとインストール
RUN git clone https://github.com/Netflix/vmaf.git /vmaf && \
    cd /vmaf/libvmaf && \
    meson setup build --buildtype release && \
    ninja -C build && \
    ninja -C build install && \
    ldconfig

# FFmpegのビルドとインストール
RUN git clone https://git.ffmpeg.org/ffmpeg.git /ffmpeg && \
    cd /ffmpeg && \
    ./configure --enable-gpl --enable-libx264 --enable-libx265 --enable-libvmaf && \
    make && make install

# 実行用のディレクトリを作成
RUN mkdir /workspace
WORKDIR /workspace

で、

docker build -t custom-ffmpeg-vmaf .

をやっても、かならず、こんなエラーが出てきて、これが、どうしても消せない。

G:\home\ebata\vmaf>docker run --rm -it -v G:\download:/workspace ffmpeg-vmaf ffmpeg -i /workspace/104014_1280x720.mp4 -i /workspace/104014_1280x720.mp4 -lavfi libvmaf="model=/workspace/vmaf_v0.6.1.json" -f null -
ffmpeg version N-118070-g17e4746687 Copyright (c) 2000-2024 the FFmpeg developers
built with gcc 11 (Ubuntu 11.4.0-1ubuntu1~22.04)
configuration: --enable-gpl --enable-libx264 --enable-libx265 --enable-libvmaf
libavutil 59. 51.100 / 59. 51.100
libavcodec 61. 27.101 / 61. 27.101
libavformat 61. 9.101 / 61. 9.101
libavdevice 61. 4.100 / 61. 4.100
libavfilter 10. 6.101 / 10. 6.101
libswscale 8. 12.100 / 8. 12.100
libswresample 5. 4.100 / 5. 4.100
libpostproc 58. 4.100 / 58. 4.100
[Parsed_libvmaf_0 @ 0x55f5cc0b97c0] could not parse model config: /workspace/vmaf_v0.6.1.json
[AVFilterGraph @ 0x55f5cc0b91c0] Error initializing filters
Error : Invalid argument


2日間かけたのですが、疲れ果てたので、闘うのを止めました。

で、代替手段です。

■ブラウザのサービスを使う方法

VMAF on Browser

■VMAFを組み込んだffmpegのバイナリダウンロード
https://www.gyan.dev/ffmpeg/builds/
https://www.gyan.dev/ffmpeg/builds/#git-master-builds

ダウンロードして展開した場所の内部で(パスが通っていない)ffmpegを実行

G:\download\ffmpeg-2024-12-11-git-a518b5540d-full_build\bin>ffmpeg -i input.mp4 -i reference.mp4 -lavfi libvmaf -f null -

実行結果は以下の通り(欲しいのは以下の赤字のところだけ) # 今回は同じファイルを使って比較しただけ

G:\download\ffmpeg-2024-12-11-git-a518b5540d-full_build\bin>ffmpeg -i input.mp4 -i reference.mp4 -lavfi libvmaf -f null -
ffmpeg version 2024-12-11-git-a518b5540d-full_build-www.gyan.dev Copyright (c) 2000-2024 the FFmpeg developers
built with gcc 14.2.0 (Rev1, Built by MSYS2 project)
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads --disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2 --enable-gmp --enable-bzlib --enable-lzma --enable-libsnappy --enable-zlib --enable-librist --enable-libsrt --enable-libssh --enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2 --enable-libaribb24 --enable-libaribcaption --enable-libdav1d --enable-libdavs2 --enable-libopenjpeg --enable-libquirc --enable-libuavs3d --enable-libxevd --enable-libzvbi --enable-libqrencode --enable-librav1e --enable-libsvtav1 --enable-libvvenc --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxavs2 --enable-libxeve --enable-libxvid --enable-libaom --enable-libjxl --enable-libvpx --enable-mediafoundation --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi --enable-libharfbuzz --enable-liblensfun --enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm --enable-cuvid --enable-dxva2 --enable-d3d11va --enable-d3d12va --enable-ffnvcodec --enable-libvpl --enable-nvdec --enable-nvenc --enable-vaapi --enable-libshaderc --enable-vulkan --enable-libplacebo --enable-opencl --enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt --enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora --enable-libtwolame --enable-libvo-amrwbenc --enable-libcodec2 --enable-libilbc --enable-libgsm --enable-liblc3 --enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis --enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband --enable-libsoxr --enable-chromaprint
libavutil 59. 49.100 / 59. 49.100
libavcodec 61. 26.100 / 61. 26.100
libavformat 61. 9.100 / 61. 9.100
libavdevice 61. 4.100 / 61. 4.100
libavfilter 10. 6.101 / 10. 6.101
libswscale 8. 12.100 / 8. 12.100
libswresample 5. 4.100 / 5. 4.100
libpostproc 58. 4.100 / 58. 4.100
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'input.mp4':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp41isom
creation_time : 2024-02-14T12:54:44.000000Z
Duration: 00:00:30.46, start: 0.000000, bitrate: 2228 kb/s
Stream #0:0[0x1](und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(progressive), 722x444 [SAR 1:1 DAR 361:222], 2032 kb/s, 29.97 fps, 29.97 tbr, 29970 tbn (default)
Metadata:
creation_time : 2024-02-14T12:54:44.000000Z
handler_name : VideoHandler
vendor_id : [0][0][0][0]
encoder : AVC Coding
Stream #0:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 195 kb/s (default)
Metadata:
creation_time : 2024-02-14T12:54:44.000000Z
handler_name : SoundHandler
vendor_id : [0][0][0][0]
Input #1, mov,mp4,m4a,3gp,3g2,mj2, from 'reference.mp4':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp41isom
creation_time : 2024-02-14T12:54:44.000000Z
Duration: 00:00:30.46, start: 0.000000, bitrate: 2228 kb/s
Stream #1:0[0x1](und): Video: h264 (Constrained Baseline) (avc1 / 0x31637661), yuv420p(progressive), 722x444 [SAR 1:1 DAR 361:222], 2032 kb/s, 29.97 fps, 29.97 tbr, 29970 tbn (default)
Metadata:
creation_time : 2024-02-14T12:54:44.000000Z
handler_name : VideoHandler
vendor_id : [0][0][0][0]
encoder : AVC Coding
Stream #1:1[0x2](und): Audio: aac (LC) (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 195 kb/s (default)
Metadata:
creation_time : 2024-02-14T12:54:44.000000Z
handler_name : SoundHandler
vendor_id : [0][0][0][0]
Stream mapping:
Stream #0:0 (h264) -> libvmaf
Stream #1:0 (h264) -> libvmaf
libvmaf:default -> Stream #0:0 (wrapped_avframe)
Stream #0:1 -> #0:1 (aac (native) -> pcm_s16le (native))
Press [q] to stop, [?] for help
Output #0, null, to 'pipe:':
Metadata:
major_brand : mp42
minor_version : 0
compatible_brands: mp41isom
encoder : Lavf61.9.100
Stream #0:0: Video: wrapped_avframe, yuv420p(progressive), 722x444 [SAR 1:1 DAR 361:222], q=2-31, 200 kb/s, 29.97 fps, 29.97 tbn
Metadata:
encoder : Lavc61.26.100 wrapped_avframe
Stream #0:1(und): Audio: pcm_s16le, 48000 Hz, stereo, s16, 1536 kb/s (default)
Metadata:
encoder : Lavc61.26.100 pcm_s16le
creation_time : 2024-02-14T12:54:44.000000Z
handler_name : SoundHandler
vendor_id : [0][0][0][0]
[Parsed_libvmaf_0 @ 00000226c63e6640] VMAF score: 99.992967e=N/A speed=1.57x
[out#0/null @ 00000226c61d1ac0] video:392KiB audio:5616KiB subtitle:0KiB other streams:0KiB global headers:0KiB muxing overhead: unknown
frame= 913 fps= 47 q=-0.0 Lsize=N/A time=00:00:29.95 bitrate=N/A speed=1.55x

で、この評価の見方は以下の通りです。


1. [Parsed_libvmaf_0 @ 000001f6e343da40] VMAF score: 97.465053

  • [Parsed_libvmaf_0 @ ...]
    libvmafフィルタが処理を行ったことを示しています。
  • VMAF score: 97.465053
    最終的なVMAFスコアが97.465です。この値は、評価対象の動画(input.mp4)と参照動画(reference.mp4)の視覚的な品質の差異を数値化したものです。スコアは通常 0〜100 の範囲で表され、高いスコア(90以上)は非常に高品質であることを意味します。

2. e=N/A speed=0.299x

  • e=N/A
    現在のところ特定のエンコードステータスがありません(通常は気にしなくて大丈夫です)。
  • speed=0.299x
    処理速度を示しています。speed=0.299xは、処理がリアルタイムの29.9%の速度で進行したことを意味します。この低速さは、VMAF計算が非常に計算集約的であるためです。

3. [out#0/null @ 000001f6e1dc98c0] video:754KiB ... muxing overhead: unknown

  • video:754KiB
    出力された動画データの合計サイズが754KiBであることを示します。
  • audio:0KiB subtitle:0KiB other streams:0KiB
    音声、字幕、その他のストリームが含まれていないことを示します(VMAF評価ではこれらは不要)。
  • global headers:0KiB
    グローバルヘッダーのデータサイズも0KiBであることを示します。
  • muxing overhead: unknown
    出力ファイルのオーバーヘッド(余分なデータ量)が計算されていないことを示しています。nullフォーマットを使用しているため、通常は無視して問題ありません。

4. frame= 1755 fps= 18 q=-0.0 Lsize=N/A time=00:00:29.27 bitrate=N/A speed=0.299x

  • frame=1755
    VMAF計算の対象となったフレーム数です。1755フレームが処理されました。
  • fps=18
    平均で1秒間に18フレームが処理されたことを示します。
  • q=-0.0
    映像の品質パラメータで、VMAF評価では特に重要ではありません。
  • Lsize=N/A
    出力ファイルの総サイズ(nullフォーマットなので未計算)。
  • time=00:00:29.27
    処理された動画の総時間が29.27秒であることを示します。
  • bitrate=N/A
    ビットレートが計算されていないことを示します(評価中なので未計算)。
  • speed=0.299x
    処理速度がリアルタイムの約30%であることを再度示しています。

 

■VMAFについての評価方法や読み方については、以下が詳しいです。

【Netflixでも使ってる!】 映像品質評価ライブラリVMAFを使ったエンコードパラメータの決定方法の紹介

2024,江端さんの技術メモ

注意点は、通信プロトコルのメトリクスが2以上あろうとも、
main()の開始近くで

GMainLoop *main_loop = g_main_loop_new(NULL, FALSE);
を1回、

main()の終了近くで

// 共有のイベントループを実行
g_main_loop_run(main_loop);

// メインループをクリーンアップ
g_main_loop_unref(main_loop);

を、それぞれ1回だけ発行すること。

g_main_loop_run(main_loop)でプログラムは無限ループに入ってロックするので、その下のプログラムは実行されないことにも注意。

(以下はイメージ。コンパイルしても稼動しません)

#include <glib.h>
#include <gst/gst.h>
#include <pthread.h>

typedef struct {
    GstElement *pipeline;
    GMainLoop *main_loop;  // 共有のイベントループ
} ArgsRtspRTSP;

typedef struct {
    GstElement *srtserversink;
    GMainLoop *main_loop;  // 共有のイベントループ
} Args;

void *print_rtsp_metrics_thread(void *arg) {
    ArgsRtspRTSP *args = (ArgsRtspRTSP *)arg;
    // RTSPメトリクス計測のロジック
    while (1) {
        g_usleep(1000000); // 1秒ごとに計測
        g_print("RTSP Metrics: Calculating...\n");
    }
    return NULL;
}

void *print_srt_metrics_thread(void *arg) {
    Args *args = (Args *)arg;
    // SRTメトリクス計測のロジック
    while (1) {
        g_usleep(1000000); // 1秒ごとに計測
        g_print("SRT Metrics: Calculating...\n");
    }
    return NULL;
}

void initialize_rtsp_pipeline(ArgsRtspRTSP *args) {
    // パイプラインの初期化
    args->pipeline = gst_pipeline_new("rtsp-pipeline");
    GstBus *bus = gst_element_get_bus(args->pipeline);
    gst_bus_add_watch(bus, (GstBusFunc)gst_bus_async_signal_func, args->main_loop);
    gst_object_unref(bus);
}

void initialize_srt_pipeline(Args *args) {
    // パイプラインの初期化
    args->srtserversink = gst_element_factory_make("srtserversink", "srt-sink");
    GstBus *bus = gst_element_get_bus(args->srtserversink);
    gst_bus_add_watch(bus, (GstBusFunc)gst_bus_async_signal_func, args->main_loop);
    gst_object_unref(bus);
}

int main(int argc, char *argv[]) {
    gst_init(&argc, &argv);

    // 共有のイベントループを作成
    GMainLoop *main_loop = g_main_loop_new(NULL, FALSE);

    // RTSP引数の初期化
    ArgsRtspRTSP rtsp_args;
    rtsp_args.main_loop = main_loop;
    initialize_rtsp_pipeline(&rtsp_args);

    // SRT引数の初期化
    Args srt_args;
    srt_args.main_loop = main_loop;
    initialize_srt_pipeline(&srt_args);

    // スレッドの作成
    pthread_t rtsp_thread, srt_thread;

    pthread_create(&rtsp_thread, NULL, print_rtsp_metrics_thread, &rtsp_args);
    pthread_create(&srt_thread, NULL, print_srt_metrics_thread, &srt_args);

    // 共有のイベントループを実行
    g_main_loop_run(main_loop);

    // メインループをクリーンアップ
    g_main_loop_unref(main_loop);
    return 0;
}

ようするに、開始と終了は、1回のみ宣言する、ということ。

g_main_loop_run(main_loop) の後は、プログラムがそこでロックされて、その後のプログラムコードに進まないことに注意。

2024,江端さんの技術メモ

改めてGeoEntityDBの作り方の手順を示して下さい。

 

以下に、GeoEntityDB を作成する手順を改めて示します。


GeoEntityDBの作成手順

1. GeoEntityDBの作成

  1. PostgreSQLに接続します。
    psql -U postgres

     

  2. 新しいデータベースを作成し、PostGISを有効化します。
    CREATE DATABASE GeoEntityDB; \c GeoEntityDB CREATE EXTENSION postgis; CREATE EXTENSION pgrouting; -- 必要に応じて有効化

     


2. 地物テーブルの作成

地物データを格納するためのテーブルを作成します。

  1. テーブルスキーマを定義します。以下はカテゴリ付きの地物データ用のスキーマです。
    CREATE TABLE geo_entities ( id SERIAL PRIMARY KEY, -- 自動生成の一意識別子 category VARCHAR(10), -- カテゴリ (例: A1, B1, D2) stay_time INTEGER, -- 滞在時間 (分) geom GEOMETRY(POINT, 4326) -- 座標データ (EPSG:4326の緯度経度) );

    # id SERIAL PRIMARY KEY の部分は自動的にインポートされるので、csvの先頭の番号は行削除しておくこと

  2. インデックスを作成して検索性能を向上させます。
    CREATE INDEX geo_entities_geom_idx ON geo_entities USING GIST (geom);

     


3. CSVデータのインポート

CSVファイルから地物データをインポートします。

  1. 一時テーブルを作成(緯度・経度データを変換するために使用)。
    CREATE TEMP TABLE temp_geo_entities ( category VARCHAR(10), stay_time INTEGER, latitude DOUBLE PRECISION, longitude DOUBLE PRECISION );

     

  2. CSVをインポート(ファイルのパスは実際のCSVファイルの場所を指定してください)。
    COPY temp_geo_entities (category, stay_time, latitude, longitude) FROM '/path/to/geo_entities.csv' DELIMITER ',' CSV HEADER;

    # 実は、'/path/to/geo_entities.csv'の部分で酷く転けた記憶があるが、結果的にインポートできてしまったので、忘れた。

  3. 本テーブルに挿入(緯度・経度を POINT 型に変換して挿入)。
    INSERT INTO geo_entities (category, stay_time, geom) SELECT category, stay_time, ST_SetSRID(ST_MakePoint(longitude, latitude), 4326) FROM temp_geo_entities;

     

  4. データが正しくインポートされたか確認します。
    SELECT * FROM geo_entities LIMIT 10;

    #ここから先は試していないが、メモに残しておく。


4. 既存DBとの連携設定

方法 1: postgres_fdw を使った連携

hakata_ex_cost_db(道路ネットワークDB)と GeoEntityDB を連携させます。

  1. postgres_fdw を有効化(GeoEntityDBで実行)。
    CREATE EXTENSION postgres_fdw;

     

  2. 外部サーバーを登録
    CREATE SERVER hakata_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (dbname 'hakata_ex_cost_db', host 'localhost');

     

  3. 外部DBへのユーザーマッピングを作成
    CREATE USER MAPPING FOR current_user SERVER hakata_server OPTIONS (user 'postgres', password 'your_password');

     

  4. 必要なテーブルをインポート(例: 道路ネットワークテーブル)。
    IMPORT FOREIGN SCHEMA public FROM SERVER hakata_server INTO public;

     

  5. テーブルが使用可能か確認
    SELECT * FROM ways LIMIT 10;

     


5. 経路計算の設定

最短経路計算の基本例

エージェントの現在地から目的地(地物)までの経路を計算します。

  1. エージェントの現在地に最も近いノードを取得
    SELECT id FROM ways_vertices_pgr ORDER BY geom <-> ST_SetSRID(ST_MakePoint(130.3980676, 33.59543503), 4326) LIMIT 1;

     

  2. 地物の位置に最も近いノードを取得
    SELECT id FROM ways_vertices_pgr ORDER BY geom <-> (SELECT geom FROM geo_entities WHERE id = 1) LIMIT 1;

     

  3. Dijkstraアルゴリズムで経路計算
    SELECT seq, id1 AS node, id2 AS edge, cost FROM pgr_dijkstra( 'SELECT id, source, target, cost FROM ways', start_node_id, end_node_id, directed := false );

     


6. 地物データと経路計算の組み合わせ

以下のようなクエリで、地物データの位置と経路計算を連携させます。

  1. エージェントの現在地から15メートル以内の地物を検索(これ、上手く稼動しません。下の方は使って下さい)
    SELECT id, category, stay_time, ST_Distance(geom, ST_SetSRID(ST_MakePoint(130.3980676, 33.59543503), 4326)) AS distance FROM geo_entities WHERE ST_DWithin(geom, ST_SetSRID(ST_MakePoint(130.3980676, 33.59543503), 4326), 15);

     

  2. 発見した地物への最短経路を計算
    SELECT seq, id1 AS node, id2 AS edge, cost FROM pgr_dijkstra( 'SELECT id, source, target, cost FROM ways', start_node_id, (SELECT id FROM ways_vertices_pgr ORDER BY geom <-> (SELECT geom FROM geo_entities WHERE id = found_geo_id) LIMIT 1), directed := false );

     


まとめ

  1. GeoEntityDBの作成とデータインポート:
    • PostGISとpgRoutingを有効化し、CSVデータを地物テーブルに格納。
  2. 既存DBとの連携:
    • postgres_fdw を使用して、hakata_ex_cost_db の道路ネットワークを参照。
  3. 経路計算の実装:
    • エージェントの現在地と地物データを連携させ、道路ネットワーク上で最短経路を計算。

これにより、エージェントが嗜好に応じて地物に向かう動きをリアルにシミュレーションする仕組みを構築できます。

ーーーーー
こういうミスもやったので、以下のメモに残しておく。

SELECT * FROM geo_entities WHERE category = 'B1'; としたらエントリーがでてきませんが、
SELECT * FROM geo_entities WHERE category = ' B1'; としたらエントリーがでてきました。
categoryの要素でえ最初に空白が入っているものを修正したいのですが、どうしたら良いでしょうか

 

category の要素の先頭に空白が含まれている場合、それを修正するには TRIM 関数を使用して先頭と末尾の空白を取り除く必要があります。以下に、データを修正する方法を説明します。


手順

1. データの確認

まず、空白が含まれているデータを確認します。

SELECT * FROM geo_entities WHERE category LIKE ' %' OR category LIKE '% ';

 

  • LIKE ' %' は先頭に空白があるデータを検索します。
  • LIKE '% ' は末尾に空白があるデータを検索します。

2. 空白を修正

TRIM 関数を使用して、category カラムから空白を取り除きます。

すべてのエントリを修正する場合

以下のクエリで、空白を削除した値に更新します。

UPDATE geo_entities SET category = TRIM(category) WHERE category LIKE ' %' OR category LIKE '% ';

 


3. 修正結果の確認

修正後に、データが正しく更新されたか確認します。

SELECT * FROM geo_entities WHERE category LIKE ' %' OR category LIKE '% ';

 

  • クエリの結果が空であれば、すべての空白が正常に削除されたことを意味します。

4. 再度クエリを実行

修正後、以下のようにクエリを実行して正しいデータが取得できるか確認します。

SELECT * FROM geo_entities WHERE category = 'B1';

 


補足

  • TRIM の動作: TRIM 関数は、先頭と末尾の両方から空白を削除します(不要なスペースも含む)。
  • データの一時バックアップ: データ更新は破壊的変更となるため、念のため更新前にバックアップを取得しておくことをおすすめします。

これで問題が解決するはずです!もしさらなるサポートが必要であればお知らせください。


ざっくりとした使い方

 

geoentitydb=# \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
--------------------+----------+----------+-----------------+------------+------------+------------+-----------+-----------------------
geoentitydb | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
hakata_db | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
hakata_ex_cost_db | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |

geoentitydb=# SELECT id, category, stay_time, geom FROM geo_entities where id = 29;
id | category | stay_time | geom
----+----------+-----------+----------------------------------------------------
29 | B1 | 20 | 0101000020E610000006802A6E9C4C6040099FB5F44BCB4040

 

geoentitydb=# SELECT id, category, stay_time, ST_X(geom) AS longitude, ST_Y(geom) AS latitude FROM geo_entities where id = 29;
id | category | stay_time | longitude | latitude
----+----------+-----------+-------------+-------------
29 | B1 | 20 | 130.3940955 | 33.58825549

こんな感じででてくる。

以下は、130.4064827799044, 33.591348965846656 から 100メートル以内のジオエンティティを抽出するSQLです。(こっちが正しい)

geoentitydb=# SELECT id, category, stay_time, ST_Distance(geom::geography, ST_SetSRID(ST_MakePoint(130.4064827799044, 33.591348965846656), 4326)::geography) AS distance FROM geo_entities WHERE ST_DWithin(geom::geography, ST_SetSRID(ST_MakePoint(130.4064827799044, 33.591348965846656), 4326)::geography, 100);

id | category | stay_time | distance
------+----------+-----------+-------------
304 | A2 | 60 | 68.03775787
357 | A3 | 120 | 75.80618279
366 | A3 | 120 | 76.48825161
414 | D2 | 180 | 80.65210758
415 | D2 | 180 | 79.93815148
429 | D2 | 180 | 61.23626794
435 | D2 | 180 | 62.50441807
546 | E1 | 480 | 77.36793898
550 | E1 | 480 | 90.09868468
813 | E2 | 300 | 54.40053345
1174 | F2 | 10 | 92.85196166
1217 | F2 | 10 | 89.44133931
1933 | H2 | 30 | 64.5772006
1942 | H2 | 30 | 84.70916514
(14 rows)

2024,江端さんの技術メモ

エクスプローラで、ファイルをコピーする場合、同じファイル名の場合には時刻が新しい場合のみ上書きしたいですがどうすれば良いですか
ChatGPT
ChatGPT
Windowsエクスプローラでファイルをコピーする際、同じファイル名の場合は時刻が新しい場合のみ上書きするという機能は標準では提供されていません。しかし、以下の方法でこの操作を実現できます。

方法1: コマンドプロンプトでrobocopyを使用
Windowsにはrobocopyという強力なコマンドラインツールがあり、これを使用してファイルのコピーを行うと、指定した条件に基づいてファイルを上書きできます。

以下の手順で行います。

コマンドプロンプトを管理者権限で開く

以下のrobocopyコマンドを実行

cmd
コードをコピーする
robocopy "コピー元フォルダのパス" "コピー先フォルダのパス" /XO
ここで、/XOオプションは古いファイルを除外します。これにより、コピー元フォルダのファイルがコピー先フォルダのファイルより新しい場合にのみ上書きされます。


コピー元フォルダがC:\source、コピー先フォルダがD:\destinationの場合、次のように入力します。

cmd
コードをコピーする
robocopy "C:\source" "D:\destination" /XO


cmd.exeを管理者モードで立ち上げ

C:\WINDOWS\system32>robocopy "f:\しゅらばしゅう" "\\192.168.0.23\ynu-share\しゅらばしゅう_フルバックアップ" /XO /E /Z

C:\WINDOWS\system32>robocopy "\\192.168.0.10\usb_mount" "\\192.168.0.23\ynu-share\usb_mount_フルバックアップ" /XO /E /Z

C:\WINDOWS\system32>robocopy "g:home\ebata\tomioka3B\src" "\\192.168.0.9\share\tomioka3B\src" /XO /E /Z
C:\WINDOWS\system32>robocopy "g:home\ebata\hakata" "\\192.168.0.9\share\hakata" /XO /E /Z

======

robocopy "f:\しゅらばしゅう" "\\192.168.0.23\ynu-share\しゅらばしゅう_フルバックアップ" /XO /E /Z

 

robocopy "\\192.168.0.10\usb_mount" "\\192.168.0.23\ynu-share\usb_mount_フルバックアップ" /XO /E /Z

 

robocopy "g:home\ebata\tomioka3B\src" "\\192.168.0.9\share\tomioka3B\src" /XO /E /Z

 

robocopy "g:home\ebata\hakata" "\\192.168.0.9\share\hakata" /XO /E /Z

 

echo

======

-------------------------------------------------------------------------------
   ROBOCOPY     ::     Windows の堅牢性の高いファイル コピー
-------------------------------------------------------------------------------
  開始: 2024年6月23日 10:23:39
   コピー元 : G:\home\ebata\tomioka3B\src\
     コピー先 : \\192.168.0.9\share\tomioka3B\src\
    ファイル: *.*
  オプション: *.* /S /E /DCOPY:DA /COPY:DAT /Z /XO /R:1000000 /W:30
------------------------------------------------------------------------------
                           4    G:\home\ebata\tomioka3B\src\
                           2    G:\home\ebata\tomioka3B\src\.vscode\
                          20    G:\home\ebata\tomioka3B\src\Agent\
                           4    G:\home\ebata\tomioka3B\src\Agent\ldarp\
                           2    G:\home\ebata\tomioka3B\src\Agent\old\
                          14    G:\home\ebata\tomioka3B\src\Agent - kese09161446\
                           2    G:\home\ebata\tomioka3B\src\Agent - kese09161446\ldarp\
                           2    G:\home\ebata\tomioka3B\src\Agent - kese09161446\old\
                          14    G:\home\ebata\tomioka3B\src\Agent-kese-09161400\
                           2    G:\home\ebata\tomioka3B\src\Agent-kese-09161400\ldarp\
                           2    G:\home\ebata\tomioka3B\src\Agent-kese-09161400\old\
                           1    G:\home\ebata\tomioka3B\src\agent_db\
                          14    G:\home\ebata\tomioka3B\src\CartはAgentに併合\
                           1    G:\home\ebata\tomioka3B\src\CartはAgentに併合\ldarp\
                           2    G:\home\ebata\tomioka3B\src\CartはAgentに併合\old\
                           1    G:\home\ebata\tomioka3B\src\inc\
                           5    G:\home\ebata\tomioka3B\src\Join\
                           1    G:\home\ebata\tomioka3B\src\Join\ldarp\
                           3    G:\home\ebata\tomioka3B\src\ldarp\
                           4    G:\home\ebata\tomioka3B\src\old\
                          22    G:\home\ebata\tomioka3B\src\old\doc\
                         253    G:\home\ebata\tomioka3B\src\others\
                           1    G:\home\ebata\tomioka3B\src\others\.vscode\
                           8    G:\home\ebata\tomioka3B\src\others\chart\
                         105    G:\home\ebata\tomioka3B\src\others\csvファイルのバックアップ\
                           1    G:\home\ebata\tomioka3B\src\others\main49\
                           1    G:\home\ebata\tomioka3B\src\others\main50\
                           8    G:\home\ebata\tomioka3B\src\PrumeMobile\
                          10    G:\home\ebata\tomioka3B\src\PrumeMobile\chart\
                           9    G:\home\ebata\tomioka3B\src\PrumeMobile\chart2\
                           8    G:\home\ebata\tomioka3B\src\PrumeMobile\old\
                          12    G:\home\ebata\tomioka3B\src\PrumeMobile\static\
                          25    G:\home\ebata\tomioka3B\src\trip_cluster\
100%      新しいファイル                     105        #robocopy#
100%            新しい              7540        main.go
                          12    G:\home\ebata\tomioka3B\src\trip_cluster\bic\
100%      新しいファイル                     858        plot_cluster7.gnuplot
                           4    G:\home\ebata\tomioka3B\src\trip_cluster\bus\
100%      新しいファイル                     357        plot_cluster2.gnuplot
100%      新しいファイル                     858        plot_cluster2.gnuplot~
                          56    G:\home\ebata\tomioka3B\src\trip_cluster\data(最初に手動で作成)\
                           9    G:\home\ebata\tomioka3B\src\trip_cluster\walk\
100%      新しいファイル                     775        plot_cluster7.gnuplot
100%      新しいファイル                     858        plot_cluster7.gnuplot~
                        2001    G:\home\ebata\tomioka3B\src\trip_normalization\
100%            新しい              4466        main.go
100%      新しいファイル                    3678        main.go~
                          73    G:\home\ebata\tomioka3B\src\trip_test\
                          75    G:\home\ebata\tomioka3B\src\trip_test_tomioka2018\
100%            新しい             23276        main.go
100%      新しいファイル                     124        robocopy
                           0    G:\home\ebata\tomioka3B\src\trip_test_tomioka2018\main copy.go(5\
                          75    G:\home\ebata\tomioka3B\src\trip_test_tomioka2018_harugakkai\
                           6    G:\home\ebata\tomioka3B\src\trip_test_tomioka2018_harugakkai\others\
                           4    G:\home\ebata\tomioka3B\src\trip_tomioka2018\
                           4    G:\home\ebata\tomioka3B\src\trip_tomioka2018\others\
100%      新しいファイル                     587        dummy.txt
100%      新しいファイル                      58        dummy.txt~
100%            新しい              3272        main.go
        新しいディレクトリ       4      G:\home\ebata\tomioka3B\src\trip_tomioka2018_harugakkai\
100%      新しいファイル                  162161        agent_od_test.csv
100%      新しいファイル                    9022        libdarp.go
100%      新しいファイル                   15470        main.go
100%      新しいファイル                   18255        tools.go
        新しいディレクトリ       4      G:\home\ebata\tomioka3B\src\trip_tomioka2018_harugakkai\others\
100%      新しいファイル                  162161        agent_od_test.csv
100%      新しいファイル                     587        dummy.txt
100%      新しいファイル                      58        dummy.txt~
100%      新しいファイル                    3272        main.go
------------------------------------------------------------------------------
                  合計     コピー済み      スキップ       不一致        失敗    Extras
   ディレクトリ:        47         2        45         0         0         0
     ファイル:      2890        22      2868         0         0         0
      バイト:   35.39 m   408.0 k   34.99 m         0         0         0
       時刻:   0:00:02   0:00:01                       0:00:00   0:00:01
       速度:              278161 バイト/秒
       速度:              15.916 MB/分
   終了: 2024年6月23日 10:23:42