{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "f5f7dfec",
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Exception in thread Thread-8 (player_thread):\n",
"Traceback (most recent call last):\n",
" File \"d:\\Anaconda3\\Lib\\threading.py\", line 1073, in _bootstrap_inner\n",
" self.run()\n",
" File \"d:\\Anaconda3\\Lib\\threading.py\", line 1010, in run\n",
" self._target(*self._args, **self._kwargs)\n",
" File \"C:\\Users\\Administrator\\AppData\\Local\\Temp\\ipykernel_16072\\2038425495.py\", line 69, in player_thread\n",
" File \"C:\\Users\\Administrator\\AppData\\Local\\Temp\\ipykernel_16072\\2038425495.py\", line 42, in decode_frames\n",
"cv2.error: OpenCV(4.11.0) :-1: error: (-5:Bad argument) in function 'open'\n",
"> Overload resolution failed:\n",
"> - Expected 'filename' to be a str or path-like object\n",
"> - VideoCapture.open() missing required argument 'apiPreference' (pos 2)\n",
"> - Argument 'index' is required to be an integer\n",
"> - VideoCapture.open() missing required argument 'apiPreference' (pos 2)\n",
"> - VideoCapture.open() missing required argument 'apiPreference' (pos 2)\n",
"\n"
]
},
{
"ename": "KeyboardInterrupt",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[1;31mKeyboardInterrupt\u001b[0m Traceback (most recent call last)",
"Cell \u001b[1;32mIn[3], line 97\u001b[0m\n\u001b[0;32m 95\u001b[0m m3u8_url \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mhttp://113.45.217.230:18800/rtp/34020000001320000155_34020000001320000155/hls.m3u8\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 96\u001b[0m player \u001b[38;5;241m=\u001b[39m HLSPlayer(m3u8_url)\n\u001b[1;32m---> 97\u001b[0m player\u001b[38;5;241m.\u001b[39mstart()\n",
"Cell \u001b[1;32mIn[3], line 88\u001b[0m, in \u001b[0;36mHLSPlayer.start\u001b[1;34m(self)\u001b[0m\n\u001b[0;32m 85\u001b[0m cv2\u001b[38;5;241m.\u001b[39mimshow(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mHLS Player\u001b[39m\u001b[38;5;124m'\u001b[39m, frame)\n\u001b[0;32m 87\u001b[0m \u001b[38;5;66;03m# 按q退出\u001b[39;00m\n\u001b[1;32m---> 88\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m cv2\u001b[38;5;241m.\u001b[39mwaitKey(\u001b[38;5;241m1\u001b[39m) \u001b[38;5;241m&\u001b[39m \u001b[38;5;241m0xFF\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;28mord\u001b[39m(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mq\u001b[39m\u001b[38;5;124m'\u001b[39m):\n\u001b[0;32m 89\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mrunning \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m\n\u001b[0;32m 90\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n",
"\u001b[1;31mKeyboardInterrupt\u001b[0m: "
]
},
{
"ename": "",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[1;31m在当前单元格或上一个单元格中执行代码时 Kernel 崩溃。\n",
"\u001b[1;31m请查看单元格中的代码,以确定故障的可能原因。\n",
"\u001b[1;31m单击此处了解详细信息。\n",
"\u001b[1;31m有关更多详细信息,请查看 Jupyter log。"
]
}
],
"source": [
"import cv2\n",
"import m3u8\n",
"import requests\n",
"import numpy as np\n",
"from urllib.parse import urljoin\n",
"from threading import Thread\n",
"from queue import Queue\n",
"\n",
"class HLSPlayer:\n",
" def __init__(self, m3u8_url):\n",
" self.m3u8_url = m3u8_url\n",
" self.playlist = None\n",
" self.current_segment = 0\n",
" self.frame_queue = Queue(maxsize=30)\n",
" self.running = False\n",
" self.load_playlist()\n",
" \n",
" def load_playlist(self):\n",
" response = requests.get(self.m3u8_url,verify=False)\n",
" if response.status_code != 200:\n",
" raise Exception(f\"无法下载m3u8文件: {response.status_code}\")\n",
" \n",
" self.playlist = m3u8.loads(response.text)\n",
" \n",
" # 如果是多码率播放列表,选择最高码率\n",
" if self.playlist.is_variant:\n",
" best_stream = max(self.playlist.playlists, key=lambda p: p.stream_info.bandwidth)\n",
" self.m3u8_url = urljoin(self.m3u8_url, best_stream.uri)\n",
" self.load_playlist()\n",
" \n",
" def fetch_segment(self, segment_url):\n",
" response = requests.get(segment_url,verify=False)\n",
" if response.status_code == 200:\n",
" # 将TS数据转换为OpenCV可读格式\n",
" video_data = np.frombuffer(response.content, dtype=np.uint8)\n",
" return video_data\n",
" return None\n",
" \n",
" def decode_frames(self, video_data):\n",
" # 使用OpenCV解码视频帧\n",
" cap = cv2.VideoCapture()\n",
" cap.open(video_data)\n",
" \n",
" frames = []\n",
" while cap.isOpened():\n",
" ret, frame = cap.read()\n",
" if not ret:\n",
" break\n",
" frames.append(frame)\n",
" \n",
" cap.release()\n",
" return frames\n",
" \n",
" def player_thread(self):\n",
" self.running = True\n",
" while self.running:\n",
" # 获取当前片段URL\n",
" if self.current_segment >= len(self.playlist.segments):\n",
" # 如果是直播流,重新加载播放列表\n",
" self.load_playlist()\n",
" self.current_segment = 0\n",
" \n",
" segment = self.playlist.segments[self.current_segment]\n",
" segment_url = urljoin(self.m3u8_url, segment.uri)\n",
" \n",
" # 下载并解码片段\n",
" video_data = self.fetch_segment(segment_url)\n",
" if video_data is not None:\n",
" frames = self.decode_frames(video_data)\n",
" \n",
" # 将帧放入队列\n",
" for frame in frames:\n",
" self.frame_queue.put(frame)\n",
" \n",
" self.current_segment += 1\n",
" \n",
" def start(self):\n",
" # 启动播放器线程\n",
" Thread(target=self.player_thread, daemon=True).start()\n",
" \n",
" # 显示视频\n",
" while True:\n",
" if not self.frame_queue.empty():\n",
" frame = self.frame_queue.get()\n",
" cv2.imshow('HLS Player', frame)\n",
" \n",
" # 按q退出\n",
" if cv2.waitKey(1) & 0xFF == ord('q'):\n",
" self.running = False\n",
" break\n",
" \n",
" cv2.destroyAllWindows()\n",
"\n",
"if __name__ == \"__main__\":\n",
" m3u8_url = \"http://113.45.217.230:18800/rtp/34020000001320000155_34020000001320000155/hls.m3u8\"\n",
" player = HLSPlayer(m3u8_url)\n",
" player.start()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "base",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.4"
}
},
"nbformat": 4,
"nbformat_minor": 5
}