cosven / cosven.github.io

个人零碎笔记,博客草稿,阅读笔记
10 stars 0 forks source link

PyQt5 QtMultiMedia 写一个可以缓存音乐的播放器 #9

Closed cosven closed 7 years ago

cosven commented 8 years ago

背景描述,在Linux下,想使用 Qt 的 QtMultiMeida 这个模块写一个播放器,它在播放音乐的时候可以缓存这首音乐。

不缓存音乐

这样可以很简单

from PyQt5.QtWidgets import *
from PyQt5.QtMultimedia import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

app = QtWidgets.QApplication(sys.argv)
mp3_url = 'http://m1.music.126.net/Gybpf5bX9zfNesjXxZl3qw==/2053887720715417.mp3'
player = QMediaPlayer()
player.setMedia(QMediaContent(QUrl(mp3_url)))
player.play()
w = Widget()
w.show()
sys.exit(app.exec_())

带缓存的版本(未实现)

思路描述:之前以为可以很简单的写一个带缓存的版本。以为 QMediaPlayer,QtMultiMedia 这个模块提供了相关函数可以直接拿到缓存,不过经过一番探索,没能找到办法。

所以第二个思路,自己控制mediacontent. 也就是说我用 requests 先把音乐下载下来,然后在播放。恩,这样的确可以,但是常规办法貌似不能 边下载,边播放。所以想到了 QMediaPlayer 的 stream 播放模式,利用多线程,一个线程下载,一个线程播放。player = QMediaPlayer(flags=QMediaPlayer.StreamPlayback)

代码如下:

#! /usr/bin/env python3
import sys
import time

from PyQt5 import QtWidgets
from PyQt5.QtWidgets import *
from PyQt5.QtMultimedia import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *

from _thread import start_new_thread

import requests
import os

mp3_url = 'http://m1.music.126.net/Gybpf5bX9zfNesjXxZl3qw==/2053887720715417.mp3'    # 这首歌时间较短

mp3_url = 'http://m2.music.126.net/Jx2_zeePijuCGjqGSgTGyw==/6656443395918618.mp3'  # 这首歌时间较长

mp3_url = 'http://m2.music.126.net/7tEJKStKIbxKPVKw7urYkA==/6033020302257380.mp3'  # 这首歌时间很长

global i
i = 0

def print_duration(duration):
    time_text = QTime(0, (duration / 60000) % 60, (duration/ 1000) % 60)
    print("duration:", time_text.toString("mm:ss"))

def print_position(position):
    time_text = QTime(0, (position/ 60000) % 60, (position/ 1000) % 60)
    print('position:', time_text.toString("mm:ss"), i)

def print_media_changed(media):
    print ("media changed")

def print_buffer(status):
    print ('buffer', status)

class Player(QObject):
    def __init__(self):
        super().__init__()
        self.player = QMediaPlayer(flags=QMediaPlayer.StreamPlayback)
        # self.player.positionChanged.connect(print_position)
        self.player.durationChanged.connect(print_duration)
        self.player.currentMediaChanged.connect(print_media_changed)
        self.player.bufferStatusChanged.connect(print_buffer)
        self.tmp_pos = 0

    def start(self):
        print('start')
        start_new_thread(self.async, ())
        time.sleep(2)
        self.player.setMedia(QMediaContent(), self.music)
        self.player.play()

    def async(self):
        global i
        self.res = requests.get(mp3_url, stream=True)
        self.music = QFile('temp.mp3')
        # self.music = QBuffer()
        flag = self.music.open(QIODevice.ReadWrite)
        for chunk in self.res.iter_content(1024000):
            print('i:', i, end='')
            self.player.pause()
            self.music.seek(self.music.size())
            print ('file seek pos: ', self.music.pos(), end='')
            self.music.write(chunk)   # 这里有问题
            print ('file seek pos: ', self.music.pos())
            self.player.play()
            i += 1

        print("finished")

class Widget(QWidget):
    def __init__(self):
        super().__init__()
        self.player = Player()
        self.button = QPushButton('play', self)
        self.button.clicked.connect(self.player.start)

app = QtWidgets.QApplication(sys.argv)
w = Widget()
w.show()
sys.exit(app.exec_())

看似上面的代码可以实现缓存,但是实践发现存在一个问题。这一段

补充背景知识:player 在播放的时候,也会读 self.music 这个文件,所以它也会控制 self.music文件指针,seek到了文件的某个位置。

self.player.pause()
self.music.seek(self.music.size())  # 
print ('file seek pos: ', self.music.pos(), end='')
self.music.write(chunk)   # 这里有问题,有可能不会写到文件的末尾,而是文件的某个地方
print ('file seek pos: ', self.music.pos())
self.player.play()

所以换句话说,上面的问题变成了 多个线程操作一个文件 出现了冲突。但是它又有