272 lines
8.2 KiB
Python
272 lines
8.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Example Usage: Quality Checker Module
|
|
======================================
|
|
Demonstrates how to use the quality_checker module to analyze video quality
|
|
and detect potential quality degradation before encoding.
|
|
"""
|
|
|
|
import logging
|
|
import sys
|
|
from pathlib import Path
|
|
from quality_checker import QualityChecker
|
|
|
|
# Setup logging
|
|
logging.basicConfig(
|
|
level=logging.INFO,
|
|
format='%(asctime)s - %(levelname)s - %(message)s'
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def example_analyze_quality(video_path: Path):
|
|
"""Example: Analyze video quality"""
|
|
print("\n" + "="*80)
|
|
print("EXAMPLE 1: Analyze Video Quality")
|
|
print("="*80)
|
|
|
|
checker = QualityChecker(logger)
|
|
quality = checker.analyze_quality(video_path)
|
|
|
|
if quality:
|
|
print(f"\nVideo Quality Analysis for: {video_path.name}")
|
|
print("-" * 80)
|
|
print(f" Resolution: {quality.resolution[0]}x{quality.resolution[1]}")
|
|
print(f" Bitrate: {quality.bitrate/1000000:.2f} Mbps")
|
|
print(f" Codec: {quality.codec}")
|
|
print(f" FPS: {quality.fps:.2f}")
|
|
print(f" HDR: {'Yes' if quality.is_hdr else 'No'}")
|
|
print(f" Quality Score: {quality.quality_score}/100")
|
|
print()
|
|
|
|
# Quality interpretation
|
|
if quality.quality_score >= 95:
|
|
print(" Rating: ⭐⭐⭐⭐⭐ Near-lossless / Exceptional")
|
|
elif quality.quality_score >= 85:
|
|
print(" Rating: ⭐⭐⭐⭐ Excellent / Archival quality")
|
|
elif quality.quality_score >= 70:
|
|
print(" Rating: ⭐⭐⭐ Good / Transparent quality")
|
|
elif quality.quality_score >= 50:
|
|
print(" Rating: ⭐⭐ Acceptable / Visible compression")
|
|
else:
|
|
print(" Rating: ⭐ Poor / Heavy compression artifacts")
|
|
else:
|
|
print(f"❌ Failed to analyze {video_path}")
|
|
|
|
|
|
def example_check_degradation(video_path: Path):
|
|
"""Example: Check if encoding will degrade quality"""
|
|
print("\n" + "="*80)
|
|
print("EXAMPLE 2: Check Quality Degradation")
|
|
print("="*80)
|
|
|
|
checker = QualityChecker(logger)
|
|
quality = checker.analyze_quality(video_path)
|
|
|
|
if not quality:
|
|
print(f"❌ Failed to analyze {video_path}")
|
|
return
|
|
|
|
# Example encoding profiles
|
|
profiles = [
|
|
{
|
|
'name': 'Sweet Spot (CRF 21)',
|
|
'encoder': 'nvidia_nvenc_h265',
|
|
'quality': 21,
|
|
'bitrate': None # Will be estimated
|
|
},
|
|
{
|
|
'name': 'Balanced (CRF 23)',
|
|
'encoder': 'nvidia_nvenc_h265',
|
|
'quality': 23,
|
|
'bitrate': None
|
|
},
|
|
{
|
|
'name': 'Fast (CRF 26)',
|
|
'encoder': 'nvidia_nvenc_h264',
|
|
'quality': 26,
|
|
'bitrate': None
|
|
}
|
|
]
|
|
|
|
print(f"\nQuality Degradation Check for: {video_path.name}")
|
|
print(f"Source Quality Score: {quality.quality_score}/100")
|
|
print("-" * 80)
|
|
|
|
for profile in profiles:
|
|
# Estimate target bitrate based on profile
|
|
target_bitrate = checker.estimate_target_bitrate(
|
|
profile,
|
|
quality.resolution,
|
|
quality.fps
|
|
)
|
|
|
|
# Check if encoding will degrade quality
|
|
will_degrade, reason = checker.will_degrade_quality(
|
|
quality,
|
|
target_bitrate,
|
|
profile['encoder'],
|
|
threshold=10.0
|
|
)
|
|
|
|
target_score = checker._calculate_quality_score(
|
|
target_bitrate,
|
|
quality.resolution[0],
|
|
quality.resolution[1],
|
|
profile['encoder'],
|
|
quality.fps
|
|
)
|
|
|
|
status = "⚠️ WARNING" if will_degrade else "✅ OK"
|
|
print(f"\n{status} - {profile['name']}")
|
|
print(f" Target Bitrate: {target_bitrate/1000000:.2f} Mbps")
|
|
print(f" Target Score: {target_score:.1f}/100")
|
|
print(f" Quality Drop: {quality.quality_score - target_score:.1f} points")
|
|
|
|
if will_degrade:
|
|
print(f" {reason}")
|
|
|
|
|
|
def example_comprehensive_check(video_path: Path):
|
|
"""Example: Comprehensive pre-encode quality check"""
|
|
print("\n" + "="*80)
|
|
print("EXAMPLE 3: Comprehensive Pre-Encode Check")
|
|
print("="*80)
|
|
|
|
checker = QualityChecker(logger)
|
|
|
|
# Define encoding profile
|
|
profile = {
|
|
'encoder': 'nvidia_nvenc_h265',
|
|
'quality': 21, # CRF value
|
|
'hdr_handling': 'preserve'
|
|
}
|
|
|
|
# Perform comprehensive check
|
|
result = checker.check_before_encode(
|
|
video_path,
|
|
profile,
|
|
warn_threshold=10.0,
|
|
error_threshold=20.0
|
|
)
|
|
|
|
print(f"\nPre-Encode Quality Check for: {video_path.name}")
|
|
print("-" * 80)
|
|
|
|
if result['source_quality']:
|
|
q = result['source_quality']
|
|
print(f"Source Quality:")
|
|
print(f" Resolution: {q.resolution[0]}x{q.resolution[1]}")
|
|
print(f" Bitrate: {q.bitrate/1000000:.2f} Mbps")
|
|
print(f" Quality Score: {q.quality_score}/100")
|
|
print(f" HDR: {'Yes' if q.is_hdr else 'No'}")
|
|
print()
|
|
|
|
print(f"Target Settings:")
|
|
print(f" Encoder: {profile['encoder']}")
|
|
print(f" CRF: {profile['quality']}")
|
|
print(f" Est. Bitrate: {result['estimated_target_bitrate']/1000000:.2f} Mbps")
|
|
print()
|
|
|
|
if result['ok']:
|
|
print("✅ Status: OK - Encoding can proceed")
|
|
elif result['error']:
|
|
print("❌ Status: ERROR - Quality degradation too severe")
|
|
print(f" Quality drop: {result['quality_drop']:.1f} points")
|
|
elif result['warning']:
|
|
print("⚠️ Status: WARNING - Quality may be degraded")
|
|
print(f" Quality drop: {result['quality_drop']:.1f} points")
|
|
|
|
if result['message']:
|
|
print(f"\n{result['message']}")
|
|
|
|
|
|
def example_batch_analysis(directory: Path):
|
|
"""Example: Batch analyze multiple videos"""
|
|
print("\n" + "="*80)
|
|
print("EXAMPLE 4: Batch Video Analysis")
|
|
print("="*80)
|
|
|
|
checker = QualityChecker(logger)
|
|
|
|
# Find all video files
|
|
video_extensions = {'.mkv', '.mp4', '.avi', '.m4v', '.ts', '.m2ts'}
|
|
video_files = [
|
|
f for f in directory.rglob('*')
|
|
if f.suffix.lower() in video_extensions
|
|
]
|
|
|
|
if not video_files:
|
|
print(f"No video files found in {directory}")
|
|
return
|
|
|
|
print(f"\nAnalyzing {len(video_files)} video files...")
|
|
print("-" * 80)
|
|
|
|
results = []
|
|
for video_file in video_files[:10]: # Limit to first 10 for demo
|
|
quality = checker.analyze_quality(video_file)
|
|
if quality:
|
|
results.append({
|
|
'file': video_file.name,
|
|
'quality': quality
|
|
})
|
|
|
|
# Sort by quality score
|
|
results.sort(key=lambda x: x['quality'].quality_score, reverse=True)
|
|
|
|
print(f"\n{'File':<40} {'Resolution':<12} {'Bitrate':>10} {'Score':>6}")
|
|
print("-" * 80)
|
|
|
|
for r in results:
|
|
q = r['quality']
|
|
print(
|
|
f"{r['file'][:40]:<40} "
|
|
f"{q.resolution[0]}x{q.resolution[1]:<12} "
|
|
f"{q.bitrate/1000000:>8.1f} M "
|
|
f"{q.quality_score:>6.1f}"
|
|
)
|
|
|
|
# Statistics
|
|
if results:
|
|
avg_score = sum(r['quality'].quality_score for r in results) / len(results)
|
|
avg_bitrate = sum(r['quality'].bitrate for r in results) / len(results)
|
|
|
|
print("-" * 80)
|
|
print(f"{'Average':<40} {'':<12} {avg_bitrate/1000000:>8.1f} M {avg_score:>6.1f}")
|
|
|
|
|
|
def main():
|
|
if len(sys.argv) < 2:
|
|
print("Usage: python3 example_quality_check.py <video_file_or_directory>")
|
|
print()
|
|
print("Examples:")
|
|
print(" # Analyze single video")
|
|
print(" python3 example_quality_check.py /movies/example.mkv")
|
|
print()
|
|
print(" # Batch analyze directory")
|
|
print(" python3 example_quality_check.py /movies/")
|
|
sys.exit(1)
|
|
|
|
path = Path(sys.argv[1])
|
|
|
|
if not path.exists():
|
|
print(f"❌ Error: {path} does not exist")
|
|
sys.exit(1)
|
|
|
|
if path.is_file():
|
|
# Single file analysis
|
|
example_analyze_quality(path)
|
|
example_check_degradation(path)
|
|
example_comprehensive_check(path)
|
|
elif path.is_dir():
|
|
# Batch analysis
|
|
example_batch_analysis(path)
|
|
else:
|
|
print(f"❌ Error: {path} is neither a file nor directory")
|
|
sys.exit(1)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|