#include "pch.h" #include "HeadlessDecoder.h" #include #include namespace Vav2Player { HeadlessDecoder::HeadlessDecoder() = default; HeadlessDecoder::~HeadlessDecoder() = default; bool HeadlessDecoder::ProcessFile(const std::string& input_file_path) { try { m_input_file_path = input_file_path; m_start_time = std::chrono::high_resolution_clock::now(); std::cout << "=== Vav2Player Headless Mode ===" << std::endl; std::cout << "Input file: " << input_file_path << std::endl; std::cout << std::endl; // 1. 컴포넌트 초기화 std::cout << "Step 1: Initializing components..." << std::endl; if (!InitializeComponents()) { std::cerr << "Error: Failed to initialize components" << std::endl; return false; } std::cout << "Components initialized successfully" << std::endl; // 2. WebM 파일 열기 std::cout << "Step 2: Opening WebM file..." << std::endl; if (!OpenWebMFile(input_file_path)) { std::cerr << "Error: Failed to open WebM file" << std::endl; return false; } // 3. 디코더 초기화 std::cout << "Step 3: Initializing decoder..." << std::endl; if (!InitializeDecoder()) { std::cerr << "Error: Failed to initialize decoder" << std::endl; return false; } // 4. 출력 초기화 std::cout << "Step 4: Initializing output..." << std::endl; if (!InitializeOutput()) { std::cerr << "Error: Failed to initialize output" << std::endl; return false; } // 5. 모든 프레임 처리 std::cout << "Step 5: Starting frame processing..." << std::endl; if (!ProcessAllFrames()) { std::cerr << "Error: Frame processing failed" << std::endl; return false; } // 6. 요약 출력 PrintSummary(); return true; } catch (const std::exception& e) { std::cerr << "*** EXCEPTION in ProcessFile: " << e.what() << std::endl; return false; } catch (...) { std::cerr << "*** UNKNOWN EXCEPTION in ProcessFile" << std::endl; return false; } } bool HeadlessDecoder::InitializeComponents() { try { std::cout << " Creating WebMFileReader..." << std::endl; std::cout.flush(); m_file_reader = std::make_unique(); std::cout << " WebMFileReader created successfully" << std::endl; std::cout << " Creating FileOutput..." << std::endl; std::cout.flush(); m_file_output = std::make_unique(); std::cout << " FileOutput created successfully" << std::endl; return true; } catch (const std::exception& e) { std::cerr << "Exception during component initialization: " << e.what() << std::endl; return false; } catch (...) { std::cerr << "Unknown exception during component initialization" << std::endl; return false; } } bool HeadlessDecoder::OpenWebMFile(const std::string& file_path) { std::cout << " Attempting to open file: " << file_path << std::endl; std::cout.flush(); if (!m_file_reader->OpenFile(file_path)) { auto error_code = m_file_reader->GetLastError(); std::string error_msg = m_file_reader->GetLastErrorString(); std::cerr << "Failed to open WebM file: " << error_msg << std::endl; std::cerr << "Error code: " << static_cast(error_code) << std::endl; // 디버깅: 발견된 트랙 정보 출력 auto tracks = m_file_reader->GetVideoTracks(); if (!tracks.empty()) { std::cout << "Found " << tracks.size() << " video track(s):" << std::endl; for (const auto& track : tracks) { std::cout << " Track #" << track.track_number << ": " << track.codec_name << " (" << track.codec_id << ")" << " " << track.width << "x" << track.height << std::endl; } } return false; } // 메타데이터 얻기 m_metadata = m_file_reader->GetVideoMetadata(); std::cout << "WebM file opened successfully:" << std::endl; std::cout << " Resolution: " << m_metadata.width << "x" << m_metadata.height << std::endl; std::cout << " Codec: " << m_metadata.codec_name << std::endl; std::cout << " Frame rate: " << m_metadata.frame_rate << " fps" << std::endl; std::cout << " Total frames: " << m_metadata.total_frames << std::endl; std::cout << " Duration: " << std::fixed << std::setprecision(2) << m_metadata.duration_seconds << " seconds" << std::endl; std::cout << std::endl; return true; } bool HeadlessDecoder::InitializeDecoder() { std::cout << " Creating decoder for codec: " << m_metadata.codec_name << " (type: " << static_cast(m_metadata.codec_type) << ")" << std::endl; std::cout.flush(); m_decoder = VideoDecoderFactory::CreateDecoder(m_metadata.codec_type); if (!m_decoder) { std::cerr << "Failed to create decoder for codec: " << m_metadata.codec_name << std::endl; std::cerr << "Codec type: " << static_cast(m_metadata.codec_type) << std::endl; return false; } std::cout << " Decoder created successfully" << std::endl; std::cout << " Initializing decoder with metadata..." << std::endl; std::cout.flush(); if (!m_decoder->Initialize(m_metadata)) { std::cerr << "Failed to initialize decoder" << std::endl; return false; } std::cout << "Decoder initialized successfully" << std::endl; return true; } bool HeadlessDecoder::InitializeOutput() { std::cout << " Setting up output configuration..." << std::endl; std::cout.flush(); // 출력 디렉토리 설정 FileOutput::OutputConfig config; config.format = FileOutput::OutputFormat::BMP; config.output_directory = "output_frames"; config.filename_prefix = "frame"; config.create_subdirectories = true; config.overwrite_existing = true; std::cout << " Applying output config..." << std::endl; std::cout.flush(); m_file_output->SetConfig(config); std::cout << " Creating output directory: " << config.output_directory << std::endl; std::cout.flush(); if (!m_file_output->CreateOutputDirectory()) { std::cerr << "Failed to create output directory" << std::endl; return false; } std::cout << "Output configured: " << config.output_directory << "/" << config.filename_prefix << "_XXXXX.bmp" << std::endl; std::cout << std::endl; return true; } bool HeadlessDecoder::ProcessAllFrames() { m_processed_frames = 0; m_successful_frames = 0; m_failed_frames = 0; uint64_t total_frames = m_metadata.total_frames; if (total_frames == 0) total_frames = 1000; // 알 수 없는 경우 추정값 std::cout << " Expected total frames: " << total_frames << std::endl; std::cout << " Starting frame processing loop..." << std::endl; std::cout.flush(); VideoPacket packet; VideoFrame frame; std::cout << " Reading first packet..." << std::endl; std::cout.flush(); while (m_file_reader->ReadNextPacket(packet)) { m_processed_frames++; if (m_processed_frames <= 5) { // 처음 5프레임만 상세 로그 std::cout << " Processing frame #" << m_processed_frames << std::endl; std::cout.flush(); } // 디코딩 시도 if (m_decoder->DecodeFrame(packet, frame)) { if (m_processed_frames <= 5) { std::cout << " Decoding successful, saving frame..." << std::endl; std::cout.flush(); } // 프레임 저장 auto save_result = m_file_output->SaveFrame(frame, m_processed_frames); if (save_result.success) { m_successful_frames++; if (m_processed_frames <= 5) { std::cout << " Frame saved successfully" << std::endl; } if (m_verbose && m_successful_frames % 10 == 0) { PrintProgress(m_processed_frames, total_frames); } } else { m_failed_frames++; std::cerr << "Failed to save frame #" << m_processed_frames << ": " << save_result.error_message << std::endl; } } else { m_failed_frames++; if (m_processed_frames <= 5 || m_verbose) { std::cerr << "Failed to decode frame #" << m_processed_frames << std::endl; } } // 진행률 출력 (매 30프레임마다) if (m_processed_frames % 30 == 0) { PrintProgress(m_processed_frames, total_frames); } // 처음 몇 프레임만 처리하고 테스트 (디버그용) if (m_processed_frames >= 10) { std::cout << " Stopping after 10 frames for debug..." << std::endl; break; } } std::cout << " Frame processing completed" << std::endl; std::cout << " Total read attempts: " << m_processed_frames << std::endl; std::cout << " Successful decodes: " << m_successful_frames << std::endl; std::cout << " Failed decodes: " << m_failed_frames << std::endl; return m_successful_frames > 0; } void HeadlessDecoder::PrintProgress(uint64_t current_frame, uint64_t total_frames) { double percentage = 0.0; if (total_frames > 0) { percentage = (double)current_frame / total_frames * 100.0; } std::cout << "\rProgress: " << current_frame << "/" << total_frames << " (" << std::fixed << std::setprecision(1) << percentage << "%) " << "Success: " << m_successful_frames << " Failed: " << m_failed_frames << std::flush; } void HeadlessDecoder::PrintSummary() { auto end_time = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration_cast(end_time - m_start_time); std::cout << std::endl << std::endl; std::cout << "=== Processing Summary ===" << std::endl; std::cout << "Total frames processed: " << m_processed_frames << std::endl; std::cout << "Successfully decoded: " << m_successful_frames << std::endl; std::cout << "Failed to decode: " << m_failed_frames << std::endl; std::cout << "Processing time: " << duration.count() << " ms" << std::endl; if (duration.count() > 0 && m_successful_frames > 0) { double fps = (double)m_successful_frames / duration.count() * 1000.0; std::cout << "Average FPS: " << std::fixed << std::setprecision(2) << fps << std::endl; } if (m_successful_frames > 0) { std::cout << "Output files saved to: output_frames/" << std::endl; std::cout << "Success rate: " << std::fixed << std::setprecision(1) << (double)m_successful_frames / m_processed_frames * 100.0 << "%" << std::endl; } std::cout << std::endl; if (m_successful_frames == 0) { std::cout << "No frames were successfully processed!" << std::endl; } else { std::cout << "Processing completed successfully!" << std::endl; } } } // namespace Vav2Player