Reading h264 or h265 stream with ffmpeg/OpenCV: Which is faster? - c++

I'm using OpenCV with ffmpeg support to read a RTSP stream coming from an IP camera and then to write the frames to a video. The problem is that the frame size is 2816x2816 at 20 fps i.e. there's a lot of data coming in.
I noticed that there was a significant delay in the stream, so I set the buffer size of the cv::VideoCapture object to 1, because I thought that the frames might just get stuck in the buffer instead of being grabbed and processed. This however just caused for frames to be dropped instead.
My next move was to experiment a bit with the frame size/fps and the encoding of the video that I'm writing. All of those things helped to improve the situation, but in the long run I still have to use a frame size of 2816x2816 and support up to 20 fps, so I can't set it lower sadly.
That's where my question comes in: given the fact that the camera stream is going to be either h264 or h265, which one would be read faster by the cv::VideoCapture object? And how should I encode the video I'm writing in order to minimize the time spent decoding/encoding frames?
That's the code I'm using for reference:
using namespace cv;
int main(int argc, char** argv)
{
VideoCapture cap;
cap.set(CAP_PROP_BUFFERSIZE, 1); // internal buffer will now store only 1 frames
if (!cap.open("rtsp://admin:admin#1.1.1.1:554/stream")) {
return -1;
}
VideoWriter videoWr;
Mat frame;
cap >> frame;
//int x264 = cv::VideoWriter::fourcc('x', '2', '6', '4'); //I was trying different options
int x264 = cv::VideoWriter::fourcc('M', 'J', 'P', 'G');
videoWr = cv::VideoWriter("test_video.avi", 0, 0, 20, frame.size(), true);
namedWindow("test", WINDOW_NORMAL);
cv::resizeWindow("test", 1024, 768);
for (;;)
{
cap >> frame;
if (frame.empty()) break; // end of video stream
imshow("test", frame);
if (waitKey(10) == 27) break;
videoWr << frame;
}
return 0;
}

Related

Write frame into MP4 format using opencv2 and c++

I am trying to write the frame into my local machine in mp4 format. The frame is read from an existing
mp4 file. After running the following code, I was able to see the VideoOutput.mp4 file, but it is corrupted for some reason. Anyone knows why?
VideoCapture capture("videoSample.mp4");
if (capture.isOpened())
{
while (true)
{
capture >> frame;
}
VideoWriter video("somepath\\videoOutput.mp4", VideoWriter::fourcc('m', 'p', '4', 'v'), 10, Size(win_width, win_width * 2));
video.write(frame);
}

Recording desktop to file, using openCV

I'm attempting to utilize OpenCV, C++, on my windows 10 system to record the screen as part of a larger program I am writing. I need the ability to record the display and save the recording for later review.
I was able to find this link on stackoverflow
How to capture the desktop in OpenCV (ie. turn a bitmap into a Mat)?
User john ktejik created a function that in essence completed exactly what I am looking to accomplish, short of saving the stream to file.
Now what I have always done in the past was once I've opened a connection to my webcam or a video file, I could simply create a VideoWriter Object and write the individual frames to file. I have attempted to do just that utilizing John's function to act as a video source.
int main (int argc, char **argv)
{
HWND hwndDesktop = GetDesktopWindow ();
int key = 0;
int frame_width = 1920;
int frame_height = 1080;
VideoWriter video ("screenCap.avi", CV_FOURCC ('M', 'J', 'P', 'G'), 15, Size (frame_width, frame_height));
while (key != 27)
{
Mat src = hwnd2mat (hwndDesktop);
video.write (src);
imshow ("Screen Capture", src);
key = waitKey (27);
}
video.release ();
destroyAllWindows ();
return 0;
}
What I'm seeing as the output, is the file labeled "screenCap.avi", however the file is empty of video. The file saves as 16KB storage space.
John's function is on point, as it displays the frames just fine via imshow(), but doesn't seem to allow me to save them.
So over the weekend I played with the software some more. And as I really don't have a firm grasp on it, I figured that there had to be a problem with settings between the screen capture and the file writer.
So I started looking at each of the lines in John's function. I came across
src.create(height, width, CV_8UC4);
It seems that the Mat object is being created as with 4 color channels. Did a bit more digging and I found a couple references that point to Videowriter expecting 3 channels.
So a simple change was to convert the output of Johns function from 4 channels to 3 channels. This fixed the problem and I can now write the frames to file.
int main (int argc, char **argv)
{
HWND hwndDesktop = GetDesktopWindow ();
int key = 0;
int frame_width = 1920;
int frame_height = 1080;
VideoWriter video ("screenCap.avi", CV_FOURCC ('M', 'J', 'P', 'G'), 15, Size
(frame_width, frame_height));
while (key != 27)
{
Mat src = hwnd2mat (hwndDesktop);
Mat dst;
cvtColor (src, dst, COLOR_BGRA2RGB);
video.write (dst);
imshow ("Screen Capture", dst);
key = waitKey (27);
}
video.release ();
destroyAllWindows ();
return 0;
}

