This milestone completes the video processor with full 360° video support: ## New Features - Complete 360° video analysis and processing pipeline - Multi-projection support (equirectangular, cubemap, EAC, stereographic, fisheye) - Viewport extraction and animated viewport tracking - Spatial audio processing (ambisonic, binaural, object-based) - 360° adaptive streaming with tiled encoding - AI-enhanced 360° content analysis integration - Comprehensive test infrastructure with synthetic video generation ## Core Components - Video360Processor: Complete 360° analysis and processing - ProjectionConverter: Batch conversion between projections - SpatialAudioProcessor: Advanced spatial audio handling - Video360StreamProcessor: Viewport-adaptive streaming - Comprehensive data models and validation ## Test Infrastructure - 360° video downloader with curated test sources - Synthetic 360° video generator for CI/CD - Integration tests covering full processing pipeline - Performance benchmarks for parallel processing ## Documentation & Examples - Complete 360° processing examples and workflows - Comprehensive development summary documentation - Integration guides for all four processing phases This completes the roadmap: AI analysis, advanced codecs, streaming, and 360° video processing. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
178 lines
6.7 KiB
Python
178 lines
6.7 KiB
Python
"""Tests for 360° video functionality."""
|
|
|
|
import pytest
|
|
|
|
from video_processor import HAS_360_SUPPORT, ProcessorConfig
|
|
from video_processor.utils.video_360 import Video360Detection, Video360Utils
|
|
|
|
|
|
class TestVideo360Detection:
|
|
"""Tests for 360° video detection."""
|
|
|
|
def test_aspect_ratio_detection(self):
|
|
"""Test 360° detection based on aspect ratio."""
|
|
# Mock metadata for 2:1 aspect ratio (typical 360° video)
|
|
metadata = {
|
|
"video": {
|
|
"width": 3840,
|
|
"height": 1920,
|
|
},
|
|
"filename": "test_video.mp4",
|
|
}
|
|
|
|
result = Video360Detection.detect_360_video(metadata)
|
|
|
|
assert result["is_360_video"] is True
|
|
assert "aspect_ratio" in result["detection_methods"]
|
|
assert result["confidence"] >= 0.8
|
|
|
|
def test_filename_pattern_detection(self):
|
|
"""Test 360° detection based on filename patterns."""
|
|
metadata = {
|
|
"video": {"width": 1920, "height": 1080},
|
|
"filename": "my_360_video.mp4",
|
|
}
|
|
|
|
result = Video360Detection.detect_360_video(metadata)
|
|
|
|
assert result["is_360_video"] is True
|
|
assert "filename" in result["detection_methods"]
|
|
assert result["projection_type"] == "equirectangular"
|
|
|
|
def test_spherical_metadata_detection(self):
|
|
"""Test 360° detection based on spherical metadata."""
|
|
metadata = {
|
|
"video": {"width": 1920, "height": 1080},
|
|
"filename": "test.mp4",
|
|
"format": {"tags": {"Spherical": "1", "ProjectionType": "equirectangular"}},
|
|
}
|
|
|
|
result = Video360Detection.detect_360_video(metadata)
|
|
|
|
assert result["is_360_video"] is True
|
|
assert "spherical_metadata" in result["detection_methods"]
|
|
assert result["confidence"] == 1.0
|
|
assert result["projection_type"] == "equirectangular"
|
|
|
|
def test_no_360_detection(self):
|
|
"""Test that regular videos are not detected as 360°."""
|
|
metadata = {
|
|
"video": {"width": 1920, "height": 1080},
|
|
"filename": "regular_video.mp4",
|
|
}
|
|
|
|
result = Video360Detection.detect_360_video(metadata)
|
|
|
|
assert result["is_360_video"] is False
|
|
assert result["confidence"] == 0.0
|
|
assert len(result["detection_methods"]) == 0
|
|
|
|
|
|
class TestVideo360Utils:
|
|
"""Tests for 360° video utilities."""
|
|
|
|
def test_bitrate_multipliers(self):
|
|
"""Test bitrate multipliers for different projection types."""
|
|
assert (
|
|
Video360Utils.get_recommended_bitrate_multiplier("equirectangular") == 2.5
|
|
)
|
|
assert Video360Utils.get_recommended_bitrate_multiplier("cubemap") == 2.0
|
|
assert Video360Utils.get_recommended_bitrate_multiplier("unknown") == 2.0
|
|
|
|
def test_optimal_resolutions(self):
|
|
"""Test optimal resolution recommendations."""
|
|
equirect_resolutions = Video360Utils.get_optimal_resolutions("equirectangular")
|
|
assert (3840, 1920) in equirect_resolutions # 4K 360°
|
|
assert (1920, 960) in equirect_resolutions # 2K 360°
|
|
|
|
def test_missing_dependencies(self):
|
|
"""Test missing dependency detection."""
|
|
missing = Video360Utils.get_missing_dependencies()
|
|
assert isinstance(missing, list)
|
|
|
|
# Without optional dependencies, these should be missing
|
|
if not HAS_360_SUPPORT:
|
|
assert "opencv-python" in missing
|
|
assert "py360convert" in missing
|
|
|
|
|
|
class TestProcessorConfig360:
|
|
"""Tests for 360° configuration."""
|
|
|
|
def test_default_360_settings(self):
|
|
"""Test default 360° configuration values."""
|
|
config = ProcessorConfig()
|
|
|
|
assert config.enable_360_processing == HAS_360_SUPPORT
|
|
assert config.auto_detect_360 is True
|
|
assert config.force_360_projection is None
|
|
assert config.video_360_bitrate_multiplier == 2.5
|
|
assert config.generate_360_thumbnails is True
|
|
assert "front" in config.thumbnail_360_projections
|
|
assert "stereographic" in config.thumbnail_360_projections
|
|
|
|
def test_360_validation_without_dependencies(self):
|
|
"""Test that 360° processing can't be enabled without dependencies."""
|
|
if not HAS_360_SUPPORT:
|
|
with pytest.raises(
|
|
ValueError, match="360° processing requires optional dependencies"
|
|
):
|
|
ProcessorConfig(enable_360_processing=True)
|
|
|
|
@pytest.mark.skipif(not HAS_360_SUPPORT, reason="360° dependencies not available")
|
|
def test_360_validation_with_dependencies(self):
|
|
"""Test that 360° processing can be enabled with dependencies."""
|
|
config = ProcessorConfig(enable_360_processing=True)
|
|
assert config.enable_360_processing is True
|
|
|
|
def test_bitrate_multiplier_validation(self):
|
|
"""Test bitrate multiplier validation."""
|
|
# Valid range
|
|
config = ProcessorConfig(video_360_bitrate_multiplier=3.0)
|
|
assert config.video_360_bitrate_multiplier == 3.0
|
|
|
|
# Invalid range should raise validation error
|
|
with pytest.raises(ValueError):
|
|
ProcessorConfig(video_360_bitrate_multiplier=0.5) # Below minimum
|
|
|
|
with pytest.raises(ValueError):
|
|
ProcessorConfig(video_360_bitrate_multiplier=6.0) # Above maximum
|
|
|
|
def test_custom_360_settings(self):
|
|
"""Test custom 360° configuration."""
|
|
config = ProcessorConfig(
|
|
auto_detect_360=False,
|
|
video_360_bitrate_multiplier=2.0,
|
|
generate_360_thumbnails=False,
|
|
thumbnail_360_projections=["front", "back"],
|
|
)
|
|
|
|
assert config.auto_detect_360 is False
|
|
assert config.video_360_bitrate_multiplier == 2.0
|
|
assert config.generate_360_thumbnails is False
|
|
assert config.thumbnail_360_projections == ["front", "back"]
|
|
|
|
|
|
# Integration test for basic video processor
|
|
class TestVideoProcessor360Integration:
|
|
"""Integration tests for 360° video processing."""
|
|
|
|
def test_processor_creation_without_360_support(self):
|
|
"""Test that video processor works without 360° support."""
|
|
from video_processor import VideoProcessor
|
|
|
|
config = ProcessorConfig() # 360° disabled by default when deps missing
|
|
processor = VideoProcessor(config)
|
|
|
|
assert processor.thumbnail_360_generator is None
|
|
|
|
@pytest.mark.skipif(not HAS_360_SUPPORT, reason="360° dependencies not available")
|
|
def test_processor_creation_with_360_support(self):
|
|
"""Test that video processor works with 360° support."""
|
|
from video_processor import VideoProcessor
|
|
|
|
config = ProcessorConfig(enable_360_processing=True)
|
|
processor = VideoProcessor(config)
|
|
|
|
assert processor.thumbnail_360_generator is not None
|