终于又抽出时间来继续写总结了,上一次把抽象类总结以后,这次总结一下具体封装的索尼相机类的结构和实现。突然发现好像应该一开始先总结一下相机的控制流程来的,写完这一章立刻补上。
HttpSonyCamera的结构
HttpSonyCamera类图:
索尼相机类持有两个线程类,一个负责不断的获取相机返回的预览帧二进制流数据的线程类GetLiveViewStreamThread,另一个是处理发送命令以及命令返回解析的线程类CommandThread。相机通过继承函数回调,获得两个线程返回的数据,并通过回调函数将数据交付外部使用。
HttpSonyCamera的实现
HttpSonyCamera.h:
#ifndef HTTPSONYCAMERA_H
#define HTTPSONYCAMERA_H
#include <QQueue>
#include <QThread>
#include <unistd.h>
#include "setwhitebalanceexecutor.h"
#include "setisospeedrateexecutor.h"
#include "stopliveviewexecutor.h"
#include "startliveviewexecutor.h"
#include "startrecmodeexecutor.h"
#include "setliveviewframeinfoexecutor.h"
#include "getliveviewstreamexecutor.h"
#include "getpostviewpictureexecutor.h"
#include "acthalfpressshutterexecutor.h"
#include "acttakepictureexecutor.h"
#include "httpcamera.h"
#include "getliveviewstreamthread.h"
#include "commandthread.h"
#include "zoomadjustexecutor.h"
namespace xfhttpcamera {
class HttpSonyCamera : public HttpCamera, public GetLiveViewStreamThreadCallback, public CommandThreadCallback
{
Q_OBJECT
public:
HttpSonyCamera(HTTPConfigure *config, bool needStartRec = false ,QObject *parent = 0);
~HttpSonyCamera();
virtual void open();
virtual void close();
virtual void starPreview();
virtual void stopPreview();
virtual void capture();
virtual bool setIso(QString rate);
virtual void doAF();
virtual void cancelAF();
virtual void zoomInStart();
virtual void zoomInStop();
virtual void zoomInshort();
virtual void zoomOutStart();
virtual void zoomOutStop();
virtual void zoomOutShort();
virtual void setWhiteBalance(QString mode, bool enable, int colorTemperature);
virtual void liveViewError(sonyrx::GetLiveViewStreamExecutor *exec, QString error, int errorCode);
virtual void liveViewStopped(sonyrx::GetLiveViewStreamExecutor *exec);
virtual void liveViewDataGet(QByteArray &data);
virtual void commandError(HttpExecutor *exec, QString error, int errorCode);
virtual void commandSuccess(HttpExecutor *exec);
virtual void executeCommandData(HttpExecutor *exec, QVariant &data);
bool isOpen();
bool isLiveViewing();
signals:
void startLiveView(QUrl url);
void stopLiveView();
private slots:
void starLiveViewSlot(QUrl url);
void stopLiveViewSlot();
private:
GetLiveViewStreamThread *_downloadLiveViewThread = nullptr;
CommandThread *_commandThread = nullptr;
QNetworkAccessManager *_manager = nullptr;
bool _needStartRec = false;
bool _isOpen = false, _isLiveViewing = false, _isCapturing = false;
void stopDownloadThread();
void stopCommandThread();
};
}
#endif // HTTPSONYCAMERA_H
HttpSonyCamera.cpp:
#include "httpsonycamera.h"
namespace xfhttpcamera {
HttpSonyCamera::HttpSonyCamera(HTTPConfigure *config, bool needStartRec, QObject *parent):
HttpCamera(config, parent), _needStartRec(needStartRec)
{
qDebug() << "camera thread:" << QThread::currentThreadId();
_manager = new QNetworkAccessManager;
connect(this, &HttpSonyCamera::stopLiveView, this, &HttpSonyCamera::stopLiveViewSlot, Qt::ConnectionType::AutoConnection);
}
HttpSonyCamera::~HttpSonyCamera()
{
if(_manager){
_manager->deleteLater();
_manager = nullptr;
}
if(_downloadLiveViewThread){
stopDownloadThread();
}
if(_commandThread){
stopCommandThread();
}
}
void HttpSonyCamera::open()
{
//TODO
if(!_isOpen){
_commandThread = new CommandThread(_manager);
_commandThread->setCallback(this);
_commandThread->start();
_commandThread->moveToThread(_commandThread);
_manager->moveToThread(_commandThread);
if(_needStartRec){
sonyrx::StartRecModeExecutor *exec = new sonyrx::StartRecModeExecutor(_config);
_commandThread->enqueue(exec);
}
_isOpen = true;
sleep(1);
}else
_callback->cameraError(QString("Camera has already been opened!"), Try_Open_An_Opening_Camera);
}
void HttpSonyCamera::close()
{
//TODO
if(_isOpen){
if(_downloadLiveViewThread){
qDebug() << "stop download";
stopDownloadThread();
}
if(_commandThread){
qDebug() << "stop command";
stopCommandThread();
}
_isLiveViewing = false;
_isOpen = false;
}else
_callback->cameraError(QString("Camera needs to be opened!"), Try_Operate_On_A_Closed_Camera);
}
void HttpSonyCamera::starPreview()
{
if(_isOpen){
if(!_isLiveViewing){
connect(this, &HttpSonyCamera::startLiveView, this, &HttpSonyCamera::starLiveViewSlot, Qt::ConnectionType::QueuedConnection);
if(_needStartRec){
sonyrx::setLiveviewFrameInfoExecutor *exec2 = new sonyrx::setLiveviewFrameInfoExecutor(_config, true);
_commandThread->enqueue(exec2);
}
sonyrx::StartLiveviewExecutor *exec = new sonyrx::StartLiveviewExecutor(_config);
_commandThread->enqueue(exec);
_isLiveViewing = true;
}else
_callback->cameraError(QString("Camera is downloading live view now!"), Try_Start_Preview_While_Previewing);
}else
_callback->cameraError(QString("Camera needs to be opened!"), Try_Operate_On_A_Closed_Camera);
}
void HttpSonyCamera::stopPreview()
{
if(_isOpen){
if(_isLiveViewing){
disconnect(this, &HttpSonyCamera::startLiveView, this, &HttpSonyCamera::starLiveViewSlot);
sonyrx::StopLiveViewExecutor *exec = new sonyrx::StopLiveViewExecutor(_config);
_commandThread->enqueue(exec);
}else
_callback->cameraError(QString("Camera is not downloading live view now!"), Try_Stop_Preview_While_Isnot_Previewing);
}else
_callback->cameraError(QString("Camera needs to be opened!"), Try_Operate_On_A_Closed_Camera);
}
void HttpSonyCamera::capture()
{
if(_isOpen){
if(!_isCapturing){
sonyrx::ActHalfPressShutterExecutor *exec1 = new sonyrx::ActHalfPressShutterExecutor(_config);
_commandThread->enqueue(exec1);
sonyrx::ActTakePictureExecutor *exec2 = new sonyrx::ActTakePictureExecutor(_config);
_commandThread->enqueue(exec2);
_isCapturing = true;
}else
_callback->cameraError((QString("Camera is capturing, please wait for a while!")), Try_Capture_While_Dealing_Capture);
}else
_callback->cameraError(QString("Camera needs to be opened!"), Try_Operate_On_A_Closed_Camera);
}
bool HttpSonyCamera::setIso(QString rate)
{
if(_isOpen){
sonyrx::SetIsoSpeedRateExecutor *exec = new sonyrx::SetIsoSpeedRateExecutor(_config, rate);
_commandThread->enqueue(exec);
}else
_callback->cameraError(QString("Camera needs to be opened!"), Try_Operate_On_A_Closed_Camera);
}
void HttpSonyCamera::doAF()
{
//TODO
}
void HttpSonyCamera::cancelAF()
{
//TODO
}
void HttpSonyCamera::zoomInStart()
{
if(_isOpen){
sonyrx::ZoomAdjustExecutor *exec = new sonyrx::ZoomAdjustExecutor(_config, "in", "start");
_commandThread->enqueue(exec);
}else
_callback->cameraError(QString("Camera needs to be opened!"), Try_Operate_On_A_Closed_Camera);
}
void HttpSonyCamera::zoomInStop()
{
if(_isOpen){
sonyrx::ZoomAdjustExecutor *exec = new sonyrx::ZoomAdjustExecutor(_config, "in", "stop");
_commandThread->enqueue(exec);
}else
_callback->cameraError(QString("Camera needs to be opened!"), Try_Operate_On_A_Closed_Camera);
}
void HttpSonyCamera::zoomInshort()
{
if(_isOpen){
sonyrx::ZoomAdjustExecutor *exec = new sonyrx::ZoomAdjustExecutor(_config, "in", "1shot");
_commandThread->enqueue(exec);
}else
_callback->cameraError(QString("Camera needs to be opened!"), Try_Operate_On_A_Closed_Camera);
}
void HttpSonyCamera::zoomOutStart()
{
if(_isOpen){
sonyrx::ZoomAdjustExecutor *exec = new sonyrx::ZoomAdjustExecutor(_config, "out", "start");
_commandThread->enqueue(exec);
}else
_callback->cameraError(QString("Camera needs to be opened!"), Try_Operate_On_A_Closed_Camera);
}
void HttpSonyCamera::zoomOutStop()
{
if(_isOpen){
sonyrx::ZoomAdjustExecutor *exec = new sonyrx::ZoomAdjustExecutor(_config, "out", "stop");
_commandThread->enqueue(exec);
}else
_callback->cameraError(QString("Camera needs to be opened!"), Try_Operate_On_A_Closed_Camera);
}
void HttpSonyCamera::zoomOutShort()
{
if(_isOpen){
sonyrx::ZoomAdjustExecutor *exec = new sonyrx::ZoomAdjustExecutor(_config, "out", "1shot");
_commandThread->enqueue(exec);
}else
_callback->cameraError(QString("Camera needs to be opened!"), Try_Operate_On_A_Closed_Camera);
}
void HttpSonyCamera::setWhiteBalance(QString mode, bool enable, int colorTemperature)
{
if(_isOpen){
sonyrx::SetWhiteBalanceExecutor *exec = new sonyrx::SetWhiteBalanceExecutor(_config, mode, enable, colorTemperature);
_commandThread->enqueue(exec);
}else
_callback->cameraError(QString("Camera needs to be opened!"), Try_Operate_On_A_Closed_Camera);
}
void HttpSonyCamera::liveViewError(sonyrx::GetLiveViewStreamExecutor *exec, QString error, int errorCode)
{
stopDownloadThread();
exec->deleteLater();
_callback->cameraError(error, errorCode);
}
void HttpSonyCamera::liveViewStopped(sonyrx::GetLiveViewStreamExecutor *exec)
{
emit stopLiveView();
exec->deleteLater();
}
void HttpSonyCamera::liveViewDataGet(QByteArray &data)
{
_callback->newPreviewFrameGet(data);
}
void HttpSonyCamera::commandError(HttpExecutor *exec, QString error, int errorCode)
{
stopCommandThread();
exec->deleteLater();
_callback->cameraError(error, errorCode);
}
void HttpSonyCamera::commandSuccess(HttpExecutor *exec)
{
if(dynamic_cast<sonyrx::StopLiveViewExecutor*>(exec)){
qDebug() << "emiting...";
emit stopLiveView();
}else if(dynamic_cast<sonyrx::ZoomAdjustExecutor*>(exec)){
sonyrx::ZoomAdjustExecutor* exec2 = static_cast<sonyrx::ZoomAdjustExecutor*>(exec);
if(exec2->state() == "stop")
_callback->cameraZoomStopped();
}
exec->deleteLater();
}
void HttpSonyCamera::executeCommandData(HttpExecutor *exec, QVariant &data)
{
if(dynamic_cast<sonyrx::StartLiveviewExecutor*>(exec)){
QString urlString = data.toString();
QUrl url(urlString);
qDebug() << "url:" << url;
emit startLiveView(url);
} else if(dynamic_cast<sonyrx::ActTakePictureExecutor*>(exec)){
QString urlString = data.toString();
QUrl url(urlString);
HTTPConfigure config(url.host(),url.port(), "", url.scheme());
sonyrx::GetPostViewPictureExecutor *postViewExec = new sonyrx::GetPostViewPictureExecutor(&config, "", url);
_commandThread->enqueue(postViewExec);
} else if(dynamic_cast<sonyrx::GetPostViewPictureExecutor*>(exec)){
QByteArray imageData(data.toByteArray());
_callback->newCaptureGet(imageData);
_isCapturing = false;
}
}
bool HttpSonyCamera::isOpen()
{
return _isOpen;
}
bool HttpSonyCamera::isLiveViewing()
{
return _isLiveViewing;
}
void HttpSonyCamera::starLiveViewSlot(QUrl url)
{
HTTPConfigure config(url.host(),url.port(), "", url.scheme());
sonyrx::GetLiveViewStreamExecutor *liveViewExec = new sonyrx::GetLiveViewStreamExecutor(&config, "", url);
_downloadLiveViewThread = new GetLiveViewStreamThread(liveViewExec, _manager);
_downloadLiveViewThread->setCallback(this);
// _manager->moveToThread(_downloadLiveViewThread);
_downloadLiveViewThread->start();
}
void HttpSonyCamera::stopLiveViewSlot()
{
qDebug() << "stopped!";
if(_downloadLiveViewThread)
stopDownloadThread();
}
void HttpSonyCamera::stopDownloadThread()
{
if(_isLiveViewing){
_downloadLiveViewThread->exit();
_downloadLiveViewThread->wait();
_downloadLiveViewThread->deleteLater();
_downloadLiveViewThread = nullptr;
_isLiveViewing = false;
}
}
void HttpSonyCamera::stopCommandThread()
{
_commandThread->_commandWaitCondition.wakeAll();
_commandThread->exit();
_commandThread->wait();
_commandThread->deleteLater();
_commandThread = nullptr;
}
}
再总结一下索尼相机的控制流程:
命令交互:封装json,通过http post发送指令,接收返回结果。
相机主要流程:
1. open:启动CommandThread。由于几乎所有索尼相机的命令都是一样的(与ptp控制不同,索尼还是厉害),唯一的区别在于,是否需要StartRecMode这个命令。因此,在相机类实例化的时候,CameraFactory需要根据型号来决定,传入需要StartRecMode的bool类型的参数,对于不需要的相机,这一步不是必要的。
2. startPreview:这一步要发送一个startLiveView的命令给相机,相机将会返回一个liveviewstream的url给我们,这时候启动GetLiveViewStreamThread,在线程中用get方法去访问这个地址,从而不断的获取预览帧的数据。
3. capture:这一步发送一个ActHalfPressShutter和ActTakePicture的命令,就是半按快门以及拍照的命令,拍照成功之后相机会返回照片的url,去get这个地址就可以获取到照片的二进制流数据了。
4. setProperty:设置ISO速率,白平衡,自动对角等属性设置,就直接将相关参数发送给相机,相机会返回设置成功与否的结果。
总结
相机类这部分主要是解决以上这些操作的调度问题,负责将各个命令的处理器类交付给线程处理,并将线程处理好的结果通过回调返回给外部。
对于stopPreview的命令是有问题的,到现在暂时都还没有解决,在stop了以后,再startPreview,返回来的预览帧地址收不到任何数据,因此目前对于停止预览的问题,是我外部客户端在需要停止预览的时候,无视掉回调里面的预览帧数据,也就是说实际上还在一直接收预览帧,只是不处理丢掉而已。
同时还有很多状态机来保护相机,避免错误的函数调用导致和相机通讯的崩溃。