OpenCV webcam MJPG low FPS

The problem is that I am keep getting low FPS from Logitech C270 webcam capture in OpenCV3.
Things i've tried are described in code comments
Mat frame;
int main(int argc, char *argv[])
{
// i've tried it this way
//int apiBackend = cv::CAP_DSHOW;
//cv::VideoCapture cap(0+apiBackend);
//and tis way
VideoCapture cap(0);
cap.open(0);
cap.set(CAP_PROP_FOURCC ,cv::VideoWriter::fourcc('M', 'J', 'P', 'G') );
//cap.set(CAP_PROP_EXPOSURE , 1); //changing this gives no result
//cap.set(CAP_PROP_GAIN , 10); // same with this
cap.set(CAP_PROP_FPS, 100);
cap.set(CAP_PROP_FRAME_WIDTH, 640);
cap.set(CAP_PROP_FRAME_HEIGHT, 480);
while(1)
{
float e1 = cv::getTickCount();
cap >> frame; // get a new frame from camera
imshow("frame", frame);
float e2 = cv::getTickCount();
float t = (e2 - e1)/cv::getTickFrequency();
float fps = 1.0 / t;
std::cout << fps << std::endl;
if(waitKey(1) >= 0) break;
}
return 0;
}
Changing CAP_PROP_FPS to 5 works, and FPS drops ok.
Playing with resolution didn't help: from 320*240 to 1280*720 i keep getting about 16 FPS.
Webcam drivers are latest.
Am i missing something?
Thanks fo suggestions everybody!
Looks like the answer is camera-specific: i had to install Logitech Webcam Software and disable RightLight feature, now FPS is about 30.
Maybe there is some way to disable RightLight from OpenCV using cap.set(...), but this is subject for further investigation.

Playing video to correct speed with OpenCV

I have problem with plying video file, why it is slow motion ?
How can I make it normal speed?
#include"opencv2/opencv.hpp"
using namespace cv;
int main(int, char**)
{
VideoCapture cap("eye.mp4");
// open the default camera
if (!cap.isOpened())
// check if we succeeded
return -1;
namedWindow("Video", 1);
while (1)
{
Mat frame;
cap >> frame;
imshow("Video", frame);
if (waitKey(10) == 'c')
break;
}
return 0;
}
VideoCapture isn't built for playback, it's just a way to grab frames from video file or camera. Other libraries that supports playback, such as GStreamer or Directshow, they set a clock that control the playback, so that it can be configured to play as fastest as possible or use the original framerate.
In your snippet, the interval between frames comes from the time it takes to read a frame and the waitKey(10). Try using waitKey(0), it should at least play faster. Ideally, you could use waitKey(1/fps).

OpenCV: VideoCapture::get(CV_CAP_PROP_FPS) returns 0 FPS

