mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-27 02:30:35 +00:00
29e3d09014
Android 19 added an API for dynamically changing the bitrate in a running codec. Also make it so that even when not update-able at runtime, parameters will at least be stored so that they take effect the next the codec is restarted.
382 lines
9.7 KiB
C
382 lines
9.7 KiB
C
/*
|
|
* Copyright (C) 2018 Collabora Ltd.
|
|
* Author: Xavier Claessens <xavier.claessens@collabora.com>
|
|
*
|
|
* 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
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "gstamc-internal-ml.h"
|
|
#include "gstamc-surfacetexture-ml.h"
|
|
#include "../gstamc-codec.h"
|
|
#include "../gstamc-constants.h"
|
|
|
|
#include <ml_media_codec.h>
|
|
|
|
struct _GstAmcCodec
|
|
{
|
|
MLHandle handle;
|
|
GstAmcSurfaceTexture *surface_texture;
|
|
};
|
|
|
|
gboolean
|
|
gst_amc_codec_static_init (void)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
gst_amc_buffer_free (GstAmcBuffer * buffer)
|
|
{
|
|
g_free (buffer);
|
|
}
|
|
|
|
gboolean
|
|
gst_amc_buffer_set_position_and_limit (GstAmcBuffer * buffer, GError ** err,
|
|
gint position, gint limit)
|
|
{
|
|
/* FIXME: Do we need to do something?
|
|
buffer->data = buffer->data + position;
|
|
buffer->size = limit;
|
|
*/
|
|
return TRUE;
|
|
}
|
|
|
|
GstAmcCodec *
|
|
gst_amc_codec_new (const gchar * name, gboolean is_encoder, GError ** err)
|
|
{
|
|
GstAmcCodec *codec = NULL;
|
|
MLResult result;
|
|
MLMediaCodecType type;
|
|
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
codec = g_slice_new0 (GstAmcCodec);
|
|
codec->handle = ML_INVALID_HANDLE;
|
|
type = is_encoder ? MLMediaCodecType_Encoder : MLMediaCodecType_Decoder;
|
|
result =
|
|
MLMediaCodecCreateCodec (MLMediaCodecCreation_ByName, type, name,
|
|
&codec->handle);
|
|
if (result != MLResult_Ok) {
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Failed to create codec by name %s: %d", name, result);
|
|
gst_amc_codec_free (codec);
|
|
return NULL;
|
|
}
|
|
|
|
return codec;
|
|
}
|
|
|
|
void
|
|
gst_amc_codec_free (GstAmcCodec * codec)
|
|
{
|
|
g_return_if_fail (codec != NULL);
|
|
|
|
if (codec->handle != ML_INVALID_HANDLE)
|
|
MLMediaCodecDestroy (codec->handle);
|
|
g_clear_object (&codec->surface_texture);
|
|
g_slice_free (GstAmcCodec, codec);
|
|
}
|
|
|
|
gboolean
|
|
gst_amc_codec_configure (GstAmcCodec * codec, GstAmcFormat * format,
|
|
GstAmcSurfaceTexture * surface_texture, GError ** err)
|
|
{
|
|
MLResult result;
|
|
MLHandle surface_handle = ML_INVALID_HANDLE;
|
|
|
|
g_return_val_if_fail (codec != NULL, FALSE);
|
|
g_return_val_if_fail (format != NULL, FALSE);
|
|
g_return_val_if_fail (surface_texture == NULL
|
|
|| GST_IS_AMC_SURFACE_TEXTURE_ML (surface_texture), FALSE);
|
|
|
|
g_set_object (&codec->surface_texture, surface_texture);
|
|
if (surface_texture != NULL)
|
|
surface_handle =
|
|
gst_amc_surface_texture_ml_get_handle ((GstAmcSurfaceTextureML *)
|
|
surface_texture);
|
|
|
|
result = MLMediaCodecConfigureWithSurface (codec->handle,
|
|
gst_amc_format_get_handle (format), surface_handle, 0);
|
|
if (result != MLResult_Ok) {
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Failed to configure codec %d", result);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GstAmcFormat *
|
|
gst_amc_codec_get_output_format (GstAmcCodec * codec, GError ** err)
|
|
{
|
|
MLHandle format_handle;
|
|
MLResult result;
|
|
|
|
g_return_val_if_fail (codec != NULL, NULL);
|
|
|
|
result = MLMediaCodecGetOutputFormat (codec->handle, &format_handle);
|
|
if (result != MLResult_Ok) {
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Failed to get output format %d", result);
|
|
return NULL;
|
|
}
|
|
|
|
return gst_amc_format_new_handle (format_handle);
|
|
}
|
|
|
|
gboolean
|
|
gst_amc_codec_start (GstAmcCodec * codec, GError ** err)
|
|
{
|
|
MLResult result;
|
|
|
|
g_return_val_if_fail (codec != NULL, FALSE);
|
|
|
|
result = MLMediaCodecStart (codec->handle);
|
|
if (result != MLResult_Ok) {
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Failed to start codec %d", result);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_amc_codec_stop (GstAmcCodec * codec, GError ** err)
|
|
{
|
|
MLResult result;
|
|
|
|
g_return_val_if_fail (codec != NULL, FALSE);
|
|
|
|
result = MLMediaCodecStop (codec->handle);
|
|
if (result != MLResult_Ok) {
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Failed to stop codec %d", result);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_amc_codec_flush (GstAmcCodec * codec, GError ** err)
|
|
{
|
|
MLResult result;
|
|
|
|
g_return_val_if_fail (codec != NULL, FALSE);
|
|
|
|
result = MLMediaCodecFlush (codec->handle);
|
|
if (result != MLResult_Ok) {
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Failed to flush codec %d", result);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_amc_codec_request_key_frame (GstAmcCodec * codec, GError ** err)
|
|
{
|
|
/* If MagicLeap adds an API for requesting a keyframe, call it here */
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Keyframe requests are not available on MagicLeap");
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_amc_codec_set_dynamic_bitrate (GstAmcCodec * codec, GError ** err,
|
|
gint bitrate)
|
|
{
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Dynamic bitrate control isn't available on MagicLeap");
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_amc_codec_have_dynamic_bitrate ()
|
|
{
|
|
/* If MagicLeap ever provides an API for scaling bitrate, change this to TRUE */
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
gst_amc_codec_release (GstAmcCodec * codec, GError ** err)
|
|
{
|
|
g_return_val_if_fail (codec != NULL, FALSE);
|
|
return TRUE;
|
|
}
|
|
|
|
GstAmcBuffer *
|
|
gst_amc_codec_get_output_buffer (GstAmcCodec * codec, gint index, GError ** err)
|
|
{
|
|
MLResult result;
|
|
GstAmcBuffer *ret;
|
|
|
|
g_return_val_if_fail (codec != NULL, NULL);
|
|
g_return_val_if_fail (index >= 0, NULL);
|
|
|
|
ret = g_new0 (GstAmcBuffer, 1);
|
|
|
|
/* When configured with a surface, getting the buffer pointer makes no sense,
|
|
* but on Android it's not an error, it just return NULL buffer.
|
|
* But MLMediaCodecGetInputBufferPointer() will return an error instead. */
|
|
if (codec->surface_texture != NULL) {
|
|
return ret;
|
|
}
|
|
|
|
result =
|
|
MLMediaCodecGetOutputBufferPointer (codec->handle, index,
|
|
(const uint8_t **) &ret->data, &ret->size);
|
|
if (result != MLResult_Ok) {
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Failed to get output buffer %d", result);
|
|
g_free (ret);
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
GstAmcBuffer *
|
|
gst_amc_codec_get_input_buffer (GstAmcCodec * codec, gint index, GError ** err)
|
|
{
|
|
MLResult result;
|
|
GstAmcBuffer *ret;
|
|
|
|
g_return_val_if_fail (codec != NULL, NULL);
|
|
g_return_val_if_fail (index >= 0, NULL);
|
|
|
|
ret = g_new0 (GstAmcBuffer, 1);
|
|
|
|
result =
|
|
MLMediaCodecGetInputBufferPointer (codec->handle, index, &ret->data,
|
|
&ret->size);
|
|
if (result != MLResult_Ok) {
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Failed to get input buffer %d", result);
|
|
g_free (ret);
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
gint
|
|
gst_amc_codec_dequeue_input_buffer (GstAmcCodec * codec, gint64 timeoutUs,
|
|
GError ** err)
|
|
{
|
|
MLResult result;
|
|
int64_t index;
|
|
|
|
g_return_val_if_fail (codec != NULL, G_MININT);
|
|
|
|
result = MLMediaCodecDequeueInputBuffer (codec->handle, timeoutUs, &index);
|
|
if (result != MLResult_Ok) {
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Failed to dequeue input buffer %d", result);
|
|
return G_MININT;
|
|
}
|
|
|
|
if (index == MLMediaCodec_TryAgainLater)
|
|
return INFO_TRY_AGAIN_LATER;
|
|
|
|
return index;
|
|
}
|
|
|
|
gint
|
|
gst_amc_codec_dequeue_output_buffer (GstAmcCodec * codec,
|
|
GstAmcBufferInfo * info, gint64 timeoutUs, GError ** err)
|
|
{
|
|
MLMediaCodecBufferInfo info_;
|
|
MLResult result;
|
|
int64_t index;
|
|
|
|
g_return_val_if_fail (codec != NULL, G_MININT);
|
|
|
|
result =
|
|
MLMediaCodecDequeueOutputBuffer (codec->handle, &info_, timeoutUs,
|
|
&index);
|
|
if (result != MLResult_Ok) {
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Failed to dequeue output buffer %d", result);
|
|
return G_MININT;
|
|
}
|
|
|
|
if (index == MLMediaCodec_OutputBuffersChanged) {
|
|
return gst_amc_codec_dequeue_output_buffer (codec, info, timeoutUs, err);
|
|
} else if (index == MLMediaCodec_FormatChanged) {
|
|
return INFO_OUTPUT_FORMAT_CHANGED;
|
|
} else if (index == MLMediaCodec_TryAgainLater) {
|
|
return INFO_TRY_AGAIN_LATER;
|
|
}
|
|
|
|
info->flags = info_.flags;
|
|
|
|
info->offset = info_.offset;
|
|
info->presentation_time_us = info_.presentation_time_us;
|
|
info->size = info_.size;
|
|
|
|
return index;
|
|
}
|
|
|
|
gboolean
|
|
gst_amc_codec_queue_input_buffer (GstAmcCodec * codec, gint index,
|
|
const GstAmcBufferInfo * info, GError ** err)
|
|
{
|
|
MLResult result;
|
|
|
|
g_return_val_if_fail (codec != NULL, FALSE);
|
|
g_return_val_if_fail (info != NULL, FALSE);
|
|
|
|
result = MLMediaCodecQueueInputBuffer (codec->handle, index, info->offset,
|
|
info->size, info->presentation_time_us, info->flags);
|
|
if (result != MLResult_Ok) {
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Failed to queue input buffer %d", result);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gst_amc_codec_release_output_buffer (GstAmcCodec * codec, gint index,
|
|
gboolean render, GError ** err)
|
|
{
|
|
MLResult result;
|
|
|
|
g_return_val_if_fail (codec != NULL, FALSE);
|
|
|
|
result = MLMediaCodecReleaseOutputBuffer (codec->handle, index, render);
|
|
if (result != MLResult_Ok) {
|
|
g_set_error (err, GST_LIBRARY_ERROR, GST_LIBRARY_ERROR_FAILED,
|
|
"Failed to release output buffer %d", result);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GstAmcSurfaceTexture *
|
|
gst_amc_codec_new_surface_texture (GError ** err)
|
|
{
|
|
return (GstAmcSurfaceTexture *) gst_amc_surface_texture_ml_new (err);
|
|
}
|