I'm trying to get cv::VideoWriter to work but I'm having trouble.
if (recording)
{
vid.write(imagecl);
std::cout << "\trecording..." << std::endl;
}
cv::imshow(CAMERA_TOPIC, imagecl);
rec_key = cv::waitKey(1);
imagecl.release();
if (rec_key == 114 && !recording)
{
std::cout << "\t\tStarted Recording" << std::endl;
auto now = std::chrono::system_clock::now();
now_time = std::chrono::system_clock::to_time_t(now);
vidname = VIDEO_PATH + date_str(std::ctime(&now_time)) + ".mp4";
std::cout << "\tSaving video to -> " << vidname << std::endl;
codec = cv::VideoWriter::fourcc('a','v','c','1');
vid = cv::VideoWriter(vidname, codec, 30, cv::Size(frame_width, frame_height), true);
recording = true;
}
else if (rec_key == 115 && recording)
{
std::cout << "\t\tEnded recording" << std::endl;
vid.release();
recording = false;
}
This code is part of a video_recorder function what is executing inside an infinite loop. I'm using another thread to fill the imagecl with the frames I want to record. The cv::imshow is working properly (it opens a window and displays the frames), but I can't get to save videos with cv::VideoWriter. The 'r' and 's' keys are triggering the conditions as I get the terminal messages of "started recording" and then "recording" each loop and then "ended recording". I double checked the path and it is correct. I'm saving to "/home/username/videoname.mp4". What am I doing wrong?
Related
I'm using the modern ffmpeg API which instructs me to use avcodec_send_packet and avcodec_receive_frame. There are literally no examples of usage in github so I couldn't compare with other code.
My code kinda works, but sometimes, for less than a second or two, the video gets decoded like this:
I thought it was a buffer size problem, so I increased from
const size_t bufferSize = 408304;
to
const size_t bufferSize = 10408304;
Just to see but the problem persists.
(the video size is 1920x1080 and this happens even when there's little motion in the screen)
Here's my decoder class which sends the decoded data to OpenGL in the line
this->frameUpdater->updateData(avFrame->data, avFrame->width, avFrame->height);
void FfmpegDecoder::decodeFrame(uint8_t* frameBuffer, int frameLength)
{
if (frameLength <= 0) return;
int frameFinished = 0;
AVPacket* avPacket = av_packet_alloc();
if (!avPacket) std::cout << "av packet error" << std::endl;
avPacket->size = frameLength;
avPacket->data = frameBuffer;
//Disable ffmpeg annoying output
av_log_set_level(AV_LOG_QUIET);
int sendPacketResult = avcodec_send_packet(avCodecContext, avPacket);
if (!sendPacketResult) {
int receiveFrameResult = avcodec_receive_frame(avCodecContext, avFrame);
if (!receiveFrameResult) {
this->frameUpdater->updateData(avFrame->data, avFrame->width, avFrame->height);
} else if ((receiveFrameResult < 0) && (receiveFrameResult != AVERROR(EAGAIN)) && (receiveFrameResult != AVERROR_EOF)) {
std::cout << "avcodec_receive_frame returned error " << receiveFrameResult /*<< *av_err2str(result).c_str()*/ << std::endl;
} else {
switch (receiveFrameResult) {
//Not exactly an error, we just have to wait for more data
case AVERROR(EAGAIN):
break;
//To be done: what does this error mean? I think it's literally the end of an mp4 file
case AVERROR_EOF:
std::cout << "avcodec_receive_frame AVERROR_EOF" << std::endl;
break;
//To be done: describe what error is this in std cout before stopping
default:
std::cout << "avcodec_receive_frame returned error, stopping..." << receiveFrameResult /*<< av_err2str(result).c_str()*/ << std::endl;
break;
//Error happened, should break anyway
break;
}
}
} else {
switch (sendPacketResult) {
case AVERROR(EAGAIN):
std::cout << "avcodec_send_packet EAGAIN" << std::endl;
break;
case AVERROR_EOF:
std::cout << "avcodec_send_packet AVERROR_EOF" << std::endl;
break;
default:
break;
}
}
}
I think that people who knows how decoding works might know instantly why the image gets decoded like this. It'd be very helpful to know why. Thanks!
i working in a stereo camera project i have two cameras 5megapixels in every one i connected it with my laptop and run my code but when i run it i get this error libv4l2: error turning on stream: No space left on device
im linux os that's my c++ opencv code there are any ideas how to fix it i tried others codes i found it in network but still give me the same error
#include <opencv2/opencv.hpp>
int main()
{
cv::VideoCapture cap1(1);
cv::VideoCapture cap2(2);
if(!cap1.isOpened())
{
std::cout << "Cannot open the video cam [1]" << std::endl;
return -1;
}
if(!cap2.isOpened())
{
std::cout << "Cannot open the video cam [2]" << std::endl;
return -1;
}
cap1.set(CV_CAP_PROP_FPS, 15);
cap2.set(CV_CAP_PROP_FPS, 15);
// Values taken from output of Version 1 and used to setup the exact same parameters with the exact same values!
cap1.set(CV_CAP_PROP_FRAME_WIDTH, 640);
cap1.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
cap2.set(CV_CAP_PROP_FRAME_WIDTH, 640);
cap2.set(CV_CAP_PROP_FRAME_HEIGHT, 480);
cv::namedWindow("cam[1]",CV_WINDOW_AUTOSIZE);
cv::namedWindow("cam[2]",CV_WINDOW_AUTOSIZE);
while(1)
{
cv::Mat frame1, frame2;
bool bSuccess1 = cap1.read(frame1);
bool bSuccess2 = cap2.read(frame2);
if (!bSuccess1)
{
std::cout << "Cannot read a frame from video stream [1]" << std::endl;
break;
}
if (!bSuccess2)
{
std::cout << "Cannot read a frame from video stream [2]" << std::endl;
break;
}
cv::imshow("cam[1]", frame1);
cv::imshow("cam[2]", frame2);
if(cv::waitKey(30) == 27)
{
std::cout << "ESC key is pressed by user" << std::endl;
break;
}
}
return 0;
}
I have a program that sets up an image to record measurements through user input using the mouse. After showing the image with imshow(), I ask the user if they would like to crop. While waiting for input to the command window, the namedWindow is not responding, and if the user clicks the window before specifying whether or not to crop, the window takes that as the first point to use for cropping.
Is there a way have the command window and namedWindow work in unison?
Constructor for measurment class:
MeasurementClass::MeasurmentClass(){
SetWorkingDirectory();
std::cout << "Before continuing, make sure image files are located in:\n\n "
<< "\"" + WorkingDirectory + "\"\n"
<< std::endl;
setfilename:
SetFileName(); //uncomment after debug
SetDate();
Image = cv::imread(XrayFileName, CV_LOAD_IMAGE_COLOR);
FinalImage = Image.clone();
if(Image.empty()){
std::cout << "Image failed to Load. Ensure correct image name and file location.\n"
<< std::endl;
goto setfilename;
}
GetDesktopResolution();
ResizeImageForDisplay();
FinalDisplayImage = DisplayImage.clone();
cv::namedWindow(WindowName, cv::WINDOW_AUTOSIZE);
cv::imshow(WindowName, DisplayImage);
cv::waitKey(10);
PromptForCrop();
}
PromptFor Crop()
void MeasurementClass::PromptForCrop(void){
std::cout << "Would you like to crop image? (y/n): ";
std::string strCrop;
std::getline(std::cin, strCrop);
std::cout << std::endl;
char Crop = strCrop[0];
switch(Crop){
case 'y':
case '\0':
CropImage();
default:
break;
}
}
Bonus
It has recently been brought to my attention that goto statements are terrible practice. In this situation, what is a good alternative to the goto statement.
You can use the cv::waitKey() return value to get user input. In order to do so, please give the image the window focus, not the command window otherwise it will not capture your key:
char c = 'q';
cv::Mat image = cv::imread( "C:/local/opencv30/sources/samples/data/lena.jpg" );
cv::imshow( "image", image );
do
{
c = cv::waitKey();
}
while ( c != 'y' && c != 'n' );
if ( c == 'y' )
cout << "yes" << endl;
else if ( c == 'n' )
cout << "no" << endl;
In my current project, when I call VideoCapture::open(camera device index) and the camera is in used by another program, it shows a Video Source dialog and returns true when I select a device that is already in use.
However, in my [previous] experiment project, when I called VideoCapture::open(camera device index), it doesn't show this dialog.
I want to know what is causing the Video Source dialog to show and the program to behave differently from the experimental project.
This is the source code to the experiment project:
int main (int argc, char *argv[])
{
//vars
time_duration td, td1;
ptime nextFrameTimestamp, currentFrameTimestamp, initialLoopTimestamp, finalLoopTimestamp;
int delayFound = 0;
int totalDelay= 0;
// initialize capture on default source
VideoCapture capture;
std::cout << "capture.open(0): " << capture.open(0) << std::endl;
std::cout << "NOOO" << std::endl;
namedWindow("video", 1);
// set framerate to record and capture at
int framerate = 15;
// Get the properties from the camera
double width = capture.get(CV_CAP_PROP_FRAME_WIDTH);
double height = capture.get(CV_CAP_PROP_FRAME_HEIGHT);
// print camera frame size
//cout << "Camera properties\n";
//cout << "width = " << width << endl <<"height = "<< height << endl;
// Create a matrix to keep the retrieved frame
Mat frame;
// Create the video writer
VideoWriter video("capture.avi",0, framerate, cvSize((int)width,(int)height) );
// initialize initial timestamps
nextFrameTimestamp = microsec_clock::local_time();
currentFrameTimestamp = nextFrameTimestamp;
td = (currentFrameTimestamp - nextFrameTimestamp);
// start thread to begin capture and populate Mat frame
boost::thread captureThread(captureFunc, &frame, &capture);
// loop infinitely
for(bool q=true;q;)
{
if(frame.empty()){continue;}
//if(cvWaitKey( 5 ) == 'q'){ q=false; }
// wait for X microseconds until 1second/framerate time has passed after previous frame write
while(td.total_microseconds() < 1000000/framerate){
//determine current elapsed time
currentFrameTimestamp = microsec_clock::local_time();
td = (currentFrameTimestamp - nextFrameTimestamp);
if(cvWaitKey( 5 ) == 'q'){
std::cout << "B" << std::endl;
q=false;
boost::posix_time::time_duration timeout = boost::posix_time::milliseconds(0);
captureThread.timed_join(timeout);
break;
}
}
// determine time at start of write
initialLoopTimestamp = microsec_clock::local_time();
// Save frame to video
video << frame;
imshow("video", frame);
//write previous and current frame timestamp to console
cout << nextFrameTimestamp << " " << currentFrameTimestamp << " ";
// add 1second/framerate time for next loop pause
nextFrameTimestamp = nextFrameTimestamp + microsec(1000000/framerate);
// reset time_duration so while loop engages
td = (currentFrameTimestamp - nextFrameTimestamp);
//determine and print out delay in ms, should be less than 1000/FPS
//occasionally, if delay is larger than said value, correction will occur
//if delay is consistently larger than said value, then CPU is not powerful
// enough to capture/decompress/record/compress that fast.
finalLoopTimestamp = microsec_clock::local_time();
td1 = (finalLoopTimestamp - initialLoopTimestamp);
delayFound = td1.total_milliseconds();
cout << delayFound << endl;
//output will be in following format
//[TIMESTAMP OF PREVIOUS FRAME] [TIMESTAMP OF NEW FRAME] [TIME DELAY OF WRITING]
if(!q || cvWaitKey( 5 ) == 'q'){
std::cout << "C" << std::endl;
q=false;
boost::posix_time::time_duration timeout = boost::posix_time::milliseconds(0);
captureThread.timed_join(timeout);
break;
}
}
// Exit
return 0;
}
I have two functions:
a internet-socket function which gets mp3-data and writes it to file ,
a function which decodes mp3-files.
However, I would rather decode the data, which is currently written to disk, be decoded in-memory by the decode function.
My decode function looks like this, and it is all initialized via
avformat_open_input(AVCodecContext, filename, NULL, NULL)
How can I read in the AVCodecContext without a filename, and instead using only the in-memory buffer?
I thought I would post some code to illustrate how to achieve this, I have tried to comment but am pressed for time, however it should all be relatively straightforward stuff. Return values are based on interpolation of the associated message into a hex version of 1337 speak converted to decimal values, and I have tried to keep it as light as possible in tone:)
#include <iostream>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/avutil.h>
};
std::string tooManyChannels = "The audio stream (and its frames) has/have too many channels to properly fit in\n to frame->data. Therefore, to access the audio data, you need to use\nframe->extended_data to access the audio data."
"It is a planar store, so\neach channel is in a different element.\n"
" E.G.: frame->extended_data[0] has the data for channel 1\n"
" frame->extended_data[1] has the data for channel 2\n"
"And so on.\n";
std::string nonPlanar = "Either the audio data is not planar, or there is not enough room in\n"
"frame->data to store all the channel data. Either use\n"
"frame->data\n or \nframe->extended_data to access the audio data\n"
"both should just point to the same data in this instance.\n";
std::string information1 = "If the frame is planar, each channel is in a separate element:\n"
"frame->data[0]/frame->extended_data[0] contains data for channel 1\n"
"frame->data[1]/frame->extended_data[1] contains data for channel 2\n";
std::string information2 = "If the frame is in packed format( and therefore not planar),\n"
"then all the data is contained within:\n"
"frame->data[0]/frame->extended_data[0] \n"
"Similar to the manner in which some image formats have RGB(A) pixel data packed together,\n"
"rather than containing separate R G B (and A) data.\n";
void printAudioFrameInfo(const AVCodecContext* codecContext, const AVFrame* frame)
{
/*
This url: http://ffmpeg.org/doxygen/trunk/samplefmt_8h.html#af9a51ca15301871723577c730b5865c5
contains information on the type you will need to utilise to access the audio data.
*/
// format the tabs etc. in this string to suit your font, they line up for mine but may not for yours:)
std::cout << "Audio frame info:\n"
<< "\tSample count:\t\t" << frame->nb_samples << '\n'
<< "\tChannel count:\t\t" << codecContext->channels << '\n'
<< "\tFormat:\t\t\t" << av_get_sample_fmt_name(codecContext->sample_fmt) << '\n'
<< "\tBytes per sample:\t" << av_get_bytes_per_sample(codecContext->sample_fmt) << '\n'
<< "\tPlanar storage format?:\t" << av_sample_fmt_is_planar(codecContext->sample_fmt) << '\n';
std::cout << "frame->linesize[0] tells you the size (in bytes) of each plane\n";
if (codecContext->channels > AV_NUM_DATA_POINTERS && av_sample_fmt_is_planar(codecContext->sample_fmt))
{
std::cout << tooManyChannels;
}
else
{
stc::cout << nonPlanar;
}
std::cout << information1 << information2;
}
int main()
{
// You can change the filename for any other filename/supported format
std::string filename = "../my file.ogg";
// Initialize FFmpeg
av_register_all();
AVFrame* frame = avcodec_alloc_frame();
if (!frame)
{
std::cout << "Error allocating the frame. Let's try again shall we?\n";
return 666; // fail at start: 66 = number of the beast
}
// you can change the file name to whatever yo need:)
AVFormatContext* formatContext = NULL;
if (avformat_open_input(&formatContext, filename, NULL, NULL) != 0)
{
av_free(frame);
std::cout << "Error opening file " << filename<< "\n";
return 800; // cant open file. 800 = Boo!
}
if (avformat_find_stream_info(formatContext, NULL) < 0)
{
av_free(frame);
avformat_close_input(&formatContext);
std::cout << "Error finding the stream information.\nCheck your paths/connections and the details you supplied!\n";
return 57005; // stream info error. 0xDEAD in hex is 57005 in decimal
}
// Find the audio stream
AVCodec* cdc = nullptr;
int streamIndex = av_find_best_stream(formatContext, AVMEDIA_TYPE_AUDIO, -1, -1, &cdc, 0);
if (streamIndex < 0)
{
av_free(frame);
avformat_close_input(&formatContext);
std::cout << "Could not find any audio stream in the file. Come on! I need data!\n";
return 165; // no(0) (a)udio s(5)tream: 0A5 in hex = 165 in decimal
}
AVStream* audioStream = formatContext->streams[streamIndex];
AVCodecContext* codecContext = audioStream->codec;
codecContext->codec = cdc;
if (avcodec_open2(codecContext, codecContext->codec, NULL) != 0)
{
av_free(frame);
avformat_close_input(&formatContext);
std::cout << "Couldn't open the context with the decoder. I can decode but I need to have something to decode.\nAs I couldn't find anything I have surmised the decoded output is 0!\n (Well can't have you thinking I am doing nothing can we?\n";
return 1057; // cant find/open context 1057 = lost
}
std::cout << "This stream has " << codecContext->channels << " channels with a sample rate of " << codecContext->sample_rate << "Hz\n";
std::cout << "The data presented in format: " << av_get_sample_fmt_name(codecContext->sample_fmt) << std::endl;
AVPacket readingPacket;
av_init_packet(&readingPacket);
// Read the packets in a loop
while (av_read_frame(formatContext, &readingPacket) == 0)
{
if (readingPacket.stream_index == audioStream->index)
{
AVPacket decodingPacket = readingPacket;
// Audio packets can have multiple audio frames in a single packet
while (decodingPacket.size > 0)
{
// Try to decode the packet into a frame(s)
// Some frames rely on multiple packets, so we have to make sure the frame is finished
// before utilising it
int gotFrame = 0;
int result = avcodec_decode_audio4(codecContext, frame, &gotFrame, &decodingPacket);
if (result >= 0 && gotFrame)
{
decodingPacket.size -= result;
decodingPacket.data += result;
// et voila! a decoded audio frame!
printAudioFrameInfo(codecContext, frame);
}
else
{
decodingPacket.size = 0;
decodingPacket.data = nullptr;
}
}
}
// You MUST call av_free_packet() after each call to av_read_frame()
// or you will leak so much memory on a large file you will need a memory-plumber!
av_free_packet(&readingPacket);
}
// Some codecs will cause frames to be buffered in the decoding process.
// If the CODEC_CAP_DELAY flag is set, there can be buffered frames that need to be flushed
// therefore flush them now....
if (codecContext->codec->capabilities & CODEC_CAP_DELAY)
{
av_init_packet(&readingPacket);
// Decode all the remaining frames in the buffer
int gotFrame = 0;
while (avcodec_decode_audio4(codecContext, frame, &gotFrame, &readingPacket) >= 0 && gotFrame)
{
// Again: a fully decoded audio frame!
printAudioFrameInfo(codecContext, frame);
}
}
// Clean up! (unless you have a quantum memory machine with infinite RAM....)
av_free(frame);
avcodec_close(codecContext);
avformat_close_input(&formatContext);
return 0; // success!!!!!!!!
}
Hope this helps. Let me know if you need more info, and I will try and help out:)
There is also some very good tutorial information available at dranger.com which you may find useful.
Preallocate the format context and set its pb field as suggested in the note of avformat_open_input() documentation.
.