-
-
Save Arnold1/0369ce50f72b74a52bdf84d7534af242 to your computer and use it in GitHub Desktop.
Convert from OpenCV image and write movie with FFmpeg
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Convert from OpenCV image and write movie with FFmpeg | |
* | |
* Copyright (c) 2016 yohhoy | |
*/ | |
#include <iostream> | |
#include <vector> | |
// FFmpeg | |
extern "C" { | |
#include <libavformat/avformat.h> | |
#include <libavcodec/avcodec.h> | |
#include <libavutil/avutil.h> | |
#include <libavutil/pixdesc.h> | |
#include <libswscale/swscale.h> | |
} | |
// OpenCV | |
#include <opencv2/opencv.hpp> | |
#include <opencv2/highgui.hpp> | |
int main(int argc, char* argv[]) | |
{ | |
if (argc < 2) { | |
std::cout << "Usage: cv2ff <outfile>" << std::endl; | |
return 1; | |
} | |
const char* outfile = argv[1]; | |
// initialize FFmpeg library | |
av_register_all(); | |
// av_log_set_level(AV_LOG_DEBUG); | |
int ret; | |
const int dst_width = 640; | |
const int dst_height = 480; | |
const AVRational dst_fps = {30, 1}; | |
// initialize OpenCV capture as input frame generator | |
cv::VideoCapture cvcap(0); | |
if (!cvcap.isOpened()) { | |
std::cerr << "fail to open cv::VideoCapture"; | |
return 2; | |
} | |
cvcap.set(cv::CAP_PROP_FRAME_WIDTH, dst_width); | |
cvcap.set(cv::CAP_PROP_FRAME_HEIGHT, dst_height); | |
// allocate cv::Mat with extra bytes (required by AVFrame::data) | |
std::vector<uint8_t> imgbuf(dst_height * dst_width * 3 + 16); | |
cv::Mat image(dst_height, dst_width, CV_8UC3, imgbuf.data(), dst_width * 3); | |
// open output format context | |
AVFormatContext* outctx = nullptr; | |
ret = avformat_alloc_output_context2(&outctx, nullptr, nullptr, outfile); | |
if (ret < 0) { | |
std::cerr << "fail to avformat_alloc_output_context2(" << outfile << "): ret=" << ret; | |
return 2; | |
} | |
// open output IO context | |
ret = avio_open2(&outctx->pb, outfile, AVIO_FLAG_WRITE, nullptr, nullptr); | |
if (ret < 0) { | |
std::cerr << "fail to avio_open2: ret=" << ret; | |
return 2; | |
} | |
// create new video stream | |
AVCodec* vcodec = avcodec_find_encoder(outctx->oformat->video_codec); | |
AVStream* vstrm = avformat_new_stream(outctx, vcodec); | |
if (!vstrm) { | |
std::cerr << "fail to avformat_new_stream"; | |
return 2; | |
} | |
avcodec_get_context_defaults3(vstrm->codec, vcodec); | |
vstrm->codec->width = dst_width; | |
vstrm->codec->height = dst_height; | |
vstrm->codec->pix_fmt = vcodec->pix_fmts[0]; | |
vstrm->codec->time_base = vstrm->time_base = av_inv_q(dst_fps); | |
vstrm->r_frame_rate = vstrm->avg_frame_rate = dst_fps; | |
if (outctx->oformat->flags & AVFMT_GLOBALHEADER) | |
vstrm->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; | |
// open video encoder | |
ret = avcodec_open2(vstrm->codec, vcodec, nullptr); | |
if (ret < 0) { | |
std::cerr << "fail to avcodec_open2: ret=" << ret; | |
return 2; | |
} | |
std::cout | |
<< "outfile: " << outfile << "\n" | |
<< "format: " << outctx->oformat->name << "\n" | |
<< "vcodec: " << vcodec->name << "\n" | |
<< "size: " << dst_width << 'x' << dst_height << "\n" | |
<< "fps: " << av_q2d(dst_fps) << "\n" | |
<< "pixfmt: " << av_get_pix_fmt_name(vstrm->codec->pix_fmt) << "\n" | |
<< std::flush; | |
// initialize sample scaler | |
SwsContext* swsctx = sws_getCachedContext( | |
nullptr, dst_width, dst_height, AV_PIX_FMT_BGR24, | |
dst_width, dst_height, vstrm->codec->pix_fmt, SWS_BICUBIC, nullptr, nullptr, nullptr); | |
if (!swsctx) { | |
std::cerr << "fail to sws_getCachedContext"; | |
return 2; | |
} | |
// allocate frame buffer for encoding | |
AVFrame* frame = av_frame_alloc(); | |
std::vector<uint8_t> framebuf(avpicture_get_size(vstrm->codec->pix_fmt, dst_width, dst_height)); | |
avpicture_fill(reinterpret_cast<AVPicture*>(frame), framebuf.data(), vstrm->codec->pix_fmt, dst_width, dst_height); | |
frame->width = dst_width; | |
frame->height = dst_height; | |
frame->format = static_cast<int>(vstrm->codec->pix_fmt); | |
// encoding loop | |
avformat_write_header(outctx, nullptr); | |
int64_t frame_pts = 0; | |
unsigned nb_frames = 0; | |
bool end_of_stream = false; | |
int got_pkt = 0; | |
do { | |
if (!end_of_stream) { | |
// retrieve source image | |
cvcap >> image; | |
cv::imshow("press ESC to exit", image); | |
if (cv::waitKey(33) == 0x1b) | |
end_of_stream = true; | |
} | |
if (!end_of_stream) { | |
// convert cv::Mat(OpenCV) to AVFrame(FFmpeg) | |
const int stride[] = { static_cast<int>(image.step[0]) }; | |
sws_scale(swsctx, &image.data, stride, 0, image.rows, frame->data, frame->linesize); | |
frame->pts = frame_pts++; | |
} | |
// encode video frame | |
AVPacket pkt; | |
pkt.data = nullptr; | |
pkt.size = 0; | |
av_init_packet(&pkt); | |
ret = avcodec_encode_video2(vstrm->codec, &pkt, end_of_stream ? nullptr : frame, &got_pkt); | |
if (ret < 0) { | |
std::cerr << "fail to avcodec_encode_video2: ret=" << ret << "\n"; | |
break; | |
} | |
if (got_pkt) { | |
// rescale packet timestamp | |
pkt.duration = 1; | |
av_packet_rescale_ts(&pkt, vstrm->codec->time_base, vstrm->time_base); | |
// write packet | |
av_write_frame(outctx, &pkt); | |
std::cout << nb_frames << '\r' << std::flush; // dump progress | |
++nb_frames; | |
} | |
av_free_packet(&pkt); | |
} while (!end_of_stream || got_pkt); | |
av_write_trailer(outctx); | |
std::cout << nb_frames << " frames encoded" << std::endl; | |
av_frame_free(&frame); | |
avcodec_close(vstrm->codec); | |
avio_close(outctx->pb); | |
avformat_free_context(outctx); | |
return 0; | |
} |
project(screen_capture_example)
cmake_minimum_required(VERSION 3.9)
#set(Boost_INCLUDE_DIR /usr/local/Cellar/boost/1.65.1/include)
#set(Boost_LIBRARY_DIR /usr/local/Cellar/boost/1.65.1/lib)
#set(OpenCV_INCLUDE_DIRS "/usr/local/Cellar/opencv/3.3.1/include")
#set(OpenCV_LIBS "/usr/local/Cellar/opencv/3.3.1/lib")
#set(FFMPEG_INCLUDE_DIRS /usr/local/Cellar/ffmpeg/3.4/include)
#set(FFMPEG_LIBRARIES /usr/local/Cellar/ffmpeg/3.4/lib)
FIND_PACKAGE( Boost 1.65.1 REQUIRED COMPONENTS system )
FIND_PACKAGE( OpenCV 3 REQUIRED) # core highgui imgproc videoio) # ORDER MATTERS!!! videoio must be last!
#FIND_PACKAGE( FFMPEG REQUIRED )
#PKG_CHECK_MODULES(FFMPEG, libavutil libavformat libavcodec libswscale, HAVE_FFMPEG=yes)
INCLUDE(FindPkgConfig)
if ( PKG_CONFIG_FOUND )
pkg_check_modules( AVFORMAT libavformat )
pkg_check_modules( AVCODEC libavcodec )
pkg_check_modules( AVUTIL libavutil )
pkg_check_modules( AVDEVICE libavdevice )
pkg_check_modules( SWSCALE libswscale )
pkg_check_modules( POSTPROC libpostproc )
endif ( PKG_CONFIG_FOUND )
INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIR} ${FFMPEG_INCLUDE_DIRS})
if(WIN32)
set(${PROJECT_NAME}_PLATFORM_LIBS Dwmapi)
SET( FFMPEG_LIBRARIES avformat-52.lib avcodec-51.lib avutil-49.lib avdevice-52.lib swscale-0.lib postproc-0.lib )
elseif(APPLE)
find_library(corefoundation_lib CoreFoundation)
find_library(cocoa_lib Cocoa)
set(${PROJECT_NAME}_PLATFORM_LIBS ${corefoundation_lib} ${cocoa_lib})
SET( FFMPEG_LIBRARIES avformat avcodec avutil avdevice swscale postproc )
else()
find_package(X11 REQUIRED)
if(!X11_XTest_FOUND)
message(FATAL_ERROR "X11 extensions are required, but not found!")
endif()
if(!X11_Xfixes_LIB)
message(FATAL_ERROR "X11 fixes extension is required, but not found!")
endif()
find_package(Threads REQUIRED)
set(${PROJECT_NAME}_PLATFORM_LIBS
${X11_LIBRARIES}
${X11_Xfixes_LIB}
${X11_XTest_LIB}
${X11_Xinerama_LIB}
${CMAKE_THREAD_LIBS_INIT}
)
endif()
add_executable(${PROJECT_NAME}
lodepng.cpp
Screen_Capture_Example.cpp
)
message(STATUS "Boost library status:")
message(STATUS " version: ${Boost_VERSION}")
message(STATUS " libraries: ${Boost_LIBRARIES}")
message(STATUS " include path: ${Boost_INCLUDE_DIRS}")
message(STATUS "OpenCV library status:")
message(STATUS " version: ${OpenCV_VERSION}")
message(STATUS " libraries: ${OpenCV_LIBS}")
message(STATUS " include path: ${OpenCV_INCLUDE_DIRS}")
message(STATUS "FFMPEG library status:")
message(STATUS " version: ${FFMPEG_VERSION}")
message(STATUS " libraries: ${FFMPEG_LIBRARIES}")
message(STATUS " include path: ${FFMPEG_INCLUDE_DIRS}")
target_link_libraries(${PROJECT_NAME} screen_capture_lite ${${PROJECT_NAME}_PLATFORM_LIBS} ${Boost_LIBRARIES} ${OpenCV_LIBS} ${FFMPEG_LIBRARIES})
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_FLAGS=-O2 .
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
clang++ -std=c++11 -fsanitize=address -o Screen_Capture_Example Screen_Capture_Example.cpp -lavutil -lavcodec -lavformat -lswscale -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_videoio && ASAN_OPTIONS="detect_leaks=1"