From patchwork Fri Mar 29 16:14:50 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Yufei He X-Patchwork-Id: 12533 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 9C584447660 for ; Fri, 29 Mar 2019 18:14:59 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 783F168A92D; Fri, 29 Mar 2019 18:14:59 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mtxmxout11.matrox.com (mtxmxout11.matrox.com [138.11.2.132]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id E457268A768 for ; Fri, 29 Mar 2019 18:14:52 +0200 (EET) Received: from venus.matrox.com (unknown [192.168.1.36]) by mtxmxout11.matrox.com (Postfix) with ESMTP id 26BD9401DA for ; Fri, 29 Mar 2019 12:14:51 -0400 (EDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=matrox.com; s=dkim; t=1553876091; bh=okN7dOjay2V77EEWC5dMf7rT3gnJARJ+0/hSnJ8M7s4=; h=From:To:Subject:Date; b=cM2XCAJc9cAD1FD9f1dpRveXOl6aHT0Si7x2vUAl/Z3PQq8x5iqW8Kp+qggumnVQD WUttWT8qAi7YC5AX25cCGuwreaKXhPeK1p7sOhlVptpolHoWbqQp20ztpq34wWH/xz 8FYS77C6xlU43YC/CMwjNR8XVi3+KOoTa8xhM0Tc= Received: (from ssmsp@localhost) by venus.matrox.com (8.14.6/8.13.2) id x2TGEpMo025402 for ffmpeg-devel@ffmpeg.org; Fri, 29 Mar 2019 12:14:51 -0400 Received: from localhost (localhost.localdomain [127.0.0.1]) by venus.matrox.com (Postfix) with ESMTP id 0E05F5F75B for ; Fri, 29 Mar 2019 12:14:51 -0400 (EDT) X-Virus-MTX-Scanned: by Matrox Virus scanner at venus.matrox.com Received: from venus.matrox.com ([127.0.0.1]) by localhost (venus.matrox.com [127.0.0.1]) (amavisd-new, port 10024) with LMTP id eW5mFqXxYVMw for ; Fri, 29 Mar 2019 12:14:50 -0400 (EDT) Received: from venus-in.matrox.com (localhost.localdomain [127.0.0.1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by venus.matrox.com (Postfix) with ESMTPS id D11455F74C for ; Fri, 29 Mar 2019 12:14:50 -0400 (EDT) Received: from Exchsrv01A.mtx01.matrox.com (exchsrv01a.mtx01.matrox.com [192.168.15.98]) (using TLSv1 with cipher ECDHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by venus-in.matrox.com (Postfix) with ESMTPS id CF49D61ABF for ; Fri, 29 Mar 2019 12:14:50 -0400 (EDT) Received: from EXCHSRVORANGE01.orange.matrox.com (192.168.210.30) by EXCHSRV01A.mtx01.matrox.com (192.168.15.98) with Microsoft SMTP Server (TLS) id 14.3.439.0; Fri, 29 Mar 2019 12:14:50 -0400 Received: from EXCHSRV01A.mtx01.matrox.com ([::1]) by EXCHSRVOrange01.orange.matrox.com ([::1]) with mapi id 14.03.0439.000; Fri, 29 Mar 2019 12:14:50 -0400 From: Yufei He To: FFmpeg development discussions and patches Thread-Topic: patch for supporting m264 on h.264 decoding and encoding. Thread-Index: AQHU5kqIHg4kUM9+p02KWevpjJgyYQ== Date: Fri, 29 Mar 2019 16:14:50 +0000 Message-ID: <4c507bb0-d3e1-7219-f30a-d0ab0040ff06@matrox.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: yes X-MS-TNEF-Correlator: x-originating-ip: [192.168.66.209] MIME-Version: 1.0 Subject: [FFmpeg-devel] patch for supporting m264 on h.264 decoding and encoding. X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Hi This is the fixed version of this patch that follows the way other companies do on supporting FFmpeg. 1>Put library mvm264 to the nonfree list in configure 2>Remove calling to dlopen Please review. Thanks. Yufei From 1a636c0aa2288731374a4a6139c385baad2a8e98 Mon Sep 17 00:00:00 2001 From: yhe Date: Fri, 29 Mar 2019 10:06:37 -0400 Subject: [PATCH] h.264 decoder and encoder support with M264 --- Changelog | 1 + configure | 6 + libavcodec/Makefile | 2 + libavcodec/allcodecs.c | 2 + libavcodec/m264dec.c | 304 +++++++++++++++++++++++++++++++++++++++++++++++++ libavcodec/m264dec.h | 40 +++++++ libavcodec/m264enc.c | 262 ++++++++++++++++++++++++++++++++++++++++++ libavcodec/m264enc.h | 38 +++++++ 8 files changed, 655 insertions(+) create mode 100644 libavcodec/m264dec.c create mode 100644 libavcodec/m264dec.h create mode 100644 libavcodec/m264enc.c create mode 100644 libavcodec/m264enc.h diff --git a/Changelog b/Changelog index 4d80e5b..ce0daf8 100644 --- a/Changelog +++ b/Changelog @@ -19,6 +19,7 @@ version : - ARBC decoder - libaribb24 based ARIB STD-B24 caption support (profiles A and C) - Support decoding of HEVC 4:4:4 content in nvdec and cuviddec +- Support h.264 encoding and decoding with Matrox M264 card. version 4.1: diff --git a/configure b/configure index dcead3a..b579d10 100755 --- a/configure +++ b/configure @@ -302,6 +302,7 @@ External library support: --enable-mbedtls enable mbedTLS, needed for https support if openssl, gnutls or libtls is not used [no] --enable-mediacodec enable Android MediaCodec support [no] + --enable-mvm264 enable Matrox M264 H.264 support [no] --enable-libmysofa enable libmysofa, needed for sofalizer filter [no] --enable-openal enable OpenAL 1.1 capture support [no] --enable-opencl enable OpenCL processing [no] @@ -1717,6 +1718,7 @@ EXTERNAL_LIBRARY_NONFREE_LIST=" libfdk_aac openssl libtls + mvm264 " EXTERNAL_LIBRARY_VERSION3_LIST=" @@ -2697,6 +2699,8 @@ h263p_decoder_select="h263_decoder" h263p_encoder_select="h263_encoder" h264_decoder_select="cabac golomb h264chroma h264dsp h264parse h264pred h264qpel videodsp" h264_decoder_suggest="error_resilience" +h264_m264_decoder_deps="mvm264" +h264_m264_encoder_deps="mvm264" hap_decoder_select="snappy texturedsp" hap_encoder_deps="libsnappy" hap_encoder_select="texturedspenc" @@ -2753,6 +2757,7 @@ msmpeg4v3_decoder_select="h263_decoder" msmpeg4v3_encoder_select="h263_encoder" mss2_decoder_select="mpegvideo qpeldsp vc1_decoder" mts2_decoder_select="mss34dsp" +mvm264_deps_any="libdl LoadLibrary" mwsc_decoder_deps="zlib" mxpeg_decoder_select="mjpeg_decoder" nellymoser_decoder_select="mdct sinewin" @@ -6277,6 +6282,7 @@ enabled mmal && { check_lib mmal interface/mmal/mmal.h mmal_port_co check_lib mmal interface/mmal/mmal.h mmal_port_connect -lmmal_core -lmmal_util -lmmal_vc_client -lbcm_host; } || die "ERROR: mmal not found" && check_func_headers interface/mmal/mmal.h "MMAL_PARAMETER_VIDEO_MAX_NUM_CALLBACKS"; } +enabled mvm264 && require_headers mvm264api.h enabled openal && { { for al_extralibs in "${OPENAL_LIBS}" "-lopenal" "-lOpenAL32"; do check_lib openal 'AL/al.h' alGetError "${al_extralibs}" && break; done } || die "ERROR: openal not found"; } && diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 15c43a8..8e8f9ab 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -348,6 +348,8 @@ OBJS-$(CONFIG_H264_DECODER) += h264dec.o h264_cabac.o h264_cavlc.o \ h264_slice.o h264data.o OBJS-$(CONFIG_H264_AMF_ENCODER) += amfenc_h264.o OBJS-$(CONFIG_H264_CUVID_DECODER) += cuviddec.o +OBJS-$(CONFIG_H264_M264_DECODER) += m264dec.o +OBJS-$(CONFIG_H264_M264_ENCODER) += m264enc.o OBJS-$(CONFIG_H264_MEDIACODEC_DECODER) += mediacodecdec.o OBJS-$(CONFIG_H264_MMAL_DECODER) += mmaldec.o OBJS-$(CONFIG_H264_NVENC_ENCODER) += nvenc_h264.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index b26aeca..c03a5a3 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -737,6 +737,8 @@ extern AVCodec ff_libopenh264_encoder; extern AVCodec ff_libopenh264_decoder; extern AVCodec ff_h264_amf_encoder; extern AVCodec ff_h264_cuvid_decoder; +extern AVCodec ff_h264_m264_decoder; +extern AVCodec ff_h264_m264_encoder; extern AVCodec ff_h264_nvenc_encoder; extern AVCodec ff_h264_omx_encoder; extern AVCodec ff_h264_qsv_encoder; diff --git a/libavcodec/m264dec.c b/libavcodec/m264dec.c new file mode 100644 index 0000000..cb58bad --- /dev/null +++ b/libavcodec/m264dec.c @@ -0,0 +1,304 @@ +/* + * M264 H.264 video decoder + * + * 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 + */ + +/** + * @file + * Matrox M264 card supports h.264 encoding and decoding. + */ + +#include "decode.h" +#include "cabac.h" +#include "error_resilience.h" +#include "h264_parse.h" +#include "h264_ps.h" +#include "h264_sei.h" +#include "h2645_parse.h" +#include "h264chroma.h" +#include "h264dsp.h" +#include "h264pred.h" +#include "h264qpel.h" +#include "internal.h" +#include "mpegutils.h" +#include "parser.h" +#include "qpeldsp.h" +#include "rectangle.h" +#include "videodsp.h" +#include "config.h" +#include "libswscale/swscale.h" +#include "libavutil/opt.h" +#include "mvm264api.h" +#include "m264dec.h" + +typedef struct { + uint16_t input_width; + uint16_t input_height; + uint16_t output_width; + uint16_t output_height; + uint16_t scale; + uint16_t rate; + uint16_t gop_size; + uint32_t bitrate; + uint8_t field_order; +} M264DecoderInfo; + +typedef struct { + AVClass *class; + void *decoder_context; + int64_t frames; + int eof; + char *sps_pps; + int length_sps_pps; + AVBSFContext *ctx; + char *resize_expr; + struct { + int width; + int height; + } resize; +} M264DecoderContext; + +int m264_mp4_to_annexb(AVCodecContext *avctx, AVPacket *packet) +{ + M264DecoderContext *m264_decoder_context = (M264DecoderContext *)avctx->priv_data; + int ret = 0; + + if (m264_decoder_context->ctx) { + ret = av_bsf_send_packet(m264_decoder_context->ctx, packet); + if (ret < 0) + return ret; + + ret = av_bsf_receive_packet(m264_decoder_context->ctx, packet); + if (ret < 0) + return ret; + } + + return ret; +} + +int m264_init_spspps_mp4(AVCodecContext *avctx) +{ + M264DecoderContext *m264_decoder_context = (M264DecoderContext *)avctx->priv_data; + int ret = 0; + const AVBitStreamFilter *filter = av_bsf_get_by_name("h264_mp4toannexb"); + AVBSFContext *ctx = NULL; + + ret = av_bsf_alloc(filter, &ctx); + if (ret < 0) + return ret; + + ret = avcodec_parameters_from_context(ctx->par_in, avctx); + if (ret < 0) + return ret; + + ret = av_bsf_init(ctx); + if (ret < 0) + return ret; + + m264_decoder_context->ctx = ctx; + + if (ctx->par_out->extradata_size > 0) { + m264_decoder_context->sps_pps = av_mallocz(ctx->par_out->extradata_size); + if (!m264_decoder_context->sps_pps) + return AVERROR(ENOMEM); + + memcpy(m264_decoder_context->sps_pps, ctx->par_out->extradata, ctx->par_out->extradata_size); + m264_decoder_context->length_sps_pps = ctx->par_out->extradata_size; + } + + return ret; +} + +av_cold int ff_m264_decode_init(AVCodecContext *avctx) +{ + int ret = 0; + M264DecoderContext *m264_decoder_context; + M264DecoderInfo decoder_info; + + m264_decoder_context = avctx->priv_data; + m264_decoder_context->eof = 0; + decoder_info.input_width = avctx->width; + decoder_info.input_height = avctx->height; + + if (m264_decoder_context->resize_expr) { + if (sscanf(m264_decoder_context->resize_expr, "%dx%d", &m264_decoder_context->resize.width, &m264_decoder_context->resize.height) != 2) { + av_log(avctx, AV_LOG_ERROR, "Invalid resize expressions\n"); + m264_decoder_context->resize.width = avctx->width; + m264_decoder_context->resize.height = avctx->height; + } + + if (m264_decoder_context->resize.height == -1) + m264_decoder_context->resize.height = (float)m264_decoder_context->resize.width * avctx->height / avctx->width; + + avctx->width = m264_decoder_context->resize.width; + avctx->height = m264_decoder_context->resize.height; + } + + decoder_info.output_width = avctx->width; + decoder_info.output_height = avctx->height; + decoder_info.scale = avctx->framerate.den; + decoder_info.rate = avctx->framerate.num; + decoder_info.field_order = (uint8_t)avctx->field_order; + av_log(avctx, AV_LOG_DEBUG, "m264_decode_init_h264: avctx->field_order is %d \n", avctx->field_order); + av_log(avctx, AV_LOG_DEBUG, "m264_decode_init_h264: avctx->width = %d, avctx->height = %d, avctx->framerate.num = %d, avctx->framerate.den = %d\n", + avctx->width, avctx->height, avctx->framerate.num, avctx->framerate.den); + + ret = m264_decoder_init(&decoder_info, &(m264_decoder_context->decoder_context)); + if (ret < 0) + return ret; + + avctx->priv_data = m264_decoder_context; + avctx->pix_fmt = AV_PIX_FMT_YUYV422; // m264 card only supports YUYV422 as input. + avctx->bits_per_raw_sample = 8; + + if (avctx->extradata_size > 0 && avctx->extradata) + ret = m264_init_spspps_mp4(avctx); + + return ret; +} + +int ff_m264_receive_frame(AVCodecContext *avctx, AVFrame *frame) +{ + int ret = 0; + AVPacket pkt = {0}; + void *buffer_context = NULL; + void *buffer = NULL; + unsigned long buffer_size = 0; + unsigned long line_size; + int line; + int actual_line_size; + unsigned char *srcmem,*dstmem; + long double duration = 0; + + M264DecoderContext *m264_decoder_context = (M264DecoderContext *)avctx->priv_data; + ret = m264_decoder_receive_frame(m264_decoder_context->decoder_context, &buffer_context, &buffer, &buffer_size); + if (ret == -1) { + av_log(avctx, AV_LOG_DEBUG, "ff_m264_receive_frame: m264_decoder_context->receive_packet get eos, return AVERROR_EOF \n"); + return AVERROR_EOF; + } + + if (buffer_size > 0) { + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + + line_size = buffer_size / avctx->height; + srcmem = (unsigned char *)buffer; + dstmem = (unsigned char *)frame->data[0]; + actual_line_size = avctx->width * 2; + + for (line = 0; line < avctx->height; line++) { + memcpy(dstmem, srcmem, actual_line_size); + dstmem += frame->linesize[0]; + srcmem += line_size; + } + + frame->pts = m264_decoder_context->frames * frame->pkt_duration; + m264_decoder_context->frames++; + av_log(avctx, AV_LOG_DEBUG, "ff_m264_receive_frame: m264_decoder_context->frames = %d\n", (int)m264_decoder_context->frames); + ret = m264_decoder_release_frame_buffer(m264_decoder_context->decoder_context, buffer_context); + } else { + if (m264_decoder_context->eof != 1) { + ret = ff_decode_get_packet(avctx, &pkt); + av_log(avctx, AV_LOG_DEBUG, "decoder: ff_m264_receive_frame: ff_decode_get_packet pkt.size = %d\n", pkt.size); + if (ret < 0 && ret != AVERROR_EOF) + return ret; + } + + if (ret == AVERROR_EOF ) { + if (m264_decoder_context->eof != 1) { + pkt.data = NULL; + pkt.size = 0; + m264_decoder_context->eof = 1; + ret = m264_decoder_send_packet(m264_decoder_context->decoder_context, m264_decoder_context->sps_pps, m264_decoder_context->length_sps_pps, pkt.data, pkt.size, 0); + } + } else { + if (m264_decoder_context->eof != 1) { + av_log(avctx, AV_LOG_DEBUG, "decoder: ff_m264_receive_frame: pkt.side_data_elems = %d\n", pkt.side_data_elems); + + if (pkt.data != NULL) { + ret = m264_mp4_to_annexb(avctx, &pkt); + if (ret < 0) + return ret; + } + + duration = (long double)pkt.duration * avctx->pkt_timebase.num / avctx->pkt_timebase.den; + + if (pkt.flags) + ret = m264_decoder_send_packet(m264_decoder_context->decoder_context, m264_decoder_context->sps_pps, m264_decoder_context->length_sps_pps, pkt.data, pkt.size, duration); + else + ret = m264_decoder_send_packet(m264_decoder_context->decoder_context, NULL , 0, pkt.data, pkt.size, duration); + } + } + av_packet_unref(&pkt); + + if (ret < 0) + return ret; + + ret = AVERROR(EAGAIN); + } + return ret; +} + +int av_cold ff_m264_decode_close(AVCodecContext *avctx) +{ + M264DecoderContext *m264_decoder_context = (M264DecoderContext *)avctx->priv_data; + + if (m264_decoder_context) { + + if (m264_decoder_context->decoder_context) { + m264_decoder_exit(m264_decoder_context->decoder_context); + } + + if (m264_decoder_context->ctx) { + av_bsf_free(&m264_decoder_context->ctx); + } + + av_free(m264_decoder_context->sps_pps); + } + + return 0; +} + +#define OFFSET(x) offsetof(M264DecoderContext, x) +#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM +static const AVOption h264_m264_decoder_options[] = { + { "resize", "Resize (width)x(height)", OFFSET(resize_expr), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, VD,"resize" }, + { NULL } +}; + +static const AVClass h264_m264_decoder_class = { + .class_name = "h264_m264_decoder", + .item_name = av_default_item_name, + .option = h264_m264_decoder_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVCodec ff_h264_m264_decoder = { + .name = "m264", + .long_name = NULL_IF_CONFIG_SMALL("Matrox M264 Codec"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_H264, + .init = ff_m264_decode_init, + .receive_frame = ff_m264_receive_frame, + .close = ff_m264_decode_close, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .priv_data_size = sizeof(M264DecoderContext), + .priv_class = &h264_m264_decoder_class, + .wrapper_name = "m264", +}; diff --git a/libavcodec/m264dec.h b/libavcodec/m264dec.h new file mode 100644 index 0000000..27ef7a7 --- /dev/null +++ b/libavcodec/m264dec.h @@ -0,0 +1,40 @@ +/* + * M264 H.264 video decoder + * + * 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 + */ + +/** + * @file + * Matrox M264 card supports h.264 encoding and decoding. + */ + +#ifndef AVCODEC_M264DEC_H +#define AVCODEC_M264DEC_H + +#include "libavutil/fifo.h" + +#include "avcodec.h" + +int ff_m264_decode_init(AVCodecContext *avctx); +int ff_m264_decode_close(AVCodecContext *avctx); +int ff_m264_receive_frame(AVCodecContext *avctx, AVFrame *frame); +int m264_init_spspps_mp4(AVCodecContext *avctx); +int m264_mp4_to_annexb(AVCodecContext *avctx, AVPacket *packet); + +#endif //AVCODEC_M264DEC_H + diff --git a/libavcodec/m264enc.c b/libavcodec/m264enc.c new file mode 100644 index 0000000..bb5b989 --- /dev/null +++ b/libavcodec/m264enc.c @@ -0,0 +1,262 @@ +/* + * M264 H.264 video encoder + * + * 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 + */ + +/** + * @file + * Matrox M264 card supports h.264 encoding and decoding. + */ + +#include "libswscale/swscale.h" +#include "internal.h" +#include "profiles.h" +#include "libavutil/opt.h" +#include "mvm264api.h" +#include "m264enc.h" + +typedef struct { + uint16_t width; + uint16_t height; + uint16_t scale; + uint16_t rate; + uint16_t profile; + uint16_t level; + uint16_t gop_size; + uint32_t bitrate; +} M264EncoderInfo; + +typedef struct { + AVClass *class; + void *encoder_context; + struct SwsContext *sw_context; + int profile; + int level; +} M264EncoderContext; + +av_cold int ff_m264_encode_init(AVCodecContext *avctx) +{ + int ret = 0; + M264EncoderContext *m264_encoder_context; + M264EncoderInfo encoder_info; + void *extradata = NULL; + unsigned long extradata_size = 0; + + m264_encoder_context = avctx->priv_data; + + encoder_info.width = avctx->width; + encoder_info.height = avctx->height; + encoder_info.rate = avctx->framerate.num; + encoder_info.scale = avctx->framerate.den; + encoder_info.profile = m264_encoder_context->profile; + encoder_info.level = m264_encoder_context->level; + encoder_info.gop_size = avctx->gop_size; + encoder_info.bitrate = avctx->bit_rate; + av_log(avctx, AV_LOG_DEBUG, "m264_encode_init_h264: avctx->width = %d, avctx->height = %d, avctx->framerate.num = %d, avctx->framerate.den = %d, avctx->gop_size = %d, avctx->bit_rate = %" PRId64 "\n", + avctx->width, avctx->height, avctx->framerate.num, avctx->framerate.den, avctx->gop_size, avctx->bit_rate); + + ret = m264_encoder_init(&encoder_info, &(m264_encoder_context->encoder_context)); + if (ret < 0) + return ret; + + if (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) { + ret = m264_encoder_get_extradata(m264_encoder_context->encoder_context, &extradata, &extradata_size); + if (ret < 0) + return ret; + + if (extradata_size > 0) { + avctx->extradata = av_mallocz(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE); + if (!avctx->extradata) + return AVERROR(ENOMEM); + memcpy(avctx->extradata, extradata, extradata_size); + avctx->extradata_size = extradata_size; + } + } + + return ret; +} + +int ff_m264_send_frame(AVCodecContext *avctx, const AVFrame *frame) +{ + int ret = 0; + enum AVPixelFormat src_pixel_format; + enum AVPixelFormat dest_pixel_format; + uint8_t *dst[1]; + int dst_stride[1]; + void *buffer_context; + unsigned long buffer_size; + unsigned long line_size; + int line; + int actual_line_size; + unsigned char *srcmem,*dstmem; + + M264EncoderContext *m264_encoder_context = (M264EncoderContext *)avctx->priv_data; + + if (frame) { + src_pixel_format = (enum AVPixelFormat)frame->format; + dest_pixel_format = AV_PIX_FMT_YUYV422; + if (m264_encoder_context->sw_context == NULL) { + m264_encoder_context->sw_context = sws_getContext(frame->width, frame->height, src_pixel_format, frame->width, frame->height, + dest_pixel_format, SWS_BILINEAR, NULL, NULL, NULL); + if (!m264_encoder_context->sw_context) { + av_log(avctx, AV_LOG_ERROR, "failed to get sw_context\n"); + ret = -1; + return ret; + } + } + ret = m264_encoder_get_uncompressed_buffer(m264_encoder_context->encoder_context, &buffer_context, (void **)&dst[0], &buffer_size); + if (ret < 0) + return ret; + + dst_stride[0] = buffer_size / avctx->height; + if (src_pixel_format != AV_PIX_FMT_YUYV422) { + ret = sws_scale(m264_encoder_context->sw_context, (const uint8_t * const*)frame->data, frame->linesize, 0, frame->height, dst, dst_stride); + if (ret < 0) + return ret; + } else { + line_size = buffer_size / avctx->height; + dstmem = (unsigned char *)dst[0]; + srcmem = (unsigned char *)frame->data[0]; + actual_line_size = avctx->width * 2; + + for (line = 0; line < avctx->height; line++) { + memcpy(dstmem, srcmem, actual_line_size); + srcmem += frame->linesize[0]; + dstmem += line_size; + } + } + av_log(avctx, AV_LOG_DEBUG, "ff_m264_send_frame: frame->width = %d, frame->height = %d\n", frame->width, frame->height); + ret = m264_encoder_send_frame(m264_encoder_context->encoder_context, buffer_context, 0); + if (ret < 0) + return ret; + } else { + av_log(avctx, AV_LOG_DEBUG, "ff_m264_send_frame: frame is null. eof\n"); + ret = m264_encoder_send_frame(m264_encoder_context->encoder_context, NULL, 0); + if (ret < 0) + return ret; + } + return ret; +} + +int ff_m264_receive_packet(AVCodecContext *avctx, AVPacket *avpkt) +{ + int ret; + M264EncoderContext *m264_encoder_context = (M264EncoderContext *)avctx->priv_data; + void *buffer_context = NULL; + void *buffer = NULL; + unsigned long buffer_size = 0; + int32_t pts, dts; + int8_t key; + + ret = m264_encoder_receive_packet(m264_encoder_context->encoder_context, &buffer_context, &buffer, &buffer_size, &pts, &dts, &key); + if (ret == -1) { + av_log(avctx, AV_LOG_DEBUG, "ff_m264_receive_packet: m264_encoder_context->receive_packet get eos, return AVERROR_EOF\n"); + return AVERROR_EOF; + } else if (buffer_size == 0) { + return AVERROR(EAGAIN); + } + + av_log(avctx, AV_LOG_DEBUG, "ff_m264_receive_packet m264_encoder_context->receive_packet buffer_size = %d\n", (int)buffer_size); + + ret = ff_alloc_packet2(avctx, avpkt, buffer_size, 0); + if (ret < 0) + return ret; + + memcpy(avpkt->data, buffer, buffer_size); + + avpkt->dts = dts - 2; + avpkt->pts = pts; + avpkt->flags = key ? AV_PKT_FLAG_KEY : 0; + avpkt->duration = 1; + + ret = m264_encoder_release_packet_buffer(m264_encoder_context->encoder_context, buffer_context); + if (ret < 0) + return ret; + + return ret; +} + +int av_cold ff_m264_encode_close(AVCodecContext *avctx) +{ + M264EncoderContext *m264_encoder_context = (M264EncoderContext *)avctx->priv_data; + + if (m264_encoder_context) { + if (m264_encoder_context->encoder_context) { + m264_encoder_exit(m264_encoder_context->encoder_context); + } + + if (m264_encoder_context->sw_context) { + sws_freeContext(m264_encoder_context->sw_context); + } + } + + return 0; +} + +#define OFFSET(x) offsetof(M264EncoderContext, x) +#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM +static const AVOption h264_m264_encoder_options[] = { + { "profile", "Set the encoding profile", OFFSET(profile), AV_OPT_TYPE_INT,{ .i64 = FF_PROFILE_H264_MAIN }, FF_PROFILE_H264_BASELINE, FF_PROFILE_H264_HIGH_444, VE, "profile" }, + { "baseline", "", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_BASELINE }, 0, 0, VE, "profile" }, + { "main", "", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_MAIN }, 0, 0, VE, "profile" }, + { "high", "", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_HIGH }, 0, 0, VE, "profile" }, + { "high10", "", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_HIGH_10 }, 0, 0, VE, "profile" }, + { "high422", "", 0, AV_OPT_TYPE_CONST, { .i64 = FF_PROFILE_H264_HIGH_422 }, 0, 0, VE, "profile" }, + { "level", "Profile Level", OFFSET(level), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 62, VE, "level" }, + { "1.b", "", 0, AV_OPT_TYPE_CONST, { .i64 = 9 }, 0, 0, VE, "level" }, + { "1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 10 }, 0, 0, VE, "level" }, + { "1.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 11 }, 0, 0, VE, "level" }, + { "1.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 12 }, 0, 0, VE, "level" }, + { "1.3", "", 0, AV_OPT_TYPE_CONST, { .i64 = 13 }, 0, 0, VE, "level" }, + { "2.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 20 }, 0, 0, VE, "level" }, + { "2.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 21 }, 0, 0, VE, "level" }, + { "2.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 22 }, 0, 0, VE, "level" }, + { "3.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 30 }, 0, 0, VE, "level" }, + { "3.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 31 }, 0, 0, VE, "level" }, + { "3.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 32 }, 0, 0, VE, "level" }, + { "4.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 40 }, 0, 0, VE, "level" }, + { "4.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 41 }, 0, 0, VE, "level" }, + { "4.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 42 }, 0, 0, VE, "level" }, + { "5.0", "", 0, AV_OPT_TYPE_CONST, { .i64 = 50 }, 0, 0, VE, "level" }, + { "5.1", "", 0, AV_OPT_TYPE_CONST, { .i64 = 51 }, 0, 0, VE, "level" }, + { "5.2", "", 0, AV_OPT_TYPE_CONST, { .i64 = 52 }, 0, 0, VE, "level" }, + { NULL }, +}; + +static const AVClass h264_m264_encoder_class = { + .class_name = "h264_m264_encoder", + .item_name = av_default_item_name, + .option = h264_m264_encoder_options, + .version = LIBAVUTIL_VERSION_INT, +}; + +AVCodec ff_h264_m264_encoder = { + .name = "m264", + .long_name = NULL_IF_CONFIG_SMALL("Matrox M264 Codec"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_H264, + .init = ff_m264_encode_init, + .send_frame = ff_m264_send_frame, + .receive_packet = ff_m264_receive_packet, + .close = ff_m264_encode_close, + .capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .priv_data_size = sizeof(M264EncoderContext), + .priv_class = &h264_m264_encoder_class, + .wrapper_name = "m264", +}; diff --git a/libavcodec/m264enc.h b/libavcodec/m264enc.h new file mode 100644 index 0000000..0c44b32 --- /dev/null +++ b/libavcodec/m264enc.h @@ -0,0 +1,38 @@ +/* + * M264 H.264 video encoder + * + * 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 + */ + +/** + * @file + * Matrox M264 card supports h.264 encoding and decoding. + */ + +#ifndef AVCODEC_M264ENC_H +#define AVCODEC_M264ENC_H + +#include "libavutil/fifo.h" + +#include "avcodec.h" + +int ff_m264_encode_init(AVCodecContext *avctx); +int ff_m264_encode_close(AVCodecContext *avctx); +int ff_m264_send_frame(AVCodecContext *avctx, const AVFrame *frame); +int ff_m264_receive_packet(AVCodecContext *avctx, AVPacket *avpkt); + +#endif //AVCODEC_AMAVCODEC_M264ENC_H -- 2.7.4