summaryrefslogtreecommitdiffstats
path: root/src/gsttools/qgstutils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gsttools/qgstutils.cpp')
-rw-r--r--src/gsttools/qgstutils.cpp785
1 files changed, 778 insertions, 7 deletions
diff --git a/src/gsttools/qgstutils.cpp b/src/gsttools/qgstutils.cpp
index 556fc03cc..65124c931 100644
--- a/src/gsttools/qgstutils.cpp
+++ b/src/gsttools/qgstutils.cpp
@@ -40,7 +40,14 @@
#include <QtCore/qsize.h>
#include <QtCore/qset.h>
#include <QtCore/qstringlist.h>
+#include <QtGui/qimage.h>
#include <qaudioformat.h>
+#include <QtMultimedia/qvideosurfaceformat.h>
+
+#include <gst/audio/audio.h>
+#include <gst/video/video.h>
+
+template<typename T, int N> static int lengthOf(const T (&)[N]) { return N; }
#ifdef USE_V4L
# include <private/qcore_unix_p.h>
@@ -82,15 +89,24 @@ static void addTagToMap(const GstTagList *list,
map->insert(QByteArray(tag), g_value_get_boolean(&val));
break;
case G_TYPE_CHAR:
+#if GLIB_CHECK_VERSION(2,32,0)
+ map->insert(QByteArray(tag), g_value_get_schar(&val));
+#else
map->insert(QByteArray(tag), g_value_get_char(&val));
+#endif
break;
case G_TYPE_DOUBLE:
map->insert(QByteArray(tag), g_value_get_double(&val));
break;
default:
// GST_TYPE_DATE is a function, not a constant, so pull it out of the switch
+#if GST_CHECK_VERSION(1,0,0)
+ if (G_VALUE_TYPE(&val) == G_TYPE_DATE) {
+ const GDate *date = (const GDate *)g_value_get_boxed(&val);
+#else
if (G_VALUE_TYPE(&val) == GST_TYPE_DATE) {
const GDate *date = gst_value_get_date(&val);
+#endif
if (g_date_valid(date)) {
int year = g_date_get_year(date);
int month = g_date_get_month(date);
@@ -169,6 +185,42 @@ QSize QGstUtils::capsCorrectedResolution(const GstCaps *caps)
return size;
}
+
+#if GST_CHECK_VERSION(1,0,0)
+namespace {
+
+struct AudioFormat
+{
+ GstAudioFormat format;
+ QAudioFormat::SampleType sampleType;
+ QAudioFormat::Endian byteOrder;
+ int sampleSize;
+};
+static const AudioFormat qt_audioLookup[] =
+{
+ { GST_AUDIO_FORMAT_S8 , QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 8 },
+ { GST_AUDIO_FORMAT_U8 , QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 8 },
+ { GST_AUDIO_FORMAT_S16LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 16 },
+ { GST_AUDIO_FORMAT_S16BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 16 },
+ { GST_AUDIO_FORMAT_U16LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 16 },
+ { GST_AUDIO_FORMAT_U16BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 16 },
+ { GST_AUDIO_FORMAT_S32LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 32 },
+ { GST_AUDIO_FORMAT_S32BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 32 },
+ { GST_AUDIO_FORMAT_U32LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 32 },
+ { GST_AUDIO_FORMAT_U32BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 32 },
+ { GST_AUDIO_FORMAT_S24LE, QAudioFormat::SignedInt , QAudioFormat::LittleEndian, 24 },
+ { GST_AUDIO_FORMAT_S24BE, QAudioFormat::SignedInt , QAudioFormat::BigEndian , 24 },
+ { GST_AUDIO_FORMAT_U24LE, QAudioFormat::UnSignedInt, QAudioFormat::LittleEndian, 24 },
+ { GST_AUDIO_FORMAT_U24BE, QAudioFormat::UnSignedInt, QAudioFormat::BigEndian , 24 },
+ { GST_AUDIO_FORMAT_F32LE, QAudioFormat::Float , QAudioFormat::LittleEndian, 32 },
+ { GST_AUDIO_FORMAT_F32BE, QAudioFormat::Float , QAudioFormat::BigEndian , 32 },
+ { GST_AUDIO_FORMAT_F64LE, QAudioFormat::Float , QAudioFormat::LittleEndian, 64 },
+ { GST_AUDIO_FORMAT_F64BE, QAudioFormat::Float , QAudioFormat::BigEndian , 64 }
+};
+
+}
+#endif
+
/*!
Returns audio format for caps.
If caps doesn't have a valid audio format, an empty QAudioFormat is returned.
@@ -176,9 +228,26 @@ QSize QGstUtils::capsCorrectedResolution(const GstCaps *caps)
QAudioFormat QGstUtils::audioFormatForCaps(const GstCaps *caps)
{
- const GstStructure *structure = gst_caps_get_structure(caps, 0);
-
QAudioFormat format;
+#if GST_CHECK_VERSION(1,0,0)
+ GstAudioInfo info;
+ if (gst_audio_info_from_caps(&info, caps)) {
+ for (int i = 0; i < lengthOf(qt_audioLookup); ++i) {
+ if (qt_audioLookup[i].format != info.finfo->format)
+ continue;
+
+ format.setSampleType(qt_audioLookup[i].sampleType);
+ format.setByteOrder(qt_audioLookup[i].byteOrder);
+ format.setSampleSize(qt_audioLookup[i].sampleSize);
+ format.setSampleRate(info.rate);
+ format.setChannelCount(info.channels);
+ format.setCodec(QStringLiteral("audio/pcm"));
+
+ return format;
+ }
+ }
+#else
+ const GstStructure *structure = gst_caps_get_structure(caps, 0);
if (qstrcmp(gst_structure_get_name(structure), "audio/x-raw-int") == 0) {
@@ -249,16 +318,28 @@ QAudioFormat QGstUtils::audioFormatForCaps(const GstCaps *caps)
} else {
return QAudioFormat();
}
-
+#endif
return format;
}
+#if GST_CHECK_VERSION(1,0,0)
+/*!
+ Returns audio format for a sample.
+ If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned.
+*/
+QAudioFormat QGstUtils::audioFormatForSample(GstSample *sample)
+{
+ GstCaps* caps = gst_sample_get_caps(sample);
+ if (!caps)
+ return QAudioFormat();
+ return QGstUtils::audioFormatForCaps(caps);
+}
+#else
/*!
Returns audio format for a buffer.
If the buffer doesn't have a valid audio format, an empty QAudioFormat is returned.
*/
-
QAudioFormat QGstUtils::audioFormatForBuffer(GstBuffer *buffer)
{
GstCaps* caps = gst_buffer_get_caps(buffer);
@@ -269,7 +350,7 @@ QAudioFormat QGstUtils::audioFormatForBuffer(GstBuffer *buffer)
gst_caps_unref(caps);
return format;
}
-
+#endif
/*!
Builds GstCaps for an audio format.
@@ -277,8 +358,32 @@ QAudioFormat QGstUtils::audioFormatForBuffer(GstBuffer *buffer)
Caller must unref GstCaps.
*/
-GstCaps *QGstUtils::capsForAudioFormat(QAudioFormat format)
+GstCaps *QGstUtils::capsForAudioFormat(const QAudioFormat &format)
{
+ if (!format.isValid())
+ return 0;
+
+#if GST_CHECK_VERSION(1,0,0)
+ const QAudioFormat::SampleType sampleType = format.sampleType();
+ const QAudioFormat::Endian byteOrder = format.byteOrder();
+ const int sampleSize = format.sampleSize();
+
+ for (int i = 0; i < lengthOf(qt_audioLookup); ++i) {
+ if (qt_audioLookup[i].sampleType != sampleType
+ || qt_audioLookup[i].byteOrder != byteOrder
+ || qt_audioLookup[i].sampleSize != sampleSize) {
+ continue;
+ }
+
+ return gst_caps_new_simple(
+ "audio/x-raw",
+ "format" , G_TYPE_STRING, gst_audio_format_to_string(qt_audioLookup[i].format),
+ "rate" , G_TYPE_INT , format.sampleRate(),
+ "channels", G_TYPE_INT , format.channelCount(),
+ NULL);
+ }
+ return 0;
+#else
GstStructure *structure = 0;
if (format.isValid()) {
@@ -313,6 +418,7 @@ GstCaps *QGstUtils::capsForAudioFormat(QAudioFormat format)
}
return caps;
+#endif
}
void QGstUtils::initializeGst()
@@ -576,10 +682,629 @@ QByteArray QGstUtils::cameraDriver(const QString &device, GstElementFactory *fac
return QByteArray();
}
+QSet<QString> QGstUtils::supportedMimeTypes(bool (*isValidFactory)(GstElementFactory *factory))
+{
+ QSet<QString> supportedMimeTypes;
+
+ //enumerate supported mime types
+ gst_init(NULL, NULL);
+
+#if GST_CHECK_VERSION(1,0,0)
+ GstRegistry *registry = gst_registry_get();
+ GList *orig_plugins = gst_registry_get_plugin_list(registry);
+#else
+ GstRegistry *registry = gst_registry_get_default();
+ GList *orig_plugins = gst_default_registry_get_plugin_list ();
+#endif
+ for (GList *plugins = orig_plugins; plugins; plugins = g_list_next(plugins)) {
+ GstPlugin *plugin = (GstPlugin *) (plugins->data);
+#if GST_CHECK_VERSION(1,0,0)
+ if (GST_OBJECT_FLAG_IS_SET(GST_OBJECT(plugin), GST_PLUGIN_FLAG_BLACKLISTED))
+ continue;
+#else
+ if (plugin->flags & (1<<1)) //GST_PLUGIN_FLAG_BLACKLISTED
+ continue;
+#endif
+
+ GList *orig_features = gst_registry_get_feature_list_by_plugin(
+ registry, gst_plugin_get_name(plugin));
+ for (GList *features = orig_features; features; features = g_list_next(features)) {
+ if (G_UNLIKELY(features->data == NULL))
+ continue;
+
+ GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data);
+ GstElementFactory *factory;
+
+ if (GST_IS_TYPE_FIND_FACTORY(feature)) {
+ QString name(gst_plugin_feature_get_name(feature));
+ if (name.contains('/')) //filter out any string without '/' which is obviously not a mime type
+ supportedMimeTypes.insert(name.toLower());
+ continue;
+ } else if (!GST_IS_ELEMENT_FACTORY (feature)
+ || !(factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)))) {
+ continue;
+ } else if (!isValidFactory(factory)) {
+ // Do nothing
+ } else for (const GList *pads = gst_element_factory_get_static_pad_templates(factory);
+ pads;
+ pads = g_list_next(pads)) {
+ GstStaticPadTemplate *padtemplate = static_cast<GstStaticPadTemplate *>(pads->data);
+
+ if (padtemplate->direction == GST_PAD_SINK && padtemplate->static_caps.string) {
+ GstCaps *caps = gst_static_caps_get(&padtemplate->static_caps);
+ if (gst_caps_is_any(caps) || gst_caps_is_empty(caps)) {
+ } else for (guint i = 0; i < gst_caps_get_size(caps); i++) {
+ GstStructure *structure = gst_caps_get_structure(caps, i);
+ QString nameLowcase = QString(gst_structure_get_name(structure)).toLower();
+
+ supportedMimeTypes.insert(nameLowcase);
+ if (nameLowcase.contains("mpeg")) {
+ //Because mpeg version number is only included in the detail
+ //description, it is necessary to manually extract this information
+ //in order to match the mime type of mpeg4.
+ const GValue *value = gst_structure_get_value(structure, "mpegversion");
+ if (value) {
+ gchar *str = gst_value_serialize(value);
+ QString versions(str);
+ QStringList elements = versions.split(QRegExp("\\D+"), QString::SkipEmptyParts);
+ foreach (const QString &e, elements)
+ supportedMimeTypes.insert(nameLowcase + e);
+ g_free(str);
+ }
+ }
+ }
+ }
+ }
+ gst_object_unref(factory);
+ }
+ gst_plugin_feature_list_free(orig_features);
+ }
+ gst_plugin_list_free (orig_plugins);
+
+#if defined QT_SUPPORTEDMIMETYPES_DEBUG
+ QStringList list = supportedMimeTypes.toList();
+ list.sort();
+ if (qgetenv("QT_DEBUG_PLUGINS").toInt() > 0) {
+ foreach (const QString &type, list)
+ qDebug() << type;
+ }
+#endif
+ return supportedMimeTypes;
+}
+
+namespace {
+
+struct ColorFormat { QImage::Format imageFormat; GstVideoFormat gstFormat; };
+static const ColorFormat qt_colorLookup[] =
+{
+ { QImage::Format_RGBX8888, GST_VIDEO_FORMAT_RGBx },
+ { QImage::Format_RGBA8888, GST_VIDEO_FORMAT_RGBA },
+ { QImage::Format_RGB888 , GST_VIDEO_FORMAT_RGB },
+ { QImage::Format_RGB16 , GST_VIDEO_FORMAT_RGB16 }
+};
+
+}
+
+#if GST_CHECK_VERSION(1,0,0)
+QImage QGstUtils::bufferToImage(GstBuffer *buffer, const GstVideoInfo &videoInfo)
+#else
+QImage QGstUtils::bufferToImage(GstBuffer *buffer)
+#endif
+{
+ QImage img;
+
+#if GST_CHECK_VERSION(1,0,0)
+ GstVideoInfo info = videoInfo;
+ GstVideoFrame frame;
+ if (!gst_video_frame_map(&frame, &info, buffer, GST_MAP_READ))
+ return img;
+#else
+ GstCaps *caps = gst_buffer_get_caps(buffer);
+ if (!caps)
+ return img;
+
+ GstStructure *structure = gst_caps_get_structure (caps, 0);
+ gint width = 0;
+ gint height = 0;
+
+ if (!structure
+ || !gst_structure_get_int(structure, "width", &width)
+ || !gst_structure_get_int(structure, "height", &height)
+ || width <= 0
+ || height <= 0) {
+ gst_caps_unref(caps);
+ return img;
+ }
+ gst_caps_unref(caps);
+#endif
+
+#if GST_CHECK_VERSION(1,0,0)
+ if (videoInfo.finfo->format == GST_VIDEO_FORMAT_I420) {
+ const int width = videoInfo.width;
+ const int height = videoInfo.height;
+
+ const int stride[] = { frame.info.stride[0], frame.info.stride[1], frame.info.stride[2] };
+ const uchar *data[] = {
+ static_cast<const uchar *>(frame.data[0]),
+ static_cast<const uchar *>(frame.data[1]),
+ static_cast<const uchar *>(frame.data[2])
+ };
+#else
+ if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) {
+ const int stride[] = { width, width / 2, width / 2 };
+ const uchar *data[] = {
+ (const uchar *)buffer->data,
+ (const uchar *)buffer->data + width * height,
+ (const uchar *)buffer->data + width * height * 5 / 4
+ };
+#endif
+ img = QImage(width/2, height/2, QImage::Format_RGB32);
+
+ for (int y=0; y<height; y+=2) {
+ const uchar *yLine = data[0] + (y * stride[0]);
+ const uchar *uLine = data[1] + (y * stride[1] / 2);
+ const uchar *vLine = data[2] + (y * stride[2] / 2);
+
+ for (int x=0; x<width; x+=2) {
+ const qreal Y = 1.164*(yLine[x]-16);
+ const int U = uLine[x/2]-128;
+ const int V = vLine[x/2]-128;
+
+ int b = qBound(0, int(Y + 2.018*U), 255);
+ int g = qBound(0, int(Y - 0.813*V - 0.391*U), 255);
+ int r = qBound(0, int(Y + 1.596*V), 255);
+
+ img.setPixel(x/2,y/2,qRgb(r,g,b));
+ }
+ }
+#if GST_CHECK_VERSION(1,0,0)
+ } else for (int i = 0; i < lengthOf(qt_colorLookup); ++i) {
+ if (qt_colorLookup[i].gstFormat != videoInfo.finfo->format)
+ continue;
+
+ const QImage image(
+ static_cast<const uchar *>(frame.data[0]),
+ videoInfo.width,
+ videoInfo.height,
+ frame.info.stride[0],
+ qt_colorLookup[i].imageFormat);
+ img = image;
+ img.detach();
+
+ break;
+ }
+
+ gst_video_frame_unmap(&frame);
+#else
+ } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) {
+ QImage::Format format = QImage::Format_Invalid;
+ int bpp = 0;
+ gst_structure_get_int(structure, "bpp", &bpp);
+
+ if (bpp == 24)
+ format = QImage::Format_RGB888;
+ else if (bpp == 32)
+ format = QImage::Format_RGB32;
+
+ if (format != QImage::Format_Invalid) {
+ img = QImage((const uchar *)buffer->data,
+ width,
+ height,
+ format);
+ img.bits(); //detach
+ }
+ }
+#endif
+ return img;
+}
+
+
+namespace {
+
+#if GST_CHECK_VERSION(1,0,0)
+
+struct VideoFormat
+{
+ QVideoFrame::PixelFormat pixelFormat;
+ GstVideoFormat gstFormat;
+};
+
+static const VideoFormat qt_videoFormatLookup[] =
+{
+ { QVideoFrame::Format_YUV420P, GST_VIDEO_FORMAT_I420 },
+ { QVideoFrame::Format_YV12 , GST_VIDEO_FORMAT_YV12 },
+ { QVideoFrame::Format_UYVY , GST_VIDEO_FORMAT_UYVY },
+ { QVideoFrame::Format_YUYV , GST_VIDEO_FORMAT_YUY2 },
+ { QVideoFrame::Format_NV12 , GST_VIDEO_FORMAT_NV12 },
+ { QVideoFrame::Format_NV21 , GST_VIDEO_FORMAT_NV21 },
+ { QVideoFrame::Format_AYUV444, GST_VIDEO_FORMAT_AYUV },
+#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
+ { QVideoFrame::Format_RGB32 , GST_VIDEO_FORMAT_BGRx },
+ { QVideoFrame::Format_BGR32 , GST_VIDEO_FORMAT_RGBx },
+ { QVideoFrame::Format_ARGB32, GST_VIDEO_FORMAT_BGRA },
+ { QVideoFrame::Format_BGRA32, GST_VIDEO_FORMAT_ARGB },
+#else
+ { QVideoFrame::Format_RGB32 , GST_VIDEO_FORMAT_xRGB },
+ { QVideoFrame::Format_BGR32 , GST_VIDEO_FORMAT_xBGR },
+ { QVideoFrame::Format_ARGB32, GST_VIDEO_FORMAT_ARGB },
+ { QVideoFrame::Format_BGRA32, GST_VIDEO_FORMAT_BGRA },
+#endif
+ { QVideoFrame::Format_RGB24 , GST_VIDEO_FORMAT_RGB },
+ { QVideoFrame::Format_BGR24 , GST_VIDEO_FORMAT_BGR },
+ { QVideoFrame::Format_RGB565, GST_VIDEO_FORMAT_RGB16 }
+};
+
+static int indexOfVideoFormat(QVideoFrame::PixelFormat format)
+{
+ for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i)
+ if (qt_videoFormatLookup[i].pixelFormat == format)
+ return i;
+
+ return -1;
+}
+
+static int indexOfVideoFormat(GstVideoFormat format)
+{
+ for (int i = 0; i < lengthOf(qt_videoFormatLookup); ++i)
+ if (qt_videoFormatLookup[i].gstFormat == format)
+ return i;
+
+ return -1;
+}
+
+#else
+
+struct YuvFormat
+{
+ QVideoFrame::PixelFormat pixelFormat;
+ guint32 fourcc;
+ int bitsPerPixel;
+};
+
+static const YuvFormat qt_yuvColorLookup[] =
+{
+ { QVideoFrame::Format_YUV420P, GST_MAKE_FOURCC('I','4','2','0'), 8 },
+ { QVideoFrame::Format_YV12, GST_MAKE_FOURCC('Y','V','1','2'), 8 },
+ { QVideoFrame::Format_UYVY, GST_MAKE_FOURCC('U','Y','V','Y'), 16 },
+ { QVideoFrame::Format_YUYV, GST_MAKE_FOURCC('Y','U','Y','2'), 16 },
+ { QVideoFrame::Format_NV12, GST_MAKE_FOURCC('N','V','1','2'), 8 },
+ { QVideoFrame::Format_NV21, GST_MAKE_FOURCC('N','V','2','1'), 8 },
+ { QVideoFrame::Format_AYUV444, GST_MAKE_FOURCC('A','Y','U','V'), 32 }
+};
+
+static int indexOfYuvColor(QVideoFrame::PixelFormat format)
+{
+ const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat);
+
+ for (int i = 0; i < count; ++i)
+ if (qt_yuvColorLookup[i].pixelFormat == format)
+ return i;
+
+ return -1;
+}
+
+static int indexOfYuvColor(guint32 fourcc)
+{
+ const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat);
+
+ for (int i = 0; i < count; ++i)
+ if (qt_yuvColorLookup[i].fourcc == fourcc)
+ return i;
+
+ return -1;
+}
+
+struct RgbFormat
+{
+ QVideoFrame::PixelFormat pixelFormat;
+ int bitsPerPixel;
+ int depth;
+ int endianness;
+ int red;
+ int green;
+ int blue;
+ int alpha;
+};
+
+static const RgbFormat qt_rgbColorLookup[] =
+{
+ { QVideoFrame::Format_RGB32 , 32, 24, 4321, 0x0000FF00, 0x00FF0000, int(0xFF000000), 0x00000000 },
+ { QVideoFrame::Format_RGB32 , 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 },
+ { QVideoFrame::Format_BGR32 , 32, 24, 4321, int(0xFF000000), 0x00FF0000, 0x0000FF00, 0x00000000 },
+ { QVideoFrame::Format_BGR32 , 32, 24, 1234, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 },
+ { QVideoFrame::Format_ARGB32, 32, 24, 4321, 0x0000FF00, 0x00FF0000, int(0xFF000000), 0x000000FF },
+ { QVideoFrame::Format_ARGB32, 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, int(0xFF000000) },
+ { QVideoFrame::Format_RGB24 , 24, 24, 4321, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 },
+ { QVideoFrame::Format_BGR24 , 24, 24, 4321, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 },
+ { QVideoFrame::Format_RGB565, 16, 16, 1234, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 }
+};
+
+static int indexOfRgbColor(
+ int bits, int depth, int endianness, int red, int green, int blue, int alpha)
+{
+ const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat);
+
+ for (int i = 0; i < count; ++i) {
+ if (qt_rgbColorLookup[i].bitsPerPixel == bits
+ && qt_rgbColorLookup[i].depth == depth
+ && qt_rgbColorLookup[i].endianness == endianness
+ && qt_rgbColorLookup[i].red == red
+ && qt_rgbColorLookup[i].green == green
+ && qt_rgbColorLookup[i].blue == blue
+ && qt_rgbColorLookup[i].alpha == alpha) {
+ return i;
+ }
+ }
+ return -1;
+}
+#endif
+
+}
+
+#if GST_CHECK_VERSION(1,0,0)
+
+QVideoSurfaceFormat QGstUtils::formatForCaps(
+ GstCaps *caps, GstVideoInfo *info, QAbstractVideoBuffer::HandleType handleType)
+{
+ if (gst_video_info_from_caps(info, caps)) {
+ int index = indexOfVideoFormat(info->finfo->format);
+
+ if (index != -1) {
+ QVideoSurfaceFormat format(
+ QSize(info->width, info->height),
+ qt_videoFormatLookup[index].pixelFormat,
+ handleType);
+
+ if (info->fps_d > 0)
+ format.setFrameRate(qreal(info->fps_d) / info->fps_n);
+
+ if (info->par_d > 0)
+ format.setPixelAspectRatio(info->par_n, info->par_d);
+
+ return format;
+ }
+ }
+ return QVideoSurfaceFormat();
+}
+
+#else
+
+QVideoSurfaceFormat QGstUtils::formatForCaps(
+ GstCaps *caps, int *bytesPerLine, QAbstractVideoBuffer::HandleType handleType)
+{
+ const GstStructure *structure = gst_caps_get_structure(caps, 0);
+
+ QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid;
+ int bitsPerPixel = 0;
+
+ QSize size;
+ gst_structure_get_int(structure, "width", &size.rwidth());
+ gst_structure_get_int(structure, "height", &size.rheight());
+
+ if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) {
+ guint32 fourcc = 0;
+ gst_structure_get_fourcc(structure, "format", &fourcc);
+
+ int index = indexOfYuvColor(fourcc);
+ if (index != -1) {
+ pixelFormat = qt_yuvColorLookup[index].pixelFormat;
+ bitsPerPixel = qt_yuvColorLookup[index].bitsPerPixel;
+ }
+ } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) {
+ int depth = 0;
+ int endianness = 0;
+ int red = 0;
+ int green = 0;
+ int blue = 0;
+ int alpha = 0;
+
+ gst_structure_get_int(structure, "bpp", &bitsPerPixel);
+ gst_structure_get_int(structure, "depth", &depth);
+ gst_structure_get_int(structure, "endianness", &endianness);
+ gst_structure_get_int(structure, "red_mask", &red);
+ gst_structure_get_int(structure, "green_mask", &green);
+ gst_structure_get_int(structure, "blue_mask", &blue);
+ gst_structure_get_int(structure, "alpha_mask", &alpha);
+
+ int index = indexOfRgbColor(bitsPerPixel, depth, endianness, red, green, blue, alpha);
+
+ if (index != -1)
+ pixelFormat = qt_rgbColorLookup[index].pixelFormat;
+ }
+
+ if (pixelFormat != QVideoFrame::Format_Invalid) {
+ QVideoSurfaceFormat format(size, pixelFormat, handleType);
+
+ QPair<int, int> rate;
+ gst_structure_get_fraction(structure, "framerate", &rate.first, &rate.second);
+
+ if (rate.second)
+ format.setFrameRate(qreal(rate.first)/rate.second);
+
+ gint aspectNum = 0;
+ gint aspectDenum = 0;
+ if (gst_structure_get_fraction(
+ structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) {
+ if (aspectDenum > 0)
+ format.setPixelAspectRatio(aspectNum, aspectDenum);
+ }
+
+ if (bytesPerLine)
+ *bytesPerLine = ((size.width() * bitsPerPixel / 8) + 3) & ~3;
+
+ return format;
+ }
+ return QVideoSurfaceFormat();
+}
+
+#endif
+
+GstCaps *QGstUtils::capsForFormats(const QList<QVideoFrame::PixelFormat> &formats)
+{
+ GstCaps *caps = gst_caps_new_empty();
+
+#if GST_CHECK_VERSION(1,0,0)
+ foreach (QVideoFrame::PixelFormat format, formats) {
+ int index = indexOfVideoFormat(format);
+
+ if (index != -1) {
+ gst_caps_append_structure(caps, gst_structure_new(
+ "video/x-raw",
+ "format" , G_TYPE_STRING, gst_video_format_to_string(qt_videoFormatLookup[index].gstFormat),
+ NULL));
+ }
+ }
+#else
+ foreach (QVideoFrame::PixelFormat format, formats) {
+ int index = indexOfYuvColor(format);
+
+ if (index != -1) {
+ gst_caps_append_structure(caps, gst_structure_new(
+ "video/x-raw-yuv",
+ "format", GST_TYPE_FOURCC, qt_yuvColorLookup[index].fourcc,
+ NULL));
+ continue;
+ }
+
+ const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat);
+
+ for (int i = 0; i < count; ++i) {
+ if (qt_rgbColorLookup[i].pixelFormat == format) {
+ GstStructure *structure = gst_structure_new(
+ "video/x-raw-rgb",
+ "bpp" , G_TYPE_INT, qt_rgbColorLookup[i].bitsPerPixel,
+ "depth" , G_TYPE_INT, qt_rgbColorLookup[i].depth,
+ "endianness", G_TYPE_INT, qt_rgbColorLookup[i].endianness,
+ "red_mask" , G_TYPE_INT, qt_rgbColorLookup[i].red,
+ "green_mask", G_TYPE_INT, qt_rgbColorLookup[i].green,
+ "blue_mask" , G_TYPE_INT, qt_rgbColorLookup[i].blue,
+ NULL);
+
+ if (qt_rgbColorLookup[i].alpha != 0) {
+ gst_structure_set(
+ structure, "alpha_mask", G_TYPE_INT, qt_rgbColorLookup[i].alpha, NULL);
+ }
+ gst_caps_append_structure(caps, structure);
+ }
+ }
+ }
+#endif
+
+ gst_caps_set_simple(
+ caps,
+ "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1,
+ "width" , GST_TYPE_INT_RANGE, 1, INT_MAX,
+ "height" , GST_TYPE_INT_RANGE, 1, INT_MAX,
+ NULL);
+
+ return caps;
+}
+
+void QGstUtils::setFrameTimeStamps(QVideoFrame *frame, GstBuffer *buffer)
+{
+ // GStreamer uses nanoseconds, Qt uses microseconds
+ qint64 startTime = GST_BUFFER_TIMESTAMP(buffer);
+ if (startTime >= 0) {
+ frame->setStartTime(startTime/G_GINT64_CONSTANT (1000));
+
+ qint64 duration = GST_BUFFER_DURATION(buffer);
+ if (duration >= 0)
+ frame->setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000));
+ }
+}
+
+void QGstUtils::setMetaData(GstElement *element, const QMap<QByteArray, QVariant> &data)
+{
+ if (!GST_IS_TAG_SETTER(element))
+ return;
+
+ gst_tag_setter_reset_tags(GST_TAG_SETTER(element));
+
+ QMapIterator<QByteArray, QVariant> it(data);
+ while (it.hasNext()) {
+ it.next();
+ const QString tagName = it.key();
+ const QVariant tagValue = it.value();
+
+ switch (tagValue.type()) {
+ case QVariant::String:
+ gst_tag_setter_add_tags(GST_TAG_SETTER(element),
+ GST_TAG_MERGE_REPLACE,
+ tagName.toUtf8().constData(),
+ tagValue.toString().toUtf8().constData(),
+ NULL);
+ break;
+ case QVariant::Int:
+ case QVariant::LongLong:
+ gst_tag_setter_add_tags(GST_TAG_SETTER(element),
+ GST_TAG_MERGE_REPLACE,
+ tagName.toUtf8().constData(),
+ tagValue.toInt(),
+ NULL);
+ break;
+ case QVariant::Double:
+ gst_tag_setter_add_tags(GST_TAG_SETTER(element),
+ GST_TAG_MERGE_REPLACE,
+ tagName.toUtf8().constData(),
+ tagValue.toDouble(),
+ NULL);
+ break;
+ case QVariant::DateTime: {
+ QDateTime date = tagValue.toDateTime().toLocalTime();
+ gst_tag_setter_add_tags(GST_TAG_SETTER(element),
+ GST_TAG_MERGE_REPLACE,
+ tagName.toUtf8().constData(),
+ gst_date_time_new_local_time(
+ date.date().year(), date.date().month(), date.date().day(),
+ date.time().hour(), date.time().minute(), date.time().second()),
+ NULL);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+void QGstUtils::setMetaData(GstBin *bin, const QMap<QByteArray, QVariant> &data)
+{
+ GstIterator *elements = gst_bin_iterate_all_by_interface(bin, GST_TYPE_TAG_SETTER);
+#if GST_CHECK_VERSION(1,0,0)
+ GValue item = G_VALUE_INIT;
+ while (gst_iterator_next(elements, &item) == GST_ITERATOR_OK) {
+ GstElement * const element = GST_ELEMENT(g_value_get_object(&item));
+#else
+ GstElement *element = 0;
+ while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) {
+#endif
+ setMetaData(element, data);
+ }
+ gst_iterator_free(elements);
+}
+
+
+GstCaps *QGstUtils::videoFilterCaps()
+{
+ static GstStaticCaps staticCaps = GST_STATIC_CAPS(
+#if GST_CHECK_VERSION(1,2,0)
+ "video/x-raw(ANY);"
+#elif GST_CHECK_VERSION(1,0,0)
+ "video/x-raw;"
+#else
+ "video/x-raw-yuv;"
+ "video/x-raw-rgb;"
+ "video/x-raw-data;"
+ "video/x-android-buffer;"
+#endif
+ "image/jpeg;"
+ "video/x-h264");
+
+ return gst_caps_make_writable(gst_static_caps_get(&staticCaps));
+}
void qt_gst_object_ref_sink(gpointer object)
{
-#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 24)
+#if GST_CHECK_VERSION(0,10,24)
gst_object_ref_sink(object);
#else
g_return_if_fail (GST_IS_OBJECT(object));
@@ -595,4 +1320,50 @@ void qt_gst_object_ref_sink(gpointer object)
#endif
}
+GstCaps *qt_gst_pad_get_current_caps(GstPad *pad)
+{
+#if GST_CHECK_VERSION(1,0,0)
+ return gst_pad_get_current_caps(pad);
+#else
+ return gst_pad_get_negotiated_caps(pad);
+#endif
+}
+
+GstStructure *qt_gst_structure_new_empty(const char *name)
+{
+#if GST_CHECK_VERSION(1,0,0)
+ return gst_structure_new_empty(name);
+#else
+ return gst_structure_new(name, NULL);
+#endif
+}
+
+gboolean qt_gst_element_query_position(GstElement *element, GstFormat format, gint64 *cur)
+{
+#if GST_CHECK_VERSION(1,0,0)
+ return gst_element_query_position(element, format, cur);
+#else
+ return gst_element_query_position(element, &format, cur);
+#endif
+}
+
+gboolean qt_gst_element_query_duration(GstElement *element, GstFormat format, gint64 *cur)
+{
+#if GST_CHECK_VERSION(1,0,0)
+ return gst_element_query_duration(element, format, cur);
+#else
+ return gst_element_query_duration(element, &format, cur);
+#endif
+}
+
+QDebug operator <<(QDebug debug, GstCaps *caps)
+{
+ if (caps) {
+ gchar *string = gst_caps_to_string(caps);
+ debug = debug << string;
+ g_free(string);
+ }
+ return debug;
+}
+
QT_END_NAMESPACE