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 */
76void 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
131void 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
181void 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
259static 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
272void 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