windows c++에서 rawvideo를 ffmpeg.exe를 이용 pipe로 압축하기 질문입니다.

알림
|
X

페이지 정보

작성자 no_profile 너랑나랑 182.♡.69.150
작성일 2024.07.04 19:45
297 조회
0 추천
글쓰기

본문

다음과 같이 bgr24로 된 rawvideo를 ffmpeg.exe에 파이프 출력하여 압축을 진행했습니다.

//==============================================================

const char* ffmpeg_write_cmd = "ffmpeg -y -f rawvideo -pix_fmt bgr24 -s 1280x720 -r 24 -i - -c:v libx264 -b:v 3000k output.mp4";

FILE *fp_write;
fp_write = _popen(cmd_write, "wb");
if(!fp_write)
    return -1;

// 비디오 프레임의 크기 설정
int width = 1280;
int height = 720;
int frame_size = width * height * 3; // BGR 포맷 (3채널)

cv::Mat frame(height, width, CV_8UC3);
std::vector<uchar> buffer(frame_size);

while (true) {

    //
    //buffer는 어디에선가 가져욤

    // 버퍼를 OpenCV Mat 객체로 복사
    std::memcpy(frame.data, buffer.data(), frame_size);

    // 프레임을 화면에 표시
    cv::imshow("Frame", frame);
    if (cv::waitKey(10) == 'q') {
        break;
    }

    // 프레임 데이터를 FFmpeg로 쓰기
    fwrite(buffer.data(), 1, size_write, fp_write);

}

_pclose(fp_write);
cv::destroyAllWindows();

return 0;

//==================================================================


그런데 _popen을 이용하면 ffmpeg.exe 실행될 때  콘솔창이 나오기 때문에 콘솔창을 없애려고 알아보니

CreatePipe와 CreateProcess를 이용해야 한다고 하더군요. 그래서 이리저리 삽질끝에 아래와 같이 수정하였습니다.

//-------------------------------------------------------------------------------------------------

const char* ffmpeg_write_cmd = "ffmpeg -y -f rawvideo -pix_fmt bgr24 -s 1280x720 -r 24 -i - -c:v libx264 -b:v 3000k output.mp4";

// 파이프 생성 (FFmpeg로 비디오 데이터 쓰기)

HANDLE hWritePipeRead, hWritePipeWrite;

if (!CreatePipe(&hWritePipeRead, &hWritePipeWrite, &sa, 0)) {

    std::cerr << "Error: Could not create write pipe." << std::endl;

    return -1;
}

// FFmpeg 쓰기 프로세스 설정
STARTUPINFO si_write = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi_write;
si_write.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si_write.wShowWindow = SW_HIDE; // 콘솔 창 숨기기
si_write.hStdInput = hWritePipeRead;
// FFmpeg 쓰기 프로세스 시작
if (!CreateProcess(NULL, (LPSTR)ffmpeg_write_cmd, NULL, NULL, TRUE, 0, NULL, NULL, &si_write, &pi_write)) {
    std::cerr << "Error: Could not start FFmpeg write process." << std::endl;
    CloseHandle(hWritePipeRead);
    CloseHandle(hWritePipeWrite);
    return -1;
}
// 쓰기 파이프 읽기 핸들 닫기 (쓰기 파이프만 사용)
CloseHandle(hWritePipeRead);
// 비디오 프레임의 크기 설정
int width = 1280;
int height = 720;
int frame_size = width * height * 3; // BGR 포맷 (3채널)
cv::Mat frame(height, width, CV_8UC3);
std::vector<uchar> buffer(frame_size);
while (true) {
    //
    //buffer는 어디에선가 가져욤

    // 버퍼를 OpenCV Mat 객체로 복사
    std::memcpy(frame.data, buffer.data(), frame_size);

    // 프레임을 화면에 표시
    cv::imshow("Frame", frame);

    if (cv::waitKey(10) == 'q') {
        break;
    }
    // 프레임 데이터를 FFmpeg로 쓰기
    DWORD bytesWritten;
    if (!WriteFile(hWritePipeWrite, buffer.data(), frame_size, &bytesWritten, NULL) || bytesWritten != frame_size) {
        std::cerr << "Error: Could not write frame to pipe." << std::endl;
        break;
    }
    FlushFileBuffers(hWritePipeWrite);
}
// 파이프 및 프로세스 핸들 닫기
CloseHandle(hWritePipeWrite); // close를 하면 ffmpeg이 종료되어야 하는데 종료되지 않음
// FFmpeg 쓰기 프로세스 종료

//TerminateProcess(pi_write.hProcess, 0);

WaitForSingleObject(pi_write.hProcess, INFINITE); // 강제로 종료하지 않으면 무한대기, 강제 종료하면 output.mp4가 정상종료되지 않아 플레이어에서 읽을 수 없음
CloseHandle(pi_write.hProcess);
CloseHandle(pi_write.hThread);
cv::destroyAllWindows();
return 0;

//-----------------------------------------------------------------------------------------


문제는 위 코드의 코멘트와 같이 CloseHandle을 하면 ffmpeg이 쓰기를 종료해야 하는데 그렇지 않고 프로세스가 계속 살아있어서 저장이 완료가 되지 않습니다.

그렇다고 강제로 종료시켜도 마찮가지로 저장이 완료가 되지 않구요

_popen, _pclose를 사용하면 결과가 잘 인코딩되어서 저장됩니다.

문제가 뭘까요?

  • 게시물이 없습니다.
댓글 1 / 1 페이지

BLUEnLIVE님의 댓글

작성자 BLUEnLIVE (124.♡.137.94)
작성일 07.04 19:58
ffmpeg을 파이프로 연결하는 건 앞쪽 ffmpeg(또는 비디오를 보내주는 쪽)의 EOF를 뒤쪽 ffmpeg(인코딩 하는 쪽)에서 인식할 방법이 없습니다.
필터 권한 문제니 뭐니 하는 글들 많아서 다 해봤지만 제 결론은 그냥 파일의 끝을 찾는 건 불가능하다는 것이었습니다.
TCP 소켓을 만들어 내부적으로 전송하는 테크닉이 있긴 합니다.
ffmpeg 끼리 할 때는 TCP 소켓으로 전송하니 이건 잘 인식이 됐었습니다.
글쓰기
홈으로 전체메뉴 마이메뉴 새글/새댓글
전체 검색