Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
bae2e7a
Initial plan
Copilot Jul 29, 2025
88f844b
Initial analysis and planning for ts_demuxer integration
Copilot Jul 29, 2025
c6d0ad2
Implement TS Demuxer integration with full streaming mode support
Copilot Jul 30, 2025
5d22938
Complete TS Demuxer integration with documentation and testing guide
Copilot Jul 30, 2025
d9d353e
Fix TS Demuxer parsing issues with robust packet handling
Copilot Jul 30, 2025
2e6f78c
Enhance TS Demuxer with improved packet detection and debugging
Copilot Jul 30, 2025
bf00985
Fix TS Demuxer stream type detection to support stream types 0x15 and…
Copilot Jul 30, 2025
785f3a5
Implement separate video/audio file output with proper player integra…
Copilot Jul 30, 2025
bed4220
Fix compilation errors by removing undeclared CreatePlayerWithPipes m…
Copilot Jul 30, 2025
2da32ab
Fix file deletion issue causing "File not found" error for TS Demuxer…
Copilot Jul 30, 2025
613e5fb
Optimize TS Demuxer performance to fix slow video playback and audio …
Copilot Jul 30, 2025
25ace7b
Implement named pipes to replace temporary files for TS Demuxer strea…
Copilot Jul 30, 2025
a671dff
Fix compilation error: Replace CleanupTempFiles() with CleanupNamedPi…
Copilot Jul 30, 2025
de55251
Fix named pipe output initialization timing issue
Copilot Jul 30, 2025
a2944b7
Fix named pipe timing and error handling issues in TS Demuxer
Copilot Jul 30, 2025
6d1f9fa
Fix named pipe timing issue causing "File not found" error in MPC-HC
Copilot Jul 30, 2025
d3dce11
Fix player process status detection in TS Demuxer named pipes
Copilot Jul 30, 2025
6b78224
Add player validation and initialization delay for named pipe connect…
Copilot Jul 30, 2025
eed632f
Add enhanced debug logging to diagnose player startup issues
Copilot Jul 30, 2025
f2798c3
Implement hybrid named pipes/temp files solution for TS Demuxer based…
Copilot Jul 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -418,3 +418,7 @@ demo_exit_fix
demo_exit_fix.exe
test_exit_behavior.cpp
demo_exit_fix.cpp

# TS Demuxer integration files
ts_demuxer.zip
ts_demuxer-main/
85 changes: 73 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ A Twitch stream player for Windows with enhanced TLS support.
- **Zero-Copy Operations**: Minimizes memory allocations and data copying
- **Cross-Stream Isolation**: Each stream uses independent tx-queue for optimal multi-stream performance

## TS Demuxer Integration for Discontinuity Recovery

**TS Demuxer Mode** - Advanced stream processing for better discontinuity handling:

- **Video/Audio Separation**: Demuxes MPEG Transport Stream into separate H.264 video and ADTS AAC audio streams
- **Discontinuity Recovery**: Helps media players recover from getting stuck on black screen/last frame during discontinuities
- **Elementary Stream Processing**: Extracts clean elementary streams from TS packets
- **PAT/PMT Parsing**: Proper MPEG-TS table parsing for accurate stream identification
- **Memory-Based Processing**: Processes TS data in memory for optimal performance
- **Real-Time Demuxing**: Processes HLS segments in real-time as they are downloaded

The TS Demuxer mode is particularly useful when streams experience discontinuities that cause media players to freeze on the last video frame while audio continues playing. By sending separated video and audio streams, media players can recover more gracefully from such issues.

### TX-Queue Technical Details

The TX-Queue integration includes:
Expand All @@ -40,11 +53,13 @@ The TX-Queue integration includes:
| **Multi-Stream Performance** | **Degrades with concurrent streams** | **Scales linearly with isolated tx-queues** |
| **Data Integrity** | **Basic error handling** | **Built-in checksum validation** |
| **Throughput** | **Limited by lock contention** | **High-performance lock-free design** |
| **Discontinuity Handling** | **Raw TS data can cause player freezes** | **Separated A/V streams aid recovery** |

The integration works transparently:
1. **Primary**: TX-Queue IPC mode for optimal performance (default)
2. **Fallback**: TSDuck transport stream mode for professional compatibility
3. **Legacy**: Traditional HLS segment streaming for maximum compatibility
The integration works transparently with multiple streaming modes:
1. **TS Demuxer**: Video/audio separation for discontinuity recovery (recommended for problematic streams)
2. **TX-Queue IPC**: High-performance lock-free streaming (optimal for stable streams)
3. **TSDuck Transport Stream**: Professional transport stream mode for compatibility
4. **HLS Segments**: Traditional HLS segment streaming for maximum compatibility

### TX-Queue IPC Mode (Default)

