動画保存(MediaStreamSink コーデック:H.264)
概要
IC Imaging Control 3.5のWindows専用APIを使用したPythonのプログラムでH.264のコーデックを使用して動画を保存する方法について記載しています。
サンプルプログラム
サンプル(Python) | pythonnet-qt5-mediastreamsink.zip |
---|
サンプルの出力
コード全体
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtWidgets import QWidget,QMainWindow, QLabel, QSizePolicy, QApplication, QAction, QHBoxLayout,QMessageBox
from PyQt5.QtCore import *
import sys,traceback,os
import ctypes as C
import numpy as np
import cv2
# PyhtonNetをインポートする
import clr
# 同じフォルダ内にあるIC Imaging Control3.5のDllを参照する
clr.AddReference('TIS.Imaging.ICImagingControl35')
clr.AddReference('System')
# IC Imaging Control namespaceを宣言
import TIS.Imaging
from System import TimeSpan
class DisplayBuffer:
'''
このクラスは、ビデオウィンドウに表示するために画像をピックスマップにコピーするために必要です。
'''
locked = False
pixmap = None
def Copy( self, FrameBuffer):
if( int(FrameBuffer.FrameType.BitsPerPixel/8 ) == 4):
imgcontent = C.cast(FrameBuffer.GetIntPtr().ToInt64(), C.POINTER(C.c_ubyte * FrameBuffer.FrameType.BufferSize))
qimage = QImage(imgcontent.contents, FrameBuffer.FrameType.Width,FrameBuffer.FrameType.Height, QImage.Format_RGB32).mirrored()
self.pixmap = QPixmap(qimage)
class WorkerSignals(QObject):
display = pyqtSignal(object)
class DisplayFilter(TIS.Imaging.FrameFilterImpl):
'''
このフレームフィルターは、着信フレームをDisplayBufferオブジェクトにコピーし、新しいバッファーでQApplicationに信号を送ります。
'''
__namespace__ = "DisplayFilterClass"
signals = WorkerSignals()
dispBuffer = DisplayBuffer()
def GetSupportedInputTypes(self, frameTypes):
frameTypes.Add( TIS.Imaging.FrameType(TIS.Imaging.MediaSubtypes.RGB32))
def GetTransformOutputTypes(self,inType, outTypes):
outTypes.Add(inType)
return True
def Transform(self, src, dest):
dest.CopyFrom(src)
if self.dispBuffer.locked is False:
self.dispBuffer.locked = True
self.dispBuffer.Copy(dest)
self.signals.display.emit(self.dispBuffer)
return False
####################################################################################
def SelectDevice():
ic.LiveStop()
ic.ShowDeviceSettingsDialog()
if ic.DeviceValid is True:
ic.LiveStart()
ic.SaveDeviceStateToFile("device.xml")
def ShowProperties():
if ic.DeviceValid is True:
ic.ShowPropertyDialog()
ic.SaveDeviceStateToFile("device.xml")
def Close():
if ic.DeviceValid is True:
ic.LiveStop()
app.quit()
def imageCallback(x,y,buffer):
print("hallo")
return 0
def OnDisplay(dispBuffer):
videowindow.setPixmap(dispBuffer.pixmap)
dispBuffer.locked = False
app = QApplication(sys.argv)
w = QMainWindow()
w.resize(640, 480)
w.move(300, 300)
w.setWindowTitle('動画保存')
# メニュー作成
mainMenu = w.menuBar()
fileMenu = mainMenu.addMenu('&ファイル')
exitAct = QAction("&終了",app)
exitAct.setStatusTip("Exit program")
exitAct.triggered.connect(Close)
fileMenu.addAction(exitAct)
deviceMenu = mainMenu.addMenu('&デバイス')
devselAct = QAction("&選択",app)
devselAct.triggered.connect(SelectDevice)
deviceMenu.addAction(devselAct)
devpropAct = QAction("&プロパティ",app)
devpropAct.triggered.connect(ShowProperties)
deviceMenu.addAction(devpropAct)
layout = QHBoxLayout()
mainwindow = QWidget()
videowindow = QLabel()
layout.addWidget(videowindow)
mainwindow.setLayout(layout)
w.setCentralWidget(mainwindow)
# IC Imaging Controlオブジェクトを作成
ic = TIS.Imaging.ICImagingControl()
'''
ICは、親ウィンドウがないとライブビデオを表示できないので
フレームフィルタを使用して画像を取得し、メインスレッドに画像を表示するように指示
'''
#ic.LiveDisplay = True
# ライブ表示用に表示フィルターオブジェクトをインスタンス化
displayFilter = DisplayFilter()
# ディスプレイフィルターに接続する
displayFilter.signals.display.connect(OnDisplay)
ic.DisplayFrameFilters.Add( ic.FrameFilterCreate(displayFilter))
# MP4コンテナを取得
CurrentMediaStreamContainer = None
for container in TIS.Imaging.MediaStreamContainer.MediaStreamContainers:
if container.Name == "MP4":
CurrentMediaStreamContainer = container
if CurrentMediaStreamContainer is None:
msgBox = QMessageBox(text="MP4コンテナが見つかりません。")
msgBox.exec()
quit()
# MediaFoundation H.264コーデックを取得
try:
CurrentCodec = next(c for c in TIS.Imaging.AviCompressor.AviCompressors
if CurrentMediaStreamContainer.IsCodecSupported(c) and
c.Name == "MediaFoundation h.264")
except:
msgBox = QMessageBox(text="MediaFoundation h.264コーデックが見つかりません。!")
msgBox.exec()
quit()
# 拡張性を設定
Filename = "test." + CurrentMediaStreamContainer.PreferredFileExtension
# ビデオファイルに画像データを保存するためのsinkに接続
MediaStreamSink = TIS.Imaging.MediaStreamSink(CurrentMediaStreamContainer, CurrentCodec,
Filename)
ic.Sink = MediaStreamSink
#コーデックのプロパティページを表示
CurrentCodec.ShowPropertyPage()
try:
ic.LoadDeviceStateFromFile("device.xml",True)
if ic.DeviceValid is True:
#録画開始
ic.LiveStart()
except Exception as ex:
print(ex)
pass
w.show()
app.exec()
if ic.DeviceValid is True:
#録画停止
ic.LiveStop()
解説
ここでは主にH.264に保存するための方法について説明します。
displayFilterやコールバック関数の使用方法に関しては【Qtを使ったデモアプリ(pythonnet編)】を参考にしてください。
# MP4コンテナを取得
CurrentMediaStreamContainer = None
for container in TIS.Imaging.MediaStreamContainer.MediaStreamContainers:
if container.Name == "MP4":
CurrentMediaStreamContainer = container
if CurrentMediaStreamContainer is None:
msgBox = QMessageBox(text="MP4コンテナが見つかりません。")
msgBox.exec()
quit()
MediaStreamSinkは画像データを様々なビデオフォーマットで保存することができます。またオプション次第で一つ以上のFrameFiltersを指定し、動画ファイルとして書き込む前の段階で画像データの分析や変換も可能になります。
MediaStreamSinkは高度なビデオファイルのレコーディングが可能ですが、簡単な動画撮影は【AVIファイル保存】をご覧ください。
# MediaFoundation H.264コーデックを取得
try:
CurrentCodec = next(c for c in TIS.Imaging.AviCompressor.AviCompressors
if CurrentMediaStreamContainer.IsCodecSupported(c) and
c.Name == "MediaFoundation h.264")
except:
msgBox = QMessageBox(text="MediaFoundation h.264コーデックが見つかりません。!")
msgBox.exec()
quit()
このコードはAviCompressor.AviCompressorsプロパティを使って利用可能なコーデックのリストを作成し、 MediaFoundation h.264があればコーデックとしてセットします。
# 拡張性を設定
Filename = "test." + CurrentMediaStreamContainer.PreferredFileExtension
# ビデオファイルに画像データを保存するためのsinkに接続
MediaStreamSink = TIS.Imaging.MediaStreamSink(CurrentMediaStreamContainer, CurrentCodec,
Filename)
ic.Sink = MediaStreamSink
AVI録画を行うコーデックをためにMediaStreamSinkを作成し、IC Imaging Controlのシンク(画像の受け皿)にMediaStreamSinkを入れます。
try:
ic.LoadDeviceStateFromFile("device.xml",True)
if ic.DeviceValid is True:
#録画開始
ic.LiveStart()
except Exception as ex:
print(ex)
pass
w.show()
app.exec()
if ic.DeviceValid is True:
#録画停止
ic.LiveStop()
上記のMediaStreamSinkをIC Imaging Controlのシンクにつなぐだけで、ic.LiveStart()とic.LiveStop()を呼び出すだけで録画をすることができます。