Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 16 additions & 22 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,25 +61,30 @@ jobs:
cp -r shaders release-artifacts/
cd release-artifacts
tar -czf uvc2gl-${{ steps.version.outputs.version }}-linux-x86_64.tar.gz uvc2gl shaders
mv uvc2gl-${{ steps.version.outputs.version }}-linux-x86_64.tar.gz ..
cd ..

- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref_name }}
body: |
## uvc2gl ${{ steps.version.outputs.version }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PRERELEASE_FLAG=""
if [[ "${{ github.ref_name }}" == *"-"* ]]; then
PRERELEASE_FLAG="--prerelease"
fi

gh release create ${{ github.ref_name }} \
$PRERELEASE_FLAG \
--title "Release ${{ github.ref_name }}" \
--notes "## uvc2gl ${{ steps.version.outputs.version }}

### Installation
```bash
\`\`\`bash
tar -xzf uvc2gl-${{ steps.version.outputs.version }}-linux-x86_64.tar.gz
cd uvc2gl
./uvc2gl
```
\`\`\`

### Requirements
- Linux kernel with V4L2 support
Expand All @@ -89,16 +94,5 @@ jobs:
### What's Changed
See commit history for details.

**Full Changelog**: https://github.com/${{ github.repository }}/compare/v${{ steps.version.outputs.version }}...HEAD
draft: false
prerelease: ${{ contains(github.ref, '-') && !contains(github.ref, 'v0.0.0-') }}

- name: Upload Release Asset
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./build/release-artifacts/uvc2gl-${{ steps.version.outputs.version }}-linux-x86_64.tar.gz
asset_name: uvc2gl-${{ steps.version.outputs.version }}-linux-x86_64.tar.gz
asset_content_type: application/gzip
**Full Changelog**: https://github.com/${{ github.repository }}/commits/${{ github.ref_name }}" \
./build/uvc2gl-${{ steps.version.outputs.version }}-linux-x86_64.tar.gz
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.2.0-dev
1.2.1-dev
1 change: 1 addition & 0 deletions src/audio/AudioCapture.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <alsa/asoundlib.h>
#include <atomic>
#include <mutex>
#include <string>
#include <thread>
#include <vector>
Expand Down
258 changes: 150 additions & 108 deletions src/core/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,133 +29,175 @@ Application::Application(const char* title, int width, int height) {
// Initialize ImGui
InitImGui();

// Load config
m_config.LoadFromFile(m_configPath);
m_fallbackMode = false;

// Enumerate available devices
m_availableDevices = V4L2Capabilities::EnumerateDevices();

// Try to use saved device if available, otherwise fallback
m_currentDevice = m_config.videoDevice;
bool deviceFound = false;

if (!m_availableDevices.empty()) {
for (const auto& device : m_availableDevices) {
if (device.path == m_config.videoDevice) {
deviceFound = true;
break;
try {
// Load config
m_config.LoadFromFile(m_configPath);

// Enumerate available devices
m_availableDevices = V4L2Capabilities::EnumerateDevices();

// Try to use saved device if available, otherwise fallback
m_currentDevice = m_config.videoDevice;
bool deviceFound = false;

if (!m_availableDevices.empty()) {
for (const auto& device : m_availableDevices) {
if (device.path == m_config.videoDevice) {
deviceFound = true;
break;
}
}

if (!deviceFound) {
// Saved device not found, use first available
m_currentDevice = m_availableDevices[0].path;
std::cout << "Saved device not found, using " << m_currentDevice << std::endl;
}
std::cout << "Found " << m_availableDevices.size() << " video device(s)" << std::endl;
}

if (!deviceFound) {
// Saved device not found, use first available
m_currentDevice = m_availableDevices[0].path;
std::cout << "Saved device not found, using " << m_currentDevice << std::endl;
// Query available formats for current device
m_availableFormats = V4L2Capabilities::QueryFormats(m_currentDevice);

// Try to use saved resolution/fps if available
m_currentWidth = m_config.width;
m_currentHeight = m_config.height;
m_currentFps = m_config.fps;
m_currentFormat = m_config.videoFormat;

bool formatFound = false;
if (!m_availableFormats.empty()) {
for (const auto& format : m_availableFormats) {
if (format.width == m_config.width &&
format.height == m_config.height &&
format.fps == m_config.fps) {
formatFound = true;
break;
}
}

if (!formatFound) {
// Saved format not available, use first available
m_currentWidth = m_availableFormats[0].width;
m_currentHeight = m_availableFormats[0].height;
m_currentFps = m_availableFormats[0].fps;
std::cout << "Saved format not available, using " << m_currentWidth << "x"
<< m_currentHeight << "@" << m_currentFps << "fps" << std::endl;
}
}
std::cout << "Found " << m_availableDevices.size() << " video device(s)" << std::endl;
}

// Query available formats for current device
m_availableFormats = V4L2Capabilities::QueryFormats(m_currentDevice);

// Try to use saved resolution/fps if available
m_currentWidth = m_config.width;
m_currentHeight = m_config.height;
m_currentFps = m_config.fps;
m_currentFormat = m_config.videoFormat;

bool formatFound = false;
if (!m_availableFormats.empty()) {
for (const auto& format : m_availableFormats) {
if (format.width == m_config.width &&
format.height == m_config.height &&
format.fps == m_config.fps) {
formatFound = true;
break;

// Try to initialize video capture (may fail if device not available)
bool videoInitialized = false;
try {
m_video = std::make_unique<VideoCapture>(m_currentDevice, m_currentWidth, m_currentHeight, m_currentFps, m_currentFormat, 10);
m_decoder = std::make_unique<MjpgDecoder>();
m_video->Start();

// Give it a moment to start up and validate it's actually working
std::this_thread::sleep_for(std::chrono::milliseconds(200));

if (!m_video->IsRunning() || m_video->HasError()) {
std::cerr << "Video capture failed to start properly" << std::endl;
if (m_video->HasError()) {
std::cerr << "Error: " << m_video->GetLastError() << std::endl;
}
m_video.reset();
m_decoder.reset();
} else {
std::cout << "Video capture started on " << m_currentDevice << std::endl;
videoInitialized = true;
}
} catch (const std::exception& e) {
std::cerr << "Warning: Failed to initialize video capture: " << e.what() << std::endl;
m_video.reset();
m_decoder.reset();
}

if (!formatFound) {
// Saved format not available, use first available
m_currentWidth = m_availableFormats[0].width;
m_currentHeight = m_availableFormats[0].height;
m_currentFps = m_availableFormats[0].fps;
std::cout << "Saved format not available, using " << m_currentWidth << "x"
<< m_currentHeight << "@" << m_currentFps << "fps" << std::endl;
// Initialize audio capture
m_availableAudioDevices = ALSACapabilities::EnumerateDevices();

// Try to use saved audio device if available
m_currentAudioDevice = m_config.audioDevice;
bool audioDeviceFound = false;

for (const auto& device : m_availableAudioDevices) {
if (device.name == m_config.audioDevice) {
audioDeviceFound = true;
break;
}
}
}

// Try to initialize video capture (may fail if device not available)
try {
m_video = std::make_unique<VideoCapture>(m_currentDevice, m_currentWidth, m_currentHeight, m_currentFps, m_currentFormat, 10);
m_decoder = std::make_unique<MjpgDecoder>();
m_video->Start();

// Give it a moment to start up and validate it's actually working
std::this_thread::sleep_for(std::chrono::milliseconds(200));
if (!audioDeviceFound && !m_availableAudioDevices.empty()) {
// Saved audio device not found, fallback to pulse or first device
m_currentAudioDevice = "pulse";
for (const auto& device : m_availableAudioDevices) {
if (device.description.find("USB3 Video") != std::string::npos ||
device.description.find("USB 3 Video") != std::string::npos) {
m_currentAudioDevice = device.name;
std::cout << "Found capture card audio: " << device.description << std::endl;
break;
}
}
}

if (!m_video->IsRunning()) {
std::cerr << "Video capture failed to start properly, resetting" << std::endl;
m_video.reset();
m_decoder.reset();
} else {
std::cout << "Video capture started on " << m_currentDevice << std::endl;
bool audioInitialized = false;
try {
m_audio = std::make_unique<AudioCapture>(m_currentAudioDevice, 48000, 2, 1024);
m_audio->Start();
std::cout << "Audio capture started on " << m_currentAudioDevice << std::endl;
audioInitialized = true;
} catch (const std::exception& e) {
std::cerr << "Warning: Failed to initialize audio capture: " << e.what() << std::endl;
std::cerr << "Attempting to use system audio only." << std::endl;
}
} catch (const std::exception& e) {
std::cerr << "Warning: Failed to initialize video capture: " << e.what() << std::endl;
std::cerr << "Running without video input." << std::endl;
m_video.reset();
m_decoder.reset();
}

// Initialize audio capture
m_availableAudioDevices = ALSACapabilities::EnumerateDevices();

// Try to use saved audio device if available
m_currentAudioDevice = m_config.audioDevice;
bool audioDeviceFound = false;

for (const auto& device : m_availableAudioDevices) {
if (device.name == m_config.audioDevice) {
audioDeviceFound = true;
break;

// Initialize audio playback
try {
m_audioPlayback = std::make_unique<AudioPlayback>(48000, 2);
m_audioPlayback->Start();
// Restore saved volume
m_audioPlayback->SetVolume(m_config.volume);
} catch (const std::exception& e) {
std::cerr << "Warning: Failed to initialize audio playback: " << e.what() << std::endl;
std::cerr << "Continuing without audio." << std::endl;
}
}

if (!audioDeviceFound && !m_availableAudioDevices.empty()) {
// Saved audio device not found, fallback to pulse or first device
m_currentAudioDevice = "pulse";
for (const auto& device : m_availableAudioDevices) {
if (device.description.find("USB3 Video") != std::string::npos ||
device.description.find("USB 3 Video") != std::string::npos) {
m_currentAudioDevice = device.name;
std::cout << "Found capture card audio: " << device.description << std::endl;
break;
// Determine if we need fallback mode
if (!videoInitialized) {
std::cout << "\n========== FALLBACK MODE ACTIVATED ==========" << std::endl;
std::cout << "Video capture unavailable. Running with black screen." << std::endl;
if (!audioInitialized && m_audioPlayback) {
std::cout << "Audio: Using system audio playback only." << std::endl;
} else if (!audioInitialized) {
std::cout << "Audio: Unavailable." << std::endl;
} else {
std::cout << "Audio: Capture enabled." << std::endl;
}
std::cout << "==========================================\n" << std::endl;
m_fallbackMode = true;
}
}

try {
m_audio = std::make_unique<AudioCapture>(m_currentAudioDevice, 48000, 2, 1024);
m_audio->Start();
std::cout << "Audio capture started on " << m_currentAudioDevice << std::endl;
} catch (const std::exception& e) {
std::cerr << "Warning: Failed to initialize audio capture: " << e.what() << std::endl;
std::cerr << "Running without audio input." << std::endl;
}

// Initialize audio playback
try {
m_audioPlayback = std::make_unique<AudioPlayback>(48000, 2);
m_audioPlayback->Start();
// Restore saved volume
m_audioPlayback->SetVolume(m_config.volume);

} catch (const std::exception& e) {
std::cerr << "Warning: Failed to initialize audio playback: " << e.what() << std::endl;
std::cerr << "Running without audio output." << std::endl;
std::cerr << "Critical error during initialization: " << e.what() << std::endl;
std::cerr << "Entering emergency fallback mode." << std::endl;
m_fallbackMode = true;

// Try to at least initialize audio playback for fallback
try {
if (!m_audioPlayback) {
m_audioPlayback = std::make_unique<AudioPlayback>(48000, 2);
m_audioPlayback->Start();
m_audioPlayback->SetVolume(0.5f);
}
} catch (const std::exception& e2) {
std::cerr << "Failed to initialize audio playback in fallback: " << e2.what() << std::endl;
}
}
}


Application::~Application() {
std::cout << "Shutting down application..." << std::endl;

Expand Down
1 change: 1 addition & 0 deletions src/core/Application.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class Application {
int m_currentFps = 30;
std::string m_currentFormat = "YUYV";
bool m_isFullscreen = false;
bool m_fallbackMode = false; // Black screen + system audio when video/audio init fails

AppConfig m_config;
std::string m_configPath = "uvc2gl.conf";
Expand Down
1 change: 1 addition & 0 deletions src/core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ struct AppConfig {
int fps = 30;
std::string videoFormat = "MJPEG"; // MJPEG or YUYV
float volume = 1.0f;
bool fallbackMode = false; // Use black screen + system audio if config fails to load

bool LoadFromFile(const std::string& filename) {
std::ifstream file(filename);
Expand Down
2 changes: 2 additions & 0 deletions src/video/VideoCapture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ namespace uvc2gl {
m_mjpegDecoder = std::make_unique<MjpgDecoder>();
m_yuyvDecoder = std::make_unique<YuyvDecoder>();
m_Running = false;
m_LastError = "";
}

VideoCapture::~VideoCapture() {
Expand Down Expand Up @@ -209,6 +210,7 @@ namespace uvc2gl {
}
close(fd);
} catch (const std::exception& e) {
m_LastError = e.what();
std::cerr << "Video capture error: " << e.what() << std::endl;
m_Running = false;
}
Expand Down
Loading
Loading