Expand All @@ -58,6 +73,39 @@ The integration works transparently:

TX-Queue IPC Mode provides superior performance and reliability compared to traditional mutex-based streaming.

## TS Demuxer Mode for Discontinuity Recovery

**TS Demuxer Mode** addresses a specific issue where media players get stuck on a black screen or last video frame while audio continues playing after stream discontinuities stop being transmitted. This happens because:

1. **Discontinuity Problem**: When a stream has discontinuities (breaks in transmission), the raw MPEG Transport Stream data can confuse media players
2. **Player Behavior**: Players may freeze on the last received video frame while continuing to play audio
3. **Recovery Solution**: By separating video and audio into clean elementary streams, players can recover more gracefully

### How TS Demuxer Works

The TS Demuxer mode processes streams as follows:

1. **Downloads HLS segments** from the playlist like other modes
2. **Parses MPEG-TS packets** to identify video and audio stream PIDs via PAT/PMT tables
3. **Extracts elementary streams**:
- H.264 video stream (typically PID identified from PMT)
- ADTS AAC audio stream (typically PID identified from PMT)
4. **Sends separated streams** to the media player instead of raw TS data
5. **Maintains timing information** to help with synchronization

### When to Use TS Demuxer Mode

- Streams that frequently experience discontinuities
- Channels that cause player freezing/black screen issues
- Live streams with poor signal quality or transmission issues
- When you need maximum recovery capability from stream problems

The separated elementary streams help media players:
- Recover faster from discontinuities
- Avoid getting stuck on frozen frames
- Maintain better audio/video synchronization
- Handle problematic streams more gracefully

## TLS Client Integration

