summaryrefslogtreecommitdiff
blob: 5898e0a2b8830abe7245d8f1f320529873293c87 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
From 4b74d1b679855e8c709fde124fd9f0027ba8d916 Mon Sep 17 00:00:00 2001
From: Rob Clark <rob@ti.com>
Date: Fri, 27 Nov 2009 11:13:47 -0600
Subject: [PATCH 08/24] add basic support for I420->NV12 colorspace conversion

---
 gst/stride/gststridetransform.c |  109 ++++++++++++++++++++++++++++++++++++---
 gst/stride/gststridetransform.h |    2 +-
 2 files changed, 103 insertions(+), 8 deletions(-)

diff --git a/gst/stride/gststridetransform.c b/gst/stride/gststridetransform.c
index 56207d0..03deeb1 100644
--- a/gst/stride/gststridetransform.c
+++ b/gst/stride/gststridetransform.c
@@ -227,11 +227,43 @@ add_all_fields (GstCaps *caps, const gchar *name, GstStructure *s, gboolean rows
   idx = gst_structure_n_fields (s) - 1;
   while (idx >= 0) {
     const gchar *name = gst_structure_nth_field_name (s, idx);
+    idx--;
+    if (!strcmp ("format", name)) {
+      // we can do simple color format translations, such as converting from one
+      // YUV420 format to another:
+      GValue formats = {0};
+      GValue fourccval = {0};
+      guint fourcc;
+      if (gst_structure_get_fourcc (s, name, &fourcc)) {
+        switch (gst_video_format_from_fourcc (fourcc)) {
+          case GST_VIDEO_FORMAT_NV12:
+          case GST_VIDEO_FORMAT_I420:
+GST_DEBUG ("Hmm, let's say I can convert I420<-->NV12..");
+            g_value_init (&formats, GST_TYPE_LIST);
+            g_value_init (&fourccval, GST_TYPE_FOURCC);
+            gst_value_set_fourcc (&fourccval,
+                GST_MAKE_FOURCC ('I', '4', '2', '0'));
+            gst_value_list_append_value (&formats, &fourccval);
+            gst_value_set_fourcc (&fourccval,
+                GST_MAKE_FOURCC ('N', 'V', '1', '2'));
+            gst_value_list_append_value (&formats, &fourccval);
+            gst_structure_set_value (new_s, "format", &formats);
+            continue;
+/* maybe handle other cases later..
+          case GST_VIDEO_FORMAT_YV12:
+          case GST_VIDEO_FORMAT_YUY2:
+          case GST_VIDEO_FORMAT_UYVY:
+*/
+          default:
+            break;
+        }
+      }
+    }
+
     if (strcmp ("rowstride", name)) {
       const GValue *val = gst_structure_get_value (s, name);
       gst_structure_set_value (new_s, name, val);
     }
-    idx--;
   }
 
   gst_caps_merge_structure (caps, new_s);
@@ -287,18 +319,16 @@ gst_stride_transform_set_caps (GstBaseTransform *base,
     GstCaps *incaps, GstCaps *outcaps)
 {
   GstStrideTransform *self = GST_STRIDE_TRANSFORM (base);
-  GstVideoFormat format;
   gint width, height;
 
   LOG_CAPS (self, incaps);
   LOG_CAPS (self, outcaps);
 
   g_return_val_if_fail (gst_video_format_parse_caps_strided (incaps,
-      &self->format, &self->width, &self->height, &self->in_rowstride), FALSE);
+      &self->in_format, &self->width, &self->height, &self->in_rowstride), FALSE);
   g_return_val_if_fail (gst_video_format_parse_caps_strided (outcaps,
-      &format, &width, &height, &self->out_rowstride), FALSE);
+      &self->out_format, &width, &height, &self->out_rowstride), FALSE);
 
-  g_return_val_if_fail (self->format == format, FALSE);
   g_return_val_if_fail (self->width  == width,  FALSE);
   g_return_val_if_fail (self->height == height, FALSE);
 
