最终屏幕录制方案
屏幕錄制方案
需求
- 實現任務錄制任務下發后自動將動畫和音頻錄制為MP4的視頻
- 后臺服務
- 錄制進度實時更新
- 后續分享到視頻播放平臺,如愛奇藝、抖音等
方案架構
- django command啟動服務
- gearman提交下發任務
- 后端采用PyQt5搭建服務平臺
- QProcess執行ffmpeg錄制屏幕命令
- QThread維持gearmanworker接受任務
- WebDriver加載定制頁面播放動畫及音頻
前端
- 實現動畫加載,圖片按瀏覽器寬高比率放置
- 加載完成之后修改特定標簽文本值
- 點擊事件觸發以后播放音頻,后續頁依次自動播放
- 整個動畫文件播放完畢以后修改特定標簽文本值
后端
- 搭建服務平臺
- 執行ffmpeg錄制屏幕
- 維持gearmanworker運轉,接收屏幕錄制任務
- 監測前端錄制進程,控制錄制的開始和結束
- 實時上傳錄制進度
- 上傳視頻至cos
錄制流程圖
錄制流程說明
搭建服務平臺
django manage 啟動任務,初始化PyQT5搭建的服務平臺
class?Command(BaseCommand):????def?handle(self,?*args,?**options):
????????app?=?QApplication(sys.argv)
????????win?=?MainWindow()
????????app.exit(app.exec_())
在PyQt5平臺中初始化
?參數(init_arguments)、搭載gearmanworker的(init_worker),WebDriver(init_driver),任務加載定時器(init_timer)
class?MainWindow(QMainWindow):????def?__init__(self,?parent=None):
????????super(MainWindow,?self).__init__(parent)
????????self.root_path?=?Path(__file__).parent
????????self.setWindowTitle('易哈佛')
????????self.init_arguments()
????????self.init_driver()
????????self.init_worker()
????????self.init_timer()
注冊gearmanworker
QThread中注冊gearmanworker到gearman服務器,接收到的任務都存入任務池中
class?WebWorker(QThread):????def?__init__(self):
????????super().__init__()
????????self.worker?=?None
????????self.task_pool?=?[]
????def?run(self):
????????self.worker?=?self.init_worker()
????????self.worker.work()
????def?init_worker(self):
????????convert_worker?=?JsonWorker(['c.ehafolive.com:4730',?])
????????convert_worker.set_client_id('capture')
????????convert_worker.register_task('screencapture',?self.task_listener)
????????return?convert_worker
????@property
????def?worker_(self)?->?JsonWorker:
????????return?self.worker
????def?task_listener(self,?gearman_worker,?request):
????????self.task_pool.append(request.data)
任務加載定時器
任務加載定時器會每五秒調起一次任務加載功能。任務加載功能必須在任務池存在任務,并且當前平臺沒有任務時才會執行任務加載,為了避免并發導致任務沖突,所以任務加載功能單線程執行。
def?load_url(self):????#?接受視頻錄制任務
????self.lock.acquire()
????if?self.WebWorker.task_pool?and?self.usable:
????????self.usable?=?False
????????info?=?self.WebWorker.task_pool.pop(0)
????????self.video_layout?=?info.get('layout')
????????self.h_id?=?int(info.get('pk'))
????????print(self.video_layout,?self.h_id)
????????self.driver_load()
????self.lock.release()
瀏覽器加載功能
一但允許任務加載從任務池中提取一個任務,提取出數據,調用瀏覽器加載功能,瀏覽器加載功能是通過加載定制的頁面,并通過設置瀏覽器的位置和寬高來大致確定后續需要錄制的區域,在頁面加載完畢后,需要初始化錄制動作監控定時器,并初始化一個視頻對象,后續的進度實時更新依賴于初始化的視頻對象
def?driver_load(self):????#?設置瀏覽器位置及窗口大小,加載url
????url?=?"https://xxxx?pk=%s"?%?self.h_id
????if?self.video_layout?==?'vertical':
????????width,?height?=?1000,?1446
????else:
????????width,?height?=?1373,?800
????self.set_driver(width,?height)
????self.driver.get(url)
????self.init_cmd_timer()
????self.init_video_instance()
瀏覽器設置
通過對任務的橫版和豎版的判定,將瀏覽器位置調整到合適的區域,并設置相應的寬高
def?set_driver(self,?width,?height):????if?self.video_layout?==?'vertical':
????????left,?top?=?self.chose_screen(768,?1366)
????else:
????????left,?top?=?self.chose_screen(1366,?768)
????self.screen_width?=?width?-?self.window_offset_x
????self.screen_height?=?height?-?self.window_offset_y
????self.driver.set_window_position(left?-?self.window_offset_x,?top)
????self.driver.set_window_size(width,?height)
視頻對象的初始化
因為任務可能重復下發,因此視頻對象可能早有存在,所以對于已存在的對象只需局部更新數據,不需要另外創建
def?init_video_instance(self):????from?article.models?import?Article
????from?points.models?import?Video
????queryset?=?Article.objects.filter(id=self.h_id)
????if?not?queryset.exists():
????????return
????article?=?queryset.first()
????data?=?{
????????'size':?0,
????????'process':?0,
????????'course':?self.h_id,
????????'user':?article.user,
????????'layout':?article.layout,
????????'duration':?article.duration,
????}
????video,?_?=?Video.objects.update_or_create(
????????defaults=data,
????????course=self.h_id,
????)
????self.video_id?=?video.id
錄制動作監控定時器超時事件響應
在上述任務加載完畢,并且初始化完成后,錄制動作監控定時器將迎來第一次的超時事件,超時事件將檢測相應的標簽值,在確定標簽值已經改變并由后端完成修改后,將調起相應的功能
#錄制動作監控定時器超時調起事件def?monitor_cmd(self):
????if?self.set_monitor_tag_value('click'):
????????self.mouse_click_event()
????if?self.set_monitor_tag_value('start'):
????????self.start_capture()
????if?self.set_monitor_tag_value('stop'):
????????self.stop_capture()
#監測動作對應的標簽文本值,發生變化則進行修改
def?set_monitor_tag_value(self,?tag):
????lock?=?Lock()
????lock.acquire()
????_,?value?=?self.get_monitor_tag_value(tag)
????if?value?==?'false':
????????lock.release()
????????return?False
????attributeName?=?'textContent'
????js?=?"{}.{}={}".format(tag,?attributeName,?'false')
????self.driver.execute_script(js)
????print('set?element?value?%s'?%?tag)
????lock.release()
????return?True
click標簽文本值改變關聯事件
由后端在前端確定url加載完畢后調起,實現模擬點擊事件,通知前端開始播放動畫和音頻
def?mouse_click_event(self):????#?點擊命令執行,開始播放
????self.driver.find_element_by_id('click-element').click()
start標簽文本值改變關聯事件
start標簽值的改變在前端確定動畫和音頻已經開始播放后修改,當后端檢測到文本值發生改變后,檢測錄制區域是否超出屏幕、初始化錄制視頻進程、初始化錄制進度更新定時器,開始屏幕錄制
def?start_capture(self):????#?開始錄制視頻
????if?not?self.verify_capture_size():
????????return?self.unlocked()
????capture_cmd?=?self.init_capture_cmd()
????self.process?=?RecordProcess()
????self.process.start(capture_cmd)
????self.init_process_timer()
????print(capture_cmd)
stop標簽文本值改變關聯事件
start標簽值的改變在前端確定動畫和音頻已經播放完畢后修改,當后端檢測到文本值發生改變后,調起停止視頻錄制功能,關閉錄制動作監控定時器,關閉進度更新定時器,待錄制視頻進程完全結束,視頻文件生成后調起上傳視頻
def?stop_capture(self):????#?停止錄制視頻
????if?not?hasattr(self,?'process'):?return
????self.process.quit()
????self.process_timer.stop()
????self.monitor_timer.stop()
????self.process.finished.connect(self.upload_video)
視頻上傳功能
上傳視頻到cos,上傳完成后,解除任務正在執行的標志,等待下一次任務加載,同時調起頂頂發送消息通知任務已經完成
def?upload_video(self):????#?上傳視頻到數據庫
????url?=?save_video(self.video_path,?self.video_id)
????self.usable?=?True
????task_success(TASK,?"path:%s"?%?url)
上傳視頻
如果已經錄制過視頻,原視頻從cos刪除
def?save_video(video_path,?pk):????if?not?os.path.exists(video_path):
????????print('視頻轉錄異常',?video_path)
????????return
????f?=?open(video_path,?'rb')
????url?=?upload_video(f.name,?f)
????f.seek(0)
????size?=?len(f.read())
????instance?=?Video.objects.get(id=pk)
????if?instance.url:
????????delete_from_cos(instance.url.url)
????instance.url?=?url
????instance.size?=?size
????instance.process?=?100
????instance.save()
????print(url,?pk)
????print('*'?*?100)
????f.close()
????return?url
??
轉載于:https://www.cnblogs.com/li1992/p/11057197.html
總結
- 上一篇: 消息称《刺客信条:RED》日本背景游戏有
- 下一篇: R语言对用电负荷时间序列数据进行K-me