This version includes an integrated TLS client from the [tlsclient](https://github.com/zero3k/tlsclient) repository, providing:
Expand Down Expand Up @@ -124,14 +172,27 @@ A C++ application that buffers Twitch streams to media players like MPC-HC, MPC-

## Configuration

The application defaults to using MPV as the media player. To use a different player:

1. Go to **Tools → Settings** (when implemented)
2. Set the player path and arguments

Default settings:
- Player: `mpv.exe`
- Arguments: `-` (reads from stdin)
The application defaults to using MPV as the media player and TX-Queue IPC streaming mode. To change settings:

1. Go to **Tools → Settings**
2. Configure the following options:
- **Player Path**: Path to your preferred media player (default: `mpv.exe`)
- **Player Arguments**: Command line arguments for the player (default: `-`)
- **Streaming Mode**: Choose from:
- **HLS Segments (Legacy)**: Traditional segment-based streaming
- **Transport Stream (Professional)**: TSDuck transport stream routing
- **TX-Queue IPC (High Performance)**: Lock-free high-performance streaming
- **TS Demuxer (Video/Audio Separation)**: Demuxed streams for discontinuity recovery
- **Minimize to Tray**: Hide to system tray when minimized
- **Verbose Debug**: Enable detailed debug logging
- **Log to File**: Save logs to `debug.log` file

### Streaming Mode Selection

- **Use TS Demuxer mode** when experiencing player freezes during stream discontinuities
- **Use TX-Queue IPC mode** for optimal performance with stable streams
- **Use Transport Stream mode** for professional broadcast compatibility
- **Use HLS Segments mode** for maximum compatibility with older systems

## Technical Improvements

Expand Down
136 changes: 136 additions & 0 deletions TS_DEMUXER_TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# TS Demuxer Integration Testing Guide

This document provides instructions for testing the new TS Demuxer functionality in Tardsplaya.

## Building with TS Demuxer Support

The TS Demuxer integration is now part of the main build. All necessary source files are included:

- `ts_demuxer.c/.h` - Core TS demuxing functionality
- `es_output.c/.h` - Elementary stream output handling
- `ts_demuxer_stream.cpp/.h` - C++ integration and streaming management
- `print_out.h` - Logging and output macros (Windows-compatible)

## Testing the TS Demuxer Mode

### Prerequisites
1. Build Tardsplaya with Visual Studio 2019 or later
2. Have a media player installed (MPV recommended)
3. Access to Twitch streams that experience discontinuities

### Basic Testing Steps

1. **Launch Tardsplaya**
2. **Configure TS Demuxer Mode**:
- Go to Tools → Settings
- Set "Streaming Mode" to "TS Demuxer (Video/Audio Separation)"
- Click OK
3. **Test with a Stream**:
- Enter a channel name
- Click "1. Load" to fetch qualities
- Select a quality and click "2. Watch"
- Monitor the log for TS Demuxer messages

### Expected Log Messages

When TS Demuxer mode is active, you should see log messages like:
```
[TS Demuxer (Video/Audio Separation)] Starting streaming for [channel] (quality)
[TS_DEMUX] Initializing TS Demuxer stream manager for [channel]
[TS_DEMUX] TS Demuxer streaming active for [channel]
[TS_DEMUX] Segments: X, Video: Y, Audio: Z, Bytes: NNNkB
```

### Testing Discontinuity Recovery

To test the discontinuity recovery functionality:

1. **Find a problematic stream** that frequently has discontinuities
2. **Compare behavior** between streaming modes:
- Test with "TX-Queue IPC" mode first (baseline)
- Switch to "TS Demuxer" mode and test the same stream
- Look for differences in player behavior during discontinuities

### Debugging Issues

If the TS Demuxer mode isn't working properly:

1. **Enable verbose logging**:
- Tools → Settings → Check "Verbose debug"
- Check "Log to file" to save logs to `debug.log`

2. **Check for common issues**:
- Player process fails to start
- TS parsing errors (invalid packet sizes, sync loss)
- PAT/PMT parsing failures
- Video/Audio PID detection issues

3. **Compare with other modes**:
- If TS Demuxer fails, try Transport Stream mode
- If all modes fail, check player path and arguments

### Performance Monitoring

Monitor the status bar for:
- Segment processing count
- Buffer status
- Mode-specific indicators

The TS Demuxer mode should show:
`Buffer: X packets | TS Demuxer (Video/Audio Separation) Active`

### Advanced Testing

For developers testing the implementation:

1. **Memory leak testing**: Run streams for extended periods
2. **Multiple stream testing**: Open multiple tabs with TS Demuxer mode
3. **Error condition testing**: Test with invalid URLs, network interruptions
4. **Stream format testing**: Test with different types of HLS streams

### Known Limitations

Current implementation limitations:
- Single pipe output (video and audio are interleaved, not separate pipes)
- H.264 video and ADTS AAC audio support primarily
- Memory-based processing (no file I/O like original ts_demuxer)

### Future Enhancements

Planned improvements:
- Separate named pipes for video and audio streams
- Support for additional codecs (H.265, other audio formats)
- Better error recovery and stream format detection
- Performance optimizations for high-bitrate streams

## Troubleshooting

### Common Issues

1. **"Failed to initialize TS Demuxer system"**
- Check player path in settings
- Verify media player is accessible

2. **"Failed to start TS Demuxer streaming"**
- Check network connectivity
- Verify channel is online and accessible

3. **No video/audio PID detected**
- Stream may not contain standard H.264/AAC
- Try switching to Transport Stream mode as fallback

4. **Player process dies immediately**
- Check player arguments
- Verify player supports stdin input
- Try different media player (VLC, MPC-HC, etc.)

### Debug Information

Key debug messages to look for:
- PAT table parsing success/failure
- PMT table parsing and PID detection
- Video/Audio packet counts
- Player process status
- Memory allocation and cleanup

Report issues with detailed logs from verbose debug mode enabled.
80 changes: 75 additions & 5 deletions Tardsplaya.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ bool g_verboseDebug = false; // Enable verbose debug output for troubleshooting
bool g_logAutoScroll = true;
bool g_minimizeToTray = false;
bool g_logToFile = false; // Enable logging to debug.log file
StreamingMode g_streamingMode = StreamingMode::TX_QUEUE_IPC; // Default streaming mode



Expand Down Expand Up @@ -198,6 +199,14 @@ void LoadSettings() {

// Load verbose debug setting
g_verboseDebug = GetPrivateProfileIntW(L"Settings", L"VerboseDebug", 0, iniPath.c_str()) != 0;

// Load streaming mode setting
int streamingModeInt = GetPrivateProfileIntW(L"Settings", L"StreamingMode", static_cast<int>(StreamingMode::TX_QUEUE_IPC), iniPath.c_str());
if (streamingModeInt >= 0 && streamingModeInt <= static_cast<int>(StreamingMode::TS_DEMUXER)) {
g_streamingMode = static_cast<StreamingMode>(streamingModeInt);
} else {
g_streamingMode = StreamingMode::TX_QUEUE_IPC; // Default fallback
}
}

void SaveSettings() {
Expand Down Expand Up @@ -225,6 +234,9 @@ void SaveSettings() {

// Save verbose debug setting
WritePrivateProfileStringW(L"Settings", L"VerboseDebug", g_verboseDebug ? L"1" : L"0", iniPath.c_str());

// Save streaming mode setting
WritePrivateProfileStringW(L"Settings", L"StreamingMode", std::to_wstring(static_cast<int>(g_streamingMode)).c_str(), iniPath.c_str());
}

void AddLog(const std::wstring& msg) {
Expand Down Expand Up @@ -922,10 +934,29 @@ void WatchStream(StreamTab& tab, size_t tabIndex) {
AddDebugLog(L"WatchStream: Creating stream thread for tab " + std::to_wstring(tabIndex) +
L", PlayerPath=" + g_playerPath + L", URL=" + url);

// TX-Queue IPC Mode is now the default streaming mode
StreamingMode mode = StreamingMode::TX_QUEUE_IPC;
// Use the selected streaming mode from settings
StreamingMode mode = g_streamingMode;

AddLog(L"[TX-QUEUE] Starting TX-Queue IPC streaming for " + tab.channel + L" (" + standardQuality + L")");
std::wstring modeStr;
switch (mode) {
case StreamingMode::HLS_SEGMENTS:
modeStr = L"HLS Segments";
break;
case StreamingMode::TRANSPORT_STREAM:
modeStr = L"Transport Stream";
break;
case StreamingMode::TX_QUEUE_IPC:
modeStr = L"TX-Queue IPC";
break;
case StreamingMode::TS_DEMUXER:
modeStr = L"TS Demuxer (Video/Audio Separation)";
break;
default:
modeStr = L"Unknown";
break;
}

AddLog(L"[" + modeStr + L"] Starting streaming for " + tab.channel + L" (" + standardQuality + L")");

// Start the buffering thread
tab.streamThread = StartStreamThread(
Expand Down Expand Up @@ -958,7 +989,9 @@ void WatchStream(StreamTab& tab, size_t tabIndex) {
EnableWindow(tab.hQualities, FALSE);
EnableWindow(GetDlgItem(tab.hChild, IDC_LOAD), FALSE);
SetWindowTextW(tab.hWatchBtn, L"Starting...");
UpdateStatusBar(L"Buffer: Buffering... | TX-Queue IPC Active");

std::wstring statusMsg = L"Buffer: Buffering... | " + modeStr + L" Active";
UpdateStatusBar(statusMsg);

AddDebugLog(L"WatchStream: UI updated, stream starting for tab " + std::to_wstring(tabIndex));

Expand Down Expand Up @@ -1278,6 +1311,17 @@ INT_PTR CALLBACK SettingsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM
CheckDlgButton(hDlg, IDC_MINIMIZETOTRAY, g_minimizeToTray ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(hDlg, IDC_VERBOSE_DEBUG, g_verboseDebug ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(hDlg, IDC_LOG_TO_FILE, g_logToFile ? BST_CHECKED : BST_UNCHECKED);

// Initialize streaming mode combobox
{
HWND hCombo = GetDlgItem(hDlg, IDC_STREAMING_MODE);
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)L"HLS Segments (Legacy)");
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)L"Transport Stream (Professional)");
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)L"TX-Queue IPC (High Performance)");
SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)L"TS Demuxer (Video/Audio Separation)");
SendMessage(hCombo, CB_SETCURSEL, static_cast<WPARAM>(g_streamingMode), 0);
}

