blob: 92428e85f02c39902d0718ec9bdd0de5a08084b5
1 | /* |
2 | * Android MediaCodec software buffer copy functions |
3 | * |
4 | * Copyright (c) 2015-2016 Matthieu Bouron <matthieu.bouron stupeflix.com> |
5 | * |
6 | * This file is part of FFmpeg. |
7 | * |
8 | * FFmpeg is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2.1 of the License, or (at your option) any later version. |
12 | * |
13 | * FFmpeg is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with FFmpeg; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | */ |
22 | |
23 | #include <string.h> |
24 | #include <sys/types.h> |
25 | |
26 | #include "libavutil/frame.h" |
27 | #include "libavutil/mem.h" |
28 | |
29 | #include "avcodec.h" |
30 | #include "mediacodec_wrapper.h" |
31 | #include "mediacodec_sw_buffer.h" |
32 | #include "mediacodecdec_common.h" |
33 | |
34 | #define QCOM_TILE_WIDTH 64 |
35 | #define QCOM_TILE_HEIGHT 32 |
36 | #define QCOM_TILE_SIZE (QCOM_TILE_WIDTH * QCOM_TILE_HEIGHT) |
37 | #define QCOM_TILE_GROUP_SIZE (4 * QCOM_TILE_SIZE) |
38 | |
39 | /** |
40 | * The code handling the various YUV color formats is taken from the |
41 | * GStreamer project. |
42 | * |
43 | * Gstreamer reference: |
44 | * https://cgit.freedesktop.org/gstreamer/gst-plugins-bad/tree/sys/androidmedia/ |
45 | * |
46 | * Copyright (C) 2012, Collabora Ltd. |
47 | * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk> |
48 | * |
49 | * Copyright (C) 2012, Rafaël Carré <funman@videolanorg> |
50 | * |
51 | * Copyright (C) 2015, Sebastian Dröge <sebastian@centricular.com> |
52 | * |
53 | * Copyright (C) 2014-2015, Collabora Ltd. |
54 | * Author: Matthieu Bouron <matthieu.bouron@gcollabora.com> |
55 | * |
56 | * Copyright (C) 2015, Edward Hervey |
57 | * Author: Edward Hervey <bilboed@gmail.com> |
58 | * |
59 | * Copyright (C) 2015, Matthew Waters <matthew@centricular.com> |
60 | * |
61 | * This library is free software; you can redistribute it and/or |
62 | * modify it under the terms of the GNU Lesser General Public |
63 | * License as published by the Free Software Foundation |
64 | * version 2.1 of the License. |
65 | * |
66 | * This library is distributed in the hope that it will be useful, |
67 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
68 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
69 | * Lesser General Public License for more details. |
70 | * |
71 | * You should have received a copy of the GNU Lesser General Public |
72 | * License along with this library; if not, write to the Free Software |
73 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
74 | * |
75 | */ |
76 | void ff_mediacodec_sw_buffer_copy_yuv420_planar(AVCodecContext *avctx, |
77 | MediaCodecDecContext *s, |
78 | uint8_t *data, |
79 | size_t size, |
80 | FFAMediaCodecBufferInfo *info, |
81 | AVFrame *frame) |
82 | { |
83 | int i; |
84 | uint8_t *src = NULL; |
85 | |
86 | for (i = 0; i < 3; i++) { |
87 | int stride = s->stride; |
88 | int height; |
89 | |
90 | src = data + info->offset; |
91 | if (i == 0) { |
92 | height = avctx->height; |
93 | |
94 | src += s->crop_top * s->stride; |
95 | src += s->crop_left; |
96 | } else { |
97 | height = avctx->height / 2; |
98 | stride = (s->stride + 1) / 2; |
99 | |
100 | src += s->slice_height * s->stride; |
101 | |
102 | if (i == 2) { |
103 | src += ((s->slice_height + 1) / 2) * stride; |
104 | } |
105 | |
106 | src += s->crop_top * stride; |
107 | src += (s->crop_left / 2); |
108 | } |
109 | |
110 | if (frame->linesize[i] == stride) { |
111 | memcpy(frame->data[i], src, height * stride); |
112 | } else { |
113 | int j, width; |
114 | uint8_t *dst = frame->data[i]; |
115 | |
116 | if (i == 0) { |
117 | width = avctx->width; |
118 | } else if (i >= 1) { |
119 | width = FFMIN(frame->linesize[i], FFALIGN(avctx->width, 2) / 2); |
120 | } |
121 | |
122 | for (j = 0; j < height; j++) { |
123 | memcpy(dst, src, width); |
124 | src += stride; |
125 | dst += frame->linesize[i]; |
126 | } |
127 | } |
128 | } |
129 | } |
130 | |
131 | void ff_mediacodec_sw_buffer_copy_yuv420_semi_planar(AVCodecContext *avctx, |
132 | MediaCodecDecContext *s, |
133 | uint8_t *data, |
134 | size_t size, |
135 | FFAMediaCodecBufferInfo *info, |
136 | AVFrame *frame) |
137 | { |
138 | int i; |
139 | uint8_t *src = NULL; |
140 | |
141 | for (i = 0; i < 2; i++) { |
142 | int height; |
143 | |
144 | src = data + info->offset; |
145 | if (i == 0) { |
146 | height = avctx->height; |
147 | |
148 | src += s->crop_top * s->stride; |
149 | src += s->crop_left; |
150 | } else if (i == 1) { |
151 | height = avctx->height / 2; |
152 | |
153 | src += s->slice_height * s->stride; |
154 | src += s->crop_top * s->stride; |
155 | src += s->crop_left; |
156 | } |
157 | |
158 | if (frame->linesize[i] == s->stride) { |
159 | memcpy(frame->data[i], src, height * s->stride); |
160 | } else { |
161 | int j, width; |
162 | uint8_t *dst = frame->data[i]; |
163 | |
164 | if (i == 0) { |
165 | width = avctx->width; |
166 | } else if (i == 1) { |
167 | width = FFMIN(frame->linesize[i], FFALIGN(avctx->width, 2)); |
168 | } |
169 | |
170 | for (j = 0; j < height; j++) { |
171 | memcpy(dst, src, width); |
172 | src += s->stride; |
173 | dst += frame->linesize[i]; |
174 | } |
175 | } |
176 | } |
177 | } |
178 | |
179 | |
180 | |
181 | void ff_mediacodec_sw_buffer_copy_yuv420_packed_semi_planar(AVCodecContext *avctx, |
182 | MediaCodecDecContext *s, |
183 | uint8_t *data, |
184 | size_t size, |
185 | FFAMediaCodecBufferInfo *info, |
186 | AVFrame *frame) |
187 | { |
188 | int i; |
189 | uint8_t *src = NULL; |
190 | |
191 | for (i = 0; i < 2; i++) { |
192 | int height; |
193 | |
194 | src = data + info->offset; |
195 | if (i == 0) { |
196 | height = avctx->height; |
197 | } else if (i == 1) { |
198 | height = avctx->height / 2; |
199 | |
200 | src += (s->slice_height - s->crop_top / 2) * s->stride; |
201 | |
202 | src += s->crop_top * s->stride; |
203 | src += s->crop_left; |
204 | } |
205 | |
206 | if (frame->linesize[i] == s->stride) { |
207 | memcpy(frame->data[i], src, height * s->stride); |
208 | } else { |
209 | int j, width; |
210 | uint8_t *dst = frame->data[i]; |
211 | |
212 | if (i == 0) { |
213 | width = avctx->width; |
214 | } else if (i == 1) { |
215 | width = FFMIN(frame->linesize[i], FFALIGN(avctx->width, 2)); |
216 | } |
217 | |
218 | for (j = 0; j < height; j++) { |
219 | memcpy(dst, src, width); |
220 | src += s->stride; |
221 | dst += frame->linesize[i]; |
222 | } |
223 | } |
224 | } |
225 | } |
226 | |
227 | /** |
228 | * The code handling the QCOM_FormatYUV420PackedSemiPlanar64x32Tile2m8ka |
229 | * color format is taken from the VLC project. |
230 | * |
231 | * VLC reference: |
232 | * http://git.videolan.org/?p=vlc.git;a=blob;f=modules/codec/omxil/qcom.c;hb=HEAD |
233 | * |
234 | * VLC copyright notice: |
235 | * |
236 | ***************************************************************************** |
237 | * qcom.c : pixel format translation for Qualcomm tiled nv12 |
238 | ***************************************************************************** |
239 | * Copyright © 2012 Rafaël Carré |
240 | * |
241 | * Authors: Rafaël Carré <funman@videolanorg> |
242 | * |
243 | * This program is free software; you can redistribute it and/or modify it |
244 | * under the terms of the GNU Lesser General Public License as published by |
245 | * the Free Software Foundation; either version 2.1 of the License, or |
246 | * (at your option) any later version. |
247 | * |
248 | * This program is distributed in the hope that it will be useful, |
249 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
250 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
251 | * GNU Lesser General Public License for more details. |
252 | * |
253 | * You should have received a copy of the GNU Lesser General Public License |
254 | * along with this program; if not, write to the Free Software Foundation, |
255 | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
256 | * |
257 | */ |
258 | |
259 | static size_t qcom_tile_pos(size_t x, size_t y, size_t w, size_t h) |
260 | { |
261 | size_t flim = x + (y & ~1) * w; |
262 | |
263 | if (y & 1) { |
264 | flim += (x & ~3) + 2; |
265 | } else if ((h & 1) == 0 || y != (h - 1)) { |
266 | flim += (x + 2) & ~3; |
267 | } |
268 | |
269 | return flim; |
270 | } |
271 | |
272 | void ff_mediacodec_sw_buffer_copy_yuv420_packed_semi_planar_64x32Tile2m8ka(AVCodecContext *avctx, |
273 | MediaCodecDecContext *s, |
274 | uint8_t *data, |
275 | size_t size, |
276 | FFAMediaCodecBufferInfo *info, |
277 | AVFrame *frame) |
278 | { |
279 | size_t width = frame->width; |
280 | size_t linesize = frame->linesize[0]; |
281 | size_t height = frame->height; |
282 | |
283 | const size_t tile_w = (width - 1) / QCOM_TILE_WIDTH + 1; |
284 | const size_t tile_w_align = (tile_w + 1) & ~1; |
285 | const size_t tile_h_luma = (height - 1) / QCOM_TILE_HEIGHT + 1; |
286 | const size_t tile_h_chroma = (height / 2 - 1) / QCOM_TILE_HEIGHT + 1; |
287 | |
288 | size_t luma_size = tile_w_align * tile_h_luma * QCOM_TILE_SIZE; |
289 | if((luma_size % QCOM_TILE_GROUP_SIZE) != 0) |
290 | luma_size = (((luma_size - 1) / QCOM_TILE_GROUP_SIZE) + 1) * QCOM_TILE_GROUP_SIZE; |
291 | |
292 | for(size_t y = 0; y < tile_h_luma; y++) { |
293 | size_t row_width = width; |
294 | for(size_t x = 0; x < tile_w; x++) { |
295 | size_t tile_width = row_width; |
296 | size_t tile_height = height; |
297 | /* dest luma memory index for this tile */ |
298 | size_t luma_idx = y * QCOM_TILE_HEIGHT * linesize + x * QCOM_TILE_WIDTH; |
299 | /* dest chroma memory index for this tile */ |
300 | /* XXX: remove divisions */ |
301 | size_t chroma_idx = (luma_idx / linesize) * linesize / 2 + (luma_idx % linesize); |
302 | |
303 | /* luma source pointer for this tile */ |
304 | const uint8_t *src_luma = data |
305 | + qcom_tile_pos(x, y,tile_w_align, tile_h_luma) * QCOM_TILE_SIZE; |
306 | |
307 | /* chroma source pointer for this tile */ |
308 | const uint8_t *src_chroma = data + luma_size |
309 | + qcom_tile_pos(x, y/2, tile_w_align, tile_h_chroma) * QCOM_TILE_SIZE; |
310 | if (y & 1) |
311 | src_chroma += QCOM_TILE_SIZE/2; |
312 | |
313 | /* account for right columns */ |
314 | if (tile_width > QCOM_TILE_WIDTH) |
315 | tile_width = QCOM_TILE_WIDTH; |
316 | |
317 | /* account for bottom rows */ |
318 | if (tile_height > QCOM_TILE_HEIGHT) |
319 | tile_height = QCOM_TILE_HEIGHT; |
320 | |
321 | tile_height /= 2; |
322 | while (tile_height--) { |
323 | memcpy(frame->data[0] + luma_idx, src_luma, tile_width); |
324 | src_luma += QCOM_TILE_WIDTH; |
325 | luma_idx += linesize; |
326 | |
327 | memcpy(frame->data[0] + luma_idx, src_luma, tile_width); |
328 | src_luma += QCOM_TILE_WIDTH; |
329 | luma_idx += linesize; |
330 | |
331 | memcpy(frame->data[1] + chroma_idx, src_chroma, tile_width); |
332 | src_chroma += QCOM_TILE_WIDTH; |
333 | chroma_idx += linesize; |
334 | } |
335 | row_width -= QCOM_TILE_WIDTH; |
336 | } |
337 | height -= QCOM_TILE_HEIGHT; |
338 | } |
339 | } |
340 |