Repository: streamlink/streamlink

Issue:
Streamlink Crashing after Commercial
<!--
Thanks for reporting a bug!
USE THE TEMPLATE. Otherwise your bug report may be rejected.

First, see the contribution guidelines:
https://github.com/streamlink/streamlink/blob/master/CONTRIBUTING.md#contributing-to-streamlink

Also check the list of open and closed bug reports:
https://github.com/streamlink/streamlink/issues?q=is%3Aissue+label%3A%22bug%22

Please see the text preview to avoid unnecessary formatting errors.
-->


## Bug Report

<!-- Replace [ ] with [x] in order to check the box -->
- [x] This is a bug report and I have read the contribution guidelines.


### Description

<!-- Explain the bug as thoroughly as you can. Don't leave out information which is necessary for us to reproduce and debug this issue. -->

Streamlink will either crash after a 15 second commercial break or will operate slowly after the 15 second commercial break at startup. 

### Expected / Actual behavior

<!-- What do you expect to happen, and what is actually happening? -->
After typing in my streamlink command:
streamlink twitch.tv/xqcow best --hls-live-edge 1 --twitch-low-latency

I should jump right into the live edge of the stream. Instead, I will end up watching a commercial placeholder, and then most of the time crash my VLC media player afterwards. I am currently building an application as well that pipes my streamlink output into FFMPEG, and this pipe will break as well.

### Reproduction steps / Explicit stream URLs to test

<!-- How can we reproduce this? Please note the exact steps below using the list format supplied. If you need more steps please add them. -->

1. ... Type in the command streamlink twitch.tv/channel_name best --hls-live-edge 1 --twitch-low-latency
2. ... Wait out the new 15 second commercial timeout
3. ... By this point it should have crashed. If it did not then exit VLC media player and try again


### Log output

<!--
TEXT LOG OUTPUT IS REQUIRED for a bug report!
Use the `--loglevel debug` parameter and avoid using parameters which suppress log output.
https://streamlink.github.io/cli.html#cmdoption-l

Make sure to **remove usernames and passwords**
You can copy the output to https://gist.github.com/ or paste it below.
-->

streamlink twitch.tv/xqcow best --hls-live-edge 1 --twitch-low-latency --loglevel debug
[cli][debug] OS:         Windows 10
[cli][debug] Python:     3.6.6
[cli][debug] Streamlink: 1.5.0
[cli][debug] Requests(2.24.0), Socks(1.7.1), Websocket(0.57.0)
[cli][info] Found matching plugin twitch for URL twitch.tv/xqcow
[cli][debug] Plugin specific arguments:
[cli][debug]  --twitch-low-latency=True (low_latency)
[plugin.twitch][debug] Getting live HLS streams for xqcow
[utils.l10n][debug] Language code: en_US
[cli][info] Available streams: audio_only, 160p (worst), 360p, 480p, 720p, 720p60, 1080p60 (best)
[cli][info] Opening stream: 1080p60 (hls)
[plugin.twitch][info] Low latency streaming (HLS live edge: 1)
[stream.hls][debug] Reloading playlist
[plugin.twitch][info] This is not a low latency stream
[stream.hls][debug] First Sequence: 0; Last Sequence: 2
[stream.hls][debug] Start offset: 0; Duration: None; Start Sequence: 2; End Sequence: None
[stream.hls][debug] Adding segment 2 to queue
[cli][debug] Pre-buffering 8192 bytes
[cli][info] Starting player: "C:\Program Files\VideoLAN\VLC\vlc.exe"
[cli.output][debug] Opening subprocess: "C:\Program Files\VideoLAN\VLC\vlc.exe" --input-title-format twitch.tv/xqcow -
[stream.hls][debug] Download of segment 2 complete
[cli][debug] Writing stream to output
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 3 to queue
[stream.hls][debug] Download of segment 3 complete
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 4 to queue
[stream.hls][debug] Download of segment 4 complete
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 5 to queue
[stream.hls][debug] Download of segment 5 complete
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 6 to queue
[stream.hls][debug] Download of segment 6 complete
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 7 to queue
[stream.hls][debug] Download of segment 7 complete
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 8 to queue
[stream.hls][debug] Adding segment 9 to queue
[stream.hls][debug] Download of segment 8 complete
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 10 to queue
[cli][info] Player closed
[stream.segmented][debug] Closing worker thread
[stream.segmented][debug] Closing writer thread
[cli][info] Stream ended
[cli][info] Closing currently open stream...


