LoginSignup
8
12

More than 1 year has passed since last update.

pythonで音付き動画再生!!(tkinter/imageio)

Last updated at Posted at 2020-05-04

0.記事の編集予定について

この記事の内容を執筆時から長い間研究?していたため、その結果を使ってもう一度書き直します。
公開まで今しばらくお待ちください....

1.動作確認環境

python3(python2での動作は未確認です。 2020/5/21追記)
linux(debian 64bit)
cpu: intel core i7

2.この記事のコード実行に必要なもの

5/20追記:
pip3 install tkmedia
とffmpegとportaudioのインストール(5/30追記)でインストールできるようになりました。

(翌年1月21追記)困ったことに、コードがおかしなことになっていて、pipの方が使えなさそうです。
修復しますので、今しばらくお待ちを...
あと、portaudioは、linuxを除き、sounddeviceにくっついてくるので、win and mac ではインストール要りません。
間違えた情報を投稿してしまい、すみませんでした。
linux(debian(ubuntu))ではlibportaudio0って名前で配布されてます。
sudo apt inatall libportaudio0で入ります。

python モジュール

tkinter(pythonに付属,linuxの場合python3-tkをインストール
imageio
pillow
pydub
sounddevice
numpy
あと、いくつかのpython付属モジュール
(全て"pip3 install [モジュール名]"でインストールできます。)

その他ソフトウェア

ffmpeg

(あと、そこそこの性能のPC(iから始まる会社の鉄腕OOOやセレOOは非推奨)

3.この記事のコードの利用方法

次のようにファイルを配置する。

media
|-__init__.py(空)
|-audio.py
|-video.py

"play_video" があるディレクトリで次を実行:

import media.video
import media.audio
import tkinter

video = video.Video()
audio = audio.Audio()

root = tkinter.Tk()
root.frame = tkinter.Label(root)
root.frame.pack()

video.openfile("[videofile_path]")
audio.openfile("[videofile_path]")

audio.play()
video.play()
root.mainloop()

ライセンスはフリーにしておきます。
編集、再配布、営利目的での使用等どうぞご自由に。(使う人がいるのか知りませんが)
できれば、コメントかメール(marusoftware@outlook.jp)にご一報いただけると幸いです。
あと、使用しているライブラリとソフトウェアのライセンスはよーくご確認ください。
ただし、筆者は一切責任を取りませんのでよろしくおねがいします。

4.音声の再生

pydubとsounddevice(とnumpy)を使う

audio.py
import sounddevice
import pydub
import time
import numpy

class Audio():
    def __init__(self):
        pass
    def openfile(self, filepath):
        if ".mp3" in filepath:
            self.segment = pydub.AudioSegment.from_file(filepath,codec="mp3")
        elif ".wav" in filepath:
            self.segment = pydub.AudioSegment.from_file(filepath,codec="wav")
        elif ".mp4" in filepath:
            self.segment = pydub.AudioSegment.from_file(filepath)
        else:
            self.segment = pydub.AudioSegment.from_file(filepath)
    def play(self, place=0):
        if self.segment.channels != 1:
            self.samples = numpy.array(self.segment.get_array_of_samples().tolist(),dtype="int16").reshape(-1,self.segment.channels)
        else:
            self.samples = numpy.array(self.segment.get_array_of_samples().tolist(),dtype='int16')
        sounddevice.play(self.samples,self.segment.frame_rate)
    def stop(self):
        sounddevice.stop()

5.映像の再生

imageio で読み込んでPILを介してtkinterのフレームに表示する(っていうのをマルチスレッドで行う)。

video.py
import tkinter
from tkinter import ttk
import imageio
from PIL import ImageTk, Image
import time
import threading
from imageio.plugins.ffmpeg import FfmpegFormat

class Video():
    def __init__(self):
        format = FfmpegFormat(
            "ffmpeg",
            "Many video formats and cameras (via ffmpeg)",
            ".mov .avi .mpg .mpeg .mp4 .mkv .wmv .webm",
            "I",
            )
        imageio.formats.add_format(format,True)#webm に対応させる。(だいぶ強引に)
    def openfile(self, file_path,frame):
        self.frame = frame
        try:
            self.video = imageio.get_reader(file_path)
        except imageio.core.fetching.NeedDownloadError:
            imageio.plugins.avbin.download()
            self.video = imageio.get_reader(file_path)
    def play(self):
        self.video_thread = threading.Thread(target=self._stream)
        self.video_thread.start()
    def stop(self):
        self.video_thread.stop()
    def _stream(self):
        start_time=time.time()
        sleeptime = 1/self.video.get_meta_data()["fps"]
        frame_now = 0
        for image in self.video.iter_data():
            frame_now = frame_now + 1
            if frame_now*sleeptime >= time.time()-start_time:
                frame_image = ImageTk.PhotoImage(Image.fromarray(image))
                self.frame.config(image=frame_image)
                self.frame.image = frame_image
                time.sleep(sleeptime)
            else:
                pass
8
12
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
12