Top > Network camera recording 最終更新日 : 2025/11/21

ネットワーク・カメラをLinuxで録画

  1.  概要
  2.  参考
  3.  事前準備
  4.  最短の録画スクリプト
  5.  実用的な録画スクリプト
  6.  ネットワーク・カメラの動作実績

1. 概要

ONVIF対応のネットワーク・カメラを ffmpeg で録画します。

ネットワーク・カメラの動画ストリームのURI rtsp://IPアドレス:554/・・・ は、固定ではなく都度変わる場合があるため、
ONVIFインターフェースを利用して動画ストリームのURIを都度取得して、ffmpeg の引数に指定します。


2. 参考

以下のサイトを参考にしました。

https://progzakki.sanachan.com/tools/recording-onvif-camera-by-ffmpeg/
https://qiita.com/ikeyasu/items/2a48588111d1b1e75f0c
https://brian0111.com/onvif-python-network-camera-control/
https://note.com/fine_shimit44/n/nc4e6906f73aa
https://kuttsun.blogspot.com/2024/02/python-onvif-rtsp.html


3. 事前準備

Pythonの onvif-zeep ライブラリーをインストールします。

pip install onvif-zeep

ネットワーク・カメラは、ONVIFを有効、エンコードをH.264に設定します。


4. 最短の録画スクリプト

最短のスクリプト

#!/usr/bin/python3

from onvif import ONVIFCamera

# カメラに接続
camera = ONVIFCamera( IPアドレス, ポート番号, ユーザーID, パスワード )

# Mediaサービスを取得
 media_service = camera.create_media_service()

# プロファイルを取得
profiles = media_service.GetProfiles()

# ストリーミングURIを取得
stream_uri = media_service.GetStreamUri( {'StreamSetup': {'Stream': 'RTP-Unicast', 'Transport': 'UDP'}, 'ProfileToken': profiles[プロファイル番号].token} ).Uri

# ストリーミングURIにユーザーIDとパスワードを挿入
stream_uri = stream_uri[0:7] + ユーザーID + ":" + パスワード + "@" + stream_uri[7:]

# カメラの録画開始
recording_file = 保存するディレクトリ + "/" + 任意のファイル名 + "_%Y%m%d_%H%M.mp4"

ffmpeg = [ "ffmpeg",
    "-i", stream_uri,
    "-nostdin",
    "-vcodec", "copy",                   ・・・ カメラに応じた設定
    "-acodec", "copy",                   ・・・ カメラに応じた設定
    "-f", "segment",
    "-segment_time", "出力ファイルの分割時間",
    "-segment_format", "mp4",
    "-reset_timestamps", "1",
    "-strftime", "1",
    recording_file
]

if 同期処理または非同期処理の選択条件:
    # 同期処理
    subprocess.run( ffmpeg )

else:
    # 非同期処理
    ffmpeg_process = subprocess.Popen( ffmpeg, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL )


5. 実用的な録画スクリプト

コマンド・ラインのオプションと引数の処理、エラー処理、ログ出力を実装した録画スクリプトです。

#!/usr/bin/python3

# 機能          ONVIF対応のネットワーク・カメラを録画する
#
# 引数          ipaddress port userid password outputpath
#
# オプション    -h              ヘルプ表示
#               -p 番号         録画するストリームのプロファイルの番号(デフォルト:0)
#               --video-codec   ffmpegの音声コーデック
#               --no-audio      ffmpegで音声を削除
#               --audio-codec   ffmpegの音声コーデック
#               --foregound     録画をフォアグラウンドで実行
#               --profiles      プロファイルを表示して終了
#               --stream-uri    ストリームURIを表示して終了
#
# 出力ファイル  出力パス/IPアドレス_日付_時刻.mp4
#
# ライブラリ    onvif-zeep
#
# ライブラリのインストール・コマンド
#               pip install ライブラリ名
#
# 変更履歴
# 2025/10/03  初版

import argparse, logging, os, subprocess, sys, syslog, traceback
from onvif import ONVIFCamera

CONSOLE_LOG = True

#===================================================================================================

def _info_logging( log ):

    logger = logging.getLogger(__name__)

    log = "INFO: " + log
    syslog.syslog( syslog.LOG_INFO, log )
    if CONSOLE_LOG:
        logger.info( log )

    return

#===================================================================================================

def _exception_logging( exc ):

    logger = logging.getLogger(__name__)

    tb = traceback.extract_tb( e.__traceback__, limit=1 )
    log = "ERROR: " + f"{camera_ip} {camera_port} {camera_userid} {camera_password} : {e.__class__.__name__} : LineNo={tb[0].lineno} [ {tb[0].line} ]"
    syslog.syslog( syslog.LOG_INFO, log )
    syslog.syslog( syslog.LOG_ERR, log )
    if CONSOLE_LOG:
        logger.error( log )

    return