### Additional comments, screenshots, etc.
![error_1](https://user-images.githubusercontent.com/13973467/92886009-23ed8980-f3c8-11ea-94a5-5c9894feb92e.PNG)

![error_3](https://user-images.githubusercontent.com/13973467/92886664-ad04c080-f3c8-11ea-9418-0f212f73854a.png)

I got the error in the first screenshot once and that's it. All other crashes were due to the log provided above.

Regarding the console image, the execution times used to range from 0.15ms to 0.2ms tops, and now executions times will approach at times 2seconds+. Interestingly enough it always runs great through the commercial. I am not sure if this is any help to the streamlink team but I figured I should provide all the information I possibly could.

[Love Streamlink? Please consider supporting our collective. Thanks!](https://opencollective.com/streamlink/donate)



Discussion:
Apologies for making this so messy, but I appear to get this [stream.hls][debug] Reloading playlist for each segment that is added, and the segments themselves take extremely long to load compared to segments 1-10 (the ones that are over the commercial). Attached is an image of my console log:

![image](https://user-images.githubusercontent.com/13973467/92888947-a0816780-f3ca-11ea-8bf8-f67dcc46cf26.png)


> Streamlink Crashing after Commercial
> [cli][info] Player closed

No, Streamlink is not crashing. Your player is, which will result in Streamlink terminating its process. This is happening due to the discontinuity in Streamlink's output stream when two different streams, namely the ad and the regular stream, get concatenated. This is no issue on their web player, because it's reading an HLS stream directly and can thus react to discontinuities, but Streamlink can't tell the player that there's a discontinuity in a single output stream.

Filter out ads with `--twitch-disable-ads`, if your player can't handle it, or use a different player or try to upgrade it.

#3164 
> > Streamlink Crashing after Commercial
> > [cli][info] Player closed
> 
> No, Streamlink is not creashing. Your player is, which will result in Streamlink terminating its process. This is happening due to the discontinuity in Streamlink's output stream when two different streams, namely the ad and the regular stream, get concatenated. This is no issue on their web player, because it's reading a HLS directly and can thus react to discontinuities, but Streamlink can't tell the player that there's a discontinuity in a single output stream.
> 
> Filter out ads with `--twitch-disable-ads`, if your player can't handle it, or use a different player or try to upgrade it.
> 
> #3164

Hello, I use --stream-url to extract the video stream and play it on ffmpeg and potplayer. I have added --twitch-disable-ads but there will still be advertising prompts, and then play normally. If the anchor pushes an advertisement, the video will appear on a blurred screen, and then return to normal after the advertisement ends. I added the --twitch-disable-ads parameter before, but the problem only appeared today.
> I use --stream-url to extract the video stream and play it on ffmpeg and potplayer. I have added --twitch-disable-ads but there will still be advertising

`--stream-url` and `--player-passthrough=hls` **are incompatible** with any parameters which alter Streamlink's HLS client logic, as they simply circumvent Streamlink and just resolve the HLS stream URL for your chosen player to handle it on its own.

See here:
- https://github.com/streamlink/streamlink/issues/3120#issuecomment-676817526
- https://github.com/streamlink/streamlink/issues/3120#issuecomment-676839411
> > 我使用--stream-url提取视频流并在ffmpeg和potplayer上播放。我添加了--twitch-disable-ads，但仍然会有广告
> 
> `--stream-url`并且与任何更改Streamlink的HLS客户端逻辑的参数`--player-passthrough=hls` **都不兼容**，因为它们只是绕过Streamlink并只是解析HLS流URL，供您选择的播放器自己处理。
> 
> 看这里：
> 
> * [＃3120（评论）](https://github.com/streamlink/streamlink/issues/3120#issuecomment-676817526)
> * [＃3120（评论）](https://github.com/streamlink/streamlink/issues/3120#issuecomment-676839411)

Thank you. Because the problem only appeared today, I thought twitch updated the advertising module again. If I use the -O stream pipeline method, can it be solved?
Please forgive me for not understanding enough how to add player command parameters in streamlink.
> If I use the -O stream pipeline method, can it be solved?

Yes. All output methods except where the resolved HLS stream URL gets returned (`--stream-url`) or where the resolved HLS stream URL gets passed to the player as launch argument (`--player-passthrough`) will work, which includes `--stdout` / `-O`.
> > If I use the -O stream pipeline method, can it be solved?
> 
> Yes. All output methods except where the resolved HLS stream URL gets returned (`--stream-url`) or where the resolved HLS stream URL gets passed to the player as launch argument (`--player-passthrough`) will work, which includes `--stdout` / `-O`.

Okay, thank you very much for your answers. I will try it out with pipeline streaming for now. Because I need to use ffmpeg for secondary transcoding, I haven't figured out how to add ffmpeg command parameter information in streamlink.T_T
> > If I use the -O stream pipeline method, can it be solved?
> 
> Yes. All output methods except where the resolved HLS stream URL gets returned (`--stream-url`) or where the resolved HLS stream URL gets passed to the player as launch argument (`--player-passthrough`) will work, which includes `--stdout` / `-O`.

Hello, I use the pipeline to record, but it will disconnect when the ad is pushed. I used --player-no-close to block, but it seems to have no effect.The advertisement can be skipped at the beginning, but the advertisement in the middle of the video will cause the streamlink pipeline to stop directly and end the recording.I don't know what changes twtich made today, everything was normal before then.

streamlink --twitch-disable-ads --player-no-close --twitch-disable-hosting $address best -O |  ffmpeg -re -i pipe:0 -c:v copy -c:a aac -f flv test.flv


I am using a similar ffmpeg pipe using an example stream:

streamlink twitch.tv/beyondthesummit best --hls-live-edge 1 --twitch-low-latency --twitch-disable-ads -O | ffmpeg -i pipe:0 -r 1 -update 1 <my/image/path.png>

and am noticing that my ffmpeg output .png is ~15 seconds delayed from the actual output on partnered/affiliated streams. The delay occurs whether I have the --twitch-disable-ads flag or not (Beyondthesummit is a 24/7 dota 2 stream where you can look at the clock on the top to measure time differences).

I do NOT have any of these problems on the non-partnered/non-affiliated streams that do not have pre-roll ads, such as when I run a test stream myself.

My guess is that it's ffmpeg itself that is having problems with the new stream, however I am not quite sure why. Perhaps the commercial and stream are encoded differently, which programs such as VLC and ffmpeg seem to mess up and crash on? I am not very experienced at all with streamlink or ffmpeg so I don't know exactly what is going on under the hood
It doesn't work on MPC-HC either (it stops when a commercial starts, then closes after a while). 

I don't know what player is supposed to work, since VLC and MPC-HC are by far the most used players on Windows.
> I don't know what player is supposed to work, since VLC and MPC-HC are by far the most used players on Windows.

How does this preclude the player from being a problem? We run in to tons of issues all the times due to players including VLC. I'm watching a stream right now via MPV and it's perfectly fine using `--twitch-low-latency`. I even tested restarting the stream 5 times to get the pre-roll ad, no crashes at all. The debug output is pretty clear that this isn't a problem with Streamlink.

Edit: Unless someone can show me evidence that Streamlink is causing this problem in the next 12 hours I'm going to lock this thread, we go round and round on this every time Twitch enables the pre-roll ads again. If your player can't handle the commercials then go open an issue on their issue tracker.
This is a player issue caused by the way of how Streamlink works, and VLC is especially terrible at it.

All that Streamlink can do is output a single stream. In regards to HLS streams, HLS segments, which are MPEG-TS containers, need to be concatenated to yield a cohesive stream. This is how HLS streaming works. However, when there are two different streams in the same HLS playlist with different encodings, those segments will get concatenated "incorrectly". This is called a stream discontinuity, and since Streamlink's output is just a single stream of concatenated MPEG-TS chunks without any metadata, it is causing some players (or their demuxers/decoders) to crash, as the output is not a correctly encoded stream.

Other players, like MPV for example, are more resilient to this and thus handle it better without crashing.

Players which are reading HLS streams directly, eg. when using Streamlink's --player-passthrough=hls parameter, or also web players, can see the annotated metadata in the HLS playlist and know when a discontinuity happens, so they can simply treat the segments which come after a discontinuity as a new stream to demux/decode, just like when playing the next entry of a regular media playlist.

This means that if your player has issues with this, you will either have to filter out ads, or pass the resolved HLS stream URL through to your player and let it read the HLS stream instead, but then, you will lose all the benefits Streamlink offers, like filtering ads, low latency streaming, customizable live-edge value, buffers, etc.

The only solution that could make Streamlink work with those bad players is implementing an HLS proxy, so that it doesn't output a single stream. Then the player can connect to the proxy and can still access all the metadata that's available, and discontinuities can be handled gracefully if no ads or any other unwanted secondary stream gets filtered out. This is also the solution to the VOD seeking issue, because players reading HLS streams have access to the timing metadata. But implementing an HLS proxy is rather complex, error prone, uncertain, and nobody is willing to do this.
> > I don't know what player is supposed to work, since VLC and MPC-HC are by far the most used players on Windows.
> 
> How does this preclude the player from being a problem? We run in to tons of issues all the times due to players including VLC. I'm watching a stream right now via MPV and it's perfectly fine using `--twitch-low-latency`. I even tested restarting the stream 5 times to get the pre-roll ad, no crashes at all. The debug output is pretty clear that this isn't a problem with Streamlink.
> 
> Edit: Unless someone can show me evidence that Streamlink is causing this problem in the next 12 hours I'm going to lock this thread, we go round and round on this every time Twitch enables the pre-roll ads again. If your player can't handle the commercials then go open an issue on their issue tracker.

Yes, the problem I currently encounter is not in the pre-advertising, it can be successfully skipped, but if the author plays the ad during the live broadcast, my process will be forced to quit. I currently do not know what command parameters can be added to skip.
> This is a player issue caused by the way of how Streamlink works, and VLC is especially terrible at it.
> 
> All that Streamlink can do is output a single stream. In regards to HLS streams, HLS segments, which are MPEG-TS containers, need to be concatenated to yield a cohesive stream. This is how HLS streaming works. However, when there are two different streams in the same HLS playlist with different encodings, those segments will get concatenated "incorrectly". This is called a stream discontinuity, and since Streamlink's output is just a single stream of concatenated MPEG-TS chunks without any metadata, it is causing some players (or their demuxers/decoders) to crash, as the output is not a correctly encoded stream.
> 
> Other players, like MPV for example, are more resilient to this and thus handle it better without crashing.
> 
> Players which are reading HLS streams directly, eg. when using Streamlink's --player-passthrough=hls parameter, or also web players, can see the annotated metadata in the HLS playlist and know when a discontinuity happens, so they can simply treat the segments which come after a discontinuity as a new stream to demux/decode, just like when playing the next entry of a regular media playlist.
> 
> This means that if your player has issues with this, you will either have to filter out ads, or pass the resolved HLS stream URL through to your player and let it read the HLS stream instead, but then, you will lose all the benefits Streamlink offers, like filtering ads, low latency streaming, customizable live-edge value, buffers, etc.
> 
> The only solution that could make Streamlink work with those bad players is implementing an HLS proxy, so that it doesn't output a single stream. Then the player can connect to the proxy and can still access all the metadata that's available, and discontinuities can be handled gracefully if no ads or any other unwanted secondary stream gets filtered out. This is also the solution to the VOD seeking issue, because players reading HLS streams have access to the timing metadata. But implementing an HLS proxy is rather complex, error prone, uncertain, and nobody is willing to do this.

I now use streamlink’s pipeline streaming method to skip the 15-second ad prompt, but there is still no way to prevent the anchor from playing ads in the middle of the live broadcast, which will cause my process to crash. (Not sure if it is the problem with streamlink or ffmpeg) Do I need an external command parameter to prevent ads from entering during the live broadcast?
> there is still no way to prevent the anchor from playing ads in the middle of the live broadcast

Post a debug log and the entire command. Ads should be filtered out just fine.
> > there is still no way to prevent the anchor from playing ads in the middle of the live broadcast
> 
> Post a debug log and the entire command. Ads should be filtered out just fine.

I intercepted the debug records on two servers, and they had problems at the same time:
[stream.hls][debug] Download of segment 210 complete
[stream.hls][debug] Reloading playlist36kB time=00:06:41.52 bitrate=6156.1kbits/s speed=   1x    
[stream.hls][debug] Adding segment 211 to queue
[stream.hls][debug] Adding segment 212 to queue
[stream.hls][debug] Adding segment 213 to queue
[stream.hls][debug] Download of segment 211 complete6:42.03 bitrate=6158.5kbits/s speed=   1x    
[stream.hls][debug] Download of segment 212 complete
[stream.hls][debug] Download of segment 213 complete6:42.52 bitrate=6156.5kbits/s speed=   1x    
[stream.hls][debug] Reloading playlist28kB time=00:06:46.56 bitrate=6152.2kbits/s speed=   1x    
[stream.hls][debug] Adding segment 214 to queue
[stream.hls][debug] Adding segment 215 to queue
[stream.hls][debug] Adding segment 216 to queue
[stream.hls][debug] Reloading playlist41kB time=00:06:51.60 bitrate=6148.8kbits/s speed=   1x    
[stream.hls][debug] Adding segment 217 to queue
[stream.hls][debug] Adding segment 218 to queue
[stream.hls][debug] Adding segment 219 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 220 to queue
[stream.hls][debug] Adding segment 221 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 222 to queue
[stream.hls][debug] Adding segment 223 to queue
[stream.hls][debug] Adding segment 224 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 225 to queue
[stream.hls][debug] Adding segment 226 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 227 to queue
[stream.hls][debug] Adding segment 228 to queue
[stream.hls][debug] Adding segment 229 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 230 to queue
[stream.hls][debug] Adding segment 231 to queue
[stream.hls][debug] Adding segment 232 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 233 to queue
[stream.hls][debug] Adding segment 234 to queue
[stream.hls][debug] Adding segment 235 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 236 to queue
[stream.hls][debug] Adding segment 237 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 238 to queue
[stream.hls][debug] Adding segment 239 to queue
[stream.hls][debug] Adding segment 240 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 241 to queue
[stream.hls][debug] Adding segment 242 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 243 to queue
[stream.hls][debug] Adding segment 244 to queue
[stream.hls][debug] Adding segment 245 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 246 to queue
[stream.hls][debug] Adding segment 247 to queue
[stream.hls][debug] Adding segment 248 to queue
error: Error when reading from stream: Read timeout, exiting
[stream.segmented][debug] Closing worker thread
[stream.segmented][debug] Closing writer thread
[cli][info] Stream ended
[cli][info] Closing currently open stream...
[flv @ 0x6cca2c0] Failed to update header with correct duration.ate=6148.5kbits/s speed=0.873x    
[flv @ 0x6cca2c0] Failed to update header with correct filesize.
frame=24720 fps= 52 q=-1.0 Lsize=  309241kB time=00:06:52.01 bitrate=6148.5kbits/s speed=0.873x    
video:301964kB audio:6475kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.260115%







Another server:
[stream.hls][debug] Reloading playlist51kB time=00:03:25.30 bitrate=6146.8kbits/s speed=   1x    
[stream.hls][debug] Adding segment 113 to queue
[stream.hls][debug] Adding segment 114 to queue
[stream.hls][debug] Download of segment 113 complete3:25.82 bitrate=6143.6kbits/s speed=   1x    
[stream.hls][debug] Download of segment 114 complete
[stream.hls][debug] Reloading playlist81kB time=00:03:30.37 bitrate=6144.1kbits/s speed=   1x    
[stream.hls][debug] Adding segment 115 to queue
[stream.hls][debug] Adding segment 116 to queue
[stream.hls][debug] Adding segment 117 to queue
[stream.hls][debug] Adding segment 118 to queue
[stream.hls][debug] Reloading playlist22kB time=00:03:33.89 bitrate=6136.3kbits/s speed=   1x    
[stream.hls][debug] Adding segment 119 to queue
[stream.hls][debug] Adding segment 120 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 121 to queue
[stream.hls][debug] Adding segment 122 to queue
[stream.hls][debug] Adding segment 123 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 124 to queue
[stream.hls][debug] Adding segment 125 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 126 to queue
[stream.hls][debug] Adding segment 127 to queue
[stream.hls][debug] Adding segment 128 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 129 to queue
[stream.hls][debug] Adding segment 130 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 131 to queue
[stream.hls][debug] Adding segment 132 to queue
[stream.hls][debug] Adding segment 133 to queue
[stream.hls][debug] Adding segment 134 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 135 to queue
[stream.hls][debug] Adding segment 136 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 137 to queue
[stream.hls][debug] Adding segment 138 to queue
[stream.hls][debug] Adding segment 139 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 140 to queue
[stream.hls][debug] Adding segment 141 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 142 to queue
[stream.hls][debug] Adding segment 143 to queue
[stream.hls][debug] Adding segment 144 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 145 to queue
[stream.hls][debug] Adding segment 146 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 147 to queue
[stream.hls][debug] Adding segment 148 to queue
[stream.hls][debug] Adding segment 149 to queue
[stream.hls][debug] Adding segment 150 to queue
error: Error when reading from stream: Read timeout, exiting
[stream.segmented][debug] Closing worker thread
[stream.segmented][debug] Closing writer thread
[cli][info] Stream ended
[cli][info] Closing currently open stream...
[flv @ 0x775f900] Failed to update header with correct duration.ate=6135.7kbits/s speed=0.781x    
[flv @ 0x775f900] Failed to update header with correct filesize.
frame=12840 fps= 47 q=-1.0 Lsize=  160310kB time=00:03:34.01 bitrate=6136.3kbits/s speed=0.781x    
video:156532kB audio:3361kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.260790%
[aac @ 0x76f9a40] Qavg: 186.999
> > there is still no way to prevent the anchor from playing ads in the middle of the live broadcast
> 
> Post a debug log and the entire command. Ads should be filtered out just fine.


This is my streamlink command parameter:
streamlink --player-no-close --http-header "User-Agent=Mozilla/5.0" --twitch-disable-ads https://www.twitch.tv/esl_sc2 best -O --loglevel debug | ffmpeg -re -i pipe:0 -b 8000k -c:v copy -c:a aac -f flv

If you can, I hope you can help me test this address: https://www.twitch.tv/esl_sc2

The comments also confirmed my guess that the anchor will play an advertisement every 15 minutes or so.

When the advertisement does not end, use streamlink to get it again, it will fail.

[cli][info] streamlink is running as root! Be careful!
[cli][debug] OS:         Linux-4.14.129-bbrplus-x86_64-with-debian-10.5
[cli][debug] Python:     3.7.3
[cli][debug] Streamlink: 1.5.0
[cli][debug] Requests(2.21.0), Socks(1.7.1), Websocket(0.57.0)
[cli][info] Found matching plugin twitch for URL https://www.twitch.tv/esl_sc2
[cli][debug] Plugin specific arguments:
[cli][debug]  --twitch-disable-ads=True (disable_ads)
[plugin.twitch][debug] Getting live HLS streams for esl_sc2
[utils.l10n][debug] Language code: en_US
[cli][info] Available streams: audio_only, 160p (worst), 360p, 480p, 720p, 720p60, 1080p60 (best)
[cli][info] Opening stream: 1080p60 (hls)
[plugin.twitch][info] Will skip ad segments
[stream.hls][debug] Reloading playlist
[plugin.twitch][info] Waiting for pre-roll ads to finish, be patient
[stream.hls][debug] First Sequence: 0; Last Sequence: 2
[stream.hls][debug] Start offset: 0; Duration: None; Start Sequence: 0; End Sequence: None
[stream.hls][debug] Adding segment 0 to queue
[cli][debug] Pre-buffering 8192 bytes
[stream.hls][debug] Adding segment 1 to queue
[stream.hls][debug] Adding segment 2 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 3 to queue
[stream.hls][debug] Adding segment 4 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 5 to queue
[stream.hls][debug] Adding segment 6 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 7 to queue
[stream.hls][debug] Adding segment 8 to queue
[stream.hls][debug] Adding segment 9 to queue
[stream.hls][debug] Adding segment 10 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 11 to queue
[stream.hls][debug] Adding segment 12 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 13 to queue
[stream.hls][debug] Adding segment 14 to queue
[stream.hls][debug] Adding segment 15 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 16 to queue
[stream.hls][debug] Adding segment 17 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 18 to queue
[stream.hls][debug] Adding segment 19 to queue
[stream.hls][debug] Adding segment 20 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 21 to queue
[stream.hls][debug] Adding segment 22 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 23 to queue
[stream.hls][debug] Adding segment 24 to queue
[stream.hls][debug] Adding segment 25 to queue
[stream.hls][debug] Adding segment 26 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 27 to queue
[stream.hls][debug] Adding segment 28 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 29 to queue
[stream.hls][debug] Adding segment 30 to queue
[stream.hls][debug] Adding segment 31 to queue
[stream.segmented][debug] Closing worker thread
[stream.segmented][debug] Closing writer thread
[cli][error] Try 1/1: Could not open stream <HLSStream('https://video-weaver.lax03.hls.ttvnw.net/v1/playlist/Cs4DuBtotlhEXLRd3q6QJ1QBPGvixmMIWuQD0GyHgUSz350L1sDTZhFzy7quXOWwA7MargkV3ppcn9608KM4NKfUkA5t8yIGerN-lj3A1rQhTqaXVP3HLjDNG07QEpQNQy7fjyT6HhnQsh3RMw2P52SziwGAdz70LX2RICmEdNyXCa5kXhqAHDrLqIPq8iyOUVKu42Up8Z6eO4GZ3u_mnslcRho55BgRh81K8Md0GAEXFwPoeKhz8qQWv3kwRb679ZUuyfx4P_iO6x-TZe-e65F3yRHdDoLR-d_rMAIWxyW6i4Z_Hf2hHXSa1X1aNfTA3HpSuZNvtXcfS96FQ73HAHW7AXl9XqfnremnxsfxzNLrvCZ3yWddXLX9AcgUwJpWJznWu4ZpxNwXrr7_W1Y-afX5eOrORQSych15ZG_HX9-T_DAHP8c2Zyx84T0nJjOdtgZjWRfp1vLuANC5bEF4LQxquVcgMDxN4RU2OtUk_H_hWwhHv2GQHPFOFNBkrrZU9WhdvKCGs5wBiUALbbDk_-2t3-mkEkYce2izCGIYnixzWhki11IhONm3DOEOiMV05Y53jeJuzmIfiSd5cGu2R2tW3eEYPHTlJ9K78cQQuSjGEhA9Q10HbsAk8R4IzXulICYkGgzS741Bv_2eDgui5Xs.m3u8')> (Failed to read data from stream: Read timeout)
error: Could not open stream <HLSStream('https://video-weaver.lax03.hls.ttvnw.net/v1/playlist/Cs4DuBtotlhEXLRd3q6QJ1QBPGvixmMIWuQD0GyHgUSz350L1sDTZhFzy7quXOWwA7MargkV3ppcn9608KM4NKfUkA5t8yIGerN-lj3A1rQhTqaXVP3HLjDNG07QEpQNQy7fjyT6HhnQsh3RMw2P52SziwGAdz70LX2RICmEdNyXCa5kXhqAHDrLqIPq8iyOUVKu42Up8Z6eO4GZ3u_mnslcRho55BgRh81K8Md0GAEXFwPoeKhz8qQWv3kwRb679ZUuyfx4P_iO6x-TZe-e65F3yRHdDoLR-d_rMAIWxyW6i4Z_Hf2hHXSa1X1aNfTA3HpSuZNvtXcfS96FQ73HAHW7AXl9XqfnremnxsfxzNLrvCZ3yWddXLX9AcgUwJpWJznWu4ZpxNwXrr7_W1Y-afX5eOrORQSych15ZG_HX9-T_DAHP8c2Zyx84T0nJjOdtgZjWRfp1vLuANC5bEF4LQxquVcgMDxN4RU2OtUk_H_hWwhHv2GQHPFOFNBkrrZU9WhdvKCGs5wBiUALbbDk_-2t3-mkEkYce2izCGIYnixzWhki11IhONm3DOEOiMV05Y53jeJuzmIfiSd5cGu2R2tW3eEYPHTlJ9K78cQQuSjGEhA9Q10HbsAk8R4IzXulICYkGgzS741Bv_2eDgui5Xs.m3u8')>, tried 1 times, exiting
[cli][info] Closing currently open stream...
pipe:0: Invalid data found when processing input



My ffmpeg version:
built with gcc 8 (Debian 8.3.0-6)
  configuration: --enable-gpl --enable-version3 --enable-static --disable-debug --disable-ffplay --disable-indev=sndio --disable-outdev=sndio --cc=gcc --enable-fontconfig --enable-frei0r --enable-gnutls --enable-gmp --enable-libgme --enable-gray --enable-libaom --enable-libfribidi --enable-libass --enable-libvmaf --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-librubberband --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libvorbis --enable-libopus --enable-libtheora --enable-libvidstab --enable-libvo-amrwbenc --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libdav1d --enable-libxvid --enable-libzvbi --enable-libzimg


Mid-stream ads are annotated the same way pre-roll ads are, and they get filtered out correctly. Just checked again it on your example stream, as it shows ads in short intervals, which is quite handy to debug. Regarding that, there's nothing wrong.

> error: Error when reading from stream: Read timeout, exiting

Well, there you go. You'll of course need to increase the `--hls-timeout` (or `--stream-timeout` fallback) value when filtering out ads, otherwise you'll get timeouts due to the lack of any data returned by the stream reader. This is expected behavior.

We could add a workaround for that though which disables the timeout while filtering out ads, but I'm not sure if it really makes sense and whether it requires some code refactoring.
> Mid-stream ads are annotated the same way pre-roll ads are, and they get filtered out correctly. Just checked again it on your example stream, as it shows ads in short intervals, which is quite handy to debug. Regarding that, there's nothing wrong.
> 
> > error: Error when reading from stream: Read timeout, exiting
> 
> Well, there you go. You'll of course need to increase the `--hls-timeout` (or `--stream-timeout` fallback) value when filtering out ads, otherwise you'll get timeouts due to the lack of any data returned by the stream reader. This is expected behavior.
> 
> We could add a workaround for that though which disables the timeout while filtering out ads, but I'm not sure if it really makes sense and whether it requires some code refactoring.

Thanks, I add --hls-timeout and try again. For setting the timeout period, it should be helpful for the recording process, but if it is a rebroadcast, it will probably be disconnected for a while, right?
> Mid-stream ads are annotated the same way pre-roll ads are, and they get filtered out correctly. Just checked again it on your example stream, as it shows ads in short intervals, which is quite handy to debug. Regarding that, there's nothing wrong.
> 
> > error: Error when reading from stream: Read timeout, exiting
> 
> Well, there you go. You'll of course need to increase the `--hls-timeout` (or `--stream-timeout` fallback) value when filtering out ads, otherwise you'll get timeouts due to the lack of any data returned by the stream reader. This is expected behavior.
> 
> We could add a workaround for that though which disables the timeout while filtering out ads, but I'm not sure if it really makes sense and whether it requires some code refactoring.

Hi, sorry to interrupt again. The problem reappeared, this time I set --stream-timeout 120 for testing, but it still disconnected during midfield. Because it is a live game, there will be a countdown waiting in midfield. I didn’t see any ads when I watched it with the browser. However, the player still fails to obtain the stream, and there is no advertisement fragment in the debug, it is suddenly unable to download.

The game is currently being broadcast live. ( https://www.twitch.tv/esl_sc2 )When the game is over, there will be a short countdown as a rest time, during which the streamlink will be disconnected. But the browser view everything is normal.

streamlink --player-no-close --http-header "User-Agent=Mozilla/5.0" --stream-timeout 120 --twitch-disable-ads https://www.twitch.tv/esl_sc2 best -O --loglevel debug | ffmpeg -re -i pipe:0 -b 8000k -c:v copy -c:a aac -f flv

[stream.hls][debug] Reloading playlist82kB time=00:25:22.97 bitrate=5949.6kbits/s speed=   1x    
[stream.hls][debug] Adding segment 770 to queue
[stream.hls][debug] Adding segment 771 to queue
[stream.hls][debug] Adding segment 772 to queue
[stream.hls][debug] Download of segment 770 complete
[stream.hls][debug] Download of segment 771 complete
[stream.hls][debug] Download of segment 772 complete
[stream.hls][debug] Reloading playlist81kB time=00:25:28.02 bitrate=5950.2kbits/s speed=   1x    
[stream.hls][debug] Adding segment 773 to queue
[stream.hls][debug] Adding segment 774 to queue
[stream.hls][debug] Adding segment 775 to queue
[stream.hls][debug] Reloading playlist72kB time=00:25:31.05 bitrate=5949.7kbits/s speed=   1x    
[stream.hls][debug] Adding segment 776 to queue
[stream.hls][debug] Adding segment 777 to queue
[stream.hls][debug] Adding segment 778 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 779 to queue
[stream.hls][debug] Adding segment 780 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 781 to queue
[stream.hls][debug] Adding segment 782 to queue
[stream.hls][debug] Adding segment 783 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 784 to queue
[stream.hls][debug] Adding segment 785 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 786 to queue
[stream.hls][debug] Adding segment 787 to queue
[stream.hls][debug] Adding segment 788 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 789 to queue
[stream.hls][debug] Adding segment 790 to queue
[stream.hls][debug] Adding segment 791 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 792 to queue
[stream.hls][debug] Adding segment 793 to queue
[stream.hls][debug] Adding segment 794 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 795 to queue
[stream.hls][debug] Adding segment 796 to queue
[stream.hls][debug] Adding segment 797 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 798 to queue
[stream.hls][debug] Adding segment 799 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 800 to queue
[stream.hls][debug] Adding segment 801 to queue
[stream.hls][debug] Adding segment 802 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 803 to queue
[stream.hls][debug] Adding segment 804 to queue
[stream.hls][debug] Reloading playlist
[stream.hls][debug] Adding segment 805 to queue
[stream.hls][debug] Adding segment 806 to queue
[stream.hls][debug] Adding segment 807 to queue
[stream.hls][debug] Adding segment 808 to queue
error: Error when reading from stream: Read timeout, exiting
[stream.segmented][debug] Closing worker thread
[stream.segmented][debug] Closing writer thread
[cli][info] Stream ended
[cli][info] Closing currently open stream...
[flv @ 0x5e34bc0] Failed to update header with correct duration.ate=5949.6kbits/s speed=0.962x    
[flv @ 0x5e34bc0] Failed to update header with correct filesize.
frame=91800 fps= 58 q=-1.0 Lsize= 1112298kB time=00:25:31.54 bitrate=5949.5kbits/s speed=0.962x    
video:1084914kB audio:24015kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.303856%
[aac @ 0x5e30900] Qavg: 194.405

I can somewhat confirm this, in the sense that I'm using MPC-HC and I don't think it crashes, but it does timeout. This was the behavior that includes my previous comment. It wasn't a player issue, it was the timeout.

I've now tried it running with: --hls-timeout 9500

It doesn't exit now and correctly resumes the stream (but losing what happened during that time it stays paused, so it's not different than the ads screen, functionally).
> Mid-stream ads are annotated the same way pre-roll ads are, and they get filtered out correctly. Just checked again it on your example stream, as it shows ads in short intervals, which is quite handy to debug. Regarding that, there's nothing wrong.
> 
> > error: Error when reading from stream: Read timeout, exiting
> 
> Well, there you go. You'll of course need to increase the `--hls-timeout` (or `--stream-timeout` fallback) value when filtering out ads, otherwise you'll get timeouts due to the lack of any data returned by the stream reader. This is expected behavior.
> 
> We could add a workaround for that though which disables the timeout while filtering out ads, but I'm not sure if it really makes sense and whether it requires some code refactoring.


From my observation, it seems to be different from twitch's patch ads. Because viewing on the browser and APP is normal and there is no pop-up advertisement. But the anchor must have done something that caused the streamlink to be disconnected during the rest of the game.

The picture below is one of them, and the other was not found during the live broadcast. But it also leads to disconnection.
This seems to be different from twitch's ad, because he seems to be playing normally in the background.
 ![image](https://i.loli.net/2020/09/13/7VsGvWryehLCpwZ.png)
> The picture below is one of them

That is the **content** of their stream while they have triggered an ad on their channel. People see it when they have blocked the overlaying ad system of the web player. This has nothing to do with filtering embedded ads. When the embedded ads are shorter than ESL's "commercial break in progress" screen, you will still see it in the filtered stream.

> But it also leads to disconnection

This only happens if your timeout value is too low (default value is 60). As I've said, we could think about disabling the timeout logic while filtering out HLS segments, as there won't be data to read from the HLSStreamReader in this case, but this hasn't been done yet.

- https://github.com/streamlink/streamlink/blob/master/src/streamlink_cli/main.py#L386
- https://github.com/streamlink/streamlink/blob/master/src/streamlink/buffers.py#L104-L112
- https://github.com/streamlink/streamlink/blob/master/src/streamlink/stream/segmented.py#L206-L207
- https://github.com/streamlink/streamlink/blob/master/src/streamlink/stream/segmented.py#L183-L186
- https://github.com/streamlink/streamlink/blob/master/src/streamlink/stream/hls.py#L333
- https://github.com/streamlink/streamlink/blob/master/src/streamlink/session.py#L73
@bastimeyer So I only need to increase the `--hls-timeout` instead of adding `--player-no-close`?
> > The picture below is one of them
> 
> That is the **content** of their stream while they have triggered an ad on their channel. People see it when they have blocked the overlaying ad system of the web player. This has nothing to do with filtering embedded ads. When the embedded ads are shorter than ESL's "commercial break in progress" screen, you will still see it in the filtered stream.
> 
> > But it also leads to disconnection
> 
> This only happens if your timeout value is too low (default value is 60). As I've said, we could think about disabling the timeout logic while filtering out HLS segments, as there won't be data to read from the HLSStreamReader in this case, but this hasn't been done yet.
> 
> * https://github.com/streamlink/streamlink/blob/master/src/streamlink_cli/main.py#L386
> * https://github.com/streamlink/streamlink/blob/master/src/streamlink/buffers.py#L104-L112
> * https://github.com/streamlink/streamlink/blob/master/src/streamlink/stream/segmented.py#L206-L207
> * https://github.com/streamlink/streamlink/blob/master/src/streamlink/stream/segmented.py#L183-L186
> * https://github.com/streamlink/streamlink/blob/master/src/streamlink/stream/hls.py#L333
> * https://github.com/streamlink/streamlink/blob/master/src/streamlink/session.py#L73

I set a parameter of 120, but it seems to have no effect. As my debug shows, it still automatically exits after a period of time. And only this kind of official event channel will have problems, and the personal anchor channel can operate normally.

streamlink --stream-timeout 120 --twitch-low-latency --twitch-disable-ads https://www.twitch.tv/esl_sc2 best -O --loglevel debug | ffmpeg -re -i pipe:0 -b 8000k -c:v copy -c:a aac -f flv


I don’t know if anyone can help me test this address at the same time, it will be disconnected every 15 minutes (or countdown): https://www.twitch.tv/esl_sc2

Thank you very much.
> > I don't know what player is supposed to work, since VLC and MPC-HC are by far the most used players on Windows.
> 
> How does this preclude the player from being a problem? We run in to tons of issues all the times due to players including VLC. I'm watching a stream right now via MPV and it's perfectly fine using `--twitch-low-latency`. I even tested restarting the stream 5 times to get the pre-roll ad, no crashes at all. The debug output is pretty clear that this isn't a problem with Streamlink.
> 
> Edit: Unless someone can show me evidence that Streamlink is causing this problem in the next 12 hours I'm going to lock this thread, we go round and round on this every time Twitch enables the pre-roll ads again. If your player can't handle the commercials then go open an issue on their issue tracker.

If you have time, please help me test this link address: https://www.twitch.tv/esl_sc2

If the title of the live broadcast room is "rerun", then every 15 minutes or so (there is a set advertising time in the live broadcast room), streamlink will time out and exit because it cannot get the stream clips.

If the current live broadcast is in progress, there will be a midfield countdown after the game is over, during which streamlink will also crash because it cannot get the streaming clips.

I think this is a new way of advertising, which seems to be forcibly cut off the stream transmission and resume it after the advertisement ends. I have added the disable-ads parameter, but it seems to only work at the beginning. And when I watched it with the browser, no ads popped up, but streamlink just couldn't get the data. Thank you.
> I set a parameter of 120

No, you have not, because the default value of 60 of `hls-timeout` still gets applied, as I've shown in the linked source. You will have to set `hls-timeout` instead of `stream-timeout`, as it's only a fallback parameter for different stream implementations, HLS being one of them, nothing more. And increasing the value from 60 to 120 is not enough if the mid-stream ads are longer than 2 minutes. Set it to a higher value to circumvent the stream from timing out.

I've been running the link you've posted for more than 30 minutes now, with additional segment debugging. It had pre-roll ads and several mid-stream ads, and it's working fine when setting the hls-timeout to a higher value like 3600 for example.

There is no need to further spam this thread. We are aware of the issue, even though it's not a bug, and we've shown you a way to work around the issue for now. The suggested fix/improvement still needs to be explored and implemented. Remember that this is a bug tracker and not a support forum.
> > I set a parameter of 120
> 
> No, you have not, because the default value of 60 of `hls-timeout` still gets applied, as I've shown in the linked source. You will have to set `hls-timeout` instead of `stream-timeout`, as it's only a fallback parameter for different stream implementations, HLS being one of them, nothing more. And increasing the value from 60 to 120 is not enough if the mid-stream ads are longer than 2 minutes. Set it to a higher value to circumvent the stream from timing out.
> 
> I've been running the link you've posted for more than 30 minutes now, with additional segment debugging. It had pre-roll ads and several mid-stream ads, and it's working fine when setting the hls-timeout to a higher value like 3600 for example.
> 
> There is no need to further spam this thread. We are aware of the issue, even though it's not a bug, and we've shown you a way to work around the issue for now. The suggested fix/improvement still needs to be explored and implemented. Remember that this is a bug tracker and not a support forum.

Okay, thank you again for your answer, I am sorry for my behavior.
@bastimayer
"Other players, like MPV for example, are more resilient to this and thus handle it better without crashing."

Dunno if I'm having the exact same issue or not, but for me both vlc and mpv are crashing.

Also dunno what exactly reports this error in the output, but it's exactly same for vlc and mpv: "error: Error when reading from stream: Read timeout, exiting"

Might even be more than one issue - at least past couple of days I've had issues with commercials, but also some random crashes have occured when there shouldn't be a commercial break (unless Twitch really wants to force them during prime content).
> both vlc and mpv are crashing
> error: Error when reading from stream: Read timeout, exiting

No, they are not, Streamlink is terminating the player's process and its own due to lack of content when reaching the `hls-timeout` timer (default value of 60 seconds) when filtering out ads, as mentioned above.
https://github.com/streamlink/streamlink/issues/3165#issuecomment-691500807
Ah, my bad. I used --player-no-close, but didn't realize mpv itself closes at end of stream by default. Thanks!

Resolving patch:
diff --git a/src/streamlink/plugins/twitch.py b/src/streamlink/plugins/twitch.py
--- a/src/streamlink/plugins/twitch.py
+++ b/src/streamlink/plugins/twitch.py
@@ -17,7 +17,8 @@
 from streamlink.stream import (
     HTTPStream, HLSStream, FLVPlaylist, extract_flv_header_tags
 )
-from streamlink.stream.hls import HLSStreamReader, HLSStreamWriter, HLSStreamWorker
+from streamlink.stream.hls import HLSStreamWorker
+from streamlink.stream.hls_filtered import FilteredHLSStreamWriter, FilteredHLSStreamReader
 from streamlink.stream.hls_playlist import M3U8Parser, load as load_hls_playlist
 from streamlink.utils.times import hours_minutes_seconds
 
@@ -199,13 +200,12 @@ def process_sequences(self, playlist, sequences):
         return super(TwitchHLSStreamWorker, self).process_sequences(playlist, sequences)
 
 
-class TwitchHLSStreamWriter(HLSStreamWriter):
-    def write(self, sequence, *args, **kwargs):
-        if not (self.stream.disable_ads and sequence.segment.ad):
-            return super(TwitchHLSStreamWriter, self).write(sequence, *args, **kwargs)
+class TwitchHLSStreamWriter(FilteredHLSStreamWriter):
+    def should_filter_sequence(self, sequence):
+        return self.stream.disable_ads and sequence.segment.ad
 
 
-class TwitchHLSStreamReader(HLSStreamReader):
+class TwitchHLSStreamReader(FilteredHLSStreamReader):
     __worker__ = TwitchHLSStreamWorker
     __writer__ = TwitchHLSStreamWriter
 
diff --git a/src/streamlink/stream/hls_filtered.py b/src/streamlink/stream/hls_filtered.py
new file mode 100644
--- /dev/null
+++ b/src/streamlink/stream/hls_filtered.py
@@ -0,0 +1,53 @@
+import logging
+from threading import Event
+
+from .hls import HLSStreamWriter, HLSStreamReader
+
+
+log = logging.getLogger(__name__)
+
+
+class FilteredHLSStreamWriter(HLSStreamWriter):
+    def should_filter_sequence(self, sequence):
+        return False
+
+    def write(self, sequence, *args, **kwargs):
+        if not self.should_filter_sequence(sequence):
+            try:
+                return super(FilteredHLSStreamWriter, self).write(sequence, *args, **kwargs)
+            finally:
+                # unblock reader thread after writing data to the buffer
+                if not self.reader.filter_event.is_set():
+                    log.info("Resuming stream output")
+                    self.reader.filter_event.set()
+
+        # block reader thread if filtering out segments
+        elif self.reader.filter_event.is_set():
+            log.info("Filtering out segments and pausing stream output")
+            self.reader.filter_event.clear()
+
+
+class FilteredHLSStreamReader(HLSStreamReader):
+    def __init__(self, *args, **kwargs):
+        super(FilteredHLSStreamReader, self).__init__(*args, **kwargs)
+        self.filter_event = Event()
+        self.filter_event.set()
+
+    def read(self, size):
+        while True:
+            try:
+                return super(FilteredHLSStreamReader, self).read(size)
+            except IOError:
+                # wait indefinitely until filtering ends
+                self.filter_event.wait()
+                if self.buffer.closed:
+                    return b""
+                # if data is available, try reading again
+                if self.buffer.length > 0:
+                    continue
+                # raise if not filtering and no data available
+                raise
+
+    def close(self):
+        super(FilteredHLSStreamReader, self).close()
+        self.filter_event.set()


Test patch:
diff --git a/tests/streams/test_hls_filtered.py b/tests/streams/test_hls_filtered.py
new file mode 100644
--- /dev/null
+++ b/tests/streams/test_hls_filtered.py
@@ -0,0 +1,263 @@
+import requests_mock
+from tests.mock import MagicMock, call, patch
+import unittest
+
+import itertools
+from textwrap import dedent
+from threading import Event, Thread
+
+from streamlink.session import Streamlink
+from streamlink.stream.hls import HLSStream
+from streamlink.stream.hls_filtered import FilteredHLSStreamWriter, FilteredHLSStreamReader
+
+
+class _TestSubjectFilteredHLSStreamWriter(FilteredHLSStreamWriter):
+    def __init__(self, *args, **kwargs):
+        super(_TestSubjectFilteredHLSStreamWriter, self).__init__(*args, **kwargs)
+        self.write_wait = Event()
+        self.write_done = Event()
+
+    def write(self, *args, **kwargs):
+        # only write once per step
+        self.write_wait.wait()
+        self.write_wait.clear()
+
+        # don't write again during cleanup
+        if self.closed:
+            return
+
+        super(_TestSubjectFilteredHLSStreamWriter, self).write(*args, **kwargs)
+
+        # notify main thread that writing has finished
+        self.write_done.set()
+
+
+class _TestSubjectFilteredHLSReader(FilteredHLSStreamReader):
+    __writer__ = _TestSubjectFilteredHLSStreamWriter
+
+
+class _TestSubjectReadThread(Thread):
+    """
+    Run the reader on a separate thread, so that each read can be controlled from within the main thread
+    """
+    def __init__(self, segments, playlists):
+        Thread.__init__(self)
+        self.daemon = True
+
+        self.mocks = mocks = {}
+        self.mock = requests_mock.Mocker()
+        self.mock.start()
+
+        def addmock(method, url, *args, **kwargs):
+            mocks[url] = method(url, *args, **kwargs)
+
+        addmock(self.mock.get, TestFilteredHLSStream.url_playlist, [{"text": p} for p in playlists])
+        for i, segment in enumerate(segments):
+            addmock(self.mock.get, TestFilteredHLSStream.url_segment.format(i), content=segment)
+
+        session = Streamlink()
+        session.set_option("hls-live-edge", 2)
+        session.set_option("hls-timeout", 0)
+        session.set_option("stream-timeout", 0)
+
+        self.read_wait = Event()
+        self.read_done = Event()
+        self.data = []
+        self.error = None
+
+        self.stream = HLSStream(session, TestFilteredHLSStream.url_playlist)
+        self.reader = _TestSubjectFilteredHLSReader(self.stream)
+        self.reader.open()
+
+    def run(self):
+        while True:
+            # only read once per step
+            self.read_wait.wait()
+            self.read_wait.clear()
+
+            # don't read again during cleanup
+            if self.reader.closed:
+                return
+
+            try:
+                data = self.reader.read(-1)
+                self.data.append(data)
+            except IOError as err:
+                self.error = err
+                return
+            finally:
+                # notify main thread that reading has finished
+                self.read_done.set()
+
+    def cleanup(self):
+        self.reader.close()
+        self.mock.stop()
+        # make sure that write and read threads halts on cleanup
+        self.reader.writer.write_wait.set()
+        self.read_wait.set()
+
+    def await_write(self):
+        writer = self.reader.writer
+        if not writer.closed:
+            # make one write call and wait until write call has finished
+            writer.write_wait.set()
+            writer.write_done.wait()
+            writer.write_done.clear()
+
+    def await_read(self):
+        if not self.reader.closed:
+            # make one read call and wait until read call has finished
+            self.read_wait.set()
+            self.read_done.wait()
+            self.read_done.clear()
+
+
+@patch("streamlink.stream.hls.HLSStreamWorker.wait", MagicMock(return_value=True))
+class TestFilteredHLSStream(unittest.TestCase):
+    url_playlist = "http://mocked/path/playlist.m3u8"
+    url_segment = "http://mocked/path/stream{0}.ts"
+
+    @classmethod
+    def get_segments(cls, num):
+        return ["[{0}]".format(i).encode("ascii") for i in range(num)]
+
+    @classmethod
+    def get_playlist(cls, media_sequence, items, filtered=False, end=False):
+        playlist = dedent("""
+            #EXTM3U
+            #EXT-X-VERSION:5
+            #EXT-X-TARGETDURATION:1
+            #EXT-X-MEDIA-SEQUENCE:{0}
+        """.format(media_sequence))
+
+        for item in items:
+            playlist += "#EXTINF:1.000,{1}\nstream{0}.ts\n".format(item, "filtered" if filtered else "live")
+
+        if end:
+            playlist += "#EXT-X-ENDLIST\n"
+
+        return playlist
+
+    @classmethod
+    def filter_sequence(cls, sequence):
+        return sequence.segment.title == "filtered"
+
+    def subject(self, segments, playlists):
+        thread = _TestSubjectReadThread(segments, playlists)
+        self.addCleanup(thread.cleanup)
+        thread.start()
+
+        return thread, thread.reader, thread.reader.writer
+
+    @patch("streamlink.stream.hls_filtered.FilteredHLSStreamWriter.should_filter_sequence", new=filter_sequence)
+    @patch("streamlink.stream.hls_filtered.log")
+    def test_filtered_logging(self, mock_log):
+        segments = self.get_segments(8)
+        thread, reader, writer = self.subject(segments, [
+            self.get_playlist(0, [0, 1], filtered=True),
+            self.get_playlist(2, [2, 3], filtered=False),
+            self.get_playlist(4, [4, 5], filtered=True),
+            self.get_playlist(6, [6, 7], filtered=False, end=True)
+        ])
+
+        self.assertTrue(reader.filter_event.is_set(), "Doesn't let the reader wait if not filtering")
+
+        for i in range(2):
+            thread.await_write()
+            thread.await_write()
+            self.assertEqual(len(mock_log.info.mock_calls), i * 2 + 1)
+            self.assertEqual(mock_log.info.mock_calls[i * 2 + 0], call("Filtering out segments and pausing stream output"))
+            self.assertFalse(reader.filter_event.is_set(), "Lets the reader wait if filtering")
+
+            thread.await_write()
+            thread.await_write()
+            self.assertEqual(len(mock_log.info.mock_calls), i * 2 + 2)
+            self.assertEqual(mock_log.info.mock_calls[i * 2 + 1], call("Resuming stream output"))
+            self.assertTrue(reader.filter_event.is_set(), "Doesn't let the reader wait if not filtering")
+
+            thread.await_read()
+
+        self.assertEqual(
+            b"".join(thread.data),
+            b"".join(list(itertools.chain(segments[2:4], segments[6:8]))),
+            "Correctly filters out segments"
+        )
+        for i, _ in enumerate(segments):
+            self.assertTrue(thread.mocks[TestFilteredHLSStream.url_segment.format(i)].called, "Downloads all segments")
+
+    # don't patch should_filter_sequence here (it always returns False)
+    def test_not_filtered(self):
+        segments = self.get_segments(2)
+        thread, reader, writer = self.subject(segments, [
+            self.get_playlist(0, [0, 1], filtered=True, end=True)
+        ])
+
+        thread.await_write()
+        thread.await_write()
+        thread.await_read()
+        self.assertEqual(b"".join(thread.data), b"".join(segments[0:2]), "Does not filter by default")
+
+    @patch("streamlink.stream.hls_filtered.FilteredHLSStreamWriter.should_filter_sequence", new=filter_sequence)
+    def test_filtered_timeout(self):
+        segments = self.get_segments(2)
+        thread, reader, writer = self.subject(segments, [
+            self.get_playlist(0, [0, 1], filtered=False, end=True)
+        ])
+
+        thread.await_write()
+        thread.await_read()
+        self.assertEqual(thread.data, segments[0:1], "Has read the first segment")
+
+        # simulate a timeout by having an empty buffer
+        # timeout value is set to 0
+        thread.await_read()
+        self.assertIsInstance(thread.error, IOError, "Raises a timeout error when no data is available to read")
+
+    @patch("streamlink.stream.hls_filtered.FilteredHLSStreamWriter.should_filter_sequence", new=filter_sequence)
+    def test_filtered_no_timeout(self):
+        segments = self.get_segments(4)
+        thread, reader, writer = self.subject(segments, [
+            self.get_playlist(0, [0, 1], filtered=True),
+            self.get_playlist(2, [2, 3], filtered=False, end=True)
+        ])
+
+        self.assertTrue(reader.filter_event.is_set(), "Doesn't let the reader wait if not filtering")
+
+        thread.await_write()
+        thread.await_write()
+        self.assertFalse(reader.filter_event.is_set(), "Lets the reader wait if filtering")
+
+        # make reader read (no data available yet)
+        thread.read_wait.set()
+        # once data becomes available, the reader continues reading
+        thread.await_write()
+        self.assertTrue(reader.filter_event.is_set(), "Reader is not waiting anymore")
+
+        thread.read_done.wait()
+        thread.read_done.clear()
+        self.assertFalse(thread.error, "Doesn't time out when filtering")
+        self.assertEqual(thread.data, segments[2:3], "Reads next available buffer data")
+
+        thread.await_write()
+        thread.await_read()
+        self.assertEqual(thread.data, segments[2:4])
+
+    @patch("streamlink.stream.hls_filtered.FilteredHLSStreamWriter.should_filter_sequence", new=filter_sequence)
+    def test_filtered_closed(self):
+        segments = self.get_segments(2)
+        thread, reader, writer = self.subject(segments, [
+            self.get_playlist(0, [0, 1], filtered=True)
+        ])
+
+        self.assertTrue(reader.filter_event.is_set(), "Doesn't let the reader wait if not filtering")
+        thread.await_write()
+        self.assertFalse(reader.filter_event.is_set(), "Lets the reader wait if filtering")
+
+        # make reader read (no data available yet)
+        thread.read_wait.set()
+
+        # close stream while reader is waiting for filtering to end
+        thread.reader.close()
+        thread.read_done.wait()
+        thread.read_done.clear()
+        self.assertEqual(thread.data, [b""], "Stops reading on stream close")