{
 "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单击<a href='https://aka.ms/vscodeJupyterKernelCrash'>此处</a>了解详细信息。\n",
      "\u001b[1;31m有关更多详细信息，请查看 Jupyter <a href='command:jupyter.viewOutput'>log</a>。"
     ]
    }
   ],
   "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
}
