Skip to content

Instantly share code, notes, and snippets.

@pauldotknopf
Created November 5, 2017 00:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pauldotknopf/de3495a2581c65bac3cea2ddb8dc376c to your computer and use it in GitHub Desktop.
Save pauldotknopf/de3495a2581c65bac3cea2ddb8dc376c to your computer and use it in GitHub Desktop.
#include "encoderbackend.h"
#include <QDebug>
#include <QThread>
#include <QAbstractVideoSurface>
#include <QVideoSurfaceFormat>
#include <QVideoFrame>
#include <private/qmemoryvideobuffer_p.h>
#include "encodermediaservice.h"
#include "encoderbackendasyncworker.h"
#include "encoderbackendcallbacks.h"
#include "encoderbackendplayer.h"
#include "mxencoder_internal.h"
#include "mxcommon.h"
#include "mxinputmanager.h"
EncoderBackend::EncoderBackend(QObject *parent) :
QObject(parent),
_signalPresent(false),
_isRecording(false),
encoder(NULL),
callbacks(new EncoderBackendCallbacks(this)),
player(new EncoderBackendPlayer(this)),
videoSurface(NULL),
currentSample(NULL),
currentStatus(new MxInputStatus)
{
EncoderBackendAsyncWorker *worker = new EncoderBackendAsyncWorker(this);
worker->moveToThread(&workerThread);
connect(&workerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
connect(this, &EncoderBackend::captureStillEvent, worker, &EncoderBackendAsyncWorker::captureStill);
connect(this, &EncoderBackend::startRecordingEvent, worker, &EncoderBackendAsyncWorker::startRecording);
connect(this, &EncoderBackend::stopRecordingEvent, worker, &EncoderBackendAsyncWorker::stopRecording);
workerThread.start();
}
EncoderBackend::~EncoderBackend()
{
workerThread.quit();
workerThread.wait();
destroyEncoder();
if (callbacks)
delete callbacks;
if (currentStatus)
delete currentStatus;
}
void EncoderBackend::startRecording(int bitrate)
{
Q_EMIT startRecordingEvent(bitrate);
}
void EncoderBackend::_startRecording(Bitrate bitrate)
{
encoder->StartRecording(bitrate);
}
void EncoderBackend::stopRecording()
{
Q_EMIT stopRecordingEvent();
}
void EncoderBackend::_stopRecording()
{
encoder->StopRecording();
}
void EncoderBackend::captureStill(int type)
{
Q_EMIT captureStillEvent(type);
}
void EncoderBackend::_captureStill(StillCaptureType type)
{
encoder->CaptureStill(type);
}
void EncoderBackend::presentFrame()
{
QMutexLocker locker(&presentMutex);
if(!videoSurface) return;
if(!videoSurface->isActive()) return;
if(currentSample)
{
EncoderSampleBuffer* sampleBuffer = NULL;
encoder_sample_get_buffer(currentSample, &sampleBuffer);
EncoderSampleBufferMap* sampleBufferMap = NULL;
encoder_sample_buffer_map(sampleBuffer, &sampleBufferMap);
unsigned char* data = NULL;
int size = 0;
encoder_sample_buffer_get_data(sampleBufferMap, &data, &size);
if(size > 0) {
QVideoFrame frame = QVideoFrame(new QMemoryVideoBuffer(reinterpret_cast<char*>(data), currentStatus->width),
QSize(currentStatus->width, currentStatus->height),
QVideoFrame::Format_YV12);
videoSurface->present(frame);
}
encoder_sample_buffer_unmap(sampleBuffer, sampleBufferMap);
encoder_sample_unref(currentSample);
currentSample = NULL;
}
}
void EncoderBackend::updateSignalStatus() {
QMutexLocker locker(&presentMutex);
if(currentStatus->status) {
// we have a signal
setSignalPresent(true);
if(videoSurface) {
if(videoSurface->isActive())
videoSurface->stop();
QVideoSurfaceFormat format(QSize(currentStatus->width, currentStatus->height), QVideoFrame::Format_YV12);
videoSurface->start(format);
}
} else {
// we have a signal
setSignalPresent(false);
if (videoSurface) {
if(videoSurface->isActive()) {
videoSurface->stop();
}
}
}
}
bool EncoderBackend::signalPresent()
{
return _signalPresent;
}
void EncoderBackend::setSignalPresent(bool signalPresent)
{
_signalPresent = signalPresent;
Q_EMIT signalPresentChanged();
}
bool EncoderBackend::isRecording()
{
return _isRecording;
}
void EncoderBackend::setIsRecording(bool isRecording)
{
_isRecording = isRecording;
Q_EMIT isRecordingChanged();
}
void EncoderBackend::setSurface(QAbstractVideoSurface* surface)
{
if(videoSurface) {
if(videoSurface->isActive())
videoSurface->stop();
videoSurface = NULL;
}
videoSurface = surface;
if(videoSurface)
updateSignalStatus();
}
MediaInput *EncoderBackend::input()
{
return _mediaInput;
}
void EncoderBackend::setInput(MediaInput *mediaInput)
{
if (!mediaInput || !mediaInput->input())
return;
if (encoder && mediaInput == _mediaInput) {
// normally, this should only return
// for now, this makes for a nice way to stop capturing
destroyEncoder();
return;
}
destroyEncoder();
// this can return NULL...
encoder = Encoder::fromMxInput(mediaInput->input(), callbacks, "/data/staging");
if (encoder)
_mediaInput = mediaInput;
}
void EncoderBackend::RecordingStarted(std::string file)
{
UNUSED(file);
setProperty("isRecording", true);
Q_EMIT recordingStarted(QString::fromStdString(file));
}
void EncoderBackend::RecordingStopped(std::string file, bool wasSuccesful)
{
UNUSED(wasSuccesful);
setProperty("isRecording", false);
Q_EMIT recordingStopped(QString::fromStdString(file));
}
void EncoderBackend::SignalStatusChanged(const MxInputStatus &status)
{
{
QMutexLocker locker(&presentMutex);
*currentStatus = status;
if(currentSample != NULL)
{
encoder_sample_unref(currentSample);
currentSample = NULL;
}
}
QMetaObject::invokeMethod(this, "updateSignalStatus", Qt::QueuedConnection);
}
void EncoderBackend::StillCaptured(std::string file, bool wasSuccesful)
{
UNUSED(file);
UNUSED(wasSuccesful);
Q_EMIT stillCaptured(QString::fromStdString(file));
}
void EncoderBackend::SampleArrived(EncoderSample* sample)
{
{
QMutexLocker locker(&presentMutex);
if(currentSample != NULL)
{
encoder_sample_unref(currentSample);
currentSample = NULL;
}
currentSample = sample;
encoder_sample_ref(sample);
}
QMetaObject::invokeMethod(this, "presentFrame", Qt::QueuedConnection);
}
void EncoderBackend::destroyEncoder()
{
if (!encoder)
return;
delete encoder;
encoder = NULL;
_mediaInput = NULL;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment