//-------------------------------------------------------------- bool ofxVideoRecorder::setupCustomOutput(int w, int h, float fps, int sampleRate, int channels, string outputString, bool sysClockSync, bool silent){ if(bIsInitialized) { close(); } bIsSilent = silent; bSysClockSync = sysClockSync; bRecordAudio = (sampleRate > 0 && channels > 0); bRecordVideo = (w > 0 && h > 0 && fps > 0); bFinishing = false; videoFramesRecorded = 0; audioSamplesRecorded = 0; if(!bRecordVideo && !bRecordAudio) { ofLogWarning() << "ofxVideoRecorder::setupCustomOutput(): invalid parameters, could not setup video or audio stream.\n" << "video: " << w << "x" << h << "@" << fps << "fps\n" << "audio: " << "channels: " << channels << " @ " << sampleRate << "Hz\n"; return false; } videoPipePath = ""; audioPipePath = ""; pipeNumber = requestPipeNumber(); if(bRecordVideo) { width = w; height = h; frameRate = fps; // recording video, create a FIFO pipe videoPipePath = ofFilePath::getAbsolutePath("ofxvrpipe" + ofToString(pipeNumber)); if(!ofFile::doesFileExist(videoPipePath)){ string cmd = "bash --login -c 'mkfifo " + videoPipePath + "'"; system(cmd.c_str()); // TODO: add windows compatable pipe creation (does ffmpeg work with windows pipes?) } } if(bRecordAudio) { this->sampleRate = sampleRate; audioChannels = channels; // recording video, create a FIFO pipe audioPipePath = ofFilePath::getAbsolutePath("ofxarpipe" + ofToString(pipeNumber)); if(!ofFile::doesFileExist(audioPipePath)){ string cmd = "bash --login -c 'mkfifo " + audioPipePath + "'"; system(cmd.c_str()); // TODO: add windows compatable pipe creation (does ffmpeg work with windows pipes?) } } stringstream cmd; // basic ffmpeg invocation, -y option overwrites output file cmd << "bash --login -c '" << ffmpegLocation << (bIsSilent?" -loglevel quiet ":" ") << "-y"; if(bRecordAudio){ cmd << " -acodec pcm_s16le -f s16le -ar " << sampleRate << " -ac " << audioChannels << " -i " << audioPipePath; } else { // no audio stream cmd << " -an"; } if(bRecordVideo){ // video input options and file cmd << " -r "<< fps << " -s " << w << "x" << h << " -f rawvideo -pix_fmt " << pixelFormat <<" -i " << videoPipePath << " -r " << fps; } else { // no video stream cmd << " -vn"; } cmd << " "+ outputString +"' &"; //cerr << cmd.str(); ffmpegThread.setup(cmd.str()); // start ffmpeg thread, will wait for input pipes to be opened if(bRecordAudio){ audioThread.setup(audioPipePath, &audioFrames); } if(bRecordVideo){ videoThread.setup(videoPipePath, &frames); } bIsInitialized = true; bIsRecording = false; bIsPaused = false; startTime = 0; recordingDuration = 0; totalRecordingDuration = 0; return bIsInitialized; }
bool ofxVideoRecorder::setupCustomOutput(int w, int h, float fps, int sampleRate, int channels, string outputString, bool sysClockSync, bool silent) { if (bIsInitialized) { close(); } bIsSilent = silent; bSysClockSync = sysClockSync; bRecordAudio = (sampleRate > 0 && channels > 0); bRecordVideo = (w > 0 && h > 0 && fps > 0); bFinishing = false; videoFramesRecorded = 0; audioSamplesRecorded = 0; if (!bRecordVideo && !bRecordAudio) { ofLogWarning() << "ofxVideoRecorder::setupCustomOutput(): invalid parameters, could not setup video or audio stream.\n" << "video: " << w << "x" << h << "@" << fps << "fps\n" << "audio: " << "channels: " << channels << " @ " << sampleRate << "Hz\n"; return false; } videoPipePath = ""; audioPipePath = ""; pipeNumber = requestPipeNumber(); if (bRecordVideo) { width = w; height = h; frameRate = fps; #if defined( TARGET_OSX ) || defined( TARGET_LINUX ) // recording video, create a FIFO pipe videoPipePath = ofFilePath::getAbsolutePath("ofxvrpipe" + ofToString(pipeNumber)); ofStringReplace(videoPipePath, " ", "\\ "); if (!ofFile::doesFileExist(videoPipePath)) { string cmd = "bash --login -c 'mkfifo " + videoPipePath + "'"; system(cmd.c_str()); } #endif #ifdef TARGET_WIN32 char vpip[128]; int num = ofRandom(1024); sprintf(vpip, "\\\\.\\pipe\\videoPipe%d", num); vPipename = convertCharArrayToLPCWSTR(vpip); hVPipe = CreateNamedPipe( vPipename, // name of the pipe PIPE_ACCESS_OUTBOUND, // 1-way pipe -- send only PIPE_TYPE_BYTE, // send data as a byte stream 1, // only allow 1 instance of this pipe 0, // outbound buffer defaults to system default 0, // no inbound buffer 0, // use default wait time NULL // use default security attributes ); if (!(hVPipe != INVALID_HANDLE_VALUE)) { if (GetLastError() != ERROR_PIPE_BUSY) { ofLogError("Video Pipe") << "Could not open video pipe."; } // All pipe instances are busy, so wait for 5 seconds. if (!WaitNamedPipe(vPipename, 5000)) { ofLogError("Video Pipe") << "Could not open video pipe: 5 second wait timed out."; } } #endif } if (bRecordAudio) { this->sampleRate = sampleRate; audioChannels = channels; #if defined( TARGET_OSX ) || defined( TARGET_LINUX ) // recording video, create a FIFO pipe audioPipePath = ofFilePath::getAbsolutePath("ofxarpipe" + ofToString(pipeNumber)); ofStringReplace(audioPipePath, " ", "\\ "); if (!ofFile::doesFileExist(audioPipePath)) { string cmd = "bash --login -c 'mkfifo " + audioPipePath + "'"; system(cmd.c_str()); } #endif #ifdef TARGET_WIN32 char apip[128]; int num = ofRandom(1024); sprintf(apip, "\\\\.\\pipe\\videoPipe%d", num); aPipename = convertCharArrayToLPCWSTR(apip); hAPipe = CreateNamedPipe( aPipename, PIPE_ACCESS_OUTBOUND, // 1-way pipe -- send only PIPE_TYPE_BYTE, // send data as a byte stream 1, // only allow 1 instance of this pipe 0, // outbound buffer defaults to system default 0, // no inbound buffer 0, // use default wait time NULL // use default security attributes ); if (!(hAPipe != INVALID_HANDLE_VALUE)) { if (GetLastError() != ERROR_PIPE_BUSY) { ofLogError("Audio Pipe") << "Could not open audio pipe."; } // All pipe instances are busy, so wait for 5 seconds. if (!WaitNamedPipe(aPipename, 5000)) { ofLogError("Audio Pipe") << "Could not open pipe: 5 second wait timed out."; } } #endif } stringstream cmd; // basic ffmpeg invocation, -y option overwrites output file #if defined( TARGET_OSX ) || defined( TARGET_LINUX ) cmd << "bash --login -c '" << ffmpegLocation << (bIsSilent ? " -loglevel quiet " : " ") << "-y"; if (bRecordAudio) { cmd << " -acodec pcm_s16le -f s16le -ar " << sampleRate << " -ac " << audioChannels << " -i " << audioPipePath; } else { // no audio stream cmd << " -an"; } if (bRecordVideo) { // video input options and file cmd << " -r " << fps << " -s " << w << "x" << h << " -f rawvideo -pix_fmt " << pixelFormat << " -i " << videoPipePath << " -r " << fps; } else { // no video stream cmd << " -vn"; } cmd << " " + outputString + "' &"; //cerr << cmd.str(); ffmpegThread.setup(cmd.str()); // start ffmpeg thread, will wait for input pipes to be opened if (bRecordAudio) { audioThread.setup(audioPipePath, &audioFrames); } if (bRecordVideo) { videoThread.setup(videoPipePath, &frames); } #endif #ifdef TARGET_WIN32 //evidently there are issues with multiple named pipes http://trac.ffmpeg.org/ticket/1663 if (bRecordAudio && bRecordVideo) { bool fSuccess; // Audio Thread stringstream aCmd; aCmd << ffmpegLocation << " -y " << " -f s16le -acodec " << audioCodec << " -ar " << sampleRate << " -ac " << audioChannels; aCmd << " -i " << convertWideToNarrow(aPipename) << " -b:a " << audioBitrate << " " << outputString << "_atemp" << audioFileExt; ffmpegAudioThread.setup(aCmd.str()); ofLogNotice("FFMpeg Command") << aCmd.str() << endl; fSuccess = ConnectNamedPipe(hAPipe, NULL); if (!fSuccess) { LPTSTR errorText = NULL; FormatMessageW( // use system message tables to retrieve error text FORMAT_MESSAGE_FROM_SYSTEM // allocate buffer on local heap for error text | FORMAT_MESSAGE_ALLOCATE_BUFFER // Important! will fail otherwise, since we're not // (and CANNOT) pass insertion parameters | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, // unused with FORMAT_MESSAGE_FROM_SYSTEM GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorText, // output 0, // minimum size for output buffer NULL); // arguments - see note wstring ws = errorText; string error(ws.begin(), ws.end()); ofLogError("Audio Pipe") << "SetNamedPipeHandleState failed: " << error; } else { ofLogNotice("Audio Pipe") << "\n==========================\nAudio Pipe Connected Successfully\n==========================\n" << endl; audioThread.setup(hAPipe, &audioFrames); } // Video Thread stringstream vCmd; vCmd << ffmpegLocation << " -y " << " -r " << fps << " -s " << w << "x" << h << " -f rawvideo -pix_fmt " << pixelFormat; vCmd << " -i " << convertWideToNarrow(vPipename) << " -vcodec " << videoCodec << " -b:v " << videoBitrate << " " << outputString << "_vtemp" << movFileExt; ffmpegVideoThread.setup(vCmd.str()); ofLogNotice("FFMpeg Command") << vCmd.str() << endl; fSuccess = ConnectNamedPipe(hVPipe, NULL); if (!fSuccess) { LPTSTR errorText = NULL; FormatMessageW( // use system message tables to retrieve error text FORMAT_MESSAGE_FROM_SYSTEM // allocate buffer on local heap for error text | FORMAT_MESSAGE_ALLOCATE_BUFFER // Important! will fail otherwise, since we're not // (and CANNOT) pass insertion parameters | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, // unused with FORMAT_MESSAGE_FROM_SYSTEM GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorText, // output 0, // minimum size for output buffer NULL); // arguments - see note wstring ws = errorText; string error(ws.begin(), ws.end()); ofLogError("Video Pipe") << "SetNamedPipeHandleState failed: " << error; } else { ofLogNotice("Video Pipe") << "\n==========================\nVideo Pipe Connected Successfully\n==========================\n" << endl; videoThread.setup(hVPipe, &frames); } } else { cmd << ffmpegLocation << " -y "; if (bRecordAudio) { cmd << " -f s16le -acodec " << audioCodec << " -ar " << sampleRate << " -ac " << audioChannels << " -i " << convertWideToNarrow(aPipename); } else { // no audio stream cmd << " -an"; } if (bRecordVideo) { // video input options and file cmd << " -r " << fps << " -s " << w << "x" << h << " -f rawvideo -pix_fmt " << pixelFormat << " -i " << convertWideToNarrow(vPipename); } else { // no video stream cmd << " -vn"; } if (bRecordAudio) cmd << " -b:a " << audioBitrate; if (bRecordVideo) cmd << " -vcodec " << videoCodec << " -b:v " << videoBitrate; cmd << " " << outputString << movFileExt; ofLogNotice("FFMpeg Command") << cmd.str() << endl; ffmpegThread.setup(cmd.str()); // start ffmpeg thread, will wait for input pipes to be opened if (bRecordAudio) { //this blocks, so we have to call it after ffmpeg is listening for a pipe bool fSuccess = ConnectNamedPipe(hAPipe, NULL); if (!fSuccess) { LPTSTR errorText = NULL; FormatMessageW( // use system message tables to retrieve error text FORMAT_MESSAGE_FROM_SYSTEM // allocate buffer on local heap for error text | FORMAT_MESSAGE_ALLOCATE_BUFFER // Important! will fail otherwise, since we're not // (and CANNOT) pass insertion parameters | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, // unused with FORMAT_MESSAGE_FROM_SYSTEM GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorText, // output 0, // minimum size for output buffer NULL); // arguments - see note wstring ws = errorText; string error(ws.begin(), ws.end()); ofLogError("Audio Pipe") << "SetNamedPipeHandleState failed: " << error; } else { ofLogNotice("Audio Pipe") << "\n==========================\nAudio Pipe Connected Successfully\n==========================\n" << endl; audioThread.setup(hAPipe, &audioFrames); } } if (bRecordVideo) { //this blocks, so we have to call it after ffmpeg is listening for a pipe bool fSuccess = ConnectNamedPipe(hVPipe, NULL); if (!fSuccess) { LPTSTR errorText = NULL; FormatMessageW( // use system message tables to retrieve error text FORMAT_MESSAGE_FROM_SYSTEM // allocate buffer on local heap for error text | FORMAT_MESSAGE_ALLOCATE_BUFFER // Important! will fail otherwise, since we're not // (and CANNOT) pass insertion parameters | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, // unused with FORMAT_MESSAGE_FROM_SYSTEM GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&errorText, // output 0, // minimum size for output buffer NULL); // arguments - see note wstring ws = errorText; string error(ws.begin(), ws.end()); ofLogError("Video Pipe") << "SetNamedPipeHandleState failed: " << error; } else { ofLogNotice("Video Pipe") << "\n==========================\nVideo Pipe Connected Successfully\n==========================\n" << endl; videoThread.setup(hVPipe, &frames); } } } #endif bIsInitialized = true; bIsRecording = false; bIsPaused = false; startTime = 0; recordingDuration = 0; totalRecordingDuration = 0; return bIsInitialized; }