@@ -307,6 +337,49 @@ gst_stride_transform_set_caps (GstBaseTransform *base,
 
 /* ************************************************************************* */
 
+static void
+memmove_demux (guchar *new_buf, guchar *orig_buf, gint sz, gint pxstride)
+{
+  if (new_buf > orig_buf) {
+    /* copy backwards */
+    new_buf += (sz * pxstride);
+    orig_buf += sz;
+    while(sz--) {
+      *new_buf = *orig_buf;
+      new_buf -= pxstride;
+      orig_buf--;
+    }
+  } else {
+    while(sz--) {
+      *new_buf = *orig_buf;
+      new_buf += pxstride;
+      orig_buf++;
+    }
+  }
+}
+
+static void
+stridemove_demux (guchar *new_buf, guchar *orig_buf, gint new_width, gint orig_width, gint height, gint pxstride)
+{
+  int row;
+
+  GST_DEBUG ("new_buf=%p, orig_buf=%p, new_width=%d, orig_width=%d, height=%d",
+      new_buf, orig_buf, new_width, orig_width, height);
+  /* if increasing the stride, work from bottom-up to avoid overwriting data
+   * that has not been moved yet.. otherwise, work in the opposite order,
+   * for the same reason.
+   */
+  if (new_width > orig_width) {
+    for (row=height-1; row>=0; row--) {
+      memmove_demux (new_buf+(new_width*row), orig_buf+(orig_width*row), orig_width, pxstride);
+    }
+  } else {
+    for (row=0; row<height; row++) {
+      memmove_demux (new_buf+(new_width*row), orig_buf+(orig_width*row), new_width, pxstride);
+    }
+  }
+}
+
 /**
  * Convert from one stride to another... like memmove, but can convert stride in
  * the process.  This function is not aware of pixels, only of bytes.  So widths
@@ -356,7 +429,29 @@ stridify (GstStrideTransform *self, guchar *strided, guchar *unstrided)
   gint height = self->height;
   gint stride = self->out_rowstride;
 
-  switch (self->format) {
+  if (self->out_format != self->in_format) {
+
+    if ((self->in_format == GST_VIDEO_FORMAT_I420) &&
+        (self->out_format == GST_VIDEO_FORMAT_NV12)) {
+      /* note: if not an in-place conversion, then doing the U&V in one pass
+       * would be more efficient... but if it is an in-place conversion, I'd
+       * need to think about whether it is potential for the new UV plane to
+       * corrupt the V plane before it is done copying..
+       */
+      stridemove_demux (
+          strided + (height*stride) + 1,
+          unstrided + (int)(height*width*1.25),
+          stride, width/2, height/2, 2);                        /* move V */
+      stridemove_demux (
+          strided + (height*stride),
+          unstrided + (height*width),
+          stride, width/2, height/2, 2);                        /* move U */
+      stridemove (strided, unstrided, stride, width, height);   /* move Y */
+      return GST_FLOW_OK;
+    }
+  }
+
+  switch (self->out_format) {
     case GST_VIDEO_FORMAT_NV12:
       g_return_val_if_fail (stride >= width, GST_FLOW_ERROR);
       stridemove (strided, unstrided, stride, width, (GST_ROUND_UP_2 (height) * 3) / 2);
@@ -400,7 +495,7 @@ unstridify (GstStrideTransform *self, guchar *unstrided, guchar *strided)
   gint height = self->height;
   gint stride = self->in_rowstride;
 
-  switch (self->format) {
+  switch (self->out_format) {
     case GST_VIDEO_FORMAT_NV12:
       g_return_val_if_fail (stride >= width, GST_FLOW_ERROR);
       stridemove (unstrided, strided, width, stride, (GST_ROUND_UP_2 (height) * 3) / 2);
diff --git a/gst/stride/gststridetransform.h b/gst/stride/gststridetransform.h
index 481959e..0141571 100644
--- a/gst/stride/gststridetransform.h
+++ b/gst/stride/gststridetransform.h
@@ -55,7 +55,7 @@ struct _GstStrideTransform {
   GstVideoFilter videofilter;
 
   /*< private >*/
-  GstVideoFormat format;
+  GstVideoFormat in_format, out_format;
   gint width, height;
   gint in_rowstride;
   gint out_rowstride;
-- 
1.7.1