I am trying to get the fps from my camera so that I can pass it to the VideoWriter for outputting the video. However, I am getting 0 fps by calling VideoCapture::get(CV_CAP_PROP_FPS) from my camera. If I hardcode it, my video may be too slow or too fast.
#include "opencv2/opencv.hpp"
#include <stdio.h>
#include <stdlib.h>
using namespace std;
using namespace cv;
int main(int argc, char *argv[])
{
cv::VideoCapture cap;
int key = 0;
if(argc > 1){
cap.open(string(argv[1]));
}
else
{
cap.open(CV_CAP_ANY);
}
if(!cap.isOpened())
{
printf("Error: could not load a camera or video.\n");
}
Mat frame;
cap >> frame;
waitKey(5);
namedWindow("video", 1);
double fps = cap.get(CV_CAP_PROP_FPS);
CvSize size = cvSize((int)cap.get(CV_CAP_PROP_FRAME_WIDTH),(int)cap.get(CV_CAP_PROP_FRAME_HEIGHT));
int codec = CV_FOURCC('M', 'J', 'P', 'G');
if(!codec){ waitKey(0); return 0; }
std::cout << "CODEC: " << codec << std::endl;
std::cout << "FPS: " << fps << std::endl;
VideoWriter v("Hello.avi",-1,fps,size);
while(key != 'q'){
cap >> frame;
if(!frame.data)
{
printf("Error: no frame data.\n");
break;
}
if(frame.empty()){ break; }
v << frame;
imshow("video", frame);
key = waitKey(5);
}
return(0);
}
How can I get VideoCapture::get(CV_CAP_PROP_FPS) to return the right fps or give a fps to the VideoWriter that works universally for all webcams?
CV_CAP_PROP_FPS only works on videos as far as I know. If you want to capture video data from a webcam you have to time it correctly yourself. For example use a timer to capture a frame from the webcam every 40ms and then save as 25fps video.
You can use VideoCapture::set(CV_CAP_PROP_FPS) to set the desired FPS for a webcam. However, you can't use get for some reason.
Note that sometimes the driver will choose a different FPS than what you have requested depending on the limitations of the webcam.
My workaround: capture frames during a few seconds (4 is fine in my tests, with 0.5 seconds of initial delay), and estimate the fps the camera outputs.
I've never observed CV_CAP_PROP_FPS to work. I have tried with various flavors of OpenCV 2.4.x (currently 2.4.11) using file inputs.
As a workaround in one scenario, I directly used libavformat (from ffmpeg) to get the frame rate, which I can then use in my other OpenCV code:
static double get_frame_rate(const char *filePath) {
AVFormatContext *gFormatCtx = avformat_alloc_context();
av_register_all();
if (avformat_open_input(&gFormatCtx, filePath, NULL, NULL) != 0) {
return -1;
} else if (avformat_find_stream_info(gFormatCtx, NULL) < 0) {
return -1;
}
for (int i = 0; i < gFormatCtx->nb_streams; i++) {
if (gFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
AVRational rate = gFormatCtx->streams[i]->avg_frame_rate;
return (double)av_q2d(rate);
}
}
return -1;
}
Aside from that, undoubtedly one of the slowest possible (although sure to work) methods to get the average fps, would be to step through each frame and divide the current frame number by the current time:
for (;;) {
currentFrame = cap.get(CV_CAP_PROP_POS_FRAMES);
currentTime = cap.get(CV_CAP_PROP_POS_MSEC);
fps = currentFrame / (currentTime / 1000);
# ... code ...
# stop this loop when you're satisfied ...
}
You'd probably only want to do the latter if the other methods of directly finding the fps failed, and further, there were no better way to summarily get overall duration and frame count information.
The example above works on a file -- to adapt to a camera, you could use elapsed wallclock time since beginning of capture, instead of getting CV_CAP_PROP_POS_MSEC. Then the average fps for the session would be the elapsed wall clock time divided by the current frame number.
For live video from webcam use cap.get(cv2.CAP_PROP_FPS)