#===================================================================================================

# コマンド・ラインのオプションと引数

parser = argparse.ArgumentParser( description=os.path.basename(sys.argv[0]) )

parser.add_argument( "-p", type=int, default=0, help="録画するストリームのプロファイルの番号(デフォルト: 0)" )
parser.add_argument( "--video-codec", default="", help="ffmpegの動画コーデック" )
parser.add_argument( "--no-audio", action="store_true", help="ffmpegで音声を削除" )
parser.add_argument( "--audio-codec", default="", help="ffmpegの音声コーデック" )
parser.add_argument( "--foreground", action="store_true", help="録画をフォアグラウンドで実行" )
parser.add_argument( "--profiles", action="store_true", help="プロファイルを表示して終了" )
parser.add_argument( "--stream-uri", action="store_true", help="ストリームURIを表示して終了" )
parser.add_argument( "ipaddress" )
parser.add_argument( "port" )
parser.add_argument( "userid" )
parser.add_argument( "password" )
parser.add_argument( "outputpath" )

args = parser.parse_args()

camera_ip = args.ipaddress
camera_port = args.port
camera_userid = args.userid
camera_password = args.password
output_path = args.outputpath

profile_number = args.p
ffmpeg_vcodec = args.video_codec
ffmpeg_an = args.no_audio
ffmpeg_acodec = args.audio_codec

if not os.path.isdir( output_path ):
    print( f"{os.path.basename(sys.argv[0])}: error: dir not found [{output_path}]",file=sys.stderr )
    sys.exit( 2 )

_info_logging( f"Starting camera recorder {camera_ip}" )

# カメラに接続
try:
    camera = ONVIFCamera( camera_ip, camera_port, camera_userid, camera_password )

except Exception as e:
    _exception_logging( e )
    sys.exit( 4 )

# Mediaサービスを取得
try:
    media_service = camera.create_media_service()

except Exception as e:
    _exception_logging( e )
    sys.exit( 4 )

# プロファイルを取得
try:
    profiles = media_service.GetProfiles()

except Exception as e:

    _exception_logging( e )
    sys.exit( 4 )

if args.profiles:
    print( profiles )
    sys.exit( 0 )

# ストリーミングURIを取得
try:
    stream_uri = media_service.GetStreamUri( {'StreamSetup': {'Stream': 'RTP-Unicast', 'Transport': 'UDP'}, 'ProfileToken': profiles[profile_number].token} ).Uri

except Exception as e:

    _exception_logging( e )
    sys.exit( 4 )

if args.stream_uri:
    print( stream_uri )
    sys.exit( 0 )

rtsp_camera_ip = f"rtsp://{camera_ip}"

if True and stream_uri[0:len(rtsp_camera_ip)].lower() == rtsp_camera_ip:
    # ストリーミングURIにユーザーIDとパスワードを挿入
    stream_uri = stream_uri[0:7] + camera_userid + ":" + camera_password + "@" + stream_uri[7:]

# カメラの録画開始

recording_file = output_path + "/" + camera_ip + "_%Y%m%d_%H%M.mp4"

ffmpeg = [ "ffmpeg",
    "-i", stream_uri,
    "-nostdin"
    ]

if ffmpeg_vcodec != "":
    ffmpeg += [ "-vcodec", ffmpeg_vcodec ]

if ffmpeg_an:
    ffmpeg += [ "-an" ]
elif ffmpeg_acodec != "":
    ffmpeg += [ "-acodec", ffmpeg_acodec ]

ffmpeg += [
    "-f", "segment",
    "-segment_time", "3600",
    "-segment_format", "mp4",
    "-reset_timestamps", "1",
    "-strftime", "1",
    recording_file
    ]

if args.foreground:
    # 同期処理
    print( " ".join(ffmpeg) + "\n" )
    subprocess.run( ffmpeg )

else:
    # 非同期処理
    ffmpeg_process = subprocess.Popen( ffmpeg, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL )
    _info_logging( f"Started camera recorder {camera_ip}: pid={ffmpeg_process.pid}: {recording_file}" )


6. ネットワーク・カメラの動作実績

上記のスクリプトで録画できるネットワーク・カメラは、以下のとおりです。

メーカー型番接続設定方法IPアドレスポート番号ユーザーIDパスワードプロファイル番号ffmpeg
動画コーデック
ffmpeg
音声コーデック
Ctronics880C-5MP有線、Wi-FiPCのブラウザー静的設定またはDHCP8080設定値設定値0 または 1copy
H.VIEWHV-500S2有線PoEPCの専用ツール静的設定またはDHCP80設定値設定値0 または 1copypcm_s16le
HiseeuJP-HD405-P有線PoEPCのブラウザー静的設定またはDHCP8888設定値設定値0 または 1copy
ieGeekIE60Wi-FiスマホのアプリDHCP80設定値設定値0 または 1copycopy