From 4737fe6907d2693d044d29ca06510cc6fcbec544 Mon Sep 17 00:00:00 2001 From: Matthieu Bouron Date: Thu, 21 Jan 2016 09:29:39 +0100 Subject: lavc: add h264 mediacodec decoder --- libavcodec/mediacodec_sw_buffer.c | 339 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 339 insertions(+) create mode 100644 libavcodec/mediacodec_sw_buffer.c (limited to 'libavcodec/mediacodec_sw_buffer.c') diff --git a/libavcodec/mediacodec_sw_buffer.c b/libavcodec/mediacodec_sw_buffer.c new file mode 100644 index 0000000000..df75754ba2 --- /dev/null +++ b/libavcodec/mediacodec_sw_buffer.c @@ -0,0 +1,339 @@ +/* + * Android MediaCodec software buffer copy functions + * + * Copyright (c) 2015-2016 Matthieu Bouron + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include + +#include "libavutil/frame.h" +#include "libavutil/mem.h" + +#include "avcodec.h" +#include "mediacodecdec.h" +#include "mediacodec_wrapper.h" +#include "mediacodec_sw_buffer.h" + +#define QCOM_TILE_WIDTH 64 +#define QCOM_TILE_HEIGHT 32 +#define QCOM_TILE_SIZE (QCOM_TILE_WIDTH * QCOM_TILE_HEIGHT) +#define QCOM_TILE_GROUP_SIZE (4 * QCOM_TILE_SIZE) + +/** + * The code handling the the various YUV color formats is taken from the + * GStreamer project. + * + * Gstreamer reference: + * https://cgit.freedesktop.org/gstreamer/gst-plugins-bad/tree/sys/androidmedia/ + * + * Copyright (C) 2012, Collabora Ltd. + * Author: Sebastian Dröge + * + * Copyright (C) 2012, Rafaël Carré + * + * Copyright (C) 2015, Sebastian Dröge + * + * Copyright (C) 2014-2015, Collabora Ltd. + * Author: Matthieu Bouron + * + * Copyright (C) 2015, Edward Hervey + * Author: Edward Hervey + * + * Copyright (C) 2015, Matthew Waters + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation + * version 2.1 of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +void ff_mediacodec_sw_buffer_copy_yuv420_planar(AVCodecContext *avctx, + MediaCodecDecContext *s, + uint8_t *data, + size_t size, + FFAMediaCodecBufferInfo *info, + AVFrame *frame) +{ + int i; + uint8_t *src = NULL; + + for (i = 0; i < 3; i++) { + int stride = s->stride; + int height; + + src = data + info->offset; + if (i == 0) { + height = avctx->height; + + src += s->crop_top * s->stride; + src += s->crop_left; + } else { + height = avctx->height / 2; + stride = (s->stride + 1) / 2; + + src += s->slice_height * s->stride; + + if (i == 2) { + src += ((s->slice_height + 1) / 2) * stride; + } + + src += s->crop_top * stride; + src += (s->crop_left / 2); + } + + if (frame->linesize[i] == stride) { + memcpy(frame->data[i], src, height * stride); + } else { + int j, width; + uint8_t *dst = frame->data[i]; + + if (i == 0) { + width = avctx->width; + } else if (i == 1) { + width = FFMIN(frame->linesize[i], FFALIGN(avctx->width, 2)); + } + + for (j = 0; j < height; j++) { + memcpy(dst, src, width); + src += stride; + dst += frame->linesize[i]; + } + } + } +} + +void ff_mediacodec_sw_buffer_copy_yuv420_semi_planar(AVCodecContext *avctx, + MediaCodecDecContext *s, + uint8_t *data, + size_t size, + FFAMediaCodecBufferInfo *info, + AVFrame *frame) +{ + int i; + uint8_t *src = NULL; + + for (i = 0; i < 2; i++) { + int height; + + src = data + info->offset; + if (i == 0) { + height = avctx->height; + + src += s->crop_top * s->stride; + src += s->crop_left; + } else if (i == 1) { + height = avctx->height / 2; + + src += s->slice_height * s->stride; + src += s->crop_top * s->stride; + src += s->crop_left; + } + + if (frame->linesize[i] == s->stride) { + memcpy(frame->data[i], src, height * s->stride); + } else { + int j, width; + uint8_t *dst = frame->data[i]; + + if (i == 0) { + width = avctx->width; + } else if (i == 1) { + width = FFMIN(frame->linesize[i], FFALIGN(avctx->width, 2)); + } + + for (j = 0; j < height; j++) { + memcpy(dst, src, width); + src += s->stride; + dst += frame->linesize[i]; + } + } + } +} + + + +void ff_mediacodec_sw_buffer_copy_yuv420_packed_semi_planar(AVCodecContext *avctx, + MediaCodecDecContext *s, + uint8_t *data, + size_t size, + FFAMediaCodecBufferInfo *info, + AVFrame *frame) +{ + int i; + uint8_t *src = NULL; + + for (i = 0; i < 2; i++) { + int height; + + src = data + info->offset; + if (i == 0) { + height = avctx->height; + } else if (i == 1) { + height = avctx->height / 2; + + src += (s->slice_height - s->crop_top / 2) * s->stride; + + src += s->crop_top * s->stride; + src += s->crop_left; + } + + if (frame->linesize[i] == s->stride) { + memcpy(frame->data[i], src, height * s->stride); + } else { + int j, width; + uint8_t *dst = frame->data[i]; + + if (i == 0) { + width = avctx->width; + } else if (i == 1) { + width = FFMIN(frame->linesize[i], FFALIGN(avctx->width, 2)); + } + + for (j = 0; j < height; j++) { + memcpy(dst, src, width); + src += s->stride; + dst += frame->linesize[i]; + } + } + } +} + +/** + * The code handling the QCOM_FormatYUV420PackedSemiPlanar64x32Tile2m8ka + * color format is taken from the VLC project. + * + * VLC reference: + * http://git.videolan.org/?p=vlc.git;a=blob;f=modules/codec/omxil/qcom.c;hb=HEAD + * + * VLC copyright notice: + * + ***************************************************************************** + * qcom.c : pixel format translation for Qualcomm tiled nv12 + ***************************************************************************** + * Copyright © 2012 Rafaël Carré + * + * Authors: Rafaël Carré + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * + */ + +static size_t qcom_tile_pos(size_t x, size_t y, size_t w, size_t h) +{ + size_t flim = x + (y & ~1) * w; + + if (y & 1) { + flim += (x & ~3) + 2; + } else if ((h & 1) == 0 || y != (h - 1)) { + flim += (x + 2) & ~3; + } + + return flim; +} + +void ff_mediacodec_sw_buffer_copy_yuv420_packed_semi_planar_64x32Tile2m8ka(AVCodecContext *avctx, + MediaCodecDecContext *s, + uint8_t *data, + size_t size, + FFAMediaCodecBufferInfo *info, + AVFrame *frame) +{ + size_t width = frame->width; + size_t linesize = frame->linesize[0]; + size_t height = frame->height; + + const size_t tile_w = (width - 1) / QCOM_TILE_WIDTH + 1; + const size_t tile_w_align = (tile_w + 1) & ~1; + const size_t tile_h_luma = (height - 1) / QCOM_TILE_HEIGHT + 1; + const size_t tile_h_chroma = (height / 2 - 1) / QCOM_TILE_HEIGHT + 1; + + size_t luma_size = tile_w_align * tile_h_luma * QCOM_TILE_SIZE; + if((luma_size % QCOM_TILE_GROUP_SIZE) != 0) + luma_size = (((luma_size - 1) / QCOM_TILE_GROUP_SIZE) + 1) * QCOM_TILE_GROUP_SIZE; + + for(size_t y = 0; y < tile_h_luma; y++) { + size_t row_width = width; + for(size_t x = 0; x < tile_w; x++) { + size_t tile_width = row_width; + size_t tile_height = height; + /* dest luma memory index for this tile */ + size_t luma_idx = y * QCOM_TILE_HEIGHT * linesize + x * QCOM_TILE_WIDTH; + /* dest chroma memory index for this tile */ + /* XXX: remove divisions */ + size_t chroma_idx = (luma_idx / linesize) * linesize / 2 + (luma_idx % linesize); + + /* luma source pointer for this tile */ + const uint8_t *src_luma = data + + qcom_tile_pos(x, y,tile_w_align, tile_h_luma) * QCOM_TILE_SIZE; + + /* chroma source pointer for this tile */ + const uint8_t *src_chroma = data + luma_size + + qcom_tile_pos(x, y/2, tile_w_align, tile_h_chroma) * QCOM_TILE_SIZE; + if (y & 1) + src_chroma += QCOM_TILE_SIZE/2; + + /* account for right columns */ + if (tile_width > QCOM_TILE_WIDTH) + tile_width = QCOM_TILE_WIDTH; + + /* account for bottom rows */ + if (tile_height > QCOM_TILE_HEIGHT) + tile_height = QCOM_TILE_HEIGHT; + + tile_height /= 2; + while (tile_height--) { + memcpy(frame->data[0] + luma_idx, src_luma, tile_width); + src_luma += QCOM_TILE_WIDTH; + luma_idx += linesize; + + memcpy(frame->data[0] + luma_idx, src_luma, tile_width); + src_luma += QCOM_TILE_WIDTH; + luma_idx += linesize; + + memcpy(frame->data[1] + chroma_idx, src_chroma, tile_width); + src_chroma += QCOM_TILE_WIDTH; + chroma_idx += linesize; + } + row_width -= QCOM_TILE_WIDTH; + } + height -= QCOM_TILE_HEIGHT; + } +} -- cgit v1.2.3