Check resolution.height() also instead of checking resolution.width() twice
[harmattan/cameraplus] / lib / qtcammode.cpp
1 #include "qtcammode.h"
2 #include "qtcammode_p.h"
3 #include "qtcamdevice_p.h"
4 #include "qtcamdevice.h"
5 #include <QDebug>
6 #include "qtcamgstreamermessagehandler.h"
7 #include "qtcamgstreamermessagelistener.h"
8 #include <gst/video/video.h>
9 #include <QImage>
10
11 #define PREVIEW_CAPS "video/x-raw-rgb, width = (int) %1, height = (int) %2, bpp = (int) 32, depth = (int) 24, red_mask = (int) 65280, green_mask = (int) 16711680, blue_mask = (int) -16777216"
12
13 #define CAPS "video/x-raw-yuv, width = (int) %1, height = (int) %2, framerate = (fraction) %3/%4"
14
15 class PreviewImageHandler : public QtCamGStreamerMessageHandler {
16 public:
17   PreviewImageHandler(QtCamMode *m, QObject *parent = 0) :
18     QtCamGStreamerMessageHandler("preview-image", parent) {
19     mode = m;
20   }
21
22   virtual void handleMessage(GstMessage *message) {
23     const GstStructure *s = gst_message_get_structure(message);
24     if (!s) {
25       return;
26     }
27
28     const char *file = gst_structure_get_string(s, "location");
29     if (!file) {
30       return;
31     }
32
33     const GValue *val = gst_structure_get_value(s, "buffer");
34     if (!val) {
35       return;
36     }
37
38     GstBuffer *buffer = gst_value_get_buffer(val);
39     if (!buffer) {
40       return;
41     }
42
43     int width, height;
44     GstVideoFormat fmt;
45     if (!gst_video_format_parse_caps(buffer->caps, &fmt, &width, &height)) {
46       return;
47     }
48
49     if (fmt !=  GST_VIDEO_FORMAT_BGRx || width <= 0 || height <= 0) {
50       return;
51     }
52
53     QImage image(buffer->data, width, height, QImage::Format_RGB32);
54
55     // We need to copy because GStreamer will free the buffer after we return
56     // and since QImage doesn't copythe data by default we will end up with garbage.
57     // TODO: consider a QImage subclass that takes a GstBuffer reference
58     QImage cp = image.copy();
59
60     QString fileName = QString::fromUtf8(file);
61
62     QMetaObject::invokeMethod(mode, "previewAvailable",
63                               Q_ARG(QImage, cp), Q_ARG(QString, fileName));
64   }
65
66   QtCamMode *mode;
67 };
68
69 class DoneHandler : public QtCamGStreamerMessageHandler {
70 public:
71   DoneHandler(QtCamMode *m, const char *done, QObject *parent = 0) :
72     QtCamGStreamerMessageHandler(done, parent) {
73     mode = m;
74   }
75
76   virtual void handleMessage(GstMessage *message) {
77     const GstStructure *s = gst_message_get_structure(message);
78     if (gst_structure_has_field(s, "filename")) {
79       const char *str = gst_structure_get_string(s, "filename");
80       if (str) {
81         fileName = QString::fromUtf8(str);
82       }
83     }
84
85     QMetaObject::invokeMethod(mode, "saved", Q_ARG(QString, fileName));
86   }
87
88   QString fileName;
89   QtCamMode *mode;
90 };
91
92 QtCamMode::QtCamMode(QtCamModePrivate *d, const char *mode, const char *done, QObject *parent) :
93   QObject(parent), d_ptr(d) {
94
95   d_ptr->id = d_ptr->modeId(mode);
96   d_ptr->previewImageHandler = new PreviewImageHandler(this, this);
97   d_ptr->doneHandler = new DoneHandler(this, done, this);
98 }
99
100 QtCamMode::~QtCamMode() {
101   delete d_ptr; d_ptr = 0;
102 }
103
104 void QtCamMode::activate() {
105   if (!d_ptr->dev->cameraBin) {
106     return;
107   }
108
109   if (d_ptr->dev->active == this) {
110     return;
111   }
112
113   if (d_ptr->dev->active) {
114     d_ptr->dev->active->deactivate();
115   }
116
117   d_ptr->dev->active = this;
118
119   // TODO: check that we can actually do it. Perhaps the pipeline is busy.
120
121   g_object_set(d_ptr->dev->cameraBin, "mode", d_ptr->id, NULL);
122
123   d_ptr->dev->listener->addHandler(d_ptr->previewImageHandler);
124   d_ptr->dev->listener->addHandler(d_ptr->doneHandler);
125
126   start();
127
128   applySettings();
129 }
130
131 void QtCamMode::deactivate() {
132   if (d_ptr->dev->active != this) {
133     return;
134   }
135
136   d_ptr->dev->listener->removeHandler(d_ptr->previewImageHandler);
137   d_ptr->dev->listener->removeHandler(d_ptr->doneHandler);
138
139   d_ptr->previewImageHandler->setParent(this);
140   d_ptr->doneHandler->setParent(this);
141
142   stop();
143
144   d_ptr->dev->active = 0;
145 }
146
147 bool QtCamMode::canCapture() {
148   return d_ptr->dev->cameraBin && isActive() && d_ptr->dev->q_ptr->isRunning();
149 }
150
151 bool QtCamMode::isActive() {
152   return d_ptr->dev->active == this;
153 }
154
155 void QtCamMode::setCaps(const char *property, const QSize& resolution,
156                         const QPair<int, int> frameRate) {
157
158   if (!d_ptr->dev->cameraBin) {
159     return;
160   }
161
162   // TODO: allow proceeding without specifying a frame rate (maybe we can calculate it ?)
163   if (frameRate.first <= 0 || frameRate.second <= 0) {
164     return;
165   }
166
167   if (resolution.width() <= 0 || resolution.height() <= 0) {
168     return;
169   }
170
171   QString capsString = QString(CAPS)
172     .arg(resolution.width()).arg(resolution.height())
173     .arg(frameRate.first).arg(frameRate.second);
174
175   GstCaps *caps = gst_caps_from_string(capsString.toAscii());
176
177   g_object_set(d_ptr->dev->cameraBin, property, caps, NULL);
178
179   gst_caps_unref(caps);
180 }
181
182 void QtCamMode::setPreviewSize(const QSize& size) {
183   if (!d_ptr->dev->cameraBin) {
184     return;
185   }
186
187   if (size.width() <= 0 && size.height() <= 0) {
188     g_object_set(d_ptr->dev->cameraBin, "preview-caps", NULL, "post-previews", FALSE, NULL);
189   }
190   else {
191     QString preview = QString(PREVIEW_CAPS).arg(size.width()).arg(size.height());
192
193     GstCaps *caps = gst_caps_from_string(preview.toAscii());
194
195     g_object_set(d_ptr->dev->cameraBin, "preview-caps", caps, "post-previews", TRUE, NULL);
196
197     gst_caps_unref(caps);
198   }
199 }
200
201 void QtCamMode::setFileName(const QString& fileName) {
202   d_ptr->doneHandler->fileName = fileName;
203 }