Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
8705776
Initial plan
Copilot Jul 31, 2025
c5878c3
Add browser playback support with mpegts.js integration
Copilot Jul 31, 2025
34cd79a
Implement actual stream serving for browser playback
Copilot Jul 31, 2025
054f581
Add documentation and demo for browser playback feature
Copilot Jul 31, 2025
839c337
Fix C2015 compilation errors by splitting large HTML string literals
Copilot Jul 31, 2025
1edb877
Fix C2065 compilation errors - fix raw string literals and move mode …
Copilot Jul 31, 2025
6eb8937
Fix raw string literal syntax errors causing C2065 compilation errors
Copilot Jul 31, 2025
77fc198
Fix C2065 compilation errors - correct raw string literal syntax and …
Copilot Jul 31, 2025
87694a5
Fix raw string literal syntax errors in HTML/JavaScript generation
Copilot Jul 31, 2025
388c300
Fix C++ raw string literal syntax errors in HTTP server HTML generation
Copilot Jul 31, 2025
2ba0869
Implement functional browser playback with separate JavaScript file
Copilot Jul 31, 2025
e1e88e3
Improve browser playback reliability and error handling
Copilot Jul 31, 2025
497c74f
Fix compilation errors: add missing include, fix string concatenation…
Copilot Jul 31, 2025
6bdbb94
Fix JavaScript loading issue - embed player.js content in HTTP server
Copilot Jul 31, 2025
1c13e57
Fix browser streaming connectivity issues - add CORS, debug logging, …
Copilot Jul 31, 2025
f8c2383
Enhanced HTTP server debugging and connection handling for browser st…
Copilot Jul 31, 2025
bd46215
Fix browser streaming by implementing buffer threshold to ensure data…
Copilot Jul 31, 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
94 changes: 94 additions & 0 deletions BROWSER_PLAYBACK_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# Browser Playback Feature - Testing Guide

## Overview
The browser playback feature allows Tardsplaya to serve Twitch streams directly to web browsers instead of external media players like MPV or VLC. This uses the native HTML5 MediaSource API to play MPEG-TS streams.

## How It Works

### 1. User Enables Browser Mode
- Go to Tools → Settings
- Check "Use browser playback (mpegts.js)"
- Click OK to save settings

### 2. Stream Playback Process
1. User loads a channel and selects quality (same as before)
2. User clicks "Watch" - instead of launching MPV/VLC:
- HTTP server starts on port 8080+ (unique per tab)
- Browser opens automatically to http://127.0.0.1:8080/player.html
- Stream segments are downloaded and served via HTTP
- Browser uses MediaSource API to play the stream

### 3. Technical Flow
```
Twitch HLS Stream → Tardsplaya Downloads Segments → HTTP Server → Browser Player
```

## Key Components

### HttpStreamServer Class (`http_server.h/cpp`)
- Lightweight HTTP server for serving stream data
- Handles multiple concurrent browser connections
- Serves HTML player page with MediaSource API integration
- Queues and serves MPEG-TS segments to browsers

### BufferAndServeStreamToBrowser Function (`stream_pipe.cpp`)
- Downloads HLS segments from Twitch
- Serves segments to HttpStreamServer for browser consumption
- Handles segment buffering and error recovery
- Similar to BufferAndPipeStreamToPlayer but serves HTTP instead of piping to stdin

### Browser Player (HTML/JavaScript)
- Uses native MediaSource API for MPEG-TS playback
- Automatic segment fetching and buffering
- Handles stream initialization and error recovery
- Standard video controls (play/pause/fullscreen)

## Benefits Over Media Players

1. **No External Dependencies**: No need to install MPV, VLC, etc.
2. **Cross-Platform**: Works on any OS with a modern browser
3. **Web-Native**: Familiar browser video controls and behavior
4. **Multiple Streams**: Easy to have multiple browser tabs/windows
5. **Integration**: Better integration with web-based workflows

## Testing

To test this feature:

1. Build Tardsplaya with the new code
2. Enable browser playback in settings
3. Load a channel and click Watch
4. Browser should open automatically with the stream
5. Verify stream plays correctly in browser
6. Test multiple concurrent streams in different tabs

## Compatibility

- **Browsers**: Chrome/Edge 23+, Firefox 42+, Safari 8+
- **Codecs**: H.264/AVC video, AAC audio (standard Twitch streams)
- **Format**: MPEG-TS container format
- **API**: HTML5 MediaSource Extensions

## Configuration Files

The browser mode setting is saved in `Tardsplaya.ini`:
```ini
[Settings]
UseBrowserPlayback=1
```

## Status Messages

When browser mode is active:
- Status bar shows "Browser Playback Ready" instead of "TX-Queue IPC Ready"
- Log messages are prefixed with "[BROWSER]"
- HTTP server port is logged for debugging

## Troubleshooting

If browser playback doesn't work:
1. Check that port 8080+ isn't blocked by firewall
2. Verify browser supports MediaSource API
3. Check browser developer console for JavaScript errors
4. Try disabling browser extensions that might interfere
5. Fall back to traditional media player mode if needed
7 changes: 7 additions & 0 deletions Tardsplaya-BrowserMode.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[Settings]
PlayerPath=mpv.exe
PlayerArgs=-
MinimizeToTray=0
VerboseDebug=1
LogToFile=1
UseBrowserPlayback=1
39 changes: 34 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
bool g_useBrowserPlayback = false; // Enable browser-based playback using mpegts.js



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

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

