终于又抽出时间来继续写总结了,上一次把抽象类总结以后,这次总结一下具体封装的索尼相机类的结构和实现。突然发现好像应该一开始先总结一下相机的控制流程来的,写完这一章立刻补上。

HttpSonyCamera的结构

        HttpSonyCamera类图:

        

用python控制sony相机拍照 sony 相机控制_Qt

        索尼相机类持有两个线程类,一个负责不断的获取相机返回的预览帧二进制流数据的线程类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,返回来的预览帧地址收不到任何数据,因此目前对于停止预览的问题,是我外部客户端在需要停止预览的时候,无视掉回调里面的预览帧数据,也就是说实际上还在一直接收预览帧,只是不处理丢掉而已。

        同时还有很多状态机来保护相机,避免错误的函数调用导致和相机通讯的崩溃。