因为家里老人不会用智能设备,所以做了一个超级简易的播放器。
要实现的功能有
1.拔电恢复
2.视频列表切换恢复
3.一键切换
4.自动切换电视剧
5.开机启动自动登录播放
先来看看开机自动登录吧:
实现自动登录:修改/etc/inittab文件
sudo nano /etc/inittab
向下滚动,找到行 1:2345:respawn:/sbin/getty 115200 tty1
注释掉这一行,修改为 #1:2345:respawn:/sbin/getty 115200 tty1
在该行下增加一行内容 1:2345:respawn:/bin/login -f pi tty1 </dev/tty1 >/dev/tty1 2>&1
按Ctrl+X退出,记得按Y键保存
原理:linux启动时,会读取inittab文件,执行其中的登录命令,以pi用户名自动登录
开机启动:
首先要开机挂载硬盘,不然无法播放视频:
开机挂载
/home/pi/start.sh 中写入下面的语句:
sudo -i
fdisk -l
mount /dev/sda1 /media/mymovie
其实用其他方式也可以的,后来也懒得去修改了。
开机启动
在/etc/rc.local 里面添加 ./home/pi/start.sh
然后start.sh里面添加播放脚本的运行
硬件连接
硬件连接必须先来,不然都没法调试了。
按键连接
这里是参考了帖子:http://hugozhu.myalert.info/2013/04/08/27-interrupts-with-gpio-pins.html
整体视图
程序代码
对于这整个系统,代码显得尤其重要。下面先贴上代码:
#!/usr/bin/env python2.7
#coding=utf-8
import os
import subprocess,time
import shutil
import thread
import string
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(25,GPIO.IN,pull_up_down=GPIO.PUD_UP)
global playProcess
single_movie_arr=[]
global single_movie_index
folder_arr=[]
global folder_index
global movies_in_folder_index
movies_in_folder_arr = []
global setting_in_progress
#1:folder 0:single
global single_or_folder
#1:manual 0:auto die
global dead_reason
root_dir = '/media/mymovie/movie'
class record_last:
def __init__(self):
self.hour=0
self.minute=0
self.second=0
self.is_folder=1
self.index=0
self.reseting=0
global my_record#history
global is_init
def write_history(name,current_index):
f = file(os.path.join(name,'history.txt'),'w')
f.write(str(current_index))
f.close
#must read after write or srt will be empyy
f = file(os.path.join(name,'history.txt'),'r')
f.close
def read_history(folder_name):
dest_file = os.path.join(folder_name,'history.txt')
if os.path.isfile(dest_file):
f = file(dest_file,'r')
line = f.readline()
print 'restore status:'+line
f.close
if line == '':
return 0
try:
return_val = int(line)
except:
return_val = 0
return return_val
else:
print 'restore status,no status'
return 0
def auto_switch():
global playProcess,single_movie_arr,single_movie_index,folder_arr,folder_index,\
movies_in_folder_index,setting_in_progress,single_or_folder,dead_reason,\
root_dir,movies_in_folder_arr,is_init,my_record
print 'auto switching...'
if single_or_folder == 1:
if movies_in_folder_index < len(movies_in_folder_arr) - 1:
movies_in_folder_index = movies_in_folder_index + 1
play_movie(folder_arr[folder_index],movies_in_folder_index,1)
else:
#special case:last movie in folder,we need to delete status
write_history(folder_arr[folder_index],0);
if folder_index < len(folder_arr) - 1:
folder_index = folder_index + 1
movies_in_folder_index = read_history(folder_arr[folder_index])#need change
play_movie(folder_arr[folder_index],movies_in_folder_index,1)
elif len(single_movie_arr) > 0:
single_movie_index = 0
single_or_folder = 0
play_movie(single_movie_arr[single_movie_index],0,0)
else:
folder_index = 0
movies_in_folder_index = read_history(folder_arr[folder_index])#need change
play_movie(folder_arr[folder_index],movies_in_folder_index,1)
else:
if single_movie_index < len(single_movie_arr) - 1:
single_movie_index = single_movie_index + 1
play_movie(single_movie_arr[single_movie_index],0,0)
elif len(folder_arr) > 0:
folder_index = 0
movies_in_folder_index = read_history(folder_arr[folder_index])#need change
single_or_folder = 1
play_movie(folder_arr[folder_index],movies_in_folder_index,1)
else:
single_movie_index = 0
play_movie(single_movie_arr[single_movie_index],0,0)
def keyboard_event(thread_name):
global playProcess,single_movie_arr,single_movie_index,folder_arr,folder_index,\
movies_in_folder_index,setting_in_progress,single_or_folder,dead_reason,\
root_dir,movies_in_folder_arr,is_init,my_record
while 1:
#raw_input('')
try:
GPIO.wait_for_edge(25,GPIO.FALLING)
time.sleep(1)#in case on press causes multiple interrupts
setting_in_progress = 1
dead_reason = 1
print 'switching...'
global playProcess
playProcess.stdin.write('q')
playProcess.stdin.flush()
if single_or_folder == 1:
if folder_index < len(folder_arr) - 1:
folder_index = folder_index + 1
movies_in_folder_index = read_history(folder_arr[folder_index])#need change
elif len(single_movie_arr) > 0:
single_movie_index = 0
single_or_folder = 0
else:
folder_index = 0
movies_in_folder_index = read_history(folder_arr[folder_index])#need change
else:
if single_movie_index < len(single_movie_arr) - 1:
single_movie_index = single_movie_index + 1
elif len(folder_arr) > 0:
folder_index = 0
single_or_folder = 1
movies_in_folder_index = read_history(folder_arr[folder_index])#need change
else:
single_movie_index = 0
setting_in_progress = 0
except KeyboardInterrupt:
time.sleep(1)
print 'GPIO fail!'
GPIO.cleanup()
def init_list():
global playProcess,single_movie_arr,single_movie_index,folder_arr,folder_index,\
movies_in_folder_index,setting_in_progress,single_or_folder,dead_reason,\
root_dir,movies_in_folder_arr,is_init,my_record
#add files in root dir to the list
nlist = os.listdir(root_dir)
for name in nlist:
cur_name = os.path.join(root_dir, name)
isfile = os.path.isfile(cur_name)
if isfile:
if name.endswith('.mp4') or name.endswith('.MP4'):
single_movie_arr.append(cur_name)
else:
folder_arr.append(cur_name)
single_movie_arr.sort()
print 'singles :'
for i in range(0,len(single_movie_arr)):
print single_movie_arr[i]
print 'folders :'
for i in range(0,len(folder_arr)):
print folder_arr[i]
def player_folder(name,current_index):
global playProcess,single_movie_arr,single_movie_index,folder_arr,folder_index,\
movies_in_folder_index,setting_in_progress,single_or_folder,dead_reason,\
root_dir,movies_in_folder_arr,is_init,my_record
print 'playing folder:'+name
movies_in_folder_arr=[]
nlist = os.listdir(name)
for single_name in nlist:
cur_name = os.path.join(name, single_name)
if single_name.endswith('.mp4') or single_name.endswith('.MP4'):
movies_in_folder_arr.append(cur_name)
movies_in_folder_arr.sort()
#for i in range(0,len(movies_in_folder_arr)):
# print movies_in_folder_arr[i]
write_history(name,current_index)
player_single(movies_in_folder_arr[current_index],current_index,len(movies_in_folder_arr))
def write_srt(name,current_index,total_num):
f = file('/media/mymovie/title/1.srt','w')
f.write('0\n')
f.write('00:00:00,000 --> 00:00:10,000\n')
if total_num == 1:
name = string.replace(name,'.mp4','',1)
name = string.replace(name,'.MP4','',1)
f.write('<b>正在播放 电影 '+name.split('/')[-1]+'<b>\n')
else:
f.write('<b>正在播放 电视剧 '+name.split('/')[-2]+'<b>\n')
f.write('<b>第'+str(current_index+1)+'集,共'+str(total_num)+'集<b>\n')
f.close
#must read after write or srt will be empyy
f = file('/media/mymovie/title/1.srt','r')
f.close
def player_single(name,current_index,total_num):
global playProcess,single_movie_arr,single_movie_index,folder_arr,folder_index,\
movies_in_folder_index,setting_in_progress,single_or_folder,dead_reason,\
root_dir,movies_in_folder_arr,is_init,my_record
print 'playing:'+name
write_srt(name,current_index,total_num)
if(is_init):
is_init = 0
pos_hour = str(my_record.hour)
pos_minute = str(my_record.minute)
pos_second = str(my_record.second)
if len(pos_hour) == 1:
pos_hour = '0'+pos_hour
if len(pos_minute) == 1:
pos_minute = '0'+pos_minute
if len(pos_second) == 1:
pos_second = '0'+pos_second
playProcess=subprocess.Popen(['omxplayer','--pos',pos_hour+':'+pos_minute+':'+pos_second,'--subtitles',\
'/media/mymovie/title/1.srt','--font',\
'/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc','--blank',name],\
stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE, close_fds=True)
else:
my_record.reseting = 1
playProcess=subprocess.Popen(['omxplayer','--subtitles','/media/mymovie/title/1.srt','--font',\
'/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc','--blank',name],\
stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE, close_fds=True)
playProcess.wait()
def play_movie(name,current_index,is_folder):
if is_folder == 1:
player_folder(name,current_index)
else:
player_single(name,current_index,1)
def record_thread(thread_name):
global playProcess,single_movie_arr,single_movie_index,folder_arr,folder_index,\
movies_in_folder_index,setting_in_progress,single_or_folder,dead_reason,\
root_dir,movies_in_folder_arr,is_init,my_record
while 1:
time.sleep(1)
if my_record.reseting == 1:
my_record.reseting = 0
my_record.hour = 0
my_record.minute = 0
my_record.second = 0
my_record.second = my_record.second + 1
if(my_record.second == 60):
my_record.minute = my_record.minute + 1
my_record.second = 0
if(my_record.minute == 60):
my_record.hour = my_record.hour + 1
my_record.minute = 0
dest_file = os.path.join(root_dir,'record.txt')
f = file(dest_file,'w')
f.write(str(single_or_folder)+'\n')
if single_or_folder == 1:
f.write(str(folder_index)+'\n')
else:
f.write(str(single_movie_index)+'\n')
f.write(str(my_record.hour)+'\n')
f.write(str(my_record.minute)+'\n')
f.write(str(my_record.second)+'\n')
f.close
#must read after write or srt will be empyy
f = file(dest_file,'r')
f.close
def read_from_record_file():
dest_file = os.path.join(root_dir,'record.txt')
record = record_last() #new structure
if os.path.isfile(dest_file):
#read
f = file(dest_file,'r')
line = f.readline()#single_or_folder
if line != '':
try:
record.is_folder = int(line)
line = f.readline()#index
record.index = int(line)
line = f.readline()#hour
record.hour = int(line)
line = f.readline()#minute
record.minute = int(line)
line = f.readline()#second
record.second = int(line)
except:
record.hour=0
record.minute=0
record.second=0
record.is_folder=1
record.index=0
else:
record.hour=0
record.minute=0
record.second=0
record.is_folder=1
record.index=0
f.close
else:
record.hour=0
record.minute=0
record.second=0
record.is_folder=1
record.index=0
record.reseting = 0
return record
is_init = 1
init_list()
my_record = read_from_record_file()
if my_record.is_folder == 1:
folder_index = my_record.index
movies_in_folder_index = read_history(folder_arr[folder_index])
single_or_folder = 1
single_movie_index = 0
else:
folder_index = 0
movies_in_folder_index = 0
single_or_folder = 0
single_movie_index = my_record.index
setting_in_progress = 0
dead_reason = 1
try:
thread.start_new_thread(keyboard_event,("Keyboard",))
except:
print 'start thread error!'
try:
thread.start_new_thread(record_thread,("Record Thread",))
except:
print 'start thread error!'
while 1:
while setting_in_progress == 1:
time.sleep(1)
if dead_reason == 1:
dead_reason = 0
if len(folder_arr) > 0 and single_or_folder == 1:
play_movie(folder_arr[folder_index],movies_in_folder_index,1)
elif len(single_movie_arr) > 0 and single_or_folder == 0:
play_movie(single_movie_arr[single_movie_index],0,0)
else:
print 'No any movie here!'
time.sleep(1000)
else:
auto_switch()
GPIO.cleanup()
整个代码已经是比较短的了,下面一个个函数来分析吧:
程序一开始执行read_from_record_file,这个函数的功能是读取上次记忆的功能,记忆的应该是文件的位置和电影的时间。
由于时间久远已经不太记得它的格式了。
它的目的是区分电影和电视剧。
这一段的功能是开了两个线程,一个是记录按钮事件的,另一个是不断记录目前的进度,防止断电。
try:
thread.start_new_thread(keyboard_event,("Keyboard",))
except:
print 'start thread error!'
try:
thread.start_new_thread(record_thread,("Record Thread",))
except:
print 'start thread error!'
这一片代码是判断是否dead。这里的dead的意思是是否按键切换。如果是按键切换则执行手动切换代码,否则的话执行auto_switch
while 1:
while setting_in_progress == 1:
time.sleep(1)
if dead_reason == 1:
dead_reason = 0
if len(folder_arr) > 0 and single_or_folder == 1:
play_movie(folder_arr[folder_index],movies_in_folder_index,1)
elif len(single_movie_arr) > 0 and single_or_folder == 0:
play_movie(single_movie_arr[single_movie_index],0,0)
else:
print 'No any movie here!'
time.sleep(1000)
else:
auto_switch()
接下来看看上面的一些函数:
record_thread
这个是将当前进度写入到文件
player_single
是播放一个文件
write_srt
是一开始播放的时候注明这是第几集
因为需要考虑到电影和电视剧,还有自动切换和手动切换。所以代码显得比较复杂。
自动切换分为两种,第一种是电视剧间切换,另一种是电影间切换
还要考虑到最后一个电视剧或者电影循环切回第一个。
若不用python的话相信会用掉更多的代码。
这个程序其实还可以改进,
1.再加一个按钮,方便后退
2.使用数据库记录历史数据
3.自动调整电视剧在屏幕上的缩放比例
4.断电恢复时也显示当前集数的字幕,而不是显示恢复播放
5.切换电视剧的时候不重新开始而是播放记录的部分