1. 程式人生 > 其它 >Tkinter做一個本地視訊播放器(2)——彈幕

Tkinter做一個本地視訊播放器(2)——彈幕

前文我們已經完成了一個集暫停、倍速、顯示進度條功能為一體的視訊播放器,今天我們再來增加一個新的功能——傳送彈幕。

tkinter播放視訊的原理,就是讀取每一幀的圖片,然後重新整理畫布。所以如果想實現彈幕功能,只要獲取輸入文字框中的文字,再寫到圖片上就好了。cv2.putText能夠實現這個功能,但無法新增中文,所以我們需要換一種方法。

首先,我們需要from PIL import ImageDraw,ImageFont

然後用ImageDraw.Draw轉換圖片格式,轉換後就可以呼叫.text()往圖片上寫中文了。

具體實現過程,只需要修改前文的一個函式:

def tkImage(n):
    global nowt,pointx,txt
    #倍速在這裡實現
    for i in range(n):
        ref,frame = vc1.read()
    pilImage = Image.fromarray(frame)
    draw = ImageDraw.Draw(pilImage)
    font = ImageFont.truetype("simhei.ttf", 40, encoding="utf-8")  #引數1:字型檔案路徑,引數2:字型大小
    #txt是讀取的Entry內容,在button關聯的函式中獲取
    if txt!="":
        draw.text((pointx, pointy), txt, (255, 255, 255), font=font)  #pointx, pointy是文字新增的位置
        pointx-=10  #每次向左移動10個單位
        if(pointx==0):
            pointx=size[1]
            txt=""
    pilImage = cv2.cvtColor(np.array(pilImage), cv2.COLOR_BGR2RGB)
    pilImage = Image.fromarray(pilImage)
    pilImage = pilImage.resize((window_width, window_height),Image.ANTIALIAS)
    tkImage =  ImageTk.PhotoImage(image=pilImage)
    
    nowt+=n  #記錄當前的幀數
    return tkImage

  

現在我們把前文的顯示四張影象改成一張,重新整理一下程式碼:

import time
import tkinter as tk
from tkinter import *
from tkinter import ttk
import cv2
from PIL import Image, ImageTk,ImageFilter,ImageDraw,ImageFont
import multiprocessing
import numpy as np
import random
import os

filePath = 'D:\\movie\\'  #電影存放路徑
mlist=os.listdir(filePath)  #資料夾下所有檔名稱
window_width=960  #介面寬
window_height=760  #介面長
image_width=int(window_width*0.5)  #影象寬
image_height=int(window_height*0.5)  #影象長
imagepos_x=0  #畫布位置x
imagepos_y=0  #畫布位置Y
lock=0  #暫停標誌
n=1  #初始倍速
nowt=0  #當前幀數
nows=0  #當前播放時長(秒)
pointx=0  #彈幕起始點橫座標
pointy=0  #彈幕起始點縱座標
txt=""  #彈幕內容

#獲取當前影象
def tkImage(n):
    #倍速在這裡實現
    global nowt,pointx,pointy,txt
    for i in range(n):
        ref,frame = vc1.read()
    pilImage = Image.fromarray(frame)
    draw = ImageDraw.Draw(pilImage)
    font = ImageFont.truetype("simhei.ttf", 40, encoding="utf-8")#引數1:字型檔案路徑,引數2:字型大小
    if txt!="":
        draw.text((pointx, pointy), txt, (255, 255, 255), font=font)
        pointx-=10
        if(pointx==-50):
            pointx=size[1]
            txt=""
    pilImage = cv2.cvtColor(np.array(pilImage), cv2.COLOR_BGR2RGB)
    pilImage = Image.fromarray(pilImage)
    pilImage = pilImage.resize((window_width, 720),Image.ANTIALIAS)
    tkImage1 =  ImageTk.PhotoImage(image=pilImage)
    
    nowt+=n
    return tkImage1

#影象的顯示與更新
def video():    
    global nows,nowt
    def video_loop():
       global nows,nowt
       try:
            while True:
                if lock % 2 == 0:  #是否暫停
                    picture1=tkImage(n)
                    if nowt >= fps:
                        nowt=0  #每過一秒則清零重計
                        nows+=1  #每過一秒,當前播放時間也加1
                        mi=str(int(nows/60))  #分鐘
                        se=str(int(nows%60))  #秒
                        if int(mi)<10:
                            mi="0"+mi
                        if int(se)<10:
                            se="0"+se
                        showt=mi+":"+se  #當前時間
                        show=showt+"/"+totalt  #最終顯示格式(當前時間/總時長)
                        tlabel.config(text=show)
                        canvas.coords(fill_line, (0, 0, int(window_width/tt*nows), 15))  #填充進度條
                    canvas1.create_image(0,0,anchor='nw',image=picture1)  
                    win.update_idletasks()  
                    win.update()
                else:
                    win.update_idletasks()  
                    win.update()
       except:
            return
    #每次開始播放前初始化
    nows,nowt=0,0
    canvas.coords(fill_line, (0, 0, 0, 15))        
    
    video_loop()
    vc1.release()
    cv2.destroyAllWindows()

