From b7f97af263fdf9c5d70f2cc29efbb95e27510838 Mon Sep 17 00:00:00 2001 From: Nischay Joshi Date: Mon, 3 Nov 2025 16:02:04 +0530 Subject: [PATCH] Update Readme, Add multi cam notebooks, Update API Key input in notebooks, update rtstream notebooks. --- README.md | 87 +- examples/Add Music to Sora Videos.ipynb | 14 +- examples/Adding_Brand_Elements.ipynb | 8 +- examples/Audio_Overlay.ipynb | 6 +- examples/Beep Curse Words.ipynb | 14 +- examples/Clip with Faces Rekognition.ipynb | 25 +- .../Content Moderation AWS Rekognition.ipynb | 18 +- examples/Content Moderation.ipynb | 11 +- examples/Create Clip Faces.ipynb | 11 +- ... - Replace Soundtrack with New Audio.ipynb | 12 +- examples/Elevenlabs_Voiceover_1.ipynb | 8 +- examples/Elevenlabs_Voiceover_2.ipynb | 10 +- examples/GenAI_Storyboard.ipynb | 10 +- examples/Insert Inline Video.ipynb | 11 +- examples/Intro_Outro_Inline.ipynb | 8 +- examples/Keyword_Search_1.ipynb | 8 +- examples/Keyword_Search_Counter.ipynb | 8 +- examples/Lovo_Voiceover_1.ipynb | 11 +- examples/Programmatic_Streams_1.ipynb | 795 +++---- examples/Subtitle_Styling.ipynb | 8 +- examples/Wellsaid_Voiceover_1.ipynb | 16 +- examples/conference_slide_scraper.ipynb | 8 +- examples/lecture_notes_1.ipynb | 1304 ++++++------ guides/Cleanup.ipynb | 11 +- guides/Subtitle.ipynb | 9 +- guides/TextAsset.ipynb | 8 +- guides/VideoDB_Search_and_Evaluation.ipynb | 9 +- guides/genai.ipynb | 11 +- .../Prompt_Experiments_and_Benchmarking.ipynb | 8 +- .../scene-index/advanced_visual_search.ipynb | 8 +- guides/scene-index/custom_annotations.ipynb | 8 +- .../playground_scene_extraction.ipynb | 8 +- .../llama-index/simple_video_rag.ipynb | 11 +- quickstart/Multimodal_Quickstart.ipynb | 13 +- quickstart/Scene Index QuickStart.ipynb | 9 +- quickstart/VideoDB Quickstart.ipynb | 16 +- .../scene_level_metadata_indexing.ipynb | 1860 +++++++++-------- .../Baby_Crib_Monitoring.ipynb | 694 +++--- .../Cricket_Match_Monitoring.ipynb | 954 ++++----- .../Flash_Flood_Detection.ipynb | 1052 +++++----- real_time_streaming/Intrusion_Detection.ipynb | 944 +++++---- real_time_streaming/Road_Monitoring.ipynb | 61 +- .../Multicam_Basketball_Analysis.ipynb | 1768 ++++++++++++++++ .../Multicam_Public_Surveillance.ipynb | 1825 ++++++++++++++++ 44 files changed, 7831 insertions(+), 3867 deletions(-) create mode 100644 real_time_streaming/multicam/Multicam_Basketball_Analysis.ipynb create mode 100644 real_time_streaming/multicam/Multicam_Public_Surveillance.ipynb diff --git a/README.md b/README.md index 13b5853..22c791c 100644 --- a/README.md +++ b/README.md @@ -30,10 +30,89 @@ This repo has example code and quick tutorials for solving common tasks with the Most of the code examples are written in Python, though the concepts can be applied in any language. ## Contents -| Cookbook| Colab | -|:-----:|:-----:| -| [Quickstart: VideoDB](https://colab.research.google.com/github/video-db/videodb-cookbook/blob/main/quickstart/VideoDB%20Quickstart.ipynb) | Open In Colab | -| [Quickstart: Scene Index](https://colab.research.google.com/github/video-db/videodb-cookbook/blob/main/quickstart/Scene%20Index%20QuickStart.ipynb) | Open In Colab | + +### Quickstart +| Cookbook | Colab | +|:---------|:-----:| +| [VideoDB Quickstart](./quickstart/VideoDB%20Quickstart.ipynb) | Open In Colab | +| [Scene Index QuickStart](./quickstart/Scene%20Index%20QuickStart.ipynb) | Open In Colab | +| [Multimodal Quickstart](./quickstart/Multimodal_Quickstart.ipynb) | Open In Colab | +| [Scene Level Metadata Indexing](./quickstart/scene_level_metadata_indexing.ipynb) | Open In Colab | + +### Real Time Streaming +| Cookbook | Colab | +|:---------|:-----:| +| [Baby Crib Monitoring](./real_time_streaming/Baby_Crib_Monitoring.ipynb) | Open In Colab | +| [Cricket Match Monitoring](./real_time_streaming/Cricket_Match_Monitoring.ipynb) | Open In Colab | +| [Flash Flood Detection](./real_time_streaming/Flash_Flood_Detection.ipynb) | Open In Colab | +| [Intrusion Detection](./real_time_streaming/Intrusion_Detection.ipynb) | Open In Colab | +| [Road Monitoring](./real_time_streaming/Road_Monitoring.ipynb) | Open In Colab | + +#### Multicam +| Cookbook | Colab | +|:---------|:-----:| +| [Multicam Basketball Analysis](./real_time_streaming/multicam/Multicam_Basketball_Analysis.ipynb) | Open In Colab | +| [Multicam Public Surveillance](./real_time_streaming/multicam/Multicam_Public_Surveillance.ipynb) | Open In Colab | + +### Meeting Agent +| Cookbook | Colab | +|:---------|:-----:| +| [Interview Evaluation To Slack](./meeting_agent/Interview_Evaluation_To_Slack.ipynb) | Open In Colab | +| [Sales Meeting To CRM](./meeting_agent/Sales_Meeting_To_CRM.ipynb) | Open In Colab | +| [Webinar To Linkedin](./meeting_agent/Webinar_To_Linkedin.ipynb) | Open In Colab | + +### Integrations +#### LLama Index +| Cookbook | Colab | +|:---------|:-----:| +| [Simple Video RAG](./integrations/llama-index/simple_video_rag.ipynb) | Open In Colab | + +### Guides +| Cookbook | Colab | +|:---------|:-----:| +| [Cleanup](./guides/Cleanup.ipynb) | Open In Colab | +| [GenAI](./guides/genai.ipynb) | Open In Colab | +| [Subtitle](./guides/Subtitle.ipynb) | Open In Colab | +| [TextAsset](./guides/TextAsset.ipynb) | Open In Colab | +| [VideoDB Search and Evaluation](./guides/VideoDB_Search_and_Evaluation.ipynb) | Open In Colab | + +#### Multimodal +| Cookbook | Colab | +|:---------|:-----:| +| [Prompt Experiments and Benchmarking](./guides/multimodal/Prompt_Experiments_and_Benchmarking.ipynb) | Open In Colab | + +#### Scene Index +| Cookbook | Colab | +|:---------|:-----:| +| [Advanced Visual Search](./guides/scene-index/advanced_visual_search.ipynb) | Open In Colab | +| [Custom Annotations](./guides/scene-index/custom_annotations.ipynb) | Open In Colab | +| [Playground Scene Extraction](./guides/scene-index/playground_scene_extraction.ipynb) | Open In Colab | + +### Examples +| Cookbook | Colab | +|:---------|:-----:| +| [Add Music to Sora Videos](./examples/Add%20Music%20to%20Sora%20Videos.ipynb) | Open In Colab | +| [Adding Brand Elements](./examples/Adding_Brand_Elements.ipynb) | Open In Colab | +| [Audio Overlay](./examples/Audio_Overlay.ipynb) | Open In Colab | +| [Beep Curse Words](./examples/Beep%20Curse%20Words.ipynb) | Open In Colab | +| [Clip with Faces Rekognition](./examples/Clip%20with%20Faces%20Rekognition.ipynb) | Open In Colab | +| [Conference Slide Scraper](./examples/conference_slide_scraper.ipynb) | Open In Colab | +| [Content Moderation AWS Rekognition](./examples/Content%20Moderation%20AWS%20Rekognition.ipynb) | Open In Colab | +| [Content Moderation](./examples/Content%20Moderation.ipynb) | Open In Colab | +| [Create Clip Faces](./examples/Create%20Clip%20Faces.ipynb) | Open In Colab | +| [Dubbing - Replace Soundtrack with New Audio](./examples/Dubbing%20-%20Replace%20Soundtrack%20with%20New%20Audio.ipynb) | Open In Colab | +| [Elevenlabs Voiceover 1](./examples/Elevenlabs_Voiceover_1.ipynb) | Open In Colab | +| [Elevenlabs Voiceover 2](./examples/Elevenlabs_Voiceover_2.ipynb) | Open In Colab | +| [GenAI Storyboard](./examples/GenAI_Storyboard.ipynb) | Open In Colab | +| [Insert Inline Video](./examples/Insert%20Inline%20Video.ipynb) | Open In Colab | +| [Intro Outro Inline](./examples/Intro_Outro_Inline.ipynb) | Open In Colab | +| [Keyword Search 1](./examples/Keyword_Search_1.ipynb) | Open In Colab | +| [Keyword Search Counter](./examples/Keyword_Search_Counter.ipynb) | Open In Colab | +| [Lecture Notes 1](./examples/lecture_notes_1.ipynb) | Open In Colab | +| [Lovo Voiceover 1](./examples/Lovo_Voiceover_1.ipynb) | Open In Colab | +| [Programmatic Streams 1](./examples/Programmatic_Streams_1.ipynb) | Open In Colab | +| [Subtitle Styling](./examples/Subtitle_Styling.ipynb) | Open In Colab | +| [Wellsaid Voiceover 1](./examples/Wellsaid_Voiceover_1.ipynb) | Open In Colab | ## Contributing diff --git a/examples/Add Music to Sora Videos.ipynb b/examples/Add Music to Sora Videos.ipynb index 07f593e..1863df4 100644 --- a/examples/Add Music to Sora Videos.ipynb +++ b/examples/Add Music to Sora Videos.ipynb @@ -28,15 +28,23 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": null, "id": "543dd0e9-abb0-423e-add7-058f2415f97d", "metadata": {}, "outputs": [], "source": [ - "# create a new connection with your API ket\n", + "# create a new connection with your API key\n", + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", "from videodb import connect, play_stream\n", "from videodb import MediaType\n", - "conn = connect(api_key=\"\")" + "conn = connect()" ] }, { diff --git a/examples/Adding_Brand_Elements.ipynb b/examples/Adding_Brand_Elements.ipynb index ec4bcb6..420b106 100644 --- a/examples/Adding_Brand_Elements.ipynb +++ b/examples/Adding_Brand_Elements.ipynb @@ -59,13 +59,17 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/examples/Audio_Overlay.ipynb b/examples/Audio_Overlay.ipynb index 06e29fc..e0ff27e 100644 --- a/examples/Audio_Overlay.ipynb +++ b/examples/Audio_Overlay.ipynb @@ -65,9 +65,13 @@ "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/examples/Beep Curse Words.ipynb b/examples/Beep Curse Words.ipynb index 6cae259..3ca672d 100644 --- a/examples/Beep Curse Words.ipynb +++ b/examples/Beep Curse Words.ipynb @@ -55,14 +55,22 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "3c5d3ea5-e80c-4381-b768-6a90cf96f302", "metadata": {}, "outputs": [], "source": [ - "# create a new connection with your API ket\n", + "# create a new connection with your API key\n", + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", "from videodb import connect, play_stream\n", - "conn = connect(api_key=\"\")" + "conn = connect()" ] }, { diff --git a/examples/Clip with Faces Rekognition.ipynb b/examples/Clip with Faces Rekognition.ipynb index 6d0bc3f..bfc6d79 100644 --- a/examples/Clip with Faces Rekognition.ipynb +++ b/examples/Clip with Faces Rekognition.ipynb @@ -226,8 +226,17 @@ "metadata": {}, "outputs": [], "source": [ - "from videodb import connect, play_stream\n", - "conn = connect(api_key=\"\")" + "# create a new connection with your API key\n", + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "from videodb import connect\n", + "conn = connect()\n" ] }, { @@ -329,15 +338,16 @@ }, "outputs": [], "source": [ - "# Start a Face Search Job \n", + "# Start a Face Search Job\n", "def start_face_search(video_path, collection_id, bucket_name):\n", " response = rekognition_client.start_face_search(\n", " Video={\"S3Object\": {\"Bucket\": bucket_name, \"Name\": video_path}},\n", - " CollectionId=collection_id\n", + " CollectionId=collection_id,\n", " )\n", "\n", " return response[\"JobId\"]\n", "\n", + "\n", "# Get Result of Face Search Job\n", "def get_face_search_results(job_id):\n", " wait_for = 5\n", @@ -355,14 +365,15 @@ " print(f\"Face search failed with status: {status}\")\n", " return None\n", "\n", + "\n", "# Upload our video to S3 Bucket\n", "s3.create_bucket(Bucket=bucket_name)\n", "s3.upload_file(video_output, bucket_name, video_output)\n", "\n", - "# Search faces in uploaded video using Rekogntion API \n", - "job_id = start_face_search(video_output, collection_id, bucket_name )\n", + "# Search faces in uploaded video using Rekogntion API\n", + "job_id = start_face_search(video_output, collection_id, bucket_name)\n", "print(job_id)\n", - "face_res = get_face_search_results(job_id)" + "face_res = get_face_search_results(job_id)\n" ] }, { diff --git a/examples/Content Moderation AWS Rekognition.ipynb b/examples/Content Moderation AWS Rekognition.ipynb index 483a34b..a73f9f7 100644 --- a/examples/Content Moderation AWS Rekognition.ipynb +++ b/examples/Content Moderation AWS Rekognition.ipynb @@ -102,12 +102,22 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "from videodb import connect, play_stream\n", - "conn = connect(api_key=\"\")" + "# create a new connection with your API key\n", + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "from videodb import connect\n", + "\n", + "conn = connect()" ] }, { @@ -166,7 +176,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [ { diff --git a/examples/Content Moderation.ipynb b/examples/Content Moderation.ipynb index 56ba40f..cd5bae5 100644 --- a/examples/Content Moderation.ipynb +++ b/examples/Content Moderation.ipynb @@ -81,12 +81,19 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import videodb\n", - "conn = videodb.connect(api_key=\"\")" + "import os\n", + "from getpass import getpass\n", + "\n", + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "conn = videodb.connect()" ] }, { diff --git a/examples/Create Clip Faces.ipynb b/examples/Create Clip Faces.ipynb index 6b985cf..7c0bd0b 100644 --- a/examples/Create Clip Faces.ipynb +++ b/examples/Create Clip Faces.ipynb @@ -75,12 +75,19 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import videodb\n", - "conn = videodb.connect(api_key=\"\")" + "import os\n", + "from getpass import getpass\n", + "\n", + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "conn = videodb.connect()" ] }, { diff --git a/examples/Dubbing - Replace Soundtrack with New Audio.ipynb b/examples/Dubbing - Replace Soundtrack with New Audio.ipynb index fdeda17..5c57253 100644 --- a/examples/Dubbing - Replace Soundtrack with New Audio.ipynb +++ b/examples/Dubbing - Replace Soundtrack with New Audio.ipynb @@ -60,15 +60,23 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": null, "id": "3aa868ec-58dc-4334-89c0-c4b21e8ef12b", "metadata": {}, "outputs": [], "source": [ "#connect to videodb\n", + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", "from videodb import connect\n", "from videodb import MediaType\n", - "conn = connect(api_key=\" \")" + "conn = connect()" ] }, { diff --git a/examples/Elevenlabs_Voiceover_1.ipynb b/examples/Elevenlabs_Voiceover_1.ipynb index 0fa8ee6..62d5eea 100644 --- a/examples/Elevenlabs_Voiceover_1.ipynb +++ b/examples/Elevenlabs_Voiceover_1.ipynb @@ -89,11 +89,15 @@ "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", "\n", + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", "os.environ[\"OPENAI_API_KEY\"] = \"\"\n", - "os.environ[\"ELEVEN_LABS_API_KEY\"] = \"\"\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "os.environ[\"ELEVEN_LABS_API_KEY\"] = \"\"" ] }, { diff --git a/examples/Elevenlabs_Voiceover_2.ipynb b/examples/Elevenlabs_Voiceover_2.ipynb index db814e6..178c340 100644 --- a/examples/Elevenlabs_Voiceover_2.ipynb +++ b/examples/Elevenlabs_Voiceover_2.ipynb @@ -66,15 +66,19 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", "\n", + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", "os.environ[\"OPENAI_API_KEY\"] = \"\"\n", - "os.environ[\"ELEVEN_LABS_API_KEY\"] = \"\"\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "os.environ[\"ELEVEN_LABS_API_KEY\"] = \"\"" ] }, { diff --git a/examples/GenAI_Storyboard.ipynb b/examples/GenAI_Storyboard.ipynb index bc2350f..49783a2 100644 --- a/examples/GenAI_Storyboard.ipynb +++ b/examples/GenAI_Storyboard.ipynb @@ -72,15 +72,19 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", "\n", + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", "os.environ[\"OPENAI_API_KEY\"] = \"\"\n", - "os.environ[\"ELEVEN_LABS_API_KEY\"] = \"\"\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "os.environ[\"ELEVEN_LABS_API_KEY\"] = \"\"" ] }, { diff --git a/examples/Insert Inline Video.ipynb b/examples/Insert Inline Video.ipynb index 8afad7a..2a0697d 100644 --- a/examples/Insert Inline Video.ipynb +++ b/examples/Insert Inline Video.ipynb @@ -66,12 +66,19 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import videodb\n", - "conn = videodb.connect(api_key=\"\")" + "import os\n", + "from getpass import getpass\n", + "\n", + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "conn = videodb.connect()" ] }, { diff --git a/examples/Intro_Outro_Inline.ipynb b/examples/Intro_Outro_Inline.ipynb index 7572eec..f6c7fdb 100644 --- a/examples/Intro_Outro_Inline.ipynb +++ b/examples/Intro_Outro_Inline.ipynb @@ -68,13 +68,17 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/examples/Keyword_Search_1.ipynb b/examples/Keyword_Search_1.ipynb index ceba356..e717eac 100644 --- a/examples/Keyword_Search_1.ipynb +++ b/examples/Keyword_Search_1.ipynb @@ -89,13 +89,17 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/examples/Keyword_Search_Counter.ipynb b/examples/Keyword_Search_Counter.ipynb index aea0ddd..8ade72c 100644 --- a/examples/Keyword_Search_Counter.ipynb +++ b/examples/Keyword_Search_Counter.ipynb @@ -61,13 +61,17 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/examples/Lovo_Voiceover_1.ipynb b/examples/Lovo_Voiceover_1.ipynb index 17d0345..7ce3512 100644 --- a/examples/Lovo_Voiceover_1.ipynb +++ b/examples/Lovo_Voiceover_1.ipynb @@ -95,15 +95,18 @@ }, { "cell_type": "code", - "execution_count": 74, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", "\n", - "os.environ[\"OPENAI_API_KEY\"] = \"\"\n", - "os.environ[\"LOVO_API_KEY\"] = \"\"\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "# Prompt user for API key securely\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"OPENAI_API_KEY\"] = getpass(\"Please enter your OpenAI API Key: \")\n", + "os.environ[\"LOVO_API_KEY\"] = getpass(\"Please enter your LOVO API Key: \")\n" ] }, { diff --git a/examples/Programmatic_Streams_1.ipynb b/examples/Programmatic_Streams_1.ipynb index b545a43..04c326f 100644 --- a/examples/Programmatic_Streams_1.ipynb +++ b/examples/Programmatic_Streams_1.ipynb @@ -1,405 +1,410 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "xEBvVSuzjxHh" - }, - "source": [ - "# ↔️ Building Dynamic Video Streams with VideoDB: Integrating Custom Data and APIs" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "LCwBGclTjxHi" - }, - "source": [ - "\"Open" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "w8xeWTZkjxHi" - }, - "source": [ - "## Introduction\n", - "Imagine you're watching a captivating keynote session from your favorite conference, and you’re welcomed with a personalized stream just for you.\n", - "\n", - "This tutorial demonstrates how to create dynamic video streams by integrating data from custom databases and external APIs. We'll use a practical example: a recording of a [Config 2023](https://www.youtube.com/watch?v=Nmv8XdFiej0) keynote session. By using VideoDB, we'll show how companies like [Figma](https://www.figma.com/files/recents-and-sharing?fuid=940498258276625180) can personalize the viewing experience for their audience, delivering a richer and more engaging experience.\n", - "\n", - "We'll showcase how to:\n", - "\n", - "* Fetch data from a random user API to represent a hypothetical viewer.\n", - "* Integrate this data into a custom VideoDB timeline.\n", - "* Create a personalized stream that dynamically displays relevant information alongside the keynote video.\n", - "\n", - "This tutorial is your guide to unlocking the potential of dynamic video streams and transforming your video content with personalized experiences." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "6Izs6lxqjxHi" - }, - "source": [ - "## Setup\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "JiLgb86qjxHi" - }, - "source": [ - "\n", - "### πŸ“¦ Installing packages" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "VaSLRgz-jxHj" - }, - "outputs": [], - "source": [ - "%pip install videodb" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8vQk4j1MjxHj" - }, - "source": [ - "### πŸ”‘ API Keys\n", - "Before proceeding, ensure access to [VideoDB](https://videodb.io) API key.\n", - "\n", - "> Get your API key from [VideoDB Console](https://console.videodb.io). ( Free for first 50 uploads, **No credit card required** ) πŸŽ‰" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "id": "DuhMNCgfjxHj" - }, - "outputs": [], - "source": [ - "import os\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "s8RAYTtnjxHj" - }, - "source": [ - "## Steps\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TIwDr4SIjxHj" - }, - "source": [ - "### πŸ”— Step 1: Connect to VideoDB" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "cegRs1devmg9" - }, - "source": [ - "Establish a session for uploading videos. Import the necessary modules from VideoDB library to access functionalities." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": { - "id": "GvEF198HjxHj" - }, - "outputs": [], - "source": [ - "import videodb\n", - "from videodb import connect\n", - "\n", - "conn = videodb.connect()\n", - "coll = conn.get_collection()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "bMGnv3j0jxHj" - }, - "source": [ - "### πŸ—³οΈ Step 2: Upload Base Video\n", - "Upload and play the video to ensure it's correctly loaded. We’ll be using [this video](https://www.youtube.com/watch?v=Nmv8XdFiej0) for the purpose of this tutorial." - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "id": "I4FukL25jxHj" - }, - "outputs": [ - { - "data": { - "text/plain": [ - "'https://console.videodb.io/player?url=https://dseetlpshk2tb.cloudfront.net/v3/published/manifests/5035d388-3b20-48e8-8391-ad157d3784b5.m3u8'" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Upload and play a video from a URL\n", - "video = coll.upload(url=\"https://www.youtube.com/watch?v=Nmv8XdFiej0\")\n", - "video.play()\n", - "\n", - "# Alternatively, get a video from your VideoDB collection\n", - "# video = coll.get_video('VIDEO_ID_HERE')\n", - "# video.play()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "dS6KesexjxHk" - }, - "source": [ - "### πŸ“₯ Step 3: Fetch Data from a Random User API\n", - "This code fetches a random user's data (name and picture) from the \"randomuser.me\" API. You can adapt this to retrieve data from any relevant API (e.g., product data, news articles) for your use case." - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "id": "4yLqe4g0jxHk" - }, - "outputs": [], - "source": [ - "import requests\n", - "\n", - "# Make a request to the Randomizer API\n", - "response = requests.get('https://randomuser.me/api/?results=1&nat=us,ca,gb,au')\n", - "data = response.json()\n", - "\n", - "# Extract relevant information\n", - "first_name = data['results'][0]['name']['first']\n", - "medium_picture = data['results'][0]['picture']['medium']" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "5ML2q-OWjxHk" - }, - "source": [ - "### πŸš₯ Step 4 (optional): Prepare Data for Integration\n", - "\n", - "No additional data transformation is required in this example since we are using the data directly from the API. However, in more complex scenarios, you may need to format the data to be suitable for VideoDB.\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "5c0fYqjXjxHk" - }, - "source": [ - "### 🧱 Step 5: Create VideoDB Assets\n", - "\n", - "We create VideoDB assets for the base video, the user's name (text), and their picture (image). The `TextStyle` object allows us to customize the appearance of the text elements." - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "id": "fOBsa9pdjxHk" - }, - "outputs": [], - "source": [ - "from videodb import play_stream, TextStyle, MediaType\n", - "from videodb.asset import VideoAsset, TextAsset, ImageAsset\n", - "from videodb.timeline import Timeline\n", - "\n", - "# Video Asset\n", - "video_asset = VideoAsset(asset_id=video.id, start=0)\n", - "\n", - "# Text Asset with First Name\n", - "name_asset = TextAsset(\n", - " text=f'Hi {first_name} !',\n", - " duration=4,\n", - " style=TextStyle(\n", - " fontsize=32,\n", - " font=\"montserrat\",\n", - " borderw=1,\n", - " boxcolor=\"#D2C11D\",\n", - " boxborderw=\"20\",\n", - " x=\"((w-tw)/2)\",\n", - " y=\"(h-th)/4\"\n", - " )\n", - ")\n", - "\n", - "# Image Asset with Medium Picture\n", - "image = coll.upload(url=medium_picture, media_type=MediaType.image)\n", - "image_asset = ImageAsset(\n", - " asset_id=image.id,\n", - " width=80,\n", - " height=80,\n", - " x=\"275\",\n", - " y=\"230\",\n", - " duration=4\n", - ")\n", - "\n", - "# Fixed Text Asset\n", - "cmon_asset = TextAsset(\n", - " text=\"Here are your favorite moments\",\n", - " duration=4,\n", - " style=TextStyle(\n", - " fontsize=24,\n", - " fontcolor=\"#D2C11D\",\n", - " font=\"montserrat\",\n", - " bordercolor=\"#D2C11D\",\n", - " borderw=1,\n", - " boxborderw=\"20\",\n", - " x=\"((w-tw)/2)\",\n", - " y=\"(h-200)\"\n", - " )\n", - ")\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "48WrFjfDjxHk" - }, - "source": [ - "### ↔️ Step 6: Create the VideoDB Timeline\n", - "\n", - "The VideoDB timeline allows you to arrange and layer your assets to create a dynamic video stream. In this example, we add the name and picture overlays at a specific time (5 seconds) within the base video." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": { - "id": "b291hycbjxHk" - }, - "outputs": [], - "source": [ - "# Create the timeline\n", - "timeline = Timeline(conn)\n", - "\n", - "# Add the base video to the timeline\n", - "timeline.add_inline(video_asset)\n", - "\n", - "# Add overlays to the timeline\n", - "timeline.add_overlay(5, name_asset)\n", - "timeline.add_overlay(5, cmon_asset)\n", - "timeline.add_overlay(5, image_asset)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "K8pGqSZgjxHk" - }, - "source": [ - "### ▢️ Step 7: Generate and Play the Personalized Stream" - ] - }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "xEBvVSuzjxHh" + }, + "source": [ + "# ↔️ Building Dynamic Video Streams with VideoDB: Integrating Custom Data and APIs" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LCwBGclTjxHi" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "w8xeWTZkjxHi" + }, + "source": [ + "## Introduction\n", + "Imagine you're watching a captivating keynote session from your favorite conference, and you’re welcomed with a personalized stream just for you.\n", + "\n", + "This tutorial demonstrates how to create dynamic video streams by integrating data from custom databases and external APIs. We'll use a practical example: a recording of a [Config 2023](https://www.youtube.com/watch?v=Nmv8XdFiej0) keynote session. By using VideoDB, we'll show how companies like [Figma](https://www.figma.com/files/recents-and-sharing?fuid=940498258276625180) can personalize the viewing experience for their audience, delivering a richer and more engaging experience.\n", + "\n", + "We'll showcase how to:\n", + "\n", + "* Fetch data from a random user API to represent a hypothetical viewer.\n", + "* Integrate this data into a custom VideoDB timeline.\n", + "* Create a personalized stream that dynamically displays relevant information alongside the keynote video.\n", + "\n", + "This tutorial is your guide to unlocking the potential of dynamic video streams and transforming your video content with personalized experiences." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6Izs6lxqjxHi" + }, + "source": [ + "## Setup\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JiLgb86qjxHi" + }, + "source": [ + "\n", + "### πŸ“¦ Installing packages" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VaSLRgz-jxHj" + }, + "outputs": [], + "source": [ + "%pip install videodb" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8vQk4j1MjxHj" + }, + "source": [ + "### πŸ”‘ API Keys\n", + "Before proceeding, ensure access to [VideoDB](https://videodb.io) API key.\n", + "\n", + "> Get your API key from [VideoDB Console](https://console.videodb.io). ( Free for first 50 uploads, **No credit card required** ) πŸŽ‰" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "DuhMNCgfjxHj" + }, + "outputs": [], + "source": [ + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "# Secure way to enter your VideoDB API key\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "s8RAYTtnjxHj" + }, + "source": [ + "## Steps\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TIwDr4SIjxHj" + }, + "source": [ + "### πŸ”— Step 1: Connect to VideoDB" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cegRs1devmg9" + }, + "source": [ + "Establish a session for uploading videos. Import the necessary modules from VideoDB library to access functionalities." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "id": "GvEF198HjxHj" + }, + "outputs": [], + "source": [ + "import videodb\n", + "from videodb import connect\n", + "\n", + "conn = videodb.connect()\n", + "coll = conn.get_collection()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "bMGnv3j0jxHj" + }, + "source": [ + "### πŸ—³οΈ Step 2: Upload Base Video\n", + "Upload and play the video to ensure it's correctly loaded. We’ll be using [this video](https://www.youtube.com/watch?v=Nmv8XdFiej0) for the purpose of this tutorial." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "I4FukL25jxHj" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "CNq_yCwsjxHk" - }, - "source": [ - "The `generate_stream()` method creates a streamable URL for your personalized video stream. You can then use `play_stream()` to preview it in your browser." + "data": { + "text/plain": [ + "'https://console.videodb.io/player?url=https://dseetlpshk2tb.cloudfront.net/v3/published/manifests/5035d388-3b20-48e8-8391-ad157d3784b5.m3u8'" ] - }, + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Upload and play a video from a URL\n", + "video = coll.upload(url=\"https://www.youtube.com/watch?v=Nmv8XdFiej0\")\n", + "video.play()\n", + "\n", + "# Alternatively, get a video from your VideoDB collection\n", + "# video = coll.get_video('VIDEO_ID_HERE')\n", + "# video.play()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dS6KesexjxHk" + }, + "source": [ + "### πŸ“₯ Step 3: Fetch Data from a Random User API\n", + "This code fetches a random user's data (name and picture) from the \"randomuser.me\" API. You can adapt this to retrieve data from any relevant API (e.g., product data, news articles) for your use case." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "id": "4yLqe4g0jxHk" + }, + "outputs": [], + "source": [ + "import requests\n", + "\n", + "# Make a request to the Randomizer API\n", + "response = requests.get('https://randomuser.me/api/?results=1&nat=us,ca,gb,au')\n", + "data = response.json()\n", + "\n", + "# Extract relevant information\n", + "first_name = data['results'][0]['name']['first']\n", + "medium_picture = data['results'][0]['picture']['medium']" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5ML2q-OWjxHk" + }, + "source": [ + "### πŸš₯ Step 4 (optional): Prepare Data for Integration\n", + "\n", + "No additional data transformation is required in this example since we are using the data directly from the API. However, in more complex scenarios, you may need to format the data to be suitable for VideoDB.\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "5c0fYqjXjxHk" + }, + "source": [ + "### 🧱 Step 5: Create VideoDB Assets\n", + "\n", + "We create VideoDB assets for the base video, the user's name (text), and their picture (image). The `TextStyle` object allows us to customize the appearance of the text elements." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "fOBsa9pdjxHk" + }, + "outputs": [], + "source": [ + "from videodb import play_stream, TextStyle, MediaType\n", + "from videodb.asset import VideoAsset, TextAsset, ImageAsset\n", + "from videodb.timeline import Timeline\n", + "\n", + "# Video Asset\n", + "video_asset = VideoAsset(asset_id=video.id, start=0)\n", + "\n", + "# Text Asset with First Name\n", + "name_asset = TextAsset(\n", + " text=f'Hi {first_name} !',\n", + " duration=4,\n", + " style=TextStyle(\n", + " fontsize=32,\n", + " font=\"montserrat\",\n", + " borderw=1,\n", + " boxcolor=\"#D2C11D\",\n", + " boxborderw=\"20\",\n", + " x=\"((w-tw)/2)\",\n", + " y=\"(h-th)/4\"\n", + " )\n", + ")\n", + "\n", + "# Image Asset with Medium Picture\n", + "image = coll.upload(url=medium_picture, media_type=MediaType.image)\n", + "image_asset = ImageAsset(\n", + " asset_id=image.id,\n", + " width=80,\n", + " height=80,\n", + " x=\"275\",\n", + " y=\"230\",\n", + " duration=4\n", + ")\n", + "\n", + "# Fixed Text Asset\n", + "cmon_asset = TextAsset(\n", + " text=\"Here are your favorite moments\",\n", + " duration=4,\n", + " style=TextStyle(\n", + " fontsize=24,\n", + " fontcolor=\"#D2C11D\",\n", + " font=\"montserrat\",\n", + " bordercolor=\"#D2C11D\",\n", + " borderw=1,\n", + " boxborderw=\"20\",\n", + " x=\"((w-tw)/2)\",\n", + " y=\"(h-200)\"\n", + " )\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "48WrFjfDjxHk" + }, + "source": [ + "### ↔️ Step 6: Create the VideoDB Timeline\n", + "\n", + "The VideoDB timeline allows you to arrange and layer your assets to create a dynamic video stream. In this example, we add the name and picture overlays at a specific time (5 seconds) within the base video." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "id": "b291hycbjxHk" + }, + "outputs": [], + "source": [ + "# Create the timeline\n", + "timeline = Timeline(conn)\n", + "\n", + "# Add the base video to the timeline\n", + "timeline.add_inline(video_asset)\n", + "\n", + "# Add overlays to the timeline\n", + "timeline.add_overlay(5, name_asset)\n", + "timeline.add_overlay(5, cmon_asset)\n", + "timeline.add_overlay(5, image_asset)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "K8pGqSZgjxHk" + }, + "source": [ + "### ▢️ Step 7: Generate and Play the Personalized Stream" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CNq_yCwsjxHk" + }, + "source": [ + "The `generate_stream()` method creates a streamable URL for your personalized video stream. You can then use `play_stream()` to preview it in your browser." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "id": "96SYlAhQjxHk" + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "id": "96SYlAhQjxHk" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "https://dseetlpshk2tb.cloudfront.net/v3/published/manifests/52bedad7-2da2-4664-94a6-bb6d781e7471.m3u8\n" - ] - }, - { - "data": { - "text/plain": [ - "'https://console.videodb.io/player?url=https://dseetlpshk2tb.cloudfront.net/v3/published/manifests/52bedad7-2da2-4664-94a6-bb6d781e7471.m3u8'" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "from videodb import play_stream\n", - "\n", - "stream_url = timeline.generate_stream()\n", - "print(stream_url)\n", - "play_stream(stream_url)" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "https://dseetlpshk2tb.cloudfront.net/v3/published/manifests/52bedad7-2da2-4664-94a6-bb6d781e7471.m3u8\n" + ] }, { - "cell_type": "markdown", - "metadata": { - "id": "xiuqa7o9jxHl" - }, - "source": [ - "## Conclusion\n", - "---\n", - "\n", - "This tutorial showcased how to create personalized video streams using VideoDB. By integrating data from external APIs and custom databases, you can enhance your video content, personalize user experiences, and unlock new possibilities for engagement. Explore various data sources, experiment with different integrations, and customize your video streams to suit your specific needs." + "data": { + "text/plain": [ + "'https://console.videodb.io/player?url=https://dseetlpshk2tb.cloudfront.net/v3/published/manifests/52bedad7-2da2-4664-94a6-bb6d781e7471.m3u8'" ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" } - ], - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "display_name": ".venv", - "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.11.7" - } + ], + "source": [ + "from videodb import play_stream\n", + "\n", + "stream_url = timeline.generate_stream()\n", + "print(stream_url)\n", + "play_stream(stream_url)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xiuqa7o9jxHl" + }, + "source": [ + "## Conclusion\n", + "---\n", + "\n", + "This tutorial showcased how to create personalized video streams using VideoDB. By integrating data from external APIs and custom databases, you can enhance your video content, personalize user experiences, and unlock new possibilities for engagement. Explore various data sources, experiment with different integrations, and customize your video streams to suit your specific needs." + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 0 + "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.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/examples/Subtitle_Styling.ipynb b/examples/Subtitle_Styling.ipynb index d5e02e6..6e5f838 100644 --- a/examples/Subtitle_Styling.ipynb +++ b/examples/Subtitle_Styling.ipynb @@ -78,13 +78,17 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "# Secure way to enter your VideoDB API key\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/examples/Wellsaid_Voiceover_1.ipynb b/examples/Wellsaid_Voiceover_1.ipynb index 5f42bf7..2fb9c72 100644 --- a/examples/Wellsaid_Voiceover_1.ipynb +++ b/examples/Wellsaid_Voiceover_1.ipynb @@ -95,15 +95,23 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", + "\n", + "# Secure way to enter your API keys\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "openai_key = getpass(\"Please enter your OpenAI API Key: \")\n", + "os.environ[\"OPENAI_API_KEY\"] = openai_key\n", "\n", - "os.environ[\"OPENAI_API_KEY\"] = \"\"\n", - "os.environ[\"WELLSAID_API_KEY\"] = \"\"\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "wellsaid_key = getpass(\"Please enter your Wellsaid API Key: \")\n", + "os.environ[\"WELLSAID_API_KEY\"] = wellsaid_key" ] }, { diff --git a/examples/conference_slide_scraper.ipynb b/examples/conference_slide_scraper.ipynb index 2b0bff6..3eaa7dc 100644 --- a/examples/conference_slide_scraper.ipynb +++ b/examples/conference_slide_scraper.ipynb @@ -71,15 +71,19 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": { "id": "VogQFIYQYYoQ" }, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "# Prompt user for API key securely\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/examples/lecture_notes_1.ipynb b/examples/lecture_notes_1.ipynb index 1dc3228..45825f2 100644 --- a/examples/lecture_notes_1.ipynb +++ b/examples/lecture_notes_1.ipynb @@ -1,654 +1,660 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "3pO9hnPg8w0G" - }, - "source": [ - "# πŸ—’οΈ Lecture and Meeting Videos into Concise Notes with VideoDB" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ATyOkfiU8w0I" - }, - "source": [ - "\"Open" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "QjUobHuF8w0J" - }, - "source": [ - "Taking notes during lectures or meetings can be tough, especially when trying to capture both what's on the screen and what's being said. To make it easier, we've developed a tool with VideoDB that combines different modalitites into searchable notes. Now, you can quickly find and share key moments from any session, whether it's a specific slide or a crucial point in the discussion." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "X7WJgHAV8w0K" - }, - "source": [ - "## Setup\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "66p4vrIE8w0K" - }, - "source": [ - "### πŸ“¦ Installing packages" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "awQdOTpe8w0K" - }, - "source": [ - "Before diving in, make sure you have the necessary tools installed:\n", - "\n", - "* VideoDB: For video indexing and search.\n", - "* OpenAI: To generate text summaries.\n", - "* Markdown2: For converting markdown text into HTML.\n", - "\n", - "To get started, install these packages:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true, - "id": "nFjIoItLmf51" - }, - "outputs": [], - "source": [ - "%pip install videodb openai markdown2" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "fh0GJJOj8w0L" - }, - "source": [ - "## πŸ”‘ API keys\n", - "Before proceeding, ensure access to [VideoDB](https://videodb.io), [OpenAI](https://openai.com) API key. If not, sign up for API access on the respective platforms.\n", - "\n", - "> Get your API key from [VideoDB Console](https://console.videodb.io). ( Free for first 50 uploads, **No credit card required** ) πŸŽ‰" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "X8Ns6s0Bmf52" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"OPENAI_API_KEY\"] = \"\"\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "Bl5zD_v-8w0M" - }, - "source": [ - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "c6Ap1eTq8w0M" - }, - "source": [ - "## πŸ“‹ Step 1: Connect to VideoDB" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "zDjSZ9IVmf53" - }, - "outputs": [], - "source": [ - "from videodb import connect\n", - "\n", - "conn = connect()\n", - "coll = conn.get_collection()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "TT-XcOL28w0M" - }, - "source": [ - "## 🎬 Step 2: Upload the Video" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "fTkWckI48w0M" - }, - "source": [ - "Next, let's upload our sample video:\n", - "\n", - "You can use any public url, Youtube link or local file on your system.\n", - "\n", - "This approach works particularly well with:\n", - "\n", - "* University lectures that include PowerPoint slides and detailed discussions.\n", - "* Conference presentations where speakers use slides to illustrate their points.\n", - "* Team meetings that involve screen sharing and in-depth explanations.\n", - "\n", - "In this tutorial, we're using an [explainer video](https://www.youtube.com/watch?v=QJNwK2uJyGs) focusing on β€œIntroduction to Arrays”. This example is perfect for demonstrating how to capture and summarize key points from both visual aids and spoken explanations in an educational setting." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "8DIQTx7Cmf53" - }, - "outputs": [], - "source": [ - "# Upload a video by url\n", - "video = coll.upload(url=\"https://www.youtube.com/watch?v=QJNwK2uJyGs\")" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "ebw4_QaA8w0M" - }, - "source": [ - "## πŸ“ΈπŸ—£οΈ Step 3: Index the Video on different Modalities" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "JycbxU2L8w0M" - }, - "source": [ - "Now comes the exciting part - we're going to index our video in two ways:\n", - "\n", - "1. Indexing spoken content (what's being said in the video)\n", - "2. Indexing visual content (what's being shown in the video)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "N_hHNzXY8w0M" - }, - "source": [ - "#### πŸ—£οΈ Indexing Spoken Content" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "HvCOfD3nmf53", - "outputId": "d885520b-a80c-435b-b06e-119df17386f5" - }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 100/100 [00:47<00:00, 2.12it/s]\n" - ] - } - ], - "source": [ - "# Index spoken content\n", - "\n", - "video.index_spoken_words()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "j4Oakrtt8w0N" - }, - "source": [ - "#### πŸ‘οΈ Indexing Visual Content" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "7oLRm8B68w0N" - }, - "source": [ - "To learn more about Scene Index, explore the following guides:\n", - "\n", - "* [Quickstart Guide guide](https://github.com/video-db/videodb-cookbook/blob/main/quickstart/Scene%20Index%20QuickStart.ipynb) provides a step-by-step introduction to Scene Index. It's ideal for getting started quickly and understanding the primary functions.\n", - "* [Scene Extraction Options Guide](https://github.com/video-db/videodb-cookbook/blob/main/guides/scene-index/playground_scene_extraction.ipynb) delves deeper into the various options available for scene extraction within Scene Index. It covers advanced settings, customization features, and tips for optimizing scene extraction based on different needs and preferences." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "W9aBiPG28w0N" - }, - "source": [ - "**1.Finding the Right Configuration for Scene Extraction**:\n", - "\n", - "In this case, our ideal result (notes) would depend on being able to accurately identify and extract every slide in the video. We use shot-based extraction to accurately identify transitions between slides, and thus create a scene corresponding to every slide. This involves some testingβ€”starting with a higher threshold to see the initial results, then lowering it to ensure all important slides are captured. By fine-tuning these settings, we can ensure every slide is accurately represented as a distinct scene.\n", - "\n", - "Let’s dive in and see how it works:" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "1nme-mKe8w0N" - }, - "outputs": [], - "source": [ - "from videodb import SceneExtractionType\n", - "from IPython.display import Image, display\n", - "import requests\n", - "\n", - "# Helper function that will help us view the Scene Collection Images\n", - "def display_scenes(scenes, images=True):\n", - " for scene in scenes:\n", - " print(f\"{scene.id} : {scene.start}-{scene.end}\")\n", - " if images:\n", - " for frame in scene.frames:\n", - " im = Image(requests.get(frame.url, stream=True).content)\n", - " display(im)\n", - " print(\"----\")\n", - "\n", - "\n", - "scene_collection_default = video.extract_scenes(\n", - " extraction_type=SceneExtractionType.shot_based,\n", - " extraction_config={\"threshold\": 25, \"frame_count\": 1}\n", - ")\n", - "display_scenes(scene_collection_default.scenes)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "6rSzoF0b8w0N" - }, - "source": [ - "In this example, we begin with a higher threshold, but this may cause some important slides to be overlooked. To make sure we capture every slide, we'll adjust the threshold to a lower setting and re-run the extraction." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true, - "id": "GakHZQhggiY6" - }, - "outputs": [], - "source": [ - "from videodb import SceneExtractionType\n", - "\n", - "# Extract scenes using shot-based extraction\n", - "scene_collection = video.extract_scenes(\n", - " extraction_type=SceneExtractionType.shot_based,\n", - " extraction_config={\"threshold\": 10, \"frame_count\": 1}\n", - ")\n", - "display_scenes(scene_collection.scenes)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "iyNJZypH8w0N" - }, - "source": [ - "Now that we have found the right configuration for Scene Indexing, it's like we've found the perfect matchβ€”let's commit to indexing those scenes ✨!" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "46wCeoS98w0N" - }, - "source": [ - "**2. Indexing the Scenes for Easy Search**\n", - "\n", - "You can use the default prompt or customize it to fit your needs. Whether you want general notes or something more specific, tailoring the prompt lets you get exactly the information you're looking for." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "0eYk1tmbftSp" - }, - "outputs": [], - "source": [ - "index_id = video.index_scenes(\n", - " extraction_type=SceneExtractionType.shot_based,\n", - " extraction_config={\"threshold\": 10, \"frame_count\": 1},\n", - " prompt=\"Imagine you are student studying for an exam. You are watching a given video lecture slide. Take notes from this slide and also think through if you can gather more information around the topic mentioned in the slide\"\n", - ")\n", - "scene_index = video.get_scene_index(index_id)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "LrqM2Jbs8w0N" - }, - "source": [ - "## πŸ“‹ Step 4: Processing the Video and Summarize each scene" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "fmqSE1qp8w0N" - }, - "source": [ - "In this step, we'll combine the scene descriptions (equivalent to slide descriptions) and transcripts from each scene's duration, then ask the LLM to generate a comprehensive note. This approach allows our notes to integrate information from multiple modalities, including spoken words and visual content." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "fTtVFDudyp49" - }, - "source": [ - "![](https://raw.githubusercontent.com/video-db/videodb-cookbook-assets/main/images/guides/lecture_notes_1.png)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "KUHVlW5M8w0N" - }, - "outputs": [], - "source": [ - "import openai\n", - "\n", - "summarize_prompt = \"\"\"\n", - "Scene Description: {scene_description}\n", - "Scene Transcript: {scene_transcript}\n", - "\n", - "You are an AI tasked with creating concise summary notes for scenes from a video, integrating both visual and spoken content. For each scene, you will receive a description of the visual content (e.g., slides, images, on-screen text) and a transcript of the spoken content. Your goal is to synthesize this information into a single, coherent summary that captures the main points discussed by the narrator at the time of each specific slide.\n", - "\n", - "Task:\n", - "Identify key points from the spoken content (transcript) and determine their relevance.\n", - "Integrate these key points with the most relevant visual elements (e.g., slides, images) shown during the scene.\n", - "Focus on providing a cohesive summary that reflects the main discussion and key concepts presented.\n", - "Constraints:\n", - "It is not necessary to describe each visual element separately.\n", - "Only include details that contribute to a clear understanding of the main topic discussed.\n", - "The goal is to create a concise overall summary that encapsulates the essence of the explanation given during the scene.\n", - "The summary should not exceed 200 characters.\n", - "Output Format:\n", - "Provide the summary in Markdown format under the heading \"### Scene Summary.\" The summary should seamlessly blend the spoken content and visual context, presenting a unified description of the main ideas conveyed during the scene.\n", - "Example:\n", - "Visual Content Description:\n", - "\n", - "\"A slide titled 'Introduction to Quantum Computing' with a diagram illustrating qubits and superposition.\"\n", - "Spoken Content Transcript:\n", - "\n", - "\"In this slide, we discuss the basic concepts of quantum computing, focusing on the principle of superposition. Superposition allows qubits to exist in multiple states simultaneously, unlike classical bits.\"\n", - "Example Output:\n", - "Scene Summary: Introduction to Quantum Computing\n", - "This scene introduces the fundamental concepts of quantum computing, focusing on the principle of superposition. The slide features a diagram showing qubits in multiple states, contrasting with classical bits.\"\"\"\n", - "\n", - "\n", - "def extract_transcript_for_scene(transcript, start_time, end_time):\n", - " return \" \".join(\n", - " [item[\"text\"] for item in transcript if start_time <= item[\"start\"] < end_time]\n", - " )\n", - "\n", - "def summarize_scene(scene_description, scene_transcript):\n", - " client = openai.Client()\n", - " response = client.chat.completions.create(\n", - " model=\"gpt-4o-mini\",\n", - " messages=[\n", - " {\n", - " \"role\": \"system\",\n", - " \"content\": \"You are a helpful assistant that tasked with creating concise summary notes for scenes from a video, integrating both visual and spoken content. For each scene, you will receive a description of the visual content and a transcript of the spoken content. Your goal is to synthesize this information into a coherent summary that highlights the main points discussed while presenting the scene.\",\n", - " },\n", - " {\n", - " \"role\": \"user\",\n", - " \"content\": summarize_prompt.format(\n", - " scene_description=scene_description,\n", - " scene_transcript=scene_transcript,\n", - " ),\n", - " },\n", - " ],\n", - " )\n", - "\n", - " return response.choices[0].message.content\n", - "\n", - "def process_video(transcript, scenes):\n", - " summarized_scenes = []\n", - "\n", - " for scene in scenes:\n", - " scene_transcript = extract_transcript_for_scene(\n", - " transcript, scene[\"start\"], scene[\"end\"]\n", - " )\n", - " summary = summarize_scene(scene[\"description\"], scene_transcript)\n", - "\n", - " summarized_scenes.append(\n", - " {\n", - " \"start\": scene[\"start\"],\n", - " \"end\": scene[\"end\"],\n", - " \"image_url\": scene[\"image_url\"],\n", - " \"summary\": summary,\n", - " }\n", - " )\n", - "\n", - " return summarized_scenes\n", - "\n", - "scenes = [\n", - " {\n", - " \"start\": scene.start,\n", - " \"end\": scene.end,\n", - " \"image_url\": scene.frames[0].url,\n", - " \"description\": scene_index[\"description\"],\n", - " }\n", - " for scene_index, scene in zip(scene_index, scene_collection.scenes)\n", - "]\n", - "transcript = video.get_transcript()\n", - "summary = process_video(transcript, scenes)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "dEswHDyD8w0O" - }, - "source": [ - "## Step 5 : Display Summaries" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "jAezfGRI8w0O" - }, - "source": [ - "Here we display summaries of video scenes using HTML. The summaries include images, scene timing, and text descriptions formatted with Markdown. The `create_summary_html` function generates the HTML structure, styling each summary as a card with an image and formatted text. The `display_summaries` function renders the generated HTML in an interactive, scrollable format, making it easy to visualize and review multiple scenes at once.\n", - "\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "pDwCR4Z88w0O" - }, - "outputs": [], - "source": [ - "\n", - "from IPython.display import HTML, display\n", - "import markdown2\n", - "\n", - "def create_summary_html(summaries):\n", - " html_content = \"\"\"\n", - " \n", - "
\n", - " \"\"\"\n", - "\n", - " for i, summary in enumerate(summaries, 1):\n", - " markdown_summary = markdown2.markdown(summary['summary'])\n", - " html_content += f\"\"\"\n", - "
\n", - " \"Scene\n", - "

Scene {i} ({summary['start']:.2f}s - {summary['end']:.2f}s)

\n", - "
{markdown_summary}
\n", - "
\n", - " \"\"\"\n", - "\n", - " html_content += \"
\"\n", - " return html_content\n", - "\n", - "def display_summaries(summaries):\n", - " html = create_summary_html(summaries)\n", - " display(HTML(html))\n", - "\n", - "display_summaries(summary)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "_NLJMUTC0iwW" - }, - "source": [ - "## Conclusion\n", - "---" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "OArwI7Ny0yBC" - }, - "source": [ - "In this tutorial, we've explored a sophisticated approach to extracting and identifying key points from lecture and meeting videos by combining spoken word indexing with shot-based scene indexing. By leveraging VideoDB's robust multimodal indexing capabilities, we've developed a powerful workflow that enables you to efficiently create detailed summaries or notes from your videos, capturing everything from crucial discussion points to key slides in a presentation.\n", - "\n", - "This approach is highly adaptable and can be applied to various scenarios where summarization is essential, such as:\n", - "\n", - "* Summarizing Board Meetings: Capture key decisions by linking spoken discussions with slides or whiteboard content.\n", - "* Creating Training Notes: Merge screen recordings with spoken instructions to develop comprehensive step-by-step summaries.\n", - "* Documenting Q&A Sessions: Summarize questions and answers by indexing on-screen content with verbal responses for easy reference." - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "IGjZynQ8004c" - }, - "source": [ - "### Further Resources\n", - "To learn more about Scene Index, explore the following guides:\n", - "\n", - "- [Quickstart Guide](https://github.com/video-db/videodb-cookbook/blob/main/quickstart/Scene%20Index%20QuickStart.ipynb)\n", - "- [Scene Extraction Options](https://github.com/video-db/videodb-cookbook/blob/main/guides/scene-index/playground_scene_extraction.ipynb)\n", - "- [Advanced Visual Search](https://github.com/video-db/videodb-cookbook/blob/main/guides/scene-index/advanced_visual_search.ipynb)\n", - "- [Custom Annotation Pipelines](https://github.com/video-db/videodb-cookbook/blob/main/guides/scene-index/custom_annotations.ipynb)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "XJrVRPD38w0O" - }, - "source": [ - "### πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦ Support & Community\n", - "\n", - "If you have any questions or feedback. Feel free to reach out to us πŸ™ŒπŸΌ\n", - "\n", - "* [Discord](https://colab.research.google.com/corgiredirector?site=https%3A%2F%2Fdiscord.gg%2Fpy9P639jGz)\n", - "* [GitHub](https://github.com/video-db)\n", - "* [Email](mailto:ashu@videodb.io)\n" - ] - } - ], - "metadata": { + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "3pO9hnPg8w0G" + }, + "source": [ + "# πŸ—’οΈ Lecture and Meeting Videos into Concise Notes with VideoDB" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ATyOkfiU8w0I" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "QjUobHuF8w0J" + }, + "source": [ + "Taking notes during lectures or meetings can be tough, especially when trying to capture both what's on the screen and what's being said. To make it easier, we've developed a tool with VideoDB that combines different modalitites into searchable notes. Now, you can quickly find and share key moments from any session, whether it's a specific slide or a crucial point in the discussion." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "X7WJgHAV8w0K" + }, + "source": [ + "## Setup\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "66p4vrIE8w0K" + }, + "source": [ + "### πŸ“¦ Installing packages" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "awQdOTpe8w0K" + }, + "source": [ + "Before diving in, make sure you have the necessary tools installed:\n", + "\n", + "* VideoDB: For video indexing and search.\n", + "* OpenAI: To generate text summaries.\n", + "* Markdown2: For converting markdown text into HTML.\n", + "\n", + "To get started, install these packages:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "id": "nFjIoItLmf51" + }, + "outputs": [], + "source": [ + "%pip install videodb openai markdown2" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fh0GJJOj8w0L" + }, + "source": [ + "## πŸ”‘ API keys\n", + "Before proceeding, ensure access to [VideoDB](https://videodb.io), [OpenAI](https://openai.com) API key. If not, sign up for API access on the respective platforms.\n", + "\n", + "> Get your API key from [VideoDB Console](https://console.videodb.io). ( Free for first 50 uploads, **No credit card required** ) πŸŽ‰" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "X8Ns6s0Bmf52" + }, + "outputs": [], + "source": [ + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "# Prompt user for API key securely\n", + "videodb_api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = videodb_api_key\n", + "\n", + "openai_api_key = getpass(\"Please enter your OpenAI API Key: \")\n", + "os.environ[\"OPENAI_API_KEY\"] = openai_api_key" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Bl5zD_v-8w0M" + }, + "source": [ + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "c6Ap1eTq8w0M" + }, + "source": [ + "## πŸ“‹ Step 1: Connect to VideoDB" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "zDjSZ9IVmf53" + }, + "outputs": [], + "source": [ + "from videodb import connect\n", + "\n", + "conn = connect()\n", + "coll = conn.get_collection()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "TT-XcOL28w0M" + }, + "source": [ + "## 🎬 Step 2: Upload the Video" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fTkWckI48w0M" + }, + "source": [ + "Next, let's upload our sample video:\n", + "\n", + "You can use any public url, Youtube link or local file on your system.\n", + "\n", + "This approach works particularly well with:\n", + "\n", + "* University lectures that include PowerPoint slides and detailed discussions.\n", + "* Conference presentations where speakers use slides to illustrate their points.\n", + "* Team meetings that involve screen sharing and in-depth explanations.\n", + "\n", + "In this tutorial, we're using an [explainer video](https://www.youtube.com/watch?v=QJNwK2uJyGs) focusing on β€œIntroduction to Arrays”. This example is perfect for demonstrating how to capture and summarize key points from both visual aids and spoken explanations in an educational setting." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "8DIQTx7Cmf53" + }, + "outputs": [], + "source": [ + "# Upload a video by url\n", + "video = coll.upload(url=\"https://www.youtube.com/watch?v=QJNwK2uJyGs\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ebw4_QaA8w0M" + }, + "source": [ + "## πŸ“ΈπŸ—£οΈ Step 3: Index the Video on different Modalities" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "JycbxU2L8w0M" + }, + "source": [ + "Now comes the exciting part - we're going to index our video in two ways:\n", + "\n", + "1. Indexing spoken content (what's being said in the video)\n", + "2. Indexing visual content (what's being shown in the video)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "N_hHNzXY8w0M" + }, + "source": [ + "#### πŸ—£οΈ Indexing Spoken Content" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { "colab": { - "provenance": [] - }, - "kernelspec": { - "display_name": ".venv", - "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.11.7" + "base_uri": "https://localhost:8080/" + }, + "id": "HvCOfD3nmf53", + "outputId": "d885520b-a80c-435b-b06e-119df17386f5" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 100/100 [00:47<00:00, 2.12it/s]\n" + ] } + ], + "source": [ + "# Index spoken content\n", + "\n", + "video.index_spoken_words()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "j4Oakrtt8w0N" + }, + "source": [ + "#### πŸ‘οΈ Indexing Visual Content" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "7oLRm8B68w0N" + }, + "source": [ + "To learn more about Scene Index, explore the following guides:\n", + "\n", + "* [Quickstart Guide guide](https://github.com/video-db/videodb-cookbook/blob/main/quickstart/Scene%20Index%20QuickStart.ipynb) provides a step-by-step introduction to Scene Index. It's ideal for getting started quickly and understanding the primary functions.\n", + "* [Scene Extraction Options Guide](https://github.com/video-db/videodb-cookbook/blob/main/guides/scene-index/playground_scene_extraction.ipynb) delves deeper into the various options available for scene extraction within Scene Index. It covers advanced settings, customization features, and tips for optimizing scene extraction based on different needs and preferences." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "W9aBiPG28w0N" + }, + "source": [ + "**1.Finding the Right Configuration for Scene Extraction**:\n", + "\n", + "In this case, our ideal result (notes) would depend on being able to accurately identify and extract every slide in the video. We use shot-based extraction to accurately identify transitions between slides, and thus create a scene corresponding to every slide. This involves some testingβ€”starting with a higher threshold to see the initial results, then lowering it to ensure all important slides are captured. By fine-tuning these settings, we can ensure every slide is accurately represented as a distinct scene.\n", + "\n", + "Let’s dive in and see how it works:" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "1nme-mKe8w0N" + }, + "outputs": [], + "source": [ + "from videodb import SceneExtractionType\n", + "from IPython.display import Image, display\n", + "import requests\n", + "\n", + "# Helper function that will help us view the Scene Collection Images\n", + "def display_scenes(scenes, images=True):\n", + " for scene in scenes:\n", + " print(f\"{scene.id} : {scene.start}-{scene.end}\")\n", + " if images:\n", + " for frame in scene.frames:\n", + " im = Image(requests.get(frame.url, stream=True).content)\n", + " display(im)\n", + " print(\"----\")\n", + "\n", + "\n", + "scene_collection_default = video.extract_scenes(\n", + " extraction_type=SceneExtractionType.shot_based,\n", + " extraction_config={\"threshold\": 25, \"frame_count\": 1}\n", + ")\n", + "display_scenes(scene_collection_default.scenes)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6rSzoF0b8w0N" + }, + "source": [ + "In this example, we begin with a higher threshold, but this may cause some important slides to be overlooked. To make sure we capture every slide, we'll adjust the threshold to a lower setting and re-run the extraction." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true, + "id": "GakHZQhggiY6" + }, + "outputs": [], + "source": [ + "from videodb import SceneExtractionType\n", + "\n", + "# Extract scenes using shot-based extraction\n", + "scene_collection = video.extract_scenes(\n", + " extraction_type=SceneExtractionType.shot_based,\n", + " extraction_config={\"threshold\": 10, \"frame_count\": 1}\n", + ")\n", + "display_scenes(scene_collection.scenes)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "iyNJZypH8w0N" + }, + "source": [ + "Now that we have found the right configuration for Scene Indexing, it's like we've found the perfect matchβ€”let's commit to indexing those scenes ✨!" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "46wCeoS98w0N" + }, + "source": [ + "**2. Indexing the Scenes for Easy Search**\n", + "\n", + "You can use the default prompt or customize it to fit your needs. Whether you want general notes or something more specific, tailoring the prompt lets you get exactly the information you're looking for." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "0eYk1tmbftSp" + }, + "outputs": [], + "source": [ + "index_id = video.index_scenes(\n", + " extraction_type=SceneExtractionType.shot_based,\n", + " extraction_config={\"threshold\": 10, \"frame_count\": 1},\n", + " prompt=\"Imagine you are student studying for an exam. You are watching a given video lecture slide. Take notes from this slide and also think through if you can gather more information around the topic mentioned in the slide\"\n", + ")\n", + "scene_index = video.get_scene_index(index_id)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "LrqM2Jbs8w0N" + }, + "source": [ + "## πŸ“‹ Step 4: Processing the Video and Summarize each scene" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fmqSE1qp8w0N" + }, + "source": [ + "In this step, we'll combine the scene descriptions (equivalent to slide descriptions) and transcripts from each scene's duration, then ask the LLM to generate a comprehensive note. This approach allows our notes to integrate information from multiple modalities, including spoken words and visual content." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "fTtVFDudyp49" + }, + "source": [ + "![](https://raw.githubusercontent.com/video-db/videodb-cookbook-assets/main/images/guides/lecture_notes_1.png)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "KUHVlW5M8w0N" + }, + "outputs": [], + "source": [ + "import openai\n", + "\n", + "summarize_prompt = \"\"\"\n", + "Scene Description: {scene_description}\n", + "Scene Transcript: {scene_transcript}\n", + "\n", + "You are an AI tasked with creating concise summary notes for scenes from a video, integrating both visual and spoken content. For each scene, you will receive a description of the visual content (e.g., slides, images, on-screen text) and a transcript of the spoken content. Your goal is to synthesize this information into a single, coherent summary that captures the main points discussed by the narrator at the time of each specific slide.\n", + "\n", + "Task:\n", + "Identify key points from the spoken content (transcript) and determine their relevance.\n", + "Integrate these key points with the most relevant visual elements (e.g., slides, images) shown during the scene.\n", + "Focus on providing a cohesive summary that reflects the main discussion and key concepts presented.\n", + "Constraints:\n", + "It is not necessary to describe each visual element separately.\n", + "Only include details that contribute to a clear understanding of the main topic discussed.\n", + "The goal is to create a concise overall summary that encapsulates the essence of the explanation given during the scene.\n", + "The summary should not exceed 200 characters.\n", + "Output Format:\n", + "Provide the summary in Markdown format under the heading \"### Scene Summary.\" The summary should seamlessly blend the spoken content and visual context, presenting a unified description of the main ideas conveyed during the scene.\n", + "Example:\n", + "Visual Content Description:\n", + "\n", + "\"A slide titled 'Introduction to Quantum Computing' with a diagram illustrating qubits and superposition.\"\n", + "Spoken Content Transcript:\n", + "\n", + "\"In this slide, we discuss the basic concepts of quantum computing, focusing on the principle of superposition. Superposition allows qubits to exist in multiple states simultaneously, unlike classical bits.\"\n", + "Example Output:\n", + "Scene Summary: Introduction to Quantum Computing\n", + "This scene introduces the fundamental concepts of quantum computing, focusing on the principle of superposition. The slide features a diagram showing qubits in multiple states, contrasting with classical bits.\"\"\"\n", + "\n", + "\n", + "def extract_transcript_for_scene(transcript, start_time, end_time):\n", + " return \" \".join(\n", + " [item[\"text\"] for item in transcript if start_time <= item[\"start\"] < end_time]\n", + " )\n", + "\n", + "def summarize_scene(scene_description, scene_transcript):\n", + " client = openai.Client()\n", + " response = client.chat.completions.create(\n", + " model=\"gpt-4o-mini\",\n", + " messages=[\n", + " {\n", + " \"role\": \"system\",\n", + " \"content\": \"You are a helpful assistant that tasked with creating concise summary notes for scenes from a video, integrating both visual and spoken content. For each scene, you will receive a description of the visual content and a transcript of the spoken content. Your goal is to synthesize this information into a coherent summary that highlights the main points discussed while presenting the scene.\",\n", + " },\n", + " {\n", + " \"role\": \"user\",\n", + " \"content\": summarize_prompt.format(\n", + " scene_description=scene_description,\n", + " scene_transcript=scene_transcript,\n", + " ),\n", + " },\n", + " ],\n", + " )\n", + "\n", + " return response.choices[0].message.content\n", + "\n", + "def process_video(transcript, scenes):\n", + " summarized_scenes = []\n", + "\n", + " for scene in scenes:\n", + " scene_transcript = extract_transcript_for_scene(\n", + " transcript, scene[\"start\"], scene[\"end\"]\n", + " )\n", + " summary = summarize_scene(scene[\"description\"], scene_transcript)\n", + "\n", + " summarized_scenes.append(\n", + " {\n", + " \"start\": scene[\"start\"],\n", + " \"end\": scene[\"end\"],\n", + " \"image_url\": scene[\"image_url\"],\n", + " \"summary\": summary,\n", + " }\n", + " )\n", + "\n", + " return summarized_scenes\n", + "\n", + "scenes = [\n", + " {\n", + " \"start\": scene.start,\n", + " \"end\": scene.end,\n", + " \"image_url\": scene.frames[0].url,\n", + " \"description\": scene_index[\"description\"],\n", + " }\n", + " for scene_index, scene in zip(scene_index, scene_collection.scenes)\n", + "]\n", + "transcript = video.get_transcript()\n", + "summary = process_video(transcript, scenes)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dEswHDyD8w0O" + }, + "source": [ + "## Step 5 : Display Summaries" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jAezfGRI8w0O" + }, + "source": [ + "Here we display summaries of video scenes using HTML. The summaries include images, scene timing, and text descriptions formatted with Markdown. The `create_summary_html` function generates the HTML structure, styling each summary as a card with an image and formatted text. The `display_summaries` function renders the generated HTML in an interactive, scrollable format, making it easy to visualize and review multiple scenes at once.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pDwCR4Z88w0O" + }, + "outputs": [], + "source": [ + "\n", + "from IPython.display import HTML, display\n", + "import markdown2\n", + "\n", + "def create_summary_html(summaries):\n", + " html_content = \"\"\"\n", + " \n", + "
\n", + " \"\"\"\n", + "\n", + " for i, summary in enumerate(summaries, 1):\n", + " markdown_summary = markdown2.markdown(summary['summary'])\n", + " html_content += f\"\"\"\n", + "
\n", + " \"Scene\n", + "

Scene {i} ({summary['start']:.2f}s - {summary['end']:.2f}s)

\n", + "
{markdown_summary}
\n", + "
\n", + " \"\"\"\n", + "\n", + " html_content += \"
\"\n", + " return html_content\n", + "\n", + "def display_summaries(summaries):\n", + " html = create_summary_html(summaries)\n", + " display(HTML(html))\n", + "\n", + "display_summaries(summary)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "_NLJMUTC0iwW" + }, + "source": [ + "## Conclusion\n", + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OArwI7Ny0yBC" + }, + "source": [ + "In this tutorial, we've explored a sophisticated approach to extracting and identifying key points from lecture and meeting videos by combining spoken word indexing with shot-based scene indexing. By leveraging VideoDB's robust multimodal indexing capabilities, we've developed a powerful workflow that enables you to efficiently create detailed summaries or notes from your videos, capturing everything from crucial discussion points to key slides in a presentation.\n", + "\n", + "This approach is highly adaptable and can be applied to various scenarios where summarization is essential, such as:\n", + "\n", + "* Summarizing Board Meetings: Capture key decisions by linking spoken discussions with slides or whiteboard content.\n", + "* Creating Training Notes: Merge screen recordings with spoken instructions to develop comprehensive step-by-step summaries.\n", + "* Documenting Q&A Sessions: Summarize questions and answers by indexing on-screen content with verbal responses for easy reference." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "IGjZynQ8004c" + }, + "source": [ + "### Further Resources\n", + "To learn more about Scene Index, explore the following guides:\n", + "\n", + "- [Quickstart Guide](https://github.com/video-db/videodb-cookbook/blob/main/quickstart/Scene%20Index%20QuickStart.ipynb)\n", + "- [Scene Extraction Options](https://github.com/video-db/videodb-cookbook/blob/main/guides/scene-index/playground_scene_extraction.ipynb)\n", + "- [Advanced Visual Search](https://github.com/video-db/videodb-cookbook/blob/main/guides/scene-index/advanced_visual_search.ipynb)\n", + "- [Custom Annotation Pipelines](https://github.com/video-db/videodb-cookbook/blob/main/guides/scene-index/custom_annotations.ipynb)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XJrVRPD38w0O" + }, + "source": [ + "### πŸ‘¨β€πŸ‘©β€πŸ‘§β€πŸ‘¦ Support & Community\n", + "\n", + "If you have any questions or feedback. Feel free to reach out to us πŸ™ŒπŸΌ\n", + "\n", + "* [Discord](https://colab.research.google.com/corgiredirector?site=https%3A%2F%2Fdiscord.gg%2Fpy9P639jGz)\n", + "* [GitHub](https://github.com/video-db)\n", + "* [Email](mailto:ashu@videodb.io)\n" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 0 + "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.11.7" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/guides/Cleanup.ipynb b/guides/Cleanup.ipynb index d863b93..433b079 100644 --- a/guides/Cleanup.ipynb +++ b/guides/Cleanup.ipynb @@ -54,16 +54,19 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", - "from videodb import connect\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"YOUR_KEY_HERE\"\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", "\n", - "conn = connect()" + "conn = videodb.connect()" ] }, { diff --git a/guides/Subtitle.ipynb b/guides/Subtitle.ipynb index c019ea9..a57154b 100644 --- a/guides/Subtitle.ipynb +++ b/guides/Subtitle.ipynb @@ -66,12 +66,17 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/guides/TextAsset.ipynb b/guides/TextAsset.ipynb index 8a3d14a..6854171 100644 --- a/guides/TextAsset.ipynb +++ b/guides/TextAsset.ipynb @@ -62,13 +62,17 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/guides/VideoDB_Search_and_Evaluation.ipynb b/guides/VideoDB_Search_and_Evaluation.ipynb index 75c7e8f..64daef8 100644 --- a/guides/VideoDB_Search_and_Evaluation.ipynb +++ b/guides/VideoDB_Search_and_Evaluation.ipynb @@ -97,14 +97,19 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": { "id": "MZSb3UMn4E7F" }, "outputs": [], "source": [ + "import videodb\n", "import os\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\" " + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/guides/genai.ipynb b/guides/genai.ipynb index dccd6d5..7ae13e7 100644 --- a/guides/genai.ipynb +++ b/guides/genai.ipynb @@ -64,15 +64,18 @@ "outputs": [], "source": [ "import videodb\n", + "import os\n", + "from getpass import getpass\n", "\n", - "# Replace with your actual API key\n", - "api_key = \"YOUR_API_KEY\"\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", "\n", "# Connect and get the default collection\n", - "conn = videodb.connect(api_key=api_key)\n", + "conn = videodb.connect()\n", "coll = conn.get_collection()\n", "\n", - "print(f\"Connected! Using collection ID: {coll.id}\")\n" + "print(f\"Connected! Using collection ID: {coll.id}\")" ] }, { diff --git a/guides/multimodal/Prompt_Experiments_and_Benchmarking.ipynb b/guides/multimodal/Prompt_Experiments_and_Benchmarking.ipynb index dad55cc..18b504e 100644 --- a/guides/multimodal/Prompt_Experiments_and_Benchmarking.ipynb +++ b/guides/multimodal/Prompt_Experiments_and_Benchmarking.ipynb @@ -65,13 +65,17 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/guides/scene-index/advanced_visual_search.ipynb b/guides/scene-index/advanced_visual_search.ipynb index 5ff5a62..b4699cc 100644 --- a/guides/scene-index/advanced_visual_search.ipynb +++ b/guides/scene-index/advanced_visual_search.ipynb @@ -64,13 +64,17 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/guides/scene-index/custom_annotations.ipynb b/guides/scene-index/custom_annotations.ipynb index be5c003..d1c15c2 100644 --- a/guides/scene-index/custom_annotations.ipynb +++ b/guides/scene-index/custom_annotations.ipynb @@ -60,13 +60,17 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/guides/scene-index/playground_scene_extraction.ipynb b/guides/scene-index/playground_scene_extraction.ipynb index 027a967..2c7483a 100644 --- a/guides/scene-index/playground_scene_extraction.ipynb +++ b/guides/scene-index/playground_scene_extraction.ipynb @@ -58,13 +58,17 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/integrations/llama-index/simple_video_rag.ipynb b/integrations/llama-index/simple_video_rag.ipynb index 9fd9f0e..d7f83e7 100644 --- a/integrations/llama-index/simple_video_rag.ipynb +++ b/integrations/llama-index/simple_video_rag.ipynb @@ -51,10 +51,17 @@ "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", "\n", - "os.environ[\"OPENAI_API_KEY\"] = \"\"\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "# Get VideoDB API Key\n", + "videodb_api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = videodb_api_key\n", + "\n", + "# Get OpenAI API Key \n", + "openai_api_key = getpass(\"Please enter your OpenAI API Key: \")\n", + "os.environ[\"OPENAI_API_KEY\"] = openai_api_key" ] }, { diff --git a/quickstart/Multimodal_Quickstart.ipynb b/quickstart/Multimodal_Quickstart.ipynb index 38731af..d17a20e 100644 --- a/quickstart/Multimodal_Quickstart.ipynb +++ b/quickstart/Multimodal_Quickstart.ipynb @@ -68,14 +68,21 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", + "\n", + "# Get VideoDB API Key\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", "\n", - "os.environ[\"OPENAI_API_KEY\"] = \"\"\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" + "# Get OpenAI API Key \n", + "openai_key = getpass(\"Please enter your OpenAI API Key: \")\n", + "os.environ[\"OPENAI_API_KEY\"] = openai_key" ] }, { diff --git a/quickstart/Scene Index QuickStart.ipynb b/quickstart/Scene Index QuickStart.ipynb index f024564..192066b 100644 --- a/quickstart/Scene Index QuickStart.ipynb +++ b/quickstart/Scene Index QuickStart.ipynb @@ -74,14 +74,17 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ + "import videodb\n", "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", "\n", - "#replace with your key\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"sk-xxxx-yyyyy-zzzz\"" + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" ] }, { diff --git a/quickstart/VideoDB Quickstart.ipynb b/quickstart/VideoDB Quickstart.ipynb index 8d02c13..0c6d989 100644 --- a/quickstart/VideoDB Quickstart.ipynb +++ b/quickstart/VideoDB Quickstart.ipynb @@ -55,13 +55,19 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "from videodb import connect, play_stream\n", - "# replace with your API key\n", - "conn = connect(api_key=\"sk-xxx-yyyyy-zzzz\")" + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "conn = videodb.connect()" ] }, { @@ -920,4 +926,4 @@ }, "nbformat": 4, "nbformat_minor": 4 -} \ No newline at end of file +} diff --git a/quickstart/scene_level_metadata_indexing.ipynb b/quickstart/scene_level_metadata_indexing.ipynb index 82f3eef..f624467 100644 --- a/quickstart/scene_level_metadata_indexing.ipynb +++ b/quickstart/scene_level_metadata_indexing.ipynb @@ -1,952 +1,956 @@ { - "cells": [ - { - "cell_type": "markdown", - "metadata": { - "id": "KZ634ujnvhAr" - }, - "source": [ - "# πŸ“Œ VideoDB F1 Race Search Pipeline (Turn Detection & Metadata Filtering)\n", - "\n", - "\"Open\n", - "\n", - "## 🎯 Objective\n", - "This notebook demonstrates **scene-level metadata filtering** in an F1 race video to enable precise search and retrieval.\n", - "\n", - "## πŸ” What We’re Doing:\n", - "βœ” Uploading an **F1 race video** \n", - "βœ” **Extracting scenes** every 2 seconds (1 frame per scene) \n", - "βœ” **Describing scenes** using AI-generated metadata \n", - "βœ” **Indexing scenes** with structured metadata (`camera_view` & `action_type`) \n", - "βœ” **Searching scenes** using **semantic search + metadata filtering** \n" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "XMhmSsaEv3wK" - }, - "source": [ - "# πŸ“¦ Install VideoDB SDK \n", - "Required for connecting and processing video data. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "collapsed": true, - "id": "SX2wQvG8v3es", - "outputId": "2f19fa41-eea7-4584-f034-0bf07a3f0137" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Collecting videodb\n", - " Downloading videodb-0.2.10.tar.gz (25 kB)\n", - " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - "Requirement already satisfied: requests>=2.25.1 in /usr/local/lib/python3.11/dist-packages (from videodb) (2.32.3)\n", - "Collecting backoff>=2.2.1 (from videodb)\n", - " Downloading backoff-2.2.1-py3-none-any.whl.metadata (14 kB)\n", - "Requirement already satisfied: tqdm>=4.66.1 in /usr/local/lib/python3.11/dist-packages (from videodb) (4.67.1)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/dist-packages (from requests>=2.25.1->videodb) (3.4.1)\n", - "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/dist-packages (from requests>=2.25.1->videodb) (3.10)\n", - "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.11/dist-packages (from requests>=2.25.1->videodb) (2.3.0)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/dist-packages (from requests>=2.25.1->videodb) (2025.1.31)\n", - "Downloading backoff-2.2.1-py3-none-any.whl (15 kB)\n", - "Building wheels for collected packages: videodb\n", - " Building wheel for videodb (setup.py) ... \u001b[?25l\u001b[?25hdone\n", - " Created wheel for videodb: filename=videodb-0.2.10-py3-none-any.whl size=27143 sha256=17790c3062d5620ef1448b2c684de7485815f6d4e401e0aeb980f4ae41081e68\n", - " Stored in directory: /root/.cache/pip/wheels/ac/43/46/922da11f9ba349968e03820b5e92a4949c78e423f6c8ec37a3\n", - "Successfully built videodb\n", - "Installing collected packages: backoff, videodb\n", - "Successfully installed backoff-2.2.1 videodb-0.2.10\n" - ] - } - ], - "source": [ - "!pip install videodb" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "qRrD_QWrwC7C" - }, - "source": [ - "# πŸ”‘ Set Up API Key \n", - "Authenticate with VideoDB to access indexing and search functionalities. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "id": "PLZ3zTVVv7nM" - }, - "outputs": [], - "source": [ - "import os\n", - "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = \"\"" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "8r002aIkwTpc" - }, - "source": [ - "# 🌐 Connect to VideoDB \n", - "Establishes connection to manage video storage, indexing, and search. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "b3OqQrzxwQuY", - "outputId": "c14a24d9-0b47-44f7-aa7e-18fdda67bc94" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "c-81fc6459-fe30-44ac-8c5b-ea0898c2e152\n" - ] - } - ], - "source": [ - "from videodb import connect\n", - "\n", - "conn = connect()\n", - "coll = conn.get_collection()\n", - "\n", - "print(coll.id)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "kJ8mFZtowpaz" - }, - "source": [ - "# πŸŽ₯ Upload F1 Race Video \n", - "Adds the video to VideoDB for further processing. \n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "jGRKA8wZwZcp", - "outputId": "fbaf7c62-e372-44a3-b2e2-ea42c6fb80f4" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "m-z-01954d91-651d-7ef0-a022-18aad60eabdb\n" - ] - } - ], - "source": [ - "video = coll.upload(url=\"https://www.youtube.com/watch?v=2-oslsgSaTI\")\n", - "print(video.id)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "twKYyQFb2VVl" - }, - "source": [ - "## βœ‚οΈ Extracting Scenes (Every 2 Seconds)\n", - "We split the video into **2-second scenes**, extracting a **single frame per scene** for indexing.\n", - "\n", - "### **Why?**\n", - "- This ensures **granular indexing**, making **scene-level filtering more precise**.\n", - "- By extracting **key frames**, we can later **assign AI-generated metadata** to describe each scene accurately.\n" - ] + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "KZ634ujnvhAr" + }, + "source": [ + "# πŸ“Œ VideoDB F1 Race Search Pipeline (Turn Detection & Metadata Filtering)\n", + "\n", + "\"Open\n", + "\n", + "## 🎯 Objective\n", + "This notebook demonstrates **scene-level metadata filtering** in an F1 race video to enable precise search and retrieval.\n", + "\n", + "## πŸ” What We’re Doing:\n", + "βœ” Uploading an **F1 race video** \n", + "βœ” **Extracting scenes** every 2 seconds (1 frame per scene) \n", + "βœ” **Describing scenes** using AI-generated metadata \n", + "βœ” **Indexing scenes** with structured metadata (`camera_view` & `action_type`) \n", + "βœ” **Searching scenes** using **semantic search + metadata filtering** \n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "XMhmSsaEv3wK" + }, + "source": [ + "# πŸ“¦ Install VideoDB SDK \n", + "Required for connecting and processing video data. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, + "collapsed": true, + "id": "SX2wQvG8v3es", + "outputId": "2f19fa41-eea7-4584-f034-0bf07a3f0137" + }, + "outputs": [ { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "XWE6xx0v2h3s", - "outputId": "b87a73b5-5f75-43d8-d872-4f62a2f4c408" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Scene Collection ID: tt2smf1\n", - "Total Scenes Extracted: 148\n" - ] - } - ], - "source": [ - "# Extract Scenes Every 2 Seconds (1 Frame per Scene)\n", - "from videodb import SceneExtractionType\n", - "\n", - "scene_collection = video.extract_scenes(\n", - " extraction_type=SceneExtractionType.time_based,\n", - " extraction_config={\"time\": 2, \"select_frames\": [\"middle\"]},\n", - ")\n", - "\n", - "print(f\"Scene Collection ID: {scene_collection.id}\")\n", - "\n", - "scenes = scene_collection.scenes\n", - "\n", - "print(f\"Total Scenes Extracted: {len(scenes)}\")" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting videodb\n", + " Downloading videodb-0.2.10.tar.gz (25 kB)\n", + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + "Requirement already satisfied: requests>=2.25.1 in /usr/local/lib/python3.11/dist-packages (from videodb) (2.32.3)\n", + "Collecting backoff>=2.2.1 (from videodb)\n", + " Downloading backoff-2.2.1-py3-none-any.whl.metadata (14 kB)\n", + "Requirement already satisfied: tqdm>=4.66.1 in /usr/local/lib/python3.11/dist-packages (from videodb) (4.67.1)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/dist-packages (from requests>=2.25.1->videodb) (3.4.1)\n", + "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/dist-packages (from requests>=2.25.1->videodb) (3.10)\n", + "Requirement already satisfied: urllib3<3,>=1.21.1 in /usr/local/lib/python3.11/dist-packages (from requests>=2.25.1->videodb) (2.3.0)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/dist-packages (from requests>=2.25.1->videodb) (2025.1.31)\n", + "Downloading backoff-2.2.1-py3-none-any.whl (15 kB)\n", + "Building wheels for collected packages: videodb\n", + " Building wheel for videodb (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Created wheel for videodb: filename=videodb-0.2.10-py3-none-any.whl size=27143 sha256=17790c3062d5620ef1448b2c684de7485815f6d4e401e0aeb980f4ae41081e68\n", + " Stored in directory: /root/.cache/pip/wheels/ac/43/46/922da11f9ba349968e03820b5e92a4949c78e423f6c8ec37a3\n", + "Successfully built videodb\n", + "Installing collected packages: backoff, videodb\n", + "Successfully installed backoff-2.2.1 videodb-0.2.10\n" + ] + } + ], + "source": [ + "!pip install videodb" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qRrD_QWrwC7C" + }, + "source": [ + "# πŸ”‘ Set Up API Key \n", + "Authenticate with VideoDB to access indexing and search functionalities. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PLZ3zTVVv7nM" + }, + "outputs": [], + "source": [ + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8r002aIkwTpc" + }, + "source": [ + "# 🌐 Connect to VideoDB \n", + "Establishes connection to manage video storage, indexing, and search. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, + "id": "b3OqQrzxwQuY", + "outputId": "c14a24d9-0b47-44f7-aa7e-18fdda67bc94" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "FuftaJDm3-By" - }, - "source": [ - "## πŸ” Generating Scene Metadata\n", - "To **make scenes searchable**, we use AI to **describe & categorize** each scene with the following **structured metadata**:\n", - "\n", - "### **πŸ“Œ Scene-Level Metadata Fields:**\n", - "1️⃣ **`camera_view`** β†’ **Where is the camera placed?** \n", - " - `\"road_ahead\"` β†’ Driver’s **POV looking forward** \n", - " - `\"helmet_selfie\"` β†’ Close-up of **driver’s helmet** \n", - "\n", - "2️⃣ **`action_type`** β†’ **What is the driver doing?** \n", - " - `\"clear_road\"` β†’ No cars ahead (clean lap) \n", - " - `\"chasing\"` β†’ Following another car (intense racing moment) \n", - "\n", - "### **πŸš€ Why This Matters**\n", - "- **Metadata filtering** allows us to **search for specific race scenarios.** \n", - "- **Combining metadata & semantic search** makes retrieval **highly precise**. \n" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "c-81fc6459-fe30-44ac-8c5b-ea0898c2e152\n" + ] + } + ], + "source": [ + "from videodb import connect\n", + "\n", + "conn = connect()\n", + "coll = conn.get_collection()\n", + "\n", + "print(coll.id)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kJ8mFZtowpaz" + }, + "source": [ + "# πŸŽ₯ Upload F1 Race Video \n", + "Adds the video to VideoDB for further processing. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, + "id": "jGRKA8wZwZcp", + "outputId": "fbaf7c62-e372-44a3-b2e2-ea42c6fb80f4" + }, + "outputs": [ { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "collapsed": true, - "id": "Cumx-35JPL_v", - "outputId": "0afa1cd5-e42d-4cd2-9040-a569170a79ad" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Scene from 0.0s to 2.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene is a tense overtaking maneuver at the Circuit de Monaco, featuring Max Verstappen in the Red Bull and Charles Leclerc in the Ferrari. Verstappen, having closed the gap significantly, is seen approaching Leclerc on the entry to the iconic Tunnel section, a narrow, winding portion of the track. With the tight confines and limited visibility, Verstappen must navigate the challenging turn with precision to make a successful overtake. The significance of the moment lies in the potential for a dramatic change in race leadership, as Verstappen seeks to regain the top position he lost earlier in the race.\n", - "Scene from 2.0s to 4.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene depicts a close-up, in-car perspective of a Formula 1 race. The driver, sporting a Walmart-branded livery, is navigating a tight corner on the track. The camera captures the driver's view as they follow a Red Bull Racing car, likely Max Verstappen, in the lead. This particular moment is crucial as it signifies a potential overtake attempt by the Walmart-branded driver, highlighting a critical juncture in the race where the driver is seeking to gain an advantage. The scene is a testament to the intense competition and strategic maneuvers that define Formula 1 racing.\n", - "Scene from 4.0s to 6.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene is a close-up, on-board perspective from the cockpit of the Red Bull car, driven by Max Verstappen. The car is navigating the treacherous, high-speed, and winding section of the NΓΌrburgring, specifically the \"Flugplatz\" (airport) corner. The camera angle captures the rear-view mirror reflecting a preceding car, likely a Ferrari, battling for the lead. The intense focus on the mirror suggests a dramatic battle for position, with Verstappen attempting to close the gap and potentially overtake his rival. The scene highlights the driver's intense concentration and the critical nature of this particular corner, known for its tight radius and potential for overtaking maneuvers.\n", - "Scene from 6.0s to 8.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene unfolds at the Hockenheimring, Germany, as the driver of the Red Bull Racing car closes in on a competitor, likely a Ferrari, on the fast and flowing approach to the hairpin. The camera angle provides a cockpit view, capturing the driver's perspective as they meticulously navigate the complex series of curves. The driver is in a tense battle, pushing their car to the limit to attempt an overtaking maneuver. This is a crucial moment in the race, as overtaking opportunities at Hockenheim are limited, and the driver's success here could significantly alter the race outcome.\n", - "Scene from 8.0s to 10.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene captures the moment a Red Bull driver, likely Max Verstappen, closes in on a rival car on the NΓΌrburgring circuit. The iconic German track's fast and flowing nature is evident as the driver navigates a long, sweeping corner. The driver's helmet and the \"Red Bull\" branding are the only visible elements in the image, emphasizing the driver's intense focus. This scene is likely a critical moment in the race, with the Red Bull driver pushing for a pass and potentially taking the lead.\n", - "Scene from 10.0s to 12.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: This is an in-car perspective shot from a Red Bull car, likely driven by Max Verstappen. The scene takes place on a fast, flowing section of a track, potentially the Hockenheimring. The car is trailing another car, likely a Mercedes, as they approach a slight bend. This is a pivotal moment as Verstappen is attempting to close the gap and potentially overtake Hamilton, showcasing the intense competition between the two drivers and their teams. The focus is on the aggressive driving style of Verstappen, pushing the limits of his car to gain an advantage.\n", - "Scene from 12.0s to 14.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene captures a tense moment during a Formula 1 race, showcasing the driver's perspective from the cockpit. The driver, seemingly in a Red Bull, is approaching a corner, likely the famous Eau Rouge at Spa-Francorchamps. The driver's helmet is visible, highlighting their focus as they navigate the challenging bend. The scene is significant as it portrays the intensity of the race and the driver's skill in managing the car's speed and trajectory. The backdrop features lush greenery and a guardrail, further emphasizing the iconic location of the track.\n", - "Scene from 14.0s to 16.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures the thrilling moment as Max Verstappen in his Red Bull, trailing a competitor, approaches the challenging Turn 1 of the Hockenheimring. The camera angle, mounted on the Red Bull, provides a driver's perspective as the car navigates the left-hand corner at high speed. This close-up showcases the intense focus and agility required to master this iconic circuit. The scene underscores the raw power and precision of Formula 1 racing.\n", - "Scene from 16.0s to 18.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: This scene captures a driver, presumably Max Verstappen, in his Red Bull Racing car, navigating the challenging Eau Rouge and Raidillon corners at Spa-Francorchamps. The shot is taken from the driver's perspective, emphasizing the sheer speed and force the car endures as it crests the iconic hill. The blurred background showcases the challenging nature of the corner, while the driver's helmet, bearing the slogan \"There is still a race to win\", highlights the relentless determination required for success in Formula 1.\n", - "Scene from 18.0s to 20.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene captures a driver's perspective from the cockpit of a Red Bull Racing car. The driver, likely Max Verstappen, is in the midst of a tight battle for position, battling a rival car alongside him. The action unfolds on a high-speed section of the track, likely the fast, flowing corners of the Hockenheimring. The driver's helmet, emblazoned with the message \"Race Without Trace, There Is Still A Race To Win,\" conveys the intense determination and competitive spirit driving the moment. The tension is palpable, as the driver focuses intently on his opponent, poised to make a decisive move for the lead. This scene represents a critical juncture in the race, where a single strategic maneuver could determine the outcome.\n", - "Scene from 20.0s to 22.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene is a first-person cockpit view, capturing the intense focus of a driver navigating the challenging corners of a Formula 1 circuit. We see the driver, wearing a helmet emblazoned with the message \"Race Without Trace, There is Still a Race to Win,\" which suggests a message of sustainability within the sport. The car, a Red Bull, is in the midst of the race, with the driver's hands gripping the steering wheel as they skillfully maneuver the car through a tight bend. This moment reflects the driver's unwavering determination to push their limits and secure victory despite the challenges ahead.\n", - "Scene from 22.0s to 24.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene depicts a driver, presumably Sebastian Vettel, in his Red Bull Racing car during a race. The camera captures the driver's perspective, showcasing the cockpit view from the driver's seat. The car is navigating a high-speed corner, the trees lining the track blurring by as Vettel maintains focus. The key element is the \"Race Without Trace\" helmet design featuring the message \"There is still a race to win.\" This signifies Vettel's determination and unwavering spirit in the face of adversity, pushing the limits to secure a victory.\n", - "Scene from 24.0s to 26.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene unfolds at the NΓΌrburgring, a historic and challenging circuit known for its demanding layout. The driver, Sebastian Vettel, is behind the wheel of his Red Bull Racing car, navigating the iconic \"Schwedenkreuz,\" a high-speed, multi-apex corner. The action is captured from Vettel's perspective, conveying the immense speed and the intricate steering movements required to tame this corner. The significance of the moment lies in the sheer thrill and precision demanded, a testament to the driver's expertise and the demanding nature of the track.\n", - "Scene from 26.0s to 28.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a driver, likely Max Verstappen in his Red Bull Racing car, navigating the challenging Hockenheimring circuit in Germany. The perspective is from the driver's cockpit, showcasing the exhilarating experience of piloting an F1 car at high speed. This is a crucial moment, possibly during qualifying, as the driver pushes the car to its limits, seeking the perfect lap time. The driver's focus is evident, hands gripping the steering wheel, eyes fixed on the apex of the approaching corner. The blur of the forest surrounding the track adds to the dramatic effect, emphasizing the driver's incredible speed and the technical nature of Formula 1. This scene encapsulates the essence of racing: a driver's mastery, a car's capability, and a relentless pursuit of victory.\n", - "Scene from 28.0s to 30.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Red Bull driver, likely Sebastian Vettel, navigating the challenging Ardennes Forest section of the Spa-Francorchamps circuit. The car is on the approach to the fast, flowing \"Eau Rouge\" and \"Raidillon\" corners, a classic test of driver skill and car balance. The driver is seen pushing the car to its limits, with the car leaned heavily into the corner as it tackles the uphill climb. This is a crucial moment for the driver, as a slight misstep can easily lead to a costly error in this high-speed section. The scene exemplifies the intensity and precision demanded of Formula 1 drivers on a track renowned for its demanding nature.\n", - "Scene from 30.0s to 32.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a driver in a Red Bull car navigating the challenging \"S\" curves of the NΓΌrburgring. The driver is pushing hard, the car's tires are angled aggressively, and the blur of the surroundings signifies high speed. The driver is likely in pursuit or defending a position, showcasing the intense battle for track position typical of Formula 1 racing. The \"S\" curves, with their tight turns and elevation changes, demand precision and bravery, making this a defining moment in the race.\n", - "Scene from 32.0s to 34.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene depicts a first-person perspective from the cockpit of a Red Bull Formula 1 car. It is a high-speed corner on the Hockenheimring, a German circuit known for its fast and flowing nature. The driver, likely Daniel Ricciardo, is battling for position with an unidentified opponent, as he aggressively leans into the corner, pushing the car to its limits. The significance of the moment lies in the driver's skill and commitment to achieving a strong qualifying lap time, showcasing the thrilling and demanding nature of Formula 1.\n", - "Scene from 34.0s to 36.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures a first-person perspective of a Red Bull driver navigating the challenging Hockenheimring circuit. The driver, likely Daniel Ricciardo, is approaching the iconic \"Schwabacher Kurve\" right-hander, a notoriously tricky corner known for its tight apex and potential for overtaking. The driver is smoothly negotiating the turn, maintaining control while pushing the limits of the car's grip, showcasing the finesse required for high-speed cornering in Formula 1. The scene provides a glimpse into the raw experience of a driver at the peak of their abilities, maneuvering a powerful machine with incredible precision.\n", - "Scene from 36.0s to 38.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: This is a close-up shot from the onboard camera of a Red Bull Racing car, likely driven by Max Verstappen. The scene is set on a fast section of the track, potentially the Monza circuit, where the driver is leading the pack under a Safety Car period. His helmet inscription \"There is still a race\" emphasizes the driver's determination despite the temporary pause, hinting at his ambition for a strategic advantage during the restart.\n", - "Scene from 38.0s to 40.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene captures the intense perspective of Sebastian Vettel in his Red Bull Racing car, battling for position with a rival on the fast and flowing corners of the Nurburgring Nordschleife. The German driver, with his iconic \"Race Without Trace\" helmet, pushes hard, seeking a decisive overtaking move. The action unfolds in the latter stages of the race, with Vettel looking to salvage a podium finish after a challenging start. The focus on his helmet emphasizes his determination to make up ground, illustrating the driver's unwavering focus and the high stakes of the race.\n", - "Scene from 40.0s to 42.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene is an in-car perspective of Max Verstappen, driving the Red Bull Racing car, during a race. We are looking from the driver's seat, with his helmet in focus and the car's side panel partially visible. The car is navigating a high-speed corner on a track with trees and barriers visible in the background. The significance of the scene lies in its capturing Verstappen's determination as he navigates a challenging corner, likely in the midst of a close battle for position. The focus on his helmet, with the \"There is still a race to win\" inscription, underscores the intensity and competitive spirit of the moment.\n", - "Scene from 42.0s to 44.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This is a first-person perspective shot from the cockpit of a Red Bull car, driven by Sebastian Vettel. The car is navigating the challenging Hockenheimring track, specifically the infamous \"Motodrom\" corner. Vettel is aggressively pushing the car, the rear end is slightly loose, and the tire walls are visible as the car grazes the apex of the turn. The scene highlights the driver's talent in managing a challenging corner while maintaining a high speed and pushing the car to its limits.\n", - "Scene from 44.0s to 46.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures the cockpit perspective of a Red Bull driver, presumably Daniel Ricciardo, navigating the tight and challenging corners of the Hockenheimring circuit in Germany. The car is shown with the driver's helmet and the steering wheel clearly visible. The track is lined with a guardrail and green trees on the left side, signifying a fast-paced cornering sequence. The driver's focus and concentration are palpable, highlighting the technical prowess and precision required for navigating such a demanding track. The scene likely depicts a qualifying session, with the driver pushing the car and himself to the limit for a strong grid position.\n", - "Scene from 46.0s to 48.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures the cockpit perspective of a Red Bull Racing car, presumably driven by Max Verstappen, as he navigates the challenging NΓΌrburgring track. The iconic German circuit's green foliage and characteristic guardrails frame the action. The driver, in a moment of intense focus, expertly handles the car's powerful acceleration and intricate steering through a high-speed corner. The significance of this moment lies in showcasing the driver's mastery over the car and track, a testament to the precision and skill demanded in Formula 1 racing.\n", - "Scene from 48.0s to 50.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Red Bull car, driven by Max Verstappen, navigating the treacherous Hockenheimring's turn 1. The iconic blue and red livery of the car blends with the verdant backdrop, as Verstappen powers through the turn, leaving a trail of dust in his wake. This moment exemplifies the driver's aggressive driving style and his determination to establish a decisive lead. The camera angle, likely from the onboard perspective, captures the thrilling experience of the driver pushing the car and himself to the limit.\n", - "Scene from 50.0s to 52.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene is a dramatic overtaking maneuver at the NΓΌrburgring, a track famed for its challenging corners and unforgiving nature. A Red Bull car, driven by Sebastian Vettel, is locked in a fierce battle with a Mercedes, piloted by Lewis Hamilton. As they approach the daunting, high-speed section known as the \"Flugplatz,\" Vettel executes a daring move, diving down the inside of Hamilton. The camera, mounted on Vettel's car, captures the sheer audacity of the overtaking attempt, the blur of the Mercedes in the background as Vettel's car leans into the corner, tires squealing. This moment marks a turning point in the race, as Vettel's aggression sets the tone for a thrilling, action-packed finish.\n", - "Scene from 52.0s to 54.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene is a first-person perspective from the cockpit of a Red Bull car, likely driven by Sebastian Vettel, as he approaches a left-hand corner. The car is carrying significant speed, evident from the blur of the track ahead. The driver is executing a precise and confident line through the corner, demonstrating exceptional control of the car. The location appears to be a high-speed section of a circuit with a clear view of the surrounding landscape. The scene emphasizes the driver's expertise in navigating the track with precision and control, showcasing the exhilarating nature of Formula 1 racing.\n", - "Scene from 54.0s to 56.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This is an onboard shot of a Red Bull driver, likely Max Verstappen, exiting the Hockenheimring's Turn 1. The car, carrying the iconic red bull branding, is shown navigating the sharp right-hand corner with precision. The driver is demonstrating his mastery of the track by smoothly taking the apex, showcasing the car's agility and Verstappen's skill in controlling the vehicle's momentum and maintaining speed. This corner is often a crucial overtaking opportunity, and a driver's approach here can significantly impact their race strategy. The image emphasizes the driver's perspective and the intense focus required for this high-speed turn, showcasing the challenge and skill required to compete in Formula 1.\n", - "Scene from 56.0s to 58.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene is a close-up, on-board perspective from the cockpit of a Red Bull car, presumably driven by Sebastian Vettel, navigating the fast and sweeping corners of the Hockenheimring in Germany. The car is trailing another, unseen, competitor but attempting to close the gap. The image captures the intensity of the race, with the driver's helmet and the blur of the track highlighting the speed and concentration required in this high-stakes competition.\n", - "Scene from 58.0s to 60.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene depicts a first-person perspective of a Red Bull driver during a race. The driver, wearing a helmet emblazoned with \"Race Without Trace\" and the phrase \"There is still a race to go,\" is navigating the track's winding corners. The image captures the driver's focus and determination, highlighting the intensity of the race. The specific location is unclear, but the lush greenery and clear skies suggest a summer race at a circuit known for its challenging curves. The scene emphasizes the driver's resilience and unwavering belief in his ability to compete despite any prior setbacks.\n", - "Scene from 60.0s to 62.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This scene captures the exhilarating moment of a Red Bull driver, likely Daniel Ricciardo, pushing the limits through the fast-flowing Hockenheimring. The onboard camera perspective grants a thrilling immersion as the car navigates the iconic \"Motodrom,\" showcasing the driver's finesse and precision. The slight spray from the damp track adds an element of drama and highlights the driver's unwavering focus. This iconic German track is known for its high-speed corners and challenging conditions, which further intensifies this adrenaline-fueled moment. The driver's unwavering pursuit of victory is palpable, making this an unforgettable glimpse into the raw energy of Formula 1.\n", - "Scene from 62.0s to 64.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene is a close-up, onboard shot from the cockpit of a Red Bull car, likely driven by Max Verstappen. The car is navigating the tight, high-speed turn at the Red Bull Ring in Austria, known as Turn 9. This corner is a key overtaking opportunity, and the shot highlights Verstappen's precise steering and commitment to the apex, showcasing the driver's skill and the car's grip. The scene conveys the thrill and pressure of a high-stakes battle for position, a defining moment in the race for victory.\n", - "Scene from 64.0s to 66.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene is a close-up, onboard shot from the Red Bull RB18 driven by Sergio Perez, negotiating the sweeping right-hand corner at the end of the long back straight, known as Turn 1 at the Red Bull Ring in Austria. The camera captures the intense perspective from the cockpit, highlighting Perez's meticulous steering and the rapid deceleration of the car as it transitions into the corner, showcasing the demanding nature of this high-speed circuit. This moment symbolizes the intense focus and precision required by drivers in Formula 1, even in a seemingly routine corner.\n", - "Scene from 66.0s to 68.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: The scene depicts a Red Bull Racing car, driven by Max Verstappen, navigating the challenging Turn 1 at the Hungaroring circuit in Budapest, Hungary. The onboard camera captures the car's perspective as it enters the high-speed corner, showcasing Verstappen's smooth and precise driving style. The scene highlights the driver's ability to maintain control and momentum in a demanding and technical section of the track, demonstrating his mastery of the car and the circuit.\n", - "Scene from 68.0s to 70.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Red Bull car, presumably driven by Max Verstappen, navigating a fast, flowing section of the track. The car is seen from the cockpit perspective, with the driver's helmet and steering wheel visible. The car is in a straight line, possibly approaching a corner, the asphalt streaked with tire rubber from previous laps. The location is likely a high-speed section of the track, characterized by a smooth asphalt surface, low-lying greenery, and distant hills. The significance of this moment is the driver's focus and control as he navigates a demanding section of the circuit, showcasing the precision and athleticism required in Formula 1.\n", - "Scene from 70.0s to 72.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Red Bull driver navigating the NΓΌrburgring's fast and flowing section, the FuchsrΓΆhre. The car, adorned with the iconic Red Bull livery, races through a slight right-hand bend, its rear wing flexing under the strain of high-speed cornering. The driver's focus is evident as they maintain a precise racing line, seeking to maximize speed and precision. The backdrop of lush green hills and the distant sounds of the crowd add to the thrilling atmosphere of the race. This moment showcases the raw speed and agility of Formula 1 machinery as it tackles one of the most iconic and challenging corners in motorsports.\n", - "Scene from 72.0s to 74.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Red Bull car, likely driven by Max Verstappen, navigating the iconic Hockenheimring circuit in Germany. The car is approaching the infamous \"Motodrom,\" the high-speed banked corner, a crucial overtaking spot. The camera perspective is from the driver's seat, highlighting the immense speed and g-forces experienced as the car carves through the corner. The focus is on the driver's exceptional skill in maintaining control at such high speeds, a hallmark of Verstappen's driving style. The scene captures the adrenaline-pumping action of Formula 1, where every corner offers a potential opportunity for a daring maneuver.\n", - "Scene from 74.0s to 76.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures a Red Bull driver navigating the challenging, fast-flowing turns of the Hockenheimring circuit. The car, featuring the distinctive Red Bull livery, is pushing its limits, its front wing creating a visible downforce as it carves through the apex of the corner. The driver is exhibiting impressive control and precision, a testament to his skill and the car's handling capabilities. The scene underscores the raw power and technical finesse of Formula 1 racing, showcasing the drivers' commitment to achieving optimal performance.\n", - "Scene from 76.0s to 78.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Red Bull car, driven by Daniel Ricciardo, navigating the treacherous Hockenheimring, specifically the fast and flowing section known as the \"Sachskurve.\" The car is seen from the driver's perspective, showcasing Ricciardo's aggressive approach as he pushes the limits of the car through a high-speed corner. The shot highlights the driver's focus and control, demonstrating the immense skill required to master the intricacies of Formula 1. The scene captures the essence of high-speed racing, where every action is calculated and the margin for error is minimal.\n", - "Scene from 78.0s to 80.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene depicts a thrilling overtaking maneuver at the NΓΌrburgring's famed \"Flugplatz\" section. A Red Bull driver, likely Max Verstappen or Sergio Perez, aggressively pushes past a Williams car, utilizing the superior power of the Red Bull to slingshot out of the corner. The Williams driver, possibly George Russell or Nicholas Latifi, is seen struggling to maintain pace, highlighting the gap in performance between the two teams. The verdant forest backdrop and the iconic NΓΌrburgring infrastructure create a visually stunning backdrop for this decisive move, which could potentially swing the outcome of the race.\n", - "Scene from 80.0s to 82.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene captures a driver, presumably Sebastian Vettel in his Red Bull, approaching a sweeping left-hand corner at the Hockenheimring. The car's onboard camera reveals the driver's perspective, showcasing the green hills and the protective barrier lining the circuit. The action highlights the car's precise handling and Vettel's smooth approach into the corner, demonstrating the driver's mastery and the car's incredible grip. The scene encapsulates the elegance of Formula 1 driving, emphasizing the seamless integration of car and driver in a fast and challenging corner.\n", - "Scene from 82.0s to 84.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene depicts a driver in a Red Bull car, likely during a practice session, approaching a right-hand corner on a circuit featuring a high-speed sweeping section before the corner, a characteristic feature of the Hockenheimring. The driver is looking ahead, focused on the apex of the corner while another car is visible in the distance, presumably a rival car. The moment highlights the precision and speed of Formula 1, with the driver's focus and the track's design creating a visually striking scene for motorsport enthusiasts.\n", - "Scene from 84.0s to 86.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene is a first-person perspective from the cockpit of a Red Bull Racing car, driven by Max Verstappen. The car is approaching the iconic \"Hockenheimring\" track in Germany, with the distinctive green hills and rolling landscape in the background. Verstappen's helmet, emblazoned with the \"Race Without Trace\" logo, highlights the driver's focus on sustainability. The significance of the moment lies in the anticipation of a thrilling race, as Verstappen, a fierce competitor, prepares to conquer the challenging Hockenheim circuit.\n", - "Scene from 86.0s to 88.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: This is a cockpit view from Sebastian Vettel's Aston Martin, showcasing a tense overtake on the challenging Spa-Francorchamps circuit. The German driver, sporting his signature \"Race Without Trace\" helmet, is pushing hard, attempting to gain an advantage on a rival car, likely a Red Bull, as they navigate the fast and flowing section of the track. The close proximity and intense focus of the driver underscore the high-stakes nature of the competition.\n", - "Scene from 88.0s to 90.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene unfolds from the cockpit perspective of a Red Bull car, likely driven by Max Verstappen, navigating the iconic Hockenheimring circuit in Germany. The car is exiting the fast, flowing turn 11, known as the \"Motodrom,\" and accelerating towards the long straight. The key action is the driver's focus on maintaining precise control and speed while battling the aerodynamic forces of the high-speed corner, showcasing the intense physical and technical demands of Formula 1 driving. The backdrop of lush green forests and the concrete barriers add to the scenic beauty and inherent dangers of the iconic track.\n", - "Scene from 90.0s to 92.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Red Bull driver navigating the challenging Eau Rouge and Raidillon corners at the Circuit de Spa-Francorchamps. The car, adorned in the iconic blue and red livery, is seen from the driver's perspective. The driver is pushing the limits of adhesion, tires screeching as they grip the asphalt. The background showcases the lush greenery bordering the track, with the distinctive elevation changes of the iconic Belgian circuit. This moment highlights the sheer speed and technical prowess required to master this challenging corner complex. The driver is demonstrating his skill and control as he navigates this crucial section of the track, leaving a trail of tire smoke in his wake. This scene is not only a testament to the driver's skill but also to the unrelenting demands of Formula 1.\n", - "Scene from 92.0s to 94.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene captures the perspective of Sebastian Vettel, driving the Red Bull Racing car, as he navigates the challenging corners of the NΓΌrburgring circuit. Vettel is in the lead, closely followed by a McLaren car. The action takes place during the closing stages of the race, where Vettel faces immense pressure from his rivals. This particular moment marks a pivotal turn in the race, highlighting Vettel's grit and determination to maintain his position and ultimately secure the victory. The shot captures the intensity of the competition, the pressure on Vettel, and the potential for a thrilling finish.\n", - "Scene from 94.0s to 96.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: This is a cockpit view from the Red Bull RB18, piloted by Max Verstappen, as he navigates the challenging uphill climb of Eau Rouge at the Circuit de Spa-Francorchamps. The iconic Belgian track, known for its high-speed corners and elevation changes, presents a thrilling challenge for the drivers. Verstappen is seen concentrating intensely as he steers his car through the treacherous bend, showcasing the incredible precision and control required in Formula 1. The \"Race Without Trace\" helmet inscription signifies the driver's determination and focus to leave his mark on the track. The scene captures the essence of F1's relentless pursuit of speed and precision, emphasizing the driver's unwavering commitment to deliver a dominant performance.\n", - "Scene from 96.0s to 98.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This is a first-person perspective shot from a Red Bull car, likely driven by Max Verstappen, navigating the challenging \"Schwabacher Kurve\" at the NΓΌrburgring. The driver is pushing hard, the car leans into the turn, and the forested backdrop emphasizes the speed and the tight confines of the corner. This scene captures the essence of a driver's focus and control while pushing the limits of their car on a renowned, unforgiving circuit.\n", - "Scene from 98.0s to 100.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures a thrilling moment from the 2023 Austrian Grand Prix, as Max Verstappen, piloting his Red Bull RB19, navigates the treacherous Turn 4 at the Red Bull Ring. The onboard camera captures the intense focus on Verstappen's face, his hands deftly managing the car's controls. The track, lined by lush green foliage, dips sharply, emphasizing the technical challenge of this corner. This specific moment holds significant weight as Verstappen battles his teammate Sergio Perez for the lead, demonstrating Red Bull's dominance in this race. The scene encapsulates the raw intensity of Formula 1, where every turn and every driver decision can change the race's course.\n", - "Scene from 100.0s to 102.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Red Bull car, likely driven by Max Verstappen, exiting the high-speed left-hand corner at the end of the long straight at Hockenheimring. The car is in the middle of the track, accelerating aggressively, with the green hillsides of the German circuit framing the shot. The focus on the rear wheels and the car's motion suggests a thrilling overtake or a daring defensive maneuver against a rival, with the driver's helmet momentarily visible in the cockpit.\n", - "Scene from 102.0s to 104.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This is a first-person perspective shot from the cockpit of a Red Bull Formula 1 car. The driver is navigating the fast and flowing section of the track known as the \"Motodrom\" at the Red Bull Ring in Austria. The car is traveling at high speed, the trees blurring into a green tunnel, showcasing the driver's immense focus and precision as they tackle the challenging corner. The scene highlights the driver's exceptional control and the car's performance on the track.\n", - "Scene from 104.0s to 106.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene is a first-person perspective from the cockpit of a Red Bull car, likely driven by Daniel Ricciardo, as he navigates the treacherous Hockenheimring circuit during a qualifying session. The car is approaching the iconic \"Schwetzinger\" bend, a high-speed left-hand turn that often tests the driver's bravery and car's handling capabilities. The driver is pushing the limits, the car slightly sliding on the apex, showcasing the incredible skill and confidence needed to tackle this demanding corner at top speed. This exhilarating moment captures the essence of qualifying, where drivers fight for the pole position, putting their skills and cars under extreme pressure to achieve the best lap time.\n", - "Scene from 106.0s to 108.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures Max Verstappen in his Red Bull, navigating the challenging uphill esses of the Hockenheimring. He's pushing hard, the car slightly leaning towards the apex, a testament to the driver's skill and the car's exceptional grip. The background showcases the lush green German countryside, a stark contrast to the intensity of the racing action unfolding on the track. This moment encapsulates the exhilarating combination of technical precision and raw power that defines Formula 1.\n", - "Scene from 108.0s to 110.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This is a classic overtaking maneuver at the iconic Hockenheimring, Germany. Max Verstappen, in his Red Bull, is in pursuit of Lewis Hamilton's Mercedes, who is ahead on the track. The scene unfolds at the end of the long, sweeping left-hand curve of the \"Sachskurve,\" just as Verstappen is about to make his move on the inside line, attempting a daring pass on the outside of the apex. This crucial moment captures the intensity of their ongoing rivalry, as Verstappen fights to close the gap and challenge Hamilton's lead.\n", - "Scene from 110.0s to 112.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene depicts a Red Bull driver, likely Max Verstappen, in the cockpit of his RB18, as he navigates the high-speed esses of the Hockenheimring, a historic circuit known for its flowing layout. The camera, mounted on the car, offers a first-person perspective, capturing the driver's intense focus and the blur of the surrounding landscape. The scene emphasizes the exhilarating experience of driving an F1 car at its limit. The driver's helmet inscription \"Race Without Trace\" alludes to the relentless pursuit of victory and leaving no room for error.\n", - "Scene from 112.0s to 114.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene captures a tense moment in the cockpit of a Red Bull Formula 1 car, likely during a race. The driver, obscured by their helmet bearing the inscription \"There is still a race\", navigates the track, possibly at the Hockenheimring, through a series of corners. The angle emphasizes the driver's perspective, highlighting the intensity of the race as their car battles for position against a competitor whose rear wing is visible. The scene showcases the crucial moments of a Formula 1 race, where every corner presents an opportunity for overtaking or defending position.\n", - "Scene from 114.0s to 116.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: The scene is a first-person cockpit view from Max Verstappen's Red Bull Racing car during a lap at the NΓΌrburgring. Verstappen is leading the race, having overtaken Lewis Hamilton's Mercedes earlier on the lap. He is seen raising his right hand in a celebratory gesture as he navigates the challenging corners of the track. The significance of the moment lies in the fact that Verstappen is dominating the race and is in a strong position to secure victory. The image captures the thrill and excitement of a Formula 1 race as Verstappen drives with precision and determination.\n", - "Scene from 116.0s to 118.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures a Red Bull car, likely driven by Max Verstappen, exiting the challenging Turn 10 at the Hockenheimring. The car is accelerating up the hill, the driver maintaining a steady line, the car's purple and yellow livery catching the sunlight. The significance lies in Verstappen's ability to maintain momentum through this crucial corner, crucial for maintaining a strong lead. The backdrop of lush greenery and the distant sound of cheering spectators add to the atmosphere of high-octane racing.\n", - "Scene from 118.0s to 120.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene captures a Red Bull car, likely driven by Max Verstappen, navigating the challenging uphill section of the iconic NΓΌrburgring circuit. The car is seen driving slowly, seemingly under caution due to the presence of a safety car in the distance. The distinctive yellow and blue livery of the Red Bull is prominent, and the car's rear wing is slightly angled downwards, indicating a low-speed maneuver. This scene reflects a tense moment, as the race is temporarily paused, and the drivers await the signal to resume their battle for the lead.\n", - "Scene from 120.0s to 122.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: This is a close-up, onboard shot from the cockpit of a Red Bull car, likely driven by Max Verstappen, as he navigates the tight, wooded section of the Circuit Gilles Villeneuve in Montreal, Canada. The car is in the lead, closely pursued by a black car with a white logo, likely a Mercedes driven by Lewis Hamilton. This dramatic overtake attempt at the hairpin turn is the defining moment of the scene, capturing the raw intensity of a Formula 1 battle.\n", - "Scene from 122.0s to 124.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene captures a tense overtaking maneuver at the NΓΌrburgring, a high-speed, challenging circuit in Germany. Max Verstappen in the Red Bull is hot on the tail of Lewis Hamilton in the Mercedes, approaching the daunting Turn 1. The tight, left-hand bend, known for its high-speed entry and potential for slip-ups, is where Verstappen makes his move. As Hamilton slightly overshoots the apex, Verstappen capitalizes, slipping his car through the inside line, leaving Hamilton to fight for the lead in the next sector. This is a pivotal moment in the race, showcasing Verstappen's aggressive driving style and highlighting the intense rivalry between the two championship contenders.\n", - "Scene from 124.0s to 126.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene depicts a tense moment during the race. The driver in the lead, Max Verstappen in his Red Bull, is battling for position against a rival car, likely a Ferrari, as they approach a challenging corner on the NΓΌrburgring. The Red Bull is pushing hard, with its front wheels just inside the white line, demonstrating the driver's aggressive pursuit of victory. The dust kicked up by the preceding car hints at the intensity of the close battle, as the drivers push their cars to the limit in this iconic German circuit.\n", - "Scene from 126.0s to 128.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene depicts a Formula 1 car, a Red Bull, approaching a corner on a track. The driver, likely Max Verstappen, is in the cockpit, the view showcasing the front left wheel, part of the car's nose, and the top of the cockpit. The car is approaching a left corner, with a green grassy bank on the left and a barrier on the right. The driver is focused on the corner ahead. Another car, likely a rival, is visible ahead, suggesting a close battle for position. The scene signifies the intense focus and precision required in cornering, highlighting the high-stakes nature of Formula 1 racing.\n", - "Scene from 128.0s to 130.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a driver, likely in a Formula 1 car, exiting a left-hand corner at the Hockenheimring, a circuit known for its flowing corners and high-speed straights. The driver is leading the pack, showcasing their car's superior acceleration and cornering capabilities. The moment captures the thrilling essence of a lead driver pushing their limits, while the green foliage in the background adds a touch of natural beauty to the scene. This is a testament to the driver's ability to maintain their position in the race, creating a moment of anticipation for the upcoming challenges of the race.\n", - "Scene from 130.0s to 132.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene captures a thrilling moment during a Formula 1 race, as Max Verstappen, piloting the Red Bull RB18, navigates the iconic Hockenheimring circuit. The camera follows Verstappen's car as it approaches the challenging hairpin turn, aptly named \"The Hairpin.\" He is closely followed by Lewis Hamilton in the Mercedes W13, who is clearly desperate to close the gap and reclaim the lead. Verstappen skillfully maneuvers through the tight turn, utilizing his car's superior power and handling to maintain his position, leaving Hamilton struggling to find a way through. This strategic maneuver, executed with precision and grace, underscores Verstappen's dominance and reaffirms his commitment to victory.\n", - "Scene from 132.0s to 134.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures a Red Bull driver, presumably Max Verstappen, navigating a high-speed corner on a circuit resembling the Hockenheimring. The onboard camera provides a driver's perspective, showing the driver's helmet and steering wheel, showcasing the intensity of the race. This could be a qualifying lap or a critical moment in the race, as Verstappen seeks to maintain his position and pressure his rivals. The blurred background suggests a high speed turn, highlighting the agility and precision required to navigate the demanding track. The scene underscores the thrill of Formula 1, where drivers push the limits of both car and human capabilities.\n", - "Scene from 134.0s to 136.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene depicts a Formula 1 car, adorned with the unmistakable Red Bull livery, navigating the challenging corners of the NΓΌrburgring. The driver, Max Verstappen, is battling for the lead in a tense race. The car's onboard camera captures the intense concentration of the driver as he pushes the car to its limits. This moment is particularly significant as it reveals the immense pressure and skill required to succeed in Formula 1. Verstappen is in the midst of a heated duel with Lewis Hamilton, and this scene encapsulates the high-stakes nature of their rivalry. The NΓΌrburgring's notorious corners, known for their demanding curves and unforgiving terrain, provide the perfect backdrop for this thrilling battle. The scene is a testament to the raw power and precision of the sport, highlighting the sheer skill and determination of the drivers as they strive for victory.\n", - "Scene from 136.0s to 138.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene is a first-person perspective from the cockpit of a Red Bull car, likely driven by Daniel Ricciardo, navigating the Hockenheimring's fast and flowing \"Motodrom\" section. The car is comfortably ahead of the pack, with only a few distant competitors visible in the rearview mirror. Ricciardo's focus is on maximizing the car's speed through the high-speed corners, showcasing his precision and confidence. The moment captures the exhilaration of leading the race at a legendary track, where every lap is a high-stakes battle against the clock and other drivers.\n", - "Scene from 138.0s to 140.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a **qualifying session** at the **Hockenheimring**, where **Max Verstappen** in the **Red Bull** car is **pushing hard**, attempting to set a **fast lap time**. The **onboard camera** captures Verstappen's **intense focus** as he navigates the challenging **turn** leading into the **Motodrom**, showcasing the **driver's skill and the car's capabilities**. The **blurry background** and the **high-speed** perspective convey the **dynamic and exciting nature** of Formula 1.\n", - "Scene from 140.0s to 142.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This scene captures the exhilarating perspective of a driver, likely Max Verstappen, piloting the Red Bull RB16 through the fast and flowing Turn 11 at the Hockenheimring. Verstappen's car is visibly leaning as he navigates the apex, a testament to the incredible cornering speeds these machines achieve. The view from the driver's seat showcases the immense precision and commitment required to master this iconic circuit. The scene is reminiscent of a pivotal moment in the 2020 German Grand Prix, where Verstappen's mastery of the Hockenheimring enabled him to secure a hard-fought victory.\n", - "Scene from 142.0s to 144.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: This scene captures the intense, chaotic aftermath of a multi-car collision at the Hockenheimring, Germany. The driver, sporting a helmet emblazoned with the words \"Race Without Trace,\" sits trapped in his mangled Red Bull Racing car. The damage is extensive, the front wing crumpled, and the cockpit filled with debris. The scene speaks to the high stakes and potential dangers of Formula 1, as the driver's focus shifts from winning to surviving.\n", - "Scene from 144.0s to 146.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: The scene captures a driver, likely Sebastian Vettel, in his Red Bull Racing car during a race. The car is stopped on the track, presumably due to a technical issue, as the driver is wearing his helmet and looking directly at the camera. The specific location is unclear, but the scene hints at the Hockenheimring, a German circuit, due to the driver's helmet featuring the German flag and the inscription \"There is still a race to win\". The image highlights the tension and determination of a driver facing a setback, emphasizing the unpredictable nature of Formula 1 racing.\n", - "Scene from 146.0s to 148.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: This is a first-person view from the cockpit of a Red Bull car, driven by Sebastian Vettel, as he navigates the fast, flowing Esses section of Hockenheimring. The scene captures the intense concentration required to master this high-speed sequence, where a slight misjudgement could lead to a costly spin. The car is slightly sideways, battling understeer as it exits the Esses, emphasizing the driver's precise control amidst the thrilling challenge of handling the powerful Formula 1 machine.\n", - "Scene from 148.0s to 150.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures the onboard perspective of a Red Bull driver, likely Daniel Ricciardo, navigating the challenging Hockenheimring circuit. The car is seen taking on a tight, fast right-hand corner, showcasing the driver's precision control and the car's exceptional handling capabilities. The corner, situated on the outskirts of the track, is a critical point for maintaining momentum and maximizing track position. Ricciardo's aggressive yet controlled steering through this apex reveals his mastery of the circuit, setting the stage for a potential overtake or a strategic move within the race. The scene encapsulates the high-stakes nature of Formula 1, emphasizing the driver's skill and the car's performance at its peak.\n", - "Scene from 150.0s to 152.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: This is a close-up, onboard shot of a Red Bull car navigating a left-hand bend, likely at the NΓΌrburgring. The driver, visibly focused, pushes the car through the corner, spray from the wet track kicking up behind them. While the precise driver cannot be identified, the scene suggests a thrilling battle for position as a competitor can be faintly seen ahead, battling their own cornering challenge. The moment captures the intensity and risk-taking inherent in wet-weather conditions, as the driver pushes the limits of adhesion on the slippery surface.\n", - "Scene from 152.0s to 154.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: This is a first-person perspective shot from the cockpit of a Red Bull Racing car. The driver, wearing a helmet emblazoned with the message \"Race Without Trace. There Is Still A Race To Win,\" is navigating the track in the midst of a chaotic race restart. The scene captures the driver's intense focus and determination as he maneuvers through the pack of cars, highlighting the inherent danger and thrill of Formula 1 racing. The specific location on the track is unknown, but the surrounding greenery suggests a fast, flowing circuit. The key action is the driver's unwavering focus and determination in the face of a challenging restart.\n", - "Scene from 154.0s to 156.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene depicts a tense battle for position at the Hockenheimring during the German Grand Prix. The driver, wearing a white helmet with the words \"There is still a race\" emblazoned on it, is battling for position on the left of the track, positioned slightly behind a rival car, which is partially visible. This particular corner is known for its high-speed nature, often leading to overtaking opportunities, and the driver's determination is evident in their focused expression and tight grip on the steering wheel. The scene encapsulates the essence of Formula 1's competitive spirit, as the driver fights fiercely for every inch of track.\n", - "Scene from 156.0s to 158.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This scene captures the visceral experience of a driver navigating a challenging wet section of the track. The driver, presumably in a Red Bull car, tackles a high-speed corner, likely the famed \"Eau Rouge\" at Spa-Francorchamps, under treacherous conditions. The blurred background signifies the car's speed as it pushes the limits of adhesion on the damp asphalt. The driver's intense focus and the car's dynamic movement convey the sheer thrill and danger inherent in such moments. This is a defining snapshot of the intensity and risk associated with Formula 1 racing in wet weather.\n", - "Scene from 158.0s to 160.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene is a close-up onboard shot of a Red Bull car navigating a high-speed corner at the Red Bull Ring in Austria. The driver, Max Verstappen, is pushing the car to its limits, showcasing incredible car control and precision as he maintains a tight line through the bend. The significance of this moment lies in Verstappen's dominance on his home track, demonstrating the car's strength and his driving prowess. The scene portrays the thrill of Formula 1 at its finest, with a focus on technical mastery and speed. The background features a vibrant green trackside landscape, a stark contrast to the aggressive lines of the Red Bull race car.\n", - "Scene from 160.0s to 162.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Formula 1 car, a Red Bull, navigating a tight corner at the NΓΌrburgring. The driver is pushing hard, his tires screaming as he battles for position. The corner, known for its high-speed entry and challenging exit, is a test of both driver skill and car balance. This is a critical moment in the race, with the Red Bull driver looking to gain an advantage over his rivals. The high-pressure environment of the NΓΌrburgring puts the driver under immense pressure, demanding precision and focus to maintain control. This scene captures the raw intensity of Formula 1 racing, showcasing the driver's commitment to pushing the limits of speed and performance.\n", - "Scene from 162.0s to 164.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene captures the on-board perspective of a Red Bull driver, likely Max Verstappen, navigating the iconic Hockenheimring in Germany. The driver is positioned in the middle of the track, approaching a slight right-hand curve known as the 'Ostkurve', after the long straight, with a car ahead, possibly a Ferrari, in the distance. The driver is maintaining a high speed, showcasing the raw power and agility of the Red Bull car. This moment exemplifies the intensity and technical challenges drivers face during a Formula 1 race.\n", - "Scene from 164.0s to 166.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene captures the perspective of a Red Bull driver, likely Daniel Ricciardo, navigating the iconic Hockenheimring's turn 1. He's pushing the limits, his car slightly off the racing line, creating a sense of tension and anticipation. This scene highlights the driver's skill in maintaining control and pushing the car to its boundaries, a crucial factor in securing a competitive position during the early stages of the race. The bright blue skies and the enthusiastic crowd cheering on the side emphasize the atmosphere of exhilaration and excitement that defines Formula 1.\n", - "Scene from 166.0s to 168.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: This is a first-person perspective shot from the cockpit of a Red Bull Formula 1 car. The driver, likely Max Verstappen, is approaching a left-hand corner, possibly the \"Eau Rouge\" at Spa-Francorchamps. The car is slightly off-line, with the left rear tire on the run-off area, suggesting a potential loss of control or aggressive maneuver. The other car visible in the distance is a competitor, adding to the intensity of the moment. The key action here is the driver's aggressive line through the corner, showcasing their skills and pushing the car to its limits.\n", - "Scene from 168.0s to 170.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: The scene captures a driver's perspective from within a Red Bull Racing car. The driver is positioned behind the wheel, looking directly ahead, with the iconic 'Red Bull Racing' branding visible on the side of the car. The shot is taken from the driver's perspective, with their helmet in the foreground and the track ahead, including a forest backdrop, visible in the background. The driver's helmet, adorned with a \"Race Without Track\" sticker, signifies a race-day scenario. The driver is presumably navigating a challenging corner, as evidenced by the slight tilt of the car and the trees bordering the track. The scene evokes the intense focus and determination inherent in Formula 1 racing.\n", - "Scene from 170.0s to 172.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: This is a first-person perspective shot from the cockpit of a Red Bull Racing car, likely driven by Max Verstappen. The scene unfolds as the car approaches a corner, possibly the famed Eau Rouge at Spa-Francorchamps, with the driver's helmet displaying the message \"There is still a race to win\" amidst the vibrant German colors. The car is in a tight battle for position with a rival car, potentially a Ferrari, seen in the driver's peripheral vision. This moment encapsulates the raw intensity of a Formula 1 race, highlighting the driver's focus and determination under pressure.\n", - "Scene from 172.0s to 174.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: This is a first-person onboard view from the cockpit of a Red Bull Racing car, likely driven by Max Verstappen, during a race. The driver is positioned on the track after a safety car period, with the car in front, likely a Mercedes, visible in the background. The scene captures the moment of the safety car's departure and the subsequent restart, showcasing the intense focus and determination of the driver as they prepare to battle for the lead.\n", - "Scene from 174.0s to 176.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene captures a dramatic moment in Formula 1, showcasing the aftermath of a collision. We see a Red Bull driver, helmet obscured by a white towel, likely in a state of shock or frustration, following a crash with a rival car, potentially a Mercedes, on the track. This incident, likely during a high-speed corner, suggests a tense battle for position and the consequences of aggressive maneuvering. The location seems to be a medium-speed corner with greenery surrounding the track, perhaps at the Hockenheimring, a popular venue known for its challenging layout. This scene embodies the risk and drama inherent in Formula 1, where even the slightest error can lead to costly collisions and disrupt the race's dynamic.\n", - "Scene from 176.0s to 178.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene depicts a driver, seemingly Sebastian Vettel in the Red Bull, navigating the treacherous 'Bergwerk' section of the NΓΌrburgring Nordschleife. The driver is seen pushing the car hard, its rear tires struggling for grip on the undulating tarmac. This corner is notorious for its sudden elevation changes, challenging even the most seasoned driver, making it a critical point in the race. Vettel's aggressive driving style and the tight corners in the Bergwerk section make this a tense moment as viewers anticipate a possible loss of control.\n", - "Scene from 178.0s to 180.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene is a driver's perspective, showcasing the exhilarating experience of racing on the iconic NΓΌrburgring Nordschleife. The driver, piloting a car adorned with the iconic red bull livery, navigates the challenging \"Schwabacher Kurve\" section, one of the circuit's most demanding corners. The car is shown at high speed, the blurred background emphasizing the sheer speed and precision required to tackle this legendary track. The driver's helmet and the \"Walmart\" sponsorship on the car's side add details to the scene, painting a vivid picture of the action. This moment captures the essence of Formula 1 racing, where precision, speed, and courage converge in a thrilling display of motorsport.\n", - "Scene from 180.0s to 182.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This is a first-person onboard shot of a Formula 1 car, likely a Red Bull, navigating a sweeping right-hand corner. The car, sporting the distinctive \"Walmart\" sponsorship, is visibly pushing hard, the front tires being heavily loaded as it tackles the apex. The scene captures the driver's perspective, highlighting the intensity of the race as the car carves its way through the corner. The driver's helmet, barely visible, emphasizes the sense of speed and focus required for this demanding maneuver. The verdant trees lining the track create a picturesque backdrop, adding to the overall spectacle of this high-speed ballet.\n", - "Scene from 182.0s to 184.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures a Red Bull driver, likely Max Verstappen, navigating the challenging Hockenheimring circuit in Germany. The onboard camera perspective showcases the driver's precision and speed as the car slices through the sweeping corners, the lush greenery blurring into a vibrant streak. The car is seemingly in the lead, a testament to Red Bull's dominant performance, showcasing the team's dominance during that specific race. The image is a snapshot of raw power and finesse, capturing the essence of Formula 1 racing at its finest.\n", - "Scene from 184.0s to 186.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This is a shot from the onboard camera of a Formula 1 car, most likely a practice session, approaching the iconic Hockenheimring's \"Motodrom\" section. The car, driven by an unidentified driver, is navigating the right-hand bend with speed and precision, showcasing the power and agility of the modern F1 machines. The background features lush greenery and distant spectators, adding to the grandeur of the iconic German circuit. The scene captures the essence of an F1 race, showcasing the driver's skill and the car's performance amidst the beautiful backdrop.\n", - "Scene from 186.0s to 188.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures a moment of anticipation as the Formula 1 cars approach the Hockenheimring's iconic Ostkurve, a high-speed right-hand corner. The track's distinctive tarmac and a safety barrier lined with a grassy verge are visible, with a glimpse of spectators in the distance. The drivers and teams involved are unknown, as the focus is on the imminent challenge posed by the corner's demanding nature. The scene signifies the thrilling crescendo of speed and precision as the cars prepare to navigate the treacherous bend, a testament to the drivers' expertise and the relentless pace of Formula 1.\n", - "Scene from 188.0s to 190.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene captures the intense focus of a Red Bull driver during a race, likely at the German Grand Prix. The driver's helmet, emblazoned with \"Race Without Trace,\" reflects a determination to leave a mark on the track. The car, bearing the iconic Red Bull Racing livery, is nestled behind another car, the rear wing a blur of motion. This close-up view, likely from an onboard camera, reveals the driver's unwavering concentration as they navigate a fast-paced turn. This moment highlights the high stakes and the adrenaline rush of Formula 1, where every maneuver can change the outcome.\n", - "Scene from 190.0s to 192.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene is a driver's perspective shot from the cockpit of a Red Bull Racing Formula 1 car. The driver, wearing a helmet emblazoned with \"Race Without Trace\" and the German flag, is in the middle of the pack. The car is accelerating through a high-speed corner at a track that features a green, grassy runoff area. The key action is the driver's focus and determination as they navigate the corner, emphasizing the mental fortitude required for Formula 1 racing. This shot provides a unique insight into the driver's world, highlighting their tenacity and skill as they tackle the challenging track.\n", - "Scene from 192.0s to 194.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene depicts a tense overtaking maneuver, likely during the latter stages of a race. Sebastian Vettel, driving the #5 Red Bull Racing car, is seen from the cockpit perspective as he prepares to pass the car in front, possibly a Ferrari, on a challenging corner. Vettel's focus is evident in his tight grip on the steering wheel, while the blur of the opponent's car is visible in the foreground. The tight corner and the close proximity of the cars emphasize the high stakes of the moment, highlighting the skill and precision required for a successful overtake. The significance of this moment is the opportunity for Vettel to gain a crucial position and potentially impact the race outcome. The backdrop of green grass and trees underscores the scenic beauty of the track, further adding to the visual appeal of this gripping moment.\n", - "Scene from 194.0s to 196.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures a driver, likely Max Verstappen in his Red Bull, exiting a medium-speed corner, possibly the Bergwerk on the NΓΌrburgring. His car is angled slightly towards the apex, showcasing his precise control as he navigates the challenging track. The camera perspective, a driver's view, highlights the intense focus and physicality of F1 racing, conveying the driver's perspective and the immense pressure they face. The action emphasizes Verstappen's driving prowess, illustrating how he navigates the challenging circuit with precision and confidence. This scene exemplifies the high-speed thrills and precision required in Formula 1, showcasing the driver's mastery of the car and the track.\n", - "Scene from 196.0s to 198.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Red Bull driver, presumably Sebastian Vettel, navigating the fast and flowing section of the Hockenheimring, a high-speed corner known as the \"Motodrom,\" while driving the RB8. The onboard camera captures the driver's perspective as he pushes the car to its limits, showcasing the immense speed and precision required to conquer this legendary circuit. The asphalt stretches before him, lined by dense foliage, as he gracefully carves through the corner, demonstrating both skill and confidence. This scene is significant as it exemplifies the thrilling spectacle of Formula 1, showcasing the power and agility of these racing machines in a visually stunning and technically challenging environment.\n", - "Scene from 198.0s to 200.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This is a scene from a Formula 1 race, showcasing the **intense spray generated by a car** as it **navigates a corner**. The driver, likely **Max Verstappen** in his **Red Bull**, is seen maneuvering through the **Hockenheimring** track in Germany, specifically the **Sachskurve corner**, the iconic left-hand bend. This moment captures the thrill and the **physicality of driving in wet conditions**, as the spray billows from the car's tires, obscuring the driver's vision and demonstrating the driver's skill in maintaining control amidst the challenging conditions.\n", - "Scene from 200.0s to 202.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This is a first-person view from the cockpit of a Red Bull Formula 1 car navigating the challenging \"Flugplatz\" section of the Hockenheimring. The driver is likely Daniel Ricciardo, a Red Bull driver, known for his aggressive driving style. This particular moment captures a quick, assertive maneuver through the sweeping right-hander, a corner that often demands precise throttle control and late braking. The driver is visibly pushing the car to its limits, leaving a trail of tire smoke behind as they exit the corner with impressive speed and precision. The scene showcases the raw power and skill involved in driving a Formula 1 car, providing a glimpse into the intense experience of a race driver.\n", - "Scene from 202.0s to 204.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a driver in a Red Bull car, likely Daniel Ricciardo, navigating the left-hand corner at the NΓΌrburgring, nicknamed \"The Carousel.\" The car is in the middle of the track, utilizing the widest possible line to maintain speed through the long sweeping curve. The driver is pushing hard, with the car slightly oversteered and tires squealing as he exits the corner. This is a pivotal moment for Ricciardo as he seeks to maintain his lead in the race.\n", - "Scene from 204.0s to 206.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures a Red Bull driver navigating the challenging Hockenheimring track, a legendary circuit known for its high-speed corners and technical sections. The driver is shown approaching a right-hand bend, likely the iconic \"Sachskurve,\" demonstrating precise steering and control as the car leans into the corner with its tires gripping the tarmac. This moment showcases the driver's skill and the car's performance, emphasizing the driver's commitment to pushing the limits on the track. The scene evokes the thrilling tension of a Formula 1 race, leaving viewers on the edge of their seats.\n", - "Scene from 206.0s to 208.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene is a close-up, cockpit view of a Red Bull Racing car, driven by Sebastian Vettel, navigating the high-speed Esses at the Circuit de Spa-Francorchamps. As Vettel pushes the car to its limit through the fast corners, he is shown battling for position against a rival car. The moment captures the driver's perspective, showcasing the intense focus and skill required to navigate this challenging section of the track. The scene is a testament to the raw power and precision of Formula 1 racing.\n", - "Scene from 208.0s to 210.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: The scene is a first-person perspective from the cockpit of Max Verstappen's Red Bull car. The car is approaching the final corner of the Hockenheimring circuit during the German Grand Prix. Verstappen is leading the race and is pushing hard to secure the win. The significance of the moment lies in the driver's focus and determination as he navigates the iconic corner.\n", - "Scene from 210.0s to 212.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: The scene depicts Sebastian Vettel, driving the Red Bull RB12, approaching a fast corner on the Hockenheimring. His helmet is visible from a first-person perspective, the \"Race without Trace\" message emblazoned on the visor, a reminder of his commitment to sustainability. The blue and red livery of the Red Bull, with the prominent Total sponsorship, reflects the team's color scheme. The corner, likely the \"Flugplatz Kurve\", is fast and sweeping, offering a challenge to the drivers' precision and car's handling. This moment captures the driver's perspective and emphasizes the dynamic nature of a Formula 1 race, where speed and agility are paramount.\n", - "Scene from 212.0s to 214.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: The scene captures Sebastian Vettel in his Red Bull Racing car during a celebratory lap after securing victory. His car is shown leading a pack of cars on a section of the track that features green trees and a large blue sky with white clouds. Vettel can be seen giving a \"peace sign\" to the camera. The car is adorned with the distinctive Red Bull livery, showcasing the sponsors \"Total\" and \"Boss Orange.\" This victory lap encapsulates the thrill of Formula 1, showcasing the driver's elation after a successful race.\n", - "Scene from 214.0s to 216.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: This is a cockpit view from the Red Bull of Max Verstappen, leading the race at the Hungaroring. The scene captures a tense moment as he navigates the fast, sweeping turn at the end of the long straight, closely followed by the Ferrari of Charles Leclerc. Verstappen's focus is unwavering as he maintains a slight advantage over Leclerc, showcasing his impressive racecraft. The intense rivalry between these two drivers is palpable, setting the stage for a thrilling finish.\n", - "Scene from 216.0s to 218.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene depicts a tense battle for position between two Red Bull Racing cars. The lead car, driven by Max Verstappen, is seen from the onboard perspective, with the rear of the car visible in the frame. The second Red Bull car, driven by Sergio Perez, is directly alongside Verstappen's car, with their wheels almost touching. They are approaching a tight corner, potentially setting up a thrilling overtaking maneuver. The location appears to be a high-speed section of the track, with a slight bend and trees bordering the circuit. This close encounter adds to the excitement and strategy of the race.\n", - "Scene from 218.0s to 220.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene captures the intense focus of a driver, likely during a race, as he navigates a high-speed corner. The driver, sporting a helmet emblazoned with \"Race Without Trace\" and the bold inscription \"There is still a race to win,\" is seated within the cockpit of a Red Bull Racing car. The car's distinctive livery of blue and red, alongside the prominent \"Red Bull Racing.com\" branding, leaves no doubt about his affiliation. \n", - "\n", - "The perspective is from within the cockpit, granting viewers an intimate glimpse of the driver's perspective. The tight bend, the verdant foliage bordering the track, and the blurred background of other cars hint at the immense speed and demanding nature of the circuit. The driver's focused gaze, fixed ahead, speaks volumes about the concentration and skill required to master the complex dance of Formula 1. This moment encapsulates the raw energy and unwavering determination at the heart of the sport.\n", - "Scene from 220.0s to 222.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene depicts a close-up perspective from the cockpit of a Red Bull Racing car, presumably driven by Max Verstappen, as he navigates a high-speed corner. The car is trailing another Red Bull, potentially Sergio Perez, with a tight gap separating them. The setting is a circuit featuring a sweeping left-hander, suggestive of a track like Spa-Francorchamps or Monza. This moment signifies a critical battle for track position within the team, showcasing their dominance and highlighting the intra-team competition.\n", - "Scene from 222.0s to 224.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: This is a shot from the Red Bull Ring in Austria, capturing the moment Sebastian Vettel, driving the Red Bull, emerges from a plume of tire smoke after overtaking Lewis Hamilton's Mercedes on the long, sweeping turn leading into the first corner. This overtake, achieved with a daring move on the inside, is a pivotal moment in the race as it signifies the start of a competitive duel between the two drivers, setting the stage for an exciting battle for the lead.\n", - "Scene from 224.0s to 226.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene is a follow-car shot of a single Red Bull car, likely Max Verstappen, on the long, sweeping left-hand corner of the Hockenheimring, Germany. This is during a race, the car is comfortably leading the field, and the scene highlights the driver's dominance and precision while navigating a challenging corner. The clear blue sky, with just a few fluffy clouds, and the lush greenery on the periphery of the track, create a picturesque backdrop for the scene.\n", - "Scene from 226.0s to 228.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene depicts a tense overtaking maneuver on the long, sweeping Turn 1 at the Hungaroring circuit. The driver of the Red Bull, Max Verstappen, is in hot pursuit of the leading Ferrari, driven by Charles Leclerc. The Dutchman is closing in on the Monegasque, his car inches from Leclerc's rear wing. The moment is pivotal as Verstappen seeks to capitalize on a potential slip from Leclerc, aiming to wrest the lead and gain a crucial advantage in the race. The backdrop of the lush Hungarian countryside and the iconic Hungaroring billboards adds to the dramatic atmosphere of the scene.\n", - "Scene from 228.0s to 230.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene captures the intense moment of a Formula 1 race, likely during a safety car period. The perspective is from inside the cockpit of a Red Bull Racing car, driven by Max Verstappen, as he follows a rival car. The location appears to be a high-speed section of the track, judging by the blur of the surrounding landscape. The significance lies in the dramatic tension, as Verstappen, with his helmet displaying the message \"There is still a race to win,\" remains focused and determined despite the temporary pause in the race. The image conveys the driver's mindset and the anticipation of the race resuming, adding to the thrilling spectacle of Formula 1.\n", - "Scene from 230.0s to 232.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene captures a Red Bull car, likely driven by Sebastian Vettel, as he navigates the fast, sweeping right-hand corner of the Hockenheimring. With a clear track ahead and a single car in the distance, Vettel has the opportunity to push the car to its limits and close the gap to the leader. The iconic \"Red Bull\" logo, along with the sponsor stickers, are prominent, highlighting the brand's dominance in the sport. The scene conveys the thrilling combination of speed and precision that defines Formula 1.\n", - "Scene from 232.0s to 234.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a first-person perspective of Sebastian Vettel in the Red Bull Racing car driving on the Nurburgring circuit. The camera is mounted on the helmet, capturing the intense focus of the driver as he navigates a fast, flowing section of the track. The clear blue sky and lush green trees surrounding the asphalt provide a visual contrast, highlighting the speed and precision of the car's movements. This moment is significant as Vettel is known for his mastery of the Nurburgring, demonstrating his confidence and control through this challenging portion of the track.\n", - "Scene from 234.0s to 236.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: The scene captures a driver, wearing a helmet emblazoned with \"Race Without Trace\" and \"There is Still a Race to Run,\" navigating through a tight corner on the track. The driver, seemingly undeterred by the challenging conditions, is in the cockpit of a Red Bull Racing car, highlighted by the visible \"Red Bull Racing.com\" branding on the car's side. The backdrop of verdant trees flanking the asphalt suggests a high-speed section of a classic circuit. The moment portrays the driver's determination to overcome the obstacle and continue the race, encapsulating the relentless spirit of Formula 1.\n", - "Scene from 236.0s to 238.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a **driver's perspective** shot, likely from an onboard camera, showcasing the **intense battle** for position. The driver in the **Red Bull**, likely **Max Verstappen**, navigates the **NΓΌrburgring's fast flowing corners**, showcasing the **grip and handling** of the car. \n", - "\n", - "The shot captures a **close battle** with a **Mercedes** car, likely **Lewis Hamilton**, as the **dust and debris** from a prior incident cloud the track. The **high speeds** and the **aggressive maneuvers** highlight the **raw intensity** of Formula 1 racing.\n", - "Scene from 238.0s to 240.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures a Red Bull driver, likely Sebastian Vettel, navigating a left-hand corner, likely the Hockenheimring's Turn 1. The car is at the apex of the corner, with dust kicking up from the rear tires as it accelerates out of the bend. The scene highlights the car's ability to handle the corner with precision and speed, showcasing the driver's skill and the car's performance. The location, turn 1, is crucial for establishing a good starting position in the race. The scene, therefore, represents a key moment in the race, where the driver is attempting to gain an advantage over their competitors.\n", - "Scene from 240.0s to 242.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene is a first-person perspective shot from the cockpit of a Red Bull car, likely driven by Sebastian Vettel, as he navigates the fast and flowing Hockenheimring circuit in Germany. The car is approaching a left-hand bend, kicking up a rooster tail of dust and gravel as it carries significant speed. This signifies the car's aggressive approach and the driver's confidence, perhaps during a qualifying lap. The driver's helmet, bearing the German colors, further underscores the scene's location and the significance of the moment.\n", - "Scene from 242.0s to 244.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Formula 1 car, driven by Sebastian Vettel in a Red Bull, navigating a tight corner. The camera is mounted on the car, giving viewers a first-person perspective of the race. The car is approaching a right-hand bend on a high-speed track, likely the Hockenheimring in Germany, as indicated by the surrounding greenery. Dust kicks up behind the car as it expertly maneuvers through the turn. The scene highlights the driver's skill in managing the car's speed and stability while maintaining a competitive pace.\n", - "Scene from 244.0s to 246.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a tense overtaking maneuver at the Hockenheimring, a popular German track known for its fast corners and long straights. The driver, Sebastian Vettel, in his Red Bull Racing car, is attempting to pass the leading Ferrari of Kimi RΓ€ikkΓΆnen on the approach to the infamous Ostkurve (East Curve). Vettel, known for his aggressive driving style, is pushing hard, his car kicking up dust as he tries to close the gap. The scene encapsulates the essence of Formula 1: the raw speed, precision, and strategic maneuvering required for victory.\n", - "Scene from 246.0s to 248.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene is a first-person perspective from the cockpit of a Red Bull car, likely during a race at the Hockenheimring circuit in Germany. The driver, adorned with the German flag on his helmet, is navigating the track with the car's rear wing visible behind him. The focus is on the driver's helmet, which features a bold inscription: \"Race Without Trace - There Is Still A Race To Win.\" This inscription signifies the driver's unwavering determination to succeed amidst challenging circumstances. The image captures the spirit of competition and resilience in Formula 1, where even in the face of adversity, the race continues.\n", - "Scene from 248.0s to 250.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: This is a first-person perspective shot from the cockpit of a Red Bull Racing car, likely driven by Max Verstappen. The scene captures the driver's view as he's navigating the track, potentially during a race restart. The camera focuses on Verstappen's gloved hand gripping the steering wheel, while his helmet, emblazoned with the bold inscription \"Race Without Trace,\" prominently displays the phrase \"There is Still a Race to Win.\" This scene exemplifies the intensity and determination inherent in Formula 1, showcasing the driver's unwavering focus amidst the demanding environment. The backdrop reveals the other cars on the track, emphasizing the competitive nature of the race. The composition of the shot effectively captures the driver's perspective and the surrounding environment, creating a thrilling snapshot of an F1 race moment.\n", - "Scene from 250.0s to 252.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: This is an in-car perspective from a Formula 1 race. The driver, wearing a helmet emblazoned with \"Race Without Trace: There is Still a Race,\" is in the cockpit of a blue and yellow car, likely a Red Bull. The car is navigating a corner, the green grass of the trackside visible in the peripheral vision. The driver's gloved hand grips the steering wheel, the intense focus evident in the scene. The key action is the driver's determination to continue the race despite the \"Race Without Trace\" message on the helmet, hinting at a potentially challenging or risky maneuver. This scene offers a visceral glimpse into the intensity and commitment required to compete at the highest level of Formula 1.\n", - "Scene from 252.0s to 254.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: This is a first-person perspective shot of a Formula 1 driver, Max Verstappen, in his Red Bull car, as he races through the tight, high-speed corner of Eau Rouge at the Spa-Francorchamps circuit. The scene captures the intensity of the race as Verstappen battles for the lead, his helmet displaying the message \"Race without a race, there is still a race to win.\" The shot highlights the driver's determination to recover from a poor start and reclaim the lead amidst a chaotic race. The Belgian flag on the helmet adds to the sense of national pride and the race's importance.\n", - "Scene from 254.0s to 256.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: The scene is a close-up, in-car perspective of a driver's helmet, likely during the formation lap of a Formula 1 race. The driver is wearing a white helmet with a black and yellow \"Race Without Trace\" inscription and a German flag. The driver's car, a Red Bull, is positioned on the track's right side, just before a series of turns. The significance of this moment lies in the driver's determined expression and the \"There is still a race to win\" inscription on the helmet, hinting at their unwavering focus and competitive spirit despite the early stage of the race.\n", - "Scene from 256.0s to 258.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene is a cockpit view from a Red Bull car, likely driven by Max Verstappen, as he exits the pit lane at the Circuit de Barcelona-Catalunya. The car is accelerating onto the track, potentially at the start of the race or after a pit stop. It is a moment of anticipation and excitement for the driver as they rejoin the race, ready to gain track position and challenge for the win. The camera angle emphasizes the driver's perspective, showcasing the car's speed and the track layout ahead.\n", - "Scene from 258.0s to 260.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Red Bull driver navigating the track, likely at the Hockenheimring, during a practice session. The driver, in the cockpit, is approaching a long, sweeping curve with a wall on the left. The car's front left tire is clearly visible as it tracks the apex of the bend. The driver's focus is evident as they maintain control and momentum. The significance of this moment lies in showcasing the driver's skills in handling the car's speed and trajectory through a demanding corner, highlighting the precision and finesse required in Formula 1.\n", - "Scene from 260.0s to 262.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene is a first-person perspective from the cockpit of a Red Bull Formula 1 car, likely driven by Max Verstappen. The car is approaching the final corner of a circuit, possibly the Circuit de Barcelona-Catalunya. The car is in the lead, with a significant gap to the second-placed car, seen in the background through the windscreen. The driver is accelerating out of the corner, demonstrating the car's impressive power and handling, showcasing the Red Bull's dominance in this race.\n", - "Scene from 262.0s to 264.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: This scene captures the cockpit view of a Red Bull Racing driver, likely Max Verstappen, as he approaches the start/finish line of the Hockenheimring circuit. The driver's helmet features the slogan \"Race Without Trace\" and \"There is still a race to win\", highlighting the determination and competitive spirit of the driver. This moment likely signifies a critical juncture in the race, with the driver pushing hard for victory. The backdrop of the grandstands and the surrounding track further adds to the tense atmosphere, emphasizing the thrill and high stakes of Formula 1 racing.\n", - "Scene from 264.0s to 266.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures a thrilling moment in the Malaysian Grand Prix, as Ferrari's Charles Leclerc, pushing his SF90 to its limits, enters Turn 15 at the Sepang International Circuit. The iconic covered grandstands stand as a backdrop to the action. Leclerc, the reigning race leader, is engaged in a fierce battle with Lewis Hamilton in the Mercedes W10, the two drivers separated by mere inches as they navigate the challenging corner, showcasing the raw speed and precision of Formula 1 at its finest. The high-stakes duel underscores the unpredictable nature of the race and highlights the crucial battle for the lead.\n", - "Scene from 266.0s to 268.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts Sebastian Vettel in the Red Bull car, navigating a right-hand corner, likely the Turn 10 at the Circuit de Catalunya in Barcelona. The angle suggests an on-board perspective, capturing the driver's point of view. Vettel is pushing hard, utilizing the track's width to maximize his corner exit speed. The scene emphasizes the driver's skill and focus, as he gracefully maneuvers the car through the bend.\n", - "Scene from 268.0s to 270.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This is a first-person perspective shot, capturing the cockpit view of Sebastian Vettel, driving the Red Bull, as he navigates Turn 1 of the Circuit de Barcelona-Catalunya during the 2016 Spanish Grand Prix. The scene highlights the driver's focus and control as he powers through the corner, the track's edge blurring as his car leans into the turn. The significance lies in showcasing the driver's exceptional handling skills and the sheer speed and precision of Formula 1 racing.\n", - "Scene from 270.0s to 272.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: This scene captures the moment Daniel Ricciardo, piloting the Red Bull RB12, exits the Turn 10 chicane at the Circuit de Barcelona-Catalunya during the 2016 Spanish Grand Prix. The camera angle, a driver's perspective, reveals the car's aggressive acceleration out of the corner, with the rear tires spinning slightly as Ricciardo pushes the car to its limit. This is a pivotal moment in the race as Ricciardo is attempting to close the gap to the leading Mercedes cars and reclaim the lead he lost earlier. The track's characteristic long, sweeping corners and the vibrant blue of the Red Bull livery highlight the scene's dynamic tension.\n", - "Scene from 272.0s to 274.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a driver in a Red Bull car, likely Daniel Ricciardo, navigating the Circuit de Catalunya in Barcelona. From the on-board camera perspective, we see the driver approaching Turn 1, the famed left-hand bend known for its potential for overtaking. The car is shown accelerating out of the corner, with the blue and red livery prominently displayed. This moment is crucial as it showcases the driver's ability to generate speed and maintain control, crucial for gaining a competitive edge during the race.\n", - "Scene from 274.0s to 276.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Red Bull driver, likely Daniel Ricciardo, navigating the pit lane exit at the Canadian Grand Prix. The iconic \"Formula 1 Montreal\" signage looms above the track, highlighting the race's location. The driver's perspective offers a visceral glimpse of the pit exit's sharp angle and the surrounding green grass, as he accelerates out of the lane and back onto the race track. The action underscores the crucial moment where drivers must rejoin the circuit with speed and precision, showcasing the challenge of managing tire temperature and track positioning after a pitstop.\n", - "Scene from 276.0s to 278.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene captures the intense moment of a Formula 1 driver, likely Sebastian Vettel in his Red Bull, navigating a challenging corner with a dramatic drift. The camera's perspective is from the cockpit, providing a first-person view of the driver's skill and the car's movements. The rear tires are visibly losing grip as the driver powers through the bend, resulting in a flurry of tire smoke. The blue, red, and yellow livery of the car is prominently displayed against the backdrop of the asphalt and greenery. The scene emphasizes the raw power and control exhibited during a high-speed cornering maneuver, a trademark of Formula 1 racing.\n", - "Scene from 278.0s to 280.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene depicts a tense moment during a Formula 1 race. The driver, sporting the blue and white livery of a car sponsored by Walmart, is navigating the track, likely on the exit of a corner. The driver is in close pursuit of a Red Bull car, recognizable by its distinctive livery, in front. The action takes place on a fast section of the track, with the driver attempting to close the gap on the Red Bull. The significance of this moment lies in the intense battle for position, highlighting the driver's skill and determination in overtaking the Red Bull. The scene is captured from the driver's perspective, offering a unique viewpoint of the race. This close-up perspective gives the viewer a visceral feel for the speed and pressure of the race.\n", - "Scene from 280.0s to 282.0s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene depicts a Formula 1 car, likely a Red Bull, in a close-up perspective as it navigates a sweeping right-hand corner. The driver, unseen, is focused on the upcoming apex, likely battling for position with a rival. The \"Walmart\" sponsorship on the car's rear wing suggests a potential IndyCar scene rather than Formula 1. The gritty asphalt and the slightly overcast sky add to the tense atmosphere of the overtaking maneuver. The focus on the driver's perspective provides a thrilling and immersive experience for the viewer.\n", - "Scene from 282.0s to 284.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: This is a thrilling overtake scene from an F1 race. The driver in the lead, Max Verstappen, piloting his Red Bull, is exiting a corner at Istanbul Park. He faces fierce competition from the second-placed driver, Lewis Hamilton, in his Mercedes, who is seen attempting a daring overtaking maneuver. The scene captures the intense pressure and maneuvering tactics employed by both drivers, creating a nail-biting moment as the race unfolds.\n", - "Scene from 284.0s to 286.0s\n", - "Camera View: road_ahead | Action Type: chasing\n", - "Scene Description: The scene depicts a dramatic moment during a Formula 1 race. Max Verstappen, piloting the Red Bull RB18, is shown driving through a thick cloud of smoke and debris. The location appears to be the start-finish straight of the Circuit de Monaco, the iconic street circuit known for its tight corners and high-speed sections. The smoke likely originates from a preceding incident, potentially a collision or a spin, highlighting the unpredictable nature of the race and the danger lurking around every corner. Verstappen's determined focus amidst the chaotic cloud of smoke suggests his grit and determination to overcome the obstacle and secure victory. The moment underscores the relentless pursuit of success and the high-stakes nature of Formula 1 racing.\n", - "Scene from 286.0s to 288.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: The scene is a tense moment during a Formula 1 race, captured from the driver's perspective. Sebastian Vettel, piloting the Red Bull, navigates through a thick plume of smoke obscuring the track ahead, likely the result of a preceding incident. His focus and determination are palpable as he pushes through the hazardous conditions, highlighting the raw danger and unpredictable nature of the sport. The location appears to be a high-speed corner, adding to the drama and emphasizing the driver's courage and skill. The scene captures the essence of Formula 1 - pushing limits, overcoming challenges, and battling for victory amidst uncertainty.\n", - "Scene from 288.0s to 290.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: The scene is a dramatic, almost surreal, depiction of the Red Bull driver navigating through a thick fog bank engulfing the track. The location is a fast, flowing corner, likely approaching the end of a long straight, where visibility is reduced to mere meters. The driver, his face obscured by the helmet visor, grips the steering wheel, his eyes straining to make out the track ahead through the dense fog. This scene underscores the high stakes and treacherous conditions drivers face in Formula 1, where even seemingly minor incidents can have significant ramifications. The significance lies in the driver's calm demeanor and focus despite the perilous situation, demonstrating the composure and precision required at the pinnacle of motorsport.\n", - "Scene from 290.0s to 292.0s\n", - "Camera View: helmet_selfie | Action Type: clear_road\n", - "Scene Description: The scene is a close-up from the cockpit of a Red Bull car, likely driven by Max Verstappen, during a race under extremely foggy conditions. The location appears to be a tight corner, potentially the famous Eau Rouge at Spa-Francorchamps. The key action is the sheer visibility challenge the driver faces, navigating blind through a thick fog bank, highlighting the immense skill and bravery required in Formula 1.\n", - "Scene from 292.0s to 294.0s\n", - "Camera View: helmet_selfie | Action Type: chasing\n", - "Scene Description: The scene depicts a dramatic moment at the start of the 2011 Canadian Grand Prix. Sebastian Vettel, driving for Red Bull, is battling through a thick fog of smoke billowing from the rear of a rival car. The car in front, sporting a distinctive red and blue livery, is likely the Ferrari of Fernando Alonso. The action takes place on the start-finish straight, just as the race begins. This is a thrilling scene as Vettel navigates a chaotic situation, attempting to maintain his position in the lead and demonstrating the bravery and skill required to navigate such treacherous conditions.\n", - "Scene from 294.0s to 295.6s\n", - "Camera View: road_ahead | Action Type: clear_road\n", - "Scene Description: The scene captures the exhilarating moment as Sebastian Vettel, driving the Red Bull Racing car, navigates the challenging NΓΌrburgring circuit, a renowned Formula 1 venue. This onboard shot showcases the driver's perspective, the car speeding towards the finish line. The iconic 'Formula NΓΌrburgring' banner looms above, highlighting the significance of the race. Vettel's focus is evident as he battles for a podium finish, pushing the car to its limits amidst the roar of the engine. This scene encapsulates the essence of Formula 1, where speed, precision, and relentless determination intertwine.\n", - "Total Scenes Indexed: 148\n" - ] - } - ], - "source": [ - "from videodb.scene import Scene\n", - "\n", - "# List to store described scenes\n", - "described_scenes = []\n", - "\n", - "for scene in scenes:\n", - " print(f\"Scene from {scene.start}s to {scene.end}s\")\n", - "\n", - " # Generate metadata\n", - " camera_view = scene.describe(\n", - " 'Select ONLY one of these camera views (DO NOT describe it, JUST return the category name): [\"road_ahead\", \"helmet_selfie\"]. If the view does not match exactly, pick the closest one.'\n", - " )\n", - "\n", - " action_type = scene.describe(\n", - " 'Select ONLY one of these options based on the action being performed by the driver (DO NOT describe it, JUST return the category name): [\"clear_road\", \"chasing\"]. If the view does not match exactly, pick the closest one.'\n", - " )\n", - "\n", - " scene_description = scene.describe(\n", - " \"Clearly describe a Formula 1 scene by specifying the scene type, the drivers and teams involved, the specific location on the track, and the key action or significance of the moment. Use concise, yet rich language, targeting Formula 1 enthusiasts seeking precise scene descriptions.\"\n", - " )\n", - "\n", - " print(f\"Camera View: {camera_view} | Action Type: {action_type}\")\n", - " print(f\"Scene Description: {scene_description}\")\n", - "\n", - " # Create Scene object with metadata\n", - " described_scene = Scene(\n", - " video_id=video.id,\n", - " start=scene.start,\n", - " end=scene.end,\n", - " description=scene_description,\n", - " metadata={\n", - " \"camera_view\": camera_view,\n", - " \"action_type\": action_type\n", - " }\n", - " )\n", - " described_scenes.append(described_scene)\n", - "\n", - "print(f\"Total Scenes Indexed: {len(described_scenes)}\")\n" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "m-z-01954d91-651d-7ef0-a022-18aad60eabdb\n" + ] + } + ], + "source": [ + "video = coll.upload(url=\"https://www.youtube.com/watch?v=2-oslsgSaTI\")\n", + "print(video.id)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "twKYyQFb2VVl" + }, + "source": [ + "## βœ‚οΈ Extracting Scenes (Every 2 Seconds)\n", + "We split the video into **2-second scenes**, extracting a **single frame per scene** for indexing.\n", + "\n", + "### **Why?**\n", + "- This ensures **granular indexing**, making **scene-level filtering more precise**.\n", + "- By extracting **key frames**, we can later **assign AI-generated metadata** to describe each scene accurately.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, + "id": "XWE6xx0v2h3s", + "outputId": "b87a73b5-5f75-43d8-d872-4f62a2f4c408" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "UQsPD3ThIF6D" - }, - "source": [ - "## πŸ—‚ Indexing Scenes with Metadata\n", - "Now that we have **generated metadata** for each scene, we **index them** to make them **searchable**.\n", - "\n", - "### **πŸš€ Why This is Powerful**\n", - "βœ” **Scene-level metadata makes filtering more effective**. \n", - "βœ” **Instead of searching the entire video, we only search relevant indexed segments.** \n", - "βœ” **Future searches can now filter by camera view & driver action.** \n" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Scene Collection ID: tt2smf1\n", + "Total Scenes Extracted: 148\n" + ] + } + ], + "source": [ + "# Extract Scenes Every 2 Seconds (1 Frame per Scene)\n", + "from videodb import SceneExtractionType\n", + "\n", + "scene_collection = video.extract_scenes(\n", + " extraction_type=SceneExtractionType.time_based,\n", + " extraction_config={\"time\": 2, \"select_frames\": [\"middle\"]},\n", + ")\n", + "\n", + "print(f\"Scene Collection ID: {scene_collection.id}\")\n", + "\n", + "scenes = scene_collection.scenes\n", + "\n", + "print(f\"Total Scenes Extracted: {len(scenes)}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FuftaJDm3-By" + }, + "source": [ + "## πŸ” Generating Scene Metadata\n", + "To **make scenes searchable**, we use AI to **describe & categorize** each scene with the following **structured metadata**:\n", + "\n", + "### **πŸ“Œ Scene-Level Metadata Fields:**\n", + "1️⃣ **`camera_view`** β†’ **Where is the camera placed?** \n", + " - `\"road_ahead\"` β†’ Driver’s **POV looking forward** \n", + " - `\"helmet_selfie\"` β†’ Close-up of **driver’s helmet** \n", + "\n", + "2️⃣ **`action_type`** β†’ **What is the driver doing?** \n", + " - `\"clear_road\"` β†’ No cars ahead (clean lap) \n", + " - `\"chasing\"` β†’ Following another car (intense racing moment) \n", + "\n", + "### **πŸš€ Why This Matters**\n", + "- **Metadata filtering** allows us to **search for specific race scenarios.** \n", + "- **Combining metadata & semantic search** makes retrieval **highly precise**. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, + "collapsed": true, + "id": "Cumx-35JPL_v", + "outputId": "0afa1cd5-e42d-4cd2-9040-a569170a79ad" + }, + "outputs": [ { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "IDwn56ISINIU", - "outputId": "373d3568-0186-410f-ef7e-cc5bbaeeb602" - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Scenes Indexed under ID: 5748b6e4c9b64156\n" - ] - } - ], - "source": [ - "if described_scenes:\n", - " scene_index_id = video.index_scenes(\n", - " scenes=described_scenes,\n", - " name=\"F1 Scenes\"\n", - " )\n", - " print(f\"Scenes Indexed under ID: {scene_index_id}\")" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Scene from 0.0s to 2.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene is a tense overtaking maneuver at the Circuit de Monaco, featuring Max Verstappen in the Red Bull and Charles Leclerc in the Ferrari. Verstappen, having closed the gap significantly, is seen approaching Leclerc on the entry to the iconic Tunnel section, a narrow, winding portion of the track. With the tight confines and limited visibility, Verstappen must navigate the challenging turn with precision to make a successful overtake. The significance of the moment lies in the potential for a dramatic change in race leadership, as Verstappen seeks to regain the top position he lost earlier in the race.\n", + "Scene from 2.0s to 4.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene depicts a close-up, in-car perspective of a Formula 1 race. The driver, sporting a Walmart-branded livery, is navigating a tight corner on the track. The camera captures the driver's view as they follow a Red Bull Racing car, likely Max Verstappen, in the lead. This particular moment is crucial as it signifies a potential overtake attempt by the Walmart-branded driver, highlighting a critical juncture in the race where the driver is seeking to gain an advantage. The scene is a testament to the intense competition and strategic maneuvers that define Formula 1 racing.\n", + "Scene from 4.0s to 6.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene is a close-up, on-board perspective from the cockpit of the Red Bull car, driven by Max Verstappen. The car is navigating the treacherous, high-speed, and winding section of the NΓΌrburgring, specifically the \"Flugplatz\" (airport) corner. The camera angle captures the rear-view mirror reflecting a preceding car, likely a Ferrari, battling for the lead. The intense focus on the mirror suggests a dramatic battle for position, with Verstappen attempting to close the gap and potentially overtake his rival. The scene highlights the driver's intense concentration and the critical nature of this particular corner, known for its tight radius and potential for overtaking maneuvers.\n", + "Scene from 6.0s to 8.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene unfolds at the Hockenheimring, Germany, as the driver of the Red Bull Racing car closes in on a competitor, likely a Ferrari, on the fast and flowing approach to the hairpin. The camera angle provides a cockpit view, capturing the driver's perspective as they meticulously navigate the complex series of curves. The driver is in a tense battle, pushing their car to the limit to attempt an overtaking maneuver. This is a crucial moment in the race, as overtaking opportunities at Hockenheim are limited, and the driver's success here could significantly alter the race outcome.\n", + "Scene from 8.0s to 10.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene captures the moment a Red Bull driver, likely Max Verstappen, closes in on a rival car on the NΓΌrburgring circuit. The iconic German track's fast and flowing nature is evident as the driver navigates a long, sweeping corner. The driver's helmet and the \"Red Bull\" branding are the only visible elements in the image, emphasizing the driver's intense focus. This scene is likely a critical moment in the race, with the Red Bull driver pushing for a pass and potentially taking the lead.\n", + "Scene from 10.0s to 12.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: This is an in-car perspective shot from a Red Bull car, likely driven by Max Verstappen. The scene takes place on a fast, flowing section of a track, potentially the Hockenheimring. The car is trailing another car, likely a Mercedes, as they approach a slight bend. This is a pivotal moment as Verstappen is attempting to close the gap and potentially overtake Hamilton, showcasing the intense competition between the two drivers and their teams. The focus is on the aggressive driving style of Verstappen, pushing the limits of his car to gain an advantage.\n", + "Scene from 12.0s to 14.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene captures a tense moment during a Formula 1 race, showcasing the driver's perspective from the cockpit. The driver, seemingly in a Red Bull, is approaching a corner, likely the famous Eau Rouge at Spa-Francorchamps. The driver's helmet is visible, highlighting their focus as they navigate the challenging bend. The scene is significant as it portrays the intensity of the race and the driver's skill in managing the car's speed and trajectory. The backdrop features lush greenery and a guardrail, further emphasizing the iconic location of the track.\n", + "Scene from 14.0s to 16.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures the thrilling moment as Max Verstappen in his Red Bull, trailing a competitor, approaches the challenging Turn 1 of the Hockenheimring. The camera angle, mounted on the Red Bull, provides a driver's perspective as the car navigates the left-hand corner at high speed. This close-up showcases the intense focus and agility required to master this iconic circuit. The scene underscores the raw power and precision of Formula 1 racing.\n", + "Scene from 16.0s to 18.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: This scene captures a driver, presumably Max Verstappen, in his Red Bull Racing car, navigating the challenging Eau Rouge and Raidillon corners at Spa-Francorchamps. The shot is taken from the driver's perspective, emphasizing the sheer speed and force the car endures as it crests the iconic hill. The blurred background showcases the challenging nature of the corner, while the driver's helmet, bearing the slogan \"There is still a race to win\", highlights the relentless determination required for success in Formula 1.\n", + "Scene from 18.0s to 20.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene captures a driver's perspective from the cockpit of a Red Bull Racing car. The driver, likely Max Verstappen, is in the midst of a tight battle for position, battling a rival car alongside him. The action unfolds on a high-speed section of the track, likely the fast, flowing corners of the Hockenheimring. The driver's helmet, emblazoned with the message \"Race Without Trace, There Is Still A Race To Win,\" conveys the intense determination and competitive spirit driving the moment. The tension is palpable, as the driver focuses intently on his opponent, poised to make a decisive move for the lead. This scene represents a critical juncture in the race, where a single strategic maneuver could determine the outcome.\n", + "Scene from 20.0s to 22.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene is a first-person cockpit view, capturing the intense focus of a driver navigating the challenging corners of a Formula 1 circuit. We see the driver, wearing a helmet emblazoned with the message \"Race Without Trace, There is Still a Race to Win,\" which suggests a message of sustainability within the sport. The car, a Red Bull, is in the midst of the race, with the driver's hands gripping the steering wheel as they skillfully maneuver the car through a tight bend. This moment reflects the driver's unwavering determination to push their limits and secure victory despite the challenges ahead.\n", + "Scene from 22.0s to 24.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene depicts a driver, presumably Sebastian Vettel, in his Red Bull Racing car during a race. The camera captures the driver's perspective, showcasing the cockpit view from the driver's seat. The car is navigating a high-speed corner, the trees lining the track blurring by as Vettel maintains focus. The key element is the \"Race Without Trace\" helmet design featuring the message \"There is still a race to win.\" This signifies Vettel's determination and unwavering spirit in the face of adversity, pushing the limits to secure a victory.\n", + "Scene from 24.0s to 26.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene unfolds at the NΓΌrburgring, a historic and challenging circuit known for its demanding layout. The driver, Sebastian Vettel, is behind the wheel of his Red Bull Racing car, navigating the iconic \"Schwedenkreuz,\" a high-speed, multi-apex corner. The action is captured from Vettel's perspective, conveying the immense speed and the intricate steering movements required to tame this corner. The significance of the moment lies in the sheer thrill and precision demanded, a testament to the driver's expertise and the demanding nature of the track.\n", + "Scene from 26.0s to 28.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a driver, likely Max Verstappen in his Red Bull Racing car, navigating the challenging Hockenheimring circuit in Germany. The perspective is from the driver's cockpit, showcasing the exhilarating experience of piloting an F1 car at high speed. This is a crucial moment, possibly during qualifying, as the driver pushes the car to its limits, seeking the perfect lap time. The driver's focus is evident, hands gripping the steering wheel, eyes fixed on the apex of the approaching corner. The blur of the forest surrounding the track adds to the dramatic effect, emphasizing the driver's incredible speed and the technical nature of Formula 1. This scene encapsulates the essence of racing: a driver's mastery, a car's capability, and a relentless pursuit of victory.\n", + "Scene from 28.0s to 30.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Red Bull driver, likely Sebastian Vettel, navigating the challenging Ardennes Forest section of the Spa-Francorchamps circuit. The car is on the approach to the fast, flowing \"Eau Rouge\" and \"Raidillon\" corners, a classic test of driver skill and car balance. The driver is seen pushing the car to its limits, with the car leaned heavily into the corner as it tackles the uphill climb. This is a crucial moment for the driver, as a slight misstep can easily lead to a costly error in this high-speed section. The scene exemplifies the intensity and precision demanded of Formula 1 drivers on a track renowned for its demanding nature.\n", + "Scene from 30.0s to 32.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a driver in a Red Bull car navigating the challenging \"S\" curves of the NΓΌrburgring. The driver is pushing hard, the car's tires are angled aggressively, and the blur of the surroundings signifies high speed. The driver is likely in pursuit or defending a position, showcasing the intense battle for track position typical of Formula 1 racing. The \"S\" curves, with their tight turns and elevation changes, demand precision and bravery, making this a defining moment in the race.\n", + "Scene from 32.0s to 34.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene depicts a first-person perspective from the cockpit of a Red Bull Formula 1 car. It is a high-speed corner on the Hockenheimring, a German circuit known for its fast and flowing nature. The driver, likely Daniel Ricciardo, is battling for position with an unidentified opponent, as he aggressively leans into the corner, pushing the car to its limits. The significance of the moment lies in the driver's skill and commitment to achieving a strong qualifying lap time, showcasing the thrilling and demanding nature of Formula 1.\n", + "Scene from 34.0s to 36.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures a first-person perspective of a Red Bull driver navigating the challenging Hockenheimring circuit. The driver, likely Daniel Ricciardo, is approaching the iconic \"Schwabacher Kurve\" right-hander, a notoriously tricky corner known for its tight apex and potential for overtaking. The driver is smoothly negotiating the turn, maintaining control while pushing the limits of the car's grip, showcasing the finesse required for high-speed cornering in Formula 1. The scene provides a glimpse into the raw experience of a driver at the peak of their abilities, maneuvering a powerful machine with incredible precision.\n", + "Scene from 36.0s to 38.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: This is a close-up shot from the onboard camera of a Red Bull Racing car, likely driven by Max Verstappen. The scene is set on a fast section of the track, potentially the Monza circuit, where the driver is leading the pack under a Safety Car period. His helmet inscription \"There is still a race\" emphasizes the driver's determination despite the temporary pause, hinting at his ambition for a strategic advantage during the restart.\n", + "Scene from 38.0s to 40.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene captures the intense perspective of Sebastian Vettel in his Red Bull Racing car, battling for position with a rival on the fast and flowing corners of the Nurburgring Nordschleife. The German driver, with his iconic \"Race Without Trace\" helmet, pushes hard, seeking a decisive overtaking move. The action unfolds in the latter stages of the race, with Vettel looking to salvage a podium finish after a challenging start. The focus on his helmet emphasizes his determination to make up ground, illustrating the driver's unwavering focus and the high stakes of the race.\n", + "Scene from 40.0s to 42.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene is an in-car perspective of Max Verstappen, driving the Red Bull Racing car, during a race. We are looking from the driver's seat, with his helmet in focus and the car's side panel partially visible. The car is navigating a high-speed corner on a track with trees and barriers visible in the background. The significance of the scene lies in its capturing Verstappen's determination as he navigates a challenging corner, likely in the midst of a close battle for position. The focus on his helmet, with the \"There is still a race to win\" inscription, underscores the intensity and competitive spirit of the moment.\n", + "Scene from 42.0s to 44.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This is a first-person perspective shot from the cockpit of a Red Bull car, driven by Sebastian Vettel. The car is navigating the challenging Hockenheimring track, specifically the infamous \"Motodrom\" corner. Vettel is aggressively pushing the car, the rear end is slightly loose, and the tire walls are visible as the car grazes the apex of the turn. The scene highlights the driver's talent in managing a challenging corner while maintaining a high speed and pushing the car to its limits.\n", + "Scene from 44.0s to 46.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures the cockpit perspective of a Red Bull driver, presumably Daniel Ricciardo, navigating the tight and challenging corners of the Hockenheimring circuit in Germany. The car is shown with the driver's helmet and the steering wheel clearly visible. The track is lined with a guardrail and green trees on the left side, signifying a fast-paced cornering sequence. The driver's focus and concentration are palpable, highlighting the technical prowess and precision required for navigating such a demanding track. The scene likely depicts a qualifying session, with the driver pushing the car and himself to the limit for a strong grid position.\n", + "Scene from 46.0s to 48.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures the cockpit perspective of a Red Bull Racing car, presumably driven by Max Verstappen, as he navigates the challenging NΓΌrburgring track. The iconic German circuit's green foliage and characteristic guardrails frame the action. The driver, in a moment of intense focus, expertly handles the car's powerful acceleration and intricate steering through a high-speed corner. The significance of this moment lies in showcasing the driver's mastery over the car and track, a testament to the precision and skill demanded in Formula 1 racing.\n", + "Scene from 48.0s to 50.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Red Bull car, driven by Max Verstappen, navigating the treacherous Hockenheimring's turn 1. The iconic blue and red livery of the car blends with the verdant backdrop, as Verstappen powers through the turn, leaving a trail of dust in his wake. This moment exemplifies the driver's aggressive driving style and his determination to establish a decisive lead. The camera angle, likely from the onboard perspective, captures the thrilling experience of the driver pushing the car and himself to the limit.\n", + "Scene from 50.0s to 52.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene is a dramatic overtaking maneuver at the NΓΌrburgring, a track famed for its challenging corners and unforgiving nature. A Red Bull car, driven by Sebastian Vettel, is locked in a fierce battle with a Mercedes, piloted by Lewis Hamilton. As they approach the daunting, high-speed section known as the \"Flugplatz,\" Vettel executes a daring move, diving down the inside of Hamilton. The camera, mounted on Vettel's car, captures the sheer audacity of the overtaking attempt, the blur of the Mercedes in the background as Vettel's car leans into the corner, tires squealing. This moment marks a turning point in the race, as Vettel's aggression sets the tone for a thrilling, action-packed finish.\n", + "Scene from 52.0s to 54.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene is a first-person perspective from the cockpit of a Red Bull car, likely driven by Sebastian Vettel, as he approaches a left-hand corner. The car is carrying significant speed, evident from the blur of the track ahead. The driver is executing a precise and confident line through the corner, demonstrating exceptional control of the car. The location appears to be a high-speed section of a circuit with a clear view of the surrounding landscape. The scene emphasizes the driver's expertise in navigating the track with precision and control, showcasing the exhilarating nature of Formula 1 racing.\n", + "Scene from 54.0s to 56.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This is an onboard shot of a Red Bull driver, likely Max Verstappen, exiting the Hockenheimring's Turn 1. The car, carrying the iconic red bull branding, is shown navigating the sharp right-hand corner with precision. The driver is demonstrating his mastery of the track by smoothly taking the apex, showcasing the car's agility and Verstappen's skill in controlling the vehicle's momentum and maintaining speed. This corner is often a crucial overtaking opportunity, and a driver's approach here can significantly impact their race strategy. The image emphasizes the driver's perspective and the intense focus required for this high-speed turn, showcasing the challenge and skill required to compete in Formula 1.\n", + "Scene from 56.0s to 58.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene is a close-up, on-board perspective from the cockpit of a Red Bull car, presumably driven by Sebastian Vettel, navigating the fast and sweeping corners of the Hockenheimring in Germany. The car is trailing another, unseen, competitor but attempting to close the gap. The image captures the intensity of the race, with the driver's helmet and the blur of the track highlighting the speed and concentration required in this high-stakes competition.\n", + "Scene from 58.0s to 60.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene depicts a first-person perspective of a Red Bull driver during a race. The driver, wearing a helmet emblazoned with \"Race Without Trace\" and the phrase \"There is still a race to go,\" is navigating the track's winding corners. The image captures the driver's focus and determination, highlighting the intensity of the race. The specific location is unclear, but the lush greenery and clear skies suggest a summer race at a circuit known for its challenging curves. The scene emphasizes the driver's resilience and unwavering belief in his ability to compete despite any prior setbacks.\n", + "Scene from 60.0s to 62.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This scene captures the exhilarating moment of a Red Bull driver, likely Daniel Ricciardo, pushing the limits through the fast-flowing Hockenheimring. The onboard camera perspective grants a thrilling immersion as the car navigates the iconic \"Motodrom,\" showcasing the driver's finesse and precision. The slight spray from the damp track adds an element of drama and highlights the driver's unwavering focus. This iconic German track is known for its high-speed corners and challenging conditions, which further intensifies this adrenaline-fueled moment. The driver's unwavering pursuit of victory is palpable, making this an unforgettable glimpse into the raw energy of Formula 1.\n", + "Scene from 62.0s to 64.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene is a close-up, onboard shot from the cockpit of a Red Bull car, likely driven by Max Verstappen. The car is navigating the tight, high-speed turn at the Red Bull Ring in Austria, known as Turn 9. This corner is a key overtaking opportunity, and the shot highlights Verstappen's precise steering and commitment to the apex, showcasing the driver's skill and the car's grip. The scene conveys the thrill and pressure of a high-stakes battle for position, a defining moment in the race for victory.\n", + "Scene from 64.0s to 66.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene is a close-up, onboard shot from the Red Bull RB18 driven by Sergio Perez, negotiating the sweeping right-hand corner at the end of the long back straight, known as Turn 1 at the Red Bull Ring in Austria. The camera captures the intense perspective from the cockpit, highlighting Perez's meticulous steering and the rapid deceleration of the car as it transitions into the corner, showcasing the demanding nature of this high-speed circuit. This moment symbolizes the intense focus and precision required by drivers in Formula 1, even in a seemingly routine corner.\n", + "Scene from 66.0s to 68.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: The scene depicts a Red Bull Racing car, driven by Max Verstappen, navigating the challenging Turn 1 at the Hungaroring circuit in Budapest, Hungary. The onboard camera captures the car's perspective as it enters the high-speed corner, showcasing Verstappen's smooth and precise driving style. The scene highlights the driver's ability to maintain control and momentum in a demanding and technical section of the track, demonstrating his mastery of the car and the circuit.\n", + "Scene from 68.0s to 70.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Red Bull car, presumably driven by Max Verstappen, navigating a fast, flowing section of the track. The car is seen from the cockpit perspective, with the driver's helmet and steering wheel visible. The car is in a straight line, possibly approaching a corner, the asphalt streaked with tire rubber from previous laps. The location is likely a high-speed section of the track, characterized by a smooth asphalt surface, low-lying greenery, and distant hills. The significance of this moment is the driver's focus and control as he navigates a demanding section of the circuit, showcasing the precision and athleticism required in Formula 1.\n", + "Scene from 70.0s to 72.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Red Bull driver navigating the NΓΌrburgring's fast and flowing section, the FuchsrΓΆhre. The car, adorned with the iconic Red Bull livery, races through a slight right-hand bend, its rear wing flexing under the strain of high-speed cornering. The driver's focus is evident as they maintain a precise racing line, seeking to maximize speed and precision. The backdrop of lush green hills and the distant sounds of the crowd add to the thrilling atmosphere of the race. This moment showcases the raw speed and agility of Formula 1 machinery as it tackles one of the most iconic and challenging corners in motorsports.\n", + "Scene from 72.0s to 74.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Red Bull car, likely driven by Max Verstappen, navigating the iconic Hockenheimring circuit in Germany. The car is approaching the infamous \"Motodrom,\" the high-speed banked corner, a crucial overtaking spot. The camera perspective is from the driver's seat, highlighting the immense speed and g-forces experienced as the car carves through the corner. The focus is on the driver's exceptional skill in maintaining control at such high speeds, a hallmark of Verstappen's driving style. The scene captures the adrenaline-pumping action of Formula 1, where every corner offers a potential opportunity for a daring maneuver.\n", + "Scene from 74.0s to 76.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures a Red Bull driver navigating the challenging, fast-flowing turns of the Hockenheimring circuit. The car, featuring the distinctive Red Bull livery, is pushing its limits, its front wing creating a visible downforce as it carves through the apex of the corner. The driver is exhibiting impressive control and precision, a testament to his skill and the car's handling capabilities. The scene underscores the raw power and technical finesse of Formula 1 racing, showcasing the drivers' commitment to achieving optimal performance.\n", + "Scene from 76.0s to 78.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Red Bull car, driven by Daniel Ricciardo, navigating the treacherous Hockenheimring, specifically the fast and flowing section known as the \"Sachskurve.\" The car is seen from the driver's perspective, showcasing Ricciardo's aggressive approach as he pushes the limits of the car through a high-speed corner. The shot highlights the driver's focus and control, demonstrating the immense skill required to master the intricacies of Formula 1. The scene captures the essence of high-speed racing, where every action is calculated and the margin for error is minimal.\n", + "Scene from 78.0s to 80.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene depicts a thrilling overtaking maneuver at the NΓΌrburgring's famed \"Flugplatz\" section. A Red Bull driver, likely Max Verstappen or Sergio Perez, aggressively pushes past a Williams car, utilizing the superior power of the Red Bull to slingshot out of the corner. The Williams driver, possibly George Russell or Nicholas Latifi, is seen struggling to maintain pace, highlighting the gap in performance between the two teams. The verdant forest backdrop and the iconic NΓΌrburgring infrastructure create a visually stunning backdrop for this decisive move, which could potentially swing the outcome of the race.\n", + "Scene from 80.0s to 82.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene captures a driver, presumably Sebastian Vettel in his Red Bull, approaching a sweeping left-hand corner at the Hockenheimring. The car's onboard camera reveals the driver's perspective, showcasing the green hills and the protective barrier lining the circuit. The action highlights the car's precise handling and Vettel's smooth approach into the corner, demonstrating the driver's mastery and the car's incredible grip. The scene encapsulates the elegance of Formula 1 driving, emphasizing the seamless integration of car and driver in a fast and challenging corner.\n", + "Scene from 82.0s to 84.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene depicts a driver in a Red Bull car, likely during a practice session, approaching a right-hand corner on a circuit featuring a high-speed sweeping section before the corner, a characteristic feature of the Hockenheimring. The driver is looking ahead, focused on the apex of the corner while another car is visible in the distance, presumably a rival car. The moment highlights the precision and speed of Formula 1, with the driver's focus and the track's design creating a visually striking scene for motorsport enthusiasts.\n", + "Scene from 84.0s to 86.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene is a first-person perspective from the cockpit of a Red Bull Racing car, driven by Max Verstappen. The car is approaching the iconic \"Hockenheimring\" track in Germany, with the distinctive green hills and rolling landscape in the background. Verstappen's helmet, emblazoned with the \"Race Without Trace\" logo, highlights the driver's focus on sustainability. The significance of the moment lies in the anticipation of a thrilling race, as Verstappen, a fierce competitor, prepares to conquer the challenging Hockenheim circuit.\n", + "Scene from 86.0s to 88.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: This is a cockpit view from Sebastian Vettel's Aston Martin, showcasing a tense overtake on the challenging Spa-Francorchamps circuit. The German driver, sporting his signature \"Race Without Trace\" helmet, is pushing hard, attempting to gain an advantage on a rival car, likely a Red Bull, as they navigate the fast and flowing section of the track. The close proximity and intense focus of the driver underscore the high-stakes nature of the competition.\n", + "Scene from 88.0s to 90.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene unfolds from the cockpit perspective of a Red Bull car, likely driven by Max Verstappen, navigating the iconic Hockenheimring circuit in Germany. The car is exiting the fast, flowing turn 11, known as the \"Motodrom,\" and accelerating towards the long straight. The key action is the driver's focus on maintaining precise control and speed while battling the aerodynamic forces of the high-speed corner, showcasing the intense physical and technical demands of Formula 1 driving. The backdrop of lush green forests and the concrete barriers add to the scenic beauty and inherent dangers of the iconic track.\n", + "Scene from 90.0s to 92.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Red Bull driver navigating the challenging Eau Rouge and Raidillon corners at the Circuit de Spa-Francorchamps. The car, adorned in the iconic blue and red livery, is seen from the driver's perspective. The driver is pushing the limits of adhesion, tires screeching as they grip the asphalt. The background showcases the lush greenery bordering the track, with the distinctive elevation changes of the iconic Belgian circuit. This moment highlights the sheer speed and technical prowess required to master this challenging corner complex. The driver is demonstrating his skill and control as he navigates this crucial section of the track, leaving a trail of tire smoke in his wake. This scene is not only a testament to the driver's skill but also to the unrelenting demands of Formula 1.\n", + "Scene from 92.0s to 94.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene captures the perspective of Sebastian Vettel, driving the Red Bull Racing car, as he navigates the challenging corners of the NΓΌrburgring circuit. Vettel is in the lead, closely followed by a McLaren car. The action takes place during the closing stages of the race, where Vettel faces immense pressure from his rivals. This particular moment marks a pivotal turn in the race, highlighting Vettel's grit and determination to maintain his position and ultimately secure the victory. The shot captures the intensity of the competition, the pressure on Vettel, and the potential for a thrilling finish.\n", + "Scene from 94.0s to 96.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: This is a cockpit view from the Red Bull RB18, piloted by Max Verstappen, as he navigates the challenging uphill climb of Eau Rouge at the Circuit de Spa-Francorchamps. The iconic Belgian track, known for its high-speed corners and elevation changes, presents a thrilling challenge for the drivers. Verstappen is seen concentrating intensely as he steers his car through the treacherous bend, showcasing the incredible precision and control required in Formula 1. The \"Race Without Trace\" helmet inscription signifies the driver's determination and focus to leave his mark on the track. The scene captures the essence of F1's relentless pursuit of speed and precision, emphasizing the driver's unwavering commitment to deliver a dominant performance.\n", + "Scene from 96.0s to 98.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This is a first-person perspective shot from a Red Bull car, likely driven by Max Verstappen, navigating the challenging \"Schwabacher Kurve\" at the NΓΌrburgring. The driver is pushing hard, the car leans into the turn, and the forested backdrop emphasizes the speed and the tight confines of the corner. This scene captures the essence of a driver's focus and control while pushing the limits of their car on a renowned, unforgiving circuit.\n", + "Scene from 98.0s to 100.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures a thrilling moment from the 2023 Austrian Grand Prix, as Max Verstappen, piloting his Red Bull RB19, navigates the treacherous Turn 4 at the Red Bull Ring. The onboard camera captures the intense focus on Verstappen's face, his hands deftly managing the car's controls. The track, lined by lush green foliage, dips sharply, emphasizing the technical challenge of this corner. This specific moment holds significant weight as Verstappen battles his teammate Sergio Perez for the lead, demonstrating Red Bull's dominance in this race. The scene encapsulates the raw intensity of Formula 1, where every turn and every driver decision can change the race's course.\n", + "Scene from 100.0s to 102.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Red Bull car, likely driven by Max Verstappen, exiting the high-speed left-hand corner at the end of the long straight at Hockenheimring. The car is in the middle of the track, accelerating aggressively, with the green hillsides of the German circuit framing the shot. The focus on the rear wheels and the car's motion suggests a thrilling overtake or a daring defensive maneuver against a rival, with the driver's helmet momentarily visible in the cockpit.\n", + "Scene from 102.0s to 104.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This is a first-person perspective shot from the cockpit of a Red Bull Formula 1 car. The driver is navigating the fast and flowing section of the track known as the \"Motodrom\" at the Red Bull Ring in Austria. The car is traveling at high speed, the trees blurring into a green tunnel, showcasing the driver's immense focus and precision as they tackle the challenging corner. The scene highlights the driver's exceptional control and the car's performance on the track.\n", + "Scene from 104.0s to 106.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene is a first-person perspective from the cockpit of a Red Bull car, likely driven by Daniel Ricciardo, as he navigates the treacherous Hockenheimring circuit during a qualifying session. The car is approaching the iconic \"Schwetzinger\" bend, a high-speed left-hand turn that often tests the driver's bravery and car's handling capabilities. The driver is pushing the limits, the car slightly sliding on the apex, showcasing the incredible skill and confidence needed to tackle this demanding corner at top speed. This exhilarating moment captures the essence of qualifying, where drivers fight for the pole position, putting their skills and cars under extreme pressure to achieve the best lap time.\n", + "Scene from 106.0s to 108.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures Max Verstappen in his Red Bull, navigating the challenging uphill esses of the Hockenheimring. He's pushing hard, the car slightly leaning towards the apex, a testament to the driver's skill and the car's exceptional grip. The background showcases the lush green German countryside, a stark contrast to the intensity of the racing action unfolding on the track. This moment encapsulates the exhilarating combination of technical precision and raw power that defines Formula 1.\n", + "Scene from 108.0s to 110.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This is a classic overtaking maneuver at the iconic Hockenheimring, Germany. Max Verstappen, in his Red Bull, is in pursuit of Lewis Hamilton's Mercedes, who is ahead on the track. The scene unfolds at the end of the long, sweeping left-hand curve of the \"Sachskurve,\" just as Verstappen is about to make his move on the inside line, attempting a daring pass on the outside of the apex. This crucial moment captures the intensity of their ongoing rivalry, as Verstappen fights to close the gap and challenge Hamilton's lead.\n", + "Scene from 110.0s to 112.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene depicts a Red Bull driver, likely Max Verstappen, in the cockpit of his RB18, as he navigates the high-speed esses of the Hockenheimring, a historic circuit known for its flowing layout. The camera, mounted on the car, offers a first-person perspective, capturing the driver's intense focus and the blur of the surrounding landscape. The scene emphasizes the exhilarating experience of driving an F1 car at its limit. The driver's helmet inscription \"Race Without Trace\" alludes to the relentless pursuit of victory and leaving no room for error.\n", + "Scene from 112.0s to 114.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene captures a tense moment in the cockpit of a Red Bull Formula 1 car, likely during a race. The driver, obscured by their helmet bearing the inscription \"There is still a race\", navigates the track, possibly at the Hockenheimring, through a series of corners. The angle emphasizes the driver's perspective, highlighting the intensity of the race as their car battles for position against a competitor whose rear wing is visible. The scene showcases the crucial moments of a Formula 1 race, where every corner presents an opportunity for overtaking or defending position.\n", + "Scene from 114.0s to 116.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: The scene is a first-person cockpit view from Max Verstappen's Red Bull Racing car during a lap at the NΓΌrburgring. Verstappen is leading the race, having overtaken Lewis Hamilton's Mercedes earlier on the lap. He is seen raising his right hand in a celebratory gesture as he navigates the challenging corners of the track. The significance of the moment lies in the fact that Verstappen is dominating the race and is in a strong position to secure victory. The image captures the thrill and excitement of a Formula 1 race as Verstappen drives with precision and determination.\n", + "Scene from 116.0s to 118.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures a Red Bull car, likely driven by Max Verstappen, exiting the challenging Turn 10 at the Hockenheimring. The car is accelerating up the hill, the driver maintaining a steady line, the car's purple and yellow livery catching the sunlight. The significance lies in Verstappen's ability to maintain momentum through this crucial corner, crucial for maintaining a strong lead. The backdrop of lush greenery and the distant sound of cheering spectators add to the atmosphere of high-octane racing.\n", + "Scene from 118.0s to 120.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene captures a Red Bull car, likely driven by Max Verstappen, navigating the challenging uphill section of the iconic NΓΌrburgring circuit. The car is seen driving slowly, seemingly under caution due to the presence of a safety car in the distance. The distinctive yellow and blue livery of the Red Bull is prominent, and the car's rear wing is slightly angled downwards, indicating a low-speed maneuver. This scene reflects a tense moment, as the race is temporarily paused, and the drivers await the signal to resume their battle for the lead.\n", + "Scene from 120.0s to 122.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: This is a close-up, onboard shot from the cockpit of a Red Bull car, likely driven by Max Verstappen, as he navigates the tight, wooded section of the Circuit Gilles Villeneuve in Montreal, Canada. The car is in the lead, closely pursued by a black car with a white logo, likely a Mercedes driven by Lewis Hamilton. This dramatic overtake attempt at the hairpin turn is the defining moment of the scene, capturing the raw intensity of a Formula 1 battle.\n", + "Scene from 122.0s to 124.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene captures a tense overtaking maneuver at the NΓΌrburgring, a high-speed, challenging circuit in Germany. Max Verstappen in the Red Bull is hot on the tail of Lewis Hamilton in the Mercedes, approaching the daunting Turn 1. The tight, left-hand bend, known for its high-speed entry and potential for slip-ups, is where Verstappen makes his move. As Hamilton slightly overshoots the apex, Verstappen capitalizes, slipping his car through the inside line, leaving Hamilton to fight for the lead in the next sector. This is a pivotal moment in the race, showcasing Verstappen's aggressive driving style and highlighting the intense rivalry between the two championship contenders.\n", + "Scene from 124.0s to 126.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene depicts a tense moment during the race. The driver in the lead, Max Verstappen in his Red Bull, is battling for position against a rival car, likely a Ferrari, as they approach a challenging corner on the NΓΌrburgring. The Red Bull is pushing hard, with its front wheels just inside the white line, demonstrating the driver's aggressive pursuit of victory. The dust kicked up by the preceding car hints at the intensity of the close battle, as the drivers push their cars to the limit in this iconic German circuit.\n", + "Scene from 126.0s to 128.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene depicts a Formula 1 car, a Red Bull, approaching a corner on a track. The driver, likely Max Verstappen, is in the cockpit, the view showcasing the front left wheel, part of the car's nose, and the top of the cockpit. The car is approaching a left corner, with a green grassy bank on the left and a barrier on the right. The driver is focused on the corner ahead. Another car, likely a rival, is visible ahead, suggesting a close battle for position. The scene signifies the intense focus and precision required in cornering, highlighting the high-stakes nature of Formula 1 racing.\n", + "Scene from 128.0s to 130.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a driver, likely in a Formula 1 car, exiting a left-hand corner at the Hockenheimring, a circuit known for its flowing corners and high-speed straights. The driver is leading the pack, showcasing their car's superior acceleration and cornering capabilities. The moment captures the thrilling essence of a lead driver pushing their limits, while the green foliage in the background adds a touch of natural beauty to the scene. This is a testament to the driver's ability to maintain their position in the race, creating a moment of anticipation for the upcoming challenges of the race.\n", + "Scene from 130.0s to 132.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene captures a thrilling moment during a Formula 1 race, as Max Verstappen, piloting the Red Bull RB18, navigates the iconic Hockenheimring circuit. The camera follows Verstappen's car as it approaches the challenging hairpin turn, aptly named \"The Hairpin.\" He is closely followed by Lewis Hamilton in the Mercedes W13, who is clearly desperate to close the gap and reclaim the lead. Verstappen skillfully maneuvers through the tight turn, utilizing his car's superior power and handling to maintain his position, leaving Hamilton struggling to find a way through. This strategic maneuver, executed with precision and grace, underscores Verstappen's dominance and reaffirms his commitment to victory.\n", + "Scene from 132.0s to 134.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures a Red Bull driver, presumably Max Verstappen, navigating a high-speed corner on a circuit resembling the Hockenheimring. The onboard camera provides a driver's perspective, showing the driver's helmet and steering wheel, showcasing the intensity of the race. This could be a qualifying lap or a critical moment in the race, as Verstappen seeks to maintain his position and pressure his rivals. The blurred background suggests a high speed turn, highlighting the agility and precision required to navigate the demanding track. The scene underscores the thrill of Formula 1, where drivers push the limits of both car and human capabilities.\n", + "Scene from 134.0s to 136.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene depicts a Formula 1 car, adorned with the unmistakable Red Bull livery, navigating the challenging corners of the NΓΌrburgring. The driver, Max Verstappen, is battling for the lead in a tense race. The car's onboard camera captures the intense concentration of the driver as he pushes the car to its limits. This moment is particularly significant as it reveals the immense pressure and skill required to succeed in Formula 1. Verstappen is in the midst of a heated duel with Lewis Hamilton, and this scene encapsulates the high-stakes nature of their rivalry. The NΓΌrburgring's notorious corners, known for their demanding curves and unforgiving terrain, provide the perfect backdrop for this thrilling battle. The scene is a testament to the raw power and precision of the sport, highlighting the sheer skill and determination of the drivers as they strive for victory.\n", + "Scene from 136.0s to 138.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene is a first-person perspective from the cockpit of a Red Bull car, likely driven by Daniel Ricciardo, navigating the Hockenheimring's fast and flowing \"Motodrom\" section. The car is comfortably ahead of the pack, with only a few distant competitors visible in the rearview mirror. Ricciardo's focus is on maximizing the car's speed through the high-speed corners, showcasing his precision and confidence. The moment captures the exhilaration of leading the race at a legendary track, where every lap is a high-stakes battle against the clock and other drivers.\n", + "Scene from 138.0s to 140.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a **qualifying session** at the **Hockenheimring**, where **Max Verstappen** in the **Red Bull** car is **pushing hard**, attempting to set a **fast lap time**. The **onboard camera** captures Verstappen's **intense focus** as he navigates the challenging **turn** leading into the **Motodrom**, showcasing the **driver's skill and the car's capabilities**. The **blurry background** and the **high-speed** perspective convey the **dynamic and exciting nature** of Formula 1.\n", + "Scene from 140.0s to 142.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This scene captures the exhilarating perspective of a driver, likely Max Verstappen, piloting the Red Bull RB16 through the fast and flowing Turn 11 at the Hockenheimring. Verstappen's car is visibly leaning as he navigates the apex, a testament to the incredible cornering speeds these machines achieve. The view from the driver's seat showcases the immense precision and commitment required to master this iconic circuit. The scene is reminiscent of a pivotal moment in the 2020 German Grand Prix, where Verstappen's mastery of the Hockenheimring enabled him to secure a hard-fought victory.\n", + "Scene from 142.0s to 144.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: This scene captures the intense, chaotic aftermath of a multi-car collision at the Hockenheimring, Germany. The driver, sporting a helmet emblazoned with the words \"Race Without Trace,\" sits trapped in his mangled Red Bull Racing car. The damage is extensive, the front wing crumpled, and the cockpit filled with debris. The scene speaks to the high stakes and potential dangers of Formula 1, as the driver's focus shifts from winning to surviving.\n", + "Scene from 144.0s to 146.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: The scene captures a driver, likely Sebastian Vettel, in his Red Bull Racing car during a race. The car is stopped on the track, presumably due to a technical issue, as the driver is wearing his helmet and looking directly at the camera. The specific location is unclear, but the scene hints at the Hockenheimring, a German circuit, due to the driver's helmet featuring the German flag and the inscription \"There is still a race to win\". The image highlights the tension and determination of a driver facing a setback, emphasizing the unpredictable nature of Formula 1 racing.\n", + "Scene from 146.0s to 148.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: This is a first-person view from the cockpit of a Red Bull car, driven by Sebastian Vettel, as he navigates the fast, flowing Esses section of Hockenheimring. The scene captures the intense concentration required to master this high-speed sequence, where a slight misjudgement could lead to a costly spin. The car is slightly sideways, battling understeer as it exits the Esses, emphasizing the driver's precise control amidst the thrilling challenge of handling the powerful Formula 1 machine.\n", + "Scene from 148.0s to 150.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures the onboard perspective of a Red Bull driver, likely Daniel Ricciardo, navigating the challenging Hockenheimring circuit. The car is seen taking on a tight, fast right-hand corner, showcasing the driver's precision control and the car's exceptional handling capabilities. The corner, situated on the outskirts of the track, is a critical point for maintaining momentum and maximizing track position. Ricciardo's aggressive yet controlled steering through this apex reveals his mastery of the circuit, setting the stage for a potential overtake or a strategic move within the race. The scene encapsulates the high-stakes nature of Formula 1, emphasizing the driver's skill and the car's performance at its peak.\n", + "Scene from 150.0s to 152.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: This is a close-up, onboard shot of a Red Bull car navigating a left-hand bend, likely at the NΓΌrburgring. The driver, visibly focused, pushes the car through the corner, spray from the wet track kicking up behind them. While the precise driver cannot be identified, the scene suggests a thrilling battle for position as a competitor can be faintly seen ahead, battling their own cornering challenge. The moment captures the intensity and risk-taking inherent in wet-weather conditions, as the driver pushes the limits of adhesion on the slippery surface.\n", + "Scene from 152.0s to 154.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: This is a first-person perspective shot from the cockpit of a Red Bull Racing car. The driver, wearing a helmet emblazoned with the message \"Race Without Trace. There Is Still A Race To Win,\" is navigating the track in the midst of a chaotic race restart. The scene captures the driver's intense focus and determination as he maneuvers through the pack of cars, highlighting the inherent danger and thrill of Formula 1 racing. The specific location on the track is unknown, but the surrounding greenery suggests a fast, flowing circuit. The key action is the driver's unwavering focus and determination in the face of a challenging restart.\n", + "Scene from 154.0s to 156.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene depicts a tense battle for position at the Hockenheimring during the German Grand Prix. The driver, wearing a white helmet with the words \"There is still a race\" emblazoned on it, is battling for position on the left of the track, positioned slightly behind a rival car, which is partially visible. This particular corner is known for its high-speed nature, often leading to overtaking opportunities, and the driver's determination is evident in their focused expression and tight grip on the steering wheel. The scene encapsulates the essence of Formula 1's competitive spirit, as the driver fights fiercely for every inch of track.\n", + "Scene from 156.0s to 158.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This scene captures the visceral experience of a driver navigating a challenging wet section of the track. The driver, presumably in a Red Bull car, tackles a high-speed corner, likely the famed \"Eau Rouge\" at Spa-Francorchamps, under treacherous conditions. The blurred background signifies the car's speed as it pushes the limits of adhesion on the damp asphalt. The driver's intense focus and the car's dynamic movement convey the sheer thrill and danger inherent in such moments. This is a defining snapshot of the intensity and risk associated with Formula 1 racing in wet weather.\n", + "Scene from 158.0s to 160.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene is a close-up onboard shot of a Red Bull car navigating a high-speed corner at the Red Bull Ring in Austria. The driver, Max Verstappen, is pushing the car to its limits, showcasing incredible car control and precision as he maintains a tight line through the bend. The significance of this moment lies in Verstappen's dominance on his home track, demonstrating the car's strength and his driving prowess. The scene portrays the thrill of Formula 1 at its finest, with a focus on technical mastery and speed. The background features a vibrant green trackside landscape, a stark contrast to the aggressive lines of the Red Bull race car.\n", + "Scene from 160.0s to 162.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Formula 1 car, a Red Bull, navigating a tight corner at the NΓΌrburgring. The driver is pushing hard, his tires screaming as he battles for position. The corner, known for its high-speed entry and challenging exit, is a test of both driver skill and car balance. This is a critical moment in the race, with the Red Bull driver looking to gain an advantage over his rivals. The high-pressure environment of the NΓΌrburgring puts the driver under immense pressure, demanding precision and focus to maintain control. This scene captures the raw intensity of Formula 1 racing, showcasing the driver's commitment to pushing the limits of speed and performance.\n", + "Scene from 162.0s to 164.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene captures the on-board perspective of a Red Bull driver, likely Max Verstappen, navigating the iconic Hockenheimring in Germany. The driver is positioned in the middle of the track, approaching a slight right-hand curve known as the 'Ostkurve', after the long straight, with a car ahead, possibly a Ferrari, in the distance. The driver is maintaining a high speed, showcasing the raw power and agility of the Red Bull car. This moment exemplifies the intensity and technical challenges drivers face during a Formula 1 race.\n", + "Scene from 164.0s to 166.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene captures the perspective of a Red Bull driver, likely Daniel Ricciardo, navigating the iconic Hockenheimring's turn 1. He's pushing the limits, his car slightly off the racing line, creating a sense of tension and anticipation. This scene highlights the driver's skill in maintaining control and pushing the car to its boundaries, a crucial factor in securing a competitive position during the early stages of the race. The bright blue skies and the enthusiastic crowd cheering on the side emphasize the atmosphere of exhilaration and excitement that defines Formula 1.\n", + "Scene from 166.0s to 168.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: This is a first-person perspective shot from the cockpit of a Red Bull Formula 1 car. The driver, likely Max Verstappen, is approaching a left-hand corner, possibly the \"Eau Rouge\" at Spa-Francorchamps. The car is slightly off-line, with the left rear tire on the run-off area, suggesting a potential loss of control or aggressive maneuver. The other car visible in the distance is a competitor, adding to the intensity of the moment. The key action here is the driver's aggressive line through the corner, showcasing their skills and pushing the car to its limits.\n", + "Scene from 168.0s to 170.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: The scene captures a driver's perspective from within a Red Bull Racing car. The driver is positioned behind the wheel, looking directly ahead, with the iconic 'Red Bull Racing' branding visible on the side of the car. The shot is taken from the driver's perspective, with their helmet in the foreground and the track ahead, including a forest backdrop, visible in the background. The driver's helmet, adorned with a \"Race Without Track\" sticker, signifies a race-day scenario. The driver is presumably navigating a challenging corner, as evidenced by the slight tilt of the car and the trees bordering the track. The scene evokes the intense focus and determination inherent in Formula 1 racing.\n", + "Scene from 170.0s to 172.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: This is a first-person perspective shot from the cockpit of a Red Bull Racing car, likely driven by Max Verstappen. The scene unfolds as the car approaches a corner, possibly the famed Eau Rouge at Spa-Francorchamps, with the driver's helmet displaying the message \"There is still a race to win\" amidst the vibrant German colors. The car is in a tight battle for position with a rival car, potentially a Ferrari, seen in the driver's peripheral vision. This moment encapsulates the raw intensity of a Formula 1 race, highlighting the driver's focus and determination under pressure.\n", + "Scene from 172.0s to 174.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: This is a first-person onboard view from the cockpit of a Red Bull Racing car, likely driven by Max Verstappen, during a race. The driver is positioned on the track after a safety car period, with the car in front, likely a Mercedes, visible in the background. The scene captures the moment of the safety car's departure and the subsequent restart, showcasing the intense focus and determination of the driver as they prepare to battle for the lead.\n", + "Scene from 174.0s to 176.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene captures a dramatic moment in Formula 1, showcasing the aftermath of a collision. We see a Red Bull driver, helmet obscured by a white towel, likely in a state of shock or frustration, following a crash with a rival car, potentially a Mercedes, on the track. This incident, likely during a high-speed corner, suggests a tense battle for position and the consequences of aggressive maneuvering. The location seems to be a medium-speed corner with greenery surrounding the track, perhaps at the Hockenheimring, a popular venue known for its challenging layout. This scene embodies the risk and drama inherent in Formula 1, where even the slightest error can lead to costly collisions and disrupt the race's dynamic.\n", + "Scene from 176.0s to 178.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene depicts a driver, seemingly Sebastian Vettel in the Red Bull, navigating the treacherous 'Bergwerk' section of the NΓΌrburgring Nordschleife. The driver is seen pushing the car hard, its rear tires struggling for grip on the undulating tarmac. This corner is notorious for its sudden elevation changes, challenging even the most seasoned driver, making it a critical point in the race. Vettel's aggressive driving style and the tight corners in the Bergwerk section make this a tense moment as viewers anticipate a possible loss of control.\n", + "Scene from 178.0s to 180.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene is a driver's perspective, showcasing the exhilarating experience of racing on the iconic NΓΌrburgring Nordschleife. The driver, piloting a car adorned with the iconic red bull livery, navigates the challenging \"Schwabacher Kurve\" section, one of the circuit's most demanding corners. The car is shown at high speed, the blurred background emphasizing the sheer speed and precision required to tackle this legendary track. The driver's helmet and the \"Walmart\" sponsorship on the car's side add details to the scene, painting a vivid picture of the action. This moment captures the essence of Formula 1 racing, where precision, speed, and courage converge in a thrilling display of motorsport.\n", + "Scene from 180.0s to 182.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This is a first-person onboard shot of a Formula 1 car, likely a Red Bull, navigating a sweeping right-hand corner. The car, sporting the distinctive \"Walmart\" sponsorship, is visibly pushing hard, the front tires being heavily loaded as it tackles the apex. The scene captures the driver's perspective, highlighting the intensity of the race as the car carves its way through the corner. The driver's helmet, barely visible, emphasizes the sense of speed and focus required for this demanding maneuver. The verdant trees lining the track create a picturesque backdrop, adding to the overall spectacle of this high-speed ballet.\n", + "Scene from 182.0s to 184.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures a Red Bull driver, likely Max Verstappen, navigating the challenging Hockenheimring circuit in Germany. The onboard camera perspective showcases the driver's precision and speed as the car slices through the sweeping corners, the lush greenery blurring into a vibrant streak. The car is seemingly in the lead, a testament to Red Bull's dominant performance, showcasing the team's dominance during that specific race. The image is a snapshot of raw power and finesse, capturing the essence of Formula 1 racing at its finest.\n", + "Scene from 184.0s to 186.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This is a shot from the onboard camera of a Formula 1 car, most likely a practice session, approaching the iconic Hockenheimring's \"Motodrom\" section. The car, driven by an unidentified driver, is navigating the right-hand bend with speed and precision, showcasing the power and agility of the modern F1 machines. The background features lush greenery and distant spectators, adding to the grandeur of the iconic German circuit. The scene captures the essence of an F1 race, showcasing the driver's skill and the car's performance amidst the beautiful backdrop.\n", + "Scene from 186.0s to 188.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures a moment of anticipation as the Formula 1 cars approach the Hockenheimring's iconic Ostkurve, a high-speed right-hand corner. The track's distinctive tarmac and a safety barrier lined with a grassy verge are visible, with a glimpse of spectators in the distance. The drivers and teams involved are unknown, as the focus is on the imminent challenge posed by the corner's demanding nature. The scene signifies the thrilling crescendo of speed and precision as the cars prepare to navigate the treacherous bend, a testament to the drivers' expertise and the relentless pace of Formula 1.\n", + "Scene from 188.0s to 190.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene captures the intense focus of a Red Bull driver during a race, likely at the German Grand Prix. The driver's helmet, emblazoned with \"Race Without Trace,\" reflects a determination to leave a mark on the track. The car, bearing the iconic Red Bull Racing livery, is nestled behind another car, the rear wing a blur of motion. This close-up view, likely from an onboard camera, reveals the driver's unwavering concentration as they navigate a fast-paced turn. This moment highlights the high stakes and the adrenaline rush of Formula 1, where every maneuver can change the outcome.\n", + "Scene from 190.0s to 192.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene is a driver's perspective shot from the cockpit of a Red Bull Racing Formula 1 car. The driver, wearing a helmet emblazoned with \"Race Without Trace\" and the German flag, is in the middle of the pack. The car is accelerating through a high-speed corner at a track that features a green, grassy runoff area. The key action is the driver's focus and determination as they navigate the corner, emphasizing the mental fortitude required for Formula 1 racing. This shot provides a unique insight into the driver's world, highlighting their tenacity and skill as they tackle the challenging track.\n", + "Scene from 192.0s to 194.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene depicts a tense overtaking maneuver, likely during the latter stages of a race. Sebastian Vettel, driving the #5 Red Bull Racing car, is seen from the cockpit perspective as he prepares to pass the car in front, possibly a Ferrari, on a challenging corner. Vettel's focus is evident in his tight grip on the steering wheel, while the blur of the opponent's car is visible in the foreground. The tight corner and the close proximity of the cars emphasize the high stakes of the moment, highlighting the skill and precision required for a successful overtake. The significance of this moment is the opportunity for Vettel to gain a crucial position and potentially impact the race outcome. The backdrop of green grass and trees underscores the scenic beauty of the track, further adding to the visual appeal of this gripping moment.\n", + "Scene from 194.0s to 196.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures a driver, likely Max Verstappen in his Red Bull, exiting a medium-speed corner, possibly the Bergwerk on the NΓΌrburgring. His car is angled slightly towards the apex, showcasing his precise control as he navigates the challenging track. The camera perspective, a driver's view, highlights the intense focus and physicality of F1 racing, conveying the driver's perspective and the immense pressure they face. The action emphasizes Verstappen's driving prowess, illustrating how he navigates the challenging circuit with precision and confidence. This scene exemplifies the high-speed thrills and precision required in Formula 1, showcasing the driver's mastery of the car and the track.\n", + "Scene from 196.0s to 198.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Red Bull driver, presumably Sebastian Vettel, navigating the fast and flowing section of the Hockenheimring, a high-speed corner known as the \"Motodrom,\" while driving the RB8. The onboard camera captures the driver's perspective as he pushes the car to its limits, showcasing the immense speed and precision required to conquer this legendary circuit. The asphalt stretches before him, lined by dense foliage, as he gracefully carves through the corner, demonstrating both skill and confidence. This scene is significant as it exemplifies the thrilling spectacle of Formula 1, showcasing the power and agility of these racing machines in a visually stunning and technically challenging environment.\n", + "Scene from 198.0s to 200.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This is a scene from a Formula 1 race, showcasing the **intense spray generated by a car** as it **navigates a corner**. The driver, likely **Max Verstappen** in his **Red Bull**, is seen maneuvering through the **Hockenheimring** track in Germany, specifically the **Sachskurve corner**, the iconic left-hand bend. This moment captures the thrill and the **physicality of driving in wet conditions**, as the spray billows from the car's tires, obscuring the driver's vision and demonstrating the driver's skill in maintaining control amidst the challenging conditions.\n", + "Scene from 200.0s to 202.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This is a first-person view from the cockpit of a Red Bull Formula 1 car navigating the challenging \"Flugplatz\" section of the Hockenheimring. The driver is likely Daniel Ricciardo, a Red Bull driver, known for his aggressive driving style. This particular moment captures a quick, assertive maneuver through the sweeping right-hander, a corner that often demands precise throttle control and late braking. The driver is visibly pushing the car to its limits, leaving a trail of tire smoke behind as they exit the corner with impressive speed and precision. The scene showcases the raw power and skill involved in driving a Formula 1 car, providing a glimpse into the intense experience of a race driver.\n", + "Scene from 202.0s to 204.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a driver in a Red Bull car, likely Daniel Ricciardo, navigating the left-hand corner at the NΓΌrburgring, nicknamed \"The Carousel.\" The car is in the middle of the track, utilizing the widest possible line to maintain speed through the long sweeping curve. The driver is pushing hard, with the car slightly oversteered and tires squealing as he exits the corner. This is a pivotal moment for Ricciardo as he seeks to maintain his lead in the race.\n", + "Scene from 204.0s to 206.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures a Red Bull driver navigating the challenging Hockenheimring track, a legendary circuit known for its high-speed corners and technical sections. The driver is shown approaching a right-hand bend, likely the iconic \"Sachskurve,\" demonstrating precise steering and control as the car leans into the corner with its tires gripping the tarmac. This moment showcases the driver's skill and the car's performance, emphasizing the driver's commitment to pushing the limits on the track. The scene evokes the thrilling tension of a Formula 1 race, leaving viewers on the edge of their seats.\n", + "Scene from 206.0s to 208.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene is a close-up, cockpit view of a Red Bull Racing car, driven by Sebastian Vettel, navigating the high-speed Esses at the Circuit de Spa-Francorchamps. As Vettel pushes the car to its limit through the fast corners, he is shown battling for position against a rival car. The moment captures the driver's perspective, showcasing the intense focus and skill required to navigate this challenging section of the track. The scene is a testament to the raw power and precision of Formula 1 racing.\n", + "Scene from 208.0s to 210.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: The scene is a first-person perspective from the cockpit of Max Verstappen's Red Bull car. The car is approaching the final corner of the Hockenheimring circuit during the German Grand Prix. Verstappen is leading the race and is pushing hard to secure the win. The significance of the moment lies in the driver's focus and determination as he navigates the iconic corner.\n", + "Scene from 210.0s to 212.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: The scene depicts Sebastian Vettel, driving the Red Bull RB12, approaching a fast corner on the Hockenheimring. His helmet is visible from a first-person perspective, the \"Race without Trace\" message emblazoned on the visor, a reminder of his commitment to sustainability. The blue and red livery of the Red Bull, with the prominent Total sponsorship, reflects the team's color scheme. The corner, likely the \"Flugplatz Kurve\", is fast and sweeping, offering a challenge to the drivers' precision and car's handling. This moment captures the driver's perspective and emphasizes the dynamic nature of a Formula 1 race, where speed and agility are paramount.\n", + "Scene from 212.0s to 214.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: The scene captures Sebastian Vettel in his Red Bull Racing car during a celebratory lap after securing victory. His car is shown leading a pack of cars on a section of the track that features green trees and a large blue sky with white clouds. Vettel can be seen giving a \"peace sign\" to the camera. The car is adorned with the distinctive Red Bull livery, showcasing the sponsors \"Total\" and \"Boss Orange.\" This victory lap encapsulates the thrill of Formula 1, showcasing the driver's elation after a successful race.\n", + "Scene from 214.0s to 216.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: This is a cockpit view from the Red Bull of Max Verstappen, leading the race at the Hungaroring. The scene captures a tense moment as he navigates the fast, sweeping turn at the end of the long straight, closely followed by the Ferrari of Charles Leclerc. Verstappen's focus is unwavering as he maintains a slight advantage over Leclerc, showcasing his impressive racecraft. The intense rivalry between these two drivers is palpable, setting the stage for a thrilling finish.\n", + "Scene from 216.0s to 218.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene depicts a tense battle for position between two Red Bull Racing cars. The lead car, driven by Max Verstappen, is seen from the onboard perspective, with the rear of the car visible in the frame. The second Red Bull car, driven by Sergio Perez, is directly alongside Verstappen's car, with their wheels almost touching. They are approaching a tight corner, potentially setting up a thrilling overtaking maneuver. The location appears to be a high-speed section of the track, with a slight bend and trees bordering the circuit. This close encounter adds to the excitement and strategy of the race.\n", + "Scene from 218.0s to 220.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene captures the intense focus of a driver, likely during a race, as he navigates a high-speed corner. The driver, sporting a helmet emblazoned with \"Race Without Trace\" and the bold inscription \"There is still a race to win,\" is seated within the cockpit of a Red Bull Racing car. The car's distinctive livery of blue and red, alongside the prominent \"Red Bull Racing.com\" branding, leaves no doubt about his affiliation. \n", + "\n", + "The perspective is from within the cockpit, granting viewers an intimate glimpse of the driver's perspective. The tight bend, the verdant foliage bordering the track, and the blurred background of other cars hint at the immense speed and demanding nature of the circuit. The driver's focused gaze, fixed ahead, speaks volumes about the concentration and skill required to master the complex dance of Formula 1. This moment encapsulates the raw energy and unwavering determination at the heart of the sport.\n", + "Scene from 220.0s to 222.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene depicts a close-up perspective from the cockpit of a Red Bull Racing car, presumably driven by Max Verstappen, as he navigates a high-speed corner. The car is trailing another Red Bull, potentially Sergio Perez, with a tight gap separating them. The setting is a circuit featuring a sweeping left-hander, suggestive of a track like Spa-Francorchamps or Monza. This moment signifies a critical battle for track position within the team, showcasing their dominance and highlighting the intra-team competition.\n", + "Scene from 222.0s to 224.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: This is a shot from the Red Bull Ring in Austria, capturing the moment Sebastian Vettel, driving the Red Bull, emerges from a plume of tire smoke after overtaking Lewis Hamilton's Mercedes on the long, sweeping turn leading into the first corner. This overtake, achieved with a daring move on the inside, is a pivotal moment in the race as it signifies the start of a competitive duel between the two drivers, setting the stage for an exciting battle for the lead.\n", + "Scene from 224.0s to 226.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene is a follow-car shot of a single Red Bull car, likely Max Verstappen, on the long, sweeping left-hand corner of the Hockenheimring, Germany. This is during a race, the car is comfortably leading the field, and the scene highlights the driver's dominance and precision while navigating a challenging corner. The clear blue sky, with just a few fluffy clouds, and the lush greenery on the periphery of the track, create a picturesque backdrop for the scene.\n", + "Scene from 226.0s to 228.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene depicts a tense overtaking maneuver on the long, sweeping Turn 1 at the Hungaroring circuit. The driver of the Red Bull, Max Verstappen, is in hot pursuit of the leading Ferrari, driven by Charles Leclerc. The Dutchman is closing in on the Monegasque, his car inches from Leclerc's rear wing. The moment is pivotal as Verstappen seeks to capitalize on a potential slip from Leclerc, aiming to wrest the lead and gain a crucial advantage in the race. The backdrop of the lush Hungarian countryside and the iconic Hungaroring billboards adds to the dramatic atmosphere of the scene.\n", + "Scene from 228.0s to 230.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene captures the intense moment of a Formula 1 race, likely during a safety car period. The perspective is from inside the cockpit of a Red Bull Racing car, driven by Max Verstappen, as he follows a rival car. The location appears to be a high-speed section of the track, judging by the blur of the surrounding landscape. The significance lies in the dramatic tension, as Verstappen, with his helmet displaying the message \"There is still a race to win,\" remains focused and determined despite the temporary pause in the race. The image conveys the driver's mindset and the anticipation of the race resuming, adding to the thrilling spectacle of Formula 1.\n", + "Scene from 230.0s to 232.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene captures a Red Bull car, likely driven by Sebastian Vettel, as he navigates the fast, sweeping right-hand corner of the Hockenheimring. With a clear track ahead and a single car in the distance, Vettel has the opportunity to push the car to its limits and close the gap to the leader. The iconic \"Red Bull\" logo, along with the sponsor stickers, are prominent, highlighting the brand's dominance in the sport. The scene conveys the thrilling combination of speed and precision that defines Formula 1.\n", + "Scene from 232.0s to 234.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a first-person perspective of Sebastian Vettel in the Red Bull Racing car driving on the Nurburgring circuit. The camera is mounted on the helmet, capturing the intense focus of the driver as he navigates a fast, flowing section of the track. The clear blue sky and lush green trees surrounding the asphalt provide a visual contrast, highlighting the speed and precision of the car's movements. This moment is significant as Vettel is known for his mastery of the Nurburgring, demonstrating his confidence and control through this challenging portion of the track.\n", + "Scene from 234.0s to 236.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: The scene captures a driver, wearing a helmet emblazoned with \"Race Without Trace\" and \"There is Still a Race to Run,\" navigating through a tight corner on the track. The driver, seemingly undeterred by the challenging conditions, is in the cockpit of a Red Bull Racing car, highlighted by the visible \"Red Bull Racing.com\" branding on the car's side. The backdrop of verdant trees flanking the asphalt suggests a high-speed section of a classic circuit. The moment portrays the driver's determination to overcome the obstacle and continue the race, encapsulating the relentless spirit of Formula 1.\n", + "Scene from 236.0s to 238.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a **driver's perspective** shot, likely from an onboard camera, showcasing the **intense battle** for position. The driver in the **Red Bull**, likely **Max Verstappen**, navigates the **NΓΌrburgring's fast flowing corners**, showcasing the **grip and handling** of the car. \n", + "\n", + "The shot captures a **close battle** with a **Mercedes** car, likely **Lewis Hamilton**, as the **dust and debris** from a prior incident cloud the track. The **high speeds** and the **aggressive maneuvers** highlight the **raw intensity** of Formula 1 racing.\n", + "Scene from 238.0s to 240.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures a Red Bull driver, likely Sebastian Vettel, navigating a left-hand corner, likely the Hockenheimring's Turn 1. The car is at the apex of the corner, with dust kicking up from the rear tires as it accelerates out of the bend. The scene highlights the car's ability to handle the corner with precision and speed, showcasing the driver's skill and the car's performance. The location, turn 1, is crucial for establishing a good starting position in the race. The scene, therefore, represents a key moment in the race, where the driver is attempting to gain an advantage over their competitors.\n", + "Scene from 240.0s to 242.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene is a first-person perspective shot from the cockpit of a Red Bull car, likely driven by Sebastian Vettel, as he navigates the fast and flowing Hockenheimring circuit in Germany. The car is approaching a left-hand bend, kicking up a rooster tail of dust and gravel as it carries significant speed. This signifies the car's aggressive approach and the driver's confidence, perhaps during a qualifying lap. The driver's helmet, bearing the German colors, further underscores the scene's location and the significance of the moment.\n", + "Scene from 242.0s to 244.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Formula 1 car, driven by Sebastian Vettel in a Red Bull, navigating a tight corner. The camera is mounted on the car, giving viewers a first-person perspective of the race. The car is approaching a right-hand bend on a high-speed track, likely the Hockenheimring in Germany, as indicated by the surrounding greenery. Dust kicks up behind the car as it expertly maneuvers through the turn. The scene highlights the driver's skill in managing the car's speed and stability while maintaining a competitive pace.\n", + "Scene from 244.0s to 246.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a tense overtaking maneuver at the Hockenheimring, a popular German track known for its fast corners and long straights. The driver, Sebastian Vettel, in his Red Bull Racing car, is attempting to pass the leading Ferrari of Kimi RΓ€ikkΓΆnen on the approach to the infamous Ostkurve (East Curve). Vettel, known for his aggressive driving style, is pushing hard, his car kicking up dust as he tries to close the gap. The scene encapsulates the essence of Formula 1: the raw speed, precision, and strategic maneuvering required for victory.\n", + "Scene from 246.0s to 248.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene is a first-person perspective from the cockpit of a Red Bull car, likely during a race at the Hockenheimring circuit in Germany. The driver, adorned with the German flag on his helmet, is navigating the track with the car's rear wing visible behind him. The focus is on the driver's helmet, which features a bold inscription: \"Race Without Trace - There Is Still A Race To Win.\" This inscription signifies the driver's unwavering determination to succeed amidst challenging circumstances. The image captures the spirit of competition and resilience in Formula 1, where even in the face of adversity, the race continues.\n", + "Scene from 248.0s to 250.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: This is a first-person perspective shot from the cockpit of a Red Bull Racing car, likely driven by Max Verstappen. The scene captures the driver's view as he's navigating the track, potentially during a race restart. The camera focuses on Verstappen's gloved hand gripping the steering wheel, while his helmet, emblazoned with the bold inscription \"Race Without Trace,\" prominently displays the phrase \"There is Still a Race to Win.\" This scene exemplifies the intensity and determination inherent in Formula 1, showcasing the driver's unwavering focus amidst the demanding environment. The backdrop reveals the other cars on the track, emphasizing the competitive nature of the race. The composition of the shot effectively captures the driver's perspective and the surrounding environment, creating a thrilling snapshot of an F1 race moment.\n", + "Scene from 250.0s to 252.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: This is an in-car perspective from a Formula 1 race. The driver, wearing a helmet emblazoned with \"Race Without Trace: There is Still a Race,\" is in the cockpit of a blue and yellow car, likely a Red Bull. The car is navigating a corner, the green grass of the trackside visible in the peripheral vision. The driver's gloved hand grips the steering wheel, the intense focus evident in the scene. The key action is the driver's determination to continue the race despite the \"Race Without Trace\" message on the helmet, hinting at a potentially challenging or risky maneuver. This scene offers a visceral glimpse into the intensity and commitment required to compete at the highest level of Formula 1.\n", + "Scene from 252.0s to 254.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: This is a first-person perspective shot of a Formula 1 driver, Max Verstappen, in his Red Bull car, as he races through the tight, high-speed corner of Eau Rouge at the Spa-Francorchamps circuit. The scene captures the intensity of the race as Verstappen battles for the lead, his helmet displaying the message \"Race without a race, there is still a race to win.\" The shot highlights the driver's determination to recover from a poor start and reclaim the lead amidst a chaotic race. The Belgian flag on the helmet adds to the sense of national pride and the race's importance.\n", + "Scene from 254.0s to 256.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: The scene is a close-up, in-car perspective of a driver's helmet, likely during the formation lap of a Formula 1 race. The driver is wearing a white helmet with a black and yellow \"Race Without Trace\" inscription and a German flag. The driver's car, a Red Bull, is positioned on the track's right side, just before a series of turns. The significance of this moment lies in the driver's determined expression and the \"There is still a race to win\" inscription on the helmet, hinting at their unwavering focus and competitive spirit despite the early stage of the race.\n", + "Scene from 256.0s to 258.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene is a cockpit view from a Red Bull car, likely driven by Max Verstappen, as he exits the pit lane at the Circuit de Barcelona-Catalunya. The car is accelerating onto the track, potentially at the start of the race or after a pit stop. It is a moment of anticipation and excitement for the driver as they rejoin the race, ready to gain track position and challenge for the win. The camera angle emphasizes the driver's perspective, showcasing the car's speed and the track layout ahead.\n", + "Scene from 258.0s to 260.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Red Bull driver navigating the track, likely at the Hockenheimring, during a practice session. The driver, in the cockpit, is approaching a long, sweeping curve with a wall on the left. The car's front left tire is clearly visible as it tracks the apex of the bend. The driver's focus is evident as they maintain control and momentum. The significance of this moment lies in showcasing the driver's skills in handling the car's speed and trajectory through a demanding corner, highlighting the precision and finesse required in Formula 1.\n", + "Scene from 260.0s to 262.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene is a first-person perspective from the cockpit of a Red Bull Formula 1 car, likely driven by Max Verstappen. The car is approaching the final corner of a circuit, possibly the Circuit de Barcelona-Catalunya. The car is in the lead, with a significant gap to the second-placed car, seen in the background through the windscreen. The driver is accelerating out of the corner, demonstrating the car's impressive power and handling, showcasing the Red Bull's dominance in this race.\n", + "Scene from 262.0s to 264.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: This scene captures the cockpit view of a Red Bull Racing driver, likely Max Verstappen, as he approaches the start/finish line of the Hockenheimring circuit. The driver's helmet features the slogan \"Race Without Trace\" and \"There is still a race to win\", highlighting the determination and competitive spirit of the driver. This moment likely signifies a critical juncture in the race, with the driver pushing hard for victory. The backdrop of the grandstands and the surrounding track further adds to the tense atmosphere, emphasizing the thrill and high stakes of Formula 1 racing.\n", + "Scene from 264.0s to 266.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures a thrilling moment in the Malaysian Grand Prix, as Ferrari's Charles Leclerc, pushing his SF90 to its limits, enters Turn 15 at the Sepang International Circuit. The iconic covered grandstands stand as a backdrop to the action. Leclerc, the reigning race leader, is engaged in a fierce battle with Lewis Hamilton in the Mercedes W10, the two drivers separated by mere inches as they navigate the challenging corner, showcasing the raw speed and precision of Formula 1 at its finest. The high-stakes duel underscores the unpredictable nature of the race and highlights the crucial battle for the lead.\n", + "Scene from 266.0s to 268.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts Sebastian Vettel in the Red Bull car, navigating a right-hand corner, likely the Turn 10 at the Circuit de Catalunya in Barcelona. The angle suggests an on-board perspective, capturing the driver's point of view. Vettel is pushing hard, utilizing the track's width to maximize his corner exit speed. The scene emphasizes the driver's skill and focus, as he gracefully maneuvers the car through the bend.\n", + "Scene from 268.0s to 270.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This is a first-person perspective shot, capturing the cockpit view of Sebastian Vettel, driving the Red Bull, as he navigates Turn 1 of the Circuit de Barcelona-Catalunya during the 2016 Spanish Grand Prix. The scene highlights the driver's focus and control as he powers through the corner, the track's edge blurring as his car leans into the turn. The significance lies in showcasing the driver's exceptional handling skills and the sheer speed and precision of Formula 1 racing.\n", + "Scene from 270.0s to 272.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: This scene captures the moment Daniel Ricciardo, piloting the Red Bull RB12, exits the Turn 10 chicane at the Circuit de Barcelona-Catalunya during the 2016 Spanish Grand Prix. The camera angle, a driver's perspective, reveals the car's aggressive acceleration out of the corner, with the rear tires spinning slightly as Ricciardo pushes the car to its limit. This is a pivotal moment in the race as Ricciardo is attempting to close the gap to the leading Mercedes cars and reclaim the lead he lost earlier. The track's characteristic long, sweeping corners and the vibrant blue of the Red Bull livery highlight the scene's dynamic tension.\n", + "Scene from 272.0s to 274.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a driver in a Red Bull car, likely Daniel Ricciardo, navigating the Circuit de Catalunya in Barcelona. From the on-board camera perspective, we see the driver approaching Turn 1, the famed left-hand bend known for its potential for overtaking. The car is shown accelerating out of the corner, with the blue and red livery prominently displayed. This moment is crucial as it showcases the driver's ability to generate speed and maintain control, crucial for gaining a competitive edge during the race.\n", + "Scene from 274.0s to 276.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Red Bull driver, likely Daniel Ricciardo, navigating the pit lane exit at the Canadian Grand Prix. The iconic \"Formula 1 Montreal\" signage looms above the track, highlighting the race's location. The driver's perspective offers a visceral glimpse of the pit exit's sharp angle and the surrounding green grass, as he accelerates out of the lane and back onto the race track. The action underscores the crucial moment where drivers must rejoin the circuit with speed and precision, showcasing the challenge of managing tire temperature and track positioning after a pitstop.\n", + "Scene from 276.0s to 278.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene captures the intense moment of a Formula 1 driver, likely Sebastian Vettel in his Red Bull, navigating a challenging corner with a dramatic drift. The camera's perspective is from the cockpit, providing a first-person view of the driver's skill and the car's movements. The rear tires are visibly losing grip as the driver powers through the bend, resulting in a flurry of tire smoke. The blue, red, and yellow livery of the car is prominently displayed against the backdrop of the asphalt and greenery. The scene emphasizes the raw power and control exhibited during a high-speed cornering maneuver, a trademark of Formula 1 racing.\n", + "Scene from 278.0s to 280.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene depicts a tense moment during a Formula 1 race. The driver, sporting the blue and white livery of a car sponsored by Walmart, is navigating the track, likely on the exit of a corner. The driver is in close pursuit of a Red Bull car, recognizable by its distinctive livery, in front. The action takes place on a fast section of the track, with the driver attempting to close the gap on the Red Bull. The significance of this moment lies in the intense battle for position, highlighting the driver's skill and determination in overtaking the Red Bull. The scene is captured from the driver's perspective, offering a unique viewpoint of the race. This close-up perspective gives the viewer a visceral feel for the speed and pressure of the race.\n", + "Scene from 280.0s to 282.0s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene depicts a Formula 1 car, likely a Red Bull, in a close-up perspective as it navigates a sweeping right-hand corner. The driver, unseen, is focused on the upcoming apex, likely battling for position with a rival. The \"Walmart\" sponsorship on the car's rear wing suggests a potential IndyCar scene rather than Formula 1. The gritty asphalt and the slightly overcast sky add to the tense atmosphere of the overtaking maneuver. The focus on the driver's perspective provides a thrilling and immersive experience for the viewer.\n", + "Scene from 282.0s to 284.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: This is a thrilling overtake scene from an F1 race. The driver in the lead, Max Verstappen, piloting his Red Bull, is exiting a corner at Istanbul Park. He faces fierce competition from the second-placed driver, Lewis Hamilton, in his Mercedes, who is seen attempting a daring overtaking maneuver. The scene captures the intense pressure and maneuvering tactics employed by both drivers, creating a nail-biting moment as the race unfolds.\n", + "Scene from 284.0s to 286.0s\n", + "Camera View: road_ahead | Action Type: chasing\n", + "Scene Description: The scene depicts a dramatic moment during a Formula 1 race. Max Verstappen, piloting the Red Bull RB18, is shown driving through a thick cloud of smoke and debris. The location appears to be the start-finish straight of the Circuit de Monaco, the iconic street circuit known for its tight corners and high-speed sections. The smoke likely originates from a preceding incident, potentially a collision or a spin, highlighting the unpredictable nature of the race and the danger lurking around every corner. Verstappen's determined focus amidst the chaotic cloud of smoke suggests his grit and determination to overcome the obstacle and secure victory. The moment underscores the relentless pursuit of success and the high-stakes nature of Formula 1 racing.\n", + "Scene from 286.0s to 288.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: The scene is a tense moment during a Formula 1 race, captured from the driver's perspective. Sebastian Vettel, piloting the Red Bull, navigates through a thick plume of smoke obscuring the track ahead, likely the result of a preceding incident. His focus and determination are palpable as he pushes through the hazardous conditions, highlighting the raw danger and unpredictable nature of the sport. The location appears to be a high-speed corner, adding to the drama and emphasizing the driver's courage and skill. The scene captures the essence of Formula 1 - pushing limits, overcoming challenges, and battling for victory amidst uncertainty.\n", + "Scene from 288.0s to 290.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: The scene is a dramatic, almost surreal, depiction of the Red Bull driver navigating through a thick fog bank engulfing the track. The location is a fast, flowing corner, likely approaching the end of a long straight, where visibility is reduced to mere meters. The driver, his face obscured by the helmet visor, grips the steering wheel, his eyes straining to make out the track ahead through the dense fog. This scene underscores the high stakes and treacherous conditions drivers face in Formula 1, where even seemingly minor incidents can have significant ramifications. The significance lies in the driver's calm demeanor and focus despite the perilous situation, demonstrating the composure and precision required at the pinnacle of motorsport.\n", + "Scene from 290.0s to 292.0s\n", + "Camera View: helmet_selfie | Action Type: clear_road\n", + "Scene Description: The scene is a close-up from the cockpit of a Red Bull car, likely driven by Max Verstappen, during a race under extremely foggy conditions. The location appears to be a tight corner, potentially the famous Eau Rouge at Spa-Francorchamps. The key action is the sheer visibility challenge the driver faces, navigating blind through a thick fog bank, highlighting the immense skill and bravery required in Formula 1.\n", + "Scene from 292.0s to 294.0s\n", + "Camera View: helmet_selfie | Action Type: chasing\n", + "Scene Description: The scene depicts a dramatic moment at the start of the 2011 Canadian Grand Prix. Sebastian Vettel, driving for Red Bull, is battling through a thick fog of smoke billowing from the rear of a rival car. The car in front, sporting a distinctive red and blue livery, is likely the Ferrari of Fernando Alonso. The action takes place on the start-finish straight, just as the race begins. This is a thrilling scene as Vettel navigates a chaotic situation, attempting to maintain his position in the lead and demonstrating the bravery and skill required to navigate such treacherous conditions.\n", + "Scene from 294.0s to 295.6s\n", + "Camera View: road_ahead | Action Type: clear_road\n", + "Scene Description: The scene captures the exhilarating moment as Sebastian Vettel, driving the Red Bull Racing car, navigates the challenging NΓΌrburgring circuit, a renowned Formula 1 venue. This onboard shot showcases the driver's perspective, the car speeding towards the finish line. The iconic 'Formula NΓΌrburgring' banner looms above, highlighting the significance of the race. Vettel's focus is evident as he battles for a podium finish, pushing the car to its limits amidst the roar of the engine. This scene encapsulates the essence of Formula 1, where speed, precision, and relentless determination intertwine.\n", + "Total Scenes Indexed: 148\n" + ] + } + ], + "source": [ + "from videodb.scene import Scene\n", + "\n", + "# List to store described scenes\n", + "described_scenes = []\n", + "\n", + "for scene in scenes:\n", + " print(f\"Scene from {scene.start}s to {scene.end}s\")\n", + "\n", + " # Generate metadata\n", + " camera_view = scene.describe(\n", + " 'Select ONLY one of these camera views (DO NOT describe it, JUST return the category name): [\"road_ahead\", \"helmet_selfie\"]. If the view does not match exactly, pick the closest one.'\n", + " )\n", + "\n", + " action_type = scene.describe(\n", + " 'Select ONLY one of these options based on the action being performed by the driver (DO NOT describe it, JUST return the category name): [\"clear_road\", \"chasing\"]. If the view does not match exactly, pick the closest one.'\n", + " )\n", + "\n", + " scene_description = scene.describe(\n", + " \"Clearly describe a Formula 1 scene by specifying the scene type, the drivers and teams involved, the specific location on the track, and the key action or significance of the moment. Use concise, yet rich language, targeting Formula 1 enthusiasts seeking precise scene descriptions.\"\n", + " )\n", + "\n", + " print(f\"Camera View: {camera_view} | Action Type: {action_type}\")\n", + " print(f\"Scene Description: {scene_description}\")\n", + "\n", + " # Create Scene object with metadata\n", + " described_scene = Scene(\n", + " video_id=video.id,\n", + " start=scene.start,\n", + " end=scene.end,\n", + " description=scene_description,\n", + " metadata={\n", + " \"camera_view\": camera_view,\n", + " \"action_type\": action_type\n", + " }\n", + " )\n", + " described_scenes.append(described_scene)\n", + "\n", + "print(f\"Total Scenes Indexed: {len(described_scenes)}\")\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "UQsPD3ThIF6D" + }, + "source": [ + "## πŸ—‚ Indexing Scenes with Metadata\n", + "Now that we have **generated metadata** for each scene, we **index them** to make them **searchable**.\n", + "\n", + "### **πŸš€ Why This is Powerful**\n", + "βœ” **Scene-level metadata makes filtering more effective**. \n", + "βœ” **Instead of searching the entire video, we only search relevant indexed segments.** \n", + "βœ” **Future searches can now filter by camera view & driver action.** \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" }, + "id": "IDwn56ISINIU", + "outputId": "373d3568-0186-410f-ef7e-cc5bbaeeb602" + }, + "outputs": [ { - "cell_type": "markdown", - "metadata": { - "id": "K7ZPGGq3JoFb" - }, - "source": [ - "## πŸ”Ž Searching Scenes with Metadata & AI \n", - "Now that our scenes are indexed, we can **search using a combination of**: \n", - "βœ… **Semantic Search** β†’ AI understands the meaning of the query. \n", - "βœ… **Metadata Filters** β†’ Only return relevant scenes based on camera view & action type. \n", - "\n", - "---\n", - "\n", - "#### πŸ” **Example 1: Finding Intense Chasing Moments** \n", - "Search for **scenes where a driver is chasing another car**, viewed from the **driver's perspective**. \n" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Scenes Indexed under ID: 5748b6e4c9b64156\n" + ] + } + ], + "source": [ + "if described_scenes:\n", + " scene_index_id = video.index_scenes(\n", + " scenes=described_scenes,\n", + " name=\"F1 Scenes\"\n", + " )\n", + " print(f\"Scenes Indexed under ID: {scene_index_id}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "K7ZPGGq3JoFb" + }, + "source": [ + "## πŸ”Ž Searching Scenes with Metadata & AI \n", + "Now that our scenes are indexed, we can **search using a combination of**: \n", + "βœ… **Semantic Search** β†’ AI understands the meaning of the query. \n", + "βœ… **Metadata Filters** β†’ Only return relevant scenes based on camera view & action type. \n", + "\n", + "---\n", + "\n", + "#### πŸ” **Example 1: Finding Intense Chasing Moments** \n", + "Search for **scenes where a driver is chasing another car**, viewed from the **driver's perspective**. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 422 }, + "id": "lF4LwgyxJsu-", + "outputId": "b234ce29-845d-4b8a-fce4-61b1e3dc9513" + }, + "outputs": [ { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 422 - }, - "id": "lF4LwgyxJsu-", - "outputId": "b234ce29-845d-4b8a-fce4-61b1e3dc9513" - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "\n", + " \n", + " " ], - "source": [ - "from videodb import IndexType\n", - "from videodb import SearchType\n", - "\n", - "search_results = video.search(\n", - " query = \"A skillful chasing scene\",\n", - " filter = [{\"camera_view\": \"road_ahead\"}, {\"action_type\": \"chasing\"}], # Using metadata filter\n", - " search_type = SearchType.semantic,\n", - " index_type = IndexType.scene,\n", - " result_threshold = 100,\n", - " scene_index_id = scene_index_id # Our indexed scenes\n", - ")\n", - "# Play the search results\n", - "search_results.play()" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "097qWtFY4vN6" - }, - "source": [ - "#### **πŸ” Example 2: Finding Smooth Solo Driving Moments** \n", - "Search for **scenes with clean, precise turns**, where the driver has an **open road ahead**. \n" + "text/plain": [ + "" ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from videodb import IndexType\n", + "from videodb import SearchType\n", + "\n", + "search_results = video.search(\n", + " query = \"A skillful chasing scene\",\n", + " filter = [{\"camera_view\": \"road_ahead\"}, {\"action_type\": \"chasing\"}], # Using metadata filter\n", + " search_type = SearchType.semantic,\n", + " index_type = IndexType.scene,\n", + " result_threshold = 100,\n", + " scene_index_id = scene_index_id # Our indexed scenes\n", + ")\n", + "# Play the search results\n", + "search_results.play()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "097qWtFY4vN6" + }, + "source": [ + "#### **πŸ” Example 2: Finding Smooth Solo Driving Moments** \n", + "Search for **scenes with clean, precise turns**, where the driver has an **open road ahead**. \n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 422 }, + "id": "RZa41OZmzKKl", + "outputId": "b6cd5642-bd9c-4ef2-94e1-1e275339853b" + }, + "outputs": [ { - "cell_type": "code", - "execution_count": null, - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/", - "height": 422 - }, - "id": "RZa41OZmzKKl", - "outputId": "b6cd5642-bd9c-4ef2-94e1-1e275339853b" - }, - "outputs": [ - { - "data": { - "text/html": [ - "\n", - " \n", - " " - ], - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } + "data": { + "text/html": [ + "\n", + " \n", + " " ], - "source": [ - "search_results = video.search(\n", - " query = \"Smooth turns\",\n", - " filter = [{\"camera_view\": \"road_ahead\"}, {\"action_type\": \"clear_road\"}], # Using metadata filter\n", - " search_type = SearchType.semantic,\n", - " index_type = IndexType.scene,\n", - " result_threshold = 100,\n", - " scene_index_id = scene_index_id\n", - ")\n", - "# Play the search results\n", - "search_results.play()" + "text/plain": [ + "" ] - }, - { - "cell_type": "markdown", - "metadata": { - "id": "6ZOyc3Lg484a" - }, - "source": [ - "# βœ… Conclusion: Precision Search with Scene Metadata \n", - "With **scene-level metadata indexing**, we can: \n", - "βœ” **Precisely filter race footage** by camera angles & driver actions. \n", - "βœ” **Use AI-powered semantic search** to find **specific race moments.** \n", - "βœ” **Enhance video retrieval** for F1 analysis, highlights & research. \n", - "\n", - "πŸš€ **This approach unlocks smarter, metadata-driven video searchβ€”making every second of race footage instantly accessible.** \n" - ] - } - ], - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - }, - "language_info": { - "name": "python" + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" } + ], + "source": [ + "search_results = video.search(\n", + " query = \"Smooth turns\",\n", + " filter = [{\"camera_view\": \"road_ahead\"}, {\"action_type\": \"clear_road\"}], # Using metadata filter\n", + " search_type = SearchType.semantic,\n", + " index_type = IndexType.scene,\n", + " result_threshold = 100,\n", + " scene_index_id = scene_index_id\n", + ")\n", + "# Play the search results\n", + "search_results.play()" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "6ZOyc3Lg484a" + }, + "source": [ + "# βœ… Conclusion: Precision Search with Scene Metadata \n", + "With **scene-level metadata indexing**, we can: \n", + "βœ” **Precisely filter race footage** by camera angles & driver actions. \n", + "βœ” **Use AI-powered semantic search** to find **specific race moments.** \n", + "βœ” **Enhance video retrieval** for F1 analysis, highlights & research. \n", + "\n", + "πŸš€ **This approach unlocks smarter, metadata-driven video searchβ€”making every second of race footage instantly accessible.** \n" + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 0 + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/real_time_streaming/Baby_Crib_Monitoring.ipynb b/real_time_streaming/Baby_Crib_Monitoring.ipynb index 19a8ac0..cd0d57d 100644 --- a/real_time_streaming/Baby_Crib_Monitoring.ipynb +++ b/real_time_streaming/Baby_Crib_Monitoring.ipynb @@ -1,21 +1,10 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" - } - }, "cells": [ { "cell_type": "markdown", + "metadata": { + "id": "IAAY1Sc72_F3" + }, "source": [ "# πŸ‘Ά Baby Crib Monitoring Demo using VideoDB RTStream\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/video-db/videodb-cookbook/blob/main/real_time_streaming/Baby_Crib_Monitoring.ipynb)\n", @@ -46,27 +35,22 @@ "\n", "So β€” **do you want to build an intelligent AI-powered baby monitor too?** \n", "Let’s get started!\n" - ], - "metadata": { - "id": "IAAY1Sc72_F3" - } + ] }, { "cell_type": "markdown", + "metadata": { + "id": "nhuRKVkm3DXA" + }, "source": [ "---\n", "## πŸ“¦ Step 1: Install Dependencies \n", "Before setting up the AI-powered baby monitor, let’s install the necessary VideoDB SDK.\n" - ], - "metadata": { - "id": "nhuRKVkm3DXA" - } + ] }, { "cell_type": "code", - "source": [ - "!pip install -q videodb" - ], + "execution_count": 1, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -74,20 +58,25 @@ "id": "MLYrfSnT2_5D", "outputId": "4023e402-596f-4ce3-90ac-e19912a7c012" }, - "execution_count": 1, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", " Building wheel for videodb (setup.py) ... \u001b[?25l\u001b[?25hdone\n" ] } + ], + "source": [ + "!pip install -q videodb" ] }, { "cell_type": "markdown", + "metadata": { + "id": "_PBMhJeA3ZWP" + }, "source": [ "---\n", "## πŸ“¦ Step 2: Connect to VideoDB\n", @@ -97,27 +86,11 @@ "Please enter your `VIDEO_DB_API_KEY` in the input box that appears below after you run this cell.\n", "\n", "Your input will be masked.\n" - ], - "metadata": { - "id": "_PBMhJeA3ZWP" - } + ] }, { "cell_type": "code", - "source": [ - "import videodb\n", - "import os\n", - "from getpass import getpass\n", - "\n", - "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", - "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", - "\n", - "conn = videodb.connect()\n", - "coll = conn.get_collection()\n", - "\n", - "print(\"Connected to VideoDB securely!\")" - ], + "execution_count": 2, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -125,20 +98,36 @@ "id": "rU2HzZBnOmrT", "outputId": "c86816cc-6dae-4189-ffc5-34a525ff9249" }, - "execution_count": 2, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Please enter your VideoDB API Key: Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·\n", "Connected to VideoDB securely!\n" ] } + ], + "source": [ + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "conn = videodb.connect()\n", + "coll = conn.get_collection()\n", + "\n", + "print(\"Connected to VideoDB securely!\")" ] }, { "cell_type": "markdown", + "metadata": { + "id": "Br6NTi724JLa" + }, "source": [ "---\n", "\n", @@ -148,21 +137,11 @@ "In this demo, the stream is running at `rtsp://samples.rts.videodb.io:8554/crib`.\n", "\n", "\n" - ], - "metadata": { - "id": "Br6NTi724JLa" - } + ] }, { "cell_type": "code", - "source": [ - "rtsp_url = \"rtsp://samples.rts.videodb.io:8554/crib\"\n", - "crib_stream = coll.connect_rtstream(\n", - " name=\"Baby Crib Monitor\",\n", - " url=rtsp_url,\n", - ")\n", - "print(crib_stream)" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -170,43 +149,36 @@ "id": "wjNFrhEe8uAu", "outputId": "9dd8a779-ffdb-4105-b3c2-c1b6ee6d2648" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStream(id=rts-019711a0-0fde-7911-b282-25bc0b4ecf65, name=Baby Crib Monitor, collection_id=None, created_at=None, sample_rate=30, status=connected)\n" ] } + ], + "source": [ + "rtsp_url = \"rtsp://samples.rts.videodb.io:8554/crib\"\n", + "crib_stream = coll.connect_rtstream(\n", + " name=\"Baby Crib Monitor\",\n", + " url=rtsp_url,\n", + ")\n", + "print(crib_stream)" ] }, { "cell_type": "markdown", - "source": [ - "#### Let us list all the rtstreams in our collection." - ], "metadata": { "id": "OAdSXytrOwpl" - } + }, + "source": [ + "#### Let us list all the rtstreams in our collection." + ] }, { "cell_type": "code", - "source": [ - "def list_rtstreams():\n", - " for rtstream in coll.list_rtstreams():\n", - " print(f\"\"\"RTStream:\n", - " ID : {rtstream.id}\n", - " Name : {rtstream.name}\n", - " Collection ID : {rtstream.collection_id}\n", - " Created At : {rtstream.created_at}\n", - " Sample Rate : {rtstream.sample_rate}\n", - " Status : {rtstream.status}\n", - " \"\"\")\n", - " print(\"-\" * 80)\n", - "\n", - "list_rtstreams()" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -214,11 +186,10 @@ "id": "6I7NoBqzO4uS", "outputId": "6eefa4c5-4592-4c9a-bd02-62a94c9c017e" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStream:\n", " ID : rts-019711db-1086-7750-ba79-8f47a4fed603\n", @@ -249,93 +220,114 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "def list_rtstreams():\n", + " for rtstream in coll.list_rtstreams():\n", + " print(f\"\"\"RTStream:\n", + " ID : {rtstream.id}\n", + " Name : {rtstream.name}\n", + " Collection ID : {rtstream.collection_id}\n", + " Created At : {rtstream.created_at}\n", + " Sample Rate : {rtstream.sample_rate}\n", + " Status : {rtstream.status}\n", + " \"\"\")\n", + " print(\"-\" * 80)\n", + "\n", + "list_rtstreams()" ] }, { "cell_type": "markdown", + "metadata": { + "id": "e2Q73eSPPPPj" + }, "source": [ "\n", "#### If you have already connected the stream, run the below cell with the **rtstream id** to reconnect." - ], - "metadata": { - "id": "e2Q73eSPPPPj" - } + ] }, { "cell_type": "code", - "source": [ - "# crib_stream = coll.get_rtstream(\"\")" - ], + "execution_count": 10, "metadata": { "id": "qahrd519A0C8" }, - "execution_count": 10, - "outputs": [] + "outputs": [], + "source": [ + "# crib_stream = coll.get_rtstream(\"\")" + ] }, { "cell_type": "code", - "source": [ - "# To stop the stream\n", - "# crib_stream.stop()" - ], + "execution_count": null, "metadata": { "id": "6Zd9RwlHslVJ" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To stop the stream\n", + "# crib_stream.stop()" + ] }, { "cell_type": "code", - "source": [ - "# To start the stream\n", - "# crib_stream.start()" - ], + "execution_count": null, "metadata": { "id": "N3H_QkrBsi_R" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To start the stream\n", + "# crib_stream.start()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "ltx6YPLdCMPI" + }, "source": [ "---\n", "### πŸ‘€ Let us have a look at the crib stream" - ], - "metadata": { - "id": "ltx6YPLdCMPI" - } + ] }, { "cell_type": "markdown", + "metadata": { + "id": "jAyvg1J5plDD" + }, "source": [ "\n", - "#### πŸ“Ί Helper Function: Display Video Stream\n", + "#### πŸ“Ί Helper Functions: Search and Display\n", "\n", - "This cell contains a small utility function to help visualize the video streams with helpful information. You don't need to modify this code." - ], - "metadata": { - "id": "jAyvg1J5plDD" - } + "This cell contains all the utility functions to search, fetch, and visualize video streams. You don't need to modify this code." + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "OC-6vm4YWH0p" + }, + "outputs": [], "source": [ "# To display the stream with relevant information\n", "\n", "from IPython.display import HTML\n", "import re\n", - "from datetime import datetime\n", + "import time\n", + "from datetime import datetime, UTC\n", "from videodb import play_stream\n", "\n", - "def display_stream(video_url, video_name=\"πŸŽ₯ Camera Feed\"):\n", "\n", - " match = re.search(r'/(\\d{16})-(\\d{16})\\.m3u8', video_url)\n", + "def display_stream(video_url, video_name=\"πŸŽ₯ Camera Feed\"):\n", + " match = re.search(r\"/(\\d{16})-(\\d{16})\\.m3u8\", video_url)\n", " if match:\n", " start_ts = int(match.group(1)) / 1e6\n", " end_ts = int(match.group(2)) / 1e6\n", - " start_time = datetime.utcfromtimestamp(start_ts).strftime('%Y-%m-%d %H:%M:%S')\n", - " end_time = datetime.utcfromtimestamp(end_ts).strftime('%Y-%m-%d %H:%M:%S')\n", + " start_time = datetime.fromtimestamp(start_ts, UTC).strftime(\"%Y-%m-%d %H:%M:%S\")\n", + " end_time = datetime.fromtimestamp(end_ts, UTC).strftime(\"%Y-%m-%d %H:%M:%S\")\n", " time_range = f\"{start_time} β†’ {end_time} UTC\"\n", " else:\n", " time_range = \"Time Unknown\"\n", @@ -344,49 +336,58 @@ "\n", " return HTML(f\"\"\"\n", "
\n", - " {video_player_html._repr_html_() if hasattr(video_player_html, '_repr_html_') else video_player_html}\n", + " {video_player_html._repr_html_() if hasattr(video_player_html, \"_repr_html_\") else video_player_html}\n", "
\n", " {video_name}
{time_range}\n", "
\n", "
\n", - " \"\"\")" - ], - "metadata": { - "id": "OC-6vm4YWH0p" - }, - "execution_count": 34, - "outputs": [] + " \"\"\")\n", + "\n", + "\n", + "# To dynamically set the display duration\n", + "\n", + "\n", + "def prompt_to_time(prompt):\n", + " now = int(time.time())\n", + " prompt = (\n", + " f\"It's {now} in epoch seconds. \"\n", + " f\"Convert the phrase '{prompt}' into JSON \"\n", + " f'with keys \"from\" and \"to\" (both epoch seconds)'\n", + " )\n", + "\n", + " result = coll.generate_text(\n", + " prompt=prompt,\n", + " model_name=\"pro\",\n", + " response_type=\"json\",\n", + " )\n", + " output = result.get(\"output\", {})\n", + " return output.get(\"from\"), output.get(\"to\")\n", + "\n", + "\n", + "# To fetch stream\n", + "\n", + "\n", + "def fetch_stream(rtstream):\n", + " _from, to = prompt_to_time(\"Show me last 5 mins\")\n", + " stream_url = rtstream.generate_stream(_from, to)\n", + " return stream_url\n" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "m2d82DIHpbt0" + }, "source": [ "\n", "#### πŸ”— Get & Display Recent Stream\n", "\n", - "This cell uses the helper function above to fetch and display the last few minutes of the stream." - ], - "metadata": { - "id": "m2d82DIHpbt0" - } + "This cell uses the helper functions above to fetch and display the last five minutes of the stream." + ] }, { "cell_type": "code", - "source": [ - "# To get last few minutes stream link\n", - "import time\n", - "\n", - "def fetch_stream(rtstream):\n", - "\n", - " now = int(time.time())\n", - " start = int(now - (5 * 60))\n", - " stream_url = rtstream.generate_stream(start, now)\n", - " return stream_url\n", - "\n", - "video_url = fetch_stream(crib_stream)\n", - "\n", - "video_name = \"πŸ‘Ά Baby Monitor Β· Crib Activity Feed\"\n", - "display_stream(video_url, video_name)" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -395,14 +396,9 @@ "id": "1u1_rV2HknwD", "outputId": "3bf46462-7dc1-453f-c454-cc9fdf32b60b" }, - "execution_count": 42, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - "" - ], "text/html": [ "\n", "
\n", @@ -421,15 +417,29 @@ "
\n", " \n", " " + ], + "text/plain": [ + "" ] }, + "execution_count": 42, "metadata": {}, - "execution_count": 42 + "output_type": "execute_result" } + ], + "source": [ + "# To get last five minutes stream link\n", + "video_url = fetch_stream(crib_stream)\n", + "\n", + "video_name = \"πŸ‘Ά Baby Monitor Β· Crib Activity Feed\"\n", + "display_stream(video_url, video_name)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "e6ShZC__Ci1s" + }, "source": [ "---\n", "\n", @@ -437,13 +447,27 @@ "Now, we'll create a real-time scene index that periodically analyzes the video and generates natural language descriptions of what’s happening in the crib.\n", "\n", "The AI model will watch for activity such as the baby moving, sitting, or attempting to climb out.\n" - ], - "metadata": { - "id": "e6ShZC__Ci1s" - } + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5c-FLdD9Cj7r", + "outputId": "cf358254-72f2-4568-9c82-49dda920e52f" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Scene Index ID: 5722fbf82669a81e\n" + ] + } + ], "source": [ "from videodb import SceneExtractionType\n", "\n", @@ -458,54 +482,20 @@ ")\n", "crib_index_id = crib_scene_index.rtstream_index_id\n", "print(\"Scene Index ID:\", crib_index_id)" - ], - "metadata": { - "id": "5c-FLdD9Cj7r", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "cf358254-72f2-4568-9c82-49dda920e52f" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Scene Index ID: 5722fbf82669a81e\n" - ] - } ] }, { "cell_type": "markdown", - "source": [ - "#### Let us list the scene indexes created on our rtstream." - ], "metadata": { "id": "xDS7EClEPphu" - } + }, + "source": [ + "#### Let us list the scene indexes created on our rtstream." + ] }, { "cell_type": "code", - "source": [ - "def list_rtstream_indexes(rtstream):\n", - " # List live stream indexes\n", - " rtstream_indexes = rtstream.list_scene_indexes()\n", - " for rtstream_index in rtstream_indexes:\n", - "\n", - " print(f\"\"\"RTStreamSceneIndex:\n", - " Index ID : {rtstream_index.rtstream_index_id}\n", - " RTStream ID : {rtstream_index.rtstream_id}\n", - " Name : {rtstream_index.name}\n", - " Status : {rtstream_index.status}\n", - " Config : {rtstream_index.extraction_config}\n", - " Prompt : {rtstream_index.prompt}\n", - " \"\"\")\n", - " print(\"-\" * 80)\n", - "\n", - "list_rtstream_indexes(crib_stream)" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -513,11 +503,10 @@ "id": "LCoN_XYhPyuy", "outputId": "0ab19cdc-bd8f-4da6-e580-953c0b981231" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStreamSceneIndex:\n", " Index ID : 5722fbf82669a81e\n", @@ -530,66 +519,110 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "def list_rtstream_indexes(rtstream):\n", + " # List live stream indexes\n", + " rtstream_indexes = rtstream.list_scene_indexes()\n", + " for rtstream_index in rtstream_indexes:\n", + "\n", + " print(f\"\"\"RTStreamSceneIndex:\n", + " Index ID : {rtstream_index.rtstream_index_id}\n", + " RTStream ID : {rtstream_index.rtstream_id}\n", + " Name : {rtstream_index.name}\n", + " Status : {rtstream_index.status}\n", + " Config : {rtstream_index.extraction_config}\n", + " Prompt : {rtstream_index.prompt}\n", + " \"\"\")\n", + " print(\"-\" * 80)\n", + "\n", + "list_rtstream_indexes(crib_stream)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "WiBQKKR5RmAw" + }, "source": [ "\n", "#### If you have already created a scene index, run the below cell with your **scene index id** to reconnect." - ], - "metadata": { - "id": "WiBQKKR5RmAw" - } + ] }, { "cell_type": "code", - "source": [ - "# crib_index_id = \"\"\n", - "# crib_scene_index = crib_stream.get_scene_index(crib_index_id)" - ], + "execution_count": null, "metadata": { "id": "GlkI_cg_BtsH" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# crib_index_id = \"\"\n", + "# crib_scene_index = crib_stream.get_scene_index(crib_index_id)" + ] }, { "cell_type": "code", - "source": [ - "# To stop the index\n", - "# crib_scene_index.stop()" - ], + "execution_count": null, "metadata": { "id": "fgxPexsBzw0E" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To stop the index\n", + "# crib_scene_index.stop()" + ] }, { "cell_type": "code", - "source": [ - "# To start the index\n", - "# crib_scene_index.start()" - ], + "execution_count": null, "metadata": { "id": "y1b91sf7zs_j" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To start the index\n", + "# crib_scene_index.start()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "Wl-qrb-xCwkX" + }, "source": [ "---\n", "### Let us see the result of the scene indexing" - ], - "metadata": { - "id": "Wl-qrb-xCwkX" - } + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "Qu1QLl3KCx4f", + "outputId": "d6a2ad40-9337-4578-8de0-db9d6b81a315" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2025-05-27 18:15:40-2025-05-27 18:15:50: The baby is actively trying to climb out of the crib. The baby has one arm and part of its torso over the top rail of the crib, attempting to get the rest of its body over the side.\n", + "--------------------------------------------------------------------------------\n", + "2025-05-27 18:15:29-2025-05-27 18:15:39: The baby is actively climbing out of the baby crib. The baby has managed to get one leg over the side of the crib and is in the process of maneuvering the rest of their body over the edge. It appears the baby is attempting to escape the crib.\n", + "--------------------------------------------------------------------------------\n", + "2025-05-27 18:15:19-2025-05-27 18:15:29: The baby is actively trying to climb out of the crib. The baby is using the side of the crib as a ladder, placing their feet on the slats and using their hands to grip the top rail. The baby is wearing a striped shirt and dark pants. The crib is made of wood and has a decorative design.\n", + "--------------------------------------------------------------------------------\n", + "2025-05-27 18:15:09-2025-05-27 18:15:19: The baby is standing inside the crib, holding onto the top rail with both hands. The baby is looking over the top of the rail, seemingly attempting to climb out of the crib.\n", + "--------------------------------------------------------------------------------\n", + "2025-05-27 18:14:59-2025-05-27 18:15:09: The baby is actively trying to climb out of the crib. The baby has one arm and leg over the top rail of the crib, and is using the rail to pull itself up and over. The baby is wearing a dark-colored pajama set with white spots.\n", + "--------------------------------------------------------------------------------\n" + ] + } + ], "source": [ "import time\n", "from datetime import datetime\n", @@ -607,7 +640,6 @@ " # Print indexed scenes\n", " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", " scenes = rtstream_scene_index.get_scenes(page_size=5)\n", - " # print(scenes[\"scenes\"][:2])\n", " if scenes:\n", " for scene in scenes.get(\"scenes\"):\n", " start = _convert_to_ist(scene[\"start\"])\n", @@ -619,118 +651,95 @@ " print(\"Scenes not found for given index.\")\n", "\n", "get_scenes(crib_stream , crib_index_id)" - ], - "metadata": { - "id": "Qu1QLl3KCx4f", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "d6a2ad40-9337-4578-8de0-db9d6b81a315" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "2025-05-27 18:15:40-2025-05-27 18:15:50: The baby is actively trying to climb out of the crib. The baby has one arm and part of its torso over the top rail of the crib, attempting to get the rest of its body over the side.\n", - "--------------------------------------------------------------------------------\n", - "2025-05-27 18:15:29-2025-05-27 18:15:39: The baby is actively climbing out of the baby crib. The baby has managed to get one leg over the side of the crib and is in the process of maneuvering the rest of their body over the edge. It appears the baby is attempting to escape the crib.\n", - "--------------------------------------------------------------------------------\n", - "2025-05-27 18:15:19-2025-05-27 18:15:29: The baby is actively trying to climb out of the crib. The baby is using the side of the crib as a ladder, placing their feet on the slats and using their hands to grip the top rail. The baby is wearing a striped shirt and dark pants. The crib is made of wood and has a decorative design.\n", - "--------------------------------------------------------------------------------\n", - "2025-05-27 18:15:09-2025-05-27 18:15:19: The baby is standing inside the crib, holding onto the top rail with both hands. The baby is looking over the top of the rail, seemingly attempting to climb out of the crib.\n", - "--------------------------------------------------------------------------------\n", - "2025-05-27 18:14:59-2025-05-27 18:15:09: The baby is actively trying to climb out of the crib. The baby has one arm and leg over the top rail of the crib, and is using the rail to pull itself up and over. The baby is wearing a dark-colored pajama set with white spots.\n", - "--------------------------------------------------------------------------------\n" - ] - } ] }, { "cell_type": "markdown", + "metadata": { + "id": "s2F1ELR2C5Fi" + }, "source": [ "---\n", "## πŸ“¦ Step 5: Define an Event for Baby Escape \n", "We’ll now create an event in VideoDB to detect when the AI spots the baby attempting to climb out.\n" - ], - "metadata": { - "id": "s2F1ELR2C5Fi" - } + ] }, { "cell_type": "code", - "source": [ - "event_id = conn.create_event(\n", - " event_prompt=\"Detect if the baby is trying to escape or climbing out of the crib.\",\n", - " label=\"baby_escape\",\n", - ")\n", - "print(\"Event ID:\", event_id)\n" - ], + "execution_count": null, "metadata": { - "id": "U1r6hK6JC6Ty", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "U1r6hK6JC6Ty", "outputId": "79f11bd9-8767-4263-c70f-98a232b73319" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Event ID: 3adc40d26d6fed0d\n" ] } + ], + "source": [ + "event_id = conn.create_event(\n", + " event_prompt=\"Detect if the baby is trying to escape or climbing out of the crib.\",\n", + " label=\"baby_escape\",\n", + ")\n", + "print(\"Event ID:\", event_id)\n" ] }, { "cell_type": "markdown", + "metadata": { + "id": "qsCnFEZsC-DN" + }, "source": [ "---\n", "## πŸ“¦ Step 6: Attach an Alert for the Escape Event \n", "To complete our setup, we’ll link a real-time alert to this event, which will notify the parents instantly through a webhook.\n" - ], - "metadata": { - "id": "qsCnFEZsC-DN" - } + ] }, { "cell_type": "code", - "source": [ - "# Enter link to your webhook url where you want alerts to go. You can create one simply on pipedream.\n", - "webhook_url=\"\"\n", - "\n", - "if webhook_url:\n", - " alert_id = crib_scene_index.create_alert(\n", - " event_id,\n", - " callback_url=webhook_url\n", - " )\n", - " print(\"Alert ID:\", alert_id)\n", - "else:\n", - " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" - ], + "execution_count": null, "metadata": { - "id": "h2DGX1w_C-Uv", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "h2DGX1w_C-Uv", "outputId": "7f79f22c-5ea7-4560-adee-c9c4c6fb24df" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Alert ID: 06792489c7386e07\n" ] } + ], + "source": [ + "# Enter link to your webhook url where you want alerts to go. You can create one simply on pipedream.\n", + "webhook_url=\"\"\n", + "\n", + "if webhook_url:\n", + " alert_id = crib_scene_index.create_alert(\n", + " event_id,\n", + " callback_url=webhook_url\n", + " )\n", + " print(\"Alert ID:\", alert_id)\n", + "else:\n", + " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" ] }, { "cell_type": "markdown", + "metadata": { + "id": "rDLu6OisDEbX" + }, "source": [ "---\n", "## πŸ“‘ Example Alert Received at the Webhook\n", @@ -766,29 +775,21 @@ "- **stream_url** β€” This is a temporary link you can use to view the detected scene\n", "\n", "βœ… This confirms our AI-powered baby monitor is working perfectly β€” detecting escape attempts and instantly sending alerts.\n" - ], - "metadata": { - "id": "rDLu6OisDEbX" - } + ] }, { "cell_type": "markdown", + "metadata": { + "id": "I4ssZDWxRP99" + }, "source": [ "---\n", "### Let us have a look at the stream_url received in the alert." - ], - "metadata": { - "id": "I4ssZDWxRP99" - } + ] }, { "cell_type": "code", - "source": [ - "alert_stream_url = \"https://rt.stream.videodb.io/manifests/rts-019711a0-0fde-7911-b282-25bc0b4ecf65/1748475396000000-1748475407000000.m3u8\"\n", - "video_name = \"πŸ”” Baby Monitor Β· baby_escape\"\n", - "\n", - "display_stream(alert_stream_url, video_name)" - ], + "execution_count": 43, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -797,14 +798,9 @@ "id": "0ZbUjnHARYCM", "outputId": "7e5f4eaa-e1f2-430c-e642-091e1e92ea3d" }, - "execution_count": 43, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - "" - ], "text/html": [ "\n", "
\n", @@ -823,76 +819,89 @@ "
\n", " \n", " " + ], + "text/plain": [ + "" ] }, + "execution_count": 43, "metadata": {}, - "execution_count": 43 + "output_type": "execute_result" } + ], + "source": [ + "alert_stream_url = \"https://rt.stream.videodb.io/manifests/rts-019711a0-0fde-7911-b282-25bc0b4ecf65/1748475396000000-1748475407000000.m3u8\"\n", + "video_name = \"πŸ”” Baby Monitor Β· baby_escape\"\n", + "\n", + "display_stream(alert_stream_url, video_name)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "X4DagrH7DN1o" + }, "source": [ "---\n", "- Let us disable the alert after our task is done." - ], - "metadata": { - "id": "X4DagrH7DN1o" - } + ] }, { "cell_type": "code", - "source": [ - "crib_scene_index.disable_alert(alert_id)" - ], + "execution_count": null, "metadata": { "id": "iMGZNp4v53FE" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "crib_scene_index.disable_alert(alert_id)" + ] }, { "cell_type": "markdown", - "source": [ - "- To enable the alert again" - ], "metadata": { "id": "443byT_U7saD" - } + }, + "source": [ + "- To enable the alert again" + ] }, { "cell_type": "code", - "source": [ - "crib_scene_index.enable_alert(alert_id)" - ], + "execution_count": null, "metadata": { "id": "SBJd23bR6wAt" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "crib_scene_index.enable_alert(alert_id)" + ] }, { "cell_type": "markdown", - "source": [ - "- Now we can stop the stream" - ], "metadata": { "id": "2TBU4eH3-dVo" - } + }, + "source": [ + "- Now we can stop the stream" + ] }, { "cell_type": "code", - "source": [ - "crib_stream.stop()" - ], + "execution_count": null, "metadata": { "id": "1qPJDivw-cvZ" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "crib_stream.stop()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "6rU7-6iLDRUz" + }, "source": [ "---\n", "\n", @@ -915,10 +924,21 @@ "The possibilities of real-time video intelligence at home are endless.\n", "\n", "**What would *you* monitor next?**" - ], - "metadata": { - "id": "6rU7-6iLDRUz" - } + ] } - ] + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 0 } diff --git a/real_time_streaming/Cricket_Match_Monitoring.ipynb b/real_time_streaming/Cricket_Match_Monitoring.ipynb index 8fe6f49..1624aed 100644 --- a/real_time_streaming/Cricket_Match_Monitoring.ipynb +++ b/real_time_streaming/Cricket_Match_Monitoring.ipynb @@ -1,21 +1,10 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" - } - }, "cells": [ { "cell_type": "markdown", + "metadata": { + "id": "kO86nzxaK4_5" + }, "source": [ "# 🏏 ICC World Cup Finals: Cricket Highlights Monitoring using VideoDB RTStream\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/video-db/videodb-cookbook/blob/main/real_time_streaming/Cricket_Match_Monitoring.ipynb)\n", @@ -45,36 +34,33 @@ "Let’s get started!\n", "\n", "---" - ], - "metadata": { - "id": "kO86nzxaK4_5" - } + ] }, { "cell_type": "markdown", + "metadata": { + "id": "XBmSI6lUK_0o" + }, "source": [ "## πŸ“¦ Step 1: Install Dependencies\n", "\n", "We’ll begin by installing the VideoDB SDK" - ], - "metadata": { - "id": "XBmSI6lUK_0o" - } + ] }, { "cell_type": "code", "execution_count": 1, "metadata": { - "id": "Qc_2CLkFKzVl", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "Qc_2CLkFKzVl", "outputId": "4b5156fe-fa4b-4cfa-d952-762ab1c1af5f" }, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", " Building wheel for videodb (setup.py) ... \u001b[?25l\u001b[?25hdone\n" @@ -87,6 +73,9 @@ }, { "cell_type": "markdown", + "metadata": { + "id": "UThnziTKLH_4" + }, "source": [ "---\n", "## πŸ“¦ Step 2: Connect to VideoDB\n", @@ -96,48 +85,48 @@ "Please enter your `VIDEO_DB_API_KEY` in the input box that appears below after you run this cell.\n", "\n", "Your input will be masked.\n" - ], - "metadata": { - "id": "UThnziTKLH_4" - } + ] }, { "cell_type": "code", - "source": [ - "import videodb\n", - "import os\n", - "from getpass import getpass\n", - "\n", - "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", - "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", - "\n", - "conn = videodb.connect()\n", - "coll = conn.get_collection()\n", - "\n", - "print(\"Connected to VideoDB securely!\")" - ], + "execution_count": 2, "metadata": { - "id": "XFJr9G18LIbR", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "XFJr9G18LIbR", "outputId": "5362ab1f-2bc6-4ecf-c0b7-340d6c79ebd3" }, - "execution_count": 2, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Please enter your VideoDB API Key: Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·\n", "Connected to VideoDB securely!\n" ] } + ], + "source": [ + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "conn = videodb.connect()\n", + "coll = conn.get_collection()\n", + "\n", + "print(\"Connected to VideoDB securely!\")" ] }, { "cell_type": "markdown", + "metadata": { + "id": "26NDfhgFLQo4" + }, "source": [ "---\n", "\n", @@ -145,65 +134,48 @@ "\n", "We’ll now connect to the live video stream of the match using its RTSP URL.\n", "In this demo, the stream is running at `rtsp://samples.rts.videodb.io:8554/cricket`.\n" - ], - "metadata": { - "id": "26NDfhgFLQo4" - } + ] }, { "cell_type": "code", - "source": [ - "rtsp_url = \"rtsp://samples.rts.videodb.io:8554/cricket\"\n", - "cricket_stream = coll.connect_rtstream(\n", - " name=\"Cricket Finals Stream\",\n", - " url=rtsp_url,\n", - ")\n", - "print(cricket_stream)" - ], + "execution_count": null, "metadata": { - "id": "9V0WZTUuLaSl", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "9V0WZTUuLaSl", "outputId": "fb5d5cfd-4d5d-44af-c6bb-42a98cd4ce85" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStream(id=rts-019711db-1086-7750-ba79-8f47a4fed603, name=Cricket Finals Stream, collection_id=None, created_at=None, sample_rate=30, status=connected)\n" ] } + ], + "source": [ + "rtsp_url = \"rtsp://samples.rts.videodb.io:8554/cricket\"\n", + "cricket_stream = coll.connect_rtstream(\n", + " name=\"Cricket Finals Stream\",\n", + " url=rtsp_url,\n", + ")\n", + "print(cricket_stream)" ] }, { "cell_type": "markdown", - "source": [ - "#### Let us list all the rtstreams in our collection." - ], "metadata": { "id": "hPqJX2v3WSzh" - } + }, + "source": [ + "#### Let us list all the rtstreams in our collection." + ] }, { "cell_type": "code", - "source": [ - "def list_rtstreams():\n", - " for rtstream in coll.list_rtstreams():\n", - " print(f\"\"\"RTStream:\n", - " ID : {rtstream.id}\n", - " Name : {rtstream.name}\n", - " Collection ID : {rtstream.collection_id}\n", - " Created At : {rtstream.created_at}\n", - " Sample Rate : {rtstream.sample_rate}\n", - " Status : {rtstream.status}\n", - " \"\"\")\n", - " print(\"-\" * 80)\n", - "\n", - "list_rtstreams()" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -211,11 +183,10 @@ "id": "K-UtkJyvWUuA", "outputId": "cf38d574-f4bc-487a-a8d5-0e4b52eb5c55" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStream:\n", " ID : rts-019711db-1086-7750-ba79-8f47a4fed603\n", @@ -246,93 +217,113 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "def list_rtstreams():\n", + " for rtstream in coll.list_rtstreams():\n", + " print(f\"\"\"RTStream:\n", + " ID : {rtstream.id}\n", + " Name : {rtstream.name}\n", + " Collection ID : {rtstream.collection_id}\n", + " Created At : {rtstream.created_at}\n", + " Sample Rate : {rtstream.sample_rate}\n", + " Status : {rtstream.status}\n", + " \"\"\")\n", + " print(\"-\" * 80)\n", + "\n", + "list_rtstreams()" ] }, { "cell_type": "markdown", + "metadata": { + "id": "e11P2KZF22xx" + }, "source": [ "\n", "#### If you have already connected the stream, run the below cell with the **rtstream id** to reconnect." - ], - "metadata": { - "id": "e11P2KZF22xx" - } + ] }, { "cell_type": "code", - "source": [ - "# cricket_stream = coll.get_rtstream(\"\")" - ], + "execution_count": 3, "metadata": { "id": "VPuEd_J923yY" }, - "execution_count": 3, - "outputs": [] + "outputs": [], + "source": [ + "# cricket_stream = coll.get_rtstream(\"\")" + ] }, { "cell_type": "code", - "source": [ - "# To stop the stream\n", - "# cricket_stream.stop()" - ], + "execution_count": null, "metadata": { "id": "eLk7rtL73Ci3" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To stop the stream\n", + "# cricket_stream.stop()" + ] }, { "cell_type": "code", - "source": [ - "# To start the stream\n", - "# cricket_stream.start()" - ], + "execution_count": 4, "metadata": { "id": "TqqxMTcQ3B4v" }, - "execution_count": 4, - "outputs": [] + "outputs": [], + "source": [ + "# To start the stream\n", + "# cricket_stream.start()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "70d7muCjLlFf" + }, "source": [ "---\n", "### πŸ‘€ Let us have a look at the cricket match" - ], - "metadata": { - "id": "70d7muCjLlFf" - } + ] }, { "cell_type": "markdown", - "source": [ - "\n", - "#### πŸ“Ί Helper Function: Display Video Stream\n", - "\n", - "This cell contains a small utility function to help visualize the video streams with helpful information. You don't need to modify this code." - ], "metadata": { "id": "9jjx7-4J3QCH" - } + }, + "source": [ + "#### πŸ“Ί Helper Functions: Search and Display\n", + "\n", + "This cell contains all the utility functions to search, fetch, and visualize video streams. You don't need to modify this code." + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fSl4fjg7LuNY" + }, + "outputs": [], "source": [ "# To display the stream with relevant information\n", "\n", "from IPython.display import HTML\n", "import re\n", - "from datetime import datetime\n", + "import time\n", + "from datetime import datetime, UTC\n", "from videodb import play_stream\n", "\n", - "def display_stream(video_url, video_name=\"πŸŽ₯ Camera Feed\"):\n", "\n", - " match = re.search(r'/(\\d{16})-(\\d{16})\\.m3u8', video_url)\n", + "def display_stream(video_url, video_name=\"πŸŽ₯ Camera Feed\"):\n", + " match = re.search(r\"/(\\d{16})-(\\d{16})\\.m3u8\", video_url)\n", " if match:\n", " start_ts = int(match.group(1)) / 1e6\n", " end_ts = int(match.group(2)) / 1e6\n", - " start_time = datetime.utcfromtimestamp(start_ts).strftime('%Y-%m-%d %H:%M:%S')\n", - " end_time = datetime.utcfromtimestamp(end_ts).strftime('%Y-%m-%d %H:%M:%S')\n", + " start_time = datetime.fromtimestamp(start_ts, UTC).strftime(\"%Y-%m-%d %H:%M:%S\")\n", + " end_time = datetime.fromtimestamp(end_ts, UTC).strftime(\"%Y-%m-%d %H:%M:%S\")\n", " time_range = f\"{start_time} β†’ {end_time} UTC\"\n", " else:\n", " time_range = \"Time Unknown\"\n", @@ -341,65 +332,69 @@ "\n", " return HTML(f\"\"\"\n", "
\n", - " {video_player_html._repr_html_() if hasattr(video_player_html, '_repr_html_') else video_player_html}\n", + " {video_player_html._repr_html_() if hasattr(video_player_html, \"_repr_html_\") else video_player_html}\n", "
\n", " {video_name}
{time_range}\n", "
\n", "
\n", - " \"\"\")" - ], - "metadata": { - "id": "fSl4fjg7LuNY" - }, - "execution_count": 6, - "outputs": [] + " \"\"\")\n", + "\n", + "\n", + "# To dynamically set the display duration\n", + "\n", + "\n", + "def prompt_to_time(prompt):\n", + " now = int(time.time())\n", + " prompt = (\n", + " f\"It's {now} in epoch seconds. \"\n", + " f\"Convert the phrase '{prompt}' into JSON \"\n", + " f'with keys \"from\" and \"to\" (both epoch seconds)'\n", + " )\n", + "\n", + " result = coll.generate_text(\n", + " prompt=prompt,\n", + " model_name=\"pro\",\n", + " response_type=\"json\",\n", + " )\n", + " output = result.get(\"output\", {})\n", + " return output.get(\"from\"), output.get(\"to\")\n", + "\n", + "\n", + "# To fetch stream\n", + "\n", + "\n", + "def fetch_stream(rtstream):\n", + " _from, to = prompt_to_time(\"Show me last 2 mins\")\n", + " stream_url = rtstream.generate_stream(_from, to)\n", + " return stream_url\n" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "E0gFicp13UIg" + }, "source": [ "\n", "#### πŸ”— Get & Display Recent Stream\n", "\n", - "This cell uses the helper function above to fetch and display the last few minutes of the stream." - ], - "metadata": { - "id": "E0gFicp13UIg" - } + "This cell uses the helper functions above to fetch and display the last few minutes of the stream." + ] }, { "cell_type": "code", - "source": [ - "# To get last few minutes stream link\n", - "import time\n", - "\n", - "def fetch_stream(rtstream):\n", - "\n", - " now = int(time.time())\n", - " start = int(now - (5 * 60))\n", - " stream_url = rtstream.generate_stream(start, now)\n", - " return stream_url\n", - "\n", - "video_url = fetch_stream(cricket_stream)\n", - "\n", - "video_name = \"🏏 ICC World Cup Β· India vs Pakistan\"\n", - "display_stream(video_url, video_name)" - ], + "execution_count": null, "metadata": { - "id": "Clwtuv2ZLsKs", "colab": { "base_uri": "https://localhost:8080/", "height": 422 }, + "id": "Clwtuv2ZLsKs", "outputId": "1bfc3187-3048-4cd1-d520-5a60dfa4c0c7" }, - "execution_count": 15, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - "" - ], "text/html": [ "\n", "
\n", @@ -418,15 +413,30 @@ "
\n", " \n", " " + ], + "text/plain": [ + "" ] }, + "execution_count": 15, "metadata": {}, - "execution_count": 15 + "output_type": "execute_result" } + ], + "source": [ + "# To get last few minutes stream link\n", + "\n", + "video_url = fetch_stream(cricket_stream)\n", + "\n", + "video_name = \"🏏 ICC World Cup Β· India vs Pakistan\"\n", + "display_stream(video_url, video_name)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "bIdL2H-HL1jo" + }, "source": [ "---\n", "## πŸ“¦ Step 4: Index Scenes and Describe Match Highlights\n", @@ -438,13 +448,27 @@ "- Fours (ball crosses boundary rope after bouncing)\n", "- Catches (fielder catches the ball before it touches the ground)\n", "- Wickets (batsman is dismissed)" - ], - "metadata": { - "id": "bIdL2H-HL1jo" - } + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "svXbOJVBL32W", + "outputId": "c4e90af1-dfdc-49ca-99a5-af08279a01b1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Scene Index ID: c251098cf8fc4561\n" + ] + } + ], "source": [ "from videodb import SceneExtractionType\n", "\n", @@ -459,54 +483,20 @@ ")\n", "cricket_index_id = cricket_scene_index.rtstream_index_id\n", "print(\"Scene Index ID:\", cricket_index_id)" - ], - "metadata": { - "id": "svXbOJVBL32W", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "c4e90af1-dfdc-49ca-99a5-af08279a01b1" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Scene Index ID: c251098cf8fc4561\n" - ] - } ] }, { "cell_type": "markdown", - "source": [ - "#### Let us list the scene indexes created on our rtstream." - ], "metadata": { "id": "l6CFNuv_Wakz" - } + }, + "source": [ + "#### Let us list the scene indexes created on our rtstream." + ] }, { "cell_type": "code", - "source": [ - "def list_rtstream_indexes(rtstream):\n", - " # List live stream indexes\n", - " rtstream_indexes = rtstream.list_scene_indexes()\n", - " for rtstream_index in rtstream_indexes:\n", - "\n", - " print(f\"\"\"RTStreamSceneIndex:\n", - " Index ID : {rtstream_index.rtstream_index_id}\n", - " RTStream ID : {rtstream_index.rtstream_id}\n", - " Name : {rtstream_index.name}\n", - " Status : {rtstream_index.status}\n", - " Config : {rtstream_index.extraction_config}\n", - " Prompt : {rtstream_index.prompt}\n", - " \"\"\")\n", - " print(\"-\" * 80)\n", - "\n", - "list_rtstream_indexes(cricket_stream)" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -514,11 +504,10 @@ "id": "S8GvMO0MWgOQ", "outputId": "0c12dcff-c7b0-404d-8c2c-9fd79d2ac30b" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStreamSceneIndex:\n", " Index ID : c251098cf8fc4561\n", @@ -540,108 +529,96 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "def list_rtstream_indexes(rtstream):\n", + " # List live stream indexes\n", + " rtstream_indexes = rtstream.list_scene_indexes()\n", + " for rtstream_index in rtstream_indexes:\n", + "\n", + " print(f\"\"\"RTStreamSceneIndex:\n", + " Index ID : {rtstream_index.rtstream_index_id}\n", + " RTStream ID : {rtstream_index.rtstream_id}\n", + " Name : {rtstream_index.name}\n", + " Status : {rtstream_index.status}\n", + " Config : {rtstream_index.extraction_config}\n", + " Prompt : {rtstream_index.prompt}\n", + " \"\"\")\n", + " print(\"-\" * 80)\n", + "\n", + "list_rtstream_indexes(cricket_stream)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "6pSm3bj83jgw" + }, "source": [ "\n", "#### If you have already created a scene index, run the below cell with your **scene index id** to reconnect." - ], - "metadata": { - "id": "6pSm3bj83jgw" - } + ] }, { "cell_type": "code", - "source": [ - "# cricket_index_id = \"\"\n", - "# cricket_scene_index = cricket_stream.get_scene_index(cricket_index_id)" - ], + "execution_count": null, "metadata": { "id": "8xmrpUhN3kRi" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# cricket_index_id = \"\"\n", + "# cricket_scene_index = cricket_stream.get_scene_index(cricket_index_id)" + ] }, { "cell_type": "code", - "source": [ - "# To stop the index\n", - "# cricket_scene_index.stop()" - ], + "execution_count": null, "metadata": { "id": "t_Dl05Oc3utw" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To stop the index\n", + "# cricket_scene_index.stop()" + ] }, { "cell_type": "code", - "source": [ - "# To start the index\n", - "# cricket_scene_index.start()" - ], + "execution_count": null, "metadata": { "id": "l_bKugSM3s_f" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To start the index\n", + "# cricket_scene_index.start()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "GPD_IfJsL_oo" + }, "source": [ "---\n", "### Let us see the result of the scene indexing" - ], - "metadata": { - "id": "GPD_IfJsL_oo" - } + ] }, { "cell_type": "code", - "source": [ - "import time\n", - "from datetime import datetime\n", - "from zoneinfo import ZoneInfo\n", - "\n", - "def _convert_to_ist(timestamp: float) -> str:\n", - " \"\"\"Convert UTC timestamp to IST (Asia/Kolkata) datetime string.\"\"\"\n", - " return (\n", - " datetime.fromtimestamp(timestamp)\n", - " .astimezone(ZoneInfo(\"Asia/Kolkata\"))\n", - " .strftime(\"%Y-%m-%d %H:%M:%S\")\n", - " )\n", - "\n", - "def get_scenes(rtstream, index_id):\n", - " # Print indexed scenes\n", - " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", - " scenes = rtstream_scene_index.get_scenes(page_size=5)\n", - " # print(scenes[\"scenes\"][:2])\n", - " if scenes:\n", - " for scene in scenes.get(\"scenes\"):\n", - " start = _convert_to_ist(scene[\"start\"])\n", - " end = _convert_to_ist(scene[\"end\"])\n", - " description = scene[\"description\"]\n", - " print(f\"{start}-{end}: {description}\")\n", - " print(\"-\" * 80)\n", - " else:\n", - " print(\"Scenes not found for given index.\")\n", - "\n", - "get_scenes(cricket_stream , cricket_index_id)" - ], + "execution_count": null, "metadata": { - "id": "dv0SS7FAL_7v", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "dv0SS7FAL_7v", "outputId": "9aafcd08-1f7f-4387-a7ba-c19bc3807747" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "2025-05-27 19:00:38-2025-05-27 19:00:45: Here's what I can identify from the images:\n", "\n", @@ -673,129 +650,152 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "import time\n", + "from datetime import datetime\n", + "from zoneinfo import ZoneInfo\n", + "\n", + "def _convert_to_ist(timestamp: float) -> str:\n", + " \"\"\"Convert UTC timestamp to IST (Asia/Kolkata) datetime string.\"\"\"\n", + " return (\n", + " datetime.fromtimestamp(timestamp)\n", + " .astimezone(ZoneInfo(\"Asia/Kolkata\"))\n", + " .strftime(\"%Y-%m-%d %H:%M:%S\")\n", + " )\n", + "\n", + "def get_scenes(rtstream, index_id):\n", + " # Print indexed scenes\n", + " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", + " scenes = rtstream_scene_index.get_scenes(page_size=5)\n", + " if scenes:\n", + " for scene in scenes.get(\"scenes\"):\n", + " start = _convert_to_ist(scene[\"start\"])\n", + " end = _convert_to_ist(scene[\"end\"])\n", + " description = scene[\"description\"]\n", + " print(f\"{start}-{end}: {description}\")\n", + " print(\"-\" * 80)\n", + " else:\n", + " print(\"Scenes not found for given index.\")\n", + "\n", + "get_scenes(cricket_stream , cricket_index_id)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "I6hKrjUPMFGY" + }, "source": [ "## πŸ“¦ Step 5: Define Events for Each Match Highlight\n", "\n", "Now, let’s define four events to detect each key match moment.\n" - ], - "metadata": { - "id": "I6hKrjUPMFGY" - } + ] }, { "cell_type": "markdown", - "source": [ - "1. Sixes (ball hit over the boundary rope without bouncing)" - ], "metadata": { "id": "pV3DfpiH5xVt" - } + }, + "source": [ + "1. Sixes (ball hit over the boundary rope without bouncing)" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "VD6hI3BfMGZf" + }, + "outputs": [], "source": [ "six_event_id = conn.create_event(\n", " event_prompt=\"Detect when a batsman hits a SIX.\",\n", " label=\"six_hit\"\n", ")" - ], - "metadata": { - "id": "VD6hI3BfMGZf" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "2. Fours (ball crosses boundary rope after bouncing)\n" - ], "metadata": { "id": "gd5PDLpQ54BM" - } + }, + "source": [ + "2. Fours (ball crosses boundary rope after bouncing)\n" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "4gllIq-i53ek" + }, + "outputs": [], "source": [ "four_event_id = conn.create_event(\n", " event_prompt=\"Detect when a batsman hits a FOUR.\",\n", " label=\"four_hit\"\n", ")" - ], - "metadata": { - "id": "4gllIq-i53ek" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "3. Catches (fielder catches the ball before it touches the ground)\n" - ], "metadata": { "id": "qmD5d_7t58of" - } + }, + "source": [ + "3. Catches (fielder catches the ball before it touches the ground)\n" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "bKb5pIr25-BJ" + }, + "outputs": [], "source": [ "catch_event_id = conn.create_event(\n", " event_prompt=\"Detect when a player takes a CATCH OUT.\",\n", " label=\"catch_out\"\n", ")" - ], - "metadata": { - "id": "bKb5pIr25-BJ" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "4. Wickets (batsman is dismissed)" - ], "metadata": { "id": "Es_LFBg06Bms" - } + }, + "source": [ + "4. Wickets (batsman is dismissed)" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "dVknEJtt6Cxo" + }, + "outputs": [], "source": [ "wicket_event_id = conn.create_event(\n", " event_prompt=\"Detect when a batsman is dismissed (WICKET).\",\n", " label=\"wicket\"\n", ")" - ], - "metadata": { - "id": "dVknEJtt6Cxo" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "Let us have a look at all the events created:" - ], "metadata": { "id": "wmEHFQPk6HGM" - } + }, + "source": [ + "Let us have a look at all the events created:" + ] }, { "cell_type": "code", - "source": [ - "print(f\"Successfully created events with the following IDs:\\n\"\n", - " f\"- Six Event ID: {six_event_id}\\n\"\n", - " f\"- Four Event ID: {four_event_id}\\n\"\n", - " f\"- Catch Event ID: {catch_event_id}\\n\"\n", - " f\"- Wicket Event ID: {wicket_event_id}\")" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -803,11 +803,10 @@ "id": "fG3jEJa26PHN", "outputId": "787e18a7-04c5-46b2-86b0-a91c5a320d97" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Successfully created events with the following IDs:\n", "- Six Event ID: 627cdd8124f7731b\n", @@ -816,44 +815,56 @@ "- Wicket Event ID: fae540ba13f50530\n" ] } + ], + "source": [ + "print(f\"Successfully created events with the following IDs:\\n\"\n", + " f\"- Six Event ID: {six_event_id}\\n\"\n", + " f\"- Four Event ID: {four_event_id}\\n\"\n", + " f\"- Catch Event ID: {catch_event_id}\\n\"\n", + " f\"- Wicket Event ID: {wicket_event_id}\")" ] }, { "cell_type": "markdown", + "metadata": { + "id": "oYXk7FoOMLgX" + }, "source": [ "---\n", "\n", "## πŸ“¦ Step 6: Attach Alerts for Each Event\n", "\n", "We’ll create four different alerts β€” one for each event β€” but route them all to the same Pipedream webhook.\n" - ], - "metadata": { - "id": "oYXk7FoOMLgX" - } + ] }, { "cell_type": "code", - "source": [ - "# Enter link to your webhook url where you want alerts to go. You can create one simply on pipedream.\n", - "webhook_url=\"\"" - ], + "execution_count": null, "metadata": { "id": "0zL_5_DuXJJQ" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# Enter link to your webhook url where you want alerts to go. You can create one simply on pipedream.\n", + "webhook_url=\"\"" + ] }, { "cell_type": "markdown", - "source": [ - "1. Create an alert for 'six' events and store its ID" - ], "metadata": { "id": "u0mViLY_6_pM" - } + }, + "source": [ + "1. Create an alert for 'six' events and store its ID" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "zzzCnrwWMMwX" + }, + "outputs": [], "source": [ "if webhook_url:\n", " six_alert_id = cricket_scene_index.create_alert(\n", @@ -862,24 +873,24 @@ " )\n", "else:\n", " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" - ], - "metadata": { - "id": "zzzCnrwWMMwX" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "2. Create an alert for 'four' events and store its ID" - ], "metadata": { "id": "yCHk6uLO7NVM" - } + }, + "source": [ + "2. Create an alert for 'four' events and store its ID" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "fa7z-KHq7Hcg" + }, + "outputs": [], "source": [ "if webhook_url:\n", " four_alert_id = cricket_scene_index.create_alert(\n", @@ -888,24 +899,24 @@ " )\n", "else:\n", " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" - ], - "metadata": { - "id": "fa7z-KHq7Hcg" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "3. Create an alert for 'catch' events and store its ID" - ], "metadata": { "id": "eEBjmmSb7UPV" - } + }, + "source": [ + "3. Create an alert for 'catch' events and store its ID" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MIL8N4mQ7JP9" + }, + "outputs": [], "source": [ "if webhook_url:\n", " catch_alert_id = cricket_scene_index.create_alert(\n", @@ -914,24 +925,24 @@ " )\n", "else:\n", " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" - ], - "metadata": { - "id": "MIL8N4mQ7JP9" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "4. Create an alert for 'wicket' events and store its ID" - ], "metadata": { "id": "U-I8zxJx7Zuc" - } + }, + "source": [ + "4. Create an alert for 'wicket' events and store its ID" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "5b-Prj197KuE" + }, + "outputs": [], "source": [ "if webhook_url:\n", " wicket_alert_id = cricket_scene_index.create_alert(\n", @@ -940,57 +951,32 @@ " )\n", "else:\n", " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" - ], - "metadata": { - "id": "5b-Prj197KuE" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", + "metadata": { + "id": "aQPuGgs3MxHx" + }, "source": [ "---\n", "#### Lets list all the alerts" - ], - "metadata": { - "id": "aQPuGgs3MxHx" - } + ] }, { "cell_type": "code", - "source": [ - "def list_rtstream_alerts(rtstream, index_id):\n", - " \"\"\"\n", - " Prints a list of alerts associated with a given scene index.\n", - " \"\"\"\n", - " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", - " alerts = rtstream_scene_index.list_alerts()\n", - "\n", - " for alert in alerts:\n", - " print(f\"\"\"πŸ”” RTStream Alert:\n", - " Alert ID : {alert['alert_id']}\n", - " Event ID : {alert['event_id']}\n", - " Label : {alert['label']}\n", - " Prompt : {alert['prompt']}\n", - " Status : {alert['status']}\n", - " \"\"\")\n", - " print(\"-\" * 80)\n", - "\n", - "list_rtstream_alerts(cricket_stream, cricket_index_id)\n" - ], + "execution_count": null, "metadata": { - "id": "vVfjDk9tM397", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "vVfjDk9tM397", "outputId": "10d61425-e85a-410c-d211-5a7e634ff6df" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "πŸ”” RTStream Alert:\n", " Alert ID : 1891843a2c669ea3\n", @@ -1026,10 +1012,33 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "def list_rtstream_alerts(rtstream, index_id):\n", + " \"\"\"\n", + " Prints a list of alerts associated with a given scene index.\n", + " \"\"\"\n", + " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", + " alerts = rtstream_scene_index.list_alerts()\n", + "\n", + " for alert in alerts:\n", + " print(f\"\"\"πŸ”” RTStream Alert:\n", + " Alert ID : {alert['alert_id']}\n", + " Event ID : {alert['event_id']}\n", + " Label : {alert['label']}\n", + " Prompt : {alert['prompt']}\n", + " Status : {alert['status']}\n", + " \"\"\")\n", + " print(\"-\" * 80)\n", + "\n", + "list_rtstream_alerts(cricket_stream, cricket_index_id)\n" ] }, { "cell_type": "markdown", + "metadata": { + "id": "OYe1mm-rM_H_" + }, "source": [ "---\n", "\n", @@ -1066,38 +1075,30 @@ "```\n", "\n", "βœ… Similarly, alerts will be triggered for **SIX** and **WICKET** events, routed to the same webhook URL." - ], - "metadata": { - "id": "OYe1mm-rM_H_" - } + ] }, { "cell_type": "markdown", + "metadata": { + "id": "_p2JgOc6aNeJ" + }, "source": [ "---\n", "### Let us have a look at the stream links we received in the alerts." - ], - "metadata": { - "id": "_p2JgOc6aNeJ" - } + ] }, { "cell_type": "markdown", - "source": [ - "1. FOUR HIT" - ], "metadata": { "id": "BgaYWBQjaaQ2" - } + }, + "source": [ + "1. FOUR HIT" + ] }, { "cell_type": "code", - "source": [ - "alert_stream_url = \"https://rt.stream.videodb.io/manifests/rts-019711db-1086-7750-ba79-8f47a4fed603/1748477432000000-1748477440000000.m3u8\"\n", - "video_name = \"🏏 ICC World Cup Β· four_hit\"\n", - "\n", - "display_stream(alert_stream_url,video_name)" - ], + "execution_count": 7, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -1106,14 +1107,9 @@ "id": "9zWvALmqYcTS", "outputId": "27d2b025-d844-4d2f-ffee-3b8e6fe72b60" }, - "execution_count": 7, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - "" - ], "text/html": [ "\n", "
\n", @@ -1132,30 +1128,35 @@ "
\n", " \n", " " + ], + "text/plain": [ + "" ] }, + "execution_count": 7, "metadata": {}, - "execution_count": 7 + "output_type": "execute_result" } + ], + "source": [ + "alert_stream_url = \"https://rt.stream.videodb.io/manifests/rts-019711db-1086-7750-ba79-8f47a4fed603/1748477432000000-1748477440000000.m3u8\"\n", + "video_name = \"🏏 ICC World Cup · four_hit\"\n", + "\n", + "display_stream(alert_stream_url,video_name)" ] }, { "cell_type": "markdown", - "source": [ - "2. CATCH OUT" - ], "metadata": { "id": "3kPePjTrac89" - } + }, + "source": [ + "2. CATCH OUT" + ] }, { "cell_type": "code", - "source": [ - "alert_stream_url = \"https://rt.stream.videodb.io/manifests/rts-019711db-1086-7750-ba79-8f47a4fed603/1748477424000000-1748477432000000.m3u8\"\n", - "video_name = \"🏏 ICC World Cup · catch_out\"\n", - "\n", - "display_stream(alert_stream_url,video_name)" - ], + "execution_count": 8, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -1164,14 +1165,9 @@ "id": "-Dn-kuVrahFX", "outputId": "274825df-a702-4ede-c245-06b2c46c9172" }, - "execution_count": 8, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - "" - ], "text/html": [ "\n", "
\n", @@ -1190,82 +1186,95 @@ "
\n", " \n", " " + ], + "text/plain": [ + "" ] }, + "execution_count": 8, "metadata": {}, - "execution_count": 8 + "output_type": "execute_result" } + ], + "source": [ + "alert_stream_url = \"https://rt.stream.videodb.io/manifests/rts-019711db-1086-7750-ba79-8f47a4fed603/1748477424000000-1748477432000000.m3u8\"\n", + "video_name = \"🏏 ICC World Cup Β· catch_out\"\n", + "\n", + "display_stream(alert_stream_url,video_name)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "Sn3YCec3eEs7" + }, "source": [ "---\n", "- After the match is over, we can disable the alerts." - ], - "metadata": { - "id": "Sn3YCec3eEs7" - } + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "pdHJCvzNeLJS" + }, + "outputs": [], "source": [ "cricket_scene_index.disable_alert(six_alert_id)\n", "cricket_scene_index.disable_alert(four_alert_id)\n", "cricket_scene_index.disable_alert(catch_alert_id)\n", "cricket_scene_index.disable_alert(wicket_alert_id)" - ], - "metadata": { - "id": "pdHJCvzNeLJS" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "- To enable the alerts again:" - ], "metadata": { "id": "pMYYapCYe_dp" - } + }, + "source": [ + "- To enable the alerts again:" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "-P9dKZdBfELE" + }, + "outputs": [], "source": [ "cricket_scene_index.enable_alert(six_alert_id)\n", "cricket_scene_index.enable_alert(four_alert_id)\n", "cricket_scene_index.enable_alert(catch_alert_id)\n", "cricket_scene_index.enable_alert(wicket_alert_id)" - ], - "metadata": { - "id": "-P9dKZdBfELE" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "- Now we can stop the stream" - ], "metadata": { "id": "D52QAVW1-j2V" - } + }, + "source": [ + "- Now we can stop the stream" + ] }, { "cell_type": "code", - "source": [ - "cricket_stream.stop()" - ], + "execution_count": null, "metadata": { "id": "VlMQd7uX-mf5" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "cricket_stream.stop()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "Van6xiWVNENn" + }, "source": [ "---\n", "## πŸ† Wrapping Up: Outpacing the Competition in Real Time\n", @@ -1286,10 +1295,21 @@ "The possibilities of real-time video intelligence in media and sports are endless.\n", "\n", "**What would *you* stream next?**\n" - ], - "metadata": { - "id": "Van6xiWVNENn" - } + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" } - ] -} \ No newline at end of file + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/real_time_streaming/Flash_Flood_Detection.ipynb b/real_time_streaming/Flash_Flood_Detection.ipynb index 5d59250..b771cd2 100644 --- a/real_time_streaming/Flash_Flood_Detection.ipynb +++ b/real_time_streaming/Flash_Flood_Detection.ipynb @@ -1,21 +1,10 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" - } - }, "cells": [ { "cell_type": "markdown", + "metadata": { + "id": "zpV7M-CLQJit" + }, "source": [ "# 🌊 Flash Flood Detection in Arizona Using VideoDB RTStream\n", "\n", @@ -46,50 +35,50 @@ "\n", "Let’s build it together!\n", "\n" - ], - "metadata": { - "id": "zpV7M-CLQJit" - } + ] }, { "cell_type": "markdown", + "metadata": { + "id": "f5lY3gziQNRe" + }, "source": [ "---\n", "\n", "## πŸ“¦ Step 1: Install Dependencies\n", "\n", "Before setting up our AI-powered flood monitor, let’s install the required VideoDB SDK." - ], - "metadata": { - "id": "f5lY3gziQNRe" - } + ] }, { "cell_type": "code", - "source": [ - "!pip install -q videodb" - ], + "execution_count": 1, "metadata": { - "id": "cD643idwQKJI", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "cD643idwQKJI", "outputId": "9c63f06d-d433-4347-ef9a-72a393f851cf" }, - "execution_count": 1, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", " Building wheel for videodb (setup.py) ... \u001b[?25l\u001b[?25hdone\n" ] } + ], + "source": [ + "!pip install -q videodb" ] }, { "cell_type": "markdown", + "metadata": { + "id": "JB_mg1kxQTcF" + }, "source": [ "---\n", "## πŸ“¦ Step 2: Connect to VideoDB\n", @@ -99,48 +88,48 @@ "Please enter your `VIDEO_DB_API_KEY` in the input box that appears below after you run this cell.\n", "\n", "Your input will be masked.\n" - ], - "metadata": { - "id": "JB_mg1kxQTcF" - } + ] }, { "cell_type": "code", - "source": [ - "import videodb\n", - "import os\n", - "from getpass import getpass\n", - "\n", - "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", - "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", - "\n", - "conn = videodb.connect()\n", - "coll = conn.get_collection()\n", - "\n", - "print(\"Connected to VideoDB securely!\")" - ], + "execution_count": 2, "metadata": { - "id": "ddqIQZteQWPj", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "ddqIQZteQWPj", "outputId": "6a4a57c1-c37b-4258-96eb-0cc9fad6d772" }, - "execution_count": 2, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Please enter your VideoDB API Key: Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·\n", "Connected to VideoDB securely!\n" ] } + ], + "source": [ + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "conn = videodb.connect()\n", + "coll = conn.get_collection()\n", + "\n", + "print(\"Connected to VideoDB securely!\")" ] }, { "cell_type": "markdown", + "metadata": { + "id": "GPfbBpinQbW0" + }, "source": [ "---\n", "\n", @@ -149,65 +138,48 @@ "Connect to the live camera stream monitoring a flood-prone desert area.\n", "\n", "In this demo, the stream is running at `rtsp://samples.rts.videodb.io:8554/floods`." - ], - "metadata": { - "id": "GPfbBpinQbW0" - } + ] }, { "cell_type": "code", - "source": [ - "rtsp_url = \"rtsp://samples.rts.videodb.io:8554/floods\"\n", - "flood_stream = coll.connect_rtstream(\n", - " name=\"Arizona Flood Stream\",\n", - " url=rtsp_url,\n", - ")\n", - "print(flood_stream)\n" - ], + "execution_count": null, "metadata": { - "id": "A7_zI06cQhwr", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "A7_zI06cQhwr", "outputId": "a1763469-b496-4934-9356-d103212f3b5c" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStream(id=rts-019719b2-0c84-7a71-a037-311855a8d160, name=Arizona Flood Stream, collection_id=None, created_at=None, sample_rate=30, status=connected)\n" ] } + ], + "source": [ + "rtsp_url = \"rtsp://samples.rts.videodb.io:8554/floods\"\n", + "flood_stream = coll.connect_rtstream(\n", + " name=\"Arizona Flood Stream\",\n", + " url=rtsp_url,\n", + ")\n", + "print(flood_stream)\n" ] }, { "cell_type": "markdown", - "source": [ - "#### Let us list all the rtstreams in our collection." - ], "metadata": { "id": "bdxspdrXsl9R" - } + }, + "source": [ + "#### Let us list all the rtstreams in our collection." + ] }, { "cell_type": "code", - "source": [ - "def list_rtstreams():\n", - " for rtstream in coll.list_rtstreams():\n", - " print(f\"\"\"RTStream:\n", - " ID : {rtstream.id}\n", - " Name : {rtstream.name}\n", - " Collection ID : {rtstream.collection_id}\n", - " Created At : {rtstream.created_at}\n", - " Sample Rate : {rtstream.sample_rate}\n", - " Status : {rtstream.status}\n", - " \"\"\")\n", - " print(\"-\" * 80)\n", - "\n", - "list_rtstreams()" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -215,11 +187,10 @@ "id": "gxtKJcrssmzJ", "outputId": "278db94e-e50b-486a-ae1f-46d0b04dd961" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStream:\n", " ID : rts-019719b2-0c84-7a71-a037-311855a8d160\n", @@ -259,93 +230,113 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "def list_rtstreams():\n", + " for rtstream in coll.list_rtstreams():\n", + " print(f\"\"\"RTStream:\n", + " ID : {rtstream.id}\n", + " Name : {rtstream.name}\n", + " Collection ID : {rtstream.collection_id}\n", + " Created At : {rtstream.created_at}\n", + " Sample Rate : {rtstream.sample_rate}\n", + " Status : {rtstream.status}\n", + " \"\"\")\n", + " print(\"-\" * 80)\n", + "\n", + "list_rtstreams()" ] }, { "cell_type": "markdown", + "metadata": { + "id": "DMpqfcWds2CY" + }, "source": [ "\n", "#### If you have already connected the stream, run the below cell with the **rtstream id** to reconnect." - ], - "metadata": { - "id": "DMpqfcWds2CY" - } + ] }, { "cell_type": "code", - "source": [ - "# flood_stream = coll.get_rtstream(\"\")" - ], + "execution_count": 3, "metadata": { "id": "dPPG2hsqs1Vv" }, - "execution_count": 3, - "outputs": [] + "outputs": [], + "source": [ + "# flood_stream = coll.get_rtstream(\"\")" + ] }, { "cell_type": "code", - "source": [ - "# To stop the stream\n", - "# flood_stream.stop()" - ], + "execution_count": null, "metadata": { "id": "NT0bnlhOs-pF" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To stop the stream\n", + "# flood_stream.stop()" + ] }, { "cell_type": "code", - "source": [ - "# To start the stream\n", - "# flood_stream.start()" - ], + "execution_count": 4, "metadata": { "id": "NmiNzKcfs_Na" }, - "execution_count": 4, - "outputs": [] + "outputs": [], + "source": [ + "# To start the stream\n", + "# flood_stream.start()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "KO8opDorQnpc" + }, "source": [ "---\n", "### πŸ‘€ Let's have a look at the riverbed" - ], - "metadata": { - "id": "KO8opDorQnpc" - } + ] }, { "cell_type": "markdown", - "source": [ - "\n", - "#### πŸ“Ί Helper Function: Display Video Stream\n", - "\n", - "This cell contains a small utility function to help visualize the video streams with helpful information. You don't need to modify this code." - ], "metadata": { "id": "z0BHpOB3tJgw" - } + }, + "source": [ + "#### πŸ“Ί Helper Functions: Search and Display\n", + "\n", + "This cell contains all the utility functions to search, fetch, and visualize video streams. You don't need to modify this code." + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "PpSSzPhMQqOE" + }, + "outputs": [], "source": [ "# To display the stream with relevant information\n", "\n", "from IPython.display import HTML\n", "import re\n", - "from datetime import datetime\n", + "import time\n", + "from datetime import datetime, UTC\n", "from videodb import play_stream\n", "\n", - "def display_stream(video_url, video_name=\"πŸŽ₯ Camera Feed\"):\n", "\n", - " match = re.search(r'/(\\d{16})-(\\d{16})\\.m3u8', video_url)\n", + "def display_stream(video_url, video_name=\"πŸŽ₯ Camera Feed\"):\n", + " match = re.search(r\"/(\\d{16})-(\\d{16})\\.m3u8\", video_url)\n", " if match:\n", " start_ts = int(match.group(1)) / 1e6\n", " end_ts = int(match.group(2)) / 1e6\n", - " start_time = datetime.utcfromtimestamp(start_ts).strftime('%Y-%m-%d %H:%M:%S')\n", - " end_time = datetime.utcfromtimestamp(end_ts).strftime('%Y-%m-%d %H:%M:%S')\n", + " start_time = datetime.fromtimestamp(start_ts, UTC).strftime(\"%Y-%m-%d %H:%M:%S\")\n", + " end_time = datetime.fromtimestamp(end_ts, UTC).strftime(\"%Y-%m-%d %H:%M:%S\")\n", " time_range = f\"{start_time} β†’ {end_time} UTC\"\n", " else:\n", " time_range = \"Time Unknown\"\n", @@ -354,65 +345,69 @@ "\n", " return HTML(f\"\"\"\n", "
\n", - " {video_player_html._repr_html_() if hasattr(video_player_html, '_repr_html_') else video_player_html}\n", + " {video_player_html._repr_html_() if hasattr(video_player_html, \"_repr_html_\") else video_player_html}\n", "
\n", " {video_name}
{time_range}\n", "
\n", "
\n", - " \"\"\")" - ], - "metadata": { - "id": "PpSSzPhMQqOE" - }, - "execution_count": 5, - "outputs": [] + " \"\"\")\n", + "\n", + "\n", + "# To dynamically set the display duration\n", + "\n", + "\n", + "def prompt_to_time(prompt):\n", + " now = int(time.time())\n", + " prompt = (\n", + " f\"It's {now} in epoch seconds. \"\n", + " f\"Convert the phrase '{prompt}' into JSON \"\n", + " f'with keys \"from\" and \"to\" (both epoch seconds)'\n", + " )\n", + "\n", + " result = coll.generate_text(\n", + " prompt=prompt,\n", + " model_name=\"pro\",\n", + " response_type=\"json\",\n", + " )\n", + " output = result.get(\"output\", {})\n", + " return output.get(\"from\"), output.get(\"to\")\n", + "\n", + "\n", + "# To fetch stream\n", + "\n", + "\n", + "def fetch_stream(rtstream):\n", + " _from, to = prompt_to_time(\"Show me last 5 mins\")\n", + " stream_url = rtstream.generate_stream(_from, to)\n", + " return stream_url\n" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "Kg5KSFhhtOQa" + }, "source": [ "\n", "#### πŸ”— Get & Display Recent Stream\n", "\n", - "This cell uses the helper function above to fetch and display the last few minutes of the stream." - ], - "metadata": { - "id": "Kg5KSFhhtOQa" - } + "This cell uses the helper functions above to fetch and display the last few minutes of the stream." + ] }, { "cell_type": "code", - "source": [ - "# To get last few minutes stream link\n", - "import time\n", - "\n", - "def fetch_stream(rtstream):\n", - "\n", - " now = int(time.time())\n", - " start = int(now - (5 * 60))\n", - " stream_url = rtstream.generate_stream(start, now)\n", - " return stream_url\n", - "\n", - "video_url = fetch_stream(flood_stream)\n", - "\n", - "video_name = \"🌊 Arizona Desert Β· Flash Flood Detection\"\n", - "display_stream(video_url , video_name)" - ], + "execution_count": null, "metadata": { - "id": "g5W0GEc_Qoo7", "colab": { "base_uri": "https://localhost:8080/", "height": 422 }, + "id": "g5W0GEc_Qoo7", "outputId": "65ae784e-58d9-416d-b0fc-8546997a2ad9" }, - "execution_count": 8, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - "" - ], "text/html": [ "\n", "
\n", @@ -431,15 +426,30 @@ "
\n", " \n", " " + ], + "text/plain": [ + "" ] }, + "execution_count": 8, "metadata": {}, - "execution_count": 8 + "output_type": "execute_result" } + ], + "source": [ + "# To get last few minutes stream link\n", + "\n", + "video_url = fetch_stream(flood_stream)\n", + "\n", + "video_name = \"🌊 Arizona Desert Β· Flash Flood Detection\"\n", + "display_stream(video_url , video_name)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "eVIEAJFKQvqW" + }, "source": [ "\n", "---\n", @@ -449,13 +459,27 @@ "We’ll create a real-time scene index that periodically analyzes video frames and generates natural language descriptions of what’s happening in the stream.\n", "\n", "The AI will look for sudden visual cues of water flooding dry land and describe them.\n" - ], - "metadata": { - "id": "eVIEAJFKQvqW" - } + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "CMU9WMlLQwvn", + "outputId": "eda5e049-ad57-477e-aaf0-c5d94947d7f7" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Scene Index ID: 318f52d1a4524559\n" + ] + } + ], "source": [ "from videodb import SceneExtractionType\n", "\n", @@ -470,54 +494,20 @@ ")\n", "flood_index_id = flood_scene_index.rtstream_index_id\n", "print(\"Scene Index ID:\", flood_index_id)\n" - ], - "metadata": { - "id": "CMU9WMlLQwvn", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "eda5e049-ad57-477e-aaf0-c5d94947d7f7" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Scene Index ID: 318f52d1a4524559\n" - ] - } ] }, { "cell_type": "markdown", - "source": [ - "#### Let us list the scene indexes created on our rtstream." - ], "metadata": { "id": "KjDNDgQAt1IW" - } + }, + "source": [ + "#### Let us list the scene indexes created on our rtstream." + ] }, { "cell_type": "code", - "source": [ - "def list_rtstream_indexes(rtstream):\n", - " # List live stream indexes\n", - " rtstream_indexes = rtstream.list_scene_indexes()\n", - " for rtstream_index in rtstream_indexes:\n", - "\n", - " print(f\"\"\"RTStreamSceneIndex:\n", - " Index ID : {rtstream_index.rtstream_index_id}\n", - " RTStream ID : {rtstream_index.rtstream_id}\n", - " Name : {rtstream_index.name}\n", - " Status : {rtstream_index.status}\n", - " Config : {rtstream_index.extraction_config}\n", - " Prompt : {rtstream_index.prompt}\n", - " \"\"\")\n", - " print(\"-\" * 80)\n", - "\n", - "list_rtstream_indexes(flood_stream)" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -525,11 +515,10 @@ "id": "Mcies-gct12A", "outputId": "21da65ef-62a3-43a7-b3ab-20c0eefaeccf" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStreamSceneIndex:\n", " Index ID : 318f52d1a4524559\n", @@ -542,108 +531,96 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "def list_rtstream_indexes(rtstream):\n", + " # List live stream indexes\n", + " rtstream_indexes = rtstream.list_scene_indexes()\n", + " for rtstream_index in rtstream_indexes:\n", + "\n", + " print(f\"\"\"RTStreamSceneIndex:\n", + " Index ID : {rtstream_index.rtstream_index_id}\n", + " RTStream ID : {rtstream_index.rtstream_id}\n", + " Name : {rtstream_index.name}\n", + " Status : {rtstream_index.status}\n", + " Config : {rtstream_index.extraction_config}\n", + " Prompt : {rtstream_index.prompt}\n", + " \"\"\")\n", + " print(\"-\" * 80)\n", + "\n", + "list_rtstream_indexes(flood_stream)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "abtAiqYRt8tm" + }, "source": [ "\n", "#### If you have already created a scene index, run the below cell with your **scene index id** to reconnect." - ], - "metadata": { - "id": "abtAiqYRt8tm" - } + ] }, { "cell_type": "code", - "source": [ - "# flood_index_id = \"\"\n", - "# flood_scene_index = flood_stream.get_scene_index(flood_index_id)" - ], + "execution_count": null, "metadata": { "id": "SiabmNgrt9al" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# flood_index_id = \"\"\n", + "# flood_scene_index = flood_stream.get_scene_index(flood_index_id)" + ] }, { "cell_type": "code", - "source": [ - "# To stop the index\n", - "# flood_scene_index.stop()" - ], + "execution_count": null, "metadata": { "id": "oTJX-adPt9vE" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To stop the index\n", + "# flood_scene_index.stop()" + ] }, { "cell_type": "code", - "source": [ - "# To start the index\n", - "# flood_scene_index.start()" - ], + "execution_count": null, "metadata": { "id": "fj8NNH7nt-B9" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To start the index\n", + "# flood_scene_index.start()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "yRZmI0IvQ0FF" + }, "source": [ "---\n", "### Let us see the result of the scene indexing" - ], - "metadata": { - "id": "yRZmI0IvQ0FF" - } + ] }, { - "cell_type": "code", - "source": [ - "import time\n", - "from datetime import datetime\n", - "from zoneinfo import ZoneInfo\n", - "\n", - "def _convert_to_ist(timestamp: float) -> str:\n", - " \"\"\"Convert UTC timestamp to IST (Asia/Kolkata) datetime string.\"\"\"\n", - " return (\n", - " datetime.fromtimestamp(timestamp)\n", - " .astimezone(ZoneInfo(\"Asia/Kolkata\"))\n", - " .strftime(\"%Y-%m-%d %H:%M:%S\")\n", - " )\n", - "\n", - "def get_scenes(rtstream, index_id):\n", - " # Print indexed scenes\n", - " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", - " scenes = rtstream_scene_index.get_scenes(page_size=5)\n", - " # print(scenes[\"scenes\"][:2])\n", - " if scenes:\n", - " for scene in scenes.get(\"scenes\"):\n", - " start = _convert_to_ist(scene[\"start\"])\n", - " end = _convert_to_ist(scene[\"end\"])\n", - " description = scene[\"description\"]\n", - " print(f\"{start}-{end}: {description}\")\n", - " print(\"-\" * 80)\n", - " else:\n", - " print(\"Scenes not found for given index.\")\n", - "\n", - "get_scenes(flood_stream , flood_index_id)" - ], + "cell_type": "code", + "execution_count": null, "metadata": { - "id": "XUa08hPnQ0Zw", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "XUa08hPnQ0Zw", "outputId": "ef8fc402-aa98-4dcd-a58b-f3d59fcc18d7" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "2025-05-29 07:17:51-2025-05-29 07:17:56: **Flash Flood Detected**\n", "\n", @@ -667,96 +644,128 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "import time\n", + "from datetime import datetime\n", + "from zoneinfo import ZoneInfo\n", + "\n", + "def _convert_to_ist(timestamp: float) -> str:\n", + " \"\"\"Convert UTC timestamp to IST (Asia/Kolkata) datetime string.\"\"\"\n", + " return (\n", + " datetime.fromtimestamp(timestamp)\n", + " .astimezone(ZoneInfo(\"Asia/Kolkata\"))\n", + " .strftime(\"%Y-%m-%d %H:%M:%S\")\n", + " )\n", + "\n", + "def get_scenes(rtstream, index_id):\n", + " # Print indexed scenes\n", + " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", + " scenes = rtstream_scene_index.get_scenes(page_size=5)\n", + " if scenes:\n", + " for scene in scenes.get(\"scenes\"):\n", + " start = _convert_to_ist(scene[\"start\"])\n", + " end = _convert_to_ist(scene[\"end\"])\n", + " description = scene[\"description\"]\n", + " print(f\"{start}-{end}: {description}\")\n", + " print(\"-\" * 80)\n", + " else:\n", + " print(\"Scenes not found for given index.\")\n", + "\n", + "get_scenes(flood_stream , flood_index_id)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "-kO4RguhRNbk" + }, "source": [ "---\n", "\n", "## πŸ“¦ Step 5: Define a Flash Flood Event\n", "\n", "Now, we’ll define an event type in the system to detect visual signs of a flash flood.\n" - ], - "metadata": { - "id": "-kO4RguhRNbk" - } + ] }, { "cell_type": "code", - "source": [ - "flood_event_id = conn.create_event(\n", - " event_prompt=\"Detect sudden flash floods or water surges.\",\n", - " label=\"flash_flood\"\n", - ")\n", - "print(\"Event ID:\", flood_event_id)\n" - ], + "execution_count": null, "metadata": { - "id": "VxTWmd_hROVQ", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "VxTWmd_hROVQ", "outputId": "0b63a4f4-af21-4b69-c266-325d2caf69f6" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Event ID: 7929a3ecc4624dbb\n" ] } + ], + "source": [ + "flood_event_id = conn.create_event(\n", + " event_prompt=\"Detect sudden flash floods or water surges.\",\n", + " label=\"flash_flood\"\n", + ")\n", + "print(\"Event ID:\", flood_event_id)\n" ] }, { "cell_type": "markdown", + "metadata": { + "id": "bRf6QOfrRQa2" + }, "source": [ "---\n", "\n", "## πŸ“¦ Step 6: Attach an Alert to the Flash Flood Event\n", "\n", "Finally, we’ll link a real-time alert to this event, which will send a notification to our webhook the moment a flash flood is detected.\n" - ], - "metadata": { - "id": "bRf6QOfrRQa2" - } + ] }, { "cell_type": "code", - "source": [ - "# Enter link to your webhook url where you want alerts to go. You can create one simply on pipedream.\n", - "webhook_url = \"\"\n", - "\n", - "if webhook_url:\n", - " flood_alert_id = flood_scene_index.create_alert(\n", - " flood_event_id,\n", - " callback_url=webhook_url\n", - " )\n", - " print(\"Alert ID:\", flood_alert_id)\n", - "else:\n", - " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" - ], + "execution_count": null, "metadata": { - "id": "RftcmJUPRRmG", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "RftcmJUPRRmG", "outputId": "1bb4b438-80d7-4282-ff4d-850df1a17155" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Alert ID: d9c6038e631071cd\n" ] } + ], + "source": [ + "# Enter link to your webhook url where you want alerts to go. You can create one simply on pipedream.\n", + "webhook_url = \"\"\n", + "\n", + "if webhook_url:\n", + " flood_alert_id = flood_scene_index.create_alert(\n", + " flood_event_id,\n", + " callback_url=webhook_url\n", + " )\n", + " print(\"Alert ID:\", flood_alert_id)\n", + "else:\n", + " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" ] }, { "cell_type": "markdown", + "metadata": { + "id": "o_u8OYaWRTbP" + }, "source": [ "---\n", "\n", @@ -776,19 +785,11 @@ " \"stream_url\": \"https://rt.stream.videodb.io/manifests/rts-019719b2-0c84-7a71-a037-311855a8d160/1748483434000000-1748483440000000.m3u8\"\n", "}\n", "```" - ], - "metadata": { - "id": "o_u8OYaWRTbP" - } + ] }, { "cell_type": "code", - "source": [ - "alert_stream_url = \"https://rt.stream.videodb.io/manifests/rts-019719b2-0c84-7a71-a037-311855a8d160/1748483434000000-1748483440000000.m3u8\"\n", - "video_name = \"🌊 Arizona Desert Β· flash_flood\"\n", - "\n", - "display_stream(alert_stream_url, video_name)" - ], + "execution_count": 6, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -797,14 +798,9 @@ "id": "tA8WGWzUvPQ1", "outputId": "bc690012-a143-4263-dfa9-a4f549d85756" }, - "execution_count": 6, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - "" - ], "text/html": [ "\n", "
\n", @@ -823,76 +819,89 @@ "
\n", " \n", " " + ], + "text/plain": [ + "" ] }, + "execution_count": 6, "metadata": {}, - "execution_count": 6 + "output_type": "execute_result" } + ], + "source": [ + "alert_stream_url = \"https://rt.stream.videodb.io/manifests/rts-019719b2-0c84-7a71-a037-311855a8d160/1748483434000000-1748483440000000.m3u8\"\n", + "video_name = \"🌊 Arizona Desert Β· flash_flood\"\n", + "\n", + "display_stream(alert_stream_url, video_name)" ] }, { "cell_type": "markdown", - "source": [ - "#### Let us stop the flood index and proceed with the notebook to explore more possibilities." - ], "metadata": { "id": "HuxU7xQTxBZ1" - } + }, + "source": [ + "#### Let us stop the flood index and proceed with the notebook to explore more possibilities." + ] }, { "cell_type": "code", - "source": [ - "# flood_scene_index.stop()" - ], + "execution_count": null, "metadata": { "id": "KcknjmPPxAzw" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# flood_scene_index.stop()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "9A29QLMZ9d44" + }, "source": [ "---\n", "- Let us disable the alert now." - ], - "metadata": { - "id": "9A29QLMZ9d44" - } + ] }, { "cell_type": "code", - "source": [ - "flood_scene_index.disable_alert(flood_alert_id)" - ], + "execution_count": null, "metadata": { "id": "xM6BA96q9kAZ" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "flood_scene_index.disable_alert(flood_alert_id)" + ] }, { "cell_type": "markdown", - "source": [ - "- To enable the alert again" - ], "metadata": { "id": "6e-d57Z49hY7" - } + }, + "source": [ + "- To enable the alert again" + ] }, { "cell_type": "code", - "source": [ - "flood_scene_index.enable_alert(flood_alert_id)" - ], + "execution_count": null, "metadata": { "id": "KC7tUVqw9kVT" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "flood_scene_index.enable_alert(flood_alert_id)" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "KwcEwZygRYZs" + }, "source": [ "---\n", "### Let us set up some other alerts that are necessary\n", @@ -900,58 +909,53 @@ "2. Detect the presence of a person stuck in the flash flood, for immediate rescue \n", "\n", "We can start with creating a new scene index for monitoring rainfall or a person stuck in the place." - ], - "metadata": { - "id": "KwcEwZygRYZs" - } + ] }, { "cell_type": "code", - "source": [ - "riverbed_monitoring_scene_index = flood_stream.index_scenes(\n", - " extraction_type=SceneExtractionType.time_based,\n", - " extraction_config={\n", - " \"time\": 15,\n", - " \"frame_count\": 1,\n", - " },\n", - " prompt=\"Monitor the dry riverbed and surrounding area. In case you detect heavy rainfall mention 'heavy rainfall detected'. If you detect a person stuck in the area during rainfall or flash flood mention 'person detected, rescue needed'\",\n", - " name=\"Riverbed_Monitoring_Index\"\n", - ")\n", - "riverbed_monitoring_index_id = riverbed_monitoring_scene_index.rtstream_index_id\n", - "print(\"Scene Index ID:\", riverbed_monitoring_index_id)\n" - ], + "execution_count": null, "metadata": { - "id": "GX8L7BkyRU-b", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "GX8L7BkyRU-b", "outputId": "855703dd-b7cd-43b9-c63a-c0089bc2e7d7" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Scene Index ID: a64531c43284cc99\n" ] } + ], + "source": [ + "riverbed_monitoring_scene_index = flood_stream.index_scenes(\n", + " extraction_type=SceneExtractionType.time_based,\n", + " extraction_config={\n", + " \"time\": 15,\n", + " \"frame_count\": 1,\n", + " },\n", + " prompt=\"Monitor the dry riverbed and surrounding area. In case you detect heavy rainfall mention 'heavy rainfall detected'. If you detect a person stuck in the area during rainfall or flash flood mention 'person detected, rescue needed'\",\n", + " name=\"Riverbed_Monitoring_Index\"\n", + ")\n", + "riverbed_monitoring_index_id = riverbed_monitoring_scene_index.rtstream_index_id\n", + "print(\"Scene Index ID:\", riverbed_monitoring_index_id)\n" ] }, { "cell_type": "markdown", - "source": [ - "#### Checking the list of indexes" - ], "metadata": { "id": "914lMw6PxQDu" - } + }, + "source": [ + "#### Checking the list of indexes" + ] }, { "cell_type": "code", - "source": [ - "list_rtstream_indexes(flood_stream)" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -959,11 +963,10 @@ "id": "UXuau8gPxguR", "outputId": "a6a243cc-2f82-4168-fbfe-285d401da947" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStreamSceneIndex:\n", " Index ID : 318f52d1a4524559\n", @@ -985,35 +988,35 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "list_rtstream_indexes(flood_stream)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "FFoIxQO1Rbxs" + }, "source": [ "---\n", "### Let's have a look at generated scenes" - ], - "metadata": { - "id": "FFoIxQO1Rbxs" - } + ] }, { "cell_type": "code", - "source": [ - "get_scenes(flood_stream , riverbed_monitoring_index_id)" - ], + "execution_count": null, "metadata": { - "id": "3hfHascHRcw2", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "3hfHascHRcw2", "outputId": "6147fb2d-8723-40ec-9bae-826f8c3c005c" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "2025-05-29 07:34:50-2025-05-29 07:35:05: heavy rainfall detected\n", "--------------------------------------------------------------------------------\n", @@ -1027,22 +1030,42 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "get_scenes(flood_stream , riverbed_monitoring_index_id)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "--HEDr5WRe69" + }, "source": [ "---\n", "### Now we can setup events and alerts for the index\n", "\n", "1. Rainfall Detection" - ], - "metadata": { - "id": "--HEDr5WRe69" - } + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "jf156IF7RgQf", + "outputId": "f128fcd8-4fd9-4a9a-e680-a1067c586ea1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Event ID: df1611d13772e09b\n" + ] + } + ], "source": [ "# Create rainfall event\n", "rainfall_event_id = conn.create_event(\n", @@ -1050,27 +1073,27 @@ " label=\"heavy_rainfall\"\n", ")\n", "print(\"Event ID:\", rainfall_event_id)" - ], + ] + }, + { + "cell_type": "code", + "execution_count": null, "metadata": { - "id": "jf156IF7RgQf", "colab": { "base_uri": "https://localhost:8080/" }, - "outputId": "f128fcd8-4fd9-4a9a-e680-a1067c586ea1" + "id": "Z9EnhfkOyvlg", + "outputId": "f3a54042-2aec-4c06-9627-2e9ee9db88e4" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ - "Event ID: df1611d13772e09b\n" + "Alert ID: ae31ea7c92a61bc7\n" ] } - ] - }, - { - "cell_type": "code", + ], "source": [ "# Create rainfall alert\n", "\n", @@ -1085,77 +1108,49 @@ " print(\"Alert ID:\", rainfall_alert_id)\n", "else:\n", " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" - ], - "metadata": { - "colab": { - "base_uri": "https://localhost:8080/" - }, - "id": "Z9EnhfkOyvlg", - "outputId": "f3a54042-2aec-4c06-9627-2e9ee9db88e4" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Alert ID: ae31ea7c92a61bc7\n" - ] - } ] }, { "cell_type": "markdown", + "metadata": { + "id": "hEAftnXnRiTd" + }, "source": [ "---\n", "2. Human detection for rescue" - ], - "metadata": { - "id": "hEAftnXnRiTd" - } + ] }, { "cell_type": "code", - "source": [ - "# Create rescue event\n", - "rescue_event_id = conn.create_event(\n", - " event_prompt=\"Detect if there is a person\",\n", - " label=\"human_rescue\"\n", - ")\n", - "print(\"Event ID:\", rescue_event_id)" - ], + "execution_count": null, "metadata": { - "id": "7lSfgMwNRwJZ", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "7lSfgMwNRwJZ", "outputId": "a6bda8fb-1890-4492-e9ce-5d951f1c3194" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Event ID: 717a1bcf7a705ec7\n" ] } + ], + "source": [ + "# Create rescue event\n", + "rescue_event_id = conn.create_event(\n", + " event_prompt=\"Detect if there is a person\",\n", + " label=\"human_rescue\"\n", + ")\n", + "print(\"Event ID:\", rescue_event_id)" ] }, { "cell_type": "code", - "source": [ - "# Create rescue alert\n", - "\n", - "# Enter link to your webhook url where you want alerts to go.\n", - "rescue_webhook_url = \"\"\n", - "\n", - "rescue_alert_id = riverbed_monitoring_scene_index.create_alert(\n", - " rescue_event_id,\n", - " callback_url=rescue_webhook_url\n", - ")\n", - "print(\"Alert ID:\", rescue_alert_id)" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -1163,49 +1158,41 @@ "id": "smMe1bn_y_5q", "outputId": "6465cfb6-a1bc-44de-fab7-9ccfe01b3f9c" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Alert ID: 766872338353f95b\n" ] } + ], + "source": [ + "# Create rescue alert\n", + "\n", + "# Enter link to your webhook url where you want alerts to go.\n", + "rescue_webhook_url = \"\"\n", + "\n", + "rescue_alert_id = riverbed_monitoring_scene_index.create_alert(\n", + " rescue_event_id,\n", + " callback_url=rescue_webhook_url\n", + ")\n", + "print(\"Alert ID:\", rescue_alert_id)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "AzmR7GD-zabA" + }, "source": [ "---\n", "### Let us see the list of alerts associated with the `riverbed_monitoring_scene_index`" - ], - "metadata": { - "id": "AzmR7GD-zabA" - } + ] }, { "cell_type": "code", - "source": [ - "def list_rtstream_alerts(rtstream, index_id):\n", - " \"\"\"\n", - " Prints a list of alerts associated with a given scene index.\n", - " \"\"\"\n", - " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", - " alerts = rtstream_scene_index.list_alerts()\n", - "\n", - " for alert in alerts:\n", - " print(f\"\"\"πŸ”” RTStream Alert:\n", - " Alert ID : {alert['alert_id']}\n", - " Event ID : {alert['event_id']}\n", - " Label : {alert['label']}\n", - " Prompt : {alert['prompt']}\n", - " Status : {alert['status']}\n", - " \"\"\")\n", - " print(\"-\" * 80)\n", - "\n", - "list_rtstream_alerts(flood_stream, riverbed_monitoring_index_id)\n" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -1213,11 +1200,10 @@ "id": "CNH-aKZizr8B", "outputId": "05d5647b-b620-4cd6-add9-881da6baade1" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "πŸ”” RTStream Alert:\n", " Alert ID : 766872338353f95b\n", @@ -1237,10 +1223,33 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "def list_rtstream_alerts(rtstream, index_id):\n", + " \"\"\"\n", + " Prints a list of alerts associated with a given scene index.\n", + " \"\"\"\n", + " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", + " alerts = rtstream_scene_index.list_alerts()\n", + "\n", + " for alert in alerts:\n", + " print(f\"\"\"πŸ”” RTStream Alert:\n", + " Alert ID : {alert['alert_id']}\n", + " Event ID : {alert['event_id']}\n", + " Label : {alert['label']}\n", + " Prompt : {alert['prompt']}\n", + " Status : {alert['status']}\n", + " \"\"\")\n", + " print(\"-\" * 80)\n", + "\n", + "list_rtstream_alerts(flood_stream, riverbed_monitoring_index_id)\n" ] }, { "cell_type": "markdown", + "metadata": { + "id": "DtGcRljD0UYM" + }, "source": [ "---\n", "\n", @@ -1260,19 +1269,11 @@ " \"stream_url\": \"https://rt.stream.videodb.io/manifests/rts-019719b2-0c84-7a71-a037-311855a8d160/1748485189000000-1748485205000000.m3u8\"\n", "}\n", "```" - ], - "metadata": { - "id": "DtGcRljD0UYM" - } + ] }, { "cell_type": "code", - "source": [ - "alert_stream_url = \"https://rt.stream.videodb.io/manifests/rts-019719b2-0c84-7a71-a037-311855a8d160/1748485189000000-1748485205000000.m3u8\"\n", - "video_name = \"🌊 Arizona Desert Β· human_rescue\"\n", - "\n", - "display_stream(alert_stream_url, video_name)" - ], + "execution_count": 7, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -1281,14 +1282,9 @@ "id": "MZ1LBQ3E1dcV", "outputId": "20198543-639f-4034-ee25-ceaa8272b018" }, - "execution_count": 7, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - "" - ], "text/html": [ "\n", "
\n", @@ -1307,78 +1303,91 @@ "
\n", " \n", " " + ], + "text/plain": [ + "" ] }, + "execution_count": 7, "metadata": {}, - "execution_count": 7 + "output_type": "execute_result" } + ], + "source": [ + "alert_stream_url = \"https://rt.stream.videodb.io/manifests/rts-019719b2-0c84-7a71-a037-311855a8d160/1748485189000000-1748485205000000.m3u8\"\n", + "video_name = \"🌊 Arizona Desert Β· human_rescue\"\n", + "\n", + "display_stream(alert_stream_url, video_name)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "2Q_xgt7790wZ" + }, "source": [ "---\n", "- Let us disable the alerts now." - ], - "metadata": { - "id": "2Q_xgt7790wZ" - } + ] }, { "cell_type": "code", - "source": [ - "riverbed_monitoring_scene_index.disable_alert(rainfall_alert_id)\n", - "riverbed_monitoring_scene_index.disable_alert(rescue_alert_id)" - ], + "execution_count": null, "metadata": { "id": "ZO8vF3-W951R" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "riverbed_monitoring_scene_index.disable_alert(rainfall_alert_id)\n", + "riverbed_monitoring_scene_index.disable_alert(rescue_alert_id)" + ] }, { "cell_type": "markdown", - "source": [ - "- To enable the alert again" - ], "metadata": { "id": "TnDcrHaV93jR" - } + }, + "source": [ + "- To enable the alert again" + ] }, { "cell_type": "code", - "source": [ - "riverbed_monitoring_scene_index.enable_alert(rainfall_alert_id)\n", - "riverbed_monitoring_scene_index.enable_alert(rescue_alert_id)" - ], + "execution_count": null, "metadata": { "id": "8QR3uW6x96MS" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "riverbed_monitoring_scene_index.enable_alert(rainfall_alert_id)\n", + "riverbed_monitoring_scene_index.enable_alert(rescue_alert_id)" + ] }, { "cell_type": "markdown", - "source": [ - "- Now let us stop the stream" - ], "metadata": { "id": "03jdVUVa-xoA" - } + }, + "source": [ + "- Now let us stop the stream" + ] }, { "cell_type": "code", - "source": [ - "flood_stream.stop()" - ], + "execution_count": null, "metadata": { "id": "K_3-Dw0v-xFY" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "flood_stream.stop()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "BwE1fRaoR4BL" + }, "source": [ "---\n", "\n", @@ -1447,10 +1456,21 @@ "Real-time AI video monitoring isn’t just for cities and homes β€” it can actively save lives in wild, unpredictable environments too.\n", "\n", "**What natural threat would *you* monitor next?**" - ], - "metadata": { - "id": "BwE1fRaoR4BL" - } + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" } - ] -} \ No newline at end of file + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/real_time_streaming/Intrusion_Detection.ipynb b/real_time_streaming/Intrusion_Detection.ipynb index 6a2c251..8084c2e 100644 --- a/real_time_streaming/Intrusion_Detection.ipynb +++ b/real_time_streaming/Intrusion_Detection.ipynb @@ -1,21 +1,10 @@ { - "nbformat": 4, - "nbformat_minor": 0, - "metadata": { - "colab": { - "provenance": [] - }, - "kernelspec": { - "name": "python3", - "display_name": "Python 3" - }, - "language_info": { - "name": "python" - } - }, "cells": [ { "cell_type": "markdown", + "metadata": { + "id": "Fsebg6qWNsIQ" + }, "source": [ "# 🏠 Smart Intruder Detection for Property Surveillance using VideoDB RTStream\n", "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/video-db/videodb-cookbook/blob/main/real_time_streaming/Intrusion_Detection.ipynb)\n", @@ -46,50 +35,50 @@ "- 🚨 Trigger separate real-time alerts for each level to a webhook\n", "\n", "Let’s build it!" - ], - "metadata": { - "id": "Fsebg6qWNsIQ" - } + ] }, { "cell_type": "markdown", + "metadata": { + "id": "tMOTjoKCNx4m" + }, "source": [ "---\n", "\n", "## πŸ“¦ Step 1: Install Dependencies\n", "\n", "We’ll begin by installing the VideoDB SDK" - ], - "metadata": { - "id": "tMOTjoKCNx4m" - } + ] }, { "cell_type": "code", - "source": [ - "!pip install -q videodb" - ], + "execution_count": 1, "metadata": { - "id": "VWIKa3mWNwSr", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "VWIKa3mWNwSr", "outputId": "ab516051-3cf0-4aba-f502-83a157732866" }, - "execution_count": 1, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", " Building wheel for videodb (setup.py) ... \u001b[?25l\u001b[?25hdone\n" ] } + ], + "source": [ + "!pip install -q videodb" ] }, { "cell_type": "markdown", + "metadata": { + "id": "ADeNXFVGN1g_" + }, "source": [ "---\n", "## πŸ“¦ Step 2: Connect to VideoDB\n", @@ -99,48 +88,48 @@ "Please enter your `VIDEO_DB_API_KEY` in the input box that appears below after you run this cell.\n", "\n", "Your input will be masked.\n" - ], - "metadata": { - "id": "ADeNXFVGN1g_" - } + ] }, { "cell_type": "code", - "source": [ - "import videodb\n", - "import os\n", - "from getpass import getpass\n", - "\n", - "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", - "\n", - "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", - "\n", - "conn = videodb.connect()\n", - "coll = conn.get_collection()\n", - "\n", - "print(\"Connected to VideoDB securely!\")" - ], + "execution_count": 2, "metadata": { - "id": "HX-5_YF_N2mK", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "HX-5_YF_N2mK", "outputId": "dcbfc051-8c74-4836-aa92-4b495a93cf95" }, - "execution_count": 2, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Please enter your VideoDB API Key: Β·Β·Β·Β·Β·Β·Β·Β·Β·Β·\n", "Connected to VideoDB securely!\n" ] } + ], + "source": [ + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "conn = videodb.connect()\n", + "coll = conn.get_collection()\n", + "\n", + "print(\"Connected to VideoDB securely!\")" ] }, { "cell_type": "markdown", + "metadata": { + "id": "4zMEeRBaN7Zm" + }, "source": [ "---\n", "\n", @@ -149,65 +138,48 @@ "We’ll connect to the live video stream of your property’s security camera.\n", "\n", "In this demo, the stream is running at `rtsp://samples.rts.videodb.io:8554/intruder`.\n" - ], - "metadata": { - "id": "4zMEeRBaN7Zm" - } + ] }, { "cell_type": "code", - "source": [ - "rtsp_url = \"rtsp://samples.rts.videodb.io:8554/intruder\"\n", - "intruder_stream = coll.connect_rtstream(\n", - " name=\"Property Security Stream\",\n", - " url=rtsp_url,\n", - ")\n", - "print(intruder_stream)" - ], + "execution_count": null, "metadata": { - "id": "DCXckXELOABN", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "DCXckXELOABN", "outputId": "dd7a27df-6d66-49b9-f918-92be75ef77ee" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStream(id=rts-019710fa-9511-79c3-a924-e229e4815410, name=Property Security Stream, collection_id=None, created_at=None, sample_rate=30, status=connected)\n" ] } + ], + "source": [ + "rtsp_url = \"rtsp://samples.rts.videodb.io:8554/intruder\"\n", + "intruder_stream = coll.connect_rtstream(\n", + " name=\"Property Security Stream\",\n", + " url=rtsp_url,\n", + ")\n", + "print(intruder_stream)" ] }, { "cell_type": "markdown", - "source": [ - "#### Let us list all the rtstreams in our collection." - ], "metadata": { "id": "Uek0S-lYiagX" - } + }, + "source": [ + "#### Let us list all the rtstreams in our collection." + ] }, { "cell_type": "code", - "source": [ - "def list_rtstreams():\n", - " for rtstream in coll.list_rtstreams():\n", - " print(f\"\"\"RTStream:\n", - " ID : {rtstream.id}\n", - " Name : {rtstream.name}\n", - " Collection ID : {rtstream.collection_id}\n", - " Created At : {rtstream.created_at}\n", - " Sample Rate : {rtstream.sample_rate}\n", - " Status : {rtstream.status}\n", - " \"\"\")\n", - " print(\"-\" * 80)\n", - "\n", - "list_rtstreams()" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -215,11 +187,10 @@ "id": "_LOUfC9CieDc", "outputId": "2f6d3d99-bfed-421c-a629-eadde1a65108" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStream:\n", " ID : rts-019711db-1086-7750-ba79-8f47a4fed603\n", @@ -250,93 +221,113 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "def list_rtstreams():\n", + " for rtstream in coll.list_rtstreams():\n", + " print(f\"\"\"RTStream:\n", + " ID : {rtstream.id}\n", + " Name : {rtstream.name}\n", + " Collection ID : {rtstream.collection_id}\n", + " Created At : {rtstream.created_at}\n", + " Sample Rate : {rtstream.sample_rate}\n", + " Status : {rtstream.status}\n", + " \"\"\")\n", + " print(\"-\" * 80)\n", + "\n", + "list_rtstreams()" ] }, { "cell_type": "markdown", + "metadata": { + "id": "m_GqDzuF_fmI" + }, "source": [ "\n", "#### If you have already connected the stream, run the below cell with the **rtstream id** to reconnect." - ], - "metadata": { - "id": "m_GqDzuF_fmI" - } + ] }, { "cell_type": "code", - "source": [ - "# intruder_stream = coll.get_rtstream(\"\")" - ], + "execution_count": 3, "metadata": { "id": "qnOll9oS_gnJ" }, - "execution_count": 3, - "outputs": [] + "outputs": [], + "source": [ + "# intruder_stream = coll.get_rtstream(\"\")" + ] }, { "cell_type": "code", - "source": [ - "# To stop the stream\n", - "# intruder_stream.stop()" - ], + "execution_count": null, "metadata": { "id": "gHAqL2LzBMT1" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To stop the stream\n", + "# intruder_stream.stop()" + ] }, { "cell_type": "code", - "source": [ - "# To start the stream\n", - "# intruder_stream.start()" - ], + "execution_count": 4, "metadata": { "id": "1c0QNS-tBKpX" }, - "execution_count": 4, - "outputs": [] + "outputs": [], + "source": [ + "# To start the stream\n", + "# intruder_stream.start()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "KcQmjyuFOMZ-" + }, "source": [ "---\n", "### πŸ‘€ Let us have a look at the camera feed" - ], - "metadata": { - "id": "KcQmjyuFOMZ-" - } + ] }, { "cell_type": "markdown", - "source": [ - "\n", - "#### πŸ“Ί Helper Function: Display Video Stream\n", - "\n", - "This cell contains a small utility function to help visualize the video streams with helpful information. You don't need to modify this code." - ], "metadata": { "id": "soVJxcNOB3R-" - } + }, + "source": [ + "#### πŸ“Ί Helper Functions: Search and Display\n", + "\n", + "This cell contains all the utility functions to search, fetch, and visualize video streams. You don't need to modify this code." + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "n138fEwKOSSF" + }, + "outputs": [], "source": [ "# To display the stream with relevant information\n", "\n", "from IPython.display import HTML\n", "import re\n", - "from datetime import datetime\n", + "import time\n", + "from datetime import datetime, UTC\n", "from videodb import play_stream\n", "\n", - "def display_stream(video_url, video_name=\"πŸŽ₯ Camera Feed\"):\n", "\n", - " match = re.search(r'/(\\d{16})-(\\d{16})\\.m3u8', video_url)\n", + "def display_stream(video_url, video_name=\"πŸŽ₯ Camera Feed\"):\n", + " match = re.search(r\"/(\\d{16})-(\\d{16})\\.m3u8\", video_url)\n", " if match:\n", " start_ts = int(match.group(1)) / 1e6\n", " end_ts = int(match.group(2)) / 1e6\n", - " start_time = datetime.utcfromtimestamp(start_ts).strftime('%Y-%m-%d %H:%M:%S')\n", - " end_time = datetime.utcfromtimestamp(end_ts).strftime('%Y-%m-%d %H:%M:%S')\n", + " start_time = datetime.fromtimestamp(start_ts, UTC).strftime(\"%Y-%m-%d %H:%M:%S\")\n", + " end_time = datetime.fromtimestamp(end_ts, UTC).strftime(\"%Y-%m-%d %H:%M:%S\")\n", " time_range = f\"{start_time} β†’ {end_time} UTC\"\n", " else:\n", " time_range = \"Time Unknown\"\n", @@ -345,65 +336,69 @@ "\n", " return HTML(f\"\"\"\n", "
\n", - " {video_player_html._repr_html_() if hasattr(video_player_html, '_repr_html_') else video_player_html}\n", + " {video_player_html._repr_html_() if hasattr(video_player_html, \"_repr_html_\") else video_player_html}\n", "
\n", " {video_name}
{time_range}\n", "
\n", "
\n", - " \"\"\")" - ], - "metadata": { - "id": "n138fEwKOSSF" - }, - "execution_count": 9, - "outputs": [] + " \"\"\")\n", + "\n", + "\n", + "# To dynamically set the display duration\n", + "\n", + "\n", + "def prompt_to_time(prompt):\n", + " now = int(time.time())\n", + " prompt = (\n", + " f\"It's {now} in epoch seconds. \"\n", + " f\"Convert the phrase '{prompt}' into JSON \"\n", + " f'with keys \"from\" and \"to\" (both epoch seconds)'\n", + " )\n", + "\n", + " result = coll.generate_text(\n", + " prompt=prompt,\n", + " model_name=\"pro\",\n", + " response_type=\"json\",\n", + " )\n", + " output = result.get(\"output\", {})\n", + " return output.get(\"from\"), output.get(\"to\")\n", + "\n", + "\n", + "# To fetch stream\n", + "\n", + "\n", + "def fetch_stream(rtstream):\n", + " _from, to = prompt_to_time(\"Show me last 5 mins\")\n", + " stream_url = rtstream.generate_stream(_from, to)\n", + " return stream_url" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "Y3ZVVG2xB8W4" + }, "source": [ "\n", "#### πŸ”— Get & Display Recent Stream\n", "\n", - "This cell uses the helper function above to fetch and display the last few minutes of the stream." - ], - "metadata": { - "id": "Y3ZVVG2xB8W4" - } + "This cell uses the helper functions above to fetch and display the last few minutes of the stream." + ] }, { "cell_type": "code", - "source": [ - "# To get last few minutes stream link\n", - "import time\n", - "\n", - "def fetch_stream(rtstream):\n", - "\n", - " now = int(time.time())\n", - " start = int(now - (5 * 60))\n", - " stream_url = rtstream.generate_stream(start, now)\n", - " return stream_url\n", - "\n", - "video_url = fetch_stream(intruder_stream)\n", - "\n", - "video_name = \"🚨 Private Property Β· Intruder Detection\"\n", - "display_stream(video_url, video_name)" - ], + "execution_count": null, "metadata": { - "id": "GW2ZL4e7ONmP", "colab": { "base_uri": "https://localhost:8080/", "height": 422 }, + "id": "GW2ZL4e7ONmP", "outputId": "fe92d06e-daff-4975-ce19-0c8f8d532fea" }, - "execution_count": 10, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - "" - ], "text/html": [ "\n", "
\n", @@ -422,15 +417,30 @@ "
\n", " \n", " " + ], + "text/plain": [ + "" ] }, + "execution_count": 10, "metadata": {}, - "execution_count": 10 + "output_type": "execute_result" } - ] - }, - { + ], + "source": [ + "# To get last few minutes stream link\n", + "\n", + "video_url = fetch_stream(intruder_stream)\n", + "\n", + "video_name = \"🚨 Private Property Β· Intruder Detection\"\n", + "display_stream(video_url, video_name)" + ] + }, + { "cell_type": "markdown", + "metadata": { + "id": "m3gM9_nbObaH" + }, "source": [ "---\n", "\n", @@ -442,13 +452,27 @@ "- People loitering\n", "- Approaching or physically interacting with the door/lock\n", "- Attempting to enter the property" - ], - "metadata": { - "id": "m3gM9_nbObaH" - } + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "I4IDVrPBOcaT", + "outputId": "ecf074dc-3952-44d4-99ae-1d82965b1f70" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Scene Index ID: 7a6b9e4dc0132ebe\n" + ] + } + ], "source": [ "from videodb import SceneExtractionType\n", "\n", @@ -463,54 +487,20 @@ ")\n", "intruder_index_id = intruder_scene_index.rtstream_index_id\n", "print(\"Scene Index ID:\", intruder_index_id)\n" - ], - "metadata": { - "id": "I4IDVrPBOcaT", - "colab": { - "base_uri": "https://localhost:8080/" - }, - "outputId": "ecf074dc-3952-44d4-99ae-1d82965b1f70" - }, - "execution_count": null, - "outputs": [ - { - "output_type": "stream", - "name": "stdout", - "text": [ - "Scene Index ID: 7a6b9e4dc0132ebe\n" - ] - } ] }, { "cell_type": "markdown", - "source": [ - "#### Let us list the scene indexes created on our rtstream." - ], "metadata": { "id": "f7R-LeKAioYI" - } + }, + "source": [ + "#### Let us list the scene indexes created on our rtstream." + ] }, { "cell_type": "code", - "source": [ - "def list_rtstream_indexes(rtstream):\n", - " # List live stream indexes\n", - " rtstream_indexes = rtstream.list_scene_indexes()\n", - " for rtstream_index in rtstream_indexes:\n", - "\n", - " print(f\"\"\"RTStreamSceneIndex:\n", - " Index ID : {rtstream_index.rtstream_index_id}\n", - " RTStream ID : {rtstream_index.rtstream_id}\n", - " Name : {rtstream_index.name}\n", - " Status : {rtstream_index.status}\n", - " Config : {rtstream_index.extraction_config}\n", - " Prompt : {rtstream_index.prompt}\n", - " \"\"\")\n", - " print(\"-\" * 80)\n", - "\n", - "list_rtstream_indexes(intruder_stream)" - ], + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/" @@ -518,11 +508,10 @@ "id": "MTHKJ6MCir--", "outputId": "97a74f3d-b3c5-4004-ffa5-25aec62def10" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "RTStreamSceneIndex:\n", " Index ID : 7a6b9e4dc0132ebe\n", @@ -544,108 +533,96 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "def list_rtstream_indexes(rtstream):\n", + " # List live stream indexes\n", + " rtstream_indexes = rtstream.list_scene_indexes()\n", + " for rtstream_index in rtstream_indexes:\n", + "\n", + " print(f\"\"\"RTStreamSceneIndex:\n", + " Index ID : {rtstream_index.rtstream_index_id}\n", + " RTStream ID : {rtstream_index.rtstream_id}\n", + " Name : {rtstream_index.name}\n", + " Status : {rtstream_index.status}\n", + " Config : {rtstream_index.extraction_config}\n", + " Prompt : {rtstream_index.prompt}\n", + " \"\"\")\n", + " print(\"-\" * 80)\n", + "\n", + "list_rtstream_indexes(intruder_stream)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "in8K0RIhCjpu" + }, "source": [ "\n", "#### If you have already created a scene index, run the below cell with your **scene index id** to reconnect." - ], - "metadata": { - "id": "in8K0RIhCjpu" - } + ] }, { "cell_type": "code", - "source": [ - "# intruder_index_id = \"\"\n", - "# intruder_scene_index = intruder_stream.get_scene_index(intruder_index_id)" - ], + "execution_count": null, "metadata": { "id": "WG2oQmwbCoC2" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# intruder_index_id = \"\"\n", + "# intruder_scene_index = intruder_stream.get_scene_index(intruder_index_id)" + ] }, { "cell_type": "code", - "source": [ - "# To stop the index\n", - "# intruder_scene_index.stop()" - ], + "execution_count": null, "metadata": { "id": "-o_Ov8G6Cz5a" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To stop the index\n", + "# intruder_scene_index.stop()" + ] }, { "cell_type": "code", - "source": [ - "# To start the index\n", - "# intruder_scene_index.start()" - ], + "execution_count": null, "metadata": { "id": "yjaTDD3PCycw" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# To start the index\n", + "# intruder_scene_index.start()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "7-N4193uOg7G" + }, "source": [ "---\n", "### Let us see the generated indexes" - ], - "metadata": { - "id": "7-N4193uOg7G" - } + ] }, { "cell_type": "code", - "source": [ - "import time\n", - "from datetime import datetime\n", - "from zoneinfo import ZoneInfo\n", - "\n", - "def _convert_to_ist(timestamp: float) -> str:\n", - " \"\"\"Convert UTC timestamp to IST (Asia/Kolkata) datetime string.\"\"\"\n", - " return (\n", - " datetime.fromtimestamp(timestamp)\n", - " .astimezone(ZoneInfo(\"Asia/Kolkata\"))\n", - " .strftime(\"%Y-%m-%d %H:%M:%S\")\n", - " )\n", - "\n", - "def get_scenes(rtstream, index_id):\n", - " # Print indexed scenes\n", - " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", - " scenes = rtstream_scene_index.get_scenes(page_size=5)\n", - " # print(scenes[\"scenes\"][:2])\n", - " if scenes:\n", - " for scene in scenes.get(\"scenes\"):\n", - " start = _convert_to_ist(scene[\"start\"])\n", - " end = _convert_to_ist(scene[\"end\"])\n", - " description = scene[\"description\"]\n", - " print(f\"{start}-{end} : {description}\")\n", - " print(\"-\" * 80)\n", - " else:\n", - " print(\"Scenes not found for given index.\")\n", - "\n", - "get_scenes(intruder_stream , intruder_index_id)" - ], + "execution_count": null, "metadata": { - "id": "VzzKcqWyOikc", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "VzzKcqWyOikc", "outputId": "611aef8b-5084-4350-c13e-0c868511be44" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "2025-05-27 20:25:06-2025-05-27 20:25:10 : Based on the image, I can see a person near the left side of the image. The person is near a lawn mower. It is difficult to determine the person's intent or activity with certainty. The person is not interacting with the door/lock or entering the house. Therefore, the person is loitering.\n", "--------------------------------------------------------------------------------\n", @@ -699,10 +676,42 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "import time\n", + "from datetime import datetime\n", + "from zoneinfo import ZoneInfo\n", + "\n", + "def _convert_to_ist(timestamp: float) -> str:\n", + " \"\"\"Convert UTC timestamp to IST (Asia/Kolkata) datetime string.\"\"\"\n", + " return (\n", + " datetime.fromtimestamp(timestamp)\n", + " .astimezone(ZoneInfo(\"Asia/Kolkata\"))\n", + " .strftime(\"%Y-%m-%d %H:%M:%S\")\n", + " )\n", + "\n", + "def get_scenes(rtstream, index_id):\n", + " # Print indexed scenes\n", + " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", + " scenes = rtstream_scene_index.get_scenes(page_size=5)\n", + " if scenes:\n", + " for scene in scenes.get(\"scenes\"):\n", + " start = _convert_to_ist(scene[\"start\"])\n", + " end = _convert_to_ist(scene[\"end\"])\n", + " description = scene[\"description\"]\n", + " print(f\"{start}-{end} : {description}\")\n", + " print(\"-\" * 80)\n", + " else:\n", + " print(\"Scenes not found for given index.\")\n", + "\n", + "get_scenes(intruder_stream , intruder_index_id)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "Sp9DKyYrOqVt" + }, "source": [ "---\n", "## 🚨 Level 1 Breach: Loitering Near the Property\n", @@ -712,35 +721,35 @@ "Then, we’ll immediately attach an alert to this event, so you get notified in real time.\n", "\n", "\n" - ], - "metadata": { - "id": "Sp9DKyYrOqVt" - } + ] }, { "cell_type": "markdown", + "metadata": { + "id": "Ilimj-VxjFbV" + }, "source": [ "---\n", "#### First, let us set up the webhook url we are going to use in the alerts" - ], - "metadata": { - "id": "Ilimj-VxjFbV" - } + ] }, { "cell_type": "code", - "source": [ - "# Enter link to your webhook url where you want alerts to go. You can create one simply on pipedream.\n", - "webhook_url=\"\"" - ], + "execution_count": null, "metadata": { "id": "wb_-9Q5VjPsI" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "# Enter link to your webhook url where you want alerts to go. You can create one simply on pipedream.\n", + "webhook_url=\"\"" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "F_bBDstPOuzG" + }, "source": [ "---\n", "\n", @@ -748,83 +757,83 @@ "\n", "This event will flag when someone is spotted loitering near your property boundary.\n", "\n" - ], - "metadata": { - "id": "F_bBDstPOuzG" - } + ] }, { "cell_type": "code", - "source": [ - "loitering_event_id = conn.create_event(\n", - " event_prompt=\"Detect if a person is loitering near the house perimeter.\",\n", - " label=\"loitering_near_property\"\n", - ")\n", - "print(\"Loitering Event ID:\", loitering_event_id)\n" - ], + "execution_count": null, "metadata": { - "id": "PzadAArvOvHn", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "PzadAArvOvHn", "outputId": "f067b863-01a6-4cb0-e3a6-e2454e8899d8" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Loitering Event ID: 679ade6411306f35\n" ] } + ], + "source": [ + "loitering_event_id = conn.create_event(\n", + " event_prompt=\"Detect if a person is loitering near the house perimeter.\",\n", + " label=\"loitering_near_property\"\n", + ")\n", + "print(\"Loitering Event ID:\", loitering_event_id)\n" ] }, { "cell_type": "markdown", + "metadata": { + "id": "VVK_LWHoOxjO" + }, "source": [ "---\n", "\n", "### πŸ“¦ Create Alert for Level 1 Breach\n", "\n", "As soon as the AI detects a loitering event, it’ll send a webhook alert notification." - ], - "metadata": { - "id": "VVK_LWHoOxjO" - } + ] }, { "cell_type": "code", - "source": [ - "if webhook_url:\n", - " loitering_alert_id = intruder_scene_index.create_alert(\n", - " loitering_event_id,\n", - " callback_url=webhook_url\n", - " )\n", - " print(\"Loitering Alert ID:\", loitering_alert_id)\n", - "else:\n", - " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" - ], + "execution_count": null, "metadata": { - "id": "8lbFnsy1OzEI", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "8lbFnsy1OzEI", "outputId": "0492be1b-31ab-40ad-a218-579930875c26" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Loitering Alert ID: c10272cfdf1bb7b1\n" ] } + ], + "source": [ + "if webhook_url:\n", + " loitering_alert_id = intruder_scene_index.create_alert(\n", + " loitering_event_id,\n", + " callback_url=webhook_url\n", + " )\n", + " print(\"Loitering Alert ID:\", loitering_alert_id)\n", + "else:\n", + " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" ] }, { "cell_type": "markdown", + "metadata": { + "id": "V7XyCb_iO07e" + }, "source": [ "---\n", "\n", @@ -835,13 +844,13 @@ "Then, we’ll link a dedicated alert to this event.\n", "\n", "\n" - ], - "metadata": { - "id": "V7XyCb_iO07e" - } + ] }, { "cell_type": "markdown", + "metadata": { + "id": "4kc__9fKO4_1" + }, "source": [ "---\n", "\n", @@ -849,83 +858,83 @@ "\n", "This event will flag when a person approaches the property’s door and visibly interacts with it.\n", "\n" - ], - "metadata": { - "id": "4kc__9fKO4_1" - } + ] }, { "cell_type": "code", - "source": [ - "intrusion_event_id = conn.create_event(\n", - " event_prompt=\"Detect if a person is interacting with the door or visibly checking the lock.\",\n", - " label=\"intrusion_attempt\"\n", - ")\n", - "print(\"Intrusion Attempt Event ID:\", intrusion_event_id)" - ], + "execution_count": null, "metadata": { - "id": "cPHYFJkxO33x", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "cPHYFJkxO33x", "outputId": "401bbe83-b38e-40a5-91f9-c800f4f313a9" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Intrusion Attempt Event ID: 7bd0ccf7e53637ef\n" ] } + ], + "source": [ + "intrusion_event_id = conn.create_event(\n", + " event_prompt=\"Detect if a person is interacting with the door or visibly checking the lock.\",\n", + " label=\"intrusion_attempt\"\n", + ")\n", + "print(\"Intrusion Attempt Event ID:\", intrusion_event_id)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "vjWevnd5O76N" + }, "source": [ "---\n", "\n", "### πŸ“¦ Create Alert for Level 2 Breach\n", "\n", "When AI detects this activity, an instant alert will be routed to your webhook." - ], - "metadata": { - "id": "vjWevnd5O76N" - } + ] }, { "cell_type": "code", - "source": [ - "if webhook_url:\n", - " intrusion_alert_id = intruder_scene_index.create_alert(\n", - " intrusion_event_id,\n", - " callback_url=webhook_url\n", - " )\n", - " print(\"Intrusion Alert ID:\", intrusion_alert_id)\n", - "else:\n", - " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" - ], + "execution_count": null, "metadata": { - "id": "AqE8F8jYO9vW", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "AqE8F8jYO9vW", "outputId": "f64400de-fc58-4ff4-e8db-268208564feb" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Intrusion Alert ID: 441c80e43b16fbf5\n" ] } + ], + "source": [ + "if webhook_url:\n", + " intrusion_alert_id = intruder_scene_index.create_alert(\n", + " intrusion_event_id,\n", + " callback_url=webhook_url\n", + " )\n", + " print(\"Intrusion Alert ID:\", intrusion_alert_id)\n", + "else:\n", + " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" ] }, { "cell_type": "markdown", + "metadata": { + "id": "64QtYHT8O_fG" + }, "source": [ "---\n", "\n", @@ -936,13 +945,13 @@ "Then, we’ll configure a real-time alert for it.\n", "\n", "\n" - ], - "metadata": { - "id": "64QtYHT8O_fG" - } + ] }, { "cell_type": "markdown", + "metadata": { + "id": "CzSqR3CnPDwW" + }, "source": [ "---\n", "\n", @@ -950,125 +959,102 @@ "\n", "This event triggers when AI spots someone crossing into the property, opening a door, or entering the house.\n", "\n" - ], - "metadata": { - "id": "CzSqR3CnPDwW" - } + ] }, { "cell_type": "code", - "source": [ - "entry_event_id = conn.create_event(\n", - " event_prompt=\"Detect if a person enters the house or crosses the property boundary unlawfully.\",\n", - " label=\"property_entry\"\n", - ")\n", - "print(\"Property Entry Event ID:\", entry_event_id)\n" - ], + "execution_count": null, "metadata": { - "id": "4SGBukMAPEDi", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "4SGBukMAPEDi", "outputId": "95fc3858-e655-4470-8a11-7bd9bdb115a9" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Property Entry Event ID: 796434851654377e\n" ] } + ], + "source": [ + "entry_event_id = conn.create_event(\n", + " event_prompt=\"Detect if a person enters the house or crosses the property boundary unlawfully.\",\n", + " label=\"property_entry\"\n", + ")\n", + "print(\"Property Entry Event ID:\", entry_event_id)\n" ] }, { "cell_type": "markdown", + "metadata": { + "id": "5ifh9JI_PGBe" + }, "source": [ "---\n", "\n", "### πŸ“¦ Create Alert for Level 3 Breach\n", "\n", "The AI will send an immediate webhook alert for this highest-severity breach." - ], - "metadata": { - "id": "5ifh9JI_PGBe" - } + ] }, { "cell_type": "code", - "source": [ - "if webhook_url:\n", - " entry_alert_id = intruder_scene_index.create_alert(\n", - " entry_event_id,\n", - " callback_url=webhook_url\n", - " )\n", - " print(\"Property Entry Alert ID:\", entry_alert_id)\n", - "else:\n", - " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" - ], + "execution_count": null, "metadata": { - "id": "wa-Om6R4PHHy", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "wa-Om6R4PHHy", "outputId": "4817f3a4-cce5-484a-9d5e-bc5e41ba263b" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "Property Entry Alert ID: 5d5bd37c77de2607\n" ] } + ], + "source": [ + "if webhook_url:\n", + " entry_alert_id = intruder_scene_index.create_alert(\n", + " entry_event_id,\n", + " callback_url=webhook_url\n", + " )\n", + " print(\"Property Entry Alert ID:\", entry_alert_id)\n", + "else:\n", + " print(\"Error: Please provide Webhook URL. Alert cannot be created without it.\")" ] }, { "cell_type": "markdown", + "metadata": { + "id": "HX-EL8oGPIxG" + }, "source": [ "---\n", "### Lets see the events and alerts we have set up" - ], - "metadata": { - "id": "HX-EL8oGPIxG" - } + ] }, { "cell_type": "code", - "source": [ - "def list_rtstream_alerts(rtstream, index_id):\n", - " \"\"\"\n", - " Prints a list of alerts associated with a given scene index.\n", - " \"\"\"\n", - " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", - " alerts = rtstream_scene_index.list_alerts()\n", - "\n", - " for alert in alerts:\n", - " print(f\"\"\"πŸ”” RTStream Alert:\n", - " Alert ID : {alert['alert_id']}\n", - " Event ID : {alert['event_id']}\n", - " Label : {alert['label']}\n", - " Prompt : {alert['prompt']}\n", - " Status : {alert['status']}\n", - " \"\"\")\n", - " print(\"-\" * 80)\n", - "\n", - "list_rtstream_alerts(intruder_stream, intruder_index_id)\n" - ], + "execution_count": null, "metadata": { - "id": "QJcsScd8PKDj", "colab": { "base_uri": "https://localhost:8080/" }, + "id": "QJcsScd8PKDj", "outputId": "64e856a4-3327-44c1-a9cc-71d876dda859" }, - "execution_count": null, "outputs": [ { - "output_type": "stream", "name": "stdout", + "output_type": "stream", "text": [ "πŸ”” RTStream Alert:\n", " Alert ID : 441c80e43b16fbf5\n", @@ -1096,10 +1082,33 @@ "--------------------------------------------------------------------------------\n" ] } + ], + "source": [ + "def list_rtstream_alerts(rtstream, index_id):\n", + " \"\"\"\n", + " Prints a list of alerts associated with a given scene index.\n", + " \"\"\"\n", + " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", + " alerts = rtstream_scene_index.list_alerts()\n", + "\n", + " for alert in alerts:\n", + " print(f\"\"\"πŸ”” RTStream Alert:\n", + " Alert ID : {alert['alert_id']}\n", + " Event ID : {alert['event_id']}\n", + " Label : {alert['label']}\n", + " Prompt : {alert['prompt']}\n", + " Status : {alert['status']}\n", + " \"\"\")\n", + " print(\"-\" * 80)\n", + "\n", + "list_rtstream_alerts(intruder_stream, intruder_index_id)\n" ] }, { "cell_type": "markdown", + "metadata": { + "id": "j8MBrY0lPYxm" + }, "source": [ "---\n", "\n", @@ -1121,29 +1130,21 @@ "```\n", "\n", "βœ… Similar alerts will be triggered for Level 2 and Level 3 security breaches.\n" - ], - "metadata": { - "id": "j8MBrY0lPYxm" - } + ] }, { "cell_type": "markdown", + "metadata": { + "id": "NfsfAjeulCuJ" + }, "source": [ "---\n", "### Let us have a look at the stream_url received in the alert." - ], - "metadata": { - "id": "NfsfAjeulCuJ" - } + ] }, { "cell_type": "code", - "source": [ - "alert_stream_url = \"https://rt.stream.videodb.io/manifests/rts-019710fa-9511-79c3-a924-e229e4815410/1748480575000000-1748480580000000.m3u8\"\n", - "video_name = \"🚨 Private Property Β· loitering_near_property\"\n", - "\n", - "display_stream(alert_stream_url,video_name)" - ], + "execution_count": 7, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -1152,14 +1153,9 @@ "id": "2IsLJaL1kCqh", "outputId": "d552c6ef-b16a-4cfe-c329-02a03e449ad5" }, - "execution_count": 7, "outputs": [ { - "output_type": "execute_result", "data": { - "text/plain": [ - "" - ], "text/html": [ "\n", "
\n", @@ -1178,80 +1174,93 @@ "
\n", " \n", " " + ], + "text/plain": [ + "" ] }, + "execution_count": 7, "metadata": {}, - "execution_count": 7 + "output_type": "execute_result" } + ], + "source": [ + "alert_stream_url = \"https://rt.stream.videodb.io/manifests/rts-019710fa-9511-79c3-a924-e229e4815410/1748480575000000-1748480580000000.m3u8\"\n", + "video_name = \"🚨 Private Property Β· loitering_near_property\"\n", + "\n", + "display_stream(alert_stream_url,video_name)" ] }, { "cell_type": "markdown", + "metadata": { + "id": "FZmnpmIe8o3b" + }, "source": [ "---\n", "- Let us disable the alerts now." - ], - "metadata": { - "id": "FZmnpmIe8o3b" - } + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "psQzO5wz8uTx" + }, + "outputs": [], "source": [ "intruder_scene_index.disable_alert(loitering_alert_id)\n", "intruder_scene_index.disable_alert(intrusion_alert_id)\n", "intruder_scene_index.disable_alert(entry_alert_id)" - ], - "metadata": { - "id": "psQzO5wz8uTx" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "- To enable the alert again" - ], "metadata": { "id": "OhsfaDwP8p8L" - } + }, + "source": [ + "- To enable the alert again" + ] }, { "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "MiN78lXu8upz" + }, + "outputs": [], "source": [ "intruder_scene_index.enable_alert(loitering_alert_id)\n", "intruder_scene_index.enable_alert(intrusion_alert_id)\n", "intruder_scene_index.enable_alert(entry_alert_id)" - ], - "metadata": { - "id": "MiN78lXu8upz" - }, - "execution_count": null, - "outputs": [] + ] }, { "cell_type": "markdown", - "source": [ - "- Now we can stop the stream" - ], "metadata": { "id": "7SSNYn8b-qsT" - } + }, + "source": [ + "- Now we can stop the stream" + ] }, { "cell_type": "code", - "source": [ - "intruder_stream.stop()" - ], + "execution_count": null, "metadata": { "id": "zweOPCnR-qRQ" }, - "execution_count": null, - "outputs": [] + "outputs": [], + "source": [ + "intruder_stream.stop()" + ] }, { "cell_type": "markdown", + "metadata": { + "id": "69EbvxIOPdn-" + }, "source": [ "---\n", "\n", @@ -1275,10 +1284,21 @@ "- 🏫 **Schools and public buildings**\n", "\n", "**What would *you* secure next?**" - ], - "metadata": { - "id": "69EbvxIOPdn-" - } + ] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3", + "name": "python3" + }, + "language_info": { + "name": "python" } - ] -} \ No newline at end of file + }, + "nbformat": 4, + "nbformat_minor": 0 +} diff --git a/real_time_streaming/Road_Monitoring.ipynb b/real_time_streaming/Road_Monitoring.ipynb index 44d2a93..17ca060 100644 --- a/real_time_streaming/Road_Monitoring.ipynb +++ b/real_time_streaming/Road_Monitoring.ipynb @@ -226,14 +226,14 @@ }, "source": [ "\n", - "#### πŸ“Ί Helper Function: Display Video Stream\n", + "#### πŸ“Ί Helper Functions: Search and Display\n", "\n", - "This cell contains a small utility function to help visualize the video streams with helpful information. You don't need to modify this code." + "This cell contains all the utility functions to search, fetch, and visualize video streams. You don't need to modify this code." ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": { "id": "6KyfCkwVTVKj" }, @@ -243,17 +243,18 @@ "\n", "from IPython.display import HTML\n", "import re\n", - "from datetime import datetime\n", + "import time\n", + "from datetime import datetime, UTC\n", "from videodb import play_stream\n", "\n", - "def display_stream(video_url, video_name=\"πŸŽ₯ Camera Feed\"):\n", "\n", - " match = re.search(r'/(\\d{16})-(\\d{16})\\.m3u8', video_url)\n", + "def display_stream(video_url, video_name=\"πŸŽ₯ Camera Feed\"):\n", + " match = re.search(r\"/(\\d{16})-(\\d{16})\\.m3u8\", video_url)\n", " if match:\n", " start_ts = int(match.group(1)) / 1e6\n", " end_ts = int(match.group(2)) / 1e6\n", - " start_time = datetime.utcfromtimestamp(start_ts).strftime('%Y-%m-%d %H:%M:%S')\n", - " end_time = datetime.utcfromtimestamp(end_ts).strftime('%Y-%m-%d %H:%M:%S')\n", + " start_time = datetime.fromtimestamp(start_ts, UTC).strftime(\"%Y-%m-%d %H:%M:%S\")\n", + " end_time = datetime.fromtimestamp(end_ts, UTC).strftime(\"%Y-%m-%d %H:%M:%S\")\n", " time_range = f\"{start_time} β†’ {end_time} UTC\"\n", " else:\n", " time_range = \"Time Unknown\"\n", @@ -262,12 +263,41 @@ "\n", " return HTML(f\"\"\"\n", "
\n", - " {video_player_html._repr_html_() if hasattr(video_player_html, '_repr_html_') else video_player_html}\n", + " {video_player_html._repr_html_() if hasattr(video_player_html, \"_repr_html_\") else video_player_html}\n", "
\n", " {video_name}
{time_range}\n", "
\n", "
\n", - " \"\"\")" + " \"\"\")\n", + "\n", + "\n", + "# To dynamically set the display duration\n", + "\n", + "\n", + "def prompt_to_time(prompt):\n", + " now = int(time.time())\n", + " prompt = (\n", + " f\"It's {now} in epoch seconds. \"\n", + " f\"Convert the phrase '{prompt}' into JSON \"\n", + " f'with keys \"from\" and \"to\" (both epoch seconds)'\n", + " )\n", + "\n", + " result = coll.generate_text(\n", + " prompt=prompt,\n", + " model_name=\"pro\",\n", + " response_type=\"json\",\n", + " )\n", + " output = result.get(\"output\", {})\n", + " return output.get(\"from\"), output.get(\"to\")\n", + "\n", + "\n", + "# To fetch stream\n", + "\n", + "\n", + "def fetch_stream(rtstream):\n", + " _from, to = prompt_to_time(\"Show me last 5 mins\")\n", + " stream_url = rtstream.generate_stream(_from, to)\n", + " return stream_url\n" ] }, { @@ -284,7 +314,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": { "colab": { "base_uri": "https://localhost:8080/", @@ -326,14 +356,6 @@ ], "source": [ "# To get last few minutes stream link\n", - "import time\n", - "\n", - "def fetch_stream(rtstream):\n", - "\n", - " now = int(time.time())\n", - " start = int(now - (5 * 60))\n", - " stream_url = rtstream.generate_stream(start, now)\n", - " return stream_url\n", "\n", "video_url = fetch_stream(accident_stream)\n", "\n", @@ -512,7 +534,6 @@ " # Print indexed scenes\n", " rtstream_scene_index = rtstream.get_scene_index(index_id)\n", " scenes = rtstream_scene_index.get_scenes(page_size=5)\n", - " # print(scenes[\"scenes\"][:2])\n", " if scenes:\n", " for scene in scenes.get(\"scenes\"):\n", " start = _convert_to_ist(scene[\"start\"])\n", diff --git a/real_time_streaming/multicam/Multicam_Basketball_Analysis.ipynb b/real_time_streaming/multicam/Multicam_Basketball_Analysis.ipynb new file mode 100644 index 0000000..5c6c9bd --- /dev/null +++ b/real_time_streaming/multicam/Multicam_Basketball_Analysis.ipynb @@ -0,0 +1,1768 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "9e7c4fe6", + "metadata": { + "id": "9e7c4fe6" + }, + "source": [ + "## πŸ€ Multi-Camera Basketball Analysis with VideoDB RTStream\n", + "\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/video-db/videodb-cookbook/blob/main/rtstream/Multicam_Basketball_Analysis.ipynb)\n", + "\n", + "*Transforming sports through intelligent AI-powered monitoring*\n", + "\n", + "---\n", + "\n", + "### 🎯 The Challenge: Smart Sports Analytics\n", + "\n", + "Modern sports analysis faces growing challenges in providing real-time insights. From fast-paced plays to subtle player movements, monitoring every aspect of the game is tough. \n", + "\n", + "**What if AI could monitor all feeds, detect key plays instantly, and generate highlights automatically?**\n", + "\n", + "\n", + "The Goal β†’ Automate basketball analysis with AI:\n", + "- Detect key events in real time \n", + "- Trigger instant alerts for highlights \n", + "- Provide multi-angle replays for analysis\n" + ] + }, + { + "cell_type": "markdown", + "id": "O6Oa9fBD_pXH", + "metadata": { + "id": "O6Oa9fBD_pXH" + }, + "source": [ + "### πŸš€ Enter VideoDB RTStream\n", + "**VideoDB RTStream** brings AI-powered intelligence to multi-camera sports systems. \n", + "In this demo, a **3-camera basketball setup** can: \n", + "- Monitor the court from multiple angles at once \n", + "- Analyze player actions and game events in real-time \n", + "- Trigger smart alerts for key moments (e.g., basket scored, foul)\n", + "- Provide synchronized multi-angle replays for coaches and analysts" + ] + }, + { + "cell_type": "markdown", + "id": "J4Q7QVKO_0cz", + "metadata": { + "id": "J4Q7QVKO_0cz" + }, + "source": [ + "### πŸ“Š Dataset: Simulated Live Game Footage\n", + "\n", + "We use **simulated live streams from 3 different camera angles** covering a basketball game.\n", + "- **3 cameras**, with overlapping field-of-views\n", + "- Each stream captures a unique perspective of the court.\n", + "\n", + "πŸ‘‰ This setup mimics a real-world broadcast or arena setup, perfect for demonstrating multi-camera analysis\n", + "\n", + "Source: West KY Sports Network\n" + ] + }, + { + "cell_type": "markdown", + "id": "rflMucYoAN0Z", + "metadata": { + "id": "rflMucYoAN0Z" + }, + "source": [ + "### πŸŽ₯ What You'll Build\n", + "\n", + "By the end of this notebook, you’ll: \n", + "- Connect & manage **3 synchronized streams** of a basketball game\n", + "- Run **AI-powered game analysis** to understand plays\n", + "- Set up **intelligent event detection & alerts** for key moments\n", + "\n", + "*This demo shows how AI turns raw game feeds into actionable sports intelligence.*\n" + ] + }, + { + "cell_type": "markdown", + "id": "724fa3cb", + "metadata": { + "id": "724fa3cb" + }, + "source": [ + "---\n", + "### πŸ“¦ Step 1: Install Dependencies\n", + "\n", + "First, let's install the VideoDB SDK and additional packages needed for multi-camera processing.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5b4b8fcf", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5b4b8fcf", + "outputId": "028874e5-bfaf-4145-e40d-2f2c6c641287" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Preparing metadata (setup.py) ... \u001b[?25l\u001b[?25hdone\n", + " Building wheel for videodb (setup.py) ... \u001b[?25l\u001b[?25hdone\n" + ] + } + ], + "source": [ + "%pip -q install videodb" + ] + }, + { + "cell_type": "markdown", + "id": "fdab47f4", + "metadata": { + "id": "fdab47f4" + }, + "source": [ + "---\n", + "### πŸ”— Step 2: Connect to VideoDB\n", + "\n", + "Next, let’s establish a connection to **VideoDB** so we can manage multi-camera streams seamlessly.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5fcb6e17", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5fcb6e17", + "outputId": "414a5393-0ced-413b-bdb5-c907be5b62fd" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to VideoDB securely!\n" + ] + } + ], + "source": [ + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "conn = videodb.connect()\n", + "coll = conn.get_collection()\n", + "\n", + "print(\"Connected to VideoDB securely!\")" + ] + }, + { + "cell_type": "markdown", + "id": "4cdca47c", + "metadata": { + "id": "4cdca47c" + }, + "source": [ + "---\n", + "### πŸŽ₯ Step 3: Configure Multi-Camera Streams\n", + "\n", + "Now we’ll set up the multi-camera system. \n", + "For this demo, we’ll simulate a **πŸ€ basketball arena** using three camera angles that cover the court.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8f99f6fb", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8f99f6fb", + "outputId": "9a680787-34e5-415c-a861-d04f421470b2" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "🏟️ Setting up: πŸ€ Basketball Arena Monitoring\n", + "πŸ“Ή Camera count: 3\n", + "\n", + "πŸ“‹ Camera Layout:\n", + " CAM1: Main Court Field (Center Court - Wide Angle)\n", + " CAM2: North Basket Area Field (North Basket - Close Up)\n", + " CAM3: South Basket Area Field (South Basket - Close Up)\n" + ] + } + ], + "source": [ + "# Multi-camera configuration for basketball arena\n", + "CAMERA_CONFIG = {\n", + " \"setting_name\": \"πŸ€ Basketball Arena Monitoring\",\n", + " \"cameras\": {\n", + " \"cam1\": {\n", + " \"name\": \"Main Court Field\",\n", + " \"rtsp_url\": \"rtsp://samples.rts.videodb.io:8554/bb-cam1\",\n", + " \"position\": \"Center Court - Wide Angle\",\n", + " \"description\": \"Primary court coverage with full court view\"\n", + " },\n", + " \"cam2\": {\n", + " \"name\": \"North Basket Area Field\",\n", + " \"rtsp_url\": \"rtsp://samples.rts.videodb.io:8554/bb-cam2\",\n", + " \"position\": \"North Basket - Close Up\",\n", + " \"description\": \"Focused on north basket area and three-point line\"\n", + " },\n", + " \"cam3\": {\n", + " \"name\": \"South Basket Area Field\",\n", + " \"rtsp_url\": \"rtsp://samples.rts.videodb.io:8554/bb-cam3\",\n", + " \"position\": \"South Basket - Close Up\",\n", + " \"description\": \"Focused on south basket area and paint zone\"\n", + " }\n", + " }\n", + "}\n", + "\n", + "print(f\"🏟️ Setting up: {CAMERA_CONFIG['setting_name']}\")\n", + "print(f\"πŸ“Ή Camera count: {len(CAMERA_CONFIG['cameras'])}\")\n", + "print(\"\\nπŸ“‹ Camera Layout:\")\n", + "for cam_id, cam_info in CAMERA_CONFIG['cameras'].items():\n", + " print(f\" {cam_id.upper()}: {cam_info['name']} ({cam_info['position']})\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "40402b4e", + "metadata": { + "id": "40402b4e" + }, + "source": [ + "---\n", + "### 🎯 Step 4: Connect All Camera Streams\n", + "\n", + "Now, let’s connect all **four camera streams** to **VideoDB RTStream** and run them in sync.\n" + ] + }, + { + "cell_type": "markdown", + "id": "590faacd", + "metadata": { + "id": "590faacd" + }, + "source": [ + "*Get all streams*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "60a64377", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "60a64377", + "outputId": "b310ee8d-8045-4c4f-c252-1e3ba83d011e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸ”Œ Connecting to all camera streams (reusing existing where possible)...\n", + "\n" + ] + } + ], + "source": [ + "# Connect all cameras while reusing existing streams if they already exist\n", + "# This avoids creating duplicate RTStream resources.\n", + "connected_streams = {}\n", + "print(\"πŸ”Œ Connecting to all camera streams (reusing existing where possible)...\\n\")\n", + "\n", + "# Pre-fetch existing streams once to minimize API calls\n", + "try:\n", + " existing_streams = {getattr(s, \"name\", \"\"): s for s in coll.list_rtstreams()}\n", + "except Exception as e:\n", + " print(f\"⚠️ Could not list existing streams: {e}\")\n", + " existing_streams = {}" + ] + }, + { + "cell_type": "markdown", + "id": "453b0cb6", + "metadata": { + "id": "453b0cb6" + }, + "source": [ + "*Using existing streams if available*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "cd3a8f79", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "cd3a8f79", + "outputId": "2a382e46-902f-410d-8086-a49d50fbe7e5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸ“Ή CAM1: Using existing stream 'πŸ€ Basketball Arena Monitoring - Main Court Field' (rts-01993335-5fb5-7420-8020-5f0bcc2e864f)\n", + "πŸ“Ή CAM2: Using existing stream 'πŸ€ Basketball Arena Monitoring - North Basket Area Field' (rts-01993335-606c-7332-8eac-07c580b9569a)\n", + "πŸ“Ή CAM3: Using existing stream 'πŸ€ Basketball Arena Monitoring - South Basket Area Field' (rts-01993335-6147-7601-ab8b-b218bc0ba089)\n", + "\n", + "🎯 Connection Summary: 3/3 cameras ready\n", + "πŸš€ Multi-camera system ready for scene indexing!\n" + ] + } + ], + "source": [ + "for cam_id, cam_info in CAMERA_CONFIG[\"cameras\"].items():\n", + " name = f\"{CAMERA_CONFIG['setting_name']} - {cam_info['name']}\"\n", + " existing = existing_streams.get(name)\n", + "\n", + " try:\n", + " if existing:\n", + " print(f\"πŸ“Ή {cam_id.upper()}: Using existing stream '{name}' ({existing.id})\")\n", + " if getattr(existing, \"status\", None) != \"connected\":\n", + " try:\n", + " existing.start()\n", + " print(f\" ▢️ Started existing stream: {existing.id}\")\n", + " except Exception as se:\n", + " print(f\" ⚠️ Could not start existing stream: {se}\")\n", + " connected_streams[cam_id] = {\"stream\": existing, \"info\": cam_info, \"status\": \"connected\"}\n", + " else:\n", + " print(f\"πŸ“Ή {cam_id.upper()}: Creating new stream '{name}'...\")\n", + " stream = coll.connect_rtstream(name=name, url=cam_info[\"rtsp_url\"])\n", + " print(f\" βœ… Connected: {stream.id}\")\n", + " connected_streams[cam_id] = {\"stream\": stream, \"info\": cam_info, \"status\": \"connected\"}\n", + "\n", + " except Exception as e:\n", + " print(f\" ❌ {cam_id.upper()} failed: {e}\")\n", + " connected_streams[cam_id] = {\"stream\": None, \"info\": cam_info, \"status\": \"failed\", \"error\": str(e)}\n", + "\n", + "# Summary\n", + "success = sum(1 for s in connected_streams.values() if s[\"status\"] == \"connected\")\n", + "print(f\"\\n🎯 Connection Summary: {success}/{len(CAMERA_CONFIG['cameras'])} cameras ready\")\n", + "print(\"πŸš€ Multi-camera system ready for scene indexing!\" if success else \"⚠️ No cameras ready. Check RTSP URLs/credentials.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "b7Fs9FPqNcGe", + "metadata": { + "id": "b7Fs9FPqNcGe" + }, + "source": [ + "#### πŸŽ₯ Live Preview of Raw Streams" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "rbuuJ6VLggFw", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 1000 + }, + "id": "rbuuJ6VLggFw", + "outputId": "461a8d50-f277-4973-9d2b-be1dfa894c16" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸŽ₯ Connecting to all cameras...\n", + "\n", + "βœ… Main Court Field ready:\n" + ] + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "βœ… North Basket Area Field ready:\n" + ] + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "βœ… South Basket Area Field ready:\n" + ] + }, + { + "data": { + "text/html": [ + "" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import os\n", + "from concurrent.futures import ThreadPoolExecutor, as_completed\n", + "from IPython.display import Video, display\n", + "\n", + "def connect_and_export_stream(rtsp_url, cam_name, duration=5, output_file=\"clip.mp4\"):\n", + " if os.path.exists(output_file): os.remove(output_file)\n", + " os.system(\n", + " f'ffmpeg -loglevel error -hide_banner -y -rtsp_transport tcp -i \"{rtsp_url}\" -t {duration} -c copy \"{output_file}\"'\n", + " )\n", + " if os.path.exists(output_file):\n", + " return cam_name, Video(output_file, embed=True)\n", + " else:\n", + " return cam_name, None\n", + "\n", + "CLIP_DURATION = 10 #Set the Duration here for the clip time\n", + "\n", + "print(\"πŸŽ₯ Connecting to all cameras...\\n\")\n", + "futures = []\n", + "with ThreadPoolExecutor() as executor:\n", + " for cam_id, cam_info in CAMERA_CONFIG[\"cameras\"].items():\n", + " out_file = f\"{cam_id}_clip.mp4\"\n", + " futures.append(\n", + " executor.submit(connect_and_export_stream, cam_info[\"rtsp_url\"], cam_info[\"name\"], CLIP_DURATION, out_file)\n", + " )\n", + "\n", + " for future in as_completed(futures):\n", + " cam_name, clip = future.result()\n", + " if clip:\n", + " print(f\"βœ… {cam_name} ready:\")\n", + " display(clip)\n", + " else:\n", + " print(f\"❌ Failed to capture {cam_name}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "701bce9f", + "metadata": { + "id": "701bce9f" + }, + "source": [ + "---\n", + "### πŸ—‚οΈ Step 5: Set Up Scene Indexing\n", + "\n", + "Now we’ll build **scene indexes** for each camera stream, enabling AI-powered analysis across all viewpoints.\n" + ] + }, + { + "cell_type": "markdown", + "id": "45817b27", + "metadata": { + "id": "45817b27" + }, + "source": [ + "*a. Index Config*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "a7d515e1", + "metadata": { + "id": "a7d515e1" + }, + "outputs": [], + "source": [ + "from videodb import SceneExtractionType\n", + "\n", + "# Scene indexing configuration\n", + "SCENE_INDEX_CONFIG = {\n", + " \"extraction_type\": SceneExtractionType.time_based,\n", + " \"extraction_config\": {\n", + " \"time\": 15, # Analyze every 15 seconds\n", + " \"frame_count\": 1\n", + " },\n", + " \"prompt\": \"\"\"Analyze this basketball game footage and describe:\n", + " 1. Player positions and movements on the court\n", + " 2. Ball location and which team has possession\n", + " 3. Any significant events (baskets scored, fouls, free throws, timeouts)\n", + " 4. Defensive and offensive plays being executed\n", + " 5. Crowd reactions or unusual activities\n", + " 6. Any safety or security concerns\n", + "\n", + " Be specific about what you observe in this camera angle.\"\"\"\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "580be053", + "metadata": { + "id": "580be053" + }, + "source": [ + "*b. Setup Index*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8c1a4290", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "8c1a4290", + "outputId": "c0fde6ce-bfc3-44ec-c5a4-49d85d5587b7" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸ“‘ CAM1: No indexes found\n", + "πŸ“‘ CAM2: No indexes found\n", + "πŸ“‘ CAM3: No indexes found\n" + ] + } + ], + "source": [ + "# πŸ” List existing scene indexes for connected cameras\n", + "for cam_id, cam_data in connected_streams.items():\n", + " if cam_data[\"status\"] != \"connected\":\n", + " print(f\"⏭️ {cam_id.upper()}: Stream not connected\")\n", + " continue\n", + "\n", + " try:\n", + " indexes = cam_data[\"stream\"].list_scene_indexes()\n", + " if indexes:\n", + " print(f\"πŸ“‘ {cam_id.upper()} ({cam_data['info']['name']}):\")\n", + " for idx in indexes:\n", + " print(f\" β€’ {idx.name} (ID: {idx.rtstream_index_id}, Status: {getattr(idx, 'status', 'unknown')})\")\n", + " else:\n", + " print(f\"πŸ“‘ {cam_id.upper()}: No indexes found\")\n", + " except Exception as e:\n", + " print(f\"⚠️ {cam_id.upper()}: Failed to list indexes ({e})\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bc8b8a43", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "bc8b8a43", + "outputId": "e9169b75-a6c9-41b5-f2f2-85c25c19b20c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸ”§ Setting up scene indexes...\n", + "\n", + "πŸ“Š CAM1: Creating new index 'Basketball_Arena_CAM1_Index'...\n", + " βœ… Index created: aacf53d17fa9d846\n", + "πŸ“Š CAM2: Creating new index 'Basketball_Arena_CAM2_Index'...\n", + " βœ… Index created: b316da9dae1a03fb\n", + "πŸ“Š CAM3: Creating new index 'Basketball_Arena_CAM3_Index'...\n", + " βœ… Index created: 05af0ba36e399502\n", + "\n", + "🎯 Scene Indexing Summary: 3/3 active\n", + "πŸ” AI analysis running (every 15s)\n" + ] + } + ], + "source": [ + "# βš™οΈ Setup or reuse scene indexes\n", + "scene_indexes = {}\n", + "print(\"πŸ”§ Setting up scene indexes...\\n\")\n", + "\n", + "for cam_id, cam_data in connected_streams.items():\n", + " if cam_data[\"status\"] != \"connected\":\n", + " continue\n", + "\n", + " stream = cam_data[\"stream\"]\n", + " name = f\"Basketball_Arena_{cam_id.upper()}_Index\"\n", + "\n", + " try:\n", + " # Check if an index with this name exists\n", + " existing = next((idx for idx in stream.list_scene_indexes() if getattr(idx, \"name\", \"\") == name), None)\n", + "\n", + " if existing:\n", + " print(f\"πŸ“Š {cam_id.upper()}: Using existing index '{name}' ({existing.rtstream_index_id})\")\n", + " if getattr(existing, \"status\", None) not in (\"running\", \"active\", \"connected\"):\n", + " try:\n", + " existing.start()\n", + " print(f\" ▢️ Started index: {existing.rtstream_index_id}\")\n", + " except Exception as se:\n", + " print(f\" ⚠️ Could not start index: {se}\")\n", + " scene_indexes[cam_id] = {\"index\": existing, \"index_id\": existing.rtstream_index_id, \"status\": \"active\"}\n", + " else:\n", + " print(f\"πŸ“Š {cam_id.upper()}: Creating new index '{name}'...\")\n", + " new_idx = stream.index_scenes(\n", + " extraction_type=SCENE_INDEX_CONFIG[\"extraction_type\"],\n", + " extraction_config=SCENE_INDEX_CONFIG[\"extraction_config\"],\n", + " prompt=SCENE_INDEX_CONFIG[\"prompt\"],\n", + " name=name,\n", + " )\n", + " scene_indexes[cam_id] = {\"index\": new_idx, \"index_id\": new_idx.rtstream_index_id, \"status\": \"active\"}\n", + " print(f\" βœ… Index created: {new_idx.rtstream_index_id}\")\n", + "\n", + " except Exception as e:\n", + " print(f\"❌ {cam_id.upper()}: Failed to setup index ({e})\")\n", + " scene_indexes[cam_id] = {\"index\": None, \"index_id\": None, \"status\": \"failed\", \"error\": str(e)}\n", + "\n", + "# Summary\n", + "active = sum(1 for idx in scene_indexes.values() if idx[\"status\"] == \"active\")\n", + "print(f\"\\n🎯 Scene Indexing Summary: {active}/{len(connected_streams)} active\")\n", + "print(\"πŸ” AI analysis running (every 15s)\" if active else \"⚠️ No indexes active. Check streams and retry.\")" + ] + }, + { + "cell_type": "markdown", + "id": "14e8131b", + "metadata": { + "id": "14e8131b" + }, + "source": [ + "---\n", + "## 🚨 Phase 2: Set and Receive Alerts\n", + "\n", + "In this phase, we’ll configure alert rules and handle incoming alerts from the multi-camera streams." + ] + }, + { + "cell_type": "markdown", + "id": "d7d49837", + "metadata": { + "id": "d7d49837" + }, + "source": [ + "### βš™οΈ Step 1: Configure and setup events" + ] + }, + { + "cell_type": "markdown", + "id": "27aa8782", + "metadata": { + "id": "27aa8782" + }, + "source": [ + "*Alert Config & Events*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3475b00b", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "3475b00b", + "outputId": "14bf6a63-5d95-42ce-ab9d-fc281047cf1e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸ“‘ Existing events found:\n", + " β€’ timeout_called (0bb5f68981208827)\n", + " β€’ person_with_trolley (0c9831d3a5d702b3)\n", + " β€’ large_crowd_formation (1a5585203e9374d1)\n", + " β€’ suspicious_loitering (3307388de055afbe)\n", + " β€’ basket scored (49977929774f8cd7)\n", + " β€’ player foul (53409a9e89f66846)\n", + " β€’ basket_scored (6c08568f54b59644)\n", + " β€’ unattended_luggage (87a452a83bc362ad)\n", + " β€’ timeout called (902fdd5571bacf30)\n", + " β€’ woman_in_red_coat (afe2776cf3f4608c)\n", + " β€’ player_foul (cab9b3430d908e31)\n" + ] + } + ], + "source": [ + "# πŸ€ Define basketball arena events\n", + "EVENTS_CONFIG = [\n", + " {\"label\": \"basket scored\", \"prompt\": \"Detect when a basket is scored (ball through hoop, celebrations, or score change).\", \"description\": \"Basket scored\"},\n", + " {\"label\": \"player foul\", \"prompt\": \"Detect fouls, aggressive behavior, or rule violations.\", \"description\": \"Player foul or violation\"},\n", + " {\"label\": \"timeout called\", \"prompt\": \"Detect when a timeout is called (players huddle or referee signals).\", \"description\": \"Timeout called\"},\n", + "]\n", + "\n", + "# πŸ” List existing events in VideoDB\n", + "existing_events_by_label = {}\n", + "try:\n", + " for evt in conn.list_events():\n", + " lbl = evt[\"label\"]\n", + " eid = evt[\"event_id\"]\n", + " if lbl and eid:\n", + " existing_events_by_label[lbl] = eid\n", + " if existing_events_by_label:\n", + " print(\"πŸ“‘ Existing events found:\")\n", + " for lbl, eid in existing_events_by_label.items():\n", + " print(f\" β€’ {lbl} ({eid})\")\n", + " else:\n", + " print(\"πŸ“‘ No existing events found.\")\n", + "except Exception as e:\n", + " print(f\"⚠️ Could not list existing events: {e}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ce57398c", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ce57398c", + "outputId": "bc2132f0-b9eb-4cef-ece0-73181c7b300a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "🎯 Setting up cross-camera event detection...\n", + "\n", + "πŸ“Ž Using existing event: basket scored (49977929774f8cd7)\n", + "πŸ“Ž Using existing event: player foul (53409a9e89f66846)\n", + "πŸ“Ž Using existing event: timeout called (902fdd5571bacf30)\n", + "\n", + "🎯 Events ready: 3 existing, 0 created (3/3)\n", + "πŸš€ Event detection system ready! Monitoring for:\n", + " β€’ basket scored: Basket scored\n", + " β€’ player foul: Player foul or violation\n", + " β€’ timeout called: Timeout called\n" + ] + } + ], + "source": [ + "# πŸš€ Create or reuse events in VideoDB\n", + "created_events = {}\n", + "print(\"\\n🎯 Setting up cross-camera event detection...\\n\")\n", + "\n", + "for cfg in EVENTS_CONFIG:\n", + " label = cfg[\"label\"]\n", + "\n", + " if label in existing_events_by_label:\n", + " event_id = existing_events_by_label[label]\n", + " created_events[label] = {\"event_id\": event_id, \"config\": cfg, \"status\": \"existing\"}\n", + " print(f\"πŸ“Ž Using existing event: {label} ({event_id})\")\n", + " continue\n", + "\n", + " try:\n", + " print(f\"πŸ“ Creating event: {label}...\")\n", + " event_id = conn.create_event(event_prompt=cfg[\"prompt\"], label=label)\n", + " created_events[label] = {\"event_id\": event_id, \"config\": cfg, \"status\": \"created\"}\n", + " print(f\" βœ… Created: {event_id}\")\n", + " except Exception as e:\n", + " print(f\" ❌ Failed: {e}\")\n", + " created_events[label] = {\"event_id\": None, \"config\": cfg, \"status\": \"failed\", \"error\": str(e)}\n", + "\n", + "# πŸ“Š Summary\n", + "created = sum(1 for e in created_events.values() if e[\"status\"] == \"created\")\n", + "existing = sum(1 for e in created_events.values() if e[\"status\"] == \"existing\")\n", + "print(f\"\\n🎯 Events ready: {existing} existing, {created} created ({existing+created}/{len(EVENTS_CONFIG)})\")\n", + "\n", + "if existing + created:\n", + " print(\"πŸš€ Event detection system ready! Monitoring for:\")\n", + " for lbl, evt in created_events.items():\n", + " if evt[\"status\"] in (\"created\", \"existing\"):\n", + " print(f\" β€’ {lbl}: {evt['config']['description']}\")\n", + "else:\n", + " print(\"⚠️ No events ready. Check configuration and retry.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "da2f1b16", + "metadata": { + "id": "da2f1b16" + }, + "source": [ + "---\n", + "### 🌐 Step 2: Configure Webhook & Callback" + ] + }, + { + "cell_type": "markdown", + "id": "e3038241", + "metadata": { + "id": "e3038241" + }, + "source": [ + "*a. install pyngrok*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58b7d246", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "58b7d246", + "outputId": "f17f2834-9f71-4566-f953-ed5ed08a2e73" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Collecting pyngrok\n", + " Downloading pyngrok-7.3.0-py3-none-any.whl.metadata (8.1 kB)\n", + "Requirement already satisfied: PyYAML>=5.1 in /usr/local/lib/python3.12/dist-packages (from pyngrok) (6.0.2)\n", + "Downloading pyngrok-7.3.0-py3-none-any.whl (25 kB)\n", + "Installing collected packages: pyngrok\n", + "Successfully installed pyngrok-7.3.0\n", + "Requirement already satisfied: flask in /usr/local/lib/python3.12/dist-packages (3.1.2)\n", + "Requirement already satisfied: blinker>=1.9.0 in /usr/local/lib/python3.12/dist-packages (from flask) (1.9.0)\n", + "Requirement already satisfied: click>=8.1.3 in /usr/local/lib/python3.12/dist-packages (from flask) (8.2.1)\n", + "Requirement already satisfied: itsdangerous>=2.2.0 in /usr/local/lib/python3.12/dist-packages (from flask) (2.2.0)\n", + "Requirement already satisfied: jinja2>=3.1.2 in /usr/local/lib/python3.12/dist-packages (from flask) (3.1.6)\n", + "Requirement already satisfied: markupsafe>=2.1.1 in /usr/local/lib/python3.12/dist-packages (from flask) (3.0.2)\n", + "Requirement already satisfied: werkzeug>=3.1.0 in /usr/local/lib/python3.12/dist-packages (from flask) (3.1.3)\n" + ] + } + ], + "source": [ + "!pip install pyngrok\n", + "!pip install flask" + ] + }, + { + "cell_type": "markdown", + "id": "329f0647", + "metadata": { + "id": "329f0647" + }, + "source": [ + "*b. Expose a Public Webhook URL (ngrok or fallback)*" + ] + }, + { + "cell_type": "markdown", + "id": "Dw8GuDlPKwe1", + "metadata": { + "id": "Dw8GuDlPKwe1" + }, + "source": [ + "> ⚑ **Note:** \n", + "> - **Local setup**: You can comment out these lines and just pass an empty string for the token: \n", + "> ```python\n", + "> # from google.colab import userdata\n", + "> # token = userdata.get('ngrok_auth')\n", + "> token = \"\"\n", + "> ```\n", + ">\n", + "> - **Google Colab**: Create a secret and fetch it using `userdata`. \n", + "> You can generate your Ngrok **Auth Token** here: [Ngrok Dashboard](https://dashboard.ngrok.com/get-started/your-authtoken)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f8a3ff28", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "f8a3ff28", + "outputId": "d0996525-3f6f-4049-bedb-922cbbeaad1e" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "🌍 Public webhook URL: https://aec9b708aa90.ngrok-free.app/webhook\n", + "βœ… Callback URL configured: https://aec9b708aa90.ngrok-free.app/webhook\n" + ] + } + ], + "source": [ + "import os\n", + "import socket\n", + "from pyngrok import ngrok\n", + "from google.colab import userdata\n", + "\n", + "def choose_port(start=5001, tries=5):\n", + " \"\"\"Pick an available local port (default: 5001–5005).\"\"\"\n", + " for p in range(start, start + tries):\n", + " with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:\n", + " try:\n", + " s.bind((\"0.0.0.0\", p))\n", + " return p\n", + " except OSError:\n", + " continue\n", + " return start\n", + "\n", + "try:\n", + " # Authenticate ngrok if token available\n", + " token = userdata.get('ngrok_auth')\n", + " if token:\n", + " ngrok.set_auth_token(token)\n", + "\n", + " WEBHOOK_PORT = choose_port()\n", + " tunnel = ngrok.connect(WEBHOOK_PORT)\n", + " PUBLIC_WEBHOOK_URL = f\"{tunnel.public_url}/webhook\"\n", + " print(f\"🌍 Public webhook URL: {PUBLIC_WEBHOOK_URL}\")\n", + "except Exception as e:\n", + " PUBLIC_WEBHOOK_URL = \"\"\n", + " print(f\"⚠️ ngrok tunnel not started: {e}\")\n", + " print(\"➑️ Set `WEBHOOK_URL` manually if using external service (Zapier, Pipedream, etc.).\")\n", + "\n", + "# Unified callback (used when creating alerts)\n", + "ALERT_CALLBACK_URL = PUBLIC_WEBHOOK_URL or \"\"\n", + "if ALERT_CALLBACK_URL:\n", + " print(f\"βœ… Callback URL configured: {ALERT_CALLBACK_URL}\")\n", + "else:\n", + " print(\"⚠️ No callback URL configured β€” alerts won’t trigger notifications.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "1f792c32", + "metadata": { + "id": "1f792c32" + }, + "source": [ + "*c. Webhook Receiver (Flask server)*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5eb39c1d", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5eb39c1d", + "outputId": "ae81d24a-c06d-425f-eb0d-fa3391507674" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸš€ Webhook server running at http://localhost:5001/webhook\n" + ] + } + ], + "source": [ + "import time\n", + "import threading\n", + "from flask import Flask, request, jsonify\n", + "\n", + "# In-memory store for webhook calls\n", + "webhook_data = globals().get(\"webhook_data\", [])\n", + "webhook_meta = globals().get(\"webhook_meta\", {\"started_at\": time.time(), \"count\": 0})\n", + "\n", + "app = Flask(__name__)\n", + "\n", + "@app.route(\"/webhook\", methods=[\"POST\"])\n", + "def webhook():\n", + " payload = request.get_json(silent=True) or {}\n", + " webhook_meta[\"count\"] += 1\n", + " webhook_data.append({\n", + " \"received_at\": time.time(),\n", + " \"headers\": dict(request.headers),\n", + " \"data\": payload\n", + " })\n", + " webhook_data[:] = webhook_data[-500:] # keep last 500 only\n", + " print(f\"πŸ“© Webhook #{webhook_meta['count']} received: {payload}\")\n", + " return jsonify({\"status\": \"ok\"})\n", + "\n", + "def run_webhook():\n", + " app.run(host=\"0.0.0.0\", port=WEBHOOK_PORT, debug=False, use_reloader=False)\n", + "\n", + "# Start/reuse thread safely\n", + "webhook_thread = globals().get(\"webhook_thread\")\n", + "if not webhook_thread or not webhook_thread.is_alive():\n", + " webhook_thread = threading.Thread(target=run_webhook, daemon=True)\n", + " webhook_thread.start()\n", + " globals()[\"webhook_thread\"] = webhook_thread\n", + " print(f\"πŸš€ Webhook server running at http://localhost:{WEBHOOK_PORT}/webhook\")\n", + "else:\n", + " print(f\"βœ… Webhook server already running at http://localhost:{WEBHOOK_PORT}/webhook\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "3d5f0b08", + "metadata": { + "id": "3d5f0b08" + }, + "source": [ + "---\n", + "### 🚨 Step 3: Multi-Camera Alert System\n", + "\n", + "We’ll now set up an **intelligent alerting pipeline** that continuously monitors all connected cameras. \n", + "Whenever a basketball event is detected, the system will trigger a **real-time notification** enriched with **multi-angle evidence clips** for better context." + ] + }, + { + "cell_type": "markdown", + "id": "0e5ac8cd", + "metadata": { + "id": "0e5ac8cd" + }, + "source": [ + "*a. Callback & Setup*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eaa2285b", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "eaa2285b", + "outputId": "dd733d96-06f9-4681-8688-4d67f036f045" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "🚨 Setting up alerts (reuse existing where possible)\n", + "\n" + ] + } + ], + "source": [ + "# 🚨 Step 3A: Prepare callback + alert storage\n", + "\n", + "# Use callback URL from earlier cells\n", + "callback_url = globals().get(\"ALERT_CALLBACK_URL\", \"\")\n", + "if not callback_url:\n", + " print(\"⚠️ No callback URL configured. Alerts will be created but won't send notifications.\")\n", + "\n", + "# Container for alerts per camera\n", + "created_alerts = {}\n", + "print(\"🚨 Setting up alerts (reuse existing where possible)\\n\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "afe7c72b", + "metadata": { + "id": "afe7c72b" + }, + "source": [ + "*b. Create or Reuse Alerts*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "86d680c3", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "86d680c3", + "outputId": "fa6232a3-e2b6-46f7-d804-8e556836244c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸ“Ή CAM1: Main Court Field\n", + " βœ… Created alert for 'basket scored': 18fe0dd6048b208f\n", + " βœ… Created alert for 'player foul': c5bcd7843e27ece1\n", + " βœ… Created alert for 'timeout called': 8be09efe80b15a56\n", + "πŸ“Ή CAM2: North Basket Area Field\n", + " βœ… Created alert for 'basket scored': 1620495094e55fc0\n", + " βœ… Created alert for 'player foul': 3fe89f697c326590\n", + " βœ… Created alert for 'timeout called': 0f741dd9bb46e5ea\n", + "πŸ“Ή CAM3: South Basket Area Field\n", + " βœ… Created alert for 'basket scored': af945e9619670c13\n", + " βœ… Created alert for 'player foul': 01cde5c09603ce70\n", + " βœ… Created alert for 'timeout called': 13f33c5b073a9b64\n" + ] + } + ], + "source": [ + "for cam_id, idx_data in scene_indexes.items():\n", + " if idx_data.get(\"status\") != \"active\" or not idx_data.get(\"index\"):\n", + " print(f\"⏭️ {cam_id.upper()}: Index not active β†’ skipping alerts\")\n", + " continue\n", + "\n", + " idx = idx_data[\"index\"]\n", + " cam_name = connected_streams[cam_id][\"info\"][\"name\"]\n", + " created_alerts[cam_id] = {}\n", + "\n", + " # Try to list existing alerts for this index\n", + " existing_alerts = {}\n", + " try:\n", + " for a in idx.list_alerts():\n", + " label = a[\"label\"]\n", + " aid = a[\"alert_id\"]\n", + " if label and aid:\n", + " existing_alerts[label] = a\n", + " except Exception as e:\n", + " print(f\" ⚠️ {cam_id.upper()}: Could not list existing alerts: {e}\")\n", + "\n", + " print(f\"πŸ“Ή {cam_id.upper()}: {cam_name}\")\n", + "\n", + " # For each defined event β†’ create/reuse alerts\n", + " for label, evt in created_events.items():\n", + " if evt.get(\"status\") not in (\"existing\", \"created\") or not evt.get(\"event_id\"):\n", + " continue\n", + "\n", + " if label in existing_alerts and callback_url == a[\"callback_url\"]:\n", + " if a[\"status\"] == \"disabled\":\n", + " idx.enable_alert(a[\"alert_id\"])\n", + " a = existing_alerts[label]\n", + " aid = a[\"alert_id\"]\n", + " created_alerts[cam_id][label] = {\"alert_id\": aid, \"event_id\": evt[\"event_id\"], \"status\": \"existing\"}\n", + " print(f\" πŸ“Ž Using existing alert for '{label}': {aid}\")\n", + "\n", + " else:\n", + " try:\n", + " aid = idx.create_alert(evt[\"event_id\"], callback_url=callback_url or None)\n", + " status = \"active\" if callback_url else \"created_no_webhook\"\n", + " created_alerts[cam_id][label] = {\"alert_id\": aid, \"event_id\": evt[\"event_id\"], \"status\": status}\n", + " msg = f\" βœ… Created alert for '{label}': {aid}\" if callback_url else f\" ⚠️ Created alert for '{label}' (no webhook)\"\n", + " print(msg)\n", + " except Exception as e:\n", + " created_alerts[cam_id][label] = {\"alert_id\": None, \"event_id\": evt[\"event_id\"], \"status\": \"failed\", \"error\": str(e)}\n", + " print(f\" ❌ Failed to create alert for '{label}': {e}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e8cd595f", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "e8cd595f", + "outputId": "c34452f8-a02c-4e8c-e522-67877775921a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "🎯 Alert System Summary: 9/9 alerts ready\n", + "πŸ“¬ Alerts will POST to: https://aec9b708aa90.ngrok-free.app/webhook\n" + ] + } + ], + "source": [ + "# πŸ“Š Alert system summary\n", + "num_total = sum(len(alerts) for alerts in created_alerts.values())\n", + "num_ready = sum(\n", + " 1 for cam_alerts in created_alerts.values()\n", + " for a in cam_alerts.values()\n", + " if a[\"status\"] in (\"active\", \"existing\", \"created_no_webhook\")\n", + ")\n", + "\n", + "print(f\"\\n🎯 Alert System Summary: {num_ready}/{num_total} alerts ready\")\n", + "if num_ready:\n", + " print(f\"πŸ“¬ Alerts will POST to: {callback_url or '❌ (no webhook set)'}\")\n", + "else:\n", + " print(\"⚠️ No alerts ready. Check indexes, events, and callback URL.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "4c577208", + "metadata": { + "id": "4c577208" + }, + "source": [ + "---\n", + "## πŸ“‘ Phase 3: Alerts & Data Processing\n", + "\n", + "With alerts now streaming in from all cameras, this phase focuses on **capturing, processing, and analyzing** those incoming events. \n", + "We’ll store webhook data in-memory, extract useful context, and prepare it for downstream workflows like dashboards, notifications, or automated actions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "636ac21f", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "636ac21f", + "outputId": "7fb7d01e-0f9a-4bc3-ed9c-26023342ac84" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "24" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(webhook_data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b5f3d4c9", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "b5f3d4c9", + "outputId": "78dddb24-28c9-4db0-e401-2af4e62e6fde" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'received_at': 1757503276.671652,\n", + " 'headers': {'Host': 'aec9b708aa90.ngrok-free.app',\n", + " 'User-Agent': 'Python/3.12 aiohttp/3.11.11',\n", + " 'Content-Length': '787',\n", + " 'Accept': '*/*',\n", + " 'Accept-Encoding': 'gzip, deflate',\n", + " 'Content-Type': 'application/json',\n", + " 'X-Forwarded-For': '54.205.49.109',\n", + " 'X-Forwarded-Host': 'aec9b708aa90.ngrok-free.app',\n", + " 'X-Forwarded-Proto': 'https'},\n", + " 'data': {'event_id': 'event-49977929774f8cd7',\n", + " 'label': 'basket scored',\n", + " 'confidence': 0.95,\n", + " 'explanation': 'The ball is depicted directly above the rim and net, appearing to descend through the hoop, strongly indicating a basket is being scored.',\n", + " 'timestamp': '2025-09-10T11:21:16.614553+00:00',\n", + " 'start_time': '2025-09-10T16:50:45.698108+05:30',\n", + " 'end_time': '2025-09-10T16:51:00.698108+05:30',\n", + " 'stream_url': 'https://videodb-rt-streaming-service-us-east-1.s3.us-east-1.amazonaws.com/manifests/rts-01993335-5fb5-7420-8020-5f0bcc2e864f/1757503245000000-1757503261000000.m3u8',\n", + " 'player_url': 'https://console.videodb.io/player?url=https://videodb-rt-streaming-service-us-east-1.s3.us-east-1.amazonaws.com/manifests/rts-01993335-5fb5-7420-8020-5f0bcc2e864f/1757503245000000-1757503261000000.m3u8'}}]" + ] + }, + "execution_count": 56, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "webhook_data[:1]" + ] + }, + { + "cell_type": "markdown", + "id": "d113d1e0", + "metadata": { + "id": "d113d1e0" + }, + "source": [ + "---\n", + "### 🎯 Step 1: Choose the Event\n", + "\n", + "From the list of recent alerts, select a specific **basketball event** you’d like to explore further. \n", + "This choice will be used to extract the event window (with multiple camera angles) for deeper analysis.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b2dd5daa", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "b2dd5daa", + "outputId": "98b1fa29-ed55-47a0-8f17-cafa0edd4b3c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "πŸ“‹ Recent Alerts (last 10):\n", + "\n", + "1. 🎯 basket scored | βœ… 0.95 | πŸ“ The ball is depicted directly above the rim and net, appearing to descend through the hoop, strongly...\n", + "2. 🎯 player foul | βœ… 0.9 | πŸ“ An alert is triggered because the scene analysis indicates a potential foul. Maroon player #1 is in ...\n", + "3. 🎯 timeout called | βœ… 0.9 | πŸ“ Players in dark jerseys are clustered together in what appears to be a huddle, and the overall still...\n", + "\n", + "πŸ‘‰ Select an alert (1–3): 1\n", + "\n", + "βœ… Selected: basket scored (Confidence: 0.95)\n" + ] + } + ], + "source": [ + "# πŸ“‘ View & select recent alerts (last 10)\n", + "\n", + "# Keep only the last 10 webhook events\n", + "recent_alerts = webhook_data[-10:] if webhook_data else []\n", + "\n", + "if not recent_alerts:\n", + " print(\"⚠️ No alerts received yet.\")\n", + "else:\n", + " # Normalize alerts into a compact list\n", + " cleaned_alerts = []\n", + " for item in recent_alerts:\n", + " data = item.get(\"data\", {})\n", + " cleaned_alerts.append({\n", + " \"label\": data.get(\"label\", \"N/A\"),\n", + " \"confidence\": data.get(\"confidence\", \"N/A\"),\n", + " \"explanation\": (data.get(\"explanation\") or \"\")[:100]+\"...\",\n", + " \"timestamp\": data.get(\"timestamp\", \"N/A\"),\n", + " \"start_time\": data.get(\"start_time\", \"N/A\"),\n", + " \"end_time\": data.get(\"end_time\", \"N/A\"),\n", + " \"stream_url\": data.get(\"stream_url\", \"N/A\"),\n", + " \"player_url\": data.get(\"player_url\", \"N/A\"),\n", + " \"event_id\": data.get(\"event_id\", \"N/A\"),\n", + " })\n", + "\n", + " # Display to user\n", + " print(\"\\nπŸ“‹ Recent Alerts (last 10):\\n\")\n", + " for i, alert in enumerate(cleaned_alerts, 1):\n", + " print(f\"{i}. 🎯 {alert['label']} | \"\n", + " f\"βœ… {alert['confidence']} | πŸ“ {alert['explanation']}\")\n", + "\n", + " # Interactive selection\n", + " try:\n", + " choice = int(input(\"\\nπŸ‘‰ Select an alert (1–{0}): \".format(len(cleaned_alerts))).strip())\n", + " if 1 <= choice <= len(cleaned_alerts):\n", + " selected = cleaned_alerts[choice - 1]\n", + " print(f\"\\nβœ… Selected: {selected['label']} \"\n", + " f\"(Confidence: {selected['confidence']})\")\n", + " else:\n", + " print(\"⚠️ Invalid selection. Please choose a valid alert number.\")\n", + " except (ValueError, EOFError):\n", + " print(\"⚠️ No valid input received. Skipping selection.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "bbbe1eea", + "metadata": { + "id": "bbbe1eea" + }, + "source": [ + "---\n", + "### πŸŽ₯ Step 2: Retrieve Multi-Camera Feeds for the Same Timestamp\n", + "\n", + "Once an event is selected, we’ll fetch the **synchronized video segments** from all connected cameras. \n", + "This ensures you get a **multi-angle replay** of the same moment in time, making analysis more accurate and contextual." + ] + }, + { + "cell_type": "markdown", + "id": "c9e71d7a", + "metadata": { + "id": "c9e71d7a" + }, + "source": [ + "*a. Parse the timeline*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6568351", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "b6568351", + "outputId": "35141f8a-8e86-43b2-efab-84aaf9f96f07" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "⏱️ Time window: 1757503235 β†’ 1757503271 (with Β±10s padding)\n" + ] + } + ], + "source": [ + "import re\n", + "from datetime import datetime, timezone\n", + "\n", + "# βš™οΈ Config: symmetric padding around the alert window\n", + "OFFSET_SECONDS = 10\n", + "\n", + "# πŸ”Ž Regex to extract timestamps from HLS stream URLs\n", + "_STREAM_RE = re.compile(r\"/(\\d{16})-(\\d{16})\\.m3u8\")\n", + "\n", + "def parse_stream_times(url: str):\n", + " \"\"\"Extract start/end timestamps (in seconds) from stream URL.\"\"\"\n", + " if not url:\n", + " return None, None\n", + " match = _STREAM_RE.search(url)\n", + " if not match:\n", + " return None, None\n", + " return int(match[1]) / 1e6, int(match[2]) / 1e6\n", + "\n", + "def parse_iso_ts(ts: str):\n", + " \"\"\"Convert ISO timestamp string to epoch seconds.\"\"\"\n", + " if not ts:\n", + " return None\n", + " dt = datetime.fromisoformat(ts)\n", + " if dt.tzinfo is None:\n", + " dt = dt.replace(tzinfo=timezone.utc)\n", + " return int(dt.timestamp())\n", + "\n", + "# πŸ“¦ Extract payload from selection\n", + "if not selected:\n", + " raise RuntimeError(\"❌ No event data found in webhook payload.\")\n", + "\n", + "label = selected.get(\"label\", \"unknown\")\n", + "confidence = selected.get(\"confidence\", \"N/A\")\n", + "explanation = selected.get(\"explanation\", \"\")\n", + "stream_url = selected.get(\"stream_url\", \"\")\n", + "player_url = selected.get(\"player_url\", \"\")\n", + "event_id = selected.get(\"event_id\", \"\")\n", + "timestamp = selected.get(\"timestamp\", \"\")\n", + "\n", + "# πŸ•’ Resolve event time window\n", + "start_s, end_s = parse_stream_times(stream_url)\n", + "\n", + "if not (start_s and end_s):\n", + " start_s = parse_iso_ts(selected.get(\"start_time\"))\n", + " end_s = parse_iso_ts(selected.get(\"end_time\"))\n", + "\n", + "if not (start_s and end_s):\n", + " raise RuntimeError(\"❌ Could not determine alert time window from payload.\")\n", + "\n", + "# Apply symmetric offset\n", + "start_adj = max(0, int(start_s) - OFFSET_SECONDS)\n", + "end_adj = int(end_s) + OFFSET_SECONDS\n", + "\n", + "print(f\"⏱️ Time window: {start_adj} β†’ {end_adj} (with Β±{OFFSET_SECONDS}s padding)\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "c47d200a", + "metadata": { + "id": "c47d200a" + }, + "source": [ + "*b. Generating all streams*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2e1b4072", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "2e1b4072", + "outputId": "9e035aad-8e81-443b-d15b-a7b51ea4bd33" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "======================================================================\n", + "🚨 PROCESSING ALERT: BASKET SCORED\n", + "======================================================================\n", + "πŸ“Š Confidence: 0.95\n", + "πŸ†” Event ID: event-49977929774f8cd7\n", + "⏰ Detected at: 2025-09-10T11:21:16.614553+00:00\n", + "\n", + "πŸŽ₯ Original Alert Stream: https://console.videodb.io/player?url=https://videodb-rt-streaming-service-us-east-1.s3.us-east-1.amazonaws.com/manifests/rts-01993335-5fb5-7420-8020-5f0bcc2e864f/1757503245000000-1757503261000000.m3u8\n" + ] + } + ], + "source": [ + "# Display alert details\n", + "print(\"\\n\" + \"=\"*70)\n", + "print(f\"🚨 PROCESSING ALERT: {label.upper()}\")\n", + "print(\"=\"*70)\n", + "print(f\"πŸ“Š Confidence: {confidence}\")\n", + "print(f\"πŸ†” Event ID: {event_id}\")\n", + "print(f\"⏰ Detected at: {timestamp}\")\n", + "# print(f\"πŸ“ Explanation: {explanation}\")\n", + "# print(f\"\\nπŸ• TIME WINDOW:\")\n", + "# print(f\" Original: {datetime.utcfromtimestamp(int(start_s))} β†’ {datetime.utcfromtimestamp(int(end_s))} UTC\")\n", + "# print(f\" With Β±{OFFSET_SECONDS}s: {datetime.utcfromtimestamp(start_adj)} β†’ {datetime.utcfromtimestamp(end_adj)} UTC\")\n", + "print(f\"\\nπŸŽ₯ Original Alert Stream: {player_url or stream_url}\")\n", + "\n", + "# Generate synchronized streams for all connected cameras\n", + "if not globals().get(\"connected_streams\"):\n", + " raise RuntimeError(\"connected_streams not found. Run the connection step first.\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "b6dc1e49", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "b6dc1e49", + "outputId": "45cef1be-dd9e-4ec6-e434-c44d50ef73f3" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "πŸ“Ή MULTI-CAMERA SYNCHRONIZED STREAMS:\n", + "----------------------------------------------------------------------\n", + "βœ… CAM1 - Main Court Field:\n", + "βœ… CAM2 - North Basket Area Field:\n", + "βœ… CAM3 - South Basket Area Field:\n", + "\n", + "🎯 SUMMARY: Generated 3 synchronized camera streams\n" + ] + } + ], + "source": [ + "from videodb import play_stream\n", + "\n", + "multi_camera_streams = {}\n", + "print(\"\\nπŸ“Ή MULTI-CAMERA SYNCHRONIZED STREAMS:\")\n", + "print(\"-\"*70)\n", + "\n", + "for cam_id, cam_data in connected_streams.items():\n", + " if cam_data.get(\"status\") != \"connected\" or not cam_data.get(\"stream\"):\n", + " print(f\"⏭️ {cam_id.upper()} - {cam_data['info']['name']}: Not connected\")\n", + " continue\n", + "\n", + " try:\n", + " url = cam_data[\"stream\"].generate_stream(start_adj, end_adj)\n", + " player = play_stream(url)\n", + " multi_camera_streams[cam_id] = {\n", + " \"camera_name\": cam_data[\"info\"][\"name\"],\n", + " \"stream_url\": url,\n", + " \"player_url\": player,\n", + " }\n", + " print(f\"βœ… {cam_id.upper()} - {cam_data['info']['name']}:\")\n", + " # print(f\" πŸ“Ί {player}\")\n", + "\n", + " except Exception as e:\n", + " print(f\"❌ {cam_id.upper()} - {cam_data['info']['name']}: Failed ({e})\")\n", + "\n", + "print(f\"\\n🎯 SUMMARY: Generated {len(multi_camera_streams)} synchronized camera streams\")\n", + "# print(\"πŸ’‘ Use 'multi_camera_streams' dict for multi-view rendering or timeline composition\")\n", + "\n", + "camera_feeds = [ feed[\"player_url\"] for feed in multi_camera_streams.values() ]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8HA4EUNQaDAQ", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 421 + }, + "id": "8HA4EUNQaDAQ", + "outputId": "ad10c162-58da-4272-c998-1744c55dda9a" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "camera_feeds[1]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "DMDyT8YkaCtC", + "metadata": { + "id": "DMDyT8YkaCtC" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "PN9eU84YYinM", + "metadata": { + "id": "PN9eU84YYinM" + }, + "source": [ + "#### 🧹 Finally: Clean Up Resources" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "g9MIyCoqYhyF", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "g9MIyCoqYhyF", + "outputId": "e630df8e-8a08-4bf0-e1ff-bb949d0c2a5a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "❓ Stop all 3 active streams? (y/n): y\n", + "\n", + "πŸ”Œ Disconnecting all streams...\n", + " βœ… Stopped stream: Main Court Field\n", + " βœ… Stopped stream: North Basket Area Field\n", + " βœ… Stopped stream: South Basket Area Field\n", + "\n", + "🧹 All streams have been disconnected.\n" + ] + } + ], + "source": [ + "# Confirm before stopping all streams\n", + "active_streams = [s for s in connected_streams.values() if s.get(\"status\") == \"connected\" and s.get(\"stream\")]\n", + "if not active_streams:\n", + " print(\"βœ… All streams are already disconnected.\")\n", + "else:\n", + " try:\n", + " confirm = input(f\"❓ Stop all {len(active_streams)} active streams? (y/n): \").strip().lower()\n", + " if confirm == 'y':\n", + " print(\"\\nπŸ”Œ Disconnecting all streams...\")\n", + " for cam_data in active_streams:\n", + " try:\n", + " cam_data[\"stream\"].stop()\n", + " cam_name = cam_data[\"info\"][\"name\"]\n", + " print(f\" βœ… Stopped stream: {cam_name}\")\n", + " except Exception as e:\n", + " print(f\" ❌ Failed to stop stream {cam_name}: {e}\")\n", + " print(\"\\n🧹 All streams have been disconnected.\")\n", + " else:\n", + " print(\"\\nπŸ‘ Streams will remain active.\")\n", + " except (ValueError, EOFError):\n", + " print(\"\\n⚠️ No valid input received. Streams will remain active.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "6VSpxbdMZ8Y3", + "metadata": { + "id": "6VSpxbdMZ8Y3" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kernelspec": { + "display_name": ".venv", + "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.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/real_time_streaming/multicam/Multicam_Public_Surveillance.ipynb b/real_time_streaming/multicam/Multicam_Public_Surveillance.ipynb new file mode 100644 index 0000000..534149b --- /dev/null +++ b/real_time_streaming/multicam/Multicam_Public_Surveillance.ipynb @@ -0,0 +1,1825 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0480a1f9", + "metadata": { + "id": "0480a1f9" + }, + "source": [ + "## πŸ™οΈ Multi-Camera Public Surveillance with VideoDB RTStream\n", + "\n", + "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/video-db/videodb-cookbook/blob/main/rtstream/Multicam_Public_Surveillance.ipynb)\n", + "\n", + "\"Smart\n", + "\n", + "*Transforming urban surveillance through intelligent AI-powered monitoring*\n", + "\n", + "\n", + "---\n", + "\n", + "### 🎯 The Challenge: Smart City Monitoring\n", + "\n", + "Modern cities face growing challenges in ensuring public safety. From crowded metros to busy intersections, monitoring many locations in real-time is tough. \n", + "\n", + "Traditional systems rely on humans watching multiple screens β€” costly and error-prone. \n", + "**What if AI could monitor all feeds, detect incidents instantly, and alert authorities automatically?**\n", + "\n", + "\n", + "The Goal β†’ Automate monitoring with AI:\n", + "- Detect incidents in real time \n", + "- Trigger instant alerts \n", + "- Provide multi-angle evidence" + ] + }, + { + "cell_type": "markdown", + "id": "3a810fdc", + "metadata": { + "id": "3a810fdc" + }, + "source": [ + "### πŸš€ Enter VideoDB RTStream\n", + "**VideoDB RTStream** brings AI-powered intelligence to multi-camera systems. \n", + "In this demo, a **7-camera surveillance network** can: \n", + "- Monitor multiple zones at once \n", + "- Analyze pedestrian behavior in real-time \n", + "- Trigger smart alerts for unusual activity \n", + "- Provide synchronized multi-angle evidence" + ] + }, + { + "cell_type": "markdown", + "id": "6debf04f", + "metadata": { + "id": "6debf04f" + }, + "source": [ + "### πŸ“Š Dataset: WILDTRACK\n", + "\n", + "We use the **WILDTRACK dataset** (EPFL Computer Vision Lab): \n", + "- **7 cameras**, overlapping FOVs \n", + "- **HD 1080p @ 60 fps** with precise calibration \n", + "- **Real pedestrian activity** from ETH Zurich \n", + "\n", + "πŸ‘‰ For this notebook, we optimize to **720p @ 30 fps** for smoother real-time streaming." + ] + }, + { + "cell_type": "markdown", + "id": "8189c4be", + "metadata": { + "id": "8189c4be" + }, + "source": [ + "### πŸŽ₯ What You'll Build\n", + "\n", + "By the end of this notebook, you’ll: \n", + "- Connect & manage **7 synchronized streams** \n", + "- Run **AI-powered scene analysis** \n", + "- Set up **intelligent event detection & alerts** \n", + "- Create **multi-camera evidence assets** for investigations\n", + "\n", + "\n", + "*This demo shows how AI turns raw surveillance feeds into actionable intelligence.*" + ] + }, + { + "cell_type": "markdown", + "id": "5c31541f", + "metadata": { + "id": "5c31541f" + }, + "source": [ + "---\n", + "### πŸ“¦ Step 1: Install Dependencies\n", + "\n", + "First, let's install the VideoDB SDK and additional packages needed for multi-camera processing.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "67b0b4ad", + "metadata": { + "id": "67b0b4ad" + }, + "outputs": [], + "source": [ + "%pip -q install videodb" + ] + }, + { + "cell_type": "markdown", + "id": "0b991eb3", + "metadata": { + "id": "0b991eb3" + }, + "source": [ + "---\n", + "### πŸ”— Step 2: Connect to VideoDB\n", + "\n", + "Next, let’s establish a connection to **VideoDB** so we can manage multi-camera streams seamlessly.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "503c50f0", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "503c50f0", + "outputId": "48b2d5d7-93d0-4778-aaaa-42aaad701f00" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Connected to VideoDB securely!\n" + ] + } + ], + "source": [ + "import videodb\n", + "import os\n", + "from getpass import getpass\n", + "\n", + "api_key = getpass(\"Please enter your VideoDB API Key: \")\n", + "\n", + "os.environ[\"VIDEO_DB_API_KEY\"] = api_key\n", + "\n", + "conn = videodb.connect()\n", + "coll = conn.get_collection()\n", + "\n", + "print(\"Connected to VideoDB securely!\")" + ] + }, + { + "cell_type": "markdown", + "id": "ed90a8b8", + "metadata": { + "id": "ed90a8b8" + }, + "source": [ + "---\n", + "### πŸŽ₯ Step 3: Configure Multi-Camera Streams\n", + "\n", + "Now, we'll set up our multi-camera surveillance system. For this demonstration, we're monitoring key public spaces across a city with **seven strategically placed cameras** to ensure public safety and operational efficiency.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c94b21f0", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "c94b21f0", + "outputId": "0bacd36d-12e9-40a6-ed7b-6177375c745a" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "🏟️ Setting up: πŸ™οΈ City Public Square Surveillance\n", + "πŸ“Ή Camera count: 7\n", + "\n", + "πŸ“‹ Camera Layout:\n", + " CAM1: Plaza Overview (High Angle - East)\n", + " CAM2: Main Walkway Cam (Walkway - Close Up)\n", + " CAM3: Stairway Junction (Stairs - Junction)\n", + " CAM4: Central Plaza Cam (Center Plaza - Mid Angle)\n", + " CAM5: Building Entrance Cam (Building Steps - Eye Level)\n", + " CAM6: Wide Plaza View (Wide Angle - West)\n", + " CAM7: Ground-Level Cross View (Ground Level - Center)\n" + ] + } + ], + "source": [ + "# Multi-camera configuration for public surveillance\n", + "CAMERA_CONFIG = {\n", + " \"setting_name\": \"πŸ™οΈ City Public Square Surveillance\",\n", + " \"cameras\": {\n", + " \"cam1\": {\n", + " \"name\": \"Plaza Overview\" ,\n", + " \"rtsp_url\": \"rtsp://samples.rts.videodb.io:8554/pub-cam1\",\n", + " \"position\": \"High Angle - East\",\n", + " \"description\": \"Provides a wide-angle overview of the plaza from a high vantage point, monitoring general crowd flow.\"\n", + " },\n", + " \"cam2\": {\n", + " \"name\": \"Main Walkway Cam\",\n", + " \"rtsp_url\": \"rtsp://samples.rts.videodb.io:8554/pub-cam2\",\n", + " \"position\": \"Walkway - Close Up\",\n", + " \"description\": \"Focuses on the main pedestrian walkway, capturing close-up details of foot traffic.\"\n", + " },\n", + " \"cam3\": {\n", + " \"name\": \"Stairway Junction\",\n", + " \"rtsp_url\": \"rtsp://samples.rts.videodb.io:8554/pub-cam3\",\n", + " \"position\": \"Stairs - Junction\",\n", + " \"description\": \"Monitors the area where the main plaza meets the stairs, a key junction point.\"\n", + " },\n", + " \"cam4\": {\n", + " \"name\": \"Central Plaza Cam\",\n", + " \"rtsp_url\": \"rtsp://samples.rts.videodb.io:8554/pub-cam4\",\n", + " \"position\": \"Center Plaza - Mid Angle\",\n", + " \"description\": \"Offers a comprehensive view of the central area of the plaza.\"\n", + " },\n", + " \"cam5\": {\n", + " \"name\": \"Building Entrance Cam\",\n", + " \"rtsp_url\": \"rtsp://samples.rts.videodb.io:8554/pub-cam5\",\n", + " \"position\": \"Building Steps - Eye Level\",\n", + " \"description\": \"Monitors the steps leading up to the main building entrance.\"\n", + " },\n", + " \"cam6\": {\n", + " \"name\": \"Wide Plaza View\",\n", + " \"rtsp_url\": \"rtsp://samples.rts.videodb.io:8554/pub-cam6\",\n", + " \"position\": \"Wide Angle - West\",\n", + " \"description\": \"Captures a wide shot of the plaza, including the building facade and surrounding areas.\"\n", + " },\n", + " \"cam7\": {\n", + " \"name\": \"Ground-Level Cross View\",\n", + " \"rtsp_url\": \"rtsp://samples.rts.videodb.io:8554/pub-cam7\",\n", + " \"position\": \"Ground Level - Center\",\n", + " \"description\": \"Provides a ground-level view across the plaza, tracking movement between different areas.\"\n", + " }\n", + " }\n", + "}\n", + "\n", + "print(f\"🏟️ Setting up: {CAMERA_CONFIG['setting_name']}\")\n", + "print(f\"πŸ“Ή Camera count: {len(CAMERA_CONFIG['cameras'])}\")\n", + "print(\"\\nπŸ“‹ Camera Layout:\")\n", + "for cam_id, cam_info in CAMERA_CONFIG['cameras'].items():\n", + " print(f\" {cam_id.upper()}: {cam_info['name']} ({cam_info['position']})\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "yHluoz6nVodL", + "metadata": { + "id": "yHluoz6nVodL" + }, + "source": [ + "#### πŸŽ₯ Live Preview of Raw Streams" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ES-XPSeG_hrx", + "metadata": { + "colab": { + "background_save": true, + "base_uri": "https://localhost:8080/" + }, + "id": "ES-XPSeG_hrx", + "outputId": "f7638f04-0704-4b58-dc99-000f0ad95a97" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸŽ₯ Connecting to all cameras...\n", + "\n" + ] + } + ], + "source": [ + "import os\n", + "from concurrent.futures import ThreadPoolExecutor, as_completed\n", + "from IPython.display import Video, display\n", + "\n", + "def connect_and_export_stream(rtsp_url, cam_name, duration=5, output_file=\"clip.mp4\"):\n", + " if os.path.exists(output_file): os.remove(output_file)\n", + " os.system(\n", + " f'ffmpeg -loglevel error -hide_banner -y -rtsp_transport tcp -i \"{rtsp_url}\" -t {duration} -c copy \"{output_file}\"'\n", + " )\n", + " if os.path.exists(output_file):\n", + " return cam_name, Video(output_file, embed=True, width=600)\n", + " else:\n", + " return cam_name, None\n", + "\n", + "CLIP_DURATION = 10 #Set the Duration here for the clip time\n", + "\n", + "print(\"πŸŽ₯ Connecting to all cameras...\\n\")\n", + "futures = []\n", + "with ThreadPoolExecutor() as executor:\n", + " for cam_id, cam_info in CAMERA_CONFIG[\"cameras\"].items():\n", + " out_file = f\"{cam_id}_clip.mp4\"\n", + " futures.append(\n", + " executor.submit(connect_and_export_stream, cam_info[\"rtsp_url\"], cam_info[\"name\"], CLIP_DURATION, out_file)\n", + " )\n", + "\n", + " for future in as_completed(futures):\n", + " cam_name, clip = future.result()\n", + " if clip:\n", + " print(f\"βœ… {cam_name} ready:\")\n", + " display(clip)\n", + " else:\n", + " print(f\"❌ Failed to capture {cam_name}\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "842cced0", + "metadata": { + "id": "842cced0" + }, + "source": [ + "---\n", + "### 🎯 Step 4: Connect All Camera Streams\n", + "\n", + "Now, let’s connect all **four camera streams** to **VideoDB RTStream** and run them in sync.\n" + ] + }, + { + "cell_type": "markdown", + "id": "c08a4222", + "metadata": { + "id": "c08a4222" + }, + "source": [ + "*Get all streams*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ee004661", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "ee004661", + "outputId": "21116c06-4068-464a-9bf1-39d114941877" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸ”Œ Connecting to all camera streams (reusing existing where possible)...\n", + "\n" + ] + } + ], + "source": [ + "# Connect all cameras while reusing existing streams if they already exist\n", + "connected_streams = {}\n", + "print(\"πŸ”Œ Connecting to all camera streams (reusing existing where possible)...\\n\")\n", + "\n", + "# Pre-fetch existing streams once to minimize API calls\n", + "try:\n", + " existing_streams = {getattr(s, \"name\", \"\"): s for s in coll.list_rtstreams()}\n", + "except Exception as e:\n", + " print(f\"⚠️ Could not list existing streams: {e}\")\n", + " existing_streams = {}" + ] + }, + { + "cell_type": "markdown", + "id": "620e7c0c", + "metadata": { + "id": "620e7c0c" + }, + "source": [ + "*Using existing streams if available*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f9eb46fb", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "f9eb46fb", + "outputId": "12899541-5014-4df1-d404-afa5d0bc8490" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸ“Ή CAM1: Using existing stream 'πŸ™οΈ City Public Square Surveillance - Plaza Overview' (rts-0199346c-f5f6-7031-a794-198651733c3f)\n", + " ▢️ Started existing stream: rts-0199346c-f5f6-7031-a794-198651733c3f and URL: rtsp://samples.rts.videodb.io:8554/pub-cam1\n", + "πŸ“Ή CAM2: Using existing stream 'πŸ™οΈ City Public Square Surveillance - Main Walkway Cam' (rts-0199346c-f720-7341-b813-ee7fb9ea4aa1)\n", + " ▢️ Started existing stream: rts-0199346c-f720-7341-b813-ee7fb9ea4aa1 and URL: rtsp://samples.rts.videodb.io:8554/pub-cam2\n", + "πŸ“Ή CAM3: Using existing stream 'πŸ™οΈ City Public Square Surveillance - Stairway Junction' (rts-0199346c-f7ba-7033-ac76-3507ea9b1089)\n", + " ▢️ Started existing stream: rts-0199346c-f7ba-7033-ac76-3507ea9b1089 and URL: rtsp://samples.rts.videodb.io:8554/pub-cam3\n", + "πŸ“Ή CAM4: Using existing stream 'πŸ™οΈ City Public Square Surveillance - Central Plaza Cam' (rts-0199346c-f854-7eb2-b40c-bac41b5dd7ab)\n", + " ▢️ Started existing stream: rts-0199346c-f854-7eb2-b40c-bac41b5dd7ab and URL: rtsp://samples.rts.videodb.io:8554/pub-cam4\n", + "πŸ“Ή CAM5: Creating new stream 'πŸ™οΈ City Public Square Surveillance - Building Entrance Cam'...\n", + " βœ… Connected: rts-01993492-a8ec-7ee0-8f0a-66ae69adb9f2 with URL: rtsp://samples.rts.videodb.io:8554/pub-cam5\n", + "πŸ“Ή CAM6: Creating new stream 'πŸ™οΈ City Public Square Surveillance - Wide Plaza View'...\n", + " βœ… Connected: rts-01993492-aa3e-7920-95eb-31c1fb731f4d with URL: rtsp://samples.rts.videodb.io:8554/pub-cam6\n", + "πŸ“Ή CAM7: Creating new stream 'πŸ™οΈ City Public Square Surveillance - Ground-Level Cross View'...\n", + " βœ… Connected: rts-01993492-ab3b-7203-89e9-4b2b842fb4c5 with URL: rtsp://samples.rts.videodb.io:8554/pub-cam7\n", + "\n", + "🎯 Connection Summary: 7/7 cameras ready\n", + "πŸš€ Multi-camera system ready for scene indexing!\n" + ] + } + ], + "source": [ + "for cam_id, cam_info in CAMERA_CONFIG[\"cameras\"].items():\n", + " name = f\"{CAMERA_CONFIG['setting_name']} - {cam_info['name']}\"\n", + " existing = existing_streams.get(name)\n", + "\n", + " try:\n", + " if existing:\n", + " print(f\"πŸ“Ή {cam_id.upper()}: Using existing stream '{name}' ({existing.id})\")\n", + " if getattr(existing, \"status\", None) != \"connected\":\n", + " try:\n", + " existing.start()\n", + " print(f\" ▢️ Started existing stream: {existing.id} and URL: {cam_info['rtsp_url']}\")\n", + " except Exception as se:\n", + " print(f\" ⚠️ Could not start existing stream: {se}\")\n", + " connected_streams[cam_id] = {\"stream\": existing, \"info\": cam_info, \"status\": \"connected\"}\n", + " else:\n", + " print(f\"πŸ“Ή {cam_id.upper()}: Creating new stream '{name}'...\")\n", + " stream = coll.connect_rtstream(name=name, url=cam_info[\"rtsp_url\"])\n", + " print(f\" βœ… Connected: {stream.id} with URL: {cam_info['rtsp_url']}\")\n", + " connected_streams[cam_id] = {\"stream\": stream, \"info\": cam_info, \"status\": \"connected\"}\n", + "\n", + " except Exception as e:\n", + " print(f\" ❌ {cam_id.upper()} failed: {e}\")\n", + " connected_streams[cam_id] = {\"stream\": None, \"info\": cam_info, \"status\": \"failed\", \"error\": str(e)}\n", + "\n", + "# Summary\n", + "success = sum(1 for s in connected_streams.values() if s[\"status\"] == \"connected\")\n", + "print(f\"\\n🎯 Connection Summary: {success}/{len(CAMERA_CONFIG['cameras'])} cameras ready\")\n", + "print(\"πŸš€ Multi-camera system ready for scene indexing!\" if success else \"⚠️ No cameras ready. Check RTSP URLs/credentials.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "3f9748fa", + "metadata": { + "id": "3f9748fa" + }, + "source": [ + "---\n", + "### πŸ—‚οΈ Step 5: Set Up Scene Indexing\n", + "\n", + "Now we’ll build **scene indexes** for each camera stream, enabling AI-powered analysis across all viewpoints.\n" + ] + }, + { + "cell_type": "markdown", + "id": "b0dbea37", + "metadata": { + "id": "b0dbea37" + }, + "source": [ + "*a. Index Config*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "eff2d2c3", + "metadata": { + "id": "eff2d2c3" + }, + "outputs": [], + "source": [ + "from videodb import SceneExtractionType\n", + "\n", + "# Should be generic for indexing\n", + "\n", + "# Scene indexing configuration\n", + "SCENE_INDEX_CONFIG = {\n", + " \"extraction_type\": SceneExtractionType.time_based,\n", + " \"extraction_config\": {\n", + " \"time\": 15, # Analyze every 15 seconds\n", + " \"frame_count\": 1\n", + " },\n", + " \"prompt\": \"\"\"Analyze this public surveillance footage and identify key activities. Describe:\n", + " 1. Individuals or groups with notable items (e.g., Suspicious People Gatherings, People with weired lugguage)\n", + " 2. Crowd behavior (e.g., people gathering in large groups, sudden dispersal, running).\n", + " 3. Vehicle activity (e.g., cars stopping in unusual places, prolonged idling, vans or trucks).\n", + " 4. Unusual object detection (e.g., unattended bags, boxes left in public spaces).\n", + " 5. General patterns of movement and any deviations from the norm.\n", + "\n", + " Be specific about the appearance of individuals and the location of events within this camera's view.\"\"\"\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "7278ba3d", + "metadata": { + "id": "7278ba3d" + }, + "source": [ + "*b. Setup Index*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c51e279", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7c51e279", + "outputId": "2d972b0e-885c-4404-8ff0-7d7c3fb3a8b1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸ“‘ CAM1 (Plaza Overview):\n", + " β€’ Public_Square_Surveillance1_CAM1_Index (ID: 75e34af6516a72c8, Status: running)\n", + "πŸ“‘ CAM2 (Main Walkway Cam):\n", + " β€’ Public_Square_Surveillance1_CAM2_Index (ID: d64732ea82c526d5, Status: running)\n", + "πŸ“‘ CAM3 (Stairway Junction):\n", + " β€’ Public_Square_Surveillance1_CAM3_Index (ID: 8276f034438e631b, Status: running)\n", + "πŸ“‘ CAM4 (Central Plaza Cam):\n", + " β€’ Public_Square_Surveillance1_CAM4_Index (ID: d77035b3d68d94df, Status: running)\n", + "πŸ“‘ CAM5: No indexes found\n", + "πŸ“‘ CAM6: No indexes found\n", + "πŸ“‘ CAM7: No indexes found\n" + ] + } + ], + "source": [ + "# πŸ” List existing scene indexes for connected cameras\n", + "for cam_id, cam_data in connected_streams.items():\n", + " if cam_data[\"status\"] != \"connected\":\n", + " print(f\"⏭️ {cam_id.upper()}: Stream not connected\")\n", + " continue\n", + "\n", + " try:\n", + " indexes = cam_data[\"stream\"].list_scene_indexes()\n", + " if indexes:\n", + " print(f\"πŸ“‘ {cam_id.upper()} ({cam_data['info']['name']}):\")\n", + " for idx in indexes:\n", + " print(f\" β€’ {idx.name} (ID: {idx.rtstream_index_id}, Status: {getattr(idx, 'status', 'unknown')})\")\n", + " else:\n", + " print(f\"πŸ“‘ {cam_id.upper()}: No indexes found\")\n", + " except Exception as e:\n", + " print(f\"⚠️ {cam_id.upper()}: Failed to list indexes ({e})\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f7645ba0", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "f7645ba0", + "outputId": "d52ac0ce-6a5e-4011-92f3-555b420d396c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸ”§ Setting up scene indexes...\n", + "\n", + "πŸ“Š CAM1: Using existing index 'Public_Square_Surveillance1_CAM1_Index' (75e34af6516a72c8)\n", + "πŸ“Š CAM2: Using existing index 'Public_Square_Surveillance1_CAM2_Index' (d64732ea82c526d5)\n", + "πŸ“Š CAM3: Using existing index 'Public_Square_Surveillance1_CAM3_Index' (8276f034438e631b)\n", + "πŸ“Š CAM4: Using existing index 'Public_Square_Surveillance1_CAM4_Index' (d77035b3d68d94df)\n", + "πŸ“Š CAM5: Creating new index 'Public_Square_Surveillance1_CAM5_Index'...\n", + " βœ… Index created: ea818056aa10495f\n", + "πŸ“Š CAM6: Creating new index 'Public_Square_Surveillance1_CAM6_Index'...\n", + " βœ… Index created: 3ae1aa10fb69e67f\n", + "πŸ“Š CAM7: Creating new index 'Public_Square_Surveillance1_CAM7_Index'...\n", + " βœ… Index created: ab557d9e8888caa7\n", + "\n", + "🎯 Scene Indexing Summary: 7/7 active\n", + "πŸ” AI analysis running (every 15s)\n" + ] + } + ], + "source": [ + "# βš™οΈ Setup or reuse scene indexes\n", + "scene_indexes = {}\n", + "print(\"πŸ”§ Setting up scene indexes...\\n\")\n", + "\n", + "for cam_id, cam_data in connected_streams.items():\n", + " if cam_data[\"status\"] != \"connected\":\n", + " continue\n", + "\n", + " stream = cam_data[\"stream\"]\n", + " name = f\"Public_Square_Surveillance1_{cam_id.upper()}_Index\"\n", + "\n", + " try:\n", + " # Check if an index with this name exists\n", + " existing = next((idx for idx in stream.list_scene_indexes() if getattr(idx, \"name\", \"\") == name), None)\n", + "\n", + " if existing:\n", + " print(f\"πŸ“Š {cam_id.upper()}: Using existing index '{name}' ({existing.rtstream_index_id})\")\n", + " if getattr(existing, \"status\", None) not in (\"running\", \"active\", \"connected\"):\n", + " try:\n", + " existing.start()\n", + " print(f\" ▢️ Started index: {existing.rtstream_index_id}\")\n", + " except Exception as se:\n", + " print(f\" ⚠️ Could not start index: {se}\")\n", + " scene_indexes[cam_id] = {\"index\": existing, \"index_id\": existing.rtstream_index_id, \"status\": \"active\"}\n", + " else:\n", + " print(f\"πŸ“Š {cam_id.upper()}: Creating new index '{name}'...\")\n", + " new_idx = stream.index_scenes(\n", + " extraction_type=SCENE_INDEX_CONFIG[\"extraction_type\"],\n", + " extraction_config=SCENE_INDEX_CONFIG[\"extraction_config\"],\n", + " prompt=SCENE_INDEX_CONFIG[\"prompt\"],\n", + " name=name,\n", + " )\n", + " scene_indexes[cam_id] = {\"index\": new_idx, \"index_id\": new_idx.rtstream_index_id, \"status\": \"active\"}\n", + " print(f\" βœ… Index created: {new_idx.rtstream_index_id}\")\n", + "\n", + " except Exception as e:\n", + " print(f\"❌ {cam_id.upper()}: Failed to setup index ({e})\")\n", + " scene_indexes[cam_id] = {\"index\": None, \"index_id\": None, \"status\": \"failed\", \"error\": str(e)}\n", + "\n", + "# Summary\n", + "active = sum(1 for idx in scene_indexes.values() if idx[\"status\"] == \"active\")\n", + "print(f\"\\n🎯 Scene Indexing Summary: {active}/{len(connected_streams)} active\")\n", + "print(\"πŸ” AI analysis running (every 15s)\" if active else \"⚠️ No indexes active. Check streams and retry.\")" + ] + }, + { + "cell_type": "markdown", + "id": "413cdf5d", + "metadata": { + "id": "413cdf5d" + }, + "source": [ + "---\n", + "## 🚨 Phase 2: Set and Receive Alerts\n", + "\n", + "In this phase, we’ll configure alert rules and handle incoming alerts from the multi-camera streams." + ] + }, + { + "cell_type": "markdown", + "id": "7445a33b", + "metadata": { + "id": "7445a33b" + }, + "source": [ + "### βš™οΈ Step 1: Configure and setup events" + ] + }, + { + "cell_type": "markdown", + "id": "52be5d2e", + "metadata": { + "id": "52be5d2e" + }, + "source": [ + "*Alert Config & Events*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "af4de49f", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "af4de49f", + "outputId": "444ace55-3de6-4eeb-c528-c5cf72201011" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸ“‘ Existing events found:\n", + " β€’ timeout_called (0bb5f68981208827)\n", + " β€’ person_with_trolley (0c9831d3a5d702b3)\n", + " β€’ large_crowd_formation (1a5585203e9374d1)\n", + " β€’ suspicious_loitering (3307388de055afbe)\n", + " β€’ basket scored (49977929774f8cd7)\n", + " β€’ player foul (53409a9e89f66846)\n", + " β€’ basket_scored (6c08568f54b59644)\n", + " β€’ unattended_luggage (87a452a83bc362ad)\n", + " β€’ timeout called (902fdd5571bacf30)\n", + " β€’ woman_in_red_coat (afe2776cf3f4608c)\n", + " β€’ player_foul (cab9b3430d908e31)\n" + ] + } + ], + "source": [ + "# Define surveillance events\n", + "EVENTS_CONFIG = [\n", + " {\n", + " \"label\": \"unattended_luggage\",\n", + " \"prompt\": \"Detect any luggage, backpacks, or packages left unattended for more than a minute.\",\n", + " \"description\": \"Unattended luggage detected\"\n", + " },\n", + " {\n", + " \"label\": \"large_crowd_formation\",\n", + " \"prompt\": \"Identify scenes where a large group of people (more than 4) gathers quickly in a concentrated area.\",\n", + " \"description\": \"Large crowd forming\"\n", + " },\n", + " {\n", + " \"label\": \"person_with_trolley\",\n", + " \"prompt\": \"Detect any person walking with a trolley bag or rolling suitcase.\",\n", + " \"description\": \"Person with trolley bag\"\n", + " },\n", + " {\n", + " \"label\": \"woman_in_red_coat\",\n", + " \"prompt\": \"Identify any woman wearing a distinct red coat or jacket.\",\n", + " \"description\": \"Woman in red coat spotted\"\n", + " },\n", + " {\n", + " \"label\": \"suspicious_loitering\",\n", + " \"prompt\": \"Detect individuals or small groups loitering in the same spot for an extended period without clear purpose.\",\n", + " \"description\": \"Suspicious loitering detected\"\n", + " }\n", + "]\n", + "\n", + "# πŸ” List existing events in VideoDB\n", + "existing_events_by_label = {}\n", + "try:\n", + " for evt in conn.list_events():\n", + " lbl = evt.get(\"label\")\n", + " eid = evt.get(\"event_id\")\n", + " if lbl and eid:\n", + " existing_events_by_label[lbl] = eid\n", + " if existing_events_by_label:\n", + " print(\"πŸ“‘ Existing events found:\")\n", + " for lbl, eid in existing_events_by_label.items():\n", + " print(f\" β€’ {lbl} ({eid})\")\n", + " else:\n", + " print(\"πŸ“‘ No existing events found.\")\n", + "except Exception as e:\n", + " print(f\"⚠️ Could not list existing events: {e}\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "58d501d8", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "58d501d8", + "outputId": "5f191c6b-45b7-4fa2-ec0e-78e72c222648" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "🎯 Setting up cross-camera event detection...\n", + "\n", + "πŸ“Ž Using existing event: unattended_luggage (87a452a83bc362ad)\n", + "πŸ“Ž Using existing event: large_crowd_formation (1a5585203e9374d1)\n", + "πŸ“Ž Using existing event: person_with_trolley (0c9831d3a5d702b3)\n", + "πŸ“Ž Using existing event: woman_in_red_coat (afe2776cf3f4608c)\n", + "πŸ“Ž Using existing event: suspicious_loitering (3307388de055afbe)\n", + "\n", + "🎯 Events ready: 5 existing, 0 created (5/5)\n", + "πŸš€ Event detection system ready! Monitoring for:\n", + " β€’ unattended_luggage: Unattended luggage detected\n", + " β€’ large_crowd_formation: Large crowd forming\n", + " β€’ person_with_trolley: Person with trolley bag\n", + " β€’ woman_in_red_coat: Woman in red coat spotted\n", + " β€’ suspicious_loitering: Suspicious loitering detected\n" + ] + } + ], + "source": [ + "# πŸš€ Create or reuse events in VideoDB\n", + "created_events = {}\n", + "print(\"\\n🎯 Setting up cross-camera event detection...\\n\")\n", + "\n", + "for cfg in EVENTS_CONFIG:\n", + " label = cfg[\"label\"]\n", + "\n", + " if label in existing_events_by_label:\n", + " event_id = existing_events_by_label[label]\n", + " created_events[label] = {\"event_id\": event_id, \"config\": cfg, \"status\": \"existing\"}\n", + " print(f\"πŸ“Ž Using existing event: {label} ({event_id})\")\n", + " continue\n", + "\n", + " try:\n", + " print(f\"πŸ“ Creating event: {label}...\")\n", + " event_id = conn.create_event(event_prompt=cfg[\"prompt\"], label=label)\n", + " created_events[label] = {\"event_id\": event_id, \"config\": cfg, \"status\": \"created\"}\n", + " print(f\" βœ… Created: {event_id}\")\n", + " except Exception as e:\n", + " print(f\" ❌ Failed: {e}\")\n", + " created_events[label] = {\"event_id\": None, \"config\": cfg, \"status\": \"failed\", \"error\": str(e)}\n", + "\n", + "# πŸ“Š Summary\n", + "created = sum(1 for e in created_events.values() if e[\"status\"] == \"created\")\n", + "existing = sum(1 for e in created_events.values() if e[\"status\"] == \"existing\")\n", + "print(f\"\\n🎯 Events ready: {existing} existing, {created} created ({existing+created}/{len(EVENTS_CONFIG)})\")\n", + "\n", + "if existing + created:\n", + " print(\"πŸš€ Event detection system ready! Monitoring for:\")\n", + " for lbl, evt in created_events.items():\n", + " if evt[\"status\"] in (\"created\", \"existing\"):\n", + " print(f\" β€’ {lbl}: {evt['config']['description']}\")\n", + "else:\n", + " print(\"⚠️ No events ready. Check configuration and retry.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "b913d231", + "metadata": { + "id": "b913d231" + }, + "source": [ + "---\n", + "### 🌐 Step 2: Configure Webhook & Callback" + ] + }, + { + "cell_type": "markdown", + "id": "100c576e", + "metadata": { + "id": "100c576e" + }, + "source": [ + "*a. install pyngrok*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "defe99fb", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "defe99fb", + "outputId": "7715b524-c7c5-4972-8fe8-bd5205f163c1" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: pyngrok in /usr/local/lib/python3.12/dist-packages (7.3.0)\n", + "Requirement already satisfied: PyYAML>=5.1 in /usr/local/lib/python3.12/dist-packages (from pyngrok) (6.0.2)\n", + "Requirement already satisfied: flask in /usr/local/lib/python3.12/dist-packages (3.1.2)\n", + "Requirement already satisfied: blinker>=1.9.0 in /usr/local/lib/python3.12/dist-packages (from flask) (1.9.0)\n", + "Requirement already satisfied: click>=8.1.3 in /usr/local/lib/python3.12/dist-packages (from flask) (8.2.1)\n", + "Requirement already satisfied: itsdangerous>=2.2.0 in /usr/local/lib/python3.12/dist-packages (from flask) (2.2.0)\n", + "Requirement already satisfied: jinja2>=3.1.2 in /usr/local/lib/python3.12/dist-packages (from flask) (3.1.6)\n", + "Requirement already satisfied: markupsafe>=2.1.1 in /usr/local/lib/python3.12/dist-packages (from flask) (3.0.2)\n", + "Requirement already satisfied: werkzeug>=3.1.0 in /usr/local/lib/python3.12/dist-packages (from flask) (3.1.3)\n" + ] + } + ], + "source": [ + "!pip install pyngrok\n", + "!pip install flask" + ] + }, + { + "cell_type": "markdown", + "id": "a15c4ee8", + "metadata": { + "id": "a15c4ee8" + }, + "source": [ + "*b. Expose a Public Webhook URL (ngrok or fallback)*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d1b18cea", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "d1b18cea", + "outputId": "c2026d8d-bbac-4d39-bb43-5018091b3522" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "🌍 Public webhook URL: https://9904b2f1d7e2.ngrok-free.app/webhook\n", + "βœ… Callback URL configured: https://9904b2f1d7e2.ngrok-free.app/webhook\n" + ] + } + ], + "source": [ + "import os\n", + "import socket\n", + "from pyngrok import ngrok\n", + "from google.colab import userdata\n", + "\n", + "def choose_port(start=5001, tries=5):\n", + " \"\"\"Pick an available local port (default: 5001–5005).\"\"\"\n", + " for p in range(start, start + tries):\n", + " with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:\n", + " try:\n", + " s.bind((\"0.0.0.0\", p))\n", + " return p\n", + " except OSError:\n", + " continue\n", + " return start\n", + "\n", + "try:\n", + " # Authenticate ngrok if token available\n", + " # token = os.getenv(\"NGROK_AUTHTOKEN\", \"\").strip()\n", + " token = userdata.get('ngrok_auth')\n", + " if token:\n", + " ngrok.set_auth_token(token)\n", + "\n", + " WEBHOOK_PORT = choose_port()\n", + " tunnel = ngrok.connect(WEBHOOK_PORT)\n", + " PUBLIC_WEBHOOK_URL = f\"{tunnel.public_url}/webhook\"\n", + " print(f\"🌍 Public webhook URL: {PUBLIC_WEBHOOK_URL}\")\n", + "except Exception as e:\n", + " PUBLIC_WEBHOOK_URL = \"\"\n", + " print(f\"⚠️ ngrok tunnel not started: {e}\")\n", + " print(\"➑️ Set `WEBHOOK_URL` manually if using external service (Zapier, Pipedream, etc.).\")\n", + "\n", + "# Unified callback (used when creating alerts)\n", + "ALERT_CALLBACK_URL = PUBLIC_WEBHOOK_URL or \"\"\n", + "if ALERT_CALLBACK_URL:\n", + " print(f\"βœ… Callback URL configured: {ALERT_CALLBACK_URL}\")\n", + "else:\n", + " print(\"⚠️ No callback URL configured β€” alerts won’t trigger notifications.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "e60ddcc0", + "metadata": { + "id": "e60ddcc0" + }, + "source": [ + "*c. Webhook Receiver (Flask server)*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c28d6dd", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "4c28d6dd", + "outputId": "042da0da-d3b0-4968-f55d-ac761539f226" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸš€ Webhook server running at http://localhost:5001/webhook\n", + " * Serving Flask app '__main__'\n", + " * Debug mode: off\n" + ] + } + ], + "source": [ + "import time\n", + "import threading\n", + "from flask import Flask, request, jsonify\n", + "\n", + "# In-memory store for webhook calls\n", + "webhook_data = globals().get(\"webhook_data\", [])\n", + "webhook_meta = globals().get(\"webhook_meta\", {\"started_at\": time.time(), \"count\": 0})\n", + "\n", + "app = Flask(__name__)\n", + "\n", + "@app.route(\"/webhook\", methods=[\"POST\"])\n", + "def webhook():\n", + " payload = request.get_json(silent=True) or {}\n", + " webhook_meta[\"count\"] += 1\n", + " webhook_data.append({\n", + " \"received_at\": time.time(),\n", + " \"headers\": dict(request.headers),\n", + " \"data\": payload\n", + " })\n", + " webhook_data[:] = webhook_data[-500:] # keep last 500 only\n", + " print(f\"πŸ“© Webhook #{webhook_meta['count']} received: {payload}\")\n", + " return jsonify({\"status\": \"ok\"})\n", + "\n", + "def run_webhook():\n", + " app.run(host=\"0.0.0.0\", port=WEBHOOK_PORT, debug=False, use_reloader=False)\n", + "\n", + "# Start/reuse thread safely\n", + "webhook_thread = globals().get(\"webhook_thread\")\n", + "if not webhook_thread or not webhook_thread.is_alive():\n", + " webhook_thread = threading.Thread(target=run_webhook, daemon=True)\n", + " webhook_thread.start()\n", + " globals()[\"webhook_thread\"] = webhook_thread\n", + " print(f\"πŸš€ Webhook server running at http://localhost:{WEBHOOK_PORT}/webhook\")\n", + "else:\n", + " print(f\"βœ… Webhook server already running at http://localhost:{WEBHOOK_PORT}/webhook\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "fc75f617", + "metadata": { + "id": "fc75f617" + }, + "source": [ + "---\n", + "### 🚨 Step 3: Multi-Camera Alert System\n", + "\n", + "We’ll now set up an **intelligent alerting pipeline** that continuously monitors all connected cameras. \n", + "Whenever a basketball event is detected, the system will trigger a **real-time notification** enriched with **multi-angle evidence clips** for better context." + ] + }, + { + "cell_type": "markdown", + "id": "7fd399f1", + "metadata": { + "id": "7fd399f1" + }, + "source": [ + "*a. Callback & Setup*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c3a25f4", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "5c3a25f4", + "outputId": "49f289c3-7a42-4306-8226-4f69abc17b0c" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "🚨 Setting up alerts (reuse existing where possible)\n", + "\n" + ] + } + ], + "source": [ + "# 🚨 Step 3A: Prepare callback + alert storage\n", + "\n", + "# Use callback URL from earlier cells\n", + "callback_url = globals().get(\"ALERT_CALLBACK_URL\", \"\")\n", + "if not callback_url:\n", + " print(\"⚠️ No callback URL configured. Alerts will be created but won't send notifications.\")\n", + "\n", + "# Container for alerts per camera\n", + "created_alerts = {}\n", + "print(\"🚨 Setting up alerts (reuse existing where possible)\\n\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "9cac7efd", + "metadata": { + "id": "9cac7efd" + }, + "source": [ + "*b. Create or Reuse Alerts*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "14e7b352", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "14e7b352", + "outputId": "e1c62271-8344-431e-f2bc-b139199de7bd" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "πŸ“Ή CAM1: Plaza Overview\n", + " πŸ“Ž Using existing alert for 'unattended_luggage': 040bb9cbac61956c\n", + " πŸ“Ž Using existing alert for 'large_crowd_formation': 69173b12c4d4fe74\n", + " πŸ“Ž Using existing alert for 'person_with_trolley': 36dcec103d80d324\n", + " πŸ“Ž Using existing alert for 'woman_in_red_coat': 2af505f6bcca2716\n", + " πŸ“Ž Using existing alert for 'suspicious_loitering': 86ab7803b41dab87\n", + "πŸ“Ή CAM2: Main Walkway Cam\n", + " πŸ“Ž Using existing alert for 'unattended_luggage': d907756b1d5ca1a6\n", + " πŸ“Ž Using existing alert for 'large_crowd_formation': 74114e5bb33a688a\n", + " πŸ“Ž Using existing alert for 'person_with_trolley': 65468edea695e727\n", + " πŸ“Ž Using existing alert for 'woman_in_red_coat': 7518c8e83d03fc84\n", + " πŸ“Ž Using existing alert for 'suspicious_loitering': dfeebae9823eb22c\n", + "πŸ“Ή CAM3: Stairway Junction\n", + " πŸ“Ž Using existing alert for 'unattended_luggage': d18468fac1f3a9c1\n", + " πŸ“Ž Using existing alert for 'large_crowd_formation': a2e4b507510597a4\n", + " πŸ“Ž Using existing alert for 'person_with_trolley': 8e620aa7d4257127\n", + " πŸ“Ž Using existing alert for 'woman_in_red_coat': fb0503165717edfb\n", + " πŸ“Ž Using existing alert for 'suspicious_loitering': d332312d90423a0a\n", + "πŸ“Ή CAM4: Central Plaza Cam\n", + " πŸ“Ž Using existing alert for 'unattended_luggage': 599498ecfd109572\n", + " πŸ“Ž Using existing alert for 'large_crowd_formation': e317992181c176ad\n", + " πŸ“Ž Using existing alert for 'person_with_trolley': e4f94c94986ce196\n", + " πŸ“Ž Using existing alert for 'woman_in_red_coat': 2630033bf1d13208\n", + " πŸ“Ž Using existing alert for 'suspicious_loitering': 465a2ea9e6c31f30\n", + "πŸ“Ή CAM5: Building Entrance Cam\n", + " βœ… Created alert for 'unattended_luggage': 6efc76b66887435d\n", + " βœ… Created alert for 'large_crowd_formation': 11b4507f72787cb2\n", + " βœ… Created alert for 'person_with_trolley': 5182e972ba16b30f\n", + " βœ… Created alert for 'woman_in_red_coat': 701f2e447ece68fd\n", + " βœ… Created alert for 'suspicious_loitering': 4d8b9748dd08ec85\n", + "πŸ“Ή CAM6: Wide Plaza View\n", + " βœ… Created alert for 'unattended_luggage': 050d4bf992af4425\n", + " βœ… Created alert for 'large_crowd_formation': b6a7042c1936e18c\n", + " βœ… Created alert for 'person_with_trolley': feb8e5fdefd006e9\n", + " βœ… Created alert for 'woman_in_red_coat': 7ef4321d5b2647c8\n", + " βœ… Created alert for 'suspicious_loitering': 20d7eb2857cca8c9\n", + "πŸ“Ή CAM7: Ground-Level Cross View\n", + " βœ… Created alert for 'unattended_luggage': 2c9ad8755c145097\n", + " βœ… Created alert for 'large_crowd_formation': afefccab139b51a0\n", + " βœ… Created alert for 'person_with_trolley': 56a4f53fbcfa5308\n", + " βœ… Created alert for 'woman_in_red_coat': d8305ff47ca66915\n", + " βœ… Created alert for 'suspicious_loitering': fab4d68330aac68c\n" + ] + } + ], + "source": [ + "for cam_id, idx_data in scene_indexes.items():\n", + " if idx_data.get(\"status\") != \"active\" or not idx_data.get(\"index\"):\n", + " print(f\"⏭️ {cam_id.upper()}: Index not active β†’ skipping alerts\")\n", + " continue\n", + "\n", + " idx = idx_data[\"index\"]\n", + " cam_name = connected_streams[cam_id][\"info\"][\"name\"]\n", + " created_alerts[cam_id] = {}\n", + "\n", + " # Try to list existing alerts for this index\n", + " existing_alerts = {}\n", + " try:\n", + " for a in idx.list_alerts():\n", + " label = a[\"label\"]\n", + " aid = a[\"alert_id\"]\n", + " if label and aid:\n", + " existing_alerts[label] = a\n", + " except Exception as e:\n", + " print(f\" ⚠️ {cam_id.upper()}: Could not list existing alerts: {e}\")\n", + "\n", + " print(f\"πŸ“Ή {cam_id.upper()}: {cam_name}\")\n", + "\n", + " # For each defined event β†’ create/reuse alerts\n", + " for label, evt in created_events.items():\n", + " if evt.get(\"status\") not in (\"existing\", \"created\") or not evt.get(\"event_id\"):\n", + " continue\n", + "\n", + " if label in existing_alerts: # Reuse\n", + " if a[\"status\"] == \"disabled\":\n", + " idx.enable_alert(a[\"alert_id\"])\n", + " a = existing_alerts[label]\n", + " aid = a[\"alert_id\"]\n", + " created_alerts[cam_id][label] = {\"alert_id\": aid, \"event_id\": evt[\"event_id\"], \"status\": \"existing\"}\n", + " print(f\" πŸ“Ž Using existing alert for '{label}': {aid}\")\n", + "\n", + " else: # Create new\n", + " try:\n", + " aid = idx.create_alert(evt[\"event_id\"], callback_url=callback_url or None)\n", + " status = \"active\" if callback_url else \"created_no_webhook\"\n", + " created_alerts[cam_id][label] = {\"alert_id\": aid, \"event_id\": evt[\"event_id\"], \"status\": status}\n", + " msg = f\" βœ… Created alert for '{label}': {aid}\" if callback_url else f\" ⚠️ Created alert for '{label}' (no webhook)\"\n", + " print(msg)\n", + " except Exception as e:\n", + " created_alerts[cam_id][label] = {\"alert_id\": None, \"event_id\": evt[\"event_id\"], \"status\": \"failed\", \"error\": str(e)}\n", + " print(f\" ❌ Failed to create alert for '{label}': {e}\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "90ecb643", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "90ecb643", + "outputId": "918a0daa-7846-461a-bb33-ca0334a25f50" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "🎯 Alert System Summary: 35/35 alerts ready\n", + "πŸ“¬ Alerts will POST to: https://9904b2f1d7e2.ngrok-free.app/webhook\n" + ] + } + ], + "source": [ + "# πŸ“Š Alert system summary\n", + "num_total = sum(len(alerts) for alerts in created_alerts.values())\n", + "num_ready = sum(\n", + " 1 for cam_alerts in created_alerts.values()\n", + " for a in cam_alerts.values()\n", + " if a[\"status\"] in (\"active\", \"existing\", \"created_no_webhook\")\n", + ")\n", + "\n", + "print(f\"\\n🎯 Alert System Summary: {num_ready}/{num_total} alerts ready\")\n", + "if num_ready:\n", + " print(f\"πŸ“¬ Alerts will POST to: {callback_url or '❌ (no webhook set)'}\")\n", + "else:\n", + " print(\"⚠️ No alerts ready. Check indexes, events, and callback URL.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "6ef17e5d", + "metadata": { + "id": "6ef17e5d" + }, + "source": [ + "---\n", + "## πŸ“‘ Phase 3: Alerts & Data Processing\n", + "\n", + "With alerts now streaming in from all cameras, this phase focuses on **capturing, processing, and analyzing** those incoming events. \n", + "We’ll store webhook data in-memory, extract useful context, and prepare it for downstream workflows like dashboards, notifications, or automated actions." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7c02e8ae", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7c02e8ae", + "outputId": "8dca20b5-bc1a-436e-db58-cf2c19ae281c" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "6" + ] + }, + "execution_count": 43, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "len(webhook_data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "dc2f9e47", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "dc2f9e47", + "outputId": "9f5ee0be-9e4b-4534-b572-e8266668a79f" + }, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'received_at': 1757523735.8817182,\n", + " 'headers': {'Host': '9904b2f1d7e2.ngrok-free.app',\n", + " 'User-Agent': 'Python/3.12 aiohttp/3.11.11',\n", + " 'Content-Length': '866',\n", + " 'Accept': '*/*',\n", + " 'Accept-Encoding': 'gzip, deflate',\n", + " 'Content-Type': 'application/json',\n", + " 'X-Forwarded-For': '34.228.137.199',\n", + " 'X-Forwarded-Host': '9904b2f1d7e2.ngrok-free.app',\n", + " 'X-Forwarded-Proto': 'https'},\n", + " 'data': {'event_id': 'event-0c9831d3a5d702b3',\n", + " 'label': 'person_with_trolley',\n", + " 'confidence': 0.95,\n", + " 'explanation': \"The scene analysis explicitly states: 'A female is seen pulling a black, standard-sized rolling suitcase,' which directly matches the alert context for detecting a person with a trolley bag or rolling suitcase.\",\n", + " 'timestamp': '2025-09-10T17:02:15.811085+00:00',\n", + " 'start_time': '2025-09-10T22:31:50.886736+05:30',\n", + " 'end_time': '2025-09-10T22:32:05.886736+05:30',\n", + " 'stream_url': 'https://videodb-rt-streaming-service-us-east-1.s3.us-east-1.amazonaws.com/manifests/rts-01993492-ab3b-7203-89e9-4b2b842fb4c5/1757523710000000-1757523726000000.m3u8',\n", + " 'player_url': 'https://console.videodb.io/player?url=https://videodb-rt-streaming-service-us-east-1.s3.us-east-1.amazonaws.com/manifests/rts-01993492-ab3b-7203-89e9-4b2b842fb4c5/1757523710000000-1757523726000000.m3u8'}}]" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "webhook_data[-1:]" + ] + }, + { + "cell_type": "markdown", + "id": "c999e496", + "metadata": { + "id": "c999e496" + }, + "source": [ + "---\n", + "### 🎯 Step 1: Choose the Event\n", + "\n", + "From the list of recent alerts, select a specific **basketball event** you’d like to explore further. \n", + "This choice will be used to extract the event window (with multiple camera angles) for deeper analysis.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "e4a2eaa6", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "e4a2eaa6", + "outputId": "e7fe261e-2081-4743-eb14-d804981ef3aa" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "πŸ“‹ Recent Alerts (last 10):\n", + "\n", + "1. 🎯 person_with_trolley | βœ… 0.95 | πŸ“ The scene analysis explicitly states: 'A female is seen pulling a black, standard-sized rolling suit...\n", + "2. 🎯 person_with_trolley | βœ… 0.95 | πŸ“ A man with a rolling suitcase was explicitly identified in the mid-left section of the plaza, which ...\n", + "3. 🎯 suspicious_loitering | βœ… 0.8 | πŸ“ An 'Isolated Standing Person with Shoulder Bag' is noted as standing 'relatively still and somewhat ...\n", + "4. 🎯 person_with_trolley | βœ… 1.0 | πŸ“ A male individual was observed pulling a black, standard-sized rolling suitcase, which directly matc...\n", + "5. 🎯 person_with_trolley | βœ… 1.0 | πŸ“ A person pulling a black, standard-sized rolling suitcase was identified on the left side of the pav...\n", + "6. 🎯 person_with_trolley | βœ… 0.95 | πŸ“ The scene analysis explicitly mentions 'A man in a dark jacket and light pants is standing still, lo...\n", + "\n", + "πŸ‘‰ Select an alert (1–6): 5\n", + "\n", + "βœ… Selected: person_with_trolley (Confidence: 1.0)\n" + ] + } + ], + "source": [ + "# πŸ“‘ View & select recent alerts (last 10)\n", + "\n", + "# Keep only the last 10 webhook events\n", + "recent_alerts = webhook_data[:10] if webhook_data else []\n", + "\n", + "if not recent_alerts:\n", + " print(\"⚠️ No alerts received yet.\")\n", + "else:\n", + " # Normalize alerts into a compact list\n", + " cleaned_alerts = []\n", + " for item in recent_alerts:\n", + " data = item.get(\"data\", {})\n", + " cleaned_alerts.append({\n", + " \"label\": data.get(\"label\", \"N/A\"),\n", + " \"confidence\": data.get(\"confidence\", \"N/A\"),\n", + " \"explanation\": (data.get(\"explanation\") or \"\")[:100]+\"...\",\n", + " \"timestamp\": data.get(\"timestamp\", \"N/A\"),\n", + " \"start_time\": data.get(\"start_time\", \"N/A\"),\n", + " \"end_time\": data.get(\"end_time\", \"N/A\"),\n", + " \"stream_url\": data.get(\"stream_url\", \"N/A\"),\n", + " \"player_url\": data.get(\"player_url\", \"N/A\"),\n", + " \"event_id\": data.get(\"event_id\", \"N/A\"),\n", + " })\n", + "\n", + " # Display to user\n", + " print(\"\\nπŸ“‹ Recent Alerts (last 10):\\n\")\n", + " for i, alert in enumerate(cleaned_alerts, 1):\n", + " print(f\"{i}. 🎯 {alert['label']} | \"\n", + " f\"βœ… {alert['confidence']} | πŸ“ {alert['explanation']}\")\n", + "\n", + " # Interactive selection\n", + " try:\n", + " choice = int(input(\"\\nπŸ‘‰ Select an alert (1–{0}): \".format(len(cleaned_alerts))).strip())\n", + " if 1 <= choice <= len(cleaned_alerts):\n", + " selected = cleaned_alerts[choice - 1]\n", + " print(f\"\\nβœ… Selected: {selected['label']} \"\n", + " f\"(Confidence: {selected['confidence']})\")\n", + " else:\n", + " print(\"⚠️ Invalid selection. Please choose a valid alert number.\")\n", + " except (ValueError, EOFError):\n", + " print(\"⚠️ No valid input received. Skipping selection.\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "61e26a45", + "metadata": { + "id": "61e26a45" + }, + "source": [ + "---\n", + "### πŸŽ₯ Step 2: Retrieve Multi-Camera Feeds for the Same Timestamp\n", + "\n", + "Once an event is selected, we’ll fetch the **synchronized video segments** from all connected cameras. \n", + "This ensures you get a **multi-angle replay** of the same moment in time, making analysis more accurate and contextual." + ] + }, + { + "cell_type": "markdown", + "id": "ea9ff451", + "metadata": { + "id": "ea9ff451" + }, + "source": [ + "*a. Parse the timeline*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "49581d68", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "49581d68", + "outputId": "18b22c32-bbf9-490a-d985-af6c41eeafa5" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "⏱️ Time window: 1757523700 β†’ 1757523736 (with Β±10s padding)\n" + ] + } + ], + "source": [ + "import re\n", + "from datetime import datetime, timezone\n", + "\n", + "# βš™οΈ Config: symmetric padding around the alert window\n", + "OFFSET_SECONDS = 10\n", + "\n", + "# πŸ”Ž Regex to extract timestamps from HLS stream URLs\n", + "_STREAM_RE = re.compile(r\"/(\\d{16})-(\\d{16})\\.m3u8\")\n", + "\n", + "def parse_stream_times(url: str):\n", + " \"\"\"Extract start/end timestamps (in seconds) from stream URL.\"\"\"\n", + " if not url:\n", + " return None, None\n", + " match = _STREAM_RE.search(url)\n", + " if not match:\n", + " return None, None\n", + " return int(match[1]) / 1e6, int(match[2]) / 1e6\n", + "\n", + "def parse_iso_ts(ts: str):\n", + " \"\"\"Convert ISO timestamp string to epoch seconds.\"\"\"\n", + " if not ts:\n", + " return None\n", + " dt = datetime.fromisoformat(ts)\n", + " if dt.tzinfo is None:\n", + " dt = dt.replace(tzinfo=timezone.utc)\n", + " return int(dt.timestamp())\n", + "\n", + "# πŸ“¦ Extract payload from selection\n", + "if not selected:\n", + " raise RuntimeError(\"❌ No event data found in webhook payload.\")\n", + "\n", + "label = selected.get(\"label\", \"unknown\")\n", + "confidence = selected.get(\"confidence\", \"N/A\")\n", + "explanation = selected.get(\"explanation\", \"\")\n", + "stream_url = selected.get(\"stream_url\", \"\")\n", + "player_url = selected.get(\"player_url\", \"\")\n", + "event_id = selected.get(\"event_id\", \"\")\n", + "timestamp = selected.get(\"timestamp\", \"\")\n", + "\n", + "# πŸ•’ Resolve event time window\n", + "start_s, end_s = parse_stream_times(stream_url)\n", + "\n", + "if not (start_s and end_s):\n", + " start_s = parse_iso_ts(selected.get(\"start_time\"))\n", + " end_s = parse_iso_ts(selected.get(\"end_time\"))\n", + "\n", + "if not (start_s and end_s):\n", + " raise RuntimeError(\"❌ Could not determine alert time window from payload.\")\n", + "\n", + "# Apply symmetric offset\n", + "start_adj = max(0, int(start_s) - OFFSET_SECONDS)\n", + "end_adj = int(end_s) + OFFSET_SECONDS\n", + "\n", + "print(f\"⏱️ Time window: {start_adj} β†’ {end_adj} (with Β±{OFFSET_SECONDS}s padding)\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "4c52a6b5", + "metadata": { + "id": "4c52a6b5" + }, + "source": [ + "*b. Generating all streams*" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9b2b2232", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9b2b2232", + "outputId": "48e59ea8-5193-4ee9-ad5f-23fc63af06e9" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "======================================================================\n", + "🚨 PROCESSING ALERT: PERSON_WITH_TROLLEY\n", + "======================================================================\n", + "πŸ“Š Confidence: 0.95\n", + "πŸ†” Event ID: event-0c9831d3a5d702b3\n", + "⏰ Detected at: 2025-09-10T17:02:15.811085+00:00\n", + "\n", + "πŸŽ₯ Original Alert Stream: https://console.videodb.io/player?url=https://videodb-rt-streaming-service-us-east-1.s3.us-east-1.amazonaws.com/manifests/rts-01993492-ab3b-7203-89e9-4b2b842fb4c5/1757523710000000-1757523726000000.m3u8\n" + ] + } + ], + "source": [ + "# Display alert details\n", + "print(\"\\n\" + \"=\"*70)\n", + "print(f\"🚨 PROCESSING ALERT: {label.upper()}\")\n", + "print(\"=\"*70)\n", + "print(f\"πŸ“Š Confidence: {confidence}\")\n", + "print(f\"πŸ†” Event ID: {event_id}\")\n", + "print(f\"⏰ Detected at: {timestamp}\")\n", + "# print(f\"πŸ“ Explanation: {explanation}\")\n", + "# print(f\"\\nπŸ• TIME WINDOW:\")\n", + "# print(f\" Original: {datetime.fromtimestamp(int(start_s), timezone.utc)} β†’ {datetime.fromtimestamp(int(end_s), timezone.utc)} UTC\")\n", + "# print(f\" With Β±{OFFSET_SECONDS}s: {datetime.fromtimestamp(start_adj, timezone.utc)} β†’ {datetime.fromtimestamp(end_adj, timezone.utc)} UTC\")\n", + "print(f\"\\nπŸŽ₯ Original Alert Stream: {player_url or stream_url}\")\n", + "\n", + "# Generate synchronized streams for all connected cameras\n", + "if not globals().get(\"connected_streams\"):\n", + " raise RuntimeError(\"connected_streams not found. Run the connection step first.\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35ee6efc", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "35ee6efc", + "outputId": "bca0c7f8-6897-42eb-cd17-efb7b9192b7d" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "πŸ“Ή MULTI-CAMERA SYNCHRONIZED STREAMS:\n", + "----------------------------------------------------------------------\n", + "βœ… CAM1 - Plaza Overview:\n", + "βœ… CAM2 - Main Walkway Cam:\n", + "βœ… CAM3 - Stairway Junction:\n", + "βœ… CAM4 - Central Plaza Cam:\n", + "βœ… CAM5 - Building Entrance Cam:\n", + "βœ… CAM6 - Wide Plaza View:\n", + "βœ… CAM7 - Ground-Level Cross View:\n", + "\n", + "🎯 SUMMARY: Generated 7 synchronized camera streams\n" + ] + } + ], + "source": [ + "from videodb import play_stream\n", + "\n", + "multi_camera_streams = {}\n", + "print(\"\\nπŸ“Ή MULTI-CAMERA SYNCHRONIZED STREAMS:\")\n", + "print(\"-\"*70)\n", + "\n", + "for cam_id, cam_data in connected_streams.items():\n", + " if cam_data.get(\"status\") != \"connected\" or not cam_data.get(\"stream\"):\n", + " print(f\"⏭️ {cam_id.upper()} - {cam_data['info']['name']}: Not connected\")\n", + " continue\n", + "\n", + " try:\n", + " url = cam_data[\"stream\"].generate_stream(start_adj, end_adj)\n", + " player = play_stream(url)\n", + " multi_camera_streams[cam_id] = {\n", + " \"camera_name\": cam_data[\"info\"][\"name\"],\n", + " \"stream_url\": url,\n", + " \"player_url\": player,\n", + " }\n", + " print(f\"βœ… {cam_id.upper()} - {cam_data['info']['name']}:\")\n", + " # print(f\" πŸ“Ί {player}\")\n", + " # print(url)\n", + "\n", + " except Exception as e:\n", + " print(f\"❌ {cam_id.upper()} - {cam_data['info']['name']}: Failed ({e})\")\n", + "\n", + "print(f\"\\n🎯 SUMMARY: Generated {len(multi_camera_streams)} synchronized camera streams\")\n", + "# print(\"πŸ’‘ Use 'multi_camera_streams' dict for multi-view rendering or timeline composition\")\n", + "\n", + "camera_feeds = [ feed[\"player_url\"] for feed in multi_camera_streams.values() ]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68YDD6HIkW_Z", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 421 + }, + "id": "68YDD6HIkW_Z", + "outputId": "fc71bf5d-321e-4fd7-b957-10822e4dffe8" + }, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + " " + ], + "text/plain": [ + "" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "camera_feeds[0]" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "LkMAeV0Uf8HF", + "metadata": { + "id": "LkMAeV0Uf8HF" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "t_CVyTjef8fc", + "metadata": { + "id": "t_CVyTjef8fc" + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "markdown", + "id": "69a158e0", + "metadata": { + "id": "69a158e0" + }, + "source": [ + "---\n", + "### 🧹 Finally: Clean Up Resources\n", + "\n", + "To properly manage resources, this final step allows you to **disconnect all active camera streams**. This is crucial for preventing orphaned processes and ensuring the system is ready for its next use.\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9a2c86ce", + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "9a2c86ce", + "outputId": "5965160a-8d76-4b68-edd6-bfe3bf2a4277" + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "❓ Stop all 7 active streams? (y/n): y\n", + "\n", + "πŸ”Œ Disconnecting all streams...\n", + " βœ… Stopped stream: Plaza Overview\n", + " βœ… Stopped stream: Main Walkway Cam\n", + " βœ… Stopped stream: Stairway Junction\n", + " βœ… Stopped stream: Central Plaza Cam\n", + " βœ… Stopped stream: Building Entrance Cam\n", + " βœ… Stopped stream: Wide Plaza View\n", + " βœ… Stopped stream: Ground-Level Cross View\n", + "\n", + "🧹 All streams have been disconnected.\n" + ] + } + ], + "source": [ + "# Confirm before stopping all streams\n", + "active_streams = [s for s in connected_streams.values() if s.get(\"status\") == \"connected\" and s.get(\"stream\")]\n", + "if not active_streams:\n", + " print(\"βœ… All streams are already disconnected.\")\n", + "else:\n", + " try:\n", + " confirm = input(f\"❓ Stop all {len(active_streams)} active streams? (y/n): \").strip().lower()\n", + " if confirm == 'y':\n", + " print(\"\\nπŸ”Œ Disconnecting all streams...\")\n", + " for cam_data in active_streams:\n", + " try:\n", + " cam_data[\"stream\"].stop()\n", + " cam_name = cam_data[\"info\"][\"name\"]\n", + " print(f\" βœ… Stopped stream: {cam_name}\")\n", + " except Exception as e:\n", + " print(f\" ❌ Failed to stop stream {cam_name}: {e}\")\n", + " print(\"\\n🧹 All streams have been disconnected.\")\n", + " else:\n", + " print(\"\\nπŸ‘ Streams will remain active.\")\n", + " except (ValueError, EOFError):\n", + " print(\"\\n⚠️ No valid input received. Streams will remain active.\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "414eea29", + "metadata": { + "id": "414eea29" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "colab": { + "provenance": [] + }, + "kaggle": { + "accelerator": "nvidiaTeslaT4", + "dataSources": [ + { + "datasetId": 8228445, + "sourceId": 12999101, + "sourceType": "datasetVersion" + } + ], + "dockerImageVersionId": 31090, + "isGpuEnabled": true, + "isInternetEnabled": true, + "language": "python", + "sourceType": "notebook" + }, + "kernelspec": { + "display_name": "Python 3", + "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.11.13" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}