// Load browser playback setting
g_useBrowserPlayback = GetPrivateProfileIntW(L"Settings", L"UseBrowserPlayback", 0, iniPath.c_str()) != 0;
}

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

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

// Save browser playback setting
WritePrivateProfileStringW(L"Settings", L"UseBrowserPlayback", g_useBrowserPlayback ? L"1" : L"0", iniPath.c_str());
}

void AddLog(const std::wstring& msg) {
Expand Down Expand Up @@ -843,7 +850,8 @@ void StopStream(StreamTab& tab, bool userInitiated = false) {
}

if (!hasActiveStream) {
UpdateStatusBar(L"Buffer: 0 packets");
UpdateStatusBar(std::wstring(L"Buffer: 0 packets | ") +
(g_useBrowserPlayback ? L"Browser Playback Ready" : L"TX-Queue IPC Ready"));
}

AddLog(L"Stream stopped.");
Expand Down Expand Up @@ -919,13 +927,22 @@ void WatchStream(StreamTab& tab, size_t tabIndex) {
tab.cancelToken = false;
tab.userRequestedStop = false;

// Determine streaming mode based on settings
StreamingMode mode = StreamingMode::TX_QUEUE_IPC; // Default

if (g_useBrowserPlayback) {
mode = StreamingMode::BROWSER_PLAYBACK;
}

AddDebugLog(L"WatchStream: Creating stream thread for tab " + std::to_wstring(tabIndex) +
L", Mode=" + (mode == StreamingMode::BROWSER_PLAYBACK ? L"BROWSER" : L"TX_QUEUE") +
L", PlayerPath=" + g_playerPath + L", URL=" + url);

// TX-Queue IPC Mode is now the default streaming mode
StreamingMode mode = StreamingMode::TX_QUEUE_IPC;
// StreamingMode mode = StreamingMode::TX_QUEUE_IPC;

AddLog(L"[TX-QUEUE] Starting TX-Queue IPC streaming for " + tab.channel + L" (" + standardQuality + L")");
// AddLog(L"[TX-QUEUE] Starting TX-Queue IPC streaming for " + tab.channel + L" (" + standardQuality + L")");
// ^^ These lines are now moved above

// Start the buffering thread
tab.streamThread = StartStreamThread(
Expand Down Expand Up @@ -958,7 +975,8 @@ 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");
UpdateStatusBar(std::wstring(L"Buffer: Buffering... | ") +
(mode == StreamingMode::BROWSER_PLAYBACK ? L"Browser Playback Active" : L"TX-Queue IPC Active"));

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

Expand All @@ -969,6 +987,14 @@ void WatchStream(StreamTab& tab, size_t tabIndex) {
SetTimer(g_hMainWnd, TIMER_CHUNK_UPDATE, 2000, nullptr);

// Don't detach the thread - keep it joinable for proper synchronization

// Add appropriate log message based on mode
if (mode == StreamingMode::BROWSER_PLAYBACK) {
AddLog(L"[BROWSER] Starting Browser Playback streaming for " + tab.channel + L" (" + standardQuality + L")");
} else {
// TX-Queue IPC Mode is the default streaming mode for media players
AddLog(L"[TX-QUEUE] Starting TX-Queue IPC streaming for " + tab.channel + L" (" + standardQuality + L")");
}
}

LRESULT CALLBACK StreamChildProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
Expand Down Expand Up @@ -1278,6 +1304,7 @@ 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);
CheckDlgButton(hDlg, IDC_BROWSER_PLAYBACK, g_useBrowserPlayback ? BST_CHECKED : BST_UNCHECKED);
return TRUE;

case WM_COMMAND:
Expand Down Expand Up @@ -1316,6 +1343,7 @@ INT_PTR CALLBACK SettingsDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM
g_minimizeToTray = IsDlgButtonChecked(hDlg, IDC_MINIMIZETOTRAY) == BST_CHECKED;
g_verboseDebug = IsDlgButtonChecked(hDlg, IDC_VERBOSE_DEBUG) == BST_CHECKED;
g_logToFile = IsDlgButtonChecked(hDlg, IDC_LOG_TO_FILE) == BST_CHECKED;
g_useBrowserPlayback = IsDlgButtonChecked(hDlg, IDC_BROWSER_PLAYBACK) == BST_CHECKED;

// Save settings to INI file
SaveSettings();
Expand Down Expand Up @@ -1550,7 +1578,8 @@ 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");
UpdateStatusBar(std::wstring(L"Buffer: 0 packets | ") +
(g_useBrowserPlayback ? L"Browser Playback Ready" : L"TX-Queue IPC Ready"));
}
}
break;
Expand Down
2 changes: 2 additions & 0 deletions Tardsplaya.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="favorites.cpp" />
<ClCompile Include="http_server.cpp" />
<ClCompile Include="playlist_parser.cpp" />
<ClCompile Include="stream_memory_map.cpp" />
<ClCompile Include="stream_pipe.cpp" />
Expand All @@ -122,6 +123,7 @@
</ItemGroup>
<ItemGroup>
<ClInclude Include="favorites.h" />
<ClInclude Include="http_server.h" />
<ClInclude Include="json_minimal.h" />
<ClInclude Include="tlsclient\lock.h" />
<ClInclude Include="playlist_parser.h" />
Expand Down
Loading