#右鍵事件:倍速
def right(self):
    global n
    n+=1
    if n>4:
        n=1
#左鍵事件:暫停
def left(self):
    global lock
    lock+=1
#按鈕事件:獲取彈幕
def get_txt():
    global txt,pointx,pointy
    txt=E1.get()
    pointx=size[1]  #初始x座標
    pointy=random.randint(50,150)  #初始y座標隨機
    E1.delete(0, END)
#播放選中檔案
def start():
    global vc1,size,frames_num,fps,totalt,tt,pointx
    val = theLB.get()  #獲得下拉框當前內容
    vc1 = cv2.VideoCapture(filePath+val)  #讀取視訊
    size = (int(vc1.get(cv2.CAP_PROP_FRAME_HEIGHT)), int(vc1.get(cv2.CAP_PROP_FRAME_WIDTH)))  #視訊影象的長和寬
    frames_num=vc1.get(cv2.CAP_PROP_FRAME_COUNT)  #總幀數
    fps = vc1.get(cv2.CAP_PROP_FPS)  #幀率
    totalt=str(int(frames_num/fps/60))+":"+str(int(frames_num/fps)%60)  #視訊時長
    tt=int(frames_num/fps)  #進度條疊滿所需次數
    pointx=size[1]  #彈幕起始座標x
    video()
#跳轉到指定位置
def kj():
    global nows,vc1
    time=int(int(s1.get())*frames_num/fps/100)  #要跳轉到的時長
    vc1.set(cv2.CAP_PROP_POS_FRAMES,int(time*fps))  #讀取到指定幀數
    ref,frame = vc1.read()
    nows=time

'''佈局'''
win = tk.Tk()
win.geometry(str(window_width+120)+'x'+str(window_height+20))
#顯示視訊的畫布
canvas1 =Canvas(win,bg='white',width=window_width,height=720)
canvas1.place(x=imagepos_x,y=imagepos_y)
canvas1.bind('<Button-1>', left)
canvas1.bind('<Button-3>', right)
#顯示進度條的畫布
canvas = Canvas(win, width=image_width*2, height=15, bg="white")
canvas.place(x=0, y=722)
fill_line=canvas.create_rectangle(0,0,0,15,fill = 'LightGreen')  #座標是相對於畫布的
#彈幕輸入框
E1 = Entry(win, bd =5,width=100)
E1.place(x=0, y=745)
#傳送彈幕按鈕
B1 = Button(win, text="傳送", command=get_txt,font=('黑體', 10),fg='blue',width=10,height=2)
B1.place(x=750, y=742)
#顯示時間的Label
tlabel = Label(win,font=('黑體', 13),text='')
tlabel.place(x=850, y=750)
#選擇影片下拉框
theLB = ttk.Combobox(win,width=12,height=10)
theLB["values"] = mlist
theLB.current(0)  #預設選第一個
theLB.place(x=965, y=0)
#播放影片按鈕
B2 = Button(win, text="播放", command=start,font=('黑體', 10),fg='red',width=10,height=2)
B2.place(x=980, y=50)
#跳轉按鈕
B3 = Button(win, text="跳轉", command=kj,font=('黑體', 10),fg='red',width=10,height=2)
B3.place(x=980, y=150)
#選擇跳轉位置的滾動條
s1 = Scale(win,from_=0,to=99,orient=HORIZONTAL)  #orient=HORIZONTAL設定水平方向顯示
s1.place(x=970, y=100)
win.mainloop()

  

上述程式碼不僅實現了我們所說的功能,我還用下拉框Combobox自動載入資料夾下的所有檔名,以供選擇檔案播放;並且能夠拖動滾動條跳轉到指定位置,利用了cv2.VideoCapture的.set()方法。

怎麼樣,更像一個視訊播放器了吧?如果你還想往下做,可以加入“新增到播放列表”的功能,選擇其他路徑下的檔案新增到全域性變數mlist裡,然後更新一下Combobox的values即可。