{"id":71,"date":"2025-08-28T00:00:00","date_gmt":"2025-08-27T15:00:00","guid":{"rendered":"https:\/\/www.eqmaker.kr\/en\/?p=71"},"modified":"2026-08-18T06:17:00","modified_gmt":"2026-08-17T21:17:00","slug":"obs-youtube-live-hls-capture-debug","status":"publish","type":"post","link":"https:\/\/www.eqmaker.kr\/en\/obs-youtube-live-hls-capture-debug\/","title":{"rendered":"Capture OBS YouTube Live HLS Output for Debugging"},"content":{"rendered":"\n<p id=\"EQArticleSummary\">This article shows how to capture and save <abbr title=\"Open Broadcaster Software\">OBS<\/abbr> YouTube Live <abbr title=\"HTTP Live Streaming\">HLS<\/abbr> output for analysis. A Python-based <code>PushCap<\/code> program receives the stream after the YouTube ingest address is redirected in the <code>hosts<\/code> file. It saves <code>.m3u8<\/code> playlists and <code>.ts<\/code> media segments in sequence.<\/p>\n\n<section>\n<h2 id=\"PushCap\">Capture a YouTube Live HLS Stream<\/h2>\n<p>While developing <a href=\"https:\/\/www.eqmaker.kr\/en\/how-to-hls-push-to-youtube-live-from-wowza\/\" title=\"Connect Wowza Streaming Engine to YouTube Live through HLS\" hreflang=\"en\">the <code>HLStoYTB<\/code> module for connecting Wowza Streaming Engine to YouTube Live through HLS<\/a>, one of the problems was that I could not directly see whether my module was working correctly.<\/p>\n<p>The Wowza logs and network traffic showed that the module was working and sending data. However, YouTube sometimes did not recognize the stream, so the live broadcast did not start. OBS did not have this problem.<\/p>\n<p>I needed to compare the actual output of <code>HLStoYTB<\/code> with the output of OBS. I therefore wrote a Python <strong>program that saves HLS PUSH streams<\/strong>. I named it <dfn>PushCap<\/dfn> after the song below.<\/p>\n\t<iframe loading=\"lazy\" width=\"560\" height=\"315\" src=\"https:\/\/www.youtube.com\/embed\/glKyiUq_wWM?si=O8sPZrK-j0UJiloy\" title=\"Jack Black - Anyong Pussycat\" frameborder=\"0\" allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" allowfullscreen><\/iframe>\n<\/section>\n\n<section>\n<h2 id=\"CaptureObsYouTubeHlsOutput\">Capture OBS YouTube HLS Output<\/h2>\n\t<p>OBS supports <abbr title=\"Real-Time Messaging Protocol Secure\">RTMPS<\/abbr> and HLS for YouTube Live, but <strong>its HLS ingest server address is fixed in the program<\/strong>. The normal settings screen cannot send the HLS stream directly to a <code>PushCap<\/code> server. The YouTube address must instead be redirected to <code>PushCap<\/code> during DNS resolution by the operating system.<\/p>\n\t<figure class=\"wp-block-image aligncenter size-800x500\"><a href=\"https:\/\/www.eqmaker.kr\/media-ops\/wp-content\/uploads\/2026\/08\/obs-youtube-hls-stream-destination-settings.webp\"><img decoding=\"async\" src=\"https:\/\/www.eqmaker.kr\/media-ops\/wp-content\/uploads\/2026\/08\/obs-youtube-hls-stream-destination-settings-800x381.webp\" alt=\"OBS stream settings with YouTube-HLS and the primary YouTube ingest server selected\" class=\"wp-image-420\"\/><\/a><figcaption class=\"wp-element-caption\">The server address cannot be entered manually.<\/figcaption><\/figure>\n\n\t<section>\n\t<h3 id=\"EditHostsFile\">Edit the hosts File to Capture the Stream<\/h3>\n\t\t<p>YouTube Live HLS uses <code>a.upload.youtube.com<\/code> and <code>b.upload.youtube.com<\/code> as its default addresses. When OBS sends data to one of these domains, the operating system first finds its IP address and then connects OBS to that server.<\/p>\n\t\t<p>Most operating systems check the local <code>hosts<\/code> file before requesting an address from DNS. If the YouTube domain in this file points to the IP address of the <code>PushCap<\/code> server, the data that OBS sends to YouTube goes to <code>PushCap<\/code> instead.<\/p>\n\t\t<p>The <code>hosts<\/code> file is a text file without an extension. Windows and Linux use the same filename but store it in different locations.<\/p>\n\t\t<dl><dt>Windows<\/dt><dd><code>C:\\Windows\\System32\\drivers\\etc\\hosts<\/code><\/dd>\n\t\t<dt>Linux<\/dt><dd><code>\/etc\/hosts<\/code><\/dd><\/dl>\n\t\t<p>Open the file in a text editor such as Notepad or <code>nano<\/code>. Set the YouTube upload domain to the IP address of the host running <code>PushCap<\/code>. The following example shows a Windows <code>hosts<\/code> file.<\/p>\n<pre><code># Copyright (c) 1993-2009 Microsoft Corp.\n#\n# This is a sample HOSTS file used by Microsoft TCP\/IP for Windows.\n#\n# This file contains the mappings of IP addresses to host names. Each\n# entry should be kept on an individual line. The IP address should\n# be placed in the first column followed by the corresponding host name.\n# The IP address and the host name should be separated by at least one\n# space.\n#\n# Additionally, comments (such as these) may be inserted on individual\n# lines or following the machine name denoted by a '#' symbol.\n#\n# For example:\n#\n#      102.54.94.97     rhino.acme.com          # source server\n#       38.25.63.10     x.acme.com              # x client host\n<kbd>127.0.0.1\ta.upload.youtube.com<\/kbd>\n<kbd># 127.0.0.1\tb.upload.youtube.com<\/kbd>\n# localhost name resolution is handled within DNS itself.\n#\t127.0.0.1       localhost\n#\t::1             localhost<\/code><\/pre>\n\t\t<p>In this example, only the primary upload address, <code>a.upload.youtube.com<\/code>, points to the PC running <code>PushCap<\/code>. The loopback address <code>127.0.0.1<\/code> points to the same PC. This means that OBS and <code>PushCap<\/code> run on the same host.<\/p>\n\t\t<p>The backup address starts with <code>#<\/code>, which marks the line as a comment. It is therefore not redirected. The Linux <code>hosts<\/code> file can be edited in the same way.<\/p>\n\t<\/section>\n\n\t<section>\n\t<h3 id=\"CaptureObsHlsPushOutput\">Capture the OBS HLS PUSH Output<\/h3>\n\t<p>After editing the <code>hosts<\/code> file, start YouTube HLS streaming in OBS. The HLS data sent to the YouTube ingest domain is then delivered to the <code>PushCap<\/code> server.<\/p>\n\t<ol>\n\t<li><strong>Select the HLS PUSH service<\/strong><p>Open the OBS settings, select <samp>Stream<\/samp>, and choose <code>YouTube-HLS<\/code> from the service list.<\/p>\n\t<figure class=\"wp-block-image aligncenter size-800x500\"><a href=\"https:\/\/www.eqmaker.kr\/media-ops\/wp-content\/uploads\/2026\/08\/obs-stream-service-list-youtube-hls.webp\"><img decoding=\"async\" src=\"https:\/\/www.eqmaker.kr\/media-ops\/wp-content\/uploads\/2026\/08\/obs-stream-service-list-youtube-hls-605x500.webp\" alt=\"YouTube-HLS in the service list of the OBS Stream settings\" class=\"wp-image-424\"\/><\/a><figcaption class=\"wp-element-caption\">OBS supports HLS and RTMPS for YouTube Live.<\/figcaption><\/figure><\/li>\n\t<li><strong>Set a stream key<\/strong><p>Select <samp>Use Stream Key<\/samp>, then enter any test key in the <samp>Stream Key<\/samp> field. The key does not need to match the normal YouTube key length. The value shown in the example was created only for this test.<\/p>\n\t<figure class=\"wp-block-image aligncenter size-800x500\"><a href=\"https:\/\/www.eqmaker.kr\/media-ops\/wp-content\/uploads\/2026\/08\/obs-youtube-hls-test-stream-key.webp\"><img decoding=\"async\" src=\"https:\/\/www.eqmaker.kr\/media-ops\/wp-content\/uploads\/2026\/08\/obs-youtube-hls-test-stream-key-800x216.webp\" alt=\"A test stream key entered in the OBS YouTube-HLS settings\" class=\"wp-image-421\"\/><\/a><figcaption class=\"wp-element-caption\">Enter any test stream key.<\/figcaption><\/figure><\/li>\n\t<li><strong>Start streaming<\/strong><p>Select <samp>Start Streaming<\/samp>. After the connection succeeds, OBS uploads playlists and media segments to <code>PushCap<\/code>. The console shows each received filename and file size.<\/p>\n\t<figure class=\"wp-block-image aligncenter size-800x500\"><a href=\"https:\/\/www.eqmaker.kr\/media-ops\/wp-content\/uploads\/2026\/08\/pushcap-receiving-youtube-hls-stream-data.webp\"><img decoding=\"async\" src=\"https:\/\/www.eqmaker.kr\/media-ops\/wp-content\/uploads\/2026\/08\/pushcap-receiving-youtube-hls-stream-data-800x457.webp\" alt=\"PushCap console showing received TS segments and saved M3U8 playlists\" class=\"wp-image-422\"\/><\/a><figcaption class=\"wp-element-caption\">PushCap receiving HLS data.<\/figcaption><\/figure><\/li>\n\t<\/ol>\n\t<\/section>\n\n\t<section>\n\t<h3 id=\"CheckHlsCaptureData\">Check the Captured HLS Data<\/h3>\n\t<p>Stop streaming in OBS, then open the directory that contains <code>PushCap.py<\/code>. A subdirectory named <code>uploads<\/code> has been created. Inside it is another directory named after the test stream key. This directory contains the HLS data captured from OBS.<\/p>\n\t<figure class=\"wp-block-image aligncenter size-800x500\"><a href=\"https:\/\/www.eqmaker.kr\/media-ops\/wp-content\/uploads\/2026\/08\/pushcap-captured-hls-files-directory.webp\"><img decoding=\"async\" src=\"https:\/\/www.eqmaker.kr\/media-ops\/wp-content\/uploads\/2026\/08\/pushcap-captured-hls-files-directory-582x500.webp\" alt=\"OBS YouTube Live HLS files captured by PushCap\" class=\"wp-image-423\"\/><\/a><figcaption class=\"wp-element-caption\">Captured HLS stream data.<\/figcaption><\/figure>\n\t<p>Files with the <code>.ts<\/code> extension contain the video data uploaded by OBS. Each time OBS uploads the <code>.m3u8<\/code> playlist, <code>PushCap<\/code> also saves a numbered copy.<\/p>\n\t<p>The <code>.m3u8<\/code> file without a number is the last playlist sent by OBS. It has the same content as the playlist with the highest number. Comparing the numbered files in order shows how the media sequence and segment list changed during streaming.<\/p>\n\t<\/section>\n\n\t<section>\n\t<h3 id=\"ValidateData\">Validate the Data<\/h3>\n\t<p>Check that the playlists and media segments were created correctly. Confirm that every segment named in a <code>.m3u8<\/code> file has a matching <code>.ts<\/code> file, that the media sequence number increases in order, and that each segment duration is correct.<\/p>\n\t<p>When validating an HLS stream for YouTube, use the <a href=\"https:\/\/developers.google.com\/youtube\/v3\/live\/guides\/hls-ingestion\" title=\"Delivering Live YouTube Content via HLS\" hreflang=\"en\" target=\"_blank\" rel=\"noopener\">YouTube HLS ingestion specification<\/a> to check playlist tags, segment duration, codecs, and container formats.<\/p>\n\t<\/section>\n<\/section>\n\n<section>\n\t<h2 id=\"RunServer\">PushCap Source Code and Setup<\/h2>\n\t<section>\n\t<h3 id=\"PrepareEnvironment\">Prepare the Environment<\/h3>\n\t<ol>\n\t<li><strong>Install Python<\/strong><p><code>PushCap<\/code> is written in Python. Install Python on the PC that will run the server.<\/p><\/li>\n\t<li><strong>Install the required packages<\/strong><p><code>PushCap<\/code> requires the <code>flask<\/code> and <code>tornado<\/code> packages. Install them with the following commands.<\/p>\n\t<code class=\"line\">py -m pip install flask<\/code><code class=\"line\">py -m pip install tornado<\/code><\/li>\n\t<li><strong>Create an HTTPS certificate<\/strong><p>HLS does not always require HTTPS, but the YouTube HLS ingest service receives playlists and media segments over HTTPS. OBS also uses HTTPS for this output. Copy the generated <code>cert.pem<\/code> and <code>key.pem<\/code> files to the same directory as <code>PushCap.py<\/code>.<\/p>\n\t<p>For an example of creating certificate files, see the guide for applying a private HTTPS certificate to Wowza Streaming Engine.<\/p>\n\t<dl>\n\t<dt><code>cert.pem<\/code><\/dt><dd>The certificate presented by the HTTPS server<\/dd>\n\t<dt><code>key.pem<\/code><\/dt><dd>The private key used with the certificate<\/dd>\n\t<\/dl><\/li>\n\t<\/ol>\n\t<\/section>\n\n\t<section>\n\t<h3 id=\"SourceAndSave\">Download and Run PushCap<\/h3>\n\t<ol>\n\t<li><strong>Download the code<\/strong><p>Download <code>PushCap.py<\/code> from the <a href=\"https:\/\/github.com\/DirectionalAntenna\/PushCap\" title=\"PushCap\" hreflang=\"en\" rel=\"noopener\" target=\"_blank\">PushCap project on GitHub<\/a>.<\/p><\/li>\n\t<li><strong>Save PushCap.py<\/strong><p>Save <code>PushCap.py<\/code> in a suitable directory.<\/p><\/li>\n\t<li><strong>Run PushCap<\/strong><p>Open a console, move to the directory containing <code>PushCap.py<\/code>, and run the program. Keep <code>cert.pem<\/code> and <code>key.pem<\/code> in the same directory.<\/p><\/li>\n\t<\/ol>\n\t<p>When the following message appears, the program is running and ready to receive an HLS stream.<\/p>\n\t<pre><code>Microsoft Windows [Version 10.0.20348.4052]\n(c) Microsoft Corporation. All rights reserved.\nD:\\HLSWEB><kbd>py PushCap.py<\/kbd>\nStarting PushCap HTTPS server on port 443...\n\uc2a4\ud2b8\ub9bc\uc744 \uc3d8\uc138\uc694!<\/code><\/pre>\n\t<p><code>PushCap<\/code> is distributed under the <a href=\"https:\/\/opensource.org\/license\/bsd-3-clause\" title=\"BSD 3-Clause License\" hreflang=\"en\" target=\"_blank\" rel=\"noopener\">BSD 3-Clause License<\/a>. It may be used, modified, and redistributed in source or binary form under the license terms and copyright notice. The program is provided as is, without warranty, and users are responsible for its use.<\/p>\n\t<\/section>\n<\/section>\n\n<section>\n\t<h2 id=\"Conclusion\">Conclusion<\/h2>\n\t<p>The clearest way to check whether a program works correctly is to <strong>inspect its actual output<\/strong>. A normal log message does not prove that playlists and media segments meet the requirements of an external service.<\/p>\n\t<p>I used <code>PushCap<\/code> to save the OBS YouTube Live HLS output and inspect how it changed during streaming. By comparing the normal OBS output with the output of <code>HLStoYTB<\/code>, I was able to trace why YouTube did not recognize the stream.<\/p>\n\t<p>I hope this article helps readers who need to capture HLS output from OBS or another streaming program.<\/p>\n<\/section>\n\n<section>\n<h2>FAQ<\/h2>\n\t<dl>\n\t<dt>What data is included in a YouTube Live HLS stream?<\/dt>\n\t<dd>A YouTube Live HLS stream consists of <code>.ts<\/code> files containing the media data and a <code>.m3u8<\/code> playlist that lists the files in playback order.<\/dd>\n\t<dt>What can I check by capturing OBS YouTube HLS output?<\/dt>\n\t<dd>You can inspect the HTTP requests, <code>.m3u8<\/code> playlists, and <code>.ts<\/code> media segments sent by OBS. This reveals the upload order, media sequence changes, segment duration, and whether filenames match the playlist. It is also useful when comparing OBS output with another HLS encoder or a custom module.<\/dd>\n\t<dt>What information is stored in an HLS .m3u8 file?<\/dt>\n\t<dd>An <code>.m3u8<\/code> file can contain the playlist version, segment duration, media sequence number, and segment filenames. It may also contain SCTE-35 digital cue tone data and information for multiple bitrates.<\/dd>\n\t<dt>How long are OBS YouTube Live HLS segments?<\/dt>\n\t<dd>OBS creates YouTube Live HLS segments with a target duration of about two seconds. As of June 2026, YouTube recommends one- to four-second segments and requires them to be no longer than five seconds.<\/dd>\n\t<dt>How is the YouTube HLS ingest address different from a typical HLS server?<\/dt>\n\t<dd>The YouTube HLS PUSH address includes the <code>cid<\/code> and filename parameters. It receives one encoded source stream and a Media Playlist instead of a multi-bitrate Master Playlist. An HLS PUSH encoder may therefore be incompatible if its output URL or segment format does not match these requirements.<\/dd>\n\t<dt>Why is an HTTPS certificate needed for HLS transmission?<\/dt>\n\t<dd>HLS itself does not always require an HTTPS certificate, but YouTube HLS ingest requires playlists and media segments to be sent over HTTPS. OBS therefore uses HTTPS for its YouTube HLS output. However, OBS does not perform host verification.<\/dd>\n\t<dt>Does an HTTP 202 response from YouTube mean that the transmission failed?<\/dt>\n\t<dd>No. HTTP <code>202 Accepted<\/code> means that YouTube received a media segment before receiving the playlist that lists it. The module should send the updated playlist containing that segment as soon as possible. Normal processing is generally confirmed by HTTP <code>200 OK<\/code>.<\/dd>\n\t<dt>What should I check in captured HLS data?<\/dt>\n\t<dd>Check the request URL, HTTP method, and response code. Then inspect <code>EXT-X-MEDIA-SEQUENCE<\/code>, <code>EXT-X-TARGETDURATION<\/code>, <code>EXTINF<\/code>, segment filenames, and transmission order. Also check segment duration, the <abbr title=\"MPEG-2 Transport Stream\">MPEG-2 TS<\/abbr> structure, <abbr title=\"Program Association Table\">PAT<\/abbr> and <abbr title=\"Program Map Table\">PMT<\/abbr>, codecs, audio and video muxing, and timestamp continuity.<\/dd>\n\t<dt>What must I do after the capture is complete?<\/dt>\n\t<dd>Remove or comment out the YouTube ingest domain entries added to the <code>hosts<\/code> file. Otherwise, OBS may continue to connect to the <code>PushCap<\/code> server instead of YouTube and fail to start a normal YouTube Live broadcast.<\/dd>\n\t<dt>Which HTTP methods should a YouTube HLS integration module use?<\/dt>\n\t<dd>The YouTube HLS ingest endpoint accepts HTTP PUT or POST requests over HTTPS. Keep the <code>cid<\/code> value in the base ingest URL and set the playlist or media segment filename in the <code>file<\/code> parameter. All requests must use a persistent HTTPS connection.<\/dd>\n\t<\/dl>\n<\/section>\n\n<section class=\"post-update\">\n\t<h2>Update History<\/h2>\n\t<ul>\n\t<li><time datetime=\"2025-08-28\">August 28, 2028<\/time> \u2014 <span>First published<\/span><\/li>\n\t<li><time datetime=\"2026-08-18\">August 18, 2026<\/time> \u2014 <span>Revised and moved to a new URL<\/span><\/li>\n\t<\/ul>\n<\/section>\n","protected":false},"excerpt":{"rendered":"<p>Learn how to redirect OBS YouTube Live HLS output to a Python-based PushCap server and save M3U8 playlists and TS media segments for debugging.<\/p>\n","protected":false},"author":1,"featured_media":69,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-71","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-media-ops"],"_links":{"self":[{"href":"https:\/\/www.eqmaker.kr\/en\/wp-json\/wp\/v2\/posts\/71","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.eqmaker.kr\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.eqmaker.kr\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.eqmaker.kr\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.eqmaker.kr\/en\/wp-json\/wp\/v2\/comments?post=71"}],"version-history":[{"count":2,"href":"https:\/\/www.eqmaker.kr\/en\/wp-json\/wp\/v2\/posts\/71\/revisions"}],"predecessor-version":[{"id":73,"href":"https:\/\/www.eqmaker.kr\/en\/wp-json\/wp\/v2\/posts\/71\/revisions\/73"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.eqmaker.kr\/en\/wp-json\/wp\/v2\/media\/69"}],"wp:attachment":[{"href":"https:\/\/www.eqmaker.kr\/en\/wp-json\/wp\/v2\/media?parent=71"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.eqmaker.kr\/en\/wp-json\/wp\/v2\/categories?post=71"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.eqmaker.kr\/en\/wp-json\/wp\/v2\/tags?post=71"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}