return TRUE;

case WM_COMMAND:
Expand Down Expand Up @@ -1317,6 +1361,13 @@ INT_PTR CALLBACK SettingsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM
g_verboseDebug = IsDlgButtonChecked(hDlg, IDC_VERBOSE_DEBUG) == BST_CHECKED;
g_logToFile = IsDlgButtonChecked(hDlg, IDC_LOG_TO_FILE) == BST_CHECKED;

// Save streaming mode selection
HWND hCombo = GetDlgItem(hDlg, IDC_STREAMING_MODE);
int selectedMode = (int)SendMessage(hCombo, CB_GETCURSEL, 0, 0);
if (selectedMode >= 0 && selectedMode <= static_cast<int>(StreamingMode::TS_DEMUXER)) {
g_streamingMode = static_cast<StreamingMode>(selectedMode);
}

// Save settings to INI file
SaveSettings();

Expand Down Expand Up @@ -1550,7 +1601,26 @@ LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
} else {
// No active streams, stop the timer
KillTimer(hwnd, TIMER_CHUNK_UPDATE);
UpdateStatusBar(L"Buffer: 0 packets | TX-Queue IPC Ready");

std::wstring readyMsg;
switch (g_streamingMode) {
case StreamingMode::HLS_SEGMENTS:
readyMsg = L"Buffer: 0 packets | HLS Segments Ready";
break;
case StreamingMode::TRANSPORT_STREAM:
readyMsg = L"Buffer: 0 packets | Transport Stream Ready";
break;
case StreamingMode::TX_QUEUE_IPC:
readyMsg = L"Buffer: 0 packets | TX-Queue IPC Ready";
break;
case StreamingMode::TS_DEMUXER:
readyMsg = L"Buffer: 0 packets | TS Demuxer Ready";
break;
default:
readyMsg = L"Buffer: 0 packets";
break;
}
UpdateStatusBar(readyMsg);
}
}
break;
Expand Down
Loading