Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
2076 lines (1689 sloc) 54.4 KB
/*
* Initially based on gstamc.c
*
* Copyright (C) 2013, Canonical Ltd.
* Author: Jim Hodapp <jim.hodapp@canonical.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.h"
#include "gstamc-constants.h"
#include "gstamcvideodec.h"
#include "gstamcaudiodec.h"
#include <gmodule.h>
#include <gst/gst.h>
#include <gst/video/video.h>
#include <gst/audio/audio.h>
#include <string.h>
#include <hybris/media/media_compatibility_layer.h>
#include <hybris/media/media_codec_list.h>
#include <hybris/media/surface_texture_client_hybris.h>
#include <pthread.h>
GST_DEBUG_CATEGORY (gst_amc_debug);
#define GST_CAT_DEFAULT gst_amc_debug
GQuark gst_amc_codec_info_quark = 0;
static GList *codec_infos = NULL;
#ifdef GST_AMC_IGNORE_UNKNOWN_COLOR_FORMATS
static gboolean ignore_unknown_color_formats = TRUE;
#else
static gboolean ignore_unknown_color_formats = FALSE;
#endif
static gboolean accepted_color_formats (GstAmcCodecType * type,
gboolean is_encoder);
/* FIXME: enable support for platform-api again once it's stable enough
* when using it with MIR (currently it creates all sorts of crashes */
#if 0
static struct ua_display *
create_display (void)
{
struct ua_display *display;
display = malloc (sizeof (struct ua_display));
display->display = ua_ui_display_new_with_index (0);
if (display->display == NULL) {
free (display);
return NULL;
}
display->height = ua_ui_display_query_vertical_res (display->display);
display->width = ua_ui_display_query_horizontal_res (display->display);
GST_DEBUG ("Display resolution: (%d,%d)\n", display->height, display->width);
return display;
}
static struct ua_session *
create_session (void)
{
struct ua_session *session;
char argv[1][1];
session = malloc (sizeof (struct ua_session));
session->properties = ua_ui_session_properties_new ();
ua_ui_session_properties_set_type (session->properties, U_USER_SESSION);
session->session = ua_ui_session_new_with_properties (session->properties);
if (!session->session)
GST_WARNING ("Failed to start new Ubuntu Application API session");
/* The UA requires a command line option set, so give it a fake argv array */
argv[0][0] = '\0';
session->app_options =
u_application_options_new_from_cmd_line (1, (char **) argv);
session->app_description = u_application_description_new ();
/* Required by Mir */
session->app_id = u_application_id_new_from_stringn ("gstamc", 6);
u_application_description_set_application_id (session->app_description,
session->app_id);
session->app_lifecycle_delegate = u_application_lifecycle_delegate_new ();
/* No context data to pass to the lifecycle delegate for now */
u_application_lifecycle_delegate_set_context
(session->app_lifecycle_delegate, NULL);
u_application_description_set_application_lifecycle_delegate
(session->app_description, session->app_lifecycle_delegate);
session->app_instance =
u_application_instance_new_from_description_with_options
(session->app_description, session->app_options);
if (!session->app_instance)
GST_WARNING ("Failed to start a new Ubuntu Application API instance");
return session;
}
static struct ua_window *
create_window (struct ua_display *display, struct ua_session *session,
int width, int height)
{
struct ua_window *window;
g_return_val_if_fail (display != NULL, NULL);
g_return_val_if_fail (session != NULL, NULL);
window = malloc (sizeof (struct ua_window));
window->display = display;
window->width = width;
window->height = height;
window->properties = ua_ui_window_properties_new_for_normal_window ();
ua_ui_window_properties_set_titlen (window->properties, "VideoRenderWindow",
13);
ua_ui_window_properties_set_role (window->properties, 1);
ua_ui_window_properties_set_input_cb_and_ctx (window->properties, NULL, NULL);
GST_DEBUG ("Creating new UA window");
window->window =
ua_ui_window_new_for_application_with_properties (session->app_instance,
window->properties);
GST_DEBUG ("Setting window geometry");
window->width = window->display->width;
window->height = window->display->height;
GST_DEBUG ("width: %d, height: %d", window->width, window->height);
if (height != 0 || width != 0)
ua_ui_window_resize (window->window, window->width, window->height);
window->egl_native_window = ua_ui_window_get_native_type (window->window);
return window;
}
static void
destroy_display (struct ua_display *display)
{
g_return_if_fail (display != NULL);
free (display);
}
static void
destroy_session (struct ua_session *session)
{
g_return_if_fail (session != NULL);
/* FIXME: Segfaults on SurfaceFlinger. Can uncomment once the transition to Mir is complete.
if (session->app_options)
u_application_options_destroy (session->app_options);
*/
if (session->app_id)
u_application_id_destroy (session->app_id);
if (session->app_description)
u_application_description_destroy (session->app_description);
free (session);
}
static void
destroy_window (struct ua_window *window)
{
g_return_if_fail (window != NULL);
if (window->properties)
ua_ui_window_properties_destroy (window->properties);
if (window->window)
ua_ui_window_destroy (window->window);
free (window);
}
#endif
static gchar *
locale_to_utf8 (gchar * str, gssize len)
{
GError *error = NULL;
gsize bytes_read = 0, bytes_written = 0;
gchar *out = NULL;
out = g_locale_to_utf8 (str, len, &bytes_read, &bytes_written, &error);
if (bytes_read == 0)
GST_WARNING ("Zero bytes read for UTF8 string conversion");
if (bytes_written == 0)
GST_WARNING ("Zero bytes written for UTF8 string conversion");
return out;
}
GstAmcCodec *
gst_amc_codec_new (const gchar * name)
{
GstAmcCodec *codec = NULL;
gchar *name_str = NULL;
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
g_return_val_if_fail (name != NULL, NULL);
codec = g_slice_new0 (GstAmcCodec);
name_str = g_strdup (name);
name_str = locale_to_utf8 (name_str, strlen (name));
if (name_str == NULL)
goto error;
GST_DEBUG ("codec name '%s'", name_str);
codec->surface_texture_client = NULL;
codec->codec_delegate = media_codec_create_by_codec_name (name_str);
if (codec->codec_delegate == NULL) {
GST_ERROR ("Failed to create codec '%s'", name_str);
goto error;
}
done:
if (name_str)
g_free (name_str);
name_str = NULL;
return codec;
error:
if (codec)
g_slice_free (GstAmcCodec, codec);
codec = NULL;
goto done;
}
void
gst_amc_codec_free (GstAmcCodec * codec)
{
g_return_if_fail (codec != NULL);
g_return_if_fail (codec->codec_delegate != NULL);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
// Cleanup the hardware decoder's resources
media_codec_delegate_unref (codec->codec_delegate);
g_slice_free (GstAmcCodec, codec);
}
gboolean
gst_amc_codec_configure (GstAmcCodec * codec, GstAmcFormat * format,
SurfaceTextureClientHybris stc, gint flags)
{
gboolean ret = TRUE;
int err = 0;
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
g_return_val_if_fail (codec != NULL, FALSE);
g_return_val_if_fail (format != NULL, FALSE);
if (surface_texture_client_hardware_rendering (stc)) {
guint8 i = 0;
const guint8 MAX_TRIES = 25;
/* Make sure that we have a valid texture_id before we proceed with configuring */
while (!surface_texture_client_is_ready_for_rendering (stc)
&& i < MAX_TRIES) {
GST_WARNING
("Surface texture client is not yet ready, waiting a bit for the texture id");
g_usleep (G_USEC_PER_SEC / 5);
++i;
}
if (i >= MAX_TRIES) {
GST_ERROR
("Failed to get a valid texture id, cannot configure the codec.");
ret = FALSE;
goto done;
}
} else {
GST_WARNING ("surface_texture_client_is_ready_for_rendering: %d",
surface_texture_client_is_ready_for_rendering (stc));
if (!surface_texture_client_is_ready_for_rendering (stc)) {
GST_WARNING
("SurfaceTextureClientHybris is not ready for rendering, creating EGLNativeWindowType");
#if 0
/* Create a new Ubuntu Application API session */
codec->session = create_session ();
if (codec->session == NULL) {
GST_ERROR
("Could not initialize Mir output, could not start a Mir app session");
return FALSE;
}
codec->display = create_display ();
if (codec->display == NULL) {
GST_ERROR
("Could not initialize Mir output could not create a Mir display");
return FALSE;
}
/* Create an EGLNativeWindowType instance so that a pure playbin
* scenario will render video */
codec->window =
create_window (codec->display, codec->session, codec->display->width,
codec->display->height);
surface_texture_client_create (codec->window->egl_native_window);
#endif
}
}
err = media_codec_configure (codec->codec_delegate, format->format, stc, 0);
if (err > 0) {
GST_ERROR ("Failed to configure media codec");
ret = FALSE;
goto done;
}
done:
return ret;
}
gboolean
gst_amc_codec_set_surface_texture_client (GstAmcCodec * codec,
SurfaceTextureClientHybris stc)
{
g_return_val_if_fail (codec != NULL, FALSE);
g_return_val_if_fail (stc != NULL, FALSE);
codec->surface_texture_client = stc;
GST_DEBUG_OBJECT (codec, "stc: %p", stc);
GST_DEBUG_OBJECT (codec, "codec->surface_texture_client: %p",
codec->surface_texture_client);
return TRUE;
}
SurfaceTextureClientHybris
gst_amc_codec_get_surface_texture_client (GstAmcCodec * codec)
{
g_return_val_if_fail (codec != NULL, NULL);
return codec->surface_texture_client;
}
gboolean
gst_amc_codec_queue_csd (GstAmcCodec * codec, GstAmcFormat * format)
{
gboolean ret = TRUE;
int err = 0;
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
g_return_val_if_fail (codec != NULL, FALSE);
g_return_val_if_fail (format != NULL, FALSE);
err = media_codec_queue_csd (codec->codec_delegate, format->format);
if (err > 0) {
GST_ERROR ("Failed to queue codec specific data");
ret = FALSE;
goto done;
}
done:
return ret;
}
GstAmcFormat *
gst_amc_codec_get_output_format (GstAmcCodec * codec)
{
GstAmcFormat *ret = NULL;
g_return_val_if_fail (codec != NULL, NULL);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
ret = g_slice_new0 (GstAmcFormat);
ret->format = media_codec_get_output_format (codec->codec_delegate);
if (ret->format == NULL) {
GST_ERROR ("Failed to get output format");
g_slice_free (GstAmcFormat, ret);
ret = NULL;
goto done;
}
done:
return ret;
}
gboolean
gst_amc_codec_start (GstAmcCodec * codec)
{
gboolean ret = TRUE;
int err = 0;
g_return_val_if_fail (codec != NULL, FALSE);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
err = media_codec_start (codec->codec_delegate);
if (err > 0) {
GST_ERROR ("Failed to start media codec");
ret = FALSE;
goto done;
}
done:
return ret;
}
gboolean
gst_amc_codec_stop (GstAmcCodec * codec)
{
gboolean ret = TRUE;
int err = 0;
g_return_val_if_fail (codec != NULL, FALSE);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
err = media_codec_stop (codec->codec_delegate);
if (err > 0) {
GST_ERROR ("Failed to start media codec");
ret = FALSE;
goto done;
}
#if 0
if (codec->window)
destroy_window (codec->window);
if (codec->display)
destroy_display (codec->display);
if (codec->session)
destroy_session (codec->session);
#endif
done:
return ret;
}
gboolean
gst_amc_codec_flush (GstAmcCodec * codec)
{
gboolean ret = TRUE;
gint err = 0;
g_return_val_if_fail (codec != NULL, FALSE);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
err = media_codec_flush (codec->codec_delegate);
if (err < 0) {
GST_ERROR ("Failed to flush the media codec (err: %d)", err);
ret = FALSE;
goto done;
}
done:
return ret;
}
gboolean
gst_amc_codec_release (GstAmcCodec * codec)
{
gboolean ret = TRUE;
gint err = 0;
g_return_val_if_fail (codec != NULL, FALSE);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
err = media_codec_release (codec->codec_delegate);
if (err < 0) {
GST_ERROR ("Failed to release media codec (err: %d)", err);
ret = FALSE;
goto done;
}
done:
return ret;
}
void
gst_amc_codec_free_buffers (GstAmcBuffer * buffers, gsize n_buffers)
{
g_return_if_fail (buffers != NULL);
g_free (buffers);
}
GstAmcBuffer *
gst_amc_codec_get_output_buffers (GstAmcCodec * codec, gsize * n_buffers)
{
size_t n_output_buffers;
GstAmcBuffer *ret = NULL;
size_t i;
g_return_val_if_fail (codec != NULL, NULL);
g_return_val_if_fail (n_buffers != NULL, NULL);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
*n_buffers = 0;
n_output_buffers =
media_codec_get_output_buffers_size (codec->codec_delegate);
if (n_output_buffers == 0) {
GST_ERROR ("Failed to get output buffers array length");
goto done;
}
GST_DEBUG ("n_output_buffers: %u", n_output_buffers);
*n_buffers = n_output_buffers;
ret = g_new0 (GstAmcBuffer, n_output_buffers);
for (i = 0; i < n_output_buffers; i++) {
ret[i].data = media_codec_get_nth_output_buffer (codec->codec_delegate, i);
// It's no longer an error if the buffer address is zero, it just means
// MediaCodec with v4.4+ doesn't return the buffer address when doing hardware
// rendering
if (!ret[i].data)
GST_DEBUG ("Output buffer address is NULL for buffer #%d", i);
ret[i].size =
media_codec_get_nth_output_buffer_capacity (codec->codec_delegate, i);
GST_DEBUG ("output buffer[%d] size: %d", i, ret[i].size);
}
done:
return ret;
}
GstAmcBuffer *
gst_amc_codec_get_input_buffers (GstAmcCodec * codec, gsize * n_buffers)
{
size_t n_input_buffers;
GstAmcBuffer *ret = NULL;
size_t i;
g_return_val_if_fail (codec != NULL, NULL);
g_return_val_if_fail (n_buffers != NULL, NULL);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
*n_buffers = 0;
n_input_buffers = media_codec_get_input_buffers_size (codec->codec_delegate);
if (n_input_buffers == 0) {
GST_ERROR ("Failed to get input buffers array length");
goto done;
}
GST_DEBUG ("n_input_buffers: %u", n_input_buffers);
*n_buffers = n_input_buffers;
ret = g_new0 (GstAmcBuffer, n_input_buffers);
for (i = 0; i < n_input_buffers; i++) {
ret[i].data = media_codec_get_nth_input_buffer (codec->codec_delegate, i);
if (!ret[i].data) {
GST_ERROR ("Failed to get input buffer address %d", i);
goto error;
}
ret[i].size =
media_codec_get_nth_input_buffer_capacity (codec->codec_delegate, i);
GST_DEBUG ("input buffer[%d] size: %d", i, ret[i].size);
}
done:
return ret;
error:
if (ret)
gst_amc_codec_free_buffers (ret, n_input_buffers);
ret = NULL;
*n_buffers = 0;
goto done;
}
gint
gst_amc_codec_dequeue_input_buffer (GstAmcCodec * codec, gint64 timeoutUs)
{
gint ret = G_MININT;
size_t index = 0;
g_return_val_if_fail (codec != NULL, G_MININT);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
ret =
media_codec_dequeue_input_buffer (codec->codec_delegate, &index,
timeoutUs);
if (ret < 0) {
GST_DEBUG ("Failed to dequeue input buffer (ret: %d)", ret);
if (ret == -11)
ret = INFO_TRY_AGAIN_LATER;
goto done;
}
ret = index;
GST_DEBUG ("Dequeued input buffer #%d", index);
done:
return ret;
}
gint
gst_amc_codec_dequeue_output_buffer (GstAmcCodec * codec,
GstAmcBufferInfo * info, gint64 timeoutUs)
{
gint ret = G_MININT;
MediaCodecBufferInfo priv_info;
g_return_val_if_fail (codec != NULL, G_MININT);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
ret =
media_codec_dequeue_output_buffer (codec->codec_delegate, &priv_info,
timeoutUs);
GST_DEBUG ("dequeue output buffer ret: %d", ret);
if (ret == INFO_TRY_AGAIN_LATER) {
GST_DEBUG ("media_codec_dequeue_output_buffer timed out, trying again");
info->flags = 0;
info->offset = 0;
info->size = 0;
info->presentation_time_us = 0;
goto done;
} else if (ret == INFO_OUTPUT_FORMAT_CHANGED) {
GST_INFO ("Output format has changed");
goto done;
} else if (ret == INFO_OUTPUT_BUFFERS_CHANGED) {
GST_INFO ("Output buffers have changed");
goto done;
}
info->flags = priv_info.flags;
info->offset = priv_info.offset;
info->size = priv_info.size;
info->presentation_time_us = priv_info.presentation_time_us;
GST_DEBUG ("info->flags: %d", info->flags);
GST_DEBUG ("info->offset: %d", info->offset);
GST_DEBUG ("info->size: %d", info->size);
GST_DEBUG ("info->presentation_time_us: %lld", info->presentation_time_us);
done:
return ret;
}
gboolean
gst_amc_codec_queue_input_buffer (GstAmcCodec * codec, gint index,
const GstAmcBufferInfo * info)
{
gboolean ret = TRUE;
gint err = 0;
MediaCodecBufferInfo buf_info;
g_return_val_if_fail (codec != NULL, FALSE);
g_return_val_if_fail (info != NULL, FALSE);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
buf_info.index = index;
buf_info.offset = info->offset;
buf_info.size = info->size;
buf_info.presentation_time_us = info->presentation_time_us;
buf_info.flags = info->flags;
GST_DEBUG ("buf_info.index: %d", buf_info.index);
GST_DEBUG ("buf_info.offset %d", buf_info.offset);
GST_DEBUG ("buf_info.size %d", buf_info.size);
GST_DEBUG ("buf_info.presentation_time_us %lld",
buf_info.presentation_time_us);
GST_DEBUG ("buf_info.flags %d", buf_info.flags);
err = media_codec_queue_input_buffer (codec->codec_delegate, &buf_info);
if (err < 0) {
GST_ERROR ("Failed to queue input buffer (err: %d, index: %d)", err, index);
ret = FALSE;
goto done;
}
done:
return ret;
}
gboolean
gst_amc_codec_release_output_buffer (GstAmcCodec * codec, gint index,
gboolean render)
{
gboolean ret = TRUE;
gint err = 0;
g_return_val_if_fail (codec != NULL, FALSE);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
err =
media_codec_release_output_buffer (codec->codec_delegate, index, render);
if (err < 0) {
GST_ERROR ("Failed to release output buffer (err: %d, index: %d)", err,
index);
ret = FALSE;
goto done;
}
done:
return ret;
}
GstAmcFormat *
gst_amc_format_new_audio (const gchar * mime, gint sample_rate, gint channels)
{
#if 0
JNIEnv *env;
GstAmcFormat *format = NULL;
jstring mime_str;
jobject object = NULL;
#endif
g_return_val_if_fail (mime != NULL, NULL);
#if 0
env = gst_amc_get_jni_env ();
mime_str = (*env)->NewStringUTF (env, mime);
if (mime_str == NULL)
goto error;
format = g_slice_new0 (GstAmcFormat);
object =
(*env)->CallStaticObjectMethod (env, media_format.klass,
media_format.create_audio_format, mime_str, sample_rate, channels);
if ((*env)->ExceptionCheck (env) || !object) {
(*env)->ExceptionClear (env);
GST_ERROR ("Failed to create format '%s'", mime);
goto error;
}
format->object = (*env)->NewGlobalRef (env, object);
if (!format->object) {
GST_ERROR ("Failed to create global reference");
(*env)->ExceptionClear (env);
goto error;
}
done:
if (object)
(*env)->DeleteLocalRef (env, object);
if (mime_str)
(*env)->DeleteLocalRef (env, mime_str);
mime_str = NULL;
return format;
error:
if (format)
g_slice_free (GstAmcFormat, format);
format = NULL;
goto done;
#endif
return NULL;
}
GstAmcFormat *
gst_amc_format_new_video (const gchar * mime, gint width, gint height,
gint buffsize)
{
GstAmcFormat *format = NULL;
gchar *mime_str = NULL;
g_return_val_if_fail (mime != NULL, NULL);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
mime_str = g_strdup (mime);
mime_str = locale_to_utf8 (mime_str, strlen (mime_str));
if (mime_str == NULL)
goto error;
format = g_slice_new0 (GstAmcFormat);
format->format =
media_format_create_video_format (mime_str, width, height, 0, buffsize);
if (format->format == NULL) {
GST_ERROR ("Failed to create format '%s'", mime);
goto error;
}
done:
if (mime_str)
g_free (mime_str);
mime_str = NULL;
return format;
error:
if (format)
g_slice_free (GstAmcFormat, format);
format = NULL;
goto done;
}
void
gst_amc_format_free (GstAmcFormat * format)
{
g_return_if_fail (format != NULL);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
media_format_destroy (format->format);
g_slice_free (GstAmcFormat, format);
}
gchar *
gst_amc_format_to_string (GstAmcFormat * format)
{
return NULL;
}
gboolean
gst_amc_format_contains_key (GstAmcFormat * format, const gchar * key)
{
return FALSE;
}
gboolean
gst_amc_format_get_float (GstAmcFormat * format, const gchar * key,
gfloat * value)
{
return FALSE;
}
void
gst_amc_format_set_float (GstAmcFormat * format, const gchar * key,
gfloat value)
{
}
gboolean
gst_amc_format_get_int (GstAmcFormat * format, const gchar * key, gint * value)
{
return FALSE;
}
void
gst_amc_format_set_int (GstAmcFormat * format, const gchar * key, gint value)
{
}
gboolean
gst_amc_format_get_string (GstAmcFormat * format, const gchar * key,
gchar ** value)
{
return FALSE;
}
void
gst_amc_format_set_string (GstAmcFormat * format, const gchar * key,
const gchar * value)
{
}
gboolean
gst_amc_format_get_buffer (GstAmcFormat * format, const gchar * key,
guint8 ** data, gsize * size)
{
return FALSE;
}
void
gst_amc_format_set_buffer (GstAmcFormat * format, const gchar * key,
guint8 * data, gsize size)
{
gchar *key_str = NULL;
g_return_if_fail (format != NULL);
g_return_if_fail (key != NULL);
g_return_if_fail (data != NULL);
GST_DEBUG ("%s", __PRETTY_FUNCTION__);
key_str = g_strdup (key);
key_str = locale_to_utf8 (key_str, strlen (key));
if (!key_str)
goto done;
media_format_set_byte_buffer (format->format, key, data, size);
done:
if (key_str)
g_free (key_str);
key_str = NULL;
}
void gst_amc_surface_texture_client_set_hardware_rendering
(SurfaceTextureClientHybris stc, gboolean hardware_rendering)
{
surface_texture_client_set_hardware_rendering (stc, hardware_rendering);
}
static gboolean
scan_codecs (GstPlugin * plugin)
{
gboolean ret = TRUE;
guint32 codec_count, i;
const GstStructure *cache_data;
GST_DEBUG ("Scanning available codecs");
if ((cache_data = gst_plugin_get_cache_data (plugin))) {
const GValue *arr = gst_structure_get_value (cache_data, "codecs");
guint i, n;
GST_DEBUG ("Getting codecs from cache");
n = gst_value_array_get_size (arr);
for (i = 0; i < n; i++) {
const GValue *cv = gst_value_array_get_value (arr, i);
const GstStructure *cs = gst_value_get_structure (cv);
const gchar *name;
gboolean is_encoder;
const GValue *starr;
guint j, n2;
GstAmcCodecInfo *gst_codec_info;
gst_codec_info = g_new0 (GstAmcCodecInfo, 1);
name = gst_structure_get_string (cs, "name");
gst_structure_get_boolean (cs, "is-encoder", &is_encoder);
gst_codec_info->name = g_strdup (name);
gst_codec_info->is_encoder = is_encoder;
starr = gst_structure_get_value (cs, "supported-types");
n2 = gst_value_array_get_size (starr);
gst_codec_info->n_supported_types = n2;
gst_codec_info->supported_types = g_new0 (GstAmcCodecType, n2);
for (j = 0; j < n2; j++) {
const GValue *stv = gst_value_array_get_value (starr, j);
const GstStructure *sts = gst_value_get_structure (stv);
const gchar *mime;
const GValue *cfarr;
const GValue *plarr;
guint k, n3;
GstAmcCodecType *gst_codec_type = &gst_codec_info->supported_types[j];
mime = gst_structure_get_string (sts, "mime");
gst_codec_type->mime = g_strdup (mime);
cfarr = gst_structure_get_value (sts, "color-formats");
n3 = gst_value_array_get_size (cfarr);
gst_codec_type->n_color_formats = n3;
gst_codec_type->color_formats = g_new0 (gint, n3);
for (k = 0; k < n3; k++) {
const GValue *cfv = gst_value_array_get_value (cfarr, k);
gint cf = g_value_get_int (cfv);
gst_codec_type->color_formats[k] = cf;
}
plarr = gst_structure_get_value (sts, "profile-levels");
n3 = gst_value_array_get_size (plarr);
gst_codec_type->n_profile_levels = n3;
gst_codec_type->profile_levels =
g_malloc0 (sizeof (gst_codec_type->profile_levels[0]) * n3);
for (k = 0; k < n3; k++) {
const GValue *plv = gst_value_array_get_value (plarr, k);
const GValue *p, *l;
p = gst_value_array_get_value (plv, 0);
l = gst_value_array_get_value (plv, 1);
gst_codec_type->profile_levels[k].profile = g_value_get_int (p);
gst_codec_type->profile_levels[k].level = g_value_get_int (l);
}
}
codec_infos = g_list_append (codec_infos, gst_codec_info);
}
return TRUE;
}
codec_count = media_codec_list_count_codecs ();
if (codec_count == 0) {
GST_ERROR ("Failed to get number of available codecs");
goto done;
}
GST_DEBUG ("Found %d available codecs", codec_count);
for (i = 0; i < codec_count; i++) {
GstAmcCodecInfo *gst_codec_info;
const gchar *name_str = NULL;
gboolean is_encoder;
size_t n_supported_types = 0;
size_t j;
gboolean valid_codec = TRUE;
gst_codec_info = g_new0 (GstAmcCodecInfo, 1);
media_codec_list_get_codec_info_at_id (i);
name_str = media_codec_list_get_codec_name (i);
if (!name_str) {
GST_ERROR ("Failed to get codec name");
valid_codec = FALSE;
goto next_codec;
}
GST_INFO ("Checking codec '%s'", name_str);
/* FIXME: enable software decoders once we have software rendering
* working in mir */
if (g_str_has_prefix (name_str, "OMX.google")) {
GST_INFO ("Skipping Google Software codec '%s'", name_str);
valid_codec = FALSE;
goto next_codec;
}
/* Compatibility codec names */
if (strcmp (name_str, "AACEncoder") == 0 ||
strcmp (name_str, "OMX.google.raw.decoder") == 0) {
GST_INFO ("Skipping compatibility codec '%s'", name_str);
valid_codec = FALSE;
goto next_codec;
}
if (g_str_has_suffix (name_str, ".secure")) {
GST_INFO ("Skipping DRM codec '%s'", name_str);
valid_codec = FALSE;
goto next_codec;
}
/* FIXME: Non-Google codecs usually just don't work and hang forever
* or crash when not used from a process that started the Java
* VM via the non-public AndroidRuntime class. Can we somehow
* initialize all this?
*/
#if 0
if (!g_str_has_prefix (name_str, "OMX.google.")) {
GST_INFO ("Skipping non-Google codec '%s' in standalone mode", name_str);
valid_codec = FALSE;
goto next_codec;
}
#endif
if (g_str_has_prefix (name_str, "OMX.ARICENT.")) {
GST_INFO ("Skipping possible broken codec '%s'", name_str);
valid_codec = FALSE;
goto next_codec;
}
/* FIXME:
* - Vorbis: Generates clicks for multi-channel streams
* - *Law: Generates output with too low frequencies
*/
if (strcmp (name_str, "OMX.google.vorbis.decoder") == 0 ||
strcmp (name_str, "OMX.google.g711.alaw.decoder") == 0 ||
strcmp (name_str, "OMX.google.g711.mlaw.decoder") == 0) {
GST_INFO ("Skipping known broken codec '%s'", name_str);
valid_codec = FALSE;
goto next_codec;
}
gst_codec_info->name = g_strdup (name_str);
is_encoder = media_codec_list_is_encoder (i);
gst_codec_info->is_encoder = is_encoder;
n_supported_types = media_codec_list_get_num_supported_types (i);
GST_INFO ("Codec '%s' has %d supported types", name_str, n_supported_types);
gst_codec_info->supported_types =
g_new0 (GstAmcCodecType, n_supported_types);
gst_codec_info->n_supported_types = n_supported_types;
if (n_supported_types == 0) {
valid_codec = FALSE;
GST_ERROR ("Codec has no supported types");
goto next_codec;
}
for (j = 0; j < n_supported_types; j++) {
GstAmcCodecType *gst_codec_type;
gchar *supported_type_str;
guint32 *color_formats_elems = NULL;
size_t n_elems = 0, k;
int err = 0;
size_t len = 0;
gchar *mime = NULL;
gst_codec_type = &gst_codec_info->supported_types[j];
len = media_codec_list_get_nth_supported_type_len (i, j);
supported_type_str = g_malloc (len);
err = media_codec_list_get_nth_supported_type (i, supported_type_str, j);
if (err > 0 || !supported_type_str) {
GST_ERROR ("Failed to get %d-th supported type", j);
valid_codec = FALSE;
goto next_supported_type;
}
mime = g_malloc (len);
mime = locale_to_utf8 (supported_type_str, len);
if (!mime) {
GST_ERROR ("Failed to convert supported type to UTF8");
valid_codec = FALSE;
goto next_supported_type;
}
GST_INFO ("Supported type '%s'", mime);
gst_codec_type->mime = g_strdup (mime);
n_elems = media_codec_list_get_num_color_formats (i, mime);
GST_INFO ("Type '%s' has %d supported color formats", mime, n_elems);
if (n_elems == 0) {
GST_INFO ("Zero supported color formats for type '%s'", mime);
valid_codec = FALSE;
goto next_supported_type;
}
gst_codec_type->n_color_formats = n_elems;
gst_codec_type->color_formats = g_new0 (gint, n_elems);
color_formats_elems = g_new0 (guint32, n_elems);
err =
media_codec_list_get_codec_color_formats (i, mime,
color_formats_elems);
if (!color_formats_elems) {
GST_ERROR ("Failed to get color format elements");
valid_codec = FALSE;
goto next_supported_type;
}
for (k = 0; k < n_elems; k++) {
GST_INFO ("Color format %d: %d", k, color_formats_elems[k]);
gst_codec_type->color_formats[k] = color_formats_elems[k];
}
if (g_str_has_prefix (gst_codec_type->mime, "video/")) {
if (!n_elems) {
GST_ERROR ("No supported color formats for video codec");
valid_codec = FALSE;
goto next_supported_type;
}
if (!ignore_unknown_color_formats
&& !accepted_color_formats (gst_codec_type, is_encoder)) {
GST_ERROR ("Codec has unknown color formats, ignoring");
valid_codec = FALSE;
g_assert_not_reached ();
goto next_supported_type;
}
}
n_elems = media_codec_list_get_num_profile_levels (i, mime);
GST_INFO ("Type '%s' has %d supported profile levels", mime, n_elems);
gst_codec_type->n_profile_levels = n_elems;
gst_codec_type->profile_levels =
g_malloc0 (sizeof (gst_codec_type->profile_levels[0]) * n_elems);
for (k = 0; k < n_elems; k++) {
guint32 level = 0, profile = 0;
profile_level pro_level;
err =
media_codec_list_get_nth_codec_profile_level (i, mime, &pro_level,
k);
if (err > 0) {
GST_ERROR ("Failed to get %d-th profile/level", k);
valid_codec = FALSE;
goto next_profile_level;
}
level = pro_level.level;
profile = pro_level.profile;
GST_INFO ("Level %d: 0x%08x", k, level);
gst_codec_type->profile_levels[k].level = level;
GST_INFO ("Profile %d: 0x%08x", k, profile);
gst_codec_type->profile_levels[k].profile = profile;
next_profile_level:
if (!valid_codec)
break;
}
next_supported_type:
if (color_formats_elems)
g_free (color_formats_elems);
color_formats_elems = NULL;
if (supported_type_str)
g_free (supported_type_str);
supported_type_str = NULL;
if (mime)
g_free (mime);
mime = NULL;
if (!valid_codec)
break;
}
/* We need at least a valid supported type */
if (valid_codec) {
GST_LOG ("Successfully scanned codec '%s'", name_str);
codec_infos = g_list_append (codec_infos, gst_codec_info);
gst_codec_info = NULL;
}
/* Clean up of all local references we got */
next_codec:
if (gst_codec_info) {
gint j;
for (j = 0; j < gst_codec_info->n_supported_types; j++) {
GstAmcCodecType *gst_codec_type = &gst_codec_info->supported_types[j];
g_free (gst_codec_type->mime);
g_free (gst_codec_type->color_formats);
g_free (gst_codec_type->profile_levels);
}
g_free (gst_codec_info->supported_types);
g_free (gst_codec_info->name);
g_free (gst_codec_info);
}
gst_codec_info = NULL;
valid_codec = TRUE;
}
ret = codec_infos != NULL;
/* If successful we store a cache of the codec information in
* the registry. Otherwise we would always load all codecs during
* plugin initialization which can take quite some time (because
* of hardware) and also loads lots of shared libraries (which
* number is limited by 64 in Android).
*/
if (ret) {
GstStructure *new_cache_data = gst_structure_new_empty ("gst-amc-cache");
GList *l;
GValue arr = { 0, };
g_value_init (&arr, GST_TYPE_ARRAY);
for (l = codec_infos; l; l = l->next) {
GstAmcCodecInfo *gst_codec_info = l->data;
GValue cv = { 0, };
GstStructure *cs = gst_structure_new_empty ("gst-amc-codec");
GValue starr = { 0, };
gint i;
gst_structure_set (cs, "name", G_TYPE_STRING, gst_codec_info->name,
"is-encoder", G_TYPE_BOOLEAN, gst_codec_info->is_encoder, NULL);
g_value_init (&starr, GST_TYPE_ARRAY);
for (i = 0; i < gst_codec_info->n_supported_types; i++) {
GstAmcCodecType *gst_codec_type = &gst_codec_info->supported_types[i];
GstStructure *sts = gst_structure_new_empty ("gst-amc-supported-type");
GValue stv = { 0, };
GValue tmparr = { 0, };
gint j;
gst_structure_set (sts, "mime", G_TYPE_STRING, gst_codec_type->mime,
NULL);
g_value_init (&tmparr, GST_TYPE_ARRAY);
for (j = 0; j < gst_codec_type->n_color_formats; j++) {
GValue tmp = { 0, };
g_value_init (&tmp, G_TYPE_INT);
g_value_set_int (&tmp, gst_codec_type->color_formats[j]);
gst_value_array_append_value (&tmparr, &tmp);
g_value_unset (&tmp);
}
gst_structure_set_value (sts, "color-formats", &tmparr);
g_value_unset (&tmparr);
g_value_init (&tmparr, GST_TYPE_ARRAY);
for (j = 0; j < gst_codec_type->n_profile_levels; j++) {
GValue tmparr2 = { 0, };
GValue tmp = { 0, };
g_value_init (&tmparr2, GST_TYPE_ARRAY);
g_value_init (&tmp, G_TYPE_INT);
g_value_set_int (&tmp, gst_codec_type->profile_levels[j].profile);
gst_value_array_append_value (&tmparr2, &tmp);
g_value_set_int (&tmp, gst_codec_type->profile_levels[j].level);
gst_value_array_append_value (&tmparr2, &tmp);
gst_value_array_append_value (&tmparr, &tmparr2);
g_value_unset (&tmp);
g_value_unset (&tmparr2);
}
gst_structure_set_value (sts, "profile-levels", &tmparr);
g_value_init (&stv, GST_TYPE_STRUCTURE);
gst_value_set_structure (&stv, sts);
gst_value_array_append_value (&starr, &stv);
g_value_unset (&tmparr);
gst_structure_free (sts);
}
gst_structure_set_value (cs, "supported-types", &starr);
g_value_unset (&starr);
g_value_init (&cv, GST_TYPE_STRUCTURE);
gst_value_set_structure (&cv, cs);
gst_value_array_append_value (&arr, &cv);
g_value_unset (&cv);
gst_structure_free (cs);
}
gst_structure_set_value (new_cache_data, "codecs", &arr);
g_value_unset (&arr);
gst_plugin_set_cache_data (plugin, new_cache_data);
}
done:
return ret;
}
static const struct
{
gint color_format;
GstVideoFormat video_format;
} color_format_mapping_table[] = {
{
COLOR_FormatYUV420Planar, GST_VIDEO_FORMAT_I420}, {
COLOR_FormatYUV420SemiPlanar, GST_VIDEO_FORMAT_NV12}, {
COLOR_TI_FormatYUV420PackedSemiPlanar, GST_VIDEO_FORMAT_NV12}, {
COLOR_TI_FormatYUV420PackedSemiPlanarInterlaced, GST_VIDEO_FORMAT_NV12}, {
COLOR_QCOM_FormatYUV420SemiPlanar, GST_VIDEO_FORMAT_NV12}, {
COLOR_QCOM_FormatYUV420PackedSemiPlanar64x32Tile2m8ka, GST_VIDEO_FORMAT_NV12}, {
COLOR_EXYNOS_FormatNV12Tiled, GST_VIDEO_FORMAT_NV12}, {
COLOR_EXYNOS_FormatNV21Linear, GST_VIDEO_FORMAT_NV21}, {
256, GST_VIDEO_FORMAT_NV12}, {
263, GST_VIDEO_FORMAT_NV12}, {
COLOR_MTK_FormatYV12, GST_VIDEO_FORMAT_YV12}, {
COLOR_QCOM_FormatYVU420SemiPlanar32m, GST_VIDEO_FORMAT_NV12}
};
static gboolean
accepted_color_formats (GstAmcCodecType * type, gboolean is_encoder)
{
gint i, j;
gint accepted = 0, all = type->n_color_formats;
for (i = 0; i < type->n_color_formats; i++) {
gboolean found = FALSE;
/* We ignore this one */
if (type->color_formats[i] == COLOR_FormatAndroidOpaque)
all--;
for (j = 0; j < G_N_ELEMENTS (color_format_mapping_table); j++) {
//g_print("color_format_mapping_table[%d].color_format: %d, type->color_formats[%d]: %d", j, color_format_mapping_table[j].color_format, i, type->color_formats[i]);
if (color_format_mapping_table[j].color_format == type->color_formats[i]) {
found = TRUE;
break;
}
}
if (found)
accepted++;
}
if (is_encoder)
return accepted > 0;
else
return accepted == all && all > 0;
}
GstVideoFormat
gst_amc_color_format_to_video_format (gint color_format)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (color_format_mapping_table); i++) {
if (color_format_mapping_table[i].color_format == color_format)
return color_format_mapping_table[i].video_format;
}
return GST_VIDEO_FORMAT_UNKNOWN;
}
gint
gst_amc_video_format_to_color_format (GstVideoFormat video_format)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (color_format_mapping_table); i++) {
if (color_format_mapping_table[i].video_format == video_format)
return color_format_mapping_table[i].color_format;
}
return -1;
}
static const struct
{
gint id;
const gchar *str;
const gchar *alt_str;
} avc_profile_mapping_table[] = {
{
AVCProfileBaseline, "baseline", "constrained-baseline"}, {
AVCProfileMain, "main", NULL}, {
AVCProfileExtended, "extended", NULL}, {
AVCProfileHigh, "high"}, {
AVCProfileHigh10, "high-10", "high-10-intra"}, {
AVCProfileHigh422, "high-4:2:2", "high-4:2:2-intra"}, {
AVCProfileHigh444, "high-4:4:4", "high-4:4:4-intra"}
};
const gchar *
gst_amc_avc_profile_to_string (gint profile, const gchar ** alternative)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (avc_profile_mapping_table); i++) {
if (avc_profile_mapping_table[i].id == profile) {
*alternative = avc_profile_mapping_table[i].alt_str;
return avc_profile_mapping_table[i].str;
}
}
return NULL;
}
gint
gst_amc_avc_profile_from_string (const gchar * profile)
{
gint i;
g_return_val_if_fail (profile != NULL, -1);
for (i = 0; i < G_N_ELEMENTS (avc_profile_mapping_table); i++) {
if (strcmp (avc_profile_mapping_table[i].str, profile) == 0)
return avc_profile_mapping_table[i].id;
}
return -1;
}
static const struct
{
gint id;
const gchar *str;
} avc_level_mapping_table[] = {
{
AVCLevel1, "1"}, {
AVCLevel1b, "1b"}, {
AVCLevel11, "1.1"}, {
AVCLevel12, "1.2"}, {
AVCLevel13, "1.3"}, {
AVCLevel2, "2"}, {
AVCLevel21, "2.1"}, {
AVCLevel22, "2.2"}, {
AVCLevel3, "3"}, {
AVCLevel31, "3.1"}, {
AVCLevel32, "3.2"}, {
AVCLevel4, "4"}, {
AVCLevel41, "4.1"}, {
AVCLevel42, "4.2"}, {
AVCLevel5, "5"}, {
AVCLevel51, "5.1"}
};
const gchar *
gst_amc_avc_level_to_string (gint level)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (avc_level_mapping_table); i++) {
if (avc_level_mapping_table[i].id == level)
return avc_level_mapping_table[i].str;
}
return NULL;
}
gint
gst_amc_avc_level_from_string (const gchar * level)
{
gint i;
g_return_val_if_fail (level != NULL, -1);
for (i = 0; i < G_N_ELEMENTS (avc_level_mapping_table); i++) {
if (strcmp (avc_level_mapping_table[i].str, level) == 0)
return avc_level_mapping_table[i].id;
}
return -1;
}
static const struct
{
gint id;
gint gst_id;
} h263_profile_mapping_table[] = {
{
H263ProfileBaseline, 0}, {
H263ProfileH320Coding, 1}, {
H263ProfileBackwardCompatible, 2}, {
H263ProfileISWV2, 3}, {
H263ProfileISWV3, 4}, {
H263ProfileHighCompression, 5}, {
H263ProfileInternet, 6}, {
H263ProfileInterlace, 7}, {
H263ProfileHighLatency, 8}
};
gint
gst_amc_h263_profile_to_gst_id (gint profile)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (h263_profile_mapping_table); i++) {
if (h263_profile_mapping_table[i].id == profile)
return h263_profile_mapping_table[i].gst_id;
}
return -1;
}
gint
gst_amc_h263_profile_from_gst_id (gint profile)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (h263_profile_mapping_table); i++) {
if (h263_profile_mapping_table[i].gst_id == profile)
return h263_profile_mapping_table[i].id;
}
return -1;
}
static const struct
{
gint id;
gint gst_id;
} h263_level_mapping_table[] = {
{
H263Level10, 10}, {
H263Level20, 20}, {
H263Level30, 30}, {
H263Level40, 40}, {
H263Level50, 50}, {
H263Level60, 60}, {
H263Level70, 70}
};
gint
gst_amc_h263_level_to_gst_id (gint level)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (h263_level_mapping_table); i++) {
if (h263_level_mapping_table[i].id == level)
return h263_level_mapping_table[i].gst_id;
}
return -1;
}
gint
gst_amc_h263_level_from_gst_id (gint level)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (h263_level_mapping_table); i++) {
if (h263_level_mapping_table[i].gst_id == level)
return h263_level_mapping_table[i].id;
}
return -1;
}
static const struct
{
gint id;
const gchar *str;
} mpeg4_profile_mapping_table[] = {
{
MPEG4ProfileSimple, "simple"}, {
MPEG4ProfileSimpleScalable, "simple-scalable"}, {
MPEG4ProfileCore, "core"}, {
MPEG4ProfileMain, "main"}, {
MPEG4ProfileNbit, "n-bit"}, {
MPEG4ProfileScalableTexture, "scalable"}, {
MPEG4ProfileSimpleFace, "simple-face"}, {
MPEG4ProfileSimpleFBA, "simple-fba"}, {
MPEG4ProfileBasicAnimated, "basic-animated-texture"}, {
MPEG4ProfileHybrid, "hybrid"}, {
MPEG4ProfileAdvancedRealTime, "advanced-real-time"}, {
MPEG4ProfileCoreScalable, "core-scalable"}, {
MPEG4ProfileAdvancedCoding, "advanced-coding-efficiency"}, {
MPEG4ProfileAdvancedCore, "advanced-core"}, {
MPEG4ProfileAdvancedScalable, "advanced-scalable-texture"}, {
MPEG4ProfileAdvancedSimple, "advanced-simple"}
};
const gchar *
gst_amc_mpeg4_profile_to_string (gint profile)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (mpeg4_profile_mapping_table); i++) {
if (mpeg4_profile_mapping_table[i].id == profile)
return mpeg4_profile_mapping_table[i].str;
}
return NULL;
}
gint
gst_amc_avc_mpeg4_profile_from_string (const gchar * profile)
{
gint i;
g_return_val_if_fail (profile != NULL, -1);
for (i = 0; i < G_N_ELEMENTS (mpeg4_profile_mapping_table); i++) {
if (strcmp (mpeg4_profile_mapping_table[i].str, profile) == 0)
return mpeg4_profile_mapping_table[i].id;
}
return -1;
}
static const struct
{
gint id;
const gchar *str;
} mpeg4_level_mapping_table[] = {
{
MPEG4Level0, "0"}, {
MPEG4Level0b, "0b"}, {
MPEG4Level1, "1"}, {
MPEG4Level2, "2"}, {
MPEG4Level3, "3"}, {
MPEG4Level4, "4"}, {
MPEG4Level4a, "4a"}, {
MPEG4Level5, "5"},};
const gchar *
gst_amc_mpeg4_level_to_string (gint level)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (mpeg4_level_mapping_table); i++) {
if (mpeg4_level_mapping_table[i].id == level)
return mpeg4_level_mapping_table[i].str;
}
return NULL;
}
gint
gst_amc_mpeg4_level_from_string (const gchar * level)
{
gint i;
g_return_val_if_fail (level != NULL, -1);
for (i = 0; i < G_N_ELEMENTS (mpeg4_level_mapping_table); i++) {
if (strcmp (mpeg4_level_mapping_table[i].str, level) == 0)
return mpeg4_level_mapping_table[i].id;
}
return -1;
}
static const struct
{
gint id;
const gchar *str;
} aac_profile_mapping_table[] = {
{
AACObjectMain, "main"}, {
AACObjectLC, "lc"}, {
AACObjectSSR, "ssr"}, {
AACObjectLTP, "ltp"}
};
const gchar *
gst_amc_aac_profile_to_string (gint profile)
{
gint i;
for (i = 0; i < G_N_ELEMENTS (aac_profile_mapping_table); i++) {
if (aac_profile_mapping_table[i].id == profile)
return aac_profile_mapping_table[i].str;
}
return NULL;
}
gint
gst_amc_aac_profile_from_string (const gchar * profile)
{
gint i;
g_return_val_if_fail (profile != NULL, -1);
for (i = 0; i < G_N_ELEMENTS (aac_profile_mapping_table); i++) {
if (strcmp (aac_profile_mapping_table[i].str, profile) == 0)
return aac_profile_mapping_table[i].id;
}
return -1;
}
static const struct
{
guint32 mask;
GstAudioChannelPosition pos;
} channel_mapping_table[] = {
{
CHANNEL_OUT_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT}, {
CHANNEL_OUT_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT}, {
CHANNEL_OUT_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER}, {
CHANNEL_OUT_LOW_FREQUENCY, GST_AUDIO_CHANNEL_POSITION_LFE1}, {
CHANNEL_OUT_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT}, {
CHANNEL_OUT_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT}, {
CHANNEL_OUT_FRONT_LEFT_OF_CENTER,
GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER}, {
CHANNEL_OUT_FRONT_RIGHT_OF_CENTER,
GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER}, {
CHANNEL_OUT_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER}, {
CHANNEL_OUT_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT}, {
CHANNEL_OUT_SIDE_RIGHT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT}, {
CHANNEL_OUT_TOP_CENTER, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
CHANNEL_OUT_TOP_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
CHANNEL_OUT_TOP_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
CHANNEL_OUT_TOP_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
CHANNEL_OUT_TOP_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
CHANNEL_OUT_TOP_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_INVALID}, {
CHANNEL_OUT_TOP_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_INVALID}
};
gboolean
gst_amc_audio_channel_mask_to_positions (guint32 channel_mask, gint channels,
GstAudioChannelPosition * pos)
{
gint i, j;
if (channel_mask == 0) {
if (channels == 1) {
pos[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
return TRUE;
}
if (channels == 2) {
pos[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
pos[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
return TRUE;
}
/* Now let the guesswork begin, these are the
* AAC default channel assignments for these numbers
* of channels */
if (channels == 3) {
channel_mask =
CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER;
} else if (channels == 4) {
channel_mask =
CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_CENTER;
} else if (channels == 5) {
channel_mask =
CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_LEFT |
CHANNEL_OUT_BACK_RIGHT;
} else if (channels == 6) {
channel_mask =
CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_LEFT |
CHANNEL_OUT_BACK_RIGHT | CHANNEL_OUT_LOW_FREQUENCY;
} else if (channels == 8) {
channel_mask =
CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_BACK_LEFT |
CHANNEL_OUT_BACK_RIGHT | CHANNEL_OUT_LOW_FREQUENCY |
CHANNEL_OUT_FRONT_LEFT_OF_CENTER | CHANNEL_OUT_FRONT_RIGHT_OF_CENTER;
}
}
for (i = 0, j = 0; i < G_N_ELEMENTS (channel_mapping_table); i++) {
if ((channel_mask & channel_mapping_table[i].mask)) {
pos[j++] = channel_mapping_table[i].pos;
if (channel_mapping_table[i].pos == GST_AUDIO_CHANNEL_POSITION_INVALID) {
memset (pos, 0, sizeof (GstAudioChannelPosition) * channels);
GST_ERROR ("Unable to map channel mask 0x%08x",
channel_mapping_table[i].mask);
return FALSE;
}
if (j == channels)
break;
}
}
if (j != channels) {
memset (pos, 0, sizeof (GstAudioChannelPosition) * channels);
GST_ERROR ("Unable to map all channel positions in mask 0x%08x",
channel_mask);
return FALSE;
}
return TRUE;
}
guint32
gst_amc_audio_channel_mask_from_positions (GstAudioChannelPosition * positions,
gint channels)
{
gint i, j;
guint32 channel_mask = 0;
if (channels == 1 && !positions)
return CHANNEL_OUT_FRONT_CENTER;
if (channels == 2 && !positions)
return CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT;
for (i = 0; i < channels; i++) {
if (positions[i] == GST_AUDIO_CHANNEL_POSITION_INVALID)
return 0;
for (j = 0; j < G_N_ELEMENTS (channel_mapping_table); j++) {
if (channel_mapping_table[j].pos == positions[i]) {
channel_mask |= channel_mapping_table[j].mask;
break;
}
}
if (j == G_N_ELEMENTS (channel_mapping_table)) {
GST_ERROR ("Unable to map channel position %d", positions[i]);
return 0;
}
}
return channel_mask;
}
static gchar *
create_type_name (const gchar * parent_name, const gchar * codec_name)
{
gchar *typified_name;
gint i, k;
gint parent_name_len = strlen (parent_name);
gint codec_name_len = strlen (codec_name);
gboolean upper = TRUE;
typified_name = g_new0 (gchar, parent_name_len + 1 + strlen (codec_name) + 1);
memcpy (typified_name, parent_name, parent_name_len);
typified_name[parent_name_len] = '-';
for (i = 0, k = 0; i < codec_name_len; i++) {
if (g_ascii_isalnum (codec_name[i])) {
if (upper)
typified_name[parent_name_len + 1 + k++] =
g_ascii_toupper (codec_name[i]);
else
typified_name[parent_name_len + 1 + k++] =
g_ascii_tolower (codec_name[i]);
upper = FALSE;
} else {
/* Skip all non-alnum chars and start a new upper case word */
upper = TRUE;
}
}
return typified_name;
}
static gchar *
create_element_name (gboolean video, gboolean encoder, const gchar * codec_name)
{
#define PREFIX_LEN 10
static const gchar *prefixes[] = {
"amcviddec-",
"amcauddec-",
"amcvidenc-",
"amcaudenc-"
};
gchar *element_name;
gint i, k;
gint codec_name_len = strlen (codec_name);
const gchar *prefix;
if (video && !encoder)
prefix = prefixes[0];
else if (!video && !encoder)
prefix = prefixes[1];
else if (video && encoder)
prefix = prefixes[2];
else
prefix = prefixes[3];
element_name = g_new0 (gchar, PREFIX_LEN + strlen (codec_name) + 1);
memcpy (element_name, prefix, PREFIX_LEN);
for (i = 0, k = 0; i < codec_name_len; i++) {
if (g_ascii_isalnum (codec_name[i])) {
element_name[PREFIX_LEN + k++] = g_ascii_tolower (codec_name[i]);
}
/* Skip all non-alnum chars */
}
return element_name;
}
#undef PREFIX_LEN
static gboolean
register_codecs (GstPlugin * plugin)
{
gboolean ret = TRUE;
GList *l;
GST_DEBUG ("Registering plugins");
for (l = codec_infos; l; l = l->next) {
GstAmcCodecInfo *codec_info = l->data;
gboolean is_audio = FALSE;
gboolean is_video = FALSE;
gint i;
gint n_types;
GST_DEBUG ("Registering codec '%s'", codec_info->name);
for (i = 0; i < codec_info->n_supported_types; i++) {
GstAmcCodecType *codec_type = &codec_info->supported_types[i];
if (g_str_has_prefix (codec_type->mime, "audio/"))
is_audio = TRUE;
else if (g_str_has_prefix (codec_type->mime, "video/"))
is_video = TRUE;
}
n_types = 0;
if (is_audio)
n_types++;
if (is_video)
n_types++;
for (i = 0; i < n_types; i++) {
GTypeQuery type_query;
GTypeInfo type_info = { 0, };
GType type, subtype;
gchar *type_name, *element_name;
guint rank;
if (is_video && !codec_info->is_encoder) {
type = gst_amc_video_dec_get_type ();
} else if (is_audio && !codec_info->is_encoder) {
type = gst_amc_audio_dec_get_type ();
} else {
GST_DEBUG ("Skipping unsupported codec type");
continue;
}
g_type_query (type, &type_query);
memset (&type_info, 0, sizeof (type_info));
type_info.class_size = type_query.class_size;
type_info.instance_size = type_query.instance_size;
type_name = create_type_name (type_query.type_name, codec_info->name);
if (g_type_from_name (type_name) != G_TYPE_INVALID) {
GST_ERROR ("Type '%s' already exists for codec '%s'", type_name,
codec_info->name);
g_free (type_name);
continue;
}
subtype = g_type_register_static (type, type_name, &type_info, 0);
g_free (type_name);
g_type_set_qdata (subtype, gst_amc_codec_info_quark, codec_info);
element_name =
create_element_name (is_video, codec_info->is_encoder,
codec_info->name);
/* Give the Google software codec a secondary rank,
* everything else is likely a hardware codec */
if (g_str_has_prefix (codec_info->name, "OMX.google"))
rank = GST_RANK_SECONDARY;
else
rank = GST_RANK_PRIMARY;
ret |= gst_element_register (plugin, element_name, rank, subtype);
g_free (element_name);
is_video = FALSE;
}
}
return ret;
}
static gboolean
plugin_init (GstPlugin * plugin)
{
const gchar *ignore;
GST_DEBUG_CATEGORY_INIT (gst_amc_debug, "amc", 0, "android-media-codec");
gst_plugin_add_dependency_simple (plugin, NULL, "/system/etc",
"media_codecs.xml", GST_PLUGIN_DEPENDENCY_FLAG_NONE);
/* Set this to TRUE to allow registering decoders that have
* any unknown color formats, or encoders that only have
* unknown color formats
*/
ignore = g_getenv ("GST_AMC_IGNORE_UNKNOWN_COLOR_FORMATS");
if (ignore && strcmp (ignore, "yes") == 0)
ignore_unknown_color_formats = TRUE;
/* Check if the media compat layer is available */
if (!media_compat_check_availability ())
return FALSE;
if (!scan_codecs (plugin))
return FALSE;
gst_amc_codec_info_quark = g_quark_from_static_string ("gst-amc-codec-info");
if (!register_codecs (plugin))
return FALSE;
GST_DEBUG ("Finished %s", __PRETTY_FUNCTION__);
return TRUE;
}
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
GST_VERSION_MINOR,
androidmedia,
"Android Media Hybris plugin",
plugin_init,
PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)