Notice
Link
- Today
- Total
Recent Posts
Recent Comments
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- MySQL
- 코드로 서버 재실행
- springboot 재가동
- CSS
- Activity 전체화면
- CentOS
- MariaDB
- view 획득
- DB 계정생성
- 스크롤적용
- 가변영역 스크롤
- 파티션 빠른 삭제
- rn
- mybatis exception
- SQL 마지막날
- 텍스트컬러
- kill -9
- pid 찾아 kill
- SQL 첫날
- ffmpeg
- 터치좌표 view
- Back 키 클릭 감지
- 말줌임 CSS
- vc++
- MFC
- springboot
- reactnative
- sql exception
- c언어
- 피쉬랜드
Archives
개발은 하는건가..
[VC++] 화면을 동영상으로 저장하기 본문
반응형
별도로 생성된 스레드에서 대상 윈도우의 DC 로부터 비트맵을 복제 후 ffmpeg 으로 인코딩 하여 파일로 저장한다.
const double frameInterval = 1000.0 / 30;
int calcInterval = 0;
BYTE *pImageData = NULL;
HDC hdcMemory = NULL;
BITMAPINFO *pDibBitmapInfo = &(m_DibBitmapInfo);
HBITMAP hDibBitmap = NULL, hOldBitmap = NULL;;
LONGLONG nFrameCnt = 0;
LONGLONG nStartTs = 0;
DWORD dwSleep = 0;
int startX = pThis->m_TargetRect.left;
int startY = pThis->m_TargetRect.top;
int width = pThis->m_TargetRect.Width();
int height = pThis->m_TargetRect.Height();
nStartTs = GetTickCount64();
while (m_bIsRecording == TRUE) {
HDC hdcScreen = ::GetDC(m_hTargetWnd);
if (hdcScreen != NULL) {
if (pImageData == NULL) {
hDibBitmap = ::CreateDIBSection(hdcScreen, pDibBitmapInfo, DIB_RGB_COLORS, (void **)&pImageData, 0, 0);
hdcMemory = ::CreateCompatibleDC(hdcScreen);
hOldBitmap = (HBITMAP)::SelectObject(hdcMemory, hDibBitmap);
}
::BitBlt(hdcMemory, 0, 0, width, height, hdcScreen, startX, startY, SRCCOPY);
::ReleaseDC(pThis->m_hTargetWnd, hdcScreen);
m_videoEncoder.Write(pImageData);
// Fps 를 맞추기 위해 지연시간을 조절.
calcInterval = (int)((GetTickCount64() - nStartTs) - (nFrameCnt * frameInterval));
dwSleep = (DWORD)(max(0, frameInterval - calcInterval));
Sleep(dwSleep);
nFrameCnt++;
}
else {
Sleep(1);
}
}
VideoEncoder 클래스 구현체
CVideoEncoder::~CVideoEncoder()
{
Close();
}
BOOL CVideoEncoder::Open(const char *filename, const Params ¶ms)
{
Close();
do
{
avformat_alloc_output_context2(&m_Context.format_context, nullptr, nullptr, filename);
if (!m_Context.format_context)
{
std::cout << "could not allocate output format" << std::endl;
break;
}
m_Context.codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!m_Context.codec)
{
std::cout << "could not find encoder" << std::endl;
break;
}
m_Context.stream = avformat_new_stream(m_Context.format_context, nullptr);
if (!m_Context.stream)
{
std::cout << "could not create stream" << std::endl;
break;
}
m_Context.stream->id = (int)(m_Context.format_context->nb_streams - 1);
m_Context.codec_context = avcodec_alloc_context3(m_Context.codec);
if (!m_Context.codec_context)
{
std::cout << "could not allocate mContext codec context" << std::endl;
break;
}
m_Context.codec_context->codec_id = m_Context.format_context->oformat->video_codec;
m_Context.codec_context->bit_rate = params.bitrate;
m_Context.codec_context->width = static_cast<int>(params.width);
m_Context.codec_context->height = static_cast<int>(params.height);
m_Context.stream->time_base = av_d2q(1.0 / params.fps, 120);
m_Context.codec_context->time_base = m_Context.stream->time_base;
m_Context.codec_context->pix_fmt = params.dst_format;
m_Context.codec_context->gop_size = (int)params.fps;
m_Context.codec_context->max_b_frames = 2;
if (m_Context.format_context->oformat->flags & AVFMT_GLOBALHEADER)
m_Context.codec_context->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
int ret = 0;
if (params.preset)
{
ret = av_opt_set(m_Context.codec_context->priv_data, "preset", params.preset, 0);
if (ret != 0)
{
std::cout << "could not set preset: " << params.preset << std::endl;
break;
}
}
{
ret = av_opt_set_int(m_Context.codec_context->priv_data, "crf", params.crf, 0);
if (ret != 0)
{
std::cout << "could not set crf: " << params.crf << std::endl;
break;
}
}
ret = avcodec_open2(m_Context.codec_context, m_Context.codec, nullptr);
if (ret != 0)
{
std::cout << "could not open codec: " << ret << std::endl;
break;
}
m_Context.frame = av_frame_alloc();
if (!m_Context.frame)
{
std::cout << "could not allocate mContext frame" << std::endl;
break;
}
m_Context.frame->format = m_Context.codec_context->pix_fmt;
m_Context.frame->width = m_Context.codec_context->width;
m_Context.frame->height = m_Context.codec_context->height;
ret = av_frame_get_buffer(m_Context.frame, 32);
if (ret < 0)
{
std::cout << "could not allocate the mContext frame data" << std::endl;
break;
}
ret = avcodec_parameters_from_context(m_Context.stream->codecpar, m_Context.codec_context);
if (ret < 0)
{
std::cout << "could not copy the stream parameters" << std::endl;
break;
}
m_Context.sws_context = sws_getContext(
m_Context.codec_context->width, m_Context.codec_context->height, params.src_format, // src
m_Context.codec_context->width, m_Context.codec_context->height, params.dst_format, // dst
SWS_BICUBIC, nullptr, nullptr, nullptr
);
if (!m_Context.sws_context)
{
std::cout << "could not initialize the conversion context" << std::endl;
break;
}
av_dump_format(m_Context.format_context, 0, filename, 1);
ret = avio_open(&m_Context.format_context->pb, filename, AVIO_FLAG_WRITE);
if (ret != 0)
{
std::cout << "could not open " << filename << std::endl;
break;
}
ret = avformat_write_header(m_Context.format_context, nullptr);
if (ret < 0)
{
std::cout << "could not write" << std::endl;
ret = avio_close(m_Context.format_context->pb);
if (ret != 0)
std::cout << "failed to close file" << std::endl;
break;
}
m_Context.frame_index = 0;
m_isOpen = TRUE;
m_Context.pFrameBuffer = (BYTE*)malloc(params.width * params.height * (params.bpp / 8));
m_Context.nFrameBpp = params.bpp;
return TRUE;
} while (FALSE);
Close();
return FALSE;
}
void CVideoEncoder::Close()
{
if (m_isOpen)
{
avcodec_send_frame(m_Context.codec_context, nullptr);
FlushPackets();
av_write_trailer(m_Context.format_context);
auto ret = avio_close(m_Context.format_context->pb);
if (ret != 0)
std::cout << "failed to close file" << std::endl;
}
if (m_Context.sws_context)
sws_freeContext(m_Context.sws_context);
if (m_Context.frame)
av_frame_free(&m_Context.frame);
if (m_Context.codec_context)
avcodec_free_context(&m_Context.codec_context);
if (m_Context.codec_context)
avcodec_close(m_Context.codec_context);
if (m_Context.format_context)
avformat_free_context(m_Context.format_context);
if (m_Context.pFrameBuffer != NULL) {
free(m_Context.pFrameBuffer);
}
m_Context = {};
m_isOpen = FALSE;
}
BOOL CVideoEncoder::WriteUseFrameBuffer()
{
if (m_Context.pFrameBuffer != NULL) {
return Write(m_Context.pFrameBuffer);
}
return FALSE;
}
BOOL CVideoEncoder::Write(const unsigned char *data)
{
if (!m_isOpen)
return FALSE;
auto ret = av_frame_make_writable(m_Context.frame);
if (ret < 0)
{
std::cout << "frame not writable" << std::endl;
return FALSE;
}
const int in_linesize[1] = { (int)(m_Context.codec_context->width * (m_Context.nFrameBpp >> 3)) };
sws_scale(m_Context.sws_context,
&data, in_linesize, 0, m_Context.codec_context->height, // src
m_Context.frame->data, m_Context.frame->linesize // dst
);
m_Context.frame->pts = m_Context.frame_index++;
ret = avcodec_send_frame(m_Context.codec_context, m_Context.frame);
if (ret < 0){
std::cout << "error sending a frame for encoding" << std::endl;
return FALSE;
}
return FlushPackets();
}
BOOL CVideoEncoder::IsOpen()
{
return m_isOpen;
}
BOOL CVideoEncoder::FlushPackets()
{
int ret;
do
{
AVPacket packet = { 0 };
ret = avcodec_receive_packet(m_Context.codec_context, &packet);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
if (ret < 0)
{
std::cout << "error encoding a frame: " << ret << std::endl;
return FALSE;
}
av_packet_rescale_ts(&packet, m_Context.codec_context->time_base, m_Context.stream->time_base);
packet.stream_index = m_Context.stream->index;
ret = av_interleaved_write_frame(m_Context.format_context, &packet);
av_packet_unref(&packet);
if (ret < 0)
{
std::cout << "error while writing output packet: " << ret << std::endl;
return FALSE;
}
} while (ret >= 0);
return TRUE;
}
'C, C++, MFC' 카테고리의 다른 글
특정 좌표의 원점 기준 각도 계산 관련 (0) | 2022.10.25 |
---|---|
[MFC] 오른쪽 버튼 메뉴 띄우기 (custom contextmenu) (1) | 2022.09.14 |
MFC UDP Socket Base class (0) | 2022.07.19 |
CListCtrl 선택된 Item 인덱스 (0) | 2022.07.19 |
[MFC] DC 에 동영상 출력 시 가장 빠르게 출력 하기 위한 방법 (0) | 2022.04.08 |
Comments