Skip to content
Snippets Groups Projects
Commit 966fd020 authored by Philippe Gorley's avatar Philippe Gorley Committed by Sébastien Blin
Browse files

recorder: parallelize filtering and encoding


Change-Id: Ia2de94e79912ac27da0084daf0d59bd0bcf0220a
Reviewed-by: default avatarSebastien Blin <sebastien.blin@savoirfairelinux.com>
parent cffd2587
No related branches found
No related tags found
No related merge requests found
...@@ -38,12 +38,17 @@ extern "C" { ...@@ -38,12 +38,17 @@ extern "C" {
namespace ring { namespace ring {
MediaRecorder::MediaRecorder() MediaRecorder::MediaRecorder()
: loop_([]{ return true;},
[this]{ process(); },
[]{})
{} {}
MediaRecorder::~MediaRecorder() MediaRecorder::~MediaRecorder()
{ {
if (isRecording_) if (isRecording_)
flush(); flush();
if (loop_.isRunning())
loop_.join();
} }
std::string std::string
...@@ -112,6 +117,15 @@ MediaRecorder::startRecording() ...@@ -112,6 +117,15 @@ MediaRecorder::startRecording()
ss << std::put_time(&startTime_, "%Y%m%d-%H%M%S"); ss << std::put_time(&startTime_, "%Y%m%d-%H%M%S");
filename_ = ss.str(); filename_ = ss.str();
if (!frames_.empty()) {
RING_WARN() << "Frame queue not empty at beginning of recording, frames will be lost";
while (!frames_.empty())
frames_.pop();
}
if (!loop_.isRunning())
loop_.start();
encoder_.reset(new MediaEncoder); encoder_.reset(new MediaEncoder);
RING_DBG() << "Start recording '" << getFilename() << "'"; RING_DBG() << "Start recording '" << getFilename() << "'";
...@@ -158,41 +172,13 @@ MediaRecorder::addStream(bool isVideo, bool fromPeer, MediaStream ms) ...@@ -158,41 +172,13 @@ MediaRecorder::addStream(bool isVideo, bool fromPeer, MediaStream ms)
int int
MediaRecorder::recordData(AVFrame* frame, bool isVideo, bool fromPeer) MediaRecorder::recordData(AVFrame* frame, bool isVideo, bool fromPeer)
{ {
std::lock_guard<std::mutex> lk(mutex_);
if (!isRecording_ || !isReady_) if (!isRecording_ || !isReady_)
return 0; return 0;
int streamIdx = (isVideo ? videoIdx_ : audioIdx_); // save a copy of the frame, will be filtered/encoded in another thread
auto filter = (isVideo ? videoFilter_.get() : audioFilter_.get());
if (streamIdx < 0 || !filter) {
RING_ERR() << "Specified stream is invalid: "
<< (fromPeer ? "remote " : "local ") << (isVideo ? "video" : "audio");
return -1;
}
// get filter input name if frame needs filtering
std::string inputName;
if (isVideo && nbReceivedVideoStreams_ == 2)
inputName = (fromPeer ? "v:main" : "v:overlay");
if (!isVideo && nbReceivedAudioStreams_ == 2)
inputName = (fromPeer ? "a:1" : "a:2");
// new reference because we are changing the timestamp
AVFrame* input = av_frame_clone(frame); AVFrame* input = av_frame_clone(frame);
const MediaStream& ms = streams_[isVideo][fromPeer]; frames_.emplace(input, isVideo, fromPeer);
// stream has to start at 0 return 0;
input->pts = input->pts - ms.firstTimestamp;
if (inputName.empty()) // #nofilters
return sendToEncoder(input, streamIdx);
// empty filter graph output before sending more frames
emptyFilterGraph();
int err = filter->feedInput(input, inputName);
av_frame_unref(input);
return err;
} }
int int
...@@ -437,4 +423,55 @@ MediaRecorder::flush() ...@@ -437,4 +423,55 @@ MediaRecorder::flush()
return 0; return 0;
} }
void
MediaRecorder::process()
{
std::lock_guard<std::mutex> lk(mutex_);
if (!isRecording_ || !isReady_)
return;
while (!frames_.empty()) {
auto f = frames_.front();
bool isVideo = f.isVideo;
bool fromPeer = f.fromPeer;
AVFrame* input = f.frame;
frames_.pop();
int streamIdx = (isVideo ? videoIdx_ : audioIdx_);
auto filter = (isVideo ? videoFilter_.get() : audioFilter_.get());
if (streamIdx < 0 || !filter) {
RING_ERR() << "Specified stream is invalid: "
<< (fromPeer ? "remote " : "local ") << (isVideo ? "video" : "audio");
av_frame_free(&input);
continue;
}
// get filter input name if frame needs filtering
std::string inputName;
if (isVideo && nbReceivedVideoStreams_ == 2)
inputName = (fromPeer ? "v:main" : "v:overlay");
if (!isVideo && nbReceivedAudioStreams_ == 2)
inputName = (fromPeer ? "a:1" : "a:2");
// new reference because we are changing the timestamp
const MediaStream& ms = streams_[isVideo][fromPeer];
// stream has to start at 0
input->pts = input->pts - ms.firstTimestamp;
if (inputName.empty()) { // #nofilters
if (sendToEncoder(input, streamIdx) < 0) {
RING_ERR() << "Filed to encode frame";
av_frame_free(&input);
continue;
}
}
// empty filter graph output before sending more frames
emptyFilterGraph();
filter->feedInput(input, inputName);
av_frame_free(&input);
}
}
} // namespace ring } // namespace ring
...@@ -25,10 +25,12 @@ ...@@ -25,10 +25,12 @@
#include "media_filter.h" #include "media_filter.h"
#include "media_stream.h" #include "media_stream.h"
#include "noncopyable.h" #include "noncopyable.h"
#include "threadloop.h"
#include <map> #include <map>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <queue>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <utility> #include <utility>
...@@ -102,6 +104,20 @@ class MediaRecorder { ...@@ -102,6 +104,20 @@ class MediaRecorder {
bool isRecording_ = false; bool isRecording_ = false;
bool isReady_ = false; bool isReady_ = false;
bool audioOnly_ = false; bool audioOnly_ = false;
struct RecordFrame {
AVFrame* frame;
bool isVideo;
bool fromPeer;
RecordFrame(AVFrame* f, bool v, bool p)
: frame(f)
, isVideo(v)
, fromPeer(p)
{}
};
ThreadLoop loop_;
void process();
std::queue<RecordFrame> frames_;
}; };
}; // namespace ring }; // namespace ring
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment