이전 글에서 OpenCV와 QThread를 활용해 Tab7 카메라 탭을 구현했습니다.
이번 글에서는 mjpg-streamer를 Qt 앱 내에서 직접 실행하고, QWebEngineView로 스트림을 표시하는 Tab6WebCamera를 구현하는 과정을 정리합니다.
mjpg-streamer 설치 및 브라우저에서의 스트리밍 확인은 이전 글들을 참고 부탁드립니다!

교수님이 제공하신 mjpg-streamer-master.zip을 홈 디렉토리에서 압축 해제합니다. 관련 github 링크도 첨부해둘테니 여기서 가져오셔도 됩니다.
https://github.com/jacksonliam/mjpg-streamer
cd ~
unzip mjpg-streamer-master.zip
cd mjpg-streamer-master
make
sudo make install
빌드 완료 후 디렉토리 구조를 확인합니다.
ls ~/mjpg-streamer-master
mjpg_streamer input_uvc.so output_http.so www/ start.sh ...
mjpg_streamer 실행 파일과 input_uvc.so, output_http.so, www/ 가 모두 같은 디렉토리에 위치합니다.
start.sh에서export LD_LIBRARY_PATH="$(pwd)"를 설정하는 이유도.so플러그인 파일들이 시스템 경로가 아닌 같은 디렉토리에 있기 때문입니다.
터미널에서 직접 실행해 정상 동작을 확인합니다.

cd ~/mjpg-streamer-master
vi start.sh # 필요 시 인증 옵션 확인
bash start.sh
# start.sh 실행 시 아래 명령어가 자동으로 실행됩니다
# export LD_LIBRARY_PATH="$(pwd)"
# ./mjpg_streamer -i "./input_uvc.so" -o "./output_http.so -w ./www"
브라우저에서 http://10.10.16.35:8080에 접속해 스트리밍이 출력되면 정상입니다.
AiotClient 프로젝트 우클릭 → Add New → Qt → Qt Widgets Designer Form Class → Widget 선택
Class name을 Tab6WebCamera로 입력하면 아래 파일이 자동 생성됩니다.
tab6webcamera.htab6webcamera.cpptab6webcamera.uitab6webcamera.ui를 Qt Designer에서 열고 아래와 같이 위젯을 배치합니다.

=====================================
| |
| pGPView |
| (QGraphicsView) |
| |
|-----------------------------------|
| pPBCamStart | pPBsnapShot |
=====================================
레이아웃 비율은 카메라 뷰 9 : 버튼 영역 1로 설정합니다.
| 위젯 | objectName | 역할 |
|---|---|---|
| QGraphicsView | pGPView | 초기 이미지 및 스트림 영역 |
| QPushButton | pPBCamStart | 카메라 시작/정지 (Checkable) |
| QPushButton | pPBsnapShot | 스냅샷 저장 |
pPBCamStart는 Property Editor에서checkable을 체크해야 토글 버튼으로 동작합니다.
Tab6는 QWebEngineView와 QProcess를 멤버로 가집니다.
QWebEngineView는 생성자에서 동적으로 생성하고, CamStart 버튼 클릭 시 pGPView 위에 올립니다.
#ifndef TAB6WEBCAMERA_H
#define TAB6WEBCAMERA_H
#include <QWidget>
#include <QWebEngineView>
#include <QProcess>
#include <QGraphicsPixmapItem>
#include <QThread>
#include <QGraphicsScene>
namespace Ui {
class Tab6WebCamera;
}
class Tab6WebCamera : public QWidget
{
Q_OBJECT
public:
explicit Tab6WebCamera(QWidget *parent = nullptr);
~Tab6WebCamera();
private:
Ui::Tab6WebCamera *ui;
QWebEngineView *pQWebEngineView;
QProcess *pQProcess;
QUrl webcamUrl;
QGraphicsScene initDisplayScene;
private slots:
void camStartSlot(bool);
void on_pPBsnapShot_clicked();
};
#endif // TAB6WEBCAMERA_H
생성자에서 스트림 URL을 설정하고, QProcess와 QWebEngineView를 생성합니다.
초기 화면은 QGraphicsScene에 initDisplay_2.png를 추가해 pGPView에 표시합니다.
Tab6WebCamera::Tab6WebCamera(QWidget *parent) :
QWidget(parent),
ui(new Ui::Tab6WebCamera)
{
ui->setupUi(this);
webcamUrl = QUrl("http://10.10.16.35:8080/?action=stream");
webcamUrl.setUserName("user");
webcamUrl.setPassword("1234");
pQProcess = new QProcess(this);
pQWebEngineView = new QWebEngineView(this);
QPixmap pixMap(":/Images/Images/initDisplay_2.png");
QGraphicsScene* scene = new QGraphicsScene(ui->pGPView);
scene->addPixmap(pixMap);
ui->pGPView->setScene(scene);
connect(ui->pPBCamStart, SIGNAL(clicked(bool)), this, SLOT(camStartSlot(bool)));
}
스트림 URL에
setUserName,setPassword를 설정하면 mjpg-streamer의-c user:1234인증을 자동으로 처리합니다.

Tab6의 동작 원리는 다음과 같습니다.
CamStart 시 QProcess가 ubuntu05 로컬에서 mjpg-streamer를 실행합니다. mjpg-streamer는 포트 8080을 열고 HTTP 서버 역할을 시작하고, QWebEngineView가 해당 주소에 접속해 스트림을 표시합니다.
CamStop 시 pQProcess->kill()로 mjpg-streamer 프로세스를 강제 종료합니다. 포트 8080을 열고 있던 프로세스가 종료되면 서버 자체가 사라지므로 스트리밍이 중단됩니다.
CamStart → QProcess로 mjpg-streamer 실행 → 포트 8080 오픈 → QWebEngineView로 스트림 접속
CamStop → QProcess kill → mjpg-streamer 종료 → 포트 8080 닫힘
실행 파일과 .so 플러그인이 모두 같은 디렉토리에 있기 때문에 경로를 절대경로로 지정합니다.
프로세스가 정상적으로 시작되면 200ms 대기 후 QWebEngineView에 스트림 URL을 로드하고, pGPView의 자식 위젯으로 올린 뒤 기존 QGraphicsScene을 제거합니다.
void Tab6WebCamera::camStartSlot(bool bCheck)
{
QString webcamProgrm = "/home/ubuntu/mjpg-streamer-master/mjpg_streamer";
QStringList webcamArg = {
"-i", "/home/ubuntu/mjpg-streamer-master/input_uvc.so",
"-o", "/home/ubuntu/mjpg-streamer-master/output_http.so "
"-w /home/ubuntu/mjpg-streamer-master/www -c user:1234"
};
if(bCheck)
{
pQProcess->start(webcamProgrm, webcamArg);
if(pQProcess->waitForStarted())
{
QThread::msleep(200);
pQWebEngineView->load(webcamUrl);
pQWebEngineView->saveGeometry();
ui->pPBCamStart->setText("CamStop");
pQWebEngineView->setParent(ui->pGPView);
pQWebEngineView->setGeometry(ui->pGPView->rect());
pQWebEngineView->show();
if(ui->pGPView->scene())
ui->pGPView->setScene(nullptr);
}
}
else
{
pQProcess->kill();
pQWebEngineView->stop();
pQWebEngineView->hide();
ui->pPBCamStart->setText("CamStart");
QGraphicsScene* oldScene = ui->pGPView->scene();
if(oldScene)
{
ui->pGPView->setScene(nullptr);
delete oldScene;
}
QPixmap pixMap(":/Images/Images/initDisplay_2.png");
QGraphicsScene* scene = new QGraphicsScene(ui->pGPView);
scene->addPixmap(pixMap);
ui->pGPView->setScene(scene);
}
}
setGeometry(ui->pGPView->rect())로QWebEngineView크기를pGPView영역에 맞게 설정합니다.
현재 Snapshot 기능은 미구현 상태입니다.
mjpg-streamer의 snapshot URL(?action=snapshot)을 wget으로 저장하는 방식으로 추후 구현할 예정입니다.
void Tab6WebCamera::on_pPBsnapShot_clicked()
{
//wget -O a.jpg http://10.10.16.35:8080/?action=snapshot
}
#include <tab6webcamera.h>
Tab6WebCamera *pTab6WebCamera;
pTab6WebCamera = new Tab6WebCamera(ui->pTab6);
ui->pTab6->setLayout(pTab6WebCamera->layout());
Tab6는 Tab7과 달리 소켓 연동이 없으므로 별도 Signal/Slot 연결이 필요하지 않습니다.
| Tab6 | Tab7 | |
|---|---|---|
| 카메라 처리 | mjpg-streamer (QProcess로 외부 실행) | OpenCV VideoCapture (직접) |
| 화면 표시 | QWebEngineView (HTTP 스트림) | QLabel + QPixmap |
| 스레드 | 없음 | QThread |
| RGB 분류 | 없음 | HSV 기반 색상 분류 |
| 소켓 연동 | 없음 | 있음 |
mjpg-streamer-master.zip 압축 해제 → make → sudo make install
↓
터미널에서 직접 실행 후 브라우저 스트리밍 확인
↓
Tab6WebCamera 클래스 생성 (Qt Widgets Designer Form Class)
↓
UI 구성 (QGraphicsView + QPushButton x2)
↓
생성자 : 스트림 URL 설정, QProcess/QWebEngineView 생성, 초기 이미지 표시
↓
CamStart ON : QProcess로 mjpg-streamer 실행 → 포트 8080 오픈 → QWebEngineView로 스트림 접속
↓
QWebEngineView를 pGPView 위에 자식 위젯으로 올리기
↓
CamStop : QProcess kill → mjpg-streamer 종료 → QWebEngineView hide → 초기 이미지 복원
↓
mainwidget에 Tab6 추가