From 76bf63f905962441dd165b89dc96e4bebf7d0564 Mon Sep 17 00:00:00 2001
From: alphanu1 <37101891+alphanu1@users.noreply.github.com>
Date: Wed, 30 Jan 2019 20:17:32 +0000
Subject: [PATCH 01/18] RPi Additions new switching method
Partil new switching method
---
gfx/video_crt_switch.c | 557 ++-
gfx/video_crt_switch.h | 76 +-
gfx/video_display_server.c | 224 +-
gfx/video_display_server.h | 154 +-
gfx/video_driver.c | 7561 ++++++++++++++++++------------------
gfx/video_driver.h | 2618 ++++++-------
6 files changed, 5671 insertions(+), 5519 deletions(-)
diff --git a/gfx/video_crt_switch.c b/gfx/video_crt_switch.c
index d154cfa41b..4a9e582aaa 100644
--- a/gfx/video_crt_switch.c
+++ b/gfx/video_crt_switch.c
@@ -1,206 +1,351 @@
-/* CRT SwitchRes Core
- * Copyright (C) 2018 Alphanu / Ben Templeman.
- *
- * RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2017 - Daniel De Matteis
- *
- * RetroArch is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Found-
- * ation, either version 3 of the License, or (at your option) any later version.
- *
- * RetroArch 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with RetroArch.
- * If not, see .
- */
-#include
-#include
-#include
-
-#include "video_driver.h"
-#include "video_crt_switch.h"
-#include "video_display_server.h"
-
-static unsigned ra_core_width = 0;
-static unsigned ra_core_height = 0;
-static unsigned ra_tmp_width = 0;
-static unsigned ra_tmp_height = 0;
-static unsigned ra_set_core_hz = 0;
-static unsigned orig_width = 0;
-static unsigned orig_height = 0;
-static int crt_center_adjust = 0;
-
-static bool first_run = true;
-
-static float ra_tmp_core_hz = 0.0f;
-static float fly_aspect = 0.0f;
-static float ra_core_hz = 0.0f;
-
-static void crt_check_first_run(void)
-{
- if (!first_run)
- return;
-
- first_run = false;
-}
-
-static void switch_crt_hz(void)
-{
- if (ra_core_hz == ra_tmp_core_hz)
- return;
- /* set hz float to an int for windows switching */
- if (ra_core_hz < 100)
- {
- if (ra_core_hz < 53)
- ra_set_core_hz = 50;
- if (ra_core_hz >= 53 && ra_core_hz < 57)
- ra_set_core_hz = 55;
- if (ra_core_hz >= 57)
- ra_set_core_hz = 60;
- }
-
- if (ra_core_hz > 100)
- {
- if (ra_core_hz < 106)
- ra_set_core_hz = 120;
- if (ra_core_hz >= 106 && ra_core_hz < 114)
- ra_set_core_hz = 110;
- if (ra_core_hz >= 114)
- ra_set_core_hz = 120;
- }
-
- video_monitor_set_refresh_rate(ra_set_core_hz);
-
- ra_tmp_core_hz = ra_core_hz;
-}
-
-void crt_aspect_ratio_switch(unsigned width, unsigned height)
-{
- /* send aspect float to videeo_driver */
- fly_aspect = (float)width / height;
- video_driver_set_aspect_ratio_value((float)fly_aspect);
-}
-
-static void switch_res_crt(unsigned width, unsigned height)
-{
- if (height > 100)
- {
- video_display_server_set_resolution(width, height,
- ra_set_core_hz, ra_core_hz, crt_center_adjust);
- video_driver_apply_state_changes();
- }
-}
-
-/* Create correct aspect to fit video if resolution does not exist */
-static void crt_screen_setup_aspect(unsigned width, unsigned height)
-{
-
- switch_crt_hz();
- /* get original resolution of core */
- if (height == 4)
- {
- /* detect menu only */
- if (width < 1920)
- width = 320;
-
- height = 240;
-
- crt_aspect_ratio_switch(width, height);
- }
-
- if (height < 200 && height != 144)
- {
- crt_aspect_ratio_switch(width, height);
- height = 200;
- }
-
- if (height > 200)
- crt_aspect_ratio_switch(width, height);
-
- if (height == 144 && ra_set_core_hz == 50)
- {
- height = 288;
- crt_aspect_ratio_switch(width, height);
- }
-
- if (height > 200 && height < 224)
- {
- crt_aspect_ratio_switch(width, height);
- height = 224;
- }
-
- if (height > 224 && height < 240)
- {
- crt_aspect_ratio_switch(width, height);
- height = 240;
- }
-
- if (height > 240 && height < 255)
- {
- crt_aspect_ratio_switch(width, height);
- height = 254;
- }
-
- if (height == 528 && ra_set_core_hz == 60)
- {
- crt_aspect_ratio_switch(width, height);
- height = 480;
- }
-
- if (height >= 240 && height < 255 && ra_set_core_hz == 55)
- {
- crt_aspect_ratio_switch(width, height);
- height = 254;
- }
-
- switch_res_crt(width, height);
-}
-
-void crt_switch_res_core(unsigned width, unsigned height, float hz, unsigned crt_mode, int crt_switch_center_adjust)
-{
- /* ra_core_hz float passed from within
- * void video_driver_monitor_adjust_system_rates(void) */
- ra_core_width = width;
- ra_core_height = height;
- ra_core_hz = hz;
- crt_center_adjust = crt_switch_center_adjust;
-
- if (crt_mode == 2)
- {
- if (hz > 53)
- ra_core_hz = hz * 2;
-
- if (hz <= 53)
- ra_core_hz = 120.0f;
- }
-
- crt_check_first_run();
-
- /* Detect resolution change and switch */
- if (
- (ra_tmp_height != ra_core_height) ||
- (ra_core_width != ra_tmp_width)
- )
- crt_screen_setup_aspect(width, height);
-
- ra_tmp_height = ra_core_height;
- ra_tmp_width = ra_core_width;
-
- /* Check if aspect is correct, if notchange */
- if (video_driver_get_aspect_ratio() != fly_aspect)
- {
- video_driver_set_aspect_ratio_value((float)fly_aspect);
- video_driver_apply_state_changes();
- }
-}
-
-void crt_video_restore(void)
-{
- if (first_run)
- return;
-
- first_run = true;
-}
+/* CRT SwitchRes Core
+ * Copyright (C) 2018 Alphanu / Ben Templeman.
+ *
+ * RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2017 - Daniel De Matteis
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+#include
+#include
+#include
+
+#include "video_driver.h"
+#include "video_crt_switch.h"
+#include "video_display_server.h"
+
+#if defined(__arm__)
+ #include "include/userland/interface/vmcs_host/vc_vchi_gencmd.h"
+#endif
+
+static unsigned ra_core_width = 0;
+static unsigned ra_core_height = 0;
+static unsigned ra_tmp_width = 0;
+static unsigned ra_tmp_height = 0;
+static unsigned ra_set_core_hz = 0;
+static unsigned orig_width = 0;
+static unsigned orig_height = 0;
+static int crt_center_adjust = 0;
+
+static bool first_run = true;
+
+static float ra_tmp_core_hz = 0.0f;
+static float fly_aspect = 0.0f;
+static float ra_core_hz = 0.0f;
+static unsigned crt_index = 0;
+
+static void crt_check_first_run(void)
+{
+ if (!first_run)
+ return;
+
+ first_run = false;
+}
+
+static void switch_crt_hz(void)
+{
+ if (ra_core_hz == ra_tmp_core_hz)
+ return;
+ /* set hz float to an int for windows switching */
+ if (ra_core_hz < 100)
+ {
+ if (ra_core_hz < 53)
+ ra_set_core_hz = 50;
+ if (ra_core_hz >= 53 && ra_core_hz < 57)
+ ra_set_core_hz = 55;
+ if (ra_core_hz >= 57)
+ ra_set_core_hz = 60;
+ }
+
+ if (ra_core_hz > 100)
+ {
+ if (ra_core_hz < 106)
+ ra_set_core_hz = 120;
+ if (ra_core_hz >= 106 && ra_core_hz < 114)
+ ra_set_core_hz = 110;
+ if (ra_core_hz >= 114)
+ ra_set_core_hz = 120;
+ }
+
+ video_monitor_set_refresh_rate(ra_set_core_hz);
+
+ ra_tmp_core_hz = ra_core_hz;
+}
+
+void crt_aspect_ratio_switch(unsigned width, unsigned height)
+{
+ /* send aspect float to videeo_driver */
+ fly_aspect = (float)width / height;
+ video_driver_set_aspect_ratio_value((float)fly_aspect);
+}
+
+static void switch_res_crt(unsigned width, unsigned height)
+{
+ video_display_server_set_resolution(width, height,
+ ra_set_core_hz, ra_core_hz, crt_center_adjust, crt_index);
+ #if defined(__arm__)
+ crt_rpi_switch(width, height, ra_core_hz);
+ video_monitor_set_refresh_rate(ra_core_hz);
+ crt_switch_driver_reinit();
+ #endif
+ video_driver_apply_state_changes();
+}
+
+/* Create correct aspect to fit video if resolution does not exist */
+static void crt_screen_setup_aspect(unsigned width, unsigned height)
+{
+ #if defined(__arm__)
+ if (height > 300)
+ height = height/2;
+ #endif
+
+ switch_crt_hz();
+ /* get original resolution of core */
+ if (height == 4)
+ {
+ /* detect menu only */
+ if (width < 1920)
+ width = 320;
+
+ height = 240;
+
+ crt_aspect_ratio_switch(width, height);
+ }
+
+ if (height < 200 && height != 144)
+ {
+ crt_aspect_ratio_switch(width, height);
+ height = 200;
+ }
+
+ if (height > 200)
+ crt_aspect_ratio_switch(width, height);
+
+ if (height == 144 && ra_set_core_hz == 50)
+ {
+ height = 288;
+ crt_aspect_ratio_switch(width, height);
+ }
+
+ if (height > 200 && height < 224)
+ {
+ crt_aspect_ratio_switch(width, height);
+ height = 224;
+ }
+
+ if (height > 224 && height < 240)
+ {
+ crt_aspect_ratio_switch(width, height);
+ height = 240;
+ }
+
+ if (height > 240 && height < 255)
+ {
+ crt_aspect_ratio_switch(width, height);
+ height = 254;
+ }
+
+ if (height == 528 && ra_set_core_hz == 60)
+ {
+ crt_aspect_ratio_switch(width, height);
+ height = 480;
+ }
+
+ if (height >= 240 && height < 255 && ra_set_core_hz == 55)
+ {
+ crt_aspect_ratio_switch(width, height);
+ height = 254;
+ }
+
+ switch_res_crt(width, height);
+}
+
+void crt_switch_res_core(unsigned width, unsigned height, float hz, unsigned crt_mode, int crt_switch_center_adjust)
+{
+ /* ra_core_hz float passed from within
+ * void video_driver_monitor_adjust_system_rates(void) */
+ ra_core_width = width;
+ ra_core_height = height;
+ ra_core_hz = hz;
+ crt_center_adjust = crt_switch_center_adjust;
+ crt_index = monitor_index;
+
+ if (crt_mode == 2)
+ {
+ if (hz > 53)
+ ra_core_hz = hz * 2;
+
+ if (hz <= 53)
+ ra_core_hz = 120.0f;
+ }
+
+ crt_check_first_run();
+
+ /* Detect resolution change and switch */
+ if (
+ (ra_tmp_height != ra_core_height) ||
+ (ra_core_width != ra_tmp_width)
+ )
+ crt_screen_setup_aspect(width, height);
+
+ ra_tmp_height = ra_core_height;
+ ra_tmp_width = ra_core_width;
+
+ /* Check if aspect is correct, if notchange */
+ if (video_driver_get_aspect_ratio() != fly_aspect)
+ {
+ video_driver_set_aspect_ratio_value((float)fly_aspect);
+ video_driver_apply_state_changes();
+ }
+}
+
+void crt_video_restore(void)
+{
+ if (first_run)
+ return;
+
+ first_run = true;
+}
+
+#if defined(__arm__)
+static void crt_rpi_switch(int width, int height, float hz)
+{
+ static char output[250] = {0};
+ static char output1[250] = {0};
+ static char output2[250] = {0};
+ static char set_hdmi[250] ={0};
+ static char set_hdmi_timing[250] = {0};
+ int i = 0;
+ int hfp = 0;
+ int hsp = 0;
+ int hbp = 0;
+ int vfp = 0;
+ int vsp = 0;
+ int vbp = 0;
+ int hmax = 0;
+ int vmax = 0;
+ int pdefault = 8;
+ int pwidth = 0;
+ float roundw = 0.0f;
+ float roundh = 0.0f;
+ float pixel_clock = 0;
+ int ip_flag = 0;
+
+ /* set core refresh from hz */
+ video_monitor_set_refresh_rate(hz);
+
+ /* following code is the mode line generator */
+
+ pwidth = width;
+
+ if (height < 400 && width > 400)
+ pwidth = width / 2;
+
+ roundw = roundf((float)pwidth / (float)height * 100) / 100;
+
+ if (height > width)
+ roundw = roundf((float)height / (float)width * 100) / 100;
+
+ if (roundw > 1.35)
+ roundw = 1.25;
+
+ if (roundw < 1.20)
+ roundw = 1.34;
+ hfp = width * 0.065;
+
+ hsp = width * 0.1433-hfp;
+
+ hbp = width * 0.3-hsp-hfp;
+
+
+ if (height < 241)
+ vmax = 261;
+ if (height < 241 && hz > 56 && hz < 58)
+ vmax = 280;
+ if (height < 241 && hz < 55)
+ vmax = 313;
+ if (height > 250 && height < 260 && hz > 54)
+ vmax = 296;
+ if (height > 250 && height < 260 && hz > 52 && hz < 54)
+ vmax = 285;
+ if (height > 250 && height < 260 && hz < 52)
+ vmax = 313;
+ if (height > 260 && height < 300)
+ vmax = 318;
+
+ if (height > 400 && hz > 56)
+ vmax = 533;
+ if (height > 520 && hz < 57)
+ vmax = 580;
+
+ if (height > 300 && hz < 56)
+ vmax = 615;
+ if (height > 500 && hz < 56)
+ vmax = 624;
+ if (height > 300)
+ pdefault = pdefault * 2;
+
+ vfp = (height + ((vmax - height) / 2) - pdefault) - height;
+
+ if (height < 300)
+ vsp = vfp + 3; /* needs to be 3 for progressive */
+ if (height > 300)
+ vsp = vfp + 6; /* needs to be 6 for interlaced */
+
+ vsp = 3;
+
+ vbp = (vmax-height)-vsp-vfp;
+
+ hmax = width+hfp+hsp+hbp;
+
+ if (height < 300)
+ {
+ pixel_clock = (hmax * vmax * hz) ;
+ ip_flag = 0;
+ }
+
+ if (height > 300)
+ {
+ pixel_clock = (hmax * vmax * (hz/2)) /2 ;
+ ip_flag = 1;
+ }
+ /* above code is the modeline generator */
+
+ snprintf(set_hdmi_timing, sizeof(set_hdmi_timings), "hdmi_timings %d 1 %d %d %d %d 1 %d %d %d 0 0 0 %f %d %f 1 ", width, hfp, hsp, hbp, height, vfp,vsp, vbp, hz, ip_flag, pixel_clock);
+
+ VCHI_INSTANCE_T vchi_instance;
+ VCHI_CONNECTION_T *vchi_connection = NULL;
+ char buffer[1024];
+
+ vcos_init ();
+
+ vchi_initialise (&vchi_instance);
+
+ vchi_connect (NULL, 0, vchi_instance);
+
+ vc_vchi_gencmd_init (vchi_instance, &vchi_connection, 1);
+
+
+ vc_gencmd (buffer, sizeof (buffer), set_hdmi_timing);
+
+ vc_gencmd_stop ();
+
+ vchi_disconnect (vchi_instance);
+
+ snprintf(output1, sizeof(output1),"tvservice -e \"DMT 87\" > /dev/null");
+ system(output1);
+ snprintf(output2, sizeof(output1),"fbset -g %d %d %d %d 24 > /dev/null",width, height, width, height);
+ system(output2);
+}
+#endif
+
diff --git a/gfx/video_crt_switch.h b/gfx/video_crt_switch.h
index 98cfd4150b..df388ec2ce 100644
--- a/gfx/video_crt_switch.h
+++ b/gfx/video_crt_switch.h
@@ -1,38 +1,38 @@
-/* CRT SwitchRes Core
- * Copyright (C) 2018 Alphanu / Ben Templeman.
- *
- * RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2017 - Daniel De Matteis
- *
- * RetroArch is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Found-
- * ation, either version 3 of the License, or (at your option) any later version.
- *
- * RetroArch 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with RetroArch.
- * If not, see .
- */
-
-#ifndef __VIDEO_CRT_SWITCH_H__
-#define __VIDEO_CRT_SWITCH_H__
-
-#include
-
-#include
-#include
-
-RETRO_BEGIN_DECLS
-
-void crt_switch_res_core(unsigned width, unsigned height, float hz, unsigned crt_mode, int crt_switch_center_adjust);
-
-void crt_aspect_ratio_switch(unsigned width, unsigned height);
-
-void crt_video_restore(void);
-
-RETRO_END_DECLS
-
-#endif
+/* CRT SwitchRes Core
+ * Copyright (C) 2018 Alphanu / Ben Templeman.
+ *
+ * RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2017 - Daniel De Matteis
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#ifndef __VIDEO_CRT_SWITCH_H__
+#define __VIDEO_CRT_SWITCH_H__
+
+#include
+
+#include
+#include
+
+RETRO_BEGIN_DECLS
+
+void crt_switch_res_core(unsigned width, unsigned height, float hz, unsigned crt_mode, int crt_switch_center_adjust, int monitor_index);
+
+void crt_aspect_ratio_switch(unsigned width, unsigned height);
+
+void crt_video_restore(void);
+
+RETRO_END_DECLS
+
+#endif
diff --git a/gfx/video_display_server.c b/gfx/video_display_server.c
index f5f4f917ea..9917de9ddd 100644
--- a/gfx/video_display_server.c
+++ b/gfx/video_display_server.c
@@ -1,112 +1,112 @@
-/* RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2017 - Daniel De Matteis
- * Copyright (C) 2016-2017 - Brad Parker
- *
- * RetroArch is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Found-
- * ation, either version 3 of the License, or (at your option) any later version.
- *
- * RetroArch 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with RetroArch.
- * If not, see .
- */
-
-#include
-#include "video_display_server.h"
-#include "video_driver.h"
-#include "../verbosity.h"
-
-static const video_display_server_t *current_display_server = &dispserv_null;
-static void *current_display_server_data = NULL;
-
-const char *video_display_server_get_ident(void)
-{
- if (!current_display_server)
- return "null";
- return current_display_server->ident;
-}
-
-void* video_display_server_init(void)
-{
- enum rarch_display_type type = video_driver_display_type_get();
-
- video_display_server_destroy();
-
- switch (type)
- {
- case RARCH_DISPLAY_WIN32:
-#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
- current_display_server = &dispserv_win32;
-#endif
- break;
- case RARCH_DISPLAY_X11:
-#if defined(HAVE_X11)
- current_display_server = &dispserv_x11;
-#endif
- break;
- default:
- current_display_server = &dispserv_null;
- break;
- }
-
- current_display_server_data = current_display_server->init();
-
- RARCH_LOG("[Video]: Found display server: %s\n",
- current_display_server->ident);
-
- return current_display_server_data;
-}
-
-void video_display_server_destroy(void)
-{
- if (current_display_server && current_display_server->destroy)
- if (current_display_server_data)
- current_display_server->destroy(current_display_server_data);
-}
-
-bool video_display_server_set_window_opacity(unsigned opacity)
-{
- if (current_display_server && current_display_server->set_window_opacity)
- return current_display_server->set_window_opacity(current_display_server_data, opacity);
- return false;
-}
-
-bool video_display_server_set_window_progress(int progress, bool finished)
-{
- if (current_display_server && current_display_server->set_window_progress)
- return current_display_server->set_window_progress(current_display_server_data, progress, finished);
- return false;
-}
-
-bool video_display_server_set_window_decorations(bool on)
-{
- if (current_display_server && current_display_server->set_window_decorations)
- return current_display_server->set_window_decorations(current_display_server_data, on);
- return false;
-}
-
-bool video_display_server_set_resolution(unsigned width, unsigned height,
- int int_hz, float hz, int center)
-{
- if (current_display_server && current_display_server->set_resolution)
- return current_display_server->set_resolution(current_display_server_data, width, height, int_hz, hz, center);
- return false;
-}
-
-void *video_display_server_get_resolution_list(unsigned *size)
-{
- if (current_display_server && current_display_server->get_resolution_list)
- return current_display_server->get_resolution_list(current_display_server_data, size);
- return NULL;
-}
-
-const char *video_display_server_get_output_options(void)
-{
- if (current_display_server && current_display_server->get_output_options)
- return current_display_server->get_output_options(current_display_server_data);
- return NULL;
-}
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2017 - Daniel De Matteis
+ * Copyright (C) 2016-2017 - Brad Parker
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#include
+#include "video_display_server.h"
+#include "video_driver.h"
+#include "../verbosity.h"
+
+static const video_display_server_t *current_display_server = &dispserv_null;
+static void *current_display_server_data = NULL;
+
+const char *video_display_server_get_ident(void)
+{
+ if (!current_display_server)
+ return "null";
+ return current_display_server->ident;
+}
+
+void* video_display_server_init(void)
+{
+ enum rarch_display_type type = video_driver_display_type_get();
+
+ video_display_server_destroy();
+
+ switch (type)
+ {
+ case RARCH_DISPLAY_WIN32:
+#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
+ current_display_server = &dispserv_win32;
+#endif
+ break;
+ case RARCH_DISPLAY_X11:
+#if defined(HAVE_X11)
+ current_display_server = &dispserv_x11;
+#endif
+ break;
+ default:
+ current_display_server = &dispserv_null;
+ break;
+ }
+
+ current_display_server_data = current_display_server->init();
+
+ RARCH_LOG("[Video]: Found display server: %s\n",
+ current_display_server->ident);
+
+ return current_display_server_data;
+}
+
+void video_display_server_destroy(void)
+{
+ if (current_display_server && current_display_server->destroy)
+ if (current_display_server_data)
+ current_display_server->destroy(current_display_server_data);
+}
+
+bool video_display_server_set_window_opacity(unsigned opacity)
+{
+ if (current_display_server && current_display_server->set_window_opacity)
+ return current_display_server->set_window_opacity(current_display_server_data, opacity);
+ return false;
+}
+
+bool video_display_server_set_window_progress(int progress, bool finished)
+{
+ if (current_display_server && current_display_server->set_window_progress)
+ return current_display_server->set_window_progress(current_display_server_data, progress, finished);
+ return false;
+}
+
+bool video_display_server_set_window_decorations(bool on)
+{
+ if (current_display_server && current_display_server->set_window_decorations)
+ return current_display_server->set_window_decorations(current_display_server_data, on);
+ return false;
+}
+
+bool video_display_server_set_resolution(unsigned width, unsigned height,
+ int int_hz, float hz, int center, int monitor_index)
+{
+ if (current_display_server && current_display_server->set_resolution)
+ return current_display_server->set_resolution(current_display_server_data, width, height, int_hz, hz, center, monitor_index);
+ return false;
+}
+
+void *video_display_server_get_resolution_list(unsigned *size)
+{
+ if (current_display_server && current_display_server->get_resolution_list)
+ return current_display_server->get_resolution_list(current_display_server_data, size);
+ return NULL;
+}
+
+const char *video_display_server_get_output_options(void)
+{
+ if (current_display_server && current_display_server->get_output_options)
+ return current_display_server->get_output_options(current_display_server_data);
+ return NULL;
+}
diff --git a/gfx/video_display_server.h b/gfx/video_display_server.h
index 2824b4b4ee..d3c990ca85 100644
--- a/gfx/video_display_server.h
+++ b/gfx/video_display_server.h
@@ -1,77 +1,77 @@
-/* RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2017 - Daniel De Matteis
- * Copyright (C) 2016-2017 - Brad Parker
- *
- * RetroArch is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Found-
- * ation, either version 3 of the License, or (at your option) any later version.
- *
- * RetroArch 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with RetroArch.
- * If not, see .
- */
-
-#ifndef __VIDEO_DISPLAY_SERVER__H
-#define __VIDEO_DISPLAY_SERVER__H
-
-#include
-#include
-
-RETRO_BEGIN_DECLS
-
-typedef struct video_display_config
-{
- unsigned width;
- unsigned height;
- unsigned bpp;
- unsigned refreshrate;
- unsigned idx;
- bool current;
-} video_display_config_t;
-
-typedef struct video_display_server
-{
- void *(*init)(void);
- void (*destroy)(void *data);
- bool (*set_window_opacity)(void *data, unsigned opacity);
- bool (*set_window_progress)(void *data, int progress, bool finished);
- bool (*set_window_decorations)(void *data, bool on);
- bool (*set_resolution)(void *data, unsigned width,
- unsigned height, int int_hz, float hz, int center);
- void *(*get_resolution_list)(void *data,
- unsigned *size);
- const char *(*get_output_options)(void *data);
- const char *ident;
-} video_display_server_t;
-
-void* video_display_server_init(void);
-
-void video_display_server_destroy(void);
-
-bool video_display_server_set_window_opacity(unsigned opacity);
-
-bool video_display_server_set_window_progress(int progress, bool finished);
-
-bool video_display_server_set_window_decorations(bool on);
-
-bool video_display_server_set_resolution(
- unsigned width, unsigned height,
- int int_hz, float hz, int center);
-
-void *video_display_server_get_resolution_list(unsigned *size);
-
-const char *video_display_server_get_output_options(void);
-
-const char *video_display_server_get_ident(void);
-
-extern const video_display_server_t dispserv_win32;
-extern const video_display_server_t dispserv_x11;
-extern const video_display_server_t dispserv_null;
-
-RETRO_END_DECLS
-
-#endif
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2017 - Daniel De Matteis
+ * Copyright (C) 2016-2017 - Brad Parker
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#ifndef __VIDEO_DISPLAY_SERVER__H
+#define __VIDEO_DISPLAY_SERVER__H
+
+#include
+#include
+
+RETRO_BEGIN_DECLS
+
+typedef struct video_display_config
+{
+ unsigned width;
+ unsigned height;
+ unsigned bpp;
+ unsigned refreshrate;
+ unsigned idx;
+ bool current;
+} video_display_config_t;
+
+typedef struct video_display_server
+{
+ void *(*init)(void);
+ void (*destroy)(void *data);
+ bool (*set_window_opacity)(void *data, unsigned opacity);
+ bool (*set_window_progress)(void *data, int progress, bool finished);
+ bool (*set_window_decorations)(void *data, bool on);
+ bool (*set_resolution)(void *data, unsigned width,
+ unsigned height, int int_hz, float hz, int center, int monitor_index);
+ void *(*get_resolution_list)(void *data,
+ unsigned *size);
+ const char *(*get_output_options)(void *data);
+ const char *ident;
+} video_display_server_t;
+
+void* video_display_server_init(void);
+
+void video_display_server_destroy(void);
+
+bool video_display_server_set_window_opacity(unsigned opacity);
+
+bool video_display_server_set_window_progress(int progress, bool finished);
+
+bool video_display_server_set_window_decorations(bool on);
+
+bool video_display_server_set_resolution(
+ unsigned width, unsigned height,
+ int int_hz, float hz, int center, int monitor_index);
+
+void *video_display_server_get_resolution_list(unsigned *size);
+
+const char *video_display_server_get_output_options(void);
+
+const char *video_display_server_get_ident(void);
+
+extern const video_display_server_t dispserv_win32;
+extern const video_display_server_t dispserv_x11;
+extern const video_display_server_t dispserv_null;
+
+RETRO_END_DECLS
+
+#endif
diff --git a/gfx/video_driver.c b/gfx/video_driver.c
index 3e05c64d70..f3a9bb5096 100644
--- a/gfx/video_driver.c
+++ b/gfx/video_driver.c
@@ -1,3778 +1,3783 @@
-/* RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2017 - Daniel De Matteis
- *
- * RetroArch is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Found-
- * ation, either version 3 of the License, or (at your option) any later version.
- *
- * RetroArch 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with RetroArch.
- * If not, see .
- */
-
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-
-#include "../audio/audio_driver.h"
-#include "../menu/menu_shader.h"
-
-#ifdef HAVE_CONFIG_H
-#include "../config.h"
-#endif
-
-#include "../dynamic.h"
-
-#ifdef HAVE_THREADS
-#include
-#endif
-
-#ifdef HAVE_MENU
-#include "../menu/menu_driver.h"
-#include "../menu/menu_setting.h"
-#endif
-
-#include "video_thread_wrapper.h"
-#include "video_driver.h"
-#include "video_display_server.h"
-#include "video_crt_switch.h"
-
-#include "../frontend/frontend_driver.h"
-#include "../record/record_driver.h"
-#include "../config.def.h"
-#include "../configuration.h"
-#include "../driver.h"
-#include "../retroarch.h"
-#include "../input/input_driver.h"
-#include "../list_special.h"
-#include "../core.h"
-#include "../command.h"
-#include "../msg_hash.h"
-#include "../verbosity.h"
-
-#define MEASURE_FRAME_TIME_SAMPLES_COUNT (2 * 1024)
-
-#define TIME_TO_FPS(last_time, new_time, frames) ((1000000.0f * (frames)) / ((new_time) - (last_time)))
-
-#define FPS_UPDATE_INTERVAL 256
-
-#ifdef HAVE_THREADS
-#define video_driver_is_threaded_internal() ((!video_driver_is_hw_context() && video_driver_threaded) ? true : false)
-#else
-#define video_driver_is_threaded_internal() (false)
-#endif
-
-#ifdef HAVE_THREADS
-#define video_driver_lock() \
- if (display_lock) \
- slock_lock(display_lock)
-
-#define video_driver_unlock() \
- if (display_lock) \
- slock_unlock(display_lock)
-
-#define video_driver_context_lock() \
- if (context_lock) \
- slock_lock(context_lock)
-
-#define video_driver_context_unlock() \
- if (context_lock) \
- slock_unlock(context_lock)
-
-#define video_driver_lock_free() \
- slock_free(display_lock); \
- slock_free(context_lock); \
- display_lock = NULL; \
- context_lock = NULL
-
-#define video_driver_threaded_lock(is_threaded) \
- if (is_threaded) \
- video_driver_lock()
-
-#define video_driver_threaded_unlock(is_threaded) \
- if (is_threaded) \
- video_driver_unlock()
-#else
-#define video_driver_lock() ((void)0)
-#define video_driver_unlock() ((void)0)
-#define video_driver_lock_free() ((void)0)
-#define video_driver_threaded_lock(is_threaded) ((void)0)
-#define video_driver_threaded_unlock(is_threaded) ((void)0)
-#define video_driver_context_lock() ((void)0)
-#define video_driver_context_unlock() ((void)0)
-#endif
-
-typedef struct video_pixel_scaler
-{
- struct scaler_ctx *scaler;
- void *scaler_out;
-} video_pixel_scaler_t;
-
-static void (*video_driver_cb_shader_use)(void *data,
- void *shader_data, unsigned index, bool set_active);
-static bool (*video_driver_cb_shader_set_mvp)(void *data,
- void *shader_data, const void *mat_data);
-bool (*video_driver_cb_has_focus)(void);
-
-/* Opaque handles to currently running window.
- * Used by e.g. input drivers which bind to a window.
- * Drivers are responsible for setting these if an input driver
- * could potentially make use of this. */
-static uintptr_t video_driver_display = 0;
-static uintptr_t video_driver_window = 0;
-
-static rarch_softfilter_t *video_driver_state_filter = NULL;
-static void *video_driver_state_buffer = NULL;
-static unsigned video_driver_state_scale = 0;
-static unsigned video_driver_state_out_bpp = 0;
-static bool video_driver_state_out_rgb32 = false;
-static bool video_driver_crt_switching_active = false;
-
-static struct retro_system_av_info video_driver_av_info;
-
-static enum retro_pixel_format video_driver_pix_fmt = RETRO_PIXEL_FORMAT_0RGB1555;
-
-static const void *frame_cache_data = NULL;
-static unsigned frame_cache_width = 0;
-static unsigned frame_cache_height = 0;
-static size_t frame_cache_pitch = 0;
-static bool video_driver_threaded = false;
-
-static float video_driver_core_hz = 0.0f;
-static float video_driver_aspect_ratio = 0.0f;
-static unsigned video_driver_width = 0;
-static unsigned video_driver_height = 0;
-
-static enum rarch_display_type video_driver_display_type = RARCH_DISPLAY_NONE;
-static char video_driver_title_buf[64] = {0};
-static char video_driver_window_title[512] = {0};
-static bool video_driver_window_title_update = true;
-
-static retro_time_t video_driver_frame_time_samples[MEASURE_FRAME_TIME_SAMPLES_COUNT];
-static uint64_t video_driver_frame_time_count = 0;
-static uint64_t video_driver_frame_count = 0;
-
-static void *video_driver_data = NULL;
-static video_driver_t *current_video = NULL;
-
-/* Interface for "poking". */
-static const video_poke_interface_t *video_driver_poke = NULL;
-
-/* Used for 15-bit -> 16-bit conversions that take place before
- * being passed to video driver. */
-static video_pixel_scaler_t *video_driver_scaler_ptr = NULL;
-
-static struct retro_hw_render_callback hw_render;
-
-static const struct
-retro_hw_render_context_negotiation_interface *
-hw_render_context_negotiation = NULL;
-
-/* Graphics driver requires RGBA byte order data (ABGR on little-endian)
- * for 32-bit.
- * This takes effect for overlay and shader cores that wants to load
- * data into graphics driver. Kinda hackish to place it here, it is only
- * used for GLES.
- * TODO: Refactor this better. */
-static bool video_driver_use_rgba = false;
-static bool video_driver_data_own = false;
-static bool video_driver_active = false;
-
-static video_driver_frame_t frame_bak = NULL;
-
-/* If set during context deinit, the driver should keep
- * graphics context alive to avoid having to reset all
- * context state. */
-static bool video_driver_cache_context = false;
-
-/* Set to true by driver if context caching succeeded. */
-static bool video_driver_cache_context_ack = false;
-static uint8_t *video_driver_record_gpu_buffer = NULL;
-
-#ifdef HAVE_THREADS
-static slock_t *display_lock = NULL;
-static slock_t *context_lock = NULL;
-#endif
-
-static gfx_ctx_driver_t current_video_context;
-
-static void *video_context_data = NULL;
-
-/**
- * dynamic.c:dynamic_request_hw_context will try to set flag data when the context
- * is in the middle of being rebuilt; in these cases we will save flag
- * data and set this to true.
- * When the context is reinit, it checks this, reads from
- * deferred_flag_data and cleans it.
- *
- * TODO - Dirty hack, fix it better
- */
-static bool deferred_video_context_driver_set_flags = false;
-static gfx_ctx_flags_t deferred_flag_data = {0};
-
-static bool video_started_fullscreen = false;
-
-static shader_backend_t *current_shader = NULL;
-static void *current_shader_data = NULL;
-
-struct aspect_ratio_elem aspectratio_lut[ASPECT_RATIO_END] = {
- { "4:3", 1.3333f },
- { "16:9", 1.7778f },
- { "16:10", 1.6f },
- { "16:15", 16.0f / 15.0f },
- { "21:9", 21.0f / 9.0f },
- { "1:1", 1.0f },
- { "2:1", 2.0f },
- { "3:2", 1.5f },
- { "3:4", 0.75f },
- { "4:1", 4.0f },
- { "9:16", 0.5625f },
- { "5:4", 1.25f },
- { "6:5", 1.2f },
- { "7:9", 0.7777f },
- { "8:3", 2.6666f },
- { "8:7", 1.1428f },
- { "19:12", 1.5833f },
- { "19:14", 1.3571f },
- { "30:17", 1.7647f },
- { "32:9", 3.5555f },
- { "Config", 0.0f },
- { "Square pixel", 1.0f },
- { "Core provided", 1.0f },
- { "Custom", 0.0f }
-};
-
-static const video_driver_t *video_drivers[] = {
-#ifdef HAVE_OPENGL
- &video_gl,
-#endif
-#ifdef HAVE_VULKAN
- &video_vulkan,
-#endif
-#ifdef HAVE_METAL
- &video_metal,
-#endif
-#ifdef XENON
- &video_xenon360,
-#endif
-#if defined(HAVE_D3D12)
- &video_d3d12,
-#endif
-#if defined(HAVE_D3D11)
- &video_d3d11,
-#endif
-#if defined(HAVE_D3D10)
- &video_d3d10,
-#endif
-#if defined(HAVE_D3D9)
- &video_d3d9,
-#endif
-#if defined(HAVE_D3D8)
- &video_d3d8,
-#endif
-#ifdef HAVE_VITA2D
- &video_vita2d,
-#endif
-#ifdef PSP
- &video_psp1,
-#endif
-#ifdef PS2
- &video_ps2,
-#endif
-#ifdef _3DS
- &video_ctr,
-#endif
-#ifdef SWITCH
- &video_switch,
-#endif
-#ifdef HAVE_SDL
- &video_sdl,
-#endif
-#ifdef HAVE_SDL2
- &video_sdl2,
-#endif
-#ifdef HAVE_XVIDEO
- &video_xvideo,
-#endif
-#ifdef GEKKO
- &video_gx,
-#endif
-#ifdef WIIU
- &video_wiiu,
-#endif
-#ifdef HAVE_VG
- &video_vg,
-#endif
-#ifdef HAVE_OMAP
- &video_omap,
-#endif
-#ifdef HAVE_EXYNOS
- &video_exynos,
-#endif
-#ifdef HAVE_DISPMANX
- &video_dispmanx,
-#endif
-#ifdef HAVE_SUNXI
- &video_sunxi,
-#endif
-#ifdef HAVE_PLAIN_DRM
- &video_drm,
-#endif
-#ifdef HAVE_XSHM
- &video_xshm,
-#endif
-#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
- &video_gdi,
-#endif
-#ifdef DJGPP
- &video_vga,
-#endif
-#ifdef HAVE_SIXEL
- &video_sixel,
-#endif
-#ifdef HAVE_CACA
- &video_caca,
-#endif
- &video_null,
- NULL,
-};
-
-static const gfx_ctx_driver_t *gfx_ctx_drivers[] = {
-#if defined(ORBIS)
- &orbis_ctx,
-#endif
-#if defined(HAVE_LIBNX) && defined(HAVE_OPENGL)
- &switch_ctx,
-#endif
-#if defined(__CELLOS_LV2__)
- &gfx_ctx_ps3,
-#endif
-#if defined(HAVE_VIDEOCORE)
- &gfx_ctx_videocore,
-#endif
-#if defined(HAVE_MALI_FBDEV)
- &gfx_ctx_mali_fbdev,
-#endif
-#if defined(HAVE_VIVANTE_FBDEV)
- &gfx_ctx_vivante_fbdev,
-#endif
-#if defined(HAVE_OPENDINGUX_FBDEV)
- &gfx_ctx_opendingux_fbdev,
-#endif
-#if defined(_WIN32) && (defined(HAVE_OPENGL) || defined(HAVE_VULKAN))
- &gfx_ctx_wgl,
-#endif
-#if defined(HAVE_WAYLAND)
- &gfx_ctx_wayland,
-#endif
-#if defined(HAVE_X11) && !defined(HAVE_OPENGLES)
-#if defined(HAVE_OPENGL) || defined(HAVE_VULKAN)
- &gfx_ctx_x,
-#endif
-#endif
-#if defined(HAVE_X11) && defined(HAVE_OPENGL) && defined(HAVE_EGL)
- &gfx_ctx_x_egl,
-#endif
-#if defined(HAVE_KMS)
- &gfx_ctx_drm,
-#endif
-#if defined(ANDROID)
- &gfx_ctx_android,
-#endif
-#if defined(__QNX__)
- &gfx_ctx_qnx,
-#endif
-#if defined(HAVE_COCOA) || defined(HAVE_COCOATOUCH) || defined(HAVE_COCOA_METAL)
- &gfx_ctx_cocoagl,
-#endif
-#if defined(__APPLE__) && !defined(TARGET_IPHONE_SIMULATOR) && !defined(TARGET_OS_IPHONE)
- &gfx_ctx_cgl,
-#endif
-#if (defined(HAVE_SDL) || defined(HAVE_SDL2)) && defined(HAVE_OPENGL)
- &gfx_ctx_sdl_gl,
-#endif
-#ifdef HAVE_OSMESA
- &gfx_ctx_osmesa,
-#endif
-#ifdef EMSCRIPTEN
- &gfx_ctx_emscripten,
-#endif
-#if defined(HAVE_VULKAN) && defined(HAVE_VULKAN_DISPLAY)
- &gfx_ctx_khr_display,
-#endif
-#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
- &gfx_ctx_gdi,
-#endif
-#ifdef HAVE_SIXEL
- &gfx_ctx_sixel,
-#endif
- &gfx_ctx_null,
- NULL
-};
-
-static const shader_backend_t *shader_ctx_drivers[] = {
-#ifdef HAVE_GLSL
- &gl_glsl_backend,
-#endif
-#ifdef HAVE_CG
- &gl_cg_backend,
-#endif
- &shader_null_backend,
- NULL
-};
-
-bool video_driver_started_fullscreen(void)
-{
- return video_started_fullscreen;
-}
-
-/* Stub functions */
-
-static void update_window_title_null(void *data, void *data2)
-{
-}
-
-static void swap_buffers_null(void *data, void *data2)
-{
-}
-
-static bool get_metrics_null(void *data, enum display_metric_types type,
- float *value)
-{
- return false;
-}
-
-static bool set_resize_null(void *a, unsigned b, unsigned c)
-{
- return false;
-}
-
-/**
- * video_driver_find_handle:
- * @idx : index of driver to get handle to.
- *
- * Returns: handle to video driver at index. Can be NULL
- * if nothing found.
- **/
-const void *video_driver_find_handle(int idx)
-{
- const void *drv = video_drivers[idx];
- if (!drv)
- return NULL;
- return drv;
-}
-
-/**
- * video_driver_find_ident:
- * @idx : index of driver to get handle to.
- *
- * Returns: Human-readable identifier of video driver at index. Can be NULL
- * if nothing found.
- **/
-const char *video_driver_find_ident(int idx)
-{
- const video_driver_t *drv = video_drivers[idx];
- if (!drv)
- return NULL;
- return drv->ident;
-}
-
-/**
- * config_get_video_driver_options:
- *
- * Get an enumerated list of all video driver names, separated by '|'.
- *
- * Returns: string listing of all video driver names, separated by '|'.
- **/
-const char* config_get_video_driver_options(void)
-{
- return char_list_new_special(STRING_LIST_VIDEO_DRIVERS, NULL);
-}
-
-bool video_driver_is_threaded(void)
-{
- return video_driver_is_threaded_internal();
-}
-
-#ifdef HAVE_VULKAN
-static bool hw_render_context_is_vulkan(enum retro_hw_context_type type)
-{
- return type == RETRO_HW_CONTEXT_VULKAN;
-}
-#endif
-
-#if defined(HAVE_OPENGL)
-static bool hw_render_context_is_gl(enum retro_hw_context_type type)
-{
- switch (type)
- {
- case RETRO_HW_CONTEXT_OPENGL:
- case RETRO_HW_CONTEXT_OPENGLES2:
- case RETRO_HW_CONTEXT_OPENGL_CORE:
- case RETRO_HW_CONTEXT_OPENGLES3:
- case RETRO_HW_CONTEXT_OPENGLES_VERSION:
- return true;
- default:
- break;
- }
-
- return false;
-}
-#endif
-
-bool *video_driver_get_threaded(void)
-{
- return &video_driver_threaded;
-}
-
-void video_driver_set_threaded(bool val)
-{
- video_driver_threaded = val;
-}
-
-/**
- * video_driver_get_ptr:
- *
- * Use this if you need the real video driver
- * and driver data pointers.
- *
- * Returns: video driver's userdata.
- **/
-void *video_driver_get_ptr(bool force_nonthreaded_data)
-{
-#ifdef HAVE_THREADS
- if (video_driver_is_threaded_internal() && !force_nonthreaded_data)
- return video_thread_get_ptr(NULL);
-#endif
-
- return video_driver_data;
-}
-
-const char *video_driver_get_ident(void)
-{
- return (current_video) ? current_video->ident : NULL;
-}
-
-const video_poke_interface_t *video_driver_get_poke(void)
-{
- return video_driver_poke;
-}
-
-static bool video_context_has_focus(void)
-{
- return current_video_context.has_focus && current_video_context.has_focus(video_context_data);
-}
-
-static bool video_driver_has_focus(void)
-{
- return current_video && current_video->focus && current_video->focus(video_driver_data);
-}
-
-static bool null_driver_has_focus(void)
-{
- return true;
-}
-
-static void video_context_driver_reset(void)
-{
- if (!current_video_context.get_metrics)
- current_video_context.get_metrics = get_metrics_null;
-
- if (!current_video_context.update_window_title)
- current_video_context.update_window_title = update_window_title_null;
-
- if (!current_video_context.set_resize)
- current_video_context.set_resize = set_resize_null;
-
- if (!current_video_context.swap_buffers)
- current_video_context.swap_buffers = swap_buffers_null;
-
- if (current_video_context.has_focus)
- video_driver_cb_has_focus = video_context_has_focus;
-
-}
-
-bool video_context_driver_set(const gfx_ctx_driver_t *data)
-{
- if (!data)
- return false;
- current_video_context = *data;
- video_context_driver_reset();
- return true;
-}
-
-void video_context_driver_destroy(void)
-{
- current_video_context.init = NULL;
- current_video_context.bind_api = NULL;
- current_video_context.swap_interval = NULL;
- current_video_context.set_video_mode = NULL;
- current_video_context.get_video_size = NULL;
- current_video_context.get_video_output_size = NULL;
- current_video_context.get_video_output_prev = NULL;
- current_video_context.get_video_output_next = NULL;
- current_video_context.get_metrics = get_metrics_null;
- current_video_context.translate_aspect = NULL;
- current_video_context.update_window_title = update_window_title_null;
- current_video_context.check_window = NULL;
- current_video_context.set_resize = set_resize_null;
- current_video_context.has_focus = NULL;
- current_video_context.suppress_screensaver = NULL;
- current_video_context.has_windowed = NULL;
- current_video_context.swap_buffers = swap_buffers_null;
- current_video_context.input_driver = NULL;
- current_video_context.get_proc_address = NULL;
- current_video_context.image_buffer_init = NULL;
- current_video_context.image_buffer_write = NULL;
- current_video_context.show_mouse = NULL;
- current_video_context.ident = NULL;
- current_video_context.get_flags = NULL;
- current_video_context.set_flags = NULL;
- current_video_context.bind_hw_render = NULL;
- current_video_context.get_context_data = NULL;
- current_video_context.make_current = NULL;
-}
-
-/**
- * video_driver_get_current_framebuffer:
- *
- * Gets pointer to current hardware renderer framebuffer object.
- * Used by RETRO_ENVIRONMENT_SET_HW_RENDER.
- *
- * Returns: pointer to hardware framebuffer object, otherwise 0.
- **/
-uintptr_t video_driver_get_current_framebuffer(void)
-{
- if (video_driver_poke && video_driver_poke->get_current_framebuffer)
- return video_driver_poke->get_current_framebuffer(video_driver_data);
- return 0;
-}
-
-retro_proc_address_t video_driver_get_proc_address(const char *sym)
-{
- if (video_driver_poke && video_driver_poke->get_proc_address)
- return video_driver_poke->get_proc_address(video_driver_data, sym);
- return NULL;
-}
-
-bool video_driver_set_shader(enum rarch_shader_type type,
- const char *path)
-{
- if (current_video->set_shader)
- return current_video->set_shader(video_driver_data, type, path);
- return false;
-}
-
-static void video_driver_filter_free(void)
-{
- if (video_driver_state_filter)
- rarch_softfilter_free(video_driver_state_filter);
- video_driver_state_filter = NULL;
-
- if (video_driver_state_buffer)
- {
-#ifdef _3DS
- linearFree(video_driver_state_buffer);
-#else
- free(video_driver_state_buffer);
-#endif
- }
- video_driver_state_buffer = NULL;
-
- video_driver_state_scale = 0;
- video_driver_state_out_bpp = 0;
- video_driver_state_out_rgb32 = false;
-}
-
-static void video_driver_init_filter(enum retro_pixel_format colfmt_int)
-{
- unsigned pow2_x, pow2_y, maxsize;
- void *buf = NULL;
- settings_t *settings = config_get_ptr();
- struct retro_game_geometry *geom = &video_driver_av_info.geometry;
- unsigned width = geom->max_width;
- unsigned height = geom->max_height;
- /* Deprecated format. Gets pre-converted. */
- enum retro_pixel_format colfmt =
- (colfmt_int == RETRO_PIXEL_FORMAT_0RGB1555) ?
- RETRO_PIXEL_FORMAT_RGB565 : colfmt_int;
-
- if (video_driver_is_hw_context())
- {
- RARCH_WARN("Cannot use CPU filters when hardware rendering is used.\n");
- return;
- }
-
- video_driver_state_filter = rarch_softfilter_new(
- settings->paths.path_softfilter_plugin,
- RARCH_SOFTFILTER_THREADS_AUTO, colfmt, width, height);
-
- if (!video_driver_state_filter)
- {
- RARCH_ERR("[Video]: Failed to load filter.\n");
- return;
- }
-
- rarch_softfilter_get_max_output_size(video_driver_state_filter,
- &width, &height);
-
- pow2_x = next_pow2(width);
- pow2_y = next_pow2(height);
- maxsize = MAX(pow2_x, pow2_y);
- video_driver_state_scale = maxsize / RARCH_SCALE_BASE;
- video_driver_state_out_rgb32 = rarch_softfilter_get_output_format(
- video_driver_state_filter) ==
- RETRO_PIXEL_FORMAT_XRGB8888;
-
- video_driver_state_out_bpp = video_driver_state_out_rgb32 ?
- sizeof(uint32_t) :
- sizeof(uint16_t);
-
- /* TODO: Aligned output. */
-#ifdef _3DS
- buf = linearMemAlign(
- width * height * video_driver_state_out_bpp, 0x80);
-#else
- buf = malloc(
- width * height * video_driver_state_out_bpp);
-#endif
- if (!buf)
- {
- RARCH_ERR("[Video]: Softfilter initialization failed.\n");
- video_driver_filter_free();
- return;
- }
-
- video_driver_state_buffer = buf;
-}
-
-static void video_driver_init_input(const input_driver_t *tmp)
-{
- const input_driver_t **input = input_get_double_ptr();
- if (*input)
- return;
-
- /* Video driver didn't provide an input driver,
- * so we use configured one. */
- RARCH_LOG("[Video]: Graphics driver did not initialize an input driver."
- " Attempting to pick a suitable driver.\n");
-
- if (tmp)
- *input = tmp;
- else
- input_driver_find_driver();
-
- /* This should never really happen as tmp (driver.input) is always
- * found before this in find_driver_input(), or we have aborted
- * in a similar fashion anyways. */
- if (!input_get_ptr())
- goto error;
-
- if (input_driver_init())
- return;
-
-error:
- RARCH_ERR("[Video]: Cannot initialize input driver. Exiting ...\n");
- retroarch_fail(1, "video_driver_init_input()");
-}
-
-/**
- * video_driver_monitor_compute_fps_statistics:
- *
- * Computes monitor FPS statistics.
- **/
-static void video_driver_monitor_compute_fps_statistics(void)
-{
- double avg_fps = 0.0;
- double stddev = 0.0;
- unsigned samples = 0;
-
- if (video_driver_frame_time_count <
- (2 * MEASURE_FRAME_TIME_SAMPLES_COUNT))
- {
- RARCH_LOG(
- "[Video]: Does not have enough samples for monitor refresh rate"
- " estimation. Requires to run for at least %u frames.\n",
- 2 * MEASURE_FRAME_TIME_SAMPLES_COUNT);
- return;
- }
-
- if (video_monitor_fps_statistics(&avg_fps, &stddev, &samples))
- {
- RARCH_LOG("[Video]: Average monitor Hz: %.6f Hz. (%.3f %% frame time"
- " deviation, based on %u last samples).\n",
- avg_fps, 100.0 * stddev, samples);
- }
-}
-
-static void video_driver_pixel_converter_free(void)
-{
- if (!video_driver_scaler_ptr)
- return;
-
- scaler_ctx_gen_reset(video_driver_scaler_ptr->scaler);
-
- if (video_driver_scaler_ptr->scaler)
- free(video_driver_scaler_ptr->scaler);
- video_driver_scaler_ptr->scaler = NULL;
-
- if (video_driver_scaler_ptr->scaler_out)
- free(video_driver_scaler_ptr->scaler_out);
- video_driver_scaler_ptr->scaler_out = NULL;
-
- if (video_driver_scaler_ptr)
- free(video_driver_scaler_ptr);
- video_driver_scaler_ptr = NULL;
-}
-
-static void video_driver_free_internal(void)
-{
-#ifdef HAVE_THREADS
- bool is_threaded = video_driver_is_threaded_internal();
-#endif
-
- command_event(CMD_EVENT_OVERLAY_DEINIT, NULL);
-
- if (!video_driver_is_video_cache_context())
- video_driver_free_hw_context();
-
- if (
- !input_driver_owns_driver() &&
- !input_driver_is_data_ptr_same(video_driver_data)
- )
- input_driver_deinit();
-
- if (
- !video_driver_data_own
- && video_driver_data
- && current_video && current_video->free
- )
- current_video->free(video_driver_data);
-
- video_driver_pixel_converter_free();
- video_driver_filter_free();
-
- command_event(CMD_EVENT_SHADER_DIR_DEINIT, NULL);
-
-#ifdef HAVE_THREADS
- if (is_threaded)
- return;
-#endif
-
- video_driver_monitor_compute_fps_statistics();
-}
-
-static bool video_driver_pixel_converter_init(unsigned size)
-{
- struct retro_hw_render_callback *hwr =
- video_driver_get_hw_context();
- void *scalr_out = NULL;
- video_pixel_scaler_t *scalr = NULL;
- struct scaler_ctx *scalr_ctx = NULL;
-
- /* If pixel format is not 0RGB1555, we don't need to do
- * any internal pixel conversion. */
- if (video_driver_pix_fmt != RETRO_PIXEL_FORMAT_0RGB1555)
- return true;
-
- /* No need to perform pixel conversion for HW rendering contexts. */
- if (hwr && hwr->context_type != RETRO_HW_CONTEXT_NONE)
- return true;
-
- RARCH_WARN("0RGB1555 pixel format is deprecated,"
- " and will be slower. For 15/16-bit, RGB565"
- " format is preferred.\n");
-
- scalr = (video_pixel_scaler_t*)calloc(1, sizeof(*scalr));
-
- if (!scalr)
- goto error;
-
- video_driver_scaler_ptr = scalr;
-
- scalr_ctx = (struct scaler_ctx*)calloc(1, sizeof(*scalr_ctx));
-
- if (!scalr_ctx)
- goto error;
-
- video_driver_scaler_ptr->scaler = scalr_ctx;
- video_driver_scaler_ptr->scaler->scaler_type = SCALER_TYPE_POINT;
- video_driver_scaler_ptr->scaler->in_fmt = SCALER_FMT_0RGB1555;
-
- /* TODO: Pick either ARGB8888 or RGB565 depending on driver. */
- video_driver_scaler_ptr->scaler->out_fmt = SCALER_FMT_RGB565;
-
- if (!scaler_ctx_gen_filter(scalr_ctx))
- goto error;
-
- scalr_out = calloc(sizeof(uint16_t), size * size);
-
- if (!scalr_out)
- goto error;
-
- video_driver_scaler_ptr->scaler_out = scalr_out;
-
- return true;
-
-error:
- video_driver_pixel_converter_free();
- video_driver_filter_free();
-
- return false;
-}
-
-static bool video_driver_init_internal(bool *video_is_threaded)
-{
- video_info_t video;
- unsigned max_dim, scale, width, height;
- video_viewport_t *custom_vp = NULL;
- const input_driver_t *tmp = NULL;
- rarch_system_info_t *system = NULL;
- static uint16_t dummy_pixels[32] = {0};
- settings_t *settings = config_get_ptr();
- struct retro_game_geometry *geom = &video_driver_av_info.geometry;
-
- if (!string_is_empty(settings->paths.path_softfilter_plugin))
- video_driver_init_filter(video_driver_pix_fmt);
-
- max_dim = MAX(geom->max_width, geom->max_height);
- scale = next_pow2(max_dim) / RARCH_SCALE_BASE;
- scale = MAX(scale, 1);
-
- if (video_driver_state_filter)
- scale = video_driver_state_scale;
-
- /* Update core-dependent aspect ratio values. */
- video_driver_set_viewport_square_pixel();
- video_driver_set_viewport_core();
- video_driver_set_viewport_config();
-
- /* Update CUSTOM viewport. */
- custom_vp = video_viewport_get_custom();
-
- if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
- {
- float default_aspect = aspectratio_lut[ASPECT_RATIO_CORE].value;
- aspectratio_lut[ASPECT_RATIO_CUSTOM].value =
- (custom_vp->width && custom_vp->height) ?
- (float)custom_vp->width / custom_vp->height : default_aspect;
- }
-
- video_driver_set_aspect_ratio_value(
- aspectratio_lut[settings->uints.video_aspect_ratio_idx].value);
-
- if (settings->bools.video_fullscreen|| retroarch_is_forced_fullscreen())
- {
- width = settings->uints.video_fullscreen_x;
- height = settings->uints.video_fullscreen_y;
- }
- else
- {
- /* To-Do: remove when the new window resizing core is hooked */
- if (settings->bools.video_window_save_positions &&
- (settings->uints.window_position_width || settings->uints.window_position_height))
- {
- width = settings->uints.window_position_width;
- height = settings->uints.window_position_height;
- }
- else
- {
- if (settings->bools.video_force_aspect)
- {
- /* Do rounding here to simplify integer scale correctness. */
- unsigned base_width =
- roundf(geom->base_height * video_driver_get_aspect_ratio());
- width = roundf(base_width * settings->floats.video_scale);
- }
- else
- width = roundf(geom->base_width * settings->floats.video_scale);
- height = roundf(geom->base_height * settings->floats.video_scale);
-}
- }
-
- if (width && height)
- RARCH_LOG("[Video]: Video @ %ux%u\n", width, height);
- else
- RARCH_LOG("[Video]: Video @ fullscreen\n");
-
- video_driver_display_type_set(RARCH_DISPLAY_NONE);
- video_driver_display_set(0);
- video_driver_window_set(0);
-
- if (!video_driver_pixel_converter_init(RARCH_SCALE_BASE * scale))
- {
- RARCH_ERR("[Video]: Failed to initialize pixel converter.\n");
- goto error;
- }
-
- video.width = width;
- video.height = height;
- video.fullscreen = settings->bools.video_fullscreen || retroarch_is_forced_fullscreen();
- video.vsync = settings->bools.video_vsync && !rarch_ctl(RARCH_CTL_IS_NONBLOCK_FORCED, NULL);
- video.force_aspect = settings->bools.video_force_aspect;
- video.font_enable = settings->bools.video_font_enable;
- video.swap_interval = settings->uints.video_swap_interval;
-#ifdef GEKKO
- video.viwidth = settings->uints.video_viwidth;
- video.vfilter = settings->bools.video_vfilter;
-#endif
- video.smooth = settings->bools.video_smooth;
- video.input_scale = scale;
- video.rgb32 = video_driver_state_filter ?
- video_driver_state_out_rgb32 :
- (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888);
- video.parent = 0;
-
- video_started_fullscreen = video.fullscreen;
-
- /* Reset video frame count */
- video_driver_frame_count = 0;
-
- tmp = input_get_ptr();
- /* Need to grab the "real" video driver interface on a reinit. */
- video_driver_find_driver();
-
-#ifdef HAVE_THREADS
- video.is_threaded = video_driver_is_threaded_internal();
- *video_is_threaded = video.is_threaded;
-
- if (video.is_threaded)
- {
- /* Can't do hardware rendering with threaded driver currently. */
- RARCH_LOG("[Video]: Starting threaded video driver ...\n");
-
- if (!video_init_thread((const video_driver_t**)¤t_video,
- &video_driver_data,
- input_get_double_ptr(), input_driver_get_data_ptr(),
- current_video, video))
- {
- RARCH_ERR("[Video]: Cannot open threaded video driver ... Exiting ...\n");
- goto error;
- }
- }
- else
-#endif
- video_driver_data = current_video->init(
- &video, input_get_double_ptr(),
- input_driver_get_data_ptr());
-
- if (!video_driver_data)
- {
- RARCH_ERR("[Video]: Cannot open video driver ... Exiting ...\n");
- goto error;
- }
-
- if (current_video->focus)
- video_driver_cb_has_focus = video_driver_has_focus;
-
- video_driver_poke = NULL;
- if (current_video->poke_interface)
- current_video->poke_interface(video_driver_data, &video_driver_poke);
-
- if (current_video->viewport_info &&
- (!custom_vp->width ||
- !custom_vp->height))
- {
- /* Force custom viewport to have sane parameters. */
- custom_vp->width = width;
- custom_vp->height = height;
-
- video_driver_get_viewport_info(custom_vp);
- }
-
- system = runloop_get_system_info();
-
- video_driver_set_rotation(
- (settings->uints.video_rotation + system->rotation) % 4);
-
- current_video->suppress_screensaver(video_driver_data,
- settings->bools.ui_suspend_screensaver_enable);
-
- video_driver_init_input(tmp);
-
- command_event(CMD_EVENT_OVERLAY_DEINIT, NULL);
- command_event(CMD_EVENT_OVERLAY_INIT, NULL);
-
- if (!core_is_game_loaded())
- video_driver_cached_frame_set(&dummy_pixels, 4, 4, 8);
-
-#if defined(PSP)
- video_driver_set_texture_frame(&dummy_pixels, false, 1, 1, 1.0f);
-#endif
-
- video_context_driver_reset();
-
- video_display_server_init();
-
- command_event(CMD_EVENT_SHADER_DIR_INIT, NULL);
-
- return true;
-
-error:
- retroarch_fail(1, "init_video()");
- return false;
-}
-
-bool video_driver_set_viewport(unsigned width, unsigned height,
- bool force_fullscreen, bool allow_rotate)
-{
- if (!current_video || !current_video->set_viewport)
- return false;
- current_video->set_viewport(video_driver_data, width, height,
- force_fullscreen, allow_rotate);
- return true;
-}
-
-bool video_driver_set_rotation(unsigned rotation)
-{
- if (!current_video || !current_video->set_rotation)
- return false;
- current_video->set_rotation(video_driver_data, rotation);
- return true;
-}
-
-bool video_driver_set_video_mode(unsigned width,
- unsigned height, bool fullscreen)
-{
- gfx_ctx_mode_t mode;
-
- if (video_driver_poke && video_driver_poke->set_video_mode)
- {
- video_driver_poke->set_video_mode(video_driver_data,
- width, height, fullscreen);
- return true;
- }
-
- mode.width = width;
- mode.height = height;
- mode.fullscreen = fullscreen;
-
- return video_context_driver_set_video_mode(&mode);
-}
-
-bool video_driver_get_video_output_size(unsigned *width, unsigned *height)
-{
- if (!video_driver_poke || !video_driver_poke->get_video_output_size)
- return false;
- video_driver_poke->get_video_output_size(video_driver_data,
- width, height);
- return true;
-}
-
-void video_driver_set_osd_msg(const char *msg, const void *data, void *font)
-{
- video_frame_info_t video_info;
- video_driver_build_info(&video_info);
- if (video_driver_poke && video_driver_poke->set_osd_msg)
- video_driver_poke->set_osd_msg(video_driver_data, &video_info, msg, data, font);
-}
-
-void video_driver_set_texture_enable(bool enable, bool fullscreen)
-{
- if (video_driver_poke && video_driver_poke->set_texture_enable)
- video_driver_poke->set_texture_enable(video_driver_data,
- enable, fullscreen);
-}
-
-void video_driver_set_texture_frame(const void *frame, bool rgb32,
- unsigned width, unsigned height, float alpha)
-{
- if (video_driver_poke && video_driver_poke->set_texture_frame)
- video_driver_poke->set_texture_frame(video_driver_data,
- frame, rgb32, width, height, alpha);
-}
-
-#ifdef HAVE_OVERLAY
-bool video_driver_overlay_interface(const video_overlay_interface_t **iface)
-{
- if (!current_video || !current_video->overlay_interface)
- return false;
- current_video->overlay_interface(video_driver_data, iface);
- return true;
-}
-#endif
-
-void *video_driver_read_frame_raw(unsigned *width,
- unsigned *height, size_t *pitch)
-{
- if (!current_video || !current_video->read_frame_raw)
- return NULL;
- return current_video->read_frame_raw(video_driver_data, width,
- height, pitch);
-}
-
-void video_driver_set_filtering(unsigned index, bool smooth)
-{
- if (video_driver_poke && video_driver_poke->set_filtering)
- video_driver_poke->set_filtering(video_driver_data, index, smooth);
-}
-
-void video_driver_cached_frame_set(const void *data, unsigned width,
- unsigned height, size_t pitch)
-{
- if (data)
- frame_cache_data = data;
- frame_cache_width = width;
- frame_cache_height = height;
- frame_cache_pitch = pitch;
-}
-
-void video_driver_cached_frame_get(const void **data, unsigned *width,
- unsigned *height, size_t *pitch)
-{
- if (data)
- *data = frame_cache_data;
- if (width)
- *width = frame_cache_width;
- if (height)
- *height = frame_cache_height;
- if (pitch)
- *pitch = frame_cache_pitch;
-}
-
-void video_driver_get_size(unsigned *width, unsigned *height)
-{
-#ifdef HAVE_THREADS
- bool is_threaded = video_driver_is_threaded_internal();
- video_driver_threaded_lock(is_threaded);
-#endif
- if (width)
- *width = video_driver_width;
- if (height)
- *height = video_driver_height;
-#ifdef HAVE_THREADS
- video_driver_threaded_unlock(is_threaded);
-#endif
-}
-
-void video_driver_set_size(unsigned *width, unsigned *height)
-{
-#ifdef HAVE_THREADS
- bool is_threaded = video_driver_is_threaded_internal();
- video_driver_threaded_lock(is_threaded);
-#endif
- if (width)
- video_driver_width = *width;
- if (height)
- video_driver_height = *height;
-#ifdef HAVE_THREADS
- video_driver_threaded_unlock(is_threaded);
-#endif
-}
-
-/**
- * video_monitor_set_refresh_rate:
- * @hz : New refresh rate for monitor.
- *
- * Sets monitor refresh rate to new value.
- **/
-void video_monitor_set_refresh_rate(float hz)
-{
- char msg[128];
- settings_t *settings = config_get_ptr();
-
- snprintf(msg, sizeof(msg),
- "Setting refresh rate to: %.3f Hz.", hz);
- runloop_msg_queue_push(msg, 1, 180, false);
- RARCH_LOG("%s\n", msg);
-
- configuration_set_float(settings,
- settings->floats.video_refresh_rate,
- hz);
-}
-
-/**
- * video_monitor_fps_statistics
- * @refresh_rate : Monitor refresh rate.
- * @deviation : Deviation from measured refresh rate.
- * @sample_points : Amount of sampled points.
- *
- * Gets the monitor FPS statistics based on the current
- * runtime.
- *
- * Returns: true (1) on success.
- * false (0) if:
- * a) threaded video mode is enabled
- * b) less than 2 frame time samples.
- * c) FPS monitor enable is off.
- **/
-bool video_monitor_fps_statistics(double *refresh_rate,
- double *deviation, unsigned *sample_points)
-{
- unsigned i;
- retro_time_t accum = 0;
- retro_time_t avg = 0;
- retro_time_t accum_var = 0;
- unsigned samples = 0;
-
-#ifdef HAVE_THREADS
- if (video_driver_is_threaded_internal())
- return false;
-#endif
-
- samples = MIN(MEASURE_FRAME_TIME_SAMPLES_COUNT,
- (unsigned)video_driver_frame_time_count);
-
- if (samples < 2)
- return false;
-
- /* Measure statistics on frame time (microsecs), *not* FPS. */
- for (i = 0; i < samples; i++)
- {
- accum += video_driver_frame_time_samples[i];
-#if 0
- RARCH_LOG("[Video]: Interval #%u: %d usec / frame.\n",
- i, (int)frame_time_samples[i]);
-#endif
- }
-
- avg = accum / samples;
-
- /* Drop first measurement. It is likely to be bad. */
- for (i = 0; i < samples; i++)
- {
- retro_time_t diff = video_driver_frame_time_samples[i] - avg;
- accum_var += diff * diff;
- }
-
- *deviation = sqrt((double)accum_var / (samples - 1)) / avg;
-
- if (refresh_rate)
- *refresh_rate = 1000000.0 / avg;
-
- if (sample_points)
- *sample_points = samples;
-
- return true;
-}
-
-float video_driver_get_aspect_ratio(void)
-{
- return video_driver_aspect_ratio;
-}
-
-void video_driver_set_aspect_ratio_value(float value)
-{
- video_driver_aspect_ratio = value;
-}
-
-static bool video_driver_frame_filter(
- const void *data,
- video_frame_info_t *video_info,
- unsigned width, unsigned height,
- size_t pitch,
- unsigned *output_width, unsigned *output_height,
- unsigned *output_pitch)
-{
- rarch_softfilter_get_output_size(video_driver_state_filter,
- output_width, output_height, width, height);
-
- *output_pitch = (*output_width) * video_driver_state_out_bpp;
-
- rarch_softfilter_process(video_driver_state_filter,
- video_driver_state_buffer, *output_pitch,
- data, width, height, pitch);
-
- if (video_info->post_filter_record && recording_data)
- recording_dump_frame(video_driver_state_buffer,
- *output_width, *output_height, *output_pitch,
- video_info->runloop_is_idle);
-
- return true;
-}
-
-rarch_softfilter_t *video_driver_frame_filter_get_ptr(void)
-{
- return video_driver_state_filter;
-}
-
-enum retro_pixel_format video_driver_get_pixel_format(void)
-{
- return video_driver_pix_fmt;
-}
-
-void video_driver_set_pixel_format(enum retro_pixel_format fmt)
-{
- video_driver_pix_fmt = fmt;
-}
-
-/**
- * video_driver_cached_frame:
- *
- * Renders the current video frame.
- **/
-bool video_driver_cached_frame(void)
-{
- void *recording = recording_driver_get_data_ptr();
-
- recording_driver_lock();
-
- /* Cannot allow recording when pushing duped frames. */
- recording_data = NULL;
-
- retro_ctx.frame_cb(
- (frame_cache_data != RETRO_HW_FRAME_BUFFER_VALID)
- ? frame_cache_data : NULL,
- frame_cache_width,
- frame_cache_height, frame_cache_pitch);
-
- recording_data = recording;
-
- recording_driver_unlock();
-
- return true;
-}
-
-void video_driver_monitor_adjust_system_rates(void)
-{
- float timing_skew = 0.0f;
- settings_t *settings = config_get_ptr();
- float video_refresh_rate = settings->floats.video_refresh_rate;
- float timing_skew_hz = video_refresh_rate;
- const struct retro_system_timing *info = (const struct retro_system_timing*)&video_driver_av_info.timing;
-
- rarch_ctl(RARCH_CTL_UNSET_NONBLOCK_FORCED, NULL);
-
- if (!info || info->fps <= 0.0)
- return;
-
- video_driver_core_hz = info->fps;
-
- if (video_driver_crt_switching_active)
- timing_skew_hz = video_driver_core_hz;
- timing_skew = fabs(
- 1.0f - info->fps / timing_skew_hz);
-
- if (!settings->bools.vrr_runloop_enable)
- {
- /* We don't want to adjust pitch too much. If we have extreme cases,
- * just don't readjust at all. */
- if (timing_skew <= settings->floats.audio_max_timing_skew)
- return;
-
- RARCH_LOG("[Video]: Timings deviate too much. Will not adjust."
- " (Display = %.2f Hz, Game = %.2f Hz)\n",
- video_refresh_rate,
- (float)info->fps);
- }
-
- if (info->fps <= timing_skew_hz)
- return;
-
- /* We won't be able to do VSync reliably when game FPS > monitor FPS. */
- rarch_ctl(RARCH_CTL_SET_NONBLOCK_FORCED, NULL);
- RARCH_LOG("[Video]: Game FPS > Monitor FPS. Cannot rely on VSync.\n");
-}
-
-void video_driver_menu_settings(void **list_data, void *list_info_data,
- void *group_data, void *subgroup_data, const char *parent_group)
-{
-#ifdef HAVE_MENU
- rarch_setting_t **list = (rarch_setting_t**)list_data;
- rarch_setting_info_t *list_info = (rarch_setting_info_t*)list_info_data;
- rarch_setting_group_info_t *group_info = (rarch_setting_group_info_t*)group_data;
- rarch_setting_group_info_t *subgroup_info = (rarch_setting_group_info_t*)subgroup_data;
- global_t *global = global_get_ptr();
-
- (void)list;
- (void)list_info;
- (void)group_info;
- (void)subgroup_info;
- (void)global;
-
-#if defined(__CELLOS_LV2__)
- CONFIG_BOOL(
- list, list_info,
- &global->console.screen.pal60_enable,
- MENU_ENUM_LABEL_PAL60_ENABLE,
- MENU_ENUM_LABEL_VALUE_PAL60_ENABLE,
- false,
- MENU_ENUM_LABEL_VALUE_OFF,
- MENU_ENUM_LABEL_VALUE_ON,
- group_info,
- subgroup_info,
- parent_group,
- general_write_handler,
- general_read_handler,
- SD_FLAG_NONE);
-#endif
-#if defined(GEKKO) || defined(_XBOX360)
- CONFIG_UINT(
- list, list_info,
- &global->console.screen.gamma_correction,
- MENU_ENUM_LABEL_VIDEO_GAMMA,
- MENU_ENUM_LABEL_VALUE_VIDEO_GAMMA,
- 0,
- group_info,
- subgroup_info,
- parent_group,
- general_write_handler,
- general_read_handler);
- menu_settings_list_current_add_cmd(
- list,
- list_info,
- CMD_EVENT_VIDEO_APPLY_STATE_CHANGES);
- menu_settings_list_current_add_range(
- list,
- list_info,
- 0,
- MAX_GAMMA_SETTING,
- 1,
- true,
- true);
- settings_data_list_current_add_flags(list, list_info,
- SD_FLAG_CMD_APPLY_AUTO|SD_FLAG_ADVANCED);
-#endif
-#if defined(_XBOX1) || defined(HW_RVL)
- CONFIG_BOOL(
- list, list_info,
- &global->console.softfilter_enable,
- MENU_ENUM_LABEL_VIDEO_SOFT_FILTER,
- MENU_ENUM_LABEL_VALUE_VIDEO_SOFT_FILTER,
- false,
- MENU_ENUM_LABEL_VALUE_OFF,
- MENU_ENUM_LABEL_VALUE_ON,
- group_info,
- subgroup_info,
- parent_group,
- general_write_handler,
- general_read_handler,
- SD_FLAG_NONE);
- menu_settings_list_current_add_cmd(
- list,
- list_info,
- CMD_EVENT_VIDEO_APPLY_STATE_CHANGES);
-#endif
-#ifdef _XBOX1
- CONFIG_UINT(
- list, list_info,
- &global->console.screen.flicker_filter_index,
- MENU_ENUM_LABEL_VIDEO_FILTER_FLICKER,
- MENU_ENUM_LABEL_VALUE_VIDEO_FILTER_FLICKER,
- 0,
- group_info,
- subgroup_info,
- parent_group,
- general_write_handler,
- general_read_handler);
- menu_settings_list_current_add_range(list, list_info,
- 0, 5, 1, true, true);
-#endif
-#endif
-}
-
-static void video_driver_lock_new(void)
-{
- video_driver_lock_free();
-#ifdef HAVE_THREADS
- if (!display_lock)
- display_lock = slock_new();
- retro_assert(display_lock);
-
- if (!context_lock)
- context_lock = slock_new();
- retro_assert(context_lock);
-#endif
-}
-
-void video_driver_destroy(void)
-{
- video_display_server_destroy();
- crt_video_restore();
-
- video_driver_cb_has_focus = null_driver_has_focus;
- video_driver_use_rgba = false;
- video_driver_data_own = false;
- video_driver_active = false;
- video_driver_cache_context = false;
- video_driver_cache_context_ack = false;
- video_driver_record_gpu_buffer = NULL;
- current_video = NULL;
- video_driver_set_cached_frame_ptr(NULL);
-}
-
-void video_driver_set_cached_frame_ptr(const void *data)
-{
- if (data)
- frame_cache_data = data;
-}
-
-void video_driver_set_stub_frame(void)
-{
- frame_bak = current_video->frame;
- current_video->frame = video_null.frame;
-}
-
-void video_driver_unset_stub_frame(void)
-{
- if (frame_bak != NULL)
- current_video->frame = frame_bak;
-
- frame_bak = NULL;
-}
-
-bool video_driver_is_stub_frame(void)
-{
- return current_video->frame == video_null.frame;
-}
-
-bool video_driver_supports_recording(void)
-{
- settings_t *settings = config_get_ptr();
- return settings->bools.video_gpu_record
- && current_video->read_viewport;
-}
-
-bool video_driver_supports_viewport_read(void)
-{
- settings_t *settings = config_get_ptr();
- return (settings->bools.video_gpu_screenshot ||
- (video_driver_is_hw_context() && !current_video->read_frame_raw))
- && current_video->read_viewport && current_video->viewport_info;
-}
-
-bool video_driver_supports_read_frame_raw(void)
-{
- if (current_video->read_frame_raw)
- return true;
- return false;
-}
-
-void video_driver_set_viewport_config(void)
-{
- settings_t *settings = config_get_ptr();
-
- if (settings->floats.video_aspect_ratio < 0.0f)
- {
- struct retro_game_geometry *geom = &video_driver_av_info.geometry;
-
- if (geom->aspect_ratio > 0.0f &&
- settings->bools.video_aspect_ratio_auto)
- aspectratio_lut[ASPECT_RATIO_CONFIG].value = geom->aspect_ratio;
- else
- {
- unsigned base_width = geom->base_width;
- unsigned base_height = geom->base_height;
-
- /* Get around division by zero errors */
- if (base_width == 0)
- base_width = 1;
- if (base_height == 0)
- base_height = 1;
- aspectratio_lut[ASPECT_RATIO_CONFIG].value =
- (float)base_width / base_height; /* 1:1 PAR. */
- }
- }
- else
- {
- aspectratio_lut[ASPECT_RATIO_CONFIG].value =
- settings->floats.video_aspect_ratio;
- }
-}
-
-void video_driver_set_viewport_square_pixel(void)
-{
- unsigned len, highest, i, aspect_x, aspect_y;
- struct retro_game_geometry *geom = &video_driver_av_info.geometry;
- unsigned width = geom->base_width;
- unsigned height = geom->base_height;
-
- if (width == 0 || height == 0)
- return;
-
- len = MIN(width, height);
- highest = 1;
-
- for (i = 1; i < len; i++)
- {
- if ((width % i) == 0 && (height % i) == 0)
- highest = i;
- }
-
- aspect_x = width / highest;
- aspect_y = height / highest;
-
- snprintf(aspectratio_lut[ASPECT_RATIO_SQUARE].name,
- sizeof(aspectratio_lut[ASPECT_RATIO_SQUARE].name),
- "1:1 PAR (%u:%u DAR)", aspect_x, aspect_y);
-
- aspectratio_lut[ASPECT_RATIO_SQUARE].value = (float)aspect_x / aspect_y;
-}
-
-void video_driver_set_viewport_core(void)
-{
- struct retro_game_geometry *geom = &video_driver_av_info.geometry;
-
- if (!geom || geom->base_width <= 0.0f || geom->base_height <= 0.0f)
- return;
-
- /* Fallback to 1:1 pixel ratio if none provided */
- if (geom->aspect_ratio > 0.0f)
- aspectratio_lut[ASPECT_RATIO_CORE].value = geom->aspect_ratio;
- else
- aspectratio_lut[ASPECT_RATIO_CORE].value =
- (float)geom->base_width / geom->base_height;
-}
-
-void video_driver_reset_custom_viewport(void)
-{
- struct video_viewport *custom_vp = video_viewport_get_custom();
-
- custom_vp->width = 0;
- custom_vp->height = 0;
- custom_vp->x = 0;
- custom_vp->y = 0;
-}
-
-void video_driver_set_rgba(void)
-{
- video_driver_lock();
- video_driver_use_rgba = true;
- video_driver_unlock();
-}
-
-void video_driver_unset_rgba(void)
-{
- video_driver_lock();
- video_driver_use_rgba = false;
- video_driver_unlock();
-}
-
-bool video_driver_supports_rgba(void)
-{
- bool tmp;
- video_driver_lock();
- tmp = video_driver_use_rgba;
- video_driver_unlock();
- return tmp;
-}
-
-bool video_driver_get_next_video_out(void)
-{
- if (!video_driver_poke)
- return false;
-
- if (!video_driver_poke->get_video_output_next)
- return video_context_driver_get_video_output_next();
- video_driver_poke->get_video_output_next(video_driver_data);
- return true;
-}
-
-bool video_driver_get_prev_video_out(void)
-{
- if (!video_driver_poke)
- return false;
-
- if (!video_driver_poke->get_video_output_prev)
- return video_context_driver_get_video_output_prev();
- video_driver_poke->get_video_output_prev(video_driver_data);
- return true;
-}
-
-bool video_driver_init(bool *video_is_threaded)
-{
- video_driver_lock_new();
- video_driver_filter_free();
- video_driver_set_cached_frame_ptr(NULL);
- return video_driver_init_internal(video_is_threaded);
-}
-
-void video_driver_destroy_data(void)
-{
- video_driver_data = NULL;
-}
-
-void video_driver_free(void)
-{
- video_driver_free_internal();
- video_driver_lock_free();
- video_driver_data = NULL;
- video_driver_set_cached_frame_ptr(NULL);
-}
-
-void video_driver_monitor_reset(void)
-{
- video_driver_frame_time_count = 0;
-}
-
-void video_driver_set_aspect_ratio(void)
-{
- settings_t *settings = config_get_ptr();
-
- switch (settings->uints.video_aspect_ratio_idx)
- {
- case ASPECT_RATIO_SQUARE:
- video_driver_set_viewport_square_pixel();
- break;
-
- case ASPECT_RATIO_CORE:
- video_driver_set_viewport_core();
- break;
-
- case ASPECT_RATIO_CONFIG:
- video_driver_set_viewport_config();
- break;
-
- default:
- break;
- }
-
- video_driver_set_aspect_ratio_value(
- aspectratio_lut[settings->uints.video_aspect_ratio_idx].value);
-
- if (!video_driver_poke || !video_driver_poke->set_aspect_ratio)
- return;
- video_driver_poke->set_aspect_ratio(
- video_driver_data, settings->uints.video_aspect_ratio_idx);
-}
-
-void video_driver_update_viewport(struct video_viewport* vp, bool force_full, bool keep_aspect)
-{
- gfx_ctx_aspect_t aspect_data;
- float device_aspect = (float)vp->full_width / vp->full_height;
- settings_t* settings = config_get_ptr();
-
- aspect_data.aspect = &device_aspect;
- aspect_data.width = vp->full_width;
- aspect_data.height = vp->full_height;
-
- video_context_driver_translate_aspect(&aspect_data);
-
- vp->x = 0;
- vp->y = 0;
- vp->width = vp->full_width;
- vp->height = vp->full_height;
-
- if (settings->bools.video_scale_integer && !force_full)
- {
- video_viewport_get_scaled_integer(
- vp, vp->full_width, vp->full_height, video_driver_get_aspect_ratio(), keep_aspect);
- }
- else if (keep_aspect && !force_full)
- {
- float desired_aspect = video_driver_get_aspect_ratio();
-
-#if defined(HAVE_MENU)
- if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
- {
- const struct video_viewport* custom = video_viewport_get_custom();
-
- vp->x = custom->x;
- vp->y = custom->y;
- vp->width = custom->width;
- vp->height = custom->height;
- }
- else
-#endif
- {
- float delta;
-
- if (fabsf(device_aspect - desired_aspect) < 0.0001f)
- {
- /* If the aspect ratios of screen and desired aspect
- * ratio are sufficiently equal (floating point stuff),
- * assume they are actually equal.
- */
- }
- else if (device_aspect > desired_aspect)
- {
- delta = (desired_aspect / device_aspect - 1.0f)
- / 2.0f + 0.5f;
- vp->x = (int)roundf(vp->full_width * (0.5f - delta));
- vp->width = (unsigned)roundf(2.0f * vp->full_width * delta);
- vp->y = 0;
- vp->height = vp->full_height;
- }
- else
- {
- vp->x = 0;
- vp->width = vp->full_width;
- delta = (device_aspect / desired_aspect - 1.0f)
- / 2.0f + 0.5f;
- vp->y = (int)roundf(vp->full_height * (0.5f - delta));
- vp->height = (unsigned)roundf(2.0f * vp->full_height * delta);
- }
- }
- }
-
-#if defined(RARCH_MOBILE)
- /* In portrait mode, we want viewport to gravitate to top of screen. */
- if (device_aspect < 1.0f)
- vp->y = 0;
-#endif
-}
-
-void video_driver_show_mouse(void)
-{
- if (video_driver_poke && video_driver_poke->show_mouse)
- video_driver_poke->show_mouse(video_driver_data, true);
-}
-
-void video_driver_hide_mouse(void)
-{
- if (video_driver_poke && video_driver_poke->show_mouse)
- video_driver_poke->show_mouse(video_driver_data, false);
-}
-
-void video_driver_set_nonblock_state(bool toggle)
-{
- if (current_video->set_nonblock_state)
- current_video->set_nonblock_state(video_driver_data, toggle);
-}
-
-bool video_driver_find_driver(void)
-{
- int i;
- driver_ctx_info_t drv;
- settings_t *settings = config_get_ptr();
-
- if (video_driver_is_hw_context())
- {
- struct retro_hw_render_callback *hwr = video_driver_get_hw_context();
-
- current_video = NULL;
-
- (void)hwr;
-
-#if defined(HAVE_VULKAN)
- if (hwr && hw_render_context_is_vulkan(hwr->context_type))
- {
- RARCH_LOG("[Video]: Using HW render, Vulkan driver forced.\n");
- current_video = &video_vulkan;
- }
-#endif
-
-#if defined(HAVE_OPENGL)
- if (hwr && hw_render_context_is_gl(hwr->context_type))
- {
- RARCH_LOG("[Video]: Using HW render, OpenGL driver forced.\n");
- current_video = &video_gl;
- }
-#endif
-
- if (current_video)
- return true;
- }
-
- if (frontend_driver_has_get_video_driver_func())
- {
- current_video = (video_driver_t*)frontend_driver_get_video_driver();
-
- if (current_video)
- return true;
- RARCH_WARN("Frontend supports get_video_driver() but did not specify one.\n");
- }
-
- drv.label = "video_driver";
- drv.s = settings->arrays.video_driver;
-
- driver_ctl(RARCH_DRIVER_CTL_FIND_INDEX, &drv);
-
- i = (int)drv.len;
-
- if (i >= 0)
- current_video = (video_driver_t*)video_driver_find_handle(i);
- else
- {
- if (verbosity_is_enabled())
- {
- unsigned d;
- RARCH_ERR("Couldn't find any video driver named \"%s\"\n",
- settings->arrays.video_driver);
- RARCH_LOG_OUTPUT("Available video drivers are:\n");
- for (d = 0; video_driver_find_handle(d); d++)
- RARCH_LOG_OUTPUT("\t%s\n", video_driver_find_ident(d));
- RARCH_WARN("Going to default to first video driver...\n");
- }
-
- current_video = (video_driver_t*)video_driver_find_handle(0);
-
- if (!current_video)
- retroarch_fail(1, "find_video_driver()");
- }
- return true;
-}
-
-void video_driver_apply_state_changes(void)
-{
- if (video_driver_poke &&
- video_driver_poke->apply_state_changes)
- video_driver_poke->apply_state_changes(video_driver_data);
-}
-
-bool video_driver_read_viewport(uint8_t *buffer, bool is_idle)
-{
- if ( current_video->read_viewport
- && current_video->read_viewport(
- video_driver_data, buffer, is_idle))
- return true;
- return false;
-}
-
-bool video_driver_frame_filter_alive(void)
-{
- return !!video_driver_state_filter;
-}
-
-bool video_driver_frame_filter_is_32bit(void)
-{
- return video_driver_state_out_rgb32;
-}
-
-void video_driver_default_settings(void)
-{
- global_t *global = global_get_ptr();
-
- if (!global)
- return;
-
- global->console.screen.gamma_correction = DEFAULT_GAMMA;
- global->console.flickerfilter_enable = false;
- global->console.softfilter_enable = false;
-
- global->console.screen.resolutions.current.id = 0;
-}
-
-void video_driver_load_settings(config_file_t *conf)
-{
- bool tmp_bool = false;
- global_t *global = global_get_ptr();
-
- if (!conf)
- return;
-
-#ifdef _XBOX
- CONFIG_GET_BOOL_BASE(conf, global,
- console.screen.gamma_correction, "gamma_correction");
-#else
- CONFIG_GET_INT_BASE(conf, global,
- console.screen.gamma_correction, "gamma_correction");
-#endif
-
- if (config_get_bool(conf, "flicker_filter_enable",
- &tmp_bool))
- global->console.flickerfilter_enable = tmp_bool;
-
- if (config_get_bool(conf, "soft_filter_enable",
- &tmp_bool))
- global->console.softfilter_enable = tmp_bool;
-
- CONFIG_GET_INT_BASE(conf, global,
- console.screen.soft_filter_index,
- "soft_filter_index");
- CONFIG_GET_INT_BASE(conf, global,
- console.screen.resolutions.current.id,
- "current_resolution_id");
- CONFIG_GET_INT_BASE(conf, global,
- console.screen.flicker_filter_index,
- "flicker_filter_index");
-}
-
-void video_driver_save_settings(config_file_t *conf)
-{
- global_t *global = global_get_ptr();
- if (!conf)
- return;
-
-#ifdef _XBOX
- config_set_bool(conf, "gamma_correction",
- global->console.screen.gamma_correction);
-#else
- config_set_int(conf, "gamma_correction",
- global->console.screen.gamma_correction);
-#endif
- config_set_bool(conf, "flicker_filter_enable",
- global->console.flickerfilter_enable);
- config_set_bool(conf, "soft_filter_enable",
- global->console.softfilter_enable);
-
- config_set_int(conf, "soft_filter_index",
- global->console.screen.soft_filter_index);
- config_set_int(conf, "current_resolution_id",
- global->console.screen.resolutions.current.id);
- config_set_int(conf, "flicker_filter_index",
- global->console.screen.flicker_filter_index);
-}
-
-void video_driver_reinit(void)
-{
- struct retro_hw_render_callback *hwr =
- video_driver_get_hw_context();
-
- if (hwr->cache_context)
- video_driver_cache_context = true;
- else
- video_driver_cache_context = false;
-
- video_driver_cache_context_ack = false;
- command_event(CMD_EVENT_RESET_CONTEXT, NULL);
- video_driver_cache_context = false;
-}
-
-void video_driver_set_own_driver(void)
-{
- video_driver_data_own = true;
-}
-
-void video_driver_unset_own_driver(void)
-{
- video_driver_data_own = false;
-}
-
-bool video_driver_owns_driver(void)
-{
- return video_driver_data_own;
-}
-
-bool video_driver_is_hw_context(void)
-{
- bool is_hw_context = false;
-
- video_driver_context_lock();
- is_hw_context = (hw_render.context_type != RETRO_HW_CONTEXT_NONE);
- video_driver_context_unlock();
-
- return is_hw_context;
-}
-
-void video_driver_free_hw_context(void)
-{
- video_driver_context_lock();
-
- if (hw_render.context_destroy)
- hw_render.context_destroy();
-
- memset(&hw_render, 0, sizeof(hw_render));
-
- video_driver_context_unlock();
-
- hw_render_context_negotiation = NULL;
-}
-
-struct retro_hw_render_callback *video_driver_get_hw_context(void)
-{
- return &hw_render;
-}
-
-const struct retro_hw_render_context_negotiation_interface *
- video_driver_get_context_negotiation_interface(void)
-{
- return hw_render_context_negotiation;
-}
-
-void video_driver_set_context_negotiation_interface(
- const struct retro_hw_render_context_negotiation_interface *iface)
-{
- hw_render_context_negotiation = iface;
-}
-
-bool video_driver_is_video_cache_context(void)
-{
- return video_driver_cache_context;
-}
-
-void video_driver_set_video_cache_context_ack(void)
-{
- video_driver_cache_context_ack = true;
-}
-
-void video_driver_unset_video_cache_context_ack(void)
-{
- video_driver_cache_context_ack = false;
-}
-
-bool video_driver_is_video_cache_context_ack(void)
-{
- return video_driver_cache_context_ack;
-}
-
-void video_driver_set_active(void)
-{
- video_driver_active = true;
-}
-
-void video_driver_unset_active(void)
-{
- video_driver_active = false;
-}
-
-bool video_driver_is_active(void)
-{
- return video_driver_active;
-}
-
-void video_driver_get_record_status(
- bool *has_gpu_record,
- uint8_t **gpu_buf)
-{
- *gpu_buf = video_driver_record_gpu_buffer;
- *has_gpu_record = video_driver_record_gpu_buffer != NULL;
-}
-
-bool video_driver_gpu_record_init(unsigned size)
-{
- video_driver_record_gpu_buffer = (uint8_t*)malloc(size);
- if (!video_driver_record_gpu_buffer)
- return false;
- return true;
-}
-
-void video_driver_gpu_record_deinit(void)
-{
- free(video_driver_record_gpu_buffer);
- video_driver_record_gpu_buffer = NULL;
-}
-
-bool video_driver_get_current_software_framebuffer(
- struct retro_framebuffer *fb)
-{
- if (
- video_driver_poke
- && video_driver_poke->get_current_software_framebuffer
- && video_driver_poke->get_current_software_framebuffer(
- video_driver_data, fb))
- return true;
-
- return false;
-}
-
-bool video_driver_get_hw_render_interface(
- const struct retro_hw_render_interface **iface)
-{
- if (
- video_driver_poke
- && video_driver_poke->get_hw_render_interface
- && video_driver_poke->get_hw_render_interface(
- video_driver_data, iface))
- return true;
-
- return false;
-}
-
-bool video_driver_get_viewport_info(struct video_viewport *viewport)
-{
- if (!current_video || !current_video->viewport_info)
- return false;
- current_video->viewport_info(video_driver_data, viewport);
- return true;
-}
-
-void video_driver_set_title_buf(void)
-{
- struct retro_system_info info;
- core_get_system_info(&info);
-
- fill_pathname_join_concat_noext(
- video_driver_title_buf,
- msg_hash_to_str(MSG_PROGRAM),
- " ",
- info.library_name,
- sizeof(video_driver_title_buf));
- strlcat(video_driver_title_buf,
- " ", sizeof(video_driver_title_buf));
- strlcat(video_driver_title_buf,
- info.library_version,
- sizeof(video_driver_title_buf));
-}
-
-/**
- * video_viewport_get_scaled_integer:
- * @vp : Viewport handle
- * @width : Width.
- * @height : Height.
- * @aspect_ratio : Aspect ratio (in float).
- * @keep_aspect : Preserve aspect ratio?
- *
- * Gets viewport scaling dimensions based on
- * scaled integer aspect ratio.
- **/
-void video_viewport_get_scaled_integer(struct video_viewport *vp,
- unsigned width, unsigned height,
- float aspect_ratio, bool keep_aspect)
-{
- int padding_x = 0;
- int padding_y = 0;
- settings_t *settings = config_get_ptr();
-
- if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
- {
- struct video_viewport *custom = video_viewport_get_custom();
-
- if (custom)
- {
- padding_x = width - custom->width;
- padding_y = height - custom->height;
- width = custom->width;
- height = custom->height;
- }
- }
- else
- {
- unsigned base_width;
- /* Use system reported sizes as these define the
- * geometry for the "normal" case. */
- unsigned base_height =
- video_driver_av_info.geometry.base_height;
-
- if (base_height == 0)
- base_height = 1;
-
- /* Account for non-square pixels.
- * This is sort of contradictory with the goal of integer scale,
- * but it is desirable in some cases.
- *
- * If square pixels are used, base_height will be equal to
- * system->av_info.base_height. */
- base_width = (unsigned)roundf(base_height * aspect_ratio);
-
- /* Make sure that we don't get 0x scale ... */
- if (width >= base_width && height >= base_height)
- {
- if (keep_aspect)
- {
- /* X/Y scale must be same. */
- unsigned max_scale = MIN(width / base_width,
- height / base_height);
- padding_x = width - base_width * max_scale;
- padding_y = height - base_height * max_scale;
- }
- else
- {
- /* X/Y can be independent, each scaled as much as possible. */
- padding_x = width % base_width;
- padding_y = height % base_height;
- }
- }
-
- width -= padding_x;
- height -= padding_y;
- }
-
- vp->width = width;
- vp->height = height;
- vp->x = padding_x / 2;
- vp->y = padding_y / 2;
-}
-
-struct retro_system_av_info *video_viewport_get_system_av_info(void)
-{
- return &video_driver_av_info;
-}
-
-struct video_viewport *video_viewport_get_custom(void)
-{
- settings_t *settings = config_get_ptr();
- return &settings->video_viewport_custom;
-}
-
-unsigned video_pixel_get_alignment(unsigned pitch)
-{
- if (pitch & 1)
- return 1;
- if (pitch & 2)
- return 2;
- if (pitch & 4)
- return 4;
- return 8;
-}
-
-/**
- * video_driver_frame:
- * @data : pointer to data of the video frame.
- * @width : width of the video frame.
- * @height : height of the video frame.
- * @pitch : pitch of the video frame.
- *
- * Video frame render callback function.
- **/
-void video_driver_frame(const void *data, unsigned width,
- unsigned height, size_t pitch)
-{
- static char video_driver_msg[256];
- static char title[256];
- video_frame_info_t video_info;
- static retro_time_t curr_time;
- static retro_time_t fps_time;
- static float last_fps, frame_time;
- unsigned output_width = 0;
- unsigned output_height = 0;
- unsigned output_pitch = 0;
- const char *msg = NULL;
- retro_time_t new_time =
- cpu_features_get_time_usec();
-
- if (!video_driver_active)
- return;
-
- if (video_driver_scaler_ptr && data &&
- (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_0RGB1555) &&
- (data != RETRO_HW_FRAME_BUFFER_VALID))
- {
- if (video_pixel_frame_scale(
- video_driver_scaler_ptr->scaler,
- video_driver_scaler_ptr->scaler_out,
- data, width, height, pitch))
- {
- data = video_driver_scaler_ptr->scaler_out;
- pitch = video_driver_scaler_ptr->scaler->out_stride;
- }
- }
-
- if (data)
- frame_cache_data = data;
- frame_cache_width = width;
- frame_cache_height = height;
- frame_cache_pitch = pitch;
-
- video_driver_build_info(&video_info);
-
- /* Get the amount of frames per seconds. */
- if (video_driver_frame_count)
- {
- unsigned write_index =
- video_driver_frame_time_count++ &
- (MEASURE_FRAME_TIME_SAMPLES_COUNT - 1);
- frame_time = new_time - fps_time;
- video_driver_frame_time_samples[write_index] = frame_time;
- fps_time = new_time;
-
- if (video_driver_frame_count == 1)
- strlcpy(title, video_driver_window_title, sizeof(title));
-
- if ((video_driver_frame_count % FPS_UPDATE_INTERVAL) == 0)
- {
- char frames_text[64];
- last_fps = TIME_TO_FPS(curr_time, new_time, FPS_UPDATE_INTERVAL);
-
- if (video_info.fps_show || video_info.framecount_show)
- {
- if (video_info.fps_show)
- {
- snprintf(video_info.fps_text, sizeof(video_info.fps_text),
- " || FPS: %6.1f ", last_fps);
- }
- if (video_info.framecount_show)
- {
- snprintf(frames_text,
- sizeof(frames_text),
- " || Frames: %" PRIu64,
- (uint64_t)video_driver_frame_count);
- }
- snprintf(video_driver_window_title, sizeof(video_driver_window_title),
- "%s%s%s", title,
- video_info.fps_show ? video_info.fps_text : "",
- video_info.framecount_show ? frames_text : "");
- }
- else
- {
- if (!string_is_equal(video_driver_window_title, title))
- strlcpy(video_driver_window_title, title, sizeof(video_driver_window_title));
- }
-
- curr_time = new_time;
- video_driver_window_title_update = true;
- }
-
- if (video_info.fps_show)
- {
- if (video_info.framecount_show)
- snprintf(
- video_info.fps_text,
- sizeof(video_info.fps_text),
- "FPS: %6.1f || %s: %" PRIu64,
- last_fps,
- msg_hash_to_str(MSG_FRAMES),
- (uint64_t)video_driver_frame_count);
- else
- snprintf(
- video_info.fps_text,
- sizeof(video_info.fps_text),
- "FPS: %6.1f",
- last_fps);
- }
-
- if (video_info.fps_show && video_info.framecount_show)
- snprintf(
- video_info.fps_text,
- sizeof(video_info.fps_text),
- "FPS: %6.1f || %s: %" PRIu64,
- last_fps,
- msg_hash_to_str(MSG_FRAMES),
- (uint64_t)video_driver_frame_count);
- else if (video_info.framecount_show)
- snprintf(
- video_info.fps_text,
- sizeof(video_info.fps_text),
- "%s: %" PRIu64,
- msg_hash_to_str(MSG_FRAMES),
- (uint64_t)video_driver_frame_count);
- else if (video_info.fps_show)
- snprintf(
- video_info.fps_text,
- sizeof(video_info.fps_text),
- "FPS: %6.1f",
- last_fps);
- }
- else
- {
-
- curr_time = fps_time = new_time;
-
- strlcpy(video_driver_window_title,
- video_driver_title_buf,
- sizeof(video_driver_window_title));
-
- if (video_info.fps_show)
- strlcpy(video_info.fps_text,
- msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE),
- sizeof(video_info.fps_text));
-
- video_driver_window_title_update = true;
- }
-
- video_info.frame_rate = last_fps;
- video_info.frame_time = frame_time / 1000.0f;
- video_info.frame_count = (uint64_t) video_driver_frame_count;
-
- /* Slightly messy code,
- * but we really need to do processing before blocking on VSync
- * for best possible scheduling.
- */
- if (
- (
- !video_driver_state_filter
- || !video_info.post_filter_record
- || !data
- || video_driver_record_gpu_buffer
- ) && recording_data
- )
- recording_dump_frame(data, width, height,
- pitch, video_info.runloop_is_idle);
-
- if (data && video_driver_state_filter &&
- video_driver_frame_filter(data, &video_info, width, height, pitch,
- &output_width, &output_height, &output_pitch))
- {
- data = video_driver_state_buffer;
- width = output_width;
- height = output_height;
- pitch = output_pitch;
- }
-
- video_driver_msg[0] = '\0';
-
- if ( video_info.font_enable
- && runloop_msg_queue_pull((const char**)&msg)
- && msg)
- {
-#ifdef HAVE_THREADS
- /* the msg pointer may point to data modified by another thread */
- runloop_msg_queue_lock();
-#endif
- strlcpy(video_driver_msg, msg, sizeof(video_driver_msg));
-#ifdef HAVE_THREADS
- runloop_msg_queue_unlock();
-#endif
- }
-
- if (video_info.statistics_show)
- {
- audio_statistics_t audio_stats = {0.0f};
- double stddev = 0.0;
- struct retro_system_av_info *av_info = &video_driver_av_info;
- unsigned red = 255;
- unsigned green = 255;
- unsigned blue = 255;
- unsigned alpha = 255;
-
- video_monitor_fps_statistics(NULL, &stddev, NULL);
-
- video_info.osd_stat_params.x = 0.010f;
- video_info.osd_stat_params.y = 0.950f;
- video_info.osd_stat_params.scale = 1.0f;
- video_info.osd_stat_params.full_screen = true;
- video_info.osd_stat_params.drop_x = -2;
- video_info.osd_stat_params.drop_y = -2;
- video_info.osd_stat_params.drop_mod = 0.3f;
- video_info.osd_stat_params.drop_alpha = 1.0f;
- video_info.osd_stat_params.color = COLOR_ABGR(
- red, green, blue, alpha);
-
- compute_audio_buffer_statistics(&audio_stats);
-
- snprintf(video_info.stat_text,
- sizeof(video_info.stat_text),
- "Video Statistics:\n -Frame rate: %6.2f fps\n -Frame time: %6.2f ms\n -Frame time deviation: %.3f %%\n"
- " -Frame count: %" PRIu64"\n -Viewport: %d x %d x %3.2f\n"
- "Audio Statistics:\n -Average buffer saturation: %.2f %%\n -Standard deviation: %.2f %%\n -Time spent close to underrun: %.2f %%\n -Time spent close to blocking: %.2f %%\n -Sample count: %d\n"
- "Core Geometry:\n -Size: %u x %u\n -Max Size: %u x %u\n -Aspect: %3.2f\nCore Timing:\n -FPS: %3.2f\n -Sample Rate: %6.2f\n",
- video_info.frame_rate,
- video_info.frame_time,
- 100.0 * stddev,
- video_info.frame_count,
- video_info.width,
- video_info.height,
- video_info.refresh_rate,
- audio_stats.average_buffer_saturation,
- audio_stats.std_deviation_percentage,
- audio_stats.close_to_underrun,
- audio_stats.close_to_blocking,
- audio_stats.samples,
- av_info->geometry.base_width,
- av_info->geometry.base_height,
- av_info->geometry.max_width,
- av_info->geometry.max_height,
- av_info->geometry.aspect_ratio,
- av_info->timing.fps,
- av_info->timing.sample_rate);
-
- /* TODO/FIXME - add OSD chat text here */
-#if 0
- snprintf(video_info.chat_text, sizeof(video_info.chat_text),
- "anon: does retroarch netplay have in-game chat?\nradius: I don't know \u2605");
-#endif
- }
-
- video_driver_active = current_video->frame(
- video_driver_data, data, width, height,
- video_driver_frame_count,
- (unsigned)pitch, video_driver_msg, &video_info);
-
- video_driver_frame_count++;
-
- /* Display the FPS, with a higher priority. */
- if (video_info.fps_show || video_info.framecount_show)
- runloop_msg_queue_push(video_info.fps_text, 2, 1, true);
-
- /* trigger set resolution*/
- if (video_info.crt_switch_resolution)
- {
- video_driver_crt_switching_active = true;
-
- if (video_info.crt_switch_resolution_super == 2560)
- width = 2560;
- if (video_info.crt_switch_resolution_super == 3840)
- width = 3840;
- if (video_info.crt_switch_resolution_super == 1920)
- width = 1920;
- crt_switch_res_core(width, height, video_driver_core_hz, video_info.crt_switch_resolution, video_info.crt_switch_center_adjust);
- }
- else if (!video_info.crt_switch_resolution)
- video_driver_crt_switching_active = false;
-
- /* trigger set resolution*/
-}
-
-void video_driver_display_type_set(enum rarch_display_type type)
-{
- video_driver_display_type = type;
-}
-
-uintptr_t video_driver_display_get(void)
-{
- return video_driver_display;
-}
-
-void video_driver_display_set(uintptr_t idx)
-{
- video_driver_display = idx;
-}
-
-enum rarch_display_type video_driver_display_type_get(void)
-{
- return video_driver_display_type;
-}
-
-void video_driver_window_set(uintptr_t idx)
-{
- video_driver_window = idx;
-}
-
-uintptr_t video_driver_window_get(void)
-{
- return video_driver_window;
-}
-
-bool video_driver_texture_load(void *data,
- enum texture_filter_type filter_type,
- uintptr_t *id)
-{
- if (!id || !video_driver_poke || !video_driver_poke->load_texture)
- return false;
-
- *id = video_driver_poke->load_texture(video_driver_data, data,
- video_driver_is_threaded_internal(),
- filter_type);
-
- return true;
-}
-
-bool video_driver_texture_unload(uintptr_t *id)
-{
- if (!video_driver_poke || !video_driver_poke->unload_texture)
- return false;
-
- video_driver_poke->unload_texture(video_driver_data, *id);
- *id = 0;
- return true;
-}
-
-static void video_shader_driver_use_null(void *data,
- void *shader_data, unsigned idx, bool set_active)
-{
- (void)data;
- (void)idx;
- (void)set_active;
-}
-
-static bool video_driver_cb_set_coords(void *handle_data,
- void *shader_data, const struct video_coords *coords)
-{
- video_shader_ctx_coords_t ctx_coords;
- ctx_coords.handle_data = handle_data;
- ctx_coords.data = coords;
-
- video_driver_set_coords(&ctx_coords);
- return true;
-}
-
-void video_driver_build_info(video_frame_info_t *video_info)
-{
- bool is_perfcnt_enable = false;
- bool is_paused = false;
- bool is_idle = false;
- bool is_slowmotion = false;
- settings_t *settings = NULL;
- video_viewport_t *custom_vp = NULL;
- struct retro_hw_render_callback *hwr =
- video_driver_get_hw_context();
-#ifdef HAVE_THREADS
- bool is_threaded = video_driver_is_threaded_internal();
- video_driver_threaded_lock(is_threaded);
-#endif
- settings = config_get_ptr();
- custom_vp = &settings->video_viewport_custom;
- video_info->refresh_rate = settings->floats.video_refresh_rate;
- video_info->crt_switch_resolution = settings->uints.crt_switch_resolution;
- video_info->crt_switch_resolution_super = settings->uints.crt_switch_resolution_super;
- video_info->crt_switch_center_adjust = settings->ints.crt_switch_center_adjust;
- video_info->black_frame_insertion = settings->bools.video_black_frame_insertion;
- video_info->hard_sync = settings->bools.video_hard_sync;
- video_info->hard_sync_frames = settings->uints.video_hard_sync_frames;
- video_info->fps_show = settings->bools.video_fps_show;
- video_info->statistics_show = settings->bools.video_statistics_show;
- video_info->framecount_show = settings->bools.video_framecount_show;
- video_info->scale_integer = settings->bools.video_scale_integer;
- video_info->aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
- video_info->post_filter_record = settings->bools.video_post_filter_record;
- video_info->input_menu_swap_ok_cancel_buttons = settings->bools.input_menu_swap_ok_cancel_buttons;
- video_info->max_swapchain_images = settings->uints.video_max_swapchain_images;
- video_info->windowed_fullscreen = settings->bools.video_windowed_fullscreen;
- video_info->fullscreen = settings->bools.video_fullscreen || retroarch_is_forced_fullscreen();
- video_info->monitor_index = settings->uints.video_monitor_index;
- video_info->shared_context = settings->bools.video_shared_context;
-
- if (libretro_get_shared_context() && hwr && hwr->context_type != RETRO_HW_CONTEXT_NONE)
- video_info->shared_context = true;
-
- video_info->font_enable = settings->bools.video_font_enable;
- video_info->font_msg_pos_x = settings->floats.video_msg_pos_x;
- video_info->font_msg_pos_y = settings->floats.video_msg_pos_y;
- video_info->font_msg_color_r = settings->floats.video_msg_color_r;
- video_info->font_msg_color_g = settings->floats.video_msg_color_g;
- video_info->font_msg_color_b = settings->floats.video_msg_color_b;
- video_info->custom_vp_x = custom_vp->x;
- video_info->custom_vp_y = custom_vp->y;
- video_info->custom_vp_width = custom_vp->width;
- video_info->custom_vp_height = custom_vp->height;
- video_info->custom_vp_full_width = custom_vp->full_width;
- video_info->custom_vp_full_height = custom_vp->full_height;
-
- video_info->fps_text[0] = '\0';
-
- video_info->width = video_driver_width;
- video_info->height = video_driver_height;
-
- video_info->use_rgba = video_driver_use_rgba;
-
- video_info->libretro_running = false;
- video_info->msg_bgcolor_enable = settings->bools.video_msg_bgcolor_enable;
-
-#ifdef HAVE_MENU
- video_info->menu_is_alive = menu_driver_is_alive();
- video_info->menu_footer_opacity = settings->floats.menu_footer_opacity;
- video_info->menu_header_opacity = settings->floats.menu_header_opacity;
- video_info->materialui_color_theme = settings->uints.menu_materialui_color_theme;
- video_info->ozone_color_theme = settings->uints.menu_ozone_color_theme;
- video_info->menu_shader_pipeline = settings->uints.menu_xmb_shader_pipeline;
- video_info->xmb_theme = settings->uints.menu_xmb_theme;
- video_info->xmb_color_theme = settings->uints.menu_xmb_color_theme;
- video_info->timedate_enable = settings->bools.menu_timedate_enable;
- video_info->battery_level_enable = settings->bools.menu_battery_level_enable;
- video_info->xmb_shadows_enable = settings->bools.menu_xmb_shadows_enable;
- video_info->xmb_alpha_factor = settings->uints.menu_xmb_alpha_factor;
- video_info->menu_wallpaper_opacity = settings->floats.menu_wallpaper_opacity;
- video_info->menu_framebuffer_opacity = settings->floats.menu_framebuffer_opacity;
-
- video_info->libretro_running = core_is_game_loaded();
-#else
- video_info->menu_is_alive = false;
- video_info->menu_footer_opacity = 0.0f;
- video_info->menu_header_opacity = 0.0f;
- video_info->materialui_color_theme = 0;
- video_info->menu_shader_pipeline = 0;
- video_info->xmb_color_theme = 0;
- video_info->xmb_theme = 0;
- video_info->timedate_enable = false;
- video_info->battery_level_enable = false;
- video_info->xmb_shadows_enable = false;
- video_info->xmb_alpha_factor = 0.0f;
- video_info->menu_framebuffer_opacity = 0.0f;
- video_info->menu_wallpaper_opacity = 0.0f;
-#endif
-
- runloop_get_status(&is_paused, &is_idle, &is_slowmotion, &is_perfcnt_enable);
-
- video_info->is_perfcnt_enable = is_perfcnt_enable;
- video_info->runloop_is_paused = is_paused;
- video_info->runloop_is_idle = is_idle;
- video_info->runloop_is_slowmotion = is_slowmotion;
-
- video_info->input_driver_nonblock_state = input_driver_is_nonblock_state();
-
- video_info->context_data = video_context_data;
- video_info->shader_driver = current_shader;
- video_info->shader_data = current_shader_data;
-
- video_info->cb_update_window_title = current_video_context.update_window_title;
- video_info->cb_swap_buffers = current_video_context.swap_buffers;
- video_info->cb_get_metrics = current_video_context.get_metrics;
- video_info->cb_set_resize = current_video_context.set_resize;
-
- video_info->cb_set_mvp = video_driver_cb_shader_set_mvp;
-
- video_info->userdata = video_driver_get_ptr(false);
-
-#ifdef HAVE_THREADS
- video_driver_threaded_unlock(is_threaded);
-#endif
-}
-
-/**
- * video_driver_translate_coord_viewport:
- * @mouse_x : Pointer X coordinate.
- * @mouse_y : Pointer Y coordinate.
- * @res_x : Scaled X coordinate.
- * @res_y : Scaled Y coordinate.
- * @res_screen_x : Scaled screen X coordinate.
- * @res_screen_y : Scaled screen Y coordinate.
- *
- * Translates pointer [X,Y] coordinates into scaled screen
- * coordinates based on viewport info.
- *
- * Returns: true (1) if successful, false if video driver doesn't support
- * viewport info.
- **/
-bool video_driver_translate_coord_viewport(
- struct video_viewport *vp,
- int mouse_x, int mouse_y,
- int16_t *res_x, int16_t *res_y,
- int16_t *res_screen_x, int16_t *res_screen_y)
-{
- int scaled_screen_x, scaled_screen_y, scaled_x, scaled_y;
- int norm_vp_width = (int)vp->width;
- int norm_vp_height = (int)vp->height;
- int norm_full_vp_width = (int)vp->full_width;
- int norm_full_vp_height = (int)vp->full_height;
-
- if (norm_full_vp_width <= 0 || norm_full_vp_height <= 0)
- return false;
-
- if (mouse_x >= 0 && mouse_x <= norm_full_vp_width)
- scaled_screen_x = ((2 * mouse_x * 0x7fff)
- / norm_full_vp_width) - 0x7fff;
- else
- scaled_screen_x = -0x8000; /* OOB */
-
- if (mouse_y >= 0 && mouse_y <= norm_full_vp_height)
- scaled_screen_y = ((2 * mouse_y * 0x7fff)
- / norm_full_vp_height) - 0x7fff;
- else
- scaled_screen_y = -0x8000; /* OOB */
-
- mouse_x -= vp->x;
- mouse_y -= vp->y;
-
- if (mouse_x >= 0 && mouse_x <= norm_vp_width)
- scaled_x = ((2 * mouse_x * 0x7fff)
- / norm_vp_width) - 0x7fff;
- else
- scaled_x = -0x8000; /* OOB */
-
- if (mouse_y >= 0 && mouse_y <= norm_vp_height)
- scaled_y = ((2 * mouse_y * 0x7fff)
- / norm_vp_height) - 0x7fff;
- else
- scaled_y = -0x8000; /* OOB */
-
- *res_x = scaled_x;
- *res_y = scaled_y;
- *res_screen_x = scaled_screen_x;
- *res_screen_y = scaled_screen_y;
-
- return true;
-}
-
-void video_driver_get_window_title(char *buf, unsigned len)
-{
- if (buf && video_driver_window_title_update)
- {
- strlcpy(buf, video_driver_window_title, len);
- video_driver_window_title_update = false;
- }
-}
-
-void video_driver_get_status(uint64_t *frame_count, bool * is_alive,
- bool *is_focused)
-{
- *frame_count = video_driver_frame_count;
- *is_alive = current_video ?
- current_video->alive(video_driver_data) : true;
- *is_focused = video_driver_cb_has_focus();
-}
-
-/**
- * find_video_context_driver_driver_index:
- * @ident : Identifier of resampler driver to find.
- *
- * Finds graphics context driver index by @ident name.
- *
- * Returns: graphics context driver index if driver was found, otherwise
- * -1.
- **/
-static int find_video_context_driver_index(const char *ident)
-{
- unsigned i;
- for (i = 0; gfx_ctx_drivers[i]; i++)
- if (string_is_equal_noncase(ident, gfx_ctx_drivers[i]->ident))
- return i;
- return -1;
-}
-
-/**
- * find_prev_context_driver:
- *
- * Finds previous driver in graphics context driver array.
- **/
-bool video_context_driver_find_prev_driver(void)
-{
- settings_t *settings = config_get_ptr();
- int i = find_video_context_driver_index(
- settings->arrays.video_context_driver);
-
- if (i > 0)
- {
- strlcpy(settings->arrays.video_context_driver,
- gfx_ctx_drivers[i - 1]->ident,
- sizeof(settings->arrays.video_context_driver));
- return true;
- }
-
- RARCH_WARN("Couldn't find any previous video context driver.\n");
- return false;
-}
-
-/**
- * find_next_context_driver:
- *
- * Finds next driver in graphics context driver array.
- **/
-bool video_context_driver_find_next_driver(void)
-{
- settings_t *settings = config_get_ptr();
- int i = find_video_context_driver_index(
- settings->arrays.video_context_driver);
-
- if (i >= 0 && gfx_ctx_drivers[i + 1])
- {
- strlcpy(settings->arrays.video_context_driver,
- gfx_ctx_drivers[i + 1]->ident,
- sizeof(settings->arrays.video_context_driver));
- return true;
- }
-
- RARCH_WARN("Couldn't find any next video context driver.\n");
- return false;
-}
-
-/**
- * video_context_driver_init:
- * @data : Input data.
- * @ctx : Graphics context driver to initialize.
- * @ident : Identifier of graphics context driver to find.
- * @api : API of higher-level graphics API.
- * @major : Major version number of higher-level graphics API.
- * @minor : Minor version number of higher-level graphics API.
- * @hw_render_ctx : Request a graphics context driver capable of
- * hardware rendering?
- *
- * Initialize graphics context driver.
- *
- * Returns: graphics context driver if successfully initialized,
- * otherwise NULL.
- **/
-static const gfx_ctx_driver_t *video_context_driver_init(
- void *data,
- const gfx_ctx_driver_t *ctx,
- const char *ident,
- enum gfx_ctx_api api, unsigned major,
- unsigned minor, bool hw_render_ctx,
- void **ctx_data)
-{
- video_frame_info_t video_info;
-
- if (!ctx->bind_api(data, api, major, minor))
- {
- RARCH_WARN("Failed to bind API (#%u, version %u.%u)"
- " on context driver \"%s\".\n",
- (unsigned)api, major, minor, ctx->ident);
-
- return NULL;
- }
-
- video_driver_build_info(&video_info);
-
- if (!(*ctx_data = ctx->init(&video_info, data)))
- return NULL;
-
- if (ctx->bind_hw_render)
- ctx->bind_hw_render(*ctx_data,
- video_info.shared_context && hw_render_ctx);
-
- return ctx;
-}
-
-/**
- * video_context_driver_init_first:
- * @data : Input data.
- * @ident : Identifier of graphics context driver to find.
- * @api : API of higher-level graphics API.
- * @major : Major version number of higher-level graphics API.
- * @minor : Minor version number of higher-level graphics API.
- * @hw_render_ctx : Request a graphics context driver capable of
- * hardware rendering?
- *
- * Finds first suitable graphics context driver and initializes.
- *
- * Returns: graphics context driver if found, otherwise NULL.
- **/
-const gfx_ctx_driver_t *video_context_driver_init_first(void *data,
- const char *ident, enum gfx_ctx_api api, unsigned major,
- unsigned minor, bool hw_render_ctx, void **ctx_data)
-{
- int i = find_video_context_driver_index(ident);
-
- if (i >= 0)
- {
- const gfx_ctx_driver_t *ctx = video_context_driver_init(data, gfx_ctx_drivers[i], ident,
- api, major, minor, hw_render_ctx, ctx_data);
- if (ctx)
- {
- video_context_data = *ctx_data;
- return ctx;
- }
- }
-
- for (i = 0; gfx_ctx_drivers[i]; i++)
- {
- const gfx_ctx_driver_t *ctx =
- video_context_driver_init(data, gfx_ctx_drivers[i], ident,
- api, major, minor, hw_render_ctx, ctx_data);
-
- if (ctx)
- {
- video_context_data = *ctx_data;
- return ctx;
- }
- }
-
- return NULL;
-}
-
-bool video_context_driver_init_image_buffer(const video_info_t *data)
-{
- if (
- current_video_context.image_buffer_init
- && current_video_context.image_buffer_init(
- video_context_data, data))
- return true;
- return false;
-}
-
-bool video_context_driver_write_to_image_buffer(gfx_ctx_image_t *img)
-{
- if (
- current_video_context.image_buffer_write
- && current_video_context.image_buffer_write(video_context_data,
- img->frame, img->width, img->height, img->pitch,
- img->rgb32, img->index, img->handle))
- return true;
- return false;
-}
-
-bool video_context_driver_get_video_output_prev(void)
-{
- if (!current_video_context.get_video_output_prev)
- return false;
- current_video_context.get_video_output_prev(video_context_data);
- return true;
-}
-
-bool video_context_driver_get_video_output_next(void)
-{
- if (!current_video_context.get_video_output_next)
- return false;
- current_video_context.get_video_output_next(video_context_data);
- return true;
-}
-
-void video_context_driver_make_current(bool release)
-{
- if (current_video_context.make_current)
- current_video_context.make_current(release);
-}
-
-bool video_context_driver_translate_aspect(gfx_ctx_aspect_t *aspect)
-{
- if (!video_context_data || !aspect)
- return false;
- if (!current_video_context.translate_aspect)
- return false;
- *aspect->aspect = current_video_context.translate_aspect(
- video_context_data, aspect->width, aspect->height);
- return true;
-}
-
-void video_context_driver_free(void)
-{
- if (current_video_context.destroy)
- current_video_context.destroy(video_context_data);
- video_context_driver_destroy();
- video_context_data = NULL;
-}
-
-bool video_context_driver_get_video_output_size(gfx_ctx_size_t *size_data)
-{
- if (!size_data)
- return false;
- if (!current_video_context.get_video_output_size)
- return false;
- current_video_context.get_video_output_size(video_context_data,
- size_data->width, size_data->height);
- return true;
-}
-
-bool video_context_driver_swap_interval(int *interval)
-{
- gfx_ctx_flags_t flags;
- int current_interval = *interval;
- settings_t *settings = config_get_ptr();
- bool adaptive_vsync_enabled = video_driver_get_all_flags(&flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC) && settings->bools.video_adaptive_vsync;
-
- if (!current_video_context.swap_interval)
- return false;
- if (adaptive_vsync_enabled && current_interval == 1)
- current_interval = -1;
- current_video_context.swap_interval(video_context_data, current_interval);
- return true;
-}
-
-bool video_context_driver_get_proc_address(gfx_ctx_proc_address_t *proc)
-{
- if (!current_video_context.get_proc_address)
- return false;
-
- proc->addr = current_video_context.get_proc_address(proc->sym);
-
- return true;
-}
-
-bool video_context_driver_get_metrics(gfx_ctx_metrics_t *metrics)
-{
- if (
- current_video_context.get_metrics(video_context_data,
- metrics->type,
- metrics->value))
- return true;
- return false;
-}
-
-bool video_context_driver_get_refresh_rate(float *refresh_rate)
-{
- float refresh_holder = 0;
-
- if (!current_video_context.get_refresh_rate || !refresh_rate)
- return false;
- if (!video_context_data)
- return false;
-
- if (!video_driver_crt_switching_active)
- if (refresh_rate)
- *refresh_rate =
- current_video_context.get_refresh_rate(video_context_data);
-
- if (video_driver_crt_switching_active)
- {
- if (refresh_rate)
- refresh_holder =
- current_video_context.get_refresh_rate(video_context_data);
- if (refresh_holder != video_driver_core_hz) /* Fix for incorrect interlace detsction -- HARD SET VSNC TO REQUIRED REFRESH FOR CRT*/
- *refresh_rate = video_driver_core_hz;
- }
-
- return true;
-}
-
-bool video_context_driver_input_driver(gfx_ctx_input_t *inp)
-{
- settings_t *settings = config_get_ptr();
- const char *joypad_name = settings ?
- settings->arrays.input_joypad_driver : NULL;
-
- if (!current_video_context.input_driver)
- return false;
- current_video_context.input_driver(
- video_context_data, joypad_name,
- inp->input, inp->input_data);
- return true;
-}
-
-bool video_context_driver_suppress_screensaver(bool *bool_data)
-{
- if ( video_context_data
- && current_video_context.suppress_screensaver(
- video_context_data, *bool_data))
- return true;
- return false;
-}
-
-bool video_context_driver_get_ident(gfx_ctx_ident_t *ident)
-{
- if (!ident)
- return false;
- ident->ident = current_video_context.ident;
- return true;
-}
-
-bool video_context_driver_set_video_mode(gfx_ctx_mode_t *mode_info)
-{
- video_frame_info_t video_info;
-
- if (!current_video_context.set_video_mode)
- return false;
-
- video_driver_build_info(&video_info);
-
- if (!current_video_context.set_video_mode(
- video_context_data, &video_info, mode_info->width,
- mode_info->height, mode_info->fullscreen))
- return false;
- return true;
-}
-
-bool video_context_driver_get_video_size(gfx_ctx_mode_t *mode_info)
-{
- if (!current_video_context.get_video_size)
- return false;
- current_video_context.get_video_size(video_context_data,
- &mode_info->width, &mode_info->height);
- return true;
-}
-
-bool video_context_driver_show_mouse(bool *bool_data)
-{
- if (!current_video_context.show_mouse)
- return false;
- current_video_context.show_mouse(video_context_data, *bool_data);
- return true;
-}
-
-static bool video_context_driver_get_flags(gfx_ctx_flags_t *flags)
-{
- if (!current_video_context.get_flags)
- return false;
-
- if (deferred_video_context_driver_set_flags)
- {
- flags->flags = deferred_flag_data.flags;
- deferred_video_context_driver_set_flags = false;
- return true;
- }
-
- flags->flags = current_video_context.get_flags(video_context_data);
- return true;
-}
-
-static bool video_driver_get_flags(gfx_ctx_flags_t *flags)
-{
- if (!video_driver_poke || !video_driver_poke->get_flags)
- return false;
- flags->flags = video_driver_poke->get_flags(video_driver_data);
- return true;
-}
-
-bool video_driver_get_all_flags(gfx_ctx_flags_t *flags, enum display_flags flag)
-{
- if (!flags)
- return false;
-
- if (video_driver_get_flags(flags))
- if (BIT32_GET(flags->flags, flag))
- return true;
-
- flags->flags = 0;
-
- if (video_context_driver_get_flags(flags))
- if (BIT32_GET(flags->flags, flag))
- return true;
-
- return false;
-}
-
-bool video_context_driver_set_flags(gfx_ctx_flags_t *flags)
-{
- if (!flags)
- return false;
- if (!current_video_context.set_flags)
- {
- deferred_flag_data.flags = flags->flags;
- deferred_video_context_driver_set_flags = true;
- return false;
- }
-
- current_video_context.set_flags(video_context_data, flags->flags);
- return true;
-}
-
-enum gfx_ctx_api video_context_driver_get_api(void)
-{
- enum gfx_ctx_api ctx_api = video_context_data ?
- current_video_context.get_api(video_context_data) : GFX_CTX_NONE;
-
- if (ctx_api == GFX_CTX_NONE)
- {
- const char *video_driver = video_driver_get_ident();
- if (string_is_equal(video_driver, "d3d9"))
- return GFX_CTX_DIRECT3D9_API;
- else if (string_is_equal(video_driver, "d3d10"))
- return GFX_CTX_DIRECT3D10_API;
- else if (string_is_equal(video_driver, "d3d11"))
- return GFX_CTX_DIRECT3D11_API;
- else if (string_is_equal(video_driver, "d3d12"))
- return GFX_CTX_DIRECT3D12_API;
- else if (string_is_equal(video_driver, "gx2"))
- return GFX_CTX_GX2_API;
- else if (string_is_equal(video_driver, "gx"))
- return GFX_CTX_GX_API;
- else if (string_is_equal(video_driver, "gl"))
- return GFX_CTX_OPENGL_API;
- else if (string_is_equal(video_driver, "vulkan"))
- return GFX_CTX_VULKAN_API;
- else if (string_is_equal(video_driver, "metal"))
- return GFX_CTX_METAL_API;
-
- return GFX_CTX_NONE;
- }
-
- return ctx_api;
-}
-
-bool video_driver_has_windowed(void)
-{
-#if !(defined(RARCH_CONSOLE) || defined(RARCH_MOBILE))
- if (video_driver_data && current_video->has_windowed)
- return current_video->has_windowed(video_driver_data);
- else if (video_context_data && current_video_context.has_windowed)
- return current_video_context.has_windowed(video_context_data);
-#endif
- return false;
-}
-
-bool video_driver_cached_frame_has_valid_framebuffer(void)
-{
- if (frame_cache_data)
- return (frame_cache_data == RETRO_HW_FRAME_BUFFER_VALID);
- return false;
-}
-
-static const shader_backend_t *video_shader_set_backend(
- enum rarch_shader_type type)
-{
- switch (type)
- {
- case RARCH_SHADER_CG:
- {
-#ifdef HAVE_CG
- gfx_ctx_flags_t flags;
- flags.flags = 0;
- video_context_driver_get_flags(&flags);
-
- if (BIT32_GET(flags.flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT))
- {
- RARCH_ERR("[Shader driver]: Cg cannot be used with core"
- " GL context. Trying to fall back to GLSL...\n");
- return video_shader_set_backend(RARCH_SHADER_GLSL);
- }
-
- RARCH_LOG("[Shader driver]: Using Cg shader backend.\n");
- return &gl_cg_backend;
-#else
- break;
-#endif
- }
- case RARCH_SHADER_GLSL:
-#ifdef HAVE_GLSL
- RARCH_LOG("[Shader driver]: Using GLSL shader backend.\n");
- return &gl_glsl_backend;
-#else
- break;
-#endif
- case RARCH_SHADER_HLSL:
- case RARCH_SHADER_NONE:
- default:
- break;
- }
-
- return NULL;
-}
-
-void video_shader_driver_use(video_shader_ctx_info_t *shader_info)
-{
- if (current_shader && current_shader->use)
- current_shader->use(shader_info->data, current_shader_data,
- shader_info->idx, shader_info->set_active);
-}
-
-void video_shader_driver_set_parameter(struct uniform_info *param)
-{
- if (current_shader && current_shader->set_uniform_parameter)
- current_shader->set_uniform_parameter(current_shader_data,
- param, NULL);
-}
-
-void video_shader_driver_set_parameters(video_shader_ctx_params_t *params)
-{
- if (current_shader && current_shader->set_params)
- current_shader->set_params(params, current_shader_data);
-}
-
-bool video_shader_driver_get_prev_textures(
- video_shader_ctx_texture_t *texture)
-{
- if (!texture || !current_shader)
- return false;
- texture->id = current_shader->get_prev_textures(current_shader_data);
-
- return true;
-}
-
-bool video_shader_driver_get_ident(video_shader_ctx_ident_t *ident)
-{
- if (!ident || !current_shader)
- return false;
- ident->ident = current_shader->ident;
- return true;
-}
-
-bool video_shader_driver_get_current_shader(video_shader_ctx_t *shader)
-{
- void *video_driver = video_driver_get_ptr(true);
- const video_poke_interface_t *video_poke = video_driver_get_poke();
-
- shader->data = NULL;
- if (!video_poke || !video_driver || !video_poke->get_current_shader)
- return false;
- shader->data = video_poke->get_current_shader(video_driver);
- return true;
-}
-
-bool video_shader_driver_direct_get_current_shader(
- video_shader_ctx_t *shader)
-{
- if (!current_shader)
- return false;
-
- shader->data = current_shader->get_current_shader(current_shader_data);
-
- return true;
-}
-
-bool video_shader_driver_deinit(void)
-{
- if (!current_shader)
- return false;
-
- if (current_shader->deinit)
- current_shader->deinit(current_shader_data);
-
- current_shader_data = NULL;
- current_shader = NULL;
- return true;
-}
-
-static enum gfx_wrap_type video_shader_driver_wrap_type_null(
- void *data, unsigned index)
-{
- return RARCH_WRAP_BORDER;
-}
-
-static bool video_driver_cb_set_mvp(void *data,
- void *shader_data, const void *mat_data)
-{
- video_shader_ctx_mvp_t mvp;
- mvp.data = data;
- mvp.matrix = mat_data;
-
- video_driver_set_mvp(&mvp);
- return true;
-}
-
-static struct video_shader *
-video_shader_driver_get_current_shader_null(void *data)
-{
- return NULL;
-}
-
-static void video_shader_driver_set_params_null(
- void *data, void *shader_data)
-{
-}
-
-static void video_shader_driver_scale_null(void *data,
- unsigned idx, struct gfx_fbo_scale *scale)
-{
- (void)idx;
- (void)scale;
-}
-
-static bool video_shader_driver_mipmap_input_null(
- void *data, unsigned idx)
-{
- (void)idx;
- return false;
-}
-
-static bool video_shader_driver_filter_type_null(
- void *data, unsigned idx, bool *smooth)
-{
- (void)idx;
- (void)smooth;
- return false;
-}
-
-static unsigned video_shader_driver_num_null(void *data)
-{
- return 0;
-}
-
-static bool video_shader_driver_get_feedback_pass_null(
- void *data, unsigned *idx)
-{
- (void)idx;
- return false;
-}
-
-static void video_shader_driver_reset_to_defaults(void)
-{
- if (!current_shader)
- return;
-
- if (!current_shader->wrap_type)
- current_shader->wrap_type = video_shader_driver_wrap_type_null;
- if (current_shader->set_mvp)
- video_driver_cb_shader_set_mvp = current_shader->set_mvp;
- else
- {
- current_shader->set_mvp = video_driver_cb_set_mvp;
- video_driver_cb_shader_set_mvp = video_driver_cb_set_mvp;
- }
- if (!current_shader->set_coords)
- current_shader->set_coords = video_driver_cb_set_coords;
-
- if (current_shader->use)
- video_driver_cb_shader_use = current_shader->use;
- else
- {
- current_shader->use = video_shader_driver_use_null;
- video_driver_cb_shader_use = video_shader_driver_use_null;
- }
- if (!current_shader->set_params)
- current_shader->set_params = video_shader_driver_set_params_null;
- if (!current_shader->shader_scale)
- current_shader->shader_scale = video_shader_driver_scale_null;
- if (!current_shader->mipmap_input)
- current_shader->mipmap_input = video_shader_driver_mipmap_input_null;
- if (!current_shader->filter_type)
- current_shader->filter_type = video_shader_driver_filter_type_null;
- if (!current_shader->num_shaders)
- current_shader->num_shaders = video_shader_driver_num_null;
- if (!current_shader->get_current_shader)
- current_shader->get_current_shader= video_shader_driver_get_current_shader_null;
- if (!current_shader->get_feedback_pass)
- current_shader->get_feedback_pass = video_shader_driver_get_feedback_pass_null;
-}
-
-/* Finds first suitable shader context driver. */
-bool video_shader_driver_init_first(void)
-{
- current_shader = (shader_backend_t*)shader_ctx_drivers[0];
- video_shader_driver_reset_to_defaults();
- return true;
-}
-
-bool video_shader_driver_init(video_shader_ctx_init_t *init)
-{
- void *tmp = NULL;
- settings_t *settings = config_get_ptr();
-
- if (!init->shader || !init->shader->init)
- {
- init->shader = video_shader_set_backend(init->shader_type);
-
- if (!init->shader)
- return false;
- }
-
- tmp = init->shader->init(init->data, init->path);
-
- if (!tmp)
- return false;
-
- if (string_is_equal(settings->arrays.menu_driver, "xmb")
- && init->shader->init_menu_shaders)
- {
- RARCH_LOG("Setting up menu pipeline shaders for XMB ... \n");
- init->shader->init_menu_shaders(tmp);
- }
-
- current_shader_data = tmp;
-
- RARCH_LOG("Resetting shader to defaults ... \n");
-
- current_shader = (shader_backend_t*)init->shader;
- video_shader_driver_reset_to_defaults();
-
- return true;
-}
-
-bool video_shader_driver_get_feedback_pass(unsigned *data)
-{
- return current_shader->get_feedback_pass(current_shader_data, data);
-}
-
-bool video_shader_driver_mipmap_input(unsigned *index)
-{
- return current_shader->mipmap_input(current_shader_data, *index);
-}
-
-bool video_shader_driver_scale(video_shader_ctx_scale_t *scaler)
-{
- if (!scaler || !scaler->scale)
- return false;
-
- scaler->scale->valid = false;
-
- current_shader->shader_scale(current_shader_data,
- scaler->idx, scaler->scale);
- return true;
-}
-
-bool video_shader_driver_info(video_shader_ctx_info_t *shader_info)
-{
- if (!shader_info)
- return false;
-
- shader_info->num = current_shader->num_shaders(current_shader_data);
-
- return true;
-}
-
-bool video_shader_driver_filter_type(video_shader_ctx_filter_t *filter)
-{
- if (filter)
- return current_shader->filter_type(current_shader_data,
- filter->index, filter->smooth);
- return false;
-}
-
-bool video_shader_driver_compile_program(
- struct shader_program_info *program_info)
-{
- if (program_info)
- return current_shader->compile_program(program_info->data,
- program_info->idx, NULL, program_info);
- return false;
-}
-
-bool video_shader_driver_wrap_type(video_shader_ctx_wrap_t *wrap)
-{
- wrap->type = current_shader->wrap_type(
- current_shader_data, wrap->idx);
- return true;
-}
-
-void video_driver_set_coords(video_shader_ctx_coords_t *coords)
-{
- if (current_shader && current_shader->set_coords)
- current_shader->set_coords(coords->handle_data,
- current_shader_data,
- (const struct video_coords*)coords->data);
- else
- {
- if (video_driver_poke && video_driver_poke->set_coords)
- video_driver_poke->set_coords(coords->handle_data,
- current_shader_data,
- (const struct video_coords*)coords->data);
- }
-}
-
-void video_driver_set_mvp(video_shader_ctx_mvp_t *mvp)
-{
- if (!mvp || !mvp->matrix)
- return;
-
- if (current_shader && current_shader->set_mvp)
- current_shader->set_mvp(mvp->data,
- current_shader_data, mvp->matrix);
- else
- {
- if (video_driver_poke && video_driver_poke->set_mvp)
- video_driver_poke->set_mvp(mvp->data,
- current_shader_data, mvp->matrix);
- }
-}
-
-float video_driver_get_refresh_rate(void)
-{
- if (video_driver_poke && video_driver_poke->get_refresh_rate)
- return video_driver_poke->get_refresh_rate(video_driver_data);
-
- return 0.0f;
-}
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2017 - Daniel De Matteis
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include "../audio/audio_driver.h"
+#include "../menu/menu_shader.h"
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#include "../dynamic.h"
+
+#ifdef HAVE_THREADS
+#include
+#endif
+
+#ifdef HAVE_MENU
+#include "../menu/menu_driver.h"
+#include "../menu/menu_setting.h"
+#endif
+
+#include "video_thread_wrapper.h"
+#include "video_driver.h"
+#include "video_display_server.h"
+#include "video_crt_switch.h"
+
+#include "../frontend/frontend_driver.h"
+#include "../record/record_driver.h"
+#include "../config.def.h"
+#include "../configuration.h"
+#include "../driver.h"
+#include "../retroarch.h"
+#include "../input/input_driver.h"
+#include "../list_special.h"
+#include "../core.h"
+#include "../command.h"
+#include "../msg_hash.h"
+#include "../verbosity.h"
+
+#define MEASURE_FRAME_TIME_SAMPLES_COUNT (2 * 1024)
+
+#define TIME_TO_FPS(last_time, new_time, frames) ((1000000.0f * (frames)) / ((new_time) - (last_time)))
+
+#define FPS_UPDATE_INTERVAL 256
+
+#ifdef HAVE_THREADS
+#define video_driver_is_threaded_internal() ((!video_driver_is_hw_context() && video_driver_threaded) ? true : false)
+#else
+#define video_driver_is_threaded_internal() (false)
+#endif
+
+#ifdef HAVE_THREADS
+#define video_driver_lock() \
+ if (display_lock) \
+ slock_lock(display_lock)
+
+#define video_driver_unlock() \
+ if (display_lock) \
+ slock_unlock(display_lock)
+
+#define video_driver_context_lock() \
+ if (context_lock) \
+ slock_lock(context_lock)
+
+#define video_driver_context_unlock() \
+ if (context_lock) \
+ slock_unlock(context_lock)
+
+#define video_driver_lock_free() \
+ slock_free(display_lock); \
+ slock_free(context_lock); \
+ display_lock = NULL; \
+ context_lock = NULL
+
+#define video_driver_threaded_lock(is_threaded) \
+ if (is_threaded) \
+ video_driver_lock()
+
+#define video_driver_threaded_unlock(is_threaded) \
+ if (is_threaded) \
+ video_driver_unlock()
+#else
+#define video_driver_lock() ((void)0)
+#define video_driver_unlock() ((void)0)
+#define video_driver_lock_free() ((void)0)
+#define video_driver_threaded_lock(is_threaded) ((void)0)
+#define video_driver_threaded_unlock(is_threaded) ((void)0)
+#define video_driver_context_lock() ((void)0)
+#define video_driver_context_unlock() ((void)0)
+#endif
+
+typedef struct video_pixel_scaler
+{
+ struct scaler_ctx *scaler;
+ void *scaler_out;
+} video_pixel_scaler_t;
+
+static void (*video_driver_cb_shader_use)(void *data,
+ void *shader_data, unsigned index, bool set_active);
+static bool (*video_driver_cb_shader_set_mvp)(void *data,
+ void *shader_data, const void *mat_data);
+bool (*video_driver_cb_has_focus)(void);
+
+/* Opaque handles to currently running window.
+ * Used by e.g. input drivers which bind to a window.
+ * Drivers are responsible for setting these if an input driver
+ * could potentially make use of this. */
+static uintptr_t video_driver_display = 0;
+static uintptr_t video_driver_window = 0;
+
+static rarch_softfilter_t *video_driver_state_filter = NULL;
+static void *video_driver_state_buffer = NULL;
+static unsigned video_driver_state_scale = 0;
+static unsigned video_driver_state_out_bpp = 0;
+static bool video_driver_state_out_rgb32 = false;
+static bool video_driver_crt_switching_active = false;
+
+static struct retro_system_av_info video_driver_av_info;
+
+static enum retro_pixel_format video_driver_pix_fmt = RETRO_PIXEL_FORMAT_0RGB1555;
+
+static const void *frame_cache_data = NULL;
+static unsigned frame_cache_width = 0;
+static unsigned frame_cache_height = 0;
+static size_t frame_cache_pitch = 0;
+static bool video_driver_threaded = false;
+
+static float video_driver_core_hz = 0.0f;
+static float video_driver_aspect_ratio = 0.0f;
+static unsigned video_driver_width = 0;
+static unsigned video_driver_height = 0;
+
+static enum rarch_display_type video_driver_display_type = RARCH_DISPLAY_NONE;
+static char video_driver_title_buf[64] = {0};
+static char video_driver_window_title[512] = {0};
+static bool video_driver_window_title_update = true;
+
+static retro_time_t video_driver_frame_time_samples[MEASURE_FRAME_TIME_SAMPLES_COUNT];
+static uint64_t video_driver_frame_time_count = 0;
+static uint64_t video_driver_frame_count = 0;
+
+static void *video_driver_data = NULL;
+static video_driver_t *current_video = NULL;
+
+/* Interface for "poking". */
+static const video_poke_interface_t *video_driver_poke = NULL;
+
+/* Used for 15-bit -> 16-bit conversions that take place before
+ * being passed to video driver. */
+static video_pixel_scaler_t *video_driver_scaler_ptr = NULL;
+
+static struct retro_hw_render_callback hw_render;
+
+static const struct
+retro_hw_render_context_negotiation_interface *
+hw_render_context_negotiation = NULL;
+
+/* Graphics driver requires RGBA byte order data (ABGR on little-endian)
+ * for 32-bit.
+ * This takes effect for overlay and shader cores that wants to load
+ * data into graphics driver. Kinda hackish to place it here, it is only
+ * used for GLES.
+ * TODO: Refactor this better. */
+static bool video_driver_use_rgba = false;
+static bool video_driver_data_own = false;
+static bool video_driver_active = false;
+
+static video_driver_frame_t frame_bak = NULL;
+
+/* If set during context deinit, the driver should keep
+ * graphics context alive to avoid having to reset all
+ * context state. */
+static bool video_driver_cache_context = false;
+
+/* Set to true by driver if context caching succeeded. */
+static bool video_driver_cache_context_ack = false;
+static uint8_t *video_driver_record_gpu_buffer = NULL;
+
+#ifdef HAVE_THREADS
+static slock_t *display_lock = NULL;
+static slock_t *context_lock = NULL;
+#endif
+
+static gfx_ctx_driver_t current_video_context;
+
+static void *video_context_data = NULL;
+
+/**
+ * dynamic.c:dynamic_request_hw_context will try to set flag data when the context
+ * is in the middle of being rebuilt; in these cases we will save flag
+ * data and set this to true.
+ * When the context is reinit, it checks this, reads from
+ * deferred_flag_data and cleans it.
+ *
+ * TODO - Dirty hack, fix it better
+ */
+static bool deferred_video_context_driver_set_flags = false;
+static gfx_ctx_flags_t deferred_flag_data = {0};
+
+static bool video_started_fullscreen = false;
+
+static shader_backend_t *current_shader = NULL;
+static void *current_shader_data = NULL;
+
+struct aspect_ratio_elem aspectratio_lut[ASPECT_RATIO_END] = {
+ { "4:3", 1.3333f },
+ { "16:9", 1.7778f },
+ { "16:10", 1.6f },
+ { "16:15", 16.0f / 15.0f },
+ { "21:9", 21.0f / 9.0f },
+ { "1:1", 1.0f },
+ { "2:1", 2.0f },
+ { "3:2", 1.5f },
+ { "3:4", 0.75f },
+ { "4:1", 4.0f },
+ { "9:16", 0.5625f },
+ { "5:4", 1.25f },
+ { "6:5", 1.2f },
+ { "7:9", 0.7777f },
+ { "8:3", 2.6666f },
+ { "8:7", 1.1428f },
+ { "19:12", 1.5833f },
+ { "19:14", 1.3571f },
+ { "30:17", 1.7647f },
+ { "32:9", 3.5555f },
+ { "Config", 0.0f },
+ { "Square pixel", 1.0f },
+ { "Core provided", 1.0f },
+ { "Custom", 0.0f }
+};
+
+static const video_driver_t *video_drivers[] = {
+#ifdef HAVE_OPENGL
+ &video_gl,
+#endif
+#ifdef HAVE_VULKAN
+ &video_vulkan,
+#endif
+#ifdef HAVE_METAL
+ &video_metal,
+#endif
+#ifdef XENON
+ &video_xenon360,
+#endif
+#if defined(HAVE_D3D12)
+ &video_d3d12,
+#endif
+#if defined(HAVE_D3D11)
+ &video_d3d11,
+#endif
+#if defined(HAVE_D3D10)
+ &video_d3d10,
+#endif
+#if defined(HAVE_D3D9)
+ &video_d3d9,
+#endif
+#if defined(HAVE_D3D8)
+ &video_d3d8,
+#endif
+#ifdef HAVE_VITA2D
+ &video_vita2d,
+#endif
+#ifdef PSP
+ &video_psp1,
+#endif
+#ifdef PS2
+ &video_ps2,
+#endif
+#ifdef _3DS
+ &video_ctr,
+#endif
+#ifdef SWITCH
+ &video_switch,
+#endif
+#ifdef HAVE_SDL
+ &video_sdl,
+#endif
+#ifdef HAVE_SDL2
+ &video_sdl2,
+#endif
+#ifdef HAVE_XVIDEO
+ &video_xvideo,
+#endif
+#ifdef GEKKO
+ &video_gx,
+#endif
+#ifdef WIIU
+ &video_wiiu,
+#endif
+#ifdef HAVE_VG
+ &video_vg,
+#endif
+#ifdef HAVE_OMAP
+ &video_omap,
+#endif
+#ifdef HAVE_EXYNOS
+ &video_exynos,
+#endif
+#ifdef HAVE_DISPMANX
+ &video_dispmanx,
+#endif
+#ifdef HAVE_SUNXI
+ &video_sunxi,
+#endif
+#ifdef HAVE_PLAIN_DRM
+ &video_drm,
+#endif
+#ifdef HAVE_XSHM
+ &video_xshm,
+#endif
+#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
+ &video_gdi,
+#endif
+#ifdef DJGPP
+ &video_vga,
+#endif
+#ifdef HAVE_SIXEL
+ &video_sixel,
+#endif
+#ifdef HAVE_CACA
+ &video_caca,
+#endif
+ &video_null,
+ NULL,
+};
+
+static const gfx_ctx_driver_t *gfx_ctx_drivers[] = {
+#if defined(ORBIS)
+ &orbis_ctx,
+#endif
+#if defined(HAVE_LIBNX) && defined(HAVE_OPENGL)
+ &switch_ctx,
+#endif
+#if defined(__CELLOS_LV2__)
+ &gfx_ctx_ps3,
+#endif
+#if defined(HAVE_VIDEOCORE)
+ &gfx_ctx_videocore,
+#endif
+#if defined(HAVE_MALI_FBDEV)
+ &gfx_ctx_mali_fbdev,
+#endif
+#if defined(HAVE_VIVANTE_FBDEV)
+ &gfx_ctx_vivante_fbdev,
+#endif
+#if defined(HAVE_OPENDINGUX_FBDEV)
+ &gfx_ctx_opendingux_fbdev,
+#endif
+#if defined(_WIN32) && (defined(HAVE_OPENGL) || defined(HAVE_VULKAN))
+ &gfx_ctx_wgl,
+#endif
+#if defined(HAVE_WAYLAND)
+ &gfx_ctx_wayland,
+#endif
+#if defined(HAVE_X11) && !defined(HAVE_OPENGLES)
+#if defined(HAVE_OPENGL) || defined(HAVE_VULKAN)
+ &gfx_ctx_x,
+#endif
+#endif
+#if defined(HAVE_X11) && defined(HAVE_OPENGL) && defined(HAVE_EGL)
+ &gfx_ctx_x_egl,
+#endif
+#if defined(HAVE_KMS)
+ &gfx_ctx_drm,
+#endif
+#if defined(ANDROID)
+ &gfx_ctx_android,
+#endif
+#if defined(__QNX__)
+ &gfx_ctx_qnx,
+#endif
+#if defined(HAVE_COCOA) || defined(HAVE_COCOATOUCH) || defined(HAVE_COCOA_METAL)
+ &gfx_ctx_cocoagl,
+#endif
+#if defined(__APPLE__) && !defined(TARGET_IPHONE_SIMULATOR) && !defined(TARGET_OS_IPHONE)
+ &gfx_ctx_cgl,
+#endif
+#if (defined(HAVE_SDL) || defined(HAVE_SDL2)) && defined(HAVE_OPENGL)
+ &gfx_ctx_sdl_gl,
+#endif
+#ifdef HAVE_OSMESA
+ &gfx_ctx_osmesa,
+#endif
+#ifdef EMSCRIPTEN
+ &gfx_ctx_emscripten,
+#endif
+#if defined(HAVE_VULKAN) && defined(HAVE_VULKAN_DISPLAY)
+ &gfx_ctx_khr_display,
+#endif
+#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
+ &gfx_ctx_gdi,
+#endif
+#ifdef HAVE_SIXEL
+ &gfx_ctx_sixel,
+#endif
+ &gfx_ctx_null,
+ NULL
+};
+
+static const shader_backend_t *shader_ctx_drivers[] = {
+#ifdef HAVE_GLSL
+ &gl_glsl_backend,
+#endif
+#ifdef HAVE_CG
+ &gl_cg_backend,
+#endif
+ &shader_null_backend,
+ NULL
+};
+
+bool video_driver_started_fullscreen(void)
+{
+ return video_started_fullscreen;
+}
+
+/* Stub functions */
+
+static void update_window_title_null(void *data, void *data2)
+{
+}
+
+static void swap_buffers_null(void *data, void *data2)
+{
+}
+
+static bool get_metrics_null(void *data, enum display_metric_types type,
+ float *value)
+{
+ return false;
+}
+
+static bool set_resize_null(void *a, unsigned b, unsigned c)
+{
+ return false;
+}
+
+/**
+ * video_driver_find_handle:
+ * @idx : index of driver to get handle to.
+ *
+ * Returns: handle to video driver at index. Can be NULL
+ * if nothing found.
+ **/
+const void *video_driver_find_handle(int idx)
+{
+ const void *drv = video_drivers[idx];
+ if (!drv)
+ return NULL;
+ return drv;
+}
+
+/**
+ * video_driver_find_ident:
+ * @idx : index of driver to get handle to.
+ *
+ * Returns: Human-readable identifier of video driver at index. Can be NULL
+ * if nothing found.
+ **/
+const char *video_driver_find_ident(int idx)
+{
+ const video_driver_t *drv = video_drivers[idx];
+ if (!drv)
+ return NULL;
+ return drv->ident;
+}
+
+/**
+ * config_get_video_driver_options:
+ *
+ * Get an enumerated list of all video driver names, separated by '|'.
+ *
+ * Returns: string listing of all video driver names, separated by '|'.
+ **/
+const char* config_get_video_driver_options(void)
+{
+ return char_list_new_special(STRING_LIST_VIDEO_DRIVERS, NULL);
+}
+
+bool video_driver_is_threaded(void)
+{
+ return video_driver_is_threaded_internal();
+}
+
+#ifdef HAVE_VULKAN
+static bool hw_render_context_is_vulkan(enum retro_hw_context_type type)
+{
+ return type == RETRO_HW_CONTEXT_VULKAN;
+}
+#endif
+
+#if defined(HAVE_OPENGL)
+static bool hw_render_context_is_gl(enum retro_hw_context_type type)
+{
+ switch (type)
+ {
+ case RETRO_HW_CONTEXT_OPENGL:
+ case RETRO_HW_CONTEXT_OPENGLES2:
+ case RETRO_HW_CONTEXT_OPENGL_CORE:
+ case RETRO_HW_CONTEXT_OPENGLES3:
+ case RETRO_HW_CONTEXT_OPENGLES_VERSION:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+#endif
+
+bool *video_driver_get_threaded(void)
+{
+ return &video_driver_threaded;
+}
+
+void video_driver_set_threaded(bool val)
+{
+ video_driver_threaded = val;
+}
+
+/**
+ * video_driver_get_ptr:
+ *
+ * Use this if you need the real video driver
+ * and driver data pointers.
+ *
+ * Returns: video driver's userdata.
+ **/
+void *video_driver_get_ptr(bool force_nonthreaded_data)
+{
+#ifdef HAVE_THREADS
+ if (video_driver_is_threaded_internal() && !force_nonthreaded_data)
+ return video_thread_get_ptr(NULL);
+#endif
+
+ return video_driver_data;
+}
+
+const char *video_driver_get_ident(void)
+{
+ return (current_video) ? current_video->ident : NULL;
+}
+
+const video_poke_interface_t *video_driver_get_poke(void)
+{
+ return video_driver_poke;
+}
+
+static bool video_context_has_focus(void)
+{
+ return current_video_context.has_focus && current_video_context.has_focus(video_context_data);
+}
+
+static bool video_driver_has_focus(void)
+{
+ return current_video && current_video->focus && current_video->focus(video_driver_data);
+}
+
+static bool null_driver_has_focus(void)
+{
+ return true;
+}
+
+static void video_context_driver_reset(void)
+{
+ if (!current_video_context.get_metrics)
+ current_video_context.get_metrics = get_metrics_null;
+
+ if (!current_video_context.update_window_title)
+ current_video_context.update_window_title = update_window_title_null;
+
+ if (!current_video_context.set_resize)
+ current_video_context.set_resize = set_resize_null;
+
+ if (!current_video_context.swap_buffers)
+ current_video_context.swap_buffers = swap_buffers_null;
+
+ if (current_video_context.has_focus)
+ video_driver_cb_has_focus = video_context_has_focus;
+
+}
+
+bool video_context_driver_set(const gfx_ctx_driver_t *data)
+{
+ if (!data)
+ return false;
+ current_video_context = *data;
+ video_context_driver_reset();
+ return true;
+}
+
+void video_context_driver_destroy(void)
+{
+ current_video_context.init = NULL;
+ current_video_context.bind_api = NULL;
+ current_video_context.swap_interval = NULL;
+ current_video_context.set_video_mode = NULL;
+ current_video_context.get_video_size = NULL;
+ current_video_context.get_video_output_size = NULL;
+ current_video_context.get_video_output_prev = NULL;
+ current_video_context.get_video_output_next = NULL;
+ current_video_context.get_metrics = get_metrics_null;
+ current_video_context.translate_aspect = NULL;
+ current_video_context.update_window_title = update_window_title_null;
+ current_video_context.check_window = NULL;
+ current_video_context.set_resize = set_resize_null;
+ current_video_context.has_focus = NULL;
+ current_video_context.suppress_screensaver = NULL;
+ current_video_context.has_windowed = NULL;
+ current_video_context.swap_buffers = swap_buffers_null;
+ current_video_context.input_driver = NULL;
+ current_video_context.get_proc_address = NULL;
+ current_video_context.image_buffer_init = NULL;
+ current_video_context.image_buffer_write = NULL;
+ current_video_context.show_mouse = NULL;
+ current_video_context.ident = NULL;
+ current_video_context.get_flags = NULL;
+ current_video_context.set_flags = NULL;
+ current_video_context.bind_hw_render = NULL;
+ current_video_context.get_context_data = NULL;
+ current_video_context.make_current = NULL;
+}
+
+/**
+ * video_driver_get_current_framebuffer:
+ *
+ * Gets pointer to current hardware renderer framebuffer object.
+ * Used by RETRO_ENVIRONMENT_SET_HW_RENDER.
+ *
+ * Returns: pointer to hardware framebuffer object, otherwise 0.
+ **/
+uintptr_t video_driver_get_current_framebuffer(void)
+{
+ if (video_driver_poke && video_driver_poke->get_current_framebuffer)
+ return video_driver_poke->get_current_framebuffer(video_driver_data);
+ return 0;
+}
+
+retro_proc_address_t video_driver_get_proc_address(const char *sym)
+{
+ if (video_driver_poke && video_driver_poke->get_proc_address)
+ return video_driver_poke->get_proc_address(video_driver_data, sym);
+ return NULL;
+}
+
+bool video_driver_set_shader(enum rarch_shader_type type,
+ const char *path)
+{
+ if (current_video->set_shader)
+ return current_video->set_shader(video_driver_data, type, path);
+ return false;
+}
+
+static void video_driver_filter_free(void)
+{
+ if (video_driver_state_filter)
+ rarch_softfilter_free(video_driver_state_filter);
+ video_driver_state_filter = NULL;
+
+ if (video_driver_state_buffer)
+ {
+#ifdef _3DS
+ linearFree(video_driver_state_buffer);
+#else
+ free(video_driver_state_buffer);
+#endif
+ }
+ video_driver_state_buffer = NULL;
+
+ video_driver_state_scale = 0;
+ video_driver_state_out_bpp = 0;
+ video_driver_state_out_rgb32 = false;
+}
+
+static void video_driver_init_filter(enum retro_pixel_format colfmt_int)
+{
+ unsigned pow2_x, pow2_y, maxsize;
+ void *buf = NULL;
+ settings_t *settings = config_get_ptr();
+ struct retro_game_geometry *geom = &video_driver_av_info.geometry;
+ unsigned width = geom->max_width;
+ unsigned height = geom->max_height;
+ /* Deprecated format. Gets pre-converted. */
+ enum retro_pixel_format colfmt =
+ (colfmt_int == RETRO_PIXEL_FORMAT_0RGB1555) ?
+ RETRO_PIXEL_FORMAT_RGB565 : colfmt_int;
+
+ if (video_driver_is_hw_context())
+ {
+ RARCH_WARN("Cannot use CPU filters when hardware rendering is used.\n");
+ return;
+ }
+
+ video_driver_state_filter = rarch_softfilter_new(
+ settings->paths.path_softfilter_plugin,
+ RARCH_SOFTFILTER_THREADS_AUTO, colfmt, width, height);
+
+ if (!video_driver_state_filter)
+ {
+ RARCH_ERR("[Video]: Failed to load filter.\n");
+ return;
+ }
+
+ rarch_softfilter_get_max_output_size(video_driver_state_filter,
+ &width, &height);
+
+ pow2_x = next_pow2(width);
+ pow2_y = next_pow2(height);
+ maxsize = MAX(pow2_x, pow2_y);
+ video_driver_state_scale = maxsize / RARCH_SCALE_BASE;
+ video_driver_state_out_rgb32 = rarch_softfilter_get_output_format(
+ video_driver_state_filter) ==
+ RETRO_PIXEL_FORMAT_XRGB8888;
+
+ video_driver_state_out_bpp = video_driver_state_out_rgb32 ?
+ sizeof(uint32_t) :
+ sizeof(uint16_t);
+
+ /* TODO: Aligned output. */
+#ifdef _3DS
+ buf = linearMemAlign(
+ width * height * video_driver_state_out_bpp, 0x80);
+#else
+ buf = malloc(
+ width * height * video_driver_state_out_bpp);
+#endif
+ if (!buf)
+ {
+ RARCH_ERR("[Video]: Softfilter initialization failed.\n");
+ video_driver_filter_free();
+ return;
+ }
+
+ video_driver_state_buffer = buf;
+}
+
+static void video_driver_init_input(const input_driver_t *tmp)
+{
+ const input_driver_t **input = input_get_double_ptr();
+ if (*input)
+ return;
+
+ /* Video driver didn't provide an input driver,
+ * so we use configured one. */
+ RARCH_LOG("[Video]: Graphics driver did not initialize an input driver."
+ " Attempting to pick a suitable driver.\n");
+
+ if (tmp)
+ *input = tmp;
+ else
+ input_driver_find_driver();
+
+ /* This should never really happen as tmp (driver.input) is always
+ * found before this in find_driver_input(), or we have aborted
+ * in a similar fashion anyways. */
+ if (!input_get_ptr())
+ goto error;
+
+ if (input_driver_init())
+ return;
+
+error:
+ RARCH_ERR("[Video]: Cannot initialize input driver. Exiting ...\n");
+ retroarch_fail(1, "video_driver_init_input()");
+}
+
+/**
+ * video_driver_monitor_compute_fps_statistics:
+ *
+ * Computes monitor FPS statistics.
+ **/
+static void video_driver_monitor_compute_fps_statistics(void)
+{
+ double avg_fps = 0.0;
+ double stddev = 0.0;
+ unsigned samples = 0;
+
+ if (video_driver_frame_time_count <
+ (2 * MEASURE_FRAME_TIME_SAMPLES_COUNT))
+ {
+ RARCH_LOG(
+ "[Video]: Does not have enough samples for monitor refresh rate"
+ " estimation. Requires to run for at least %u frames.\n",
+ 2 * MEASURE_FRAME_TIME_SAMPLES_COUNT);
+ return;
+ }
+
+ if (video_monitor_fps_statistics(&avg_fps, &stddev, &samples))
+ {
+ RARCH_LOG("[Video]: Average monitor Hz: %.6f Hz. (%.3f %% frame time"
+ " deviation, based on %u last samples).\n",
+ avg_fps, 100.0 * stddev, samples);
+ }
+}
+
+static void video_driver_pixel_converter_free(void)
+{
+ if (!video_driver_scaler_ptr)
+ return;
+
+ scaler_ctx_gen_reset(video_driver_scaler_ptr->scaler);
+
+ if (video_driver_scaler_ptr->scaler)
+ free(video_driver_scaler_ptr->scaler);
+ video_driver_scaler_ptr->scaler = NULL;
+
+ if (video_driver_scaler_ptr->scaler_out)
+ free(video_driver_scaler_ptr->scaler_out);
+ video_driver_scaler_ptr->scaler_out = NULL;
+
+ if (video_driver_scaler_ptr)
+ free(video_driver_scaler_ptr);
+ video_driver_scaler_ptr = NULL;
+}
+
+static void video_driver_free_internal(void)
+{
+#ifdef HAVE_THREADS
+ bool is_threaded = video_driver_is_threaded_internal();
+#endif
+
+ command_event(CMD_EVENT_OVERLAY_DEINIT, NULL);
+
+ if (!video_driver_is_video_cache_context())
+ video_driver_free_hw_context();
+
+ if (
+ !input_driver_owns_driver() &&
+ !input_driver_is_data_ptr_same(video_driver_data)
+ )
+ input_driver_deinit();
+
+ if (
+ !video_driver_data_own
+ && video_driver_data
+ && current_video && current_video->free
+ )
+ current_video->free(video_driver_data);
+
+ video_driver_pixel_converter_free();
+ video_driver_filter_free();
+
+ command_event(CMD_EVENT_SHADER_DIR_DEINIT, NULL);
+
+#ifdef HAVE_THREADS
+ if (is_threaded)
+ return;
+#endif
+
+ video_driver_monitor_compute_fps_statistics();
+}
+
+static bool video_driver_pixel_converter_init(unsigned size)
+{
+ struct retro_hw_render_callback *hwr =
+ video_driver_get_hw_context();
+ void *scalr_out = NULL;
+ video_pixel_scaler_t *scalr = NULL;
+ struct scaler_ctx *scalr_ctx = NULL;
+
+ /* If pixel format is not 0RGB1555, we don't need to do
+ * any internal pixel conversion. */
+ if (video_driver_pix_fmt != RETRO_PIXEL_FORMAT_0RGB1555)
+ return true;
+
+ /* No need to perform pixel conversion for HW rendering contexts. */
+ if (hwr && hwr->context_type != RETRO_HW_CONTEXT_NONE)
+ return true;
+
+ RARCH_WARN("0RGB1555 pixel format is deprecated,"
+ " and will be slower. For 15/16-bit, RGB565"
+ " format is preferred.\n");
+
+ scalr = (video_pixel_scaler_t*)calloc(1, sizeof(*scalr));
+
+ if (!scalr)
+ goto error;
+
+ video_driver_scaler_ptr = scalr;
+
+ scalr_ctx = (struct scaler_ctx*)calloc(1, sizeof(*scalr_ctx));
+
+ if (!scalr_ctx)
+ goto error;
+
+ video_driver_scaler_ptr->scaler = scalr_ctx;
+ video_driver_scaler_ptr->scaler->scaler_type = SCALER_TYPE_POINT;
+ video_driver_scaler_ptr->scaler->in_fmt = SCALER_FMT_0RGB1555;
+
+ /* TODO: Pick either ARGB8888 or RGB565 depending on driver. */
+ video_driver_scaler_ptr->scaler->out_fmt = SCALER_FMT_RGB565;
+
+ if (!scaler_ctx_gen_filter(scalr_ctx))
+ goto error;
+
+ scalr_out = calloc(sizeof(uint16_t), size * size);
+
+ if (!scalr_out)
+ goto error;
+
+ video_driver_scaler_ptr->scaler_out = scalr_out;
+
+ return true;
+
+error:
+ video_driver_pixel_converter_free();
+ video_driver_filter_free();
+
+ return false;
+}
+
+static bool video_driver_init_internal(bool *video_is_threaded)
+{
+ video_info_t video;
+ unsigned max_dim, scale, width, height;
+ video_viewport_t *custom_vp = NULL;
+ const input_driver_t *tmp = NULL;
+ rarch_system_info_t *system = NULL;
+ static uint16_t dummy_pixels[32] = {0};
+ settings_t *settings = config_get_ptr();
+ struct retro_game_geometry *geom = &video_driver_av_info.geometry;
+
+ if (!string_is_empty(settings->paths.path_softfilter_plugin))
+ video_driver_init_filter(video_driver_pix_fmt);
+
+ max_dim = MAX(geom->max_width, geom->max_height);
+ scale = next_pow2(max_dim) / RARCH_SCALE_BASE;
+ scale = MAX(scale, 1);
+
+ if (video_driver_state_filter)
+ scale = video_driver_state_scale;
+
+ /* Update core-dependent aspect ratio values. */
+ video_driver_set_viewport_square_pixel();
+ video_driver_set_viewport_core();
+ video_driver_set_viewport_config();
+
+ /* Update CUSTOM viewport. */
+ custom_vp = video_viewport_get_custom();
+
+ if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
+ {
+ float default_aspect = aspectratio_lut[ASPECT_RATIO_CORE].value;
+ aspectratio_lut[ASPECT_RATIO_CUSTOM].value =
+ (custom_vp->width && custom_vp->height) ?
+ (float)custom_vp->width / custom_vp->height : default_aspect;
+ }
+
+ video_driver_set_aspect_ratio_value(
+ aspectratio_lut[settings->uints.video_aspect_ratio_idx].value);
+
+ if (settings->bools.video_fullscreen|| retroarch_is_forced_fullscreen())
+ {
+ width = settings->uints.video_fullscreen_x;
+ height = settings->uints.video_fullscreen_y;
+ }
+ else
+ {
+ /* To-Do: remove when the new window resizing core is hooked */
+ if (settings->bools.video_window_save_positions &&
+ (settings->uints.window_position_width || settings->uints.window_position_height))
+ {
+ width = settings->uints.window_position_width;
+ height = settings->uints.window_position_height;
+ }
+ else
+ {
+ if (settings->bools.video_force_aspect)
+ {
+ /* Do rounding here to simplify integer scale correctness. */
+ unsigned base_width =
+ roundf(geom->base_height * video_driver_get_aspect_ratio());
+ width = roundf(base_width * settings->floats.video_scale);
+ }
+ else
+ width = roundf(geom->base_width * settings->floats.video_scale);
+ height = roundf(geom->base_height * settings->floats.video_scale);
+}
+ }
+
+ if (width && height)
+ RARCH_LOG("[Video]: Video @ %ux%u\n", width, height);
+ else
+ RARCH_LOG("[Video]: Video @ fullscreen\n");
+
+ video_driver_display_type_set(RARCH_DISPLAY_NONE);
+ video_driver_display_set(0);
+ video_driver_window_set(0);
+
+ if (!video_driver_pixel_converter_init(RARCH_SCALE_BASE * scale))
+ {
+ RARCH_ERR("[Video]: Failed to initialize pixel converter.\n");
+ goto error;
+ }
+
+ video.width = width;
+ video.height = height;
+ video.fullscreen = settings->bools.video_fullscreen || retroarch_is_forced_fullscreen();
+ video.vsync = settings->bools.video_vsync && !rarch_ctl(RARCH_CTL_IS_NONBLOCK_FORCED, NULL);
+ video.force_aspect = settings->bools.video_force_aspect;
+ video.font_enable = settings->bools.video_font_enable;
+ video.swap_interval = settings->uints.video_swap_interval;
+#ifdef GEKKO
+ video.viwidth = settings->uints.video_viwidth;
+ video.vfilter = settings->bools.video_vfilter;
+#endif
+ video.smooth = settings->bools.video_smooth;
+ video.input_scale = scale;
+ video.rgb32 = video_driver_state_filter ?
+ video_driver_state_out_rgb32 :
+ (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_XRGB8888);
+ video.parent = 0;
+
+ video_started_fullscreen = video.fullscreen;
+
+ /* Reset video frame count */
+ video_driver_frame_count = 0;
+
+ tmp = input_get_ptr();
+ /* Need to grab the "real" video driver interface on a reinit. */
+ video_driver_find_driver();
+
+#ifdef HAVE_THREADS
+ video.is_threaded = video_driver_is_threaded_internal();
+ *video_is_threaded = video.is_threaded;
+
+ if (video.is_threaded)
+ {
+ /* Can't do hardware rendering with threaded driver currently. */
+ RARCH_LOG("[Video]: Starting threaded video driver ...\n");
+
+ if (!video_init_thread((const video_driver_t**)¤t_video,
+ &video_driver_data,
+ input_get_double_ptr(), input_driver_get_data_ptr(),
+ current_video, video))
+ {
+ RARCH_ERR("[Video]: Cannot open threaded video driver ... Exiting ...\n");
+ goto error;
+ }
+ }
+ else
+#endif
+ video_driver_data = current_video->init(
+ &video, input_get_double_ptr(),
+ input_driver_get_data_ptr());
+
+ if (!video_driver_data)
+ {
+ RARCH_ERR("[Video]: Cannot open video driver ... Exiting ...\n");
+ goto error;
+ }
+
+ if (current_video->focus)
+ video_driver_cb_has_focus = video_driver_has_focus;
+
+ video_driver_poke = NULL;
+ if (current_video->poke_interface)
+ current_video->poke_interface(video_driver_data, &video_driver_poke);
+
+ if (current_video->viewport_info &&
+ (!custom_vp->width ||
+ !custom_vp->height))
+ {
+ /* Force custom viewport to have sane parameters. */
+ custom_vp->width = width;
+ custom_vp->height = height;
+
+ video_driver_get_viewport_info(custom_vp);
+ }
+
+ system = runloop_get_system_info();
+
+ video_driver_set_rotation(
+ (settings->uints.video_rotation + system->rotation) % 4);
+
+ current_video->suppress_screensaver(video_driver_data,
+ settings->bools.ui_suspend_screensaver_enable);
+
+ video_driver_init_input(tmp);
+
+ command_event(CMD_EVENT_OVERLAY_DEINIT, NULL);
+ command_event(CMD_EVENT_OVERLAY_INIT, NULL);
+
+ if (!core_is_game_loaded())
+ video_driver_cached_frame_set(&dummy_pixels, 4, 4, 8);
+
+#if defined(PSP)
+ video_driver_set_texture_frame(&dummy_pixels, false, 1, 1, 1.0f);
+#endif
+
+ video_context_driver_reset();
+
+ video_display_server_init();
+
+ command_event(CMD_EVENT_SHADER_DIR_INIT, NULL);
+
+ return true;
+
+error:
+ retroarch_fail(1, "init_video()");
+ return false;
+}
+
+bool video_driver_set_viewport(unsigned width, unsigned height,
+ bool force_fullscreen, bool allow_rotate)
+{
+ if (!current_video || !current_video->set_viewport)
+ return false;
+ current_video->set_viewport(video_driver_data, width, height,
+ force_fullscreen, allow_rotate);
+ return true;
+}
+
+bool video_driver_set_rotation(unsigned rotation)
+{
+ if (!current_video || !current_video->set_rotation)
+ return false;
+ current_video->set_rotation(video_driver_data, rotation);
+ return true;
+}
+
+bool video_driver_set_video_mode(unsigned width,
+ unsigned height, bool fullscreen)
+{
+ gfx_ctx_mode_t mode;
+
+ if (video_driver_poke && video_driver_poke->set_video_mode)
+ {
+ video_driver_poke->set_video_mode(video_driver_data,
+ width, height, fullscreen);
+ return true;
+ }
+
+ mode.width = width;
+ mode.height = height;
+ mode.fullscreen = fullscreen;
+
+ return video_context_driver_set_video_mode(&mode);
+}
+
+bool video_driver_get_video_output_size(unsigned *width, unsigned *height)
+{
+ if (!video_driver_poke || !video_driver_poke->get_video_output_size)
+ return false;
+ video_driver_poke->get_video_output_size(video_driver_data,
+ width, height);
+ return true;
+}
+
+void video_driver_set_osd_msg(const char *msg, const void *data, void *font)
+{
+ video_frame_info_t video_info;
+ video_driver_build_info(&video_info);
+ if (video_driver_poke && video_driver_poke->set_osd_msg)
+ video_driver_poke->set_osd_msg(video_driver_data, &video_info, msg, data, font);
+}
+
+void video_driver_set_texture_enable(bool enable, bool fullscreen)
+{
+ if (video_driver_poke && video_driver_poke->set_texture_enable)
+ video_driver_poke->set_texture_enable(video_driver_data,
+ enable, fullscreen);
+}
+
+void video_driver_set_texture_frame(const void *frame, bool rgb32,
+ unsigned width, unsigned height, float alpha)
+{
+ if (video_driver_poke && video_driver_poke->set_texture_frame)
+ video_driver_poke->set_texture_frame(video_driver_data,
+ frame, rgb32, width, height, alpha);
+}
+
+#ifdef HAVE_OVERLAY
+bool video_driver_overlay_interface(const video_overlay_interface_t **iface)
+{
+ if (!current_video || !current_video->overlay_interface)
+ return false;
+ current_video->overlay_interface(video_driver_data, iface);
+ return true;
+}
+#endif
+
+void *video_driver_read_frame_raw(unsigned *width,
+ unsigned *height, size_t *pitch)
+{
+ if (!current_video || !current_video->read_frame_raw)
+ return NULL;
+ return current_video->read_frame_raw(video_driver_data, width,
+ height, pitch);
+}
+
+void video_driver_set_filtering(unsigned index, bool smooth)
+{
+ if (video_driver_poke && video_driver_poke->set_filtering)
+ video_driver_poke->set_filtering(video_driver_data, index, smooth);
+}
+
+void video_driver_cached_frame_set(const void *data, unsigned width,
+ unsigned height, size_t pitch)
+{
+ if (data)
+ frame_cache_data = data;
+ frame_cache_width = width;
+ frame_cache_height = height;
+ frame_cache_pitch = pitch;
+}
+
+void video_driver_cached_frame_get(const void **data, unsigned *width,
+ unsigned *height, size_t *pitch)
+{
+ if (data)
+ *data = frame_cache_data;
+ if (width)
+ *width = frame_cache_width;
+ if (height)
+ *height = frame_cache_height;
+ if (pitch)
+ *pitch = frame_cache_pitch;
+}
+
+void video_driver_get_size(unsigned *width, unsigned *height)
+{
+#ifdef HAVE_THREADS
+ bool is_threaded = video_driver_is_threaded_internal();
+ video_driver_threaded_lock(is_threaded);
+#endif
+ if (width)
+ *width = video_driver_width;
+ if (height)
+ *height = video_driver_height;
+#ifdef HAVE_THREADS
+ video_driver_threaded_unlock(is_threaded);
+#endif
+}
+
+void video_driver_set_size(unsigned *width, unsigned *height)
+{
+#ifdef HAVE_THREADS
+ bool is_threaded = video_driver_is_threaded_internal();
+ video_driver_threaded_lock(is_threaded);
+#endif
+ if (width)
+ video_driver_width = *width;
+ if (height)
+ video_driver_height = *height;
+#ifdef HAVE_THREADS
+ video_driver_threaded_unlock(is_threaded);
+#endif
+}
+
+/**
+ * video_monitor_set_refresh_rate:
+ * @hz : New refresh rate for monitor.
+ *
+ * Sets monitor refresh rate to new value.
+ **/
+void video_monitor_set_refresh_rate(float hz)
+{
+ char msg[128];
+ settings_t *settings = config_get_ptr();
+
+ snprintf(msg, sizeof(msg),
+ "Setting refresh rate to: %.3f Hz.", hz);
+ runloop_msg_queue_push(msg, 1, 180, false);
+ RARCH_LOG("%s\n", msg);
+
+ configuration_set_float(settings,
+ settings->floats.video_refresh_rate,
+ hz);
+}
+
+/**
+ * video_monitor_fps_statistics
+ * @refresh_rate : Monitor refresh rate.
+ * @deviation : Deviation from measured refresh rate.
+ * @sample_points : Amount of sampled points.
+ *
+ * Gets the monitor FPS statistics based on the current
+ * runtime.
+ *
+ * Returns: true (1) on success.
+ * false (0) if:
+ * a) threaded video mode is enabled
+ * b) less than 2 frame time samples.
+ * c) FPS monitor enable is off.
+ **/
+bool video_monitor_fps_statistics(double *refresh_rate,
+ double *deviation, unsigned *sample_points)
+{
+ unsigned i;
+ retro_time_t accum = 0;
+ retro_time_t avg = 0;
+ retro_time_t accum_var = 0;
+ unsigned samples = 0;
+
+#ifdef HAVE_THREADS
+ if (video_driver_is_threaded_internal())
+ return false;
+#endif
+
+ samples = MIN(MEASURE_FRAME_TIME_SAMPLES_COUNT,
+ (unsigned)video_driver_frame_time_count);
+
+ if (samples < 2)
+ return false;
+
+ /* Measure statistics on frame time (microsecs), *not* FPS. */
+ for (i = 0; i < samples; i++)
+ {
+ accum += video_driver_frame_time_samples[i];
+#if 0
+ RARCH_LOG("[Video]: Interval #%u: %d usec / frame.\n",
+ i, (int)frame_time_samples[i]);
+#endif
+ }
+
+ avg = accum / samples;
+
+ /* Drop first measurement. It is likely to be bad. */
+ for (i = 0; i < samples; i++)
+ {
+ retro_time_t diff = video_driver_frame_time_samples[i] - avg;
+ accum_var += diff * diff;
+ }
+
+ *deviation = sqrt((double)accum_var / (samples - 1)) / avg;
+
+ if (refresh_rate)
+ *refresh_rate = 1000000.0 / avg;
+
+ if (sample_points)
+ *sample_points = samples;
+
+ return true;
+}
+
+float video_driver_get_aspect_ratio(void)
+{
+ return video_driver_aspect_ratio;
+}
+
+void video_driver_set_aspect_ratio_value(float value)
+{
+ video_driver_aspect_ratio = value;
+}
+
+static bool video_driver_frame_filter(
+ const void *data,
+ video_frame_info_t *video_info,
+ unsigned width, unsigned height,
+ size_t pitch,
+ unsigned *output_width, unsigned *output_height,
+ unsigned *output_pitch)
+{
+ rarch_softfilter_get_output_size(video_driver_state_filter,
+ output_width, output_height, width, height);
+
+ *output_pitch = (*output_width) * video_driver_state_out_bpp;
+
+ rarch_softfilter_process(video_driver_state_filter,
+ video_driver_state_buffer, *output_pitch,
+ data, width, height, pitch);
+
+ if (video_info->post_filter_record && recording_data)
+ recording_dump_frame(video_driver_state_buffer,
+ *output_width, *output_height, *output_pitch,
+ video_info->runloop_is_idle);
+
+ return true;
+}
+
+rarch_softfilter_t *video_driver_frame_filter_get_ptr(void)
+{
+ return video_driver_state_filter;
+}
+
+enum retro_pixel_format video_driver_get_pixel_format(void)
+{
+ return video_driver_pix_fmt;
+}
+
+void video_driver_set_pixel_format(enum retro_pixel_format fmt)
+{
+ video_driver_pix_fmt = fmt;
+}
+
+/**
+ * video_driver_cached_frame:
+ *
+ * Renders the current video frame.
+ **/
+bool video_driver_cached_frame(void)
+{
+ void *recording = recording_driver_get_data_ptr();
+
+ recording_driver_lock();
+
+ /* Cannot allow recording when pushing duped frames. */
+ recording_data = NULL;
+
+ retro_ctx.frame_cb(
+ (frame_cache_data != RETRO_HW_FRAME_BUFFER_VALID)
+ ? frame_cache_data : NULL,
+ frame_cache_width,
+ frame_cache_height, frame_cache_pitch);
+
+ recording_data = recording;
+
+ recording_driver_unlock();
+
+ return true;
+}
+
+void video_driver_monitor_adjust_system_rates(void)
+{
+ float timing_skew = 0.0f;
+ settings_t *settings = config_get_ptr();
+ float video_refresh_rate = settings->floats.video_refresh_rate;
+ float timing_skew_hz = video_refresh_rate;
+ const struct retro_system_timing *info = (const struct retro_system_timing*)&video_driver_av_info.timing;
+
+ rarch_ctl(RARCH_CTL_UNSET_NONBLOCK_FORCED, NULL);
+
+ if (!info || info->fps <= 0.0)
+ return;
+
+ video_driver_core_hz = info->fps;
+
+ if (video_driver_crt_switching_active)
+ timing_skew_hz = video_driver_core_hz;
+ timing_skew = fabs(
+ 1.0f - info->fps / timing_skew_hz);
+
+ if (!settings->bools.vrr_runloop_enable)
+ {
+ /* We don't want to adjust pitch too much. If we have extreme cases,
+ * just don't readjust at all. */
+ if (timing_skew <= settings->floats.audio_max_timing_skew)
+ return;
+
+ RARCH_LOG("[Video]: Timings deviate too much. Will not adjust."
+ " (Display = %.2f Hz, Game = %.2f Hz)\n",
+ video_refresh_rate,
+ (float)info->fps);
+ }
+
+ if (info->fps <= timing_skew_hz)
+ return;
+
+ /* We won't be able to do VSync reliably when game FPS > monitor FPS. */
+ rarch_ctl(RARCH_CTL_SET_NONBLOCK_FORCED, NULL);
+ RARCH_LOG("[Video]: Game FPS > Monitor FPS. Cannot rely on VSync.\n");
+}
+
+void video_driver_menu_settings(void **list_data, void *list_info_data,
+ void *group_data, void *subgroup_data, const char *parent_group)
+{
+#ifdef HAVE_MENU
+ rarch_setting_t **list = (rarch_setting_t**)list_data;
+ rarch_setting_info_t *list_info = (rarch_setting_info_t*)list_info_data;
+ rarch_setting_group_info_t *group_info = (rarch_setting_group_info_t*)group_data;
+ rarch_setting_group_info_t *subgroup_info = (rarch_setting_group_info_t*)subgroup_data;
+ global_t *global = global_get_ptr();
+
+ (void)list;
+ (void)list_info;
+ (void)group_info;
+ (void)subgroup_info;
+ (void)global;
+
+#if defined(__CELLOS_LV2__)
+ CONFIG_BOOL(
+ list, list_info,
+ &global->console.screen.pal60_enable,
+ MENU_ENUM_LABEL_PAL60_ENABLE,
+ MENU_ENUM_LABEL_VALUE_PAL60_ENABLE,
+ false,
+ MENU_ENUM_LABEL_VALUE_OFF,
+ MENU_ENUM_LABEL_VALUE_ON,
+ group_info,
+ subgroup_info,
+ parent_group,
+ general_write_handler,
+ general_read_handler,
+ SD_FLAG_NONE);
+#endif
+#if defined(GEKKO) || defined(_XBOX360)
+ CONFIG_UINT(
+ list, list_info,
+ &global->console.screen.gamma_correction,
+ MENU_ENUM_LABEL_VIDEO_GAMMA,
+ MENU_ENUM_LABEL_VALUE_VIDEO_GAMMA,
+ 0,
+ group_info,
+ subgroup_info,
+ parent_group,
+ general_write_handler,
+ general_read_handler);
+ menu_settings_list_current_add_cmd(
+ list,
+ list_info,
+ CMD_EVENT_VIDEO_APPLY_STATE_CHANGES);
+ menu_settings_list_current_add_range(
+ list,
+ list_info,
+ 0,
+ MAX_GAMMA_SETTING,
+ 1,
+ true,
+ true);
+ settings_data_list_current_add_flags(list, list_info,
+ SD_FLAG_CMD_APPLY_AUTO|SD_FLAG_ADVANCED);
+#endif
+#if defined(_XBOX1) || defined(HW_RVL)
+ CONFIG_BOOL(
+ list, list_info,
+ &global->console.softfilter_enable,
+ MENU_ENUM_LABEL_VIDEO_SOFT_FILTER,
+ MENU_ENUM_LABEL_VALUE_VIDEO_SOFT_FILTER,
+ false,
+ MENU_ENUM_LABEL_VALUE_OFF,
+ MENU_ENUM_LABEL_VALUE_ON,
+ group_info,
+ subgroup_info,
+ parent_group,
+ general_write_handler,
+ general_read_handler,
+ SD_FLAG_NONE);
+ menu_settings_list_current_add_cmd(
+ list,
+ list_info,
+ CMD_EVENT_VIDEO_APPLY_STATE_CHANGES);
+#endif
+#ifdef _XBOX1
+ CONFIG_UINT(
+ list, list_info,
+ &global->console.screen.flicker_filter_index,
+ MENU_ENUM_LABEL_VIDEO_FILTER_FLICKER,
+ MENU_ENUM_LABEL_VALUE_VIDEO_FILTER_FLICKER,
+ 0,
+ group_info,
+ subgroup_info,
+ parent_group,
+ general_write_handler,
+ general_read_handler);
+ menu_settings_list_current_add_range(list, list_info,
+ 0, 5, 1, true, true);
+#endif
+#endif
+}
+
+static void video_driver_lock_new(void)
+{
+ video_driver_lock_free();
+#ifdef HAVE_THREADS
+ if (!display_lock)
+ display_lock = slock_new();
+ retro_assert(display_lock);
+
+ if (!context_lock)
+ context_lock = slock_new();
+ retro_assert(context_lock);
+#endif
+}
+
+void video_driver_destroy(void)
+{
+ video_display_server_destroy();
+ crt_video_restore();
+
+ video_driver_cb_has_focus = null_driver_has_focus;
+ video_driver_use_rgba = false;
+ video_driver_data_own = false;
+ video_driver_active = false;
+ video_driver_cache_context = false;
+ video_driver_cache_context_ack = false;
+ video_driver_record_gpu_buffer = NULL;
+ current_video = NULL;
+ video_driver_set_cached_frame_ptr(NULL);
+}
+
+void video_driver_set_cached_frame_ptr(const void *data)
+{
+ if (data)
+ frame_cache_data = data;
+}
+
+void video_driver_set_stub_frame(void)
+{
+ frame_bak = current_video->frame;
+ current_video->frame = video_null.frame;
+}
+
+void video_driver_unset_stub_frame(void)
+{
+ if (frame_bak != NULL)
+ current_video->frame = frame_bak;
+
+ frame_bak = NULL;
+}
+
+bool video_driver_is_stub_frame(void)
+{
+ return current_video->frame == video_null.frame;
+}
+
+bool video_driver_supports_recording(void)
+{
+ settings_t *settings = config_get_ptr();
+ return settings->bools.video_gpu_record
+ && current_video->read_viewport;
+}
+
+bool video_driver_supports_viewport_read(void)
+{
+ settings_t *settings = config_get_ptr();
+ return (settings->bools.video_gpu_screenshot ||
+ (video_driver_is_hw_context() && !current_video->read_frame_raw))
+ && current_video->read_viewport && current_video->viewport_info;
+}
+
+bool video_driver_supports_read_frame_raw(void)
+{
+ if (current_video->read_frame_raw)
+ return true;
+ return false;
+}
+
+void video_driver_set_viewport_config(void)
+{
+ settings_t *settings = config_get_ptr();
+
+ if (settings->floats.video_aspect_ratio < 0.0f)
+ {
+ struct retro_game_geometry *geom = &video_driver_av_info.geometry;
+
+ if (geom->aspect_ratio > 0.0f &&
+ settings->bools.video_aspect_ratio_auto)
+ aspectratio_lut[ASPECT_RATIO_CONFIG].value = geom->aspect_ratio;
+ else
+ {
+ unsigned base_width = geom->base_width;
+ unsigned base_height = geom->base_height;
+
+ /* Get around division by zero errors */
+ if (base_width == 0)
+ base_width = 1;
+ if (base_height == 0)
+ base_height = 1;
+ aspectratio_lut[ASPECT_RATIO_CONFIG].value =
+ (float)base_width / base_height; /* 1:1 PAR. */
+ }
+ }
+ else
+ {
+ aspectratio_lut[ASPECT_RATIO_CONFIG].value =
+ settings->floats.video_aspect_ratio;
+ }
+}
+
+void video_driver_set_viewport_square_pixel(void)
+{
+ unsigned len, highest, i, aspect_x, aspect_y;
+ struct retro_game_geometry *geom = &video_driver_av_info.geometry;
+ unsigned width = geom->base_width;
+ unsigned height = geom->base_height;
+
+ if (width == 0 || height == 0)
+ return;
+
+ len = MIN(width, height);
+ highest = 1;
+
+ for (i = 1; i < len; i++)
+ {
+ if ((width % i) == 0 && (height % i) == 0)
+ highest = i;
+ }
+
+ aspect_x = width / highest;
+ aspect_y = height / highest;
+
+ snprintf(aspectratio_lut[ASPECT_RATIO_SQUARE].name,
+ sizeof(aspectratio_lut[ASPECT_RATIO_SQUARE].name),
+ "1:1 PAR (%u:%u DAR)", aspect_x, aspect_y);
+
+ aspectratio_lut[ASPECT_RATIO_SQUARE].value = (float)aspect_x / aspect_y;
+}
+
+void video_driver_set_viewport_core(void)
+{
+ struct retro_game_geometry *geom = &video_driver_av_info.geometry;
+
+ if (!geom || geom->base_width <= 0.0f || geom->base_height <= 0.0f)
+ return;
+
+ /* Fallback to 1:1 pixel ratio if none provided */
+ if (geom->aspect_ratio > 0.0f)
+ aspectratio_lut[ASPECT_RATIO_CORE].value = geom->aspect_ratio;
+ else
+ aspectratio_lut[ASPECT_RATIO_CORE].value =
+ (float)geom->base_width / geom->base_height;
+}
+
+void video_driver_reset_custom_viewport(void)
+{
+ struct video_viewport *custom_vp = video_viewport_get_custom();
+
+ custom_vp->width = 0;
+ custom_vp->height = 0;
+ custom_vp->x = 0;
+ custom_vp->y = 0;
+}
+
+void video_driver_set_rgba(void)
+{
+ video_driver_lock();
+ video_driver_use_rgba = true;
+ video_driver_unlock();
+}
+
+void video_driver_unset_rgba(void)
+{
+ video_driver_lock();
+ video_driver_use_rgba = false;
+ video_driver_unlock();
+}
+
+bool video_driver_supports_rgba(void)
+{
+ bool tmp;
+ video_driver_lock();
+ tmp = video_driver_use_rgba;
+ video_driver_unlock();
+ return tmp;
+}
+
+bool video_driver_get_next_video_out(void)
+{
+ if (!video_driver_poke)
+ return false;
+
+ if (!video_driver_poke->get_video_output_next)
+ return video_context_driver_get_video_output_next();
+ video_driver_poke->get_video_output_next(video_driver_data);
+ return true;
+}
+
+bool video_driver_get_prev_video_out(void)
+{
+ if (!video_driver_poke)
+ return false;
+
+ if (!video_driver_poke->get_video_output_prev)
+ return video_context_driver_get_video_output_prev();
+ video_driver_poke->get_video_output_prev(video_driver_data);
+ return true;
+}
+
+bool video_driver_init(bool *video_is_threaded)
+{
+ video_driver_lock_new();
+ video_driver_filter_free();
+ video_driver_set_cached_frame_ptr(NULL);
+ return video_driver_init_internal(video_is_threaded);
+}
+
+void video_driver_destroy_data(void)
+{
+ video_driver_data = NULL;
+}
+
+void video_driver_free(void)
+{
+ video_driver_free_internal();
+ video_driver_lock_free();
+ video_driver_data = NULL;
+ video_driver_set_cached_frame_ptr(NULL);
+}
+
+void video_driver_monitor_reset(void)
+{
+ video_driver_frame_time_count = 0;
+}
+
+void video_driver_set_aspect_ratio(void)
+{
+ settings_t *settings = config_get_ptr();
+
+ switch (settings->uints.video_aspect_ratio_idx)
+ {
+ case ASPECT_RATIO_SQUARE:
+ video_driver_set_viewport_square_pixel();
+ break;
+
+ case ASPECT_RATIO_CORE:
+ video_driver_set_viewport_core();
+ break;
+
+ case ASPECT_RATIO_CONFIG:
+ video_driver_set_viewport_config();
+ break;
+
+ default:
+ break;
+ }
+
+ video_driver_set_aspect_ratio_value(
+ aspectratio_lut[settings->uints.video_aspect_ratio_idx].value);
+
+ if (!video_driver_poke || !video_driver_poke->set_aspect_ratio)
+ return;
+ video_driver_poke->set_aspect_ratio(
+ video_driver_data, settings->uints.video_aspect_ratio_idx);
+}
+
+void video_driver_update_viewport(struct video_viewport* vp, bool force_full, bool keep_aspect)
+{
+ gfx_ctx_aspect_t aspect_data;
+ float device_aspect = (float)vp->full_width / vp->full_height;
+ settings_t* settings = config_get_ptr();
+
+ aspect_data.aspect = &device_aspect;
+ aspect_data.width = vp->full_width;
+ aspect_data.height = vp->full_height;
+
+ video_context_driver_translate_aspect(&aspect_data);
+
+ vp->x = 0;
+ vp->y = 0;
+ vp->width = vp->full_width;
+ vp->height = vp->full_height;
+
+ if (settings->bools.video_scale_integer && !force_full)
+ {
+ video_viewport_get_scaled_integer(
+ vp, vp->full_width, vp->full_height, video_driver_get_aspect_ratio(), keep_aspect);
+ }
+ else if (keep_aspect && !force_full)
+ {
+ float desired_aspect = video_driver_get_aspect_ratio();
+
+#if defined(HAVE_MENU)
+ if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
+ {
+ const struct video_viewport* custom = video_viewport_get_custom();
+
+ vp->x = custom->x;
+ vp->y = custom->y;
+ vp->width = custom->width;
+ vp->height = custom->height;
+ }
+ else
+#endif
+ {
+ float delta;
+
+ if (fabsf(device_aspect - desired_aspect) < 0.0001f)
+ {
+ /* If the aspect ratios of screen and desired aspect
+ * ratio are sufficiently equal (floating point stuff),
+ * assume they are actually equal.
+ */
+ }
+ else if (device_aspect > desired_aspect)
+ {
+ delta = (desired_aspect / device_aspect - 1.0f)
+ / 2.0f + 0.5f;
+ vp->x = (int)roundf(vp->full_width * (0.5f - delta));
+ vp->width = (unsigned)roundf(2.0f * vp->full_width * delta);
+ vp->y = 0;
+ vp->height = vp->full_height;
+ }
+ else
+ {
+ vp->x = 0;
+ vp->width = vp->full_width;
+ delta = (device_aspect / desired_aspect - 1.0f)
+ / 2.0f + 0.5f;
+ vp->y = (int)roundf(vp->full_height * (0.5f - delta));
+ vp->height = (unsigned)roundf(2.0f * vp->full_height * delta);
+ }
+ }
+ }
+
+#if defined(RARCH_MOBILE)
+ /* In portrait mode, we want viewport to gravitate to top of screen. */
+ if (device_aspect < 1.0f)
+ vp->y = 0;
+#endif
+}
+
+void video_driver_show_mouse(void)
+{
+ if (video_driver_poke && video_driver_poke->show_mouse)
+ video_driver_poke->show_mouse(video_driver_data, true);
+}
+
+void video_driver_hide_mouse(void)
+{
+ if (video_driver_poke && video_driver_poke->show_mouse)
+ video_driver_poke->show_mouse(video_driver_data, false);
+}
+
+void video_driver_set_nonblock_state(bool toggle)
+{
+ if (current_video->set_nonblock_state)
+ current_video->set_nonblock_state(video_driver_data, toggle);
+}
+
+bool video_driver_find_driver(void)
+{
+ int i;
+ driver_ctx_info_t drv;
+ settings_t *settings = config_get_ptr();
+
+ if (video_driver_is_hw_context())
+ {
+ struct retro_hw_render_callback *hwr = video_driver_get_hw_context();
+
+ current_video = NULL;
+
+ (void)hwr;
+
+#if defined(HAVE_VULKAN)
+ if (hwr && hw_render_context_is_vulkan(hwr->context_type))
+ {
+ RARCH_LOG("[Video]: Using HW render, Vulkan driver forced.\n");
+ current_video = &video_vulkan;
+ }
+#endif
+
+#if defined(HAVE_OPENGL)
+ if (hwr && hw_render_context_is_gl(hwr->context_type))
+ {
+ RARCH_LOG("[Video]: Using HW render, OpenGL driver forced.\n");
+ current_video = &video_gl;
+ }
+#endif
+
+ if (current_video)
+ return true;
+ }
+
+ if (frontend_driver_has_get_video_driver_func())
+ {
+ current_video = (video_driver_t*)frontend_driver_get_video_driver();
+
+ if (current_video)
+ return true;
+ RARCH_WARN("Frontend supports get_video_driver() but did not specify one.\n");
+ }
+
+ drv.label = "video_driver";
+ drv.s = settings->arrays.video_driver;
+
+ driver_ctl(RARCH_DRIVER_CTL_FIND_INDEX, &drv);
+
+ i = (int)drv.len;
+
+ if (i >= 0)
+ current_video = (video_driver_t*)video_driver_find_handle(i);
+ else
+ {
+ if (verbosity_is_enabled())
+ {
+ unsigned d;
+ RARCH_ERR("Couldn't find any video driver named \"%s\"\n",
+ settings->arrays.video_driver);
+ RARCH_LOG_OUTPUT("Available video drivers are:\n");
+ for (d = 0; video_driver_find_handle(d); d++)
+ RARCH_LOG_OUTPUT("\t%s\n", video_driver_find_ident(d));
+ RARCH_WARN("Going to default to first video driver...\n");
+ }
+
+ current_video = (video_driver_t*)video_driver_find_handle(0);
+
+ if (!current_video)
+ retroarch_fail(1, "find_video_driver()");
+ }
+ return true;
+}
+
+void video_driver_apply_state_changes(void)
+{
+ if (video_driver_poke &&
+ video_driver_poke->apply_state_changes)
+ video_driver_poke->apply_state_changes(video_driver_data);
+}
+
+bool video_driver_read_viewport(uint8_t *buffer, bool is_idle)
+{
+ if ( current_video->read_viewport
+ && current_video->read_viewport(
+ video_driver_data, buffer, is_idle))
+ return true;
+ return false;
+}
+
+bool video_driver_frame_filter_alive(void)
+{
+ return !!video_driver_state_filter;
+}
+
+bool video_driver_frame_filter_is_32bit(void)
+{
+ return video_driver_state_out_rgb32;
+}
+
+void video_driver_default_settings(void)
+{
+ global_t *global = global_get_ptr();
+
+ if (!global)
+ return;
+
+ global->console.screen.gamma_correction = DEFAULT_GAMMA;
+ global->console.flickerfilter_enable = false;
+ global->console.softfilter_enable = false;
+
+ global->console.screen.resolutions.current.id = 0;
+}
+
+void video_driver_load_settings(config_file_t *conf)
+{
+ bool tmp_bool = false;
+ global_t *global = global_get_ptr();
+
+ if (!conf)
+ return;
+
+#ifdef _XBOX
+ CONFIG_GET_BOOL_BASE(conf, global,
+ console.screen.gamma_correction, "gamma_correction");
+#else
+ CONFIG_GET_INT_BASE(conf, global,
+ console.screen.gamma_correction, "gamma_correction");
+#endif
+
+ if (config_get_bool(conf, "flicker_filter_enable",
+ &tmp_bool))
+ global->console.flickerfilter_enable = tmp_bool;
+
+ if (config_get_bool(conf, "soft_filter_enable",
+ &tmp_bool))
+ global->console.softfilter_enable = tmp_bool;
+
+ CONFIG_GET_INT_BASE(conf, global,
+ console.screen.soft_filter_index,
+ "soft_filter_index");
+ CONFIG_GET_INT_BASE(conf, global,
+ console.screen.resolutions.current.id,
+ "current_resolution_id");
+ CONFIG_GET_INT_BASE(conf, global,
+ console.screen.flicker_filter_index,
+ "flicker_filter_index");
+}
+
+void video_driver_save_settings(config_file_t *conf)
+{
+ global_t *global = global_get_ptr();
+ if (!conf)
+ return;
+
+#ifdef _XBOX
+ config_set_bool(conf, "gamma_correction",
+ global->console.screen.gamma_correction);
+#else
+ config_set_int(conf, "gamma_correction",
+ global->console.screen.gamma_correction);
+#endif
+ config_set_bool(conf, "flicker_filter_enable",
+ global->console.flickerfilter_enable);
+ config_set_bool(conf, "soft_filter_enable",
+ global->console.softfilter_enable);
+
+ config_set_int(conf, "soft_filter_index",
+ global->console.screen.soft_filter_index);
+ config_set_int(conf, "current_resolution_id",
+ global->console.screen.resolutions.current.id);
+ config_set_int(conf, "flicker_filter_index",
+ global->console.screen.flicker_filter_index);
+}
+
+void video_driver_reinit(void)
+{
+ struct retro_hw_render_callback *hwr =
+ video_driver_get_hw_context();
+
+ if (hwr->cache_context)
+ video_driver_cache_context = true;
+ else
+ video_driver_cache_context = false;
+
+ video_driver_cache_context_ack = false;
+ command_event(CMD_EVENT_RESET_CONTEXT, NULL);
+ video_driver_cache_context = false;
+}
+
+void video_driver_set_own_driver(void)
+{
+ video_driver_data_own = true;
+}
+
+void video_driver_unset_own_driver(void)
+{
+ video_driver_data_own = false;
+}
+
+bool video_driver_owns_driver(void)
+{
+ return video_driver_data_own;
+}
+
+bool video_driver_is_hw_context(void)
+{
+ bool is_hw_context = false;
+
+ video_driver_context_lock();
+ is_hw_context = (hw_render.context_type != RETRO_HW_CONTEXT_NONE);
+ video_driver_context_unlock();
+
+ return is_hw_context;
+}
+
+void video_driver_free_hw_context(void)
+{
+ video_driver_context_lock();
+
+ if (hw_render.context_destroy)
+ hw_render.context_destroy();
+
+ memset(&hw_render, 0, sizeof(hw_render));
+
+ video_driver_context_unlock();
+
+ hw_render_context_negotiation = NULL;
+}
+
+struct retro_hw_render_callback *video_driver_get_hw_context(void)
+{
+ return &hw_render;
+}
+
+const struct retro_hw_render_context_negotiation_interface *
+ video_driver_get_context_negotiation_interface(void)
+{
+ return hw_render_context_negotiation;
+}
+
+void video_driver_set_context_negotiation_interface(
+ const struct retro_hw_render_context_negotiation_interface *iface)
+{
+ hw_render_context_negotiation = iface;
+}
+
+bool video_driver_is_video_cache_context(void)
+{
+ return video_driver_cache_context;
+}
+
+void video_driver_set_video_cache_context_ack(void)
+{
+ video_driver_cache_context_ack = true;
+}
+
+void video_driver_unset_video_cache_context_ack(void)
+{
+ video_driver_cache_context_ack = false;
+}
+
+bool video_driver_is_video_cache_context_ack(void)
+{
+ return video_driver_cache_context_ack;
+}
+
+void video_driver_set_active(void)
+{
+ video_driver_active = true;
+}
+
+void video_driver_unset_active(void)
+{
+ video_driver_active = false;
+}
+
+bool video_driver_is_active(void)
+{
+ return video_driver_active;
+}
+
+void video_driver_get_record_status(
+ bool *has_gpu_record,
+ uint8_t **gpu_buf)
+{
+ *gpu_buf = video_driver_record_gpu_buffer;
+ *has_gpu_record = video_driver_record_gpu_buffer != NULL;
+}
+
+bool video_driver_gpu_record_init(unsigned size)
+{
+ video_driver_record_gpu_buffer = (uint8_t*)malloc(size);
+ if (!video_driver_record_gpu_buffer)
+ return false;
+ return true;
+}
+
+void video_driver_gpu_record_deinit(void)
+{
+ free(video_driver_record_gpu_buffer);
+ video_driver_record_gpu_buffer = NULL;
+}
+
+bool video_driver_get_current_software_framebuffer(
+ struct retro_framebuffer *fb)
+{
+ if (
+ video_driver_poke
+ && video_driver_poke->get_current_software_framebuffer
+ && video_driver_poke->get_current_software_framebuffer(
+ video_driver_data, fb))
+ return true;
+
+ return false;
+}
+
+bool video_driver_get_hw_render_interface(
+ const struct retro_hw_render_interface **iface)
+{
+ if (
+ video_driver_poke
+ && video_driver_poke->get_hw_render_interface
+ && video_driver_poke->get_hw_render_interface(
+ video_driver_data, iface))
+ return true;
+
+ return false;
+}
+
+bool video_driver_get_viewport_info(struct video_viewport *viewport)
+{
+ if (!current_video || !current_video->viewport_info)
+ return false;
+ current_video->viewport_info(video_driver_data, viewport);
+ return true;
+}
+
+void video_driver_set_title_buf(void)
+{
+ struct retro_system_info info;
+ core_get_system_info(&info);
+
+ fill_pathname_join_concat_noext(
+ video_driver_title_buf,
+ msg_hash_to_str(MSG_PROGRAM),
+ " ",
+ info.library_name,
+ sizeof(video_driver_title_buf));
+ strlcat(video_driver_title_buf,
+ " ", sizeof(video_driver_title_buf));
+ strlcat(video_driver_title_buf,
+ info.library_version,
+ sizeof(video_driver_title_buf));
+}
+
+/**
+ * video_viewport_get_scaled_integer:
+ * @vp : Viewport handle
+ * @width : Width.
+ * @height : Height.
+ * @aspect_ratio : Aspect ratio (in float).
+ * @keep_aspect : Preserve aspect ratio?
+ *
+ * Gets viewport scaling dimensions based on
+ * scaled integer aspect ratio.
+ **/
+void video_viewport_get_scaled_integer(struct video_viewport *vp,
+ unsigned width, unsigned height,
+ float aspect_ratio, bool keep_aspect)
+{
+ int padding_x = 0;
+ int padding_y = 0;
+ settings_t *settings = config_get_ptr();
+
+ if (settings->uints.video_aspect_ratio_idx == ASPECT_RATIO_CUSTOM)
+ {
+ struct video_viewport *custom = video_viewport_get_custom();
+
+ if (custom)
+ {
+ padding_x = width - custom->width;
+ padding_y = height - custom->height;
+ width = custom->width;
+ height = custom->height;
+ }
+ }
+ else
+ {
+ unsigned base_width;
+ /* Use system reported sizes as these define the
+ * geometry for the "normal" case. */
+ unsigned base_height =
+ video_driver_av_info.geometry.base_height;
+
+ if (base_height == 0)
+ base_height = 1;
+
+ /* Account for non-square pixels.
+ * This is sort of contradictory with the goal of integer scale,
+ * but it is desirable in some cases.
+ *
+ * If square pixels are used, base_height will be equal to
+ * system->av_info.base_height. */
+ base_width = (unsigned)roundf(base_height * aspect_ratio);
+
+ /* Make sure that we don't get 0x scale ... */
+ if (width >= base_width && height >= base_height)
+ {
+ if (keep_aspect)
+ {
+ /* X/Y scale must be same. */
+ unsigned max_scale = MIN(width / base_width,
+ height / base_height);
+ padding_x = width - base_width * max_scale;
+ padding_y = height - base_height * max_scale;
+ }
+ else
+ {
+ /* X/Y can be independent, each scaled as much as possible. */
+ padding_x = width % base_width;
+ padding_y = height % base_height;
+ }
+ }
+
+ width -= padding_x;
+ height -= padding_y;
+ }
+
+ vp->width = width;
+ vp->height = height;
+ vp->x = padding_x / 2;
+ vp->y = padding_y / 2;
+}
+
+struct retro_system_av_info *video_viewport_get_system_av_info(void)
+{
+ return &video_driver_av_info;
+}
+
+struct video_viewport *video_viewport_get_custom(void)
+{
+ settings_t *settings = config_get_ptr();
+ return &settings->video_viewport_custom;
+}
+
+unsigned video_pixel_get_alignment(unsigned pitch)
+{
+ if (pitch & 1)
+ return 1;
+ if (pitch & 2)
+ return 2;
+ if (pitch & 4)
+ return 4;
+ return 8;
+}
+
+/**
+ * video_driver_frame:
+ * @data : pointer to data of the video frame.
+ * @width : width of the video frame.
+ * @height : height of the video frame.
+ * @pitch : pitch of the video frame.
+ *
+ * Video frame render callback function.
+ **/
+void video_driver_frame(const void *data, unsigned width,
+ unsigned height, size_t pitch)
+{
+ static char video_driver_msg[256];
+ static char title[256];
+ video_frame_info_t video_info;
+ static retro_time_t curr_time;
+ static retro_time_t fps_time;
+ static float last_fps, frame_time;
+ unsigned output_width = 0;
+ unsigned output_height = 0;
+ unsigned output_pitch = 0;
+ const char *msg = NULL;
+ retro_time_t new_time =
+ cpu_features_get_time_usec();
+
+ if (!video_driver_active)
+ return;
+
+ if (video_driver_scaler_ptr && data &&
+ (video_driver_pix_fmt == RETRO_PIXEL_FORMAT_0RGB1555) &&
+ (data != RETRO_HW_FRAME_BUFFER_VALID))
+ {
+ if (video_pixel_frame_scale(
+ video_driver_scaler_ptr->scaler,
+ video_driver_scaler_ptr->scaler_out,
+ data, width, height, pitch))
+ {
+ data = video_driver_scaler_ptr->scaler_out;
+ pitch = video_driver_scaler_ptr->scaler->out_stride;
+ }
+ }
+
+ if (data)
+ frame_cache_data = data;
+ frame_cache_width = width;
+ frame_cache_height = height;
+ frame_cache_pitch = pitch;
+
+ video_driver_build_info(&video_info);
+
+ /* Get the amount of frames per seconds. */
+ if (video_driver_frame_count)
+ {
+ unsigned write_index =
+ video_driver_frame_time_count++ &
+ (MEASURE_FRAME_TIME_SAMPLES_COUNT - 1);
+ frame_time = new_time - fps_time;
+ video_driver_frame_time_samples[write_index] = frame_time;
+ fps_time = new_time;
+
+ if (video_driver_frame_count == 1)
+ strlcpy(title, video_driver_window_title, sizeof(title));
+
+ if ((video_driver_frame_count % FPS_UPDATE_INTERVAL) == 0)
+ {
+ char frames_text[64];
+ last_fps = TIME_TO_FPS(curr_time, new_time, FPS_UPDATE_INTERVAL);
+
+ if (video_info.fps_show || video_info.framecount_show)
+ {
+ if (video_info.fps_show)
+ {
+ snprintf(video_info.fps_text, sizeof(video_info.fps_text),
+ " || FPS: %6.1f ", last_fps);
+ }
+ if (video_info.framecount_show)
+ {
+ snprintf(frames_text,
+ sizeof(frames_text),
+ " || Frames: %" PRIu64,
+ (uint64_t)video_driver_frame_count);
+ }
+ snprintf(video_driver_window_title, sizeof(video_driver_window_title),
+ "%s%s%s", title,
+ video_info.fps_show ? video_info.fps_text : "",
+ video_info.framecount_show ? frames_text : "");
+ }
+ else
+ {
+ if (!string_is_equal(video_driver_window_title, title))
+ strlcpy(video_driver_window_title, title, sizeof(video_driver_window_title));
+ }
+
+ curr_time = new_time;
+ video_driver_window_title_update = true;
+ }
+
+ if (video_info.fps_show)
+ {
+ if (video_info.framecount_show)
+ snprintf(
+ video_info.fps_text,
+ sizeof(video_info.fps_text),
+ "FPS: %6.1f || %s: %" PRIu64,
+ last_fps,
+ msg_hash_to_str(MSG_FRAMES),
+ (uint64_t)video_driver_frame_count);
+ else
+ snprintf(
+ video_info.fps_text,
+ sizeof(video_info.fps_text),
+ "FPS: %6.1f",
+ last_fps);
+ }
+
+ if (video_info.fps_show && video_info.framecount_show)
+ snprintf(
+ video_info.fps_text,
+ sizeof(video_info.fps_text),
+ "FPS: %6.1f || %s: %" PRIu64,
+ last_fps,
+ msg_hash_to_str(MSG_FRAMES),
+ (uint64_t)video_driver_frame_count);
+ else if (video_info.framecount_show)
+ snprintf(
+ video_info.fps_text,
+ sizeof(video_info.fps_text),
+ "%s: %" PRIu64,
+ msg_hash_to_str(MSG_FRAMES),
+ (uint64_t)video_driver_frame_count);
+ else if (video_info.fps_show)
+ snprintf(
+ video_info.fps_text,
+ sizeof(video_info.fps_text),
+ "FPS: %6.1f",
+ last_fps);
+ }
+ else
+ {
+
+ curr_time = fps_time = new_time;
+
+ strlcpy(video_driver_window_title,
+ video_driver_title_buf,
+ sizeof(video_driver_window_title));
+
+ if (video_info.fps_show)
+ strlcpy(video_info.fps_text,
+ msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NOT_AVAILABLE),
+ sizeof(video_info.fps_text));
+
+ video_driver_window_title_update = true;
+ }
+
+ video_info.frame_rate = last_fps;
+ video_info.frame_time = frame_time / 1000.0f;
+ video_info.frame_count = (uint64_t) video_driver_frame_count;
+
+ /* Slightly messy code,
+ * but we really need to do processing before blocking on VSync
+ * for best possible scheduling.
+ */
+ if (
+ (
+ !video_driver_state_filter
+ || !video_info.post_filter_record
+ || !data
+ || video_driver_record_gpu_buffer
+ ) && recording_data
+ )
+ recording_dump_frame(data, width, height,
+ pitch, video_info.runloop_is_idle);
+
+ if (data && video_driver_state_filter &&
+ video_driver_frame_filter(data, &video_info, width, height, pitch,
+ &output_width, &output_height, &output_pitch))
+ {
+ data = video_driver_state_buffer;
+ width = output_width;
+ height = output_height;
+ pitch = output_pitch;
+ }
+
+ video_driver_msg[0] = '\0';
+
+ if ( video_info.font_enable
+ && runloop_msg_queue_pull((const char**)&msg)
+ && msg)
+ {
+#ifdef HAVE_THREADS
+ /* the msg pointer may point to data modified by another thread */
+ runloop_msg_queue_lock();
+#endif
+ strlcpy(video_driver_msg, msg, sizeof(video_driver_msg));
+#ifdef HAVE_THREADS
+ runloop_msg_queue_unlock();
+#endif
+ }
+
+ if (video_info.statistics_show)
+ {
+ audio_statistics_t audio_stats = {0.0f};
+ double stddev = 0.0;
+ struct retro_system_av_info *av_info = &video_driver_av_info;
+ unsigned red = 255;
+ unsigned green = 255;
+ unsigned blue = 255;
+ unsigned alpha = 255;
+
+ video_monitor_fps_statistics(NULL, &stddev, NULL);
+
+ video_info.osd_stat_params.x = 0.010f;
+ video_info.osd_stat_params.y = 0.950f;
+ video_info.osd_stat_params.scale = 1.0f;
+ video_info.osd_stat_params.full_screen = true;
+ video_info.osd_stat_params.drop_x = -2;
+ video_info.osd_stat_params.drop_y = -2;
+ video_info.osd_stat_params.drop_mod = 0.3f;
+ video_info.osd_stat_params.drop_alpha = 1.0f;
+ video_info.osd_stat_params.color = COLOR_ABGR(
+ red, green, blue, alpha);
+
+ compute_audio_buffer_statistics(&audio_stats);
+
+ snprintf(video_info.stat_text,
+ sizeof(video_info.stat_text),
+ "Video Statistics:\n -Frame rate: %6.2f fps\n -Frame time: %6.2f ms\n -Frame time deviation: %.3f %%\n"
+ " -Frame count: %" PRIu64"\n -Viewport: %d x %d x %3.2f\n"
+ "Audio Statistics:\n -Average buffer saturation: %.2f %%\n -Standard deviation: %.2f %%\n -Time spent close to underrun: %.2f %%\n -Time spent close to blocking: %.2f %%\n -Sample count: %d\n"
+ "Core Geometry:\n -Size: %u x %u\n -Max Size: %u x %u\n -Aspect: %3.2f\nCore Timing:\n -FPS: %3.2f\n -Sample Rate: %6.2f\n",
+ video_info.frame_rate,
+ video_info.frame_time,
+ 100.0 * stddev,
+ video_info.frame_count,
+ video_info.width,
+ video_info.height,
+ video_info.refresh_rate,
+ audio_stats.average_buffer_saturation,
+ audio_stats.std_deviation_percentage,
+ audio_stats.close_to_underrun,
+ audio_stats.close_to_blocking,
+ audio_stats.samples,
+ av_info->geometry.base_width,
+ av_info->geometry.base_height,
+ av_info->geometry.max_width,
+ av_info->geometry.max_height,
+ av_info->geometry.aspect_ratio,
+ av_info->timing.fps,
+ av_info->timing.sample_rate);
+
+ /* TODO/FIXME - add OSD chat text here */
+#if 0
+ snprintf(video_info.chat_text, sizeof(video_info.chat_text),
+ "anon: does retroarch netplay have in-game chat?\nradius: I don't know \u2605");
+#endif
+ }
+
+ video_driver_active = current_video->frame(
+ video_driver_data, data, width, height,
+ video_driver_frame_count,
+ (unsigned)pitch, video_driver_msg, &video_info);
+
+ video_driver_frame_count++;
+
+ /* Display the FPS, with a higher priority. */
+ if (video_info.fps_show || video_info.framecount_show)
+ runloop_msg_queue_push(video_info.fps_text, 2, 1, true);
+
+ /* trigger set resolution*/
+ if (video_info.crt_switch_resolution)
+ {
+ video_driver_crt_switching_active = true;
+
+ if (video_info.crt_switch_resolution_super == 2560)
+ width = 2560;
+ if (video_info.crt_switch_resolution_super == 3840)
+ width = 3840;
+ if (video_info.crt_switch_resolution_super == 1920)
+ width = 1920;
+ crt_switch_res_core(width, height, video_driver_core_hz, video_info.crt_switch_resolution, video_info.crt_switch_center_adjust, video_info.monitor_index);
+ }
+ else if (!video_info.crt_switch_resolution)
+ video_driver_crt_switching_active = false;
+
+ /* trigger set resolution*/
+}
+
+void crt_switch_driver_reinit(void)
+{
+ video_driver_reinit();
+}
+
+void video_driver_display_type_set(enum rarch_display_type type)
+{
+ video_driver_display_type = type;
+}
+
+uintptr_t video_driver_display_get(void)
+{
+ return video_driver_display;
+}
+
+void video_driver_display_set(uintptr_t idx)
+{
+ video_driver_display = idx;
+}
+
+enum rarch_display_type video_driver_display_type_get(void)
+{
+ return video_driver_display_type;
+}
+
+void video_driver_window_set(uintptr_t idx)
+{
+ video_driver_window = idx;
+}
+
+uintptr_t video_driver_window_get(void)
+{
+ return video_driver_window;
+}
+
+bool video_driver_texture_load(void *data,
+ enum texture_filter_type filter_type,
+ uintptr_t *id)
+{
+ if (!id || !video_driver_poke || !video_driver_poke->load_texture)
+ return false;
+
+ *id = video_driver_poke->load_texture(video_driver_data, data,
+ video_driver_is_threaded_internal(),
+ filter_type);
+
+ return true;
+}
+
+bool video_driver_texture_unload(uintptr_t *id)
+{
+ if (!video_driver_poke || !video_driver_poke->unload_texture)
+ return false;
+
+ video_driver_poke->unload_texture(video_driver_data, *id);
+ *id = 0;
+ return true;
+}
+
+static void video_shader_driver_use_null(void *data,
+ void *shader_data, unsigned idx, bool set_active)
+{
+ (void)data;
+ (void)idx;
+ (void)set_active;
+}
+
+static bool video_driver_cb_set_coords(void *handle_data,
+ void *shader_data, const struct video_coords *coords)
+{
+ video_shader_ctx_coords_t ctx_coords;
+ ctx_coords.handle_data = handle_data;
+ ctx_coords.data = coords;
+
+ video_driver_set_coords(&ctx_coords);
+ return true;
+}
+
+void video_driver_build_info(video_frame_info_t *video_info)
+{
+ bool is_perfcnt_enable = false;
+ bool is_paused = false;
+ bool is_idle = false;
+ bool is_slowmotion = false;
+ settings_t *settings = NULL;
+ video_viewport_t *custom_vp = NULL;
+ struct retro_hw_render_callback *hwr =
+ video_driver_get_hw_context();
+#ifdef HAVE_THREADS
+ bool is_threaded = video_driver_is_threaded_internal();
+ video_driver_threaded_lock(is_threaded);
+#endif
+ settings = config_get_ptr();
+ custom_vp = &settings->video_viewport_custom;
+ video_info->refresh_rate = settings->floats.video_refresh_rate;
+ video_info->crt_switch_resolution = settings->uints.crt_switch_resolution;
+ video_info->crt_switch_resolution_super = settings->uints.crt_switch_resolution_super;
+ video_info->crt_switch_center_adjust = settings->ints.crt_switch_center_adjust;
+ video_info->black_frame_insertion = settings->bools.video_black_frame_insertion;
+ video_info->hard_sync = settings->bools.video_hard_sync;
+ video_info->hard_sync_frames = settings->uints.video_hard_sync_frames;
+ video_info->fps_show = settings->bools.video_fps_show;
+ video_info->statistics_show = settings->bools.video_statistics_show;
+ video_info->framecount_show = settings->bools.video_framecount_show;
+ video_info->scale_integer = settings->bools.video_scale_integer;
+ video_info->aspect_ratio_idx = settings->uints.video_aspect_ratio_idx;
+ video_info->post_filter_record = settings->bools.video_post_filter_record;
+ video_info->input_menu_swap_ok_cancel_buttons = settings->bools.input_menu_swap_ok_cancel_buttons;
+ video_info->max_swapchain_images = settings->uints.video_max_swapchain_images;
+ video_info->windowed_fullscreen = settings->bools.video_windowed_fullscreen;
+ video_info->fullscreen = settings->bools.video_fullscreen || retroarch_is_forced_fullscreen();
+ video_info->monitor_index = settings->uints.video_monitor_index;
+ video_info->shared_context = settings->bools.video_shared_context;
+
+ if (libretro_get_shared_context() && hwr && hwr->context_type != RETRO_HW_CONTEXT_NONE)
+ video_info->shared_context = true;
+
+ video_info->font_enable = settings->bools.video_font_enable;
+ video_info->font_msg_pos_x = settings->floats.video_msg_pos_x;
+ video_info->font_msg_pos_y = settings->floats.video_msg_pos_y;
+ video_info->font_msg_color_r = settings->floats.video_msg_color_r;
+ video_info->font_msg_color_g = settings->floats.video_msg_color_g;
+ video_info->font_msg_color_b = settings->floats.video_msg_color_b;
+ video_info->custom_vp_x = custom_vp->x;
+ video_info->custom_vp_y = custom_vp->y;
+ video_info->custom_vp_width = custom_vp->width;
+ video_info->custom_vp_height = custom_vp->height;
+ video_info->custom_vp_full_width = custom_vp->full_width;
+ video_info->custom_vp_full_height = custom_vp->full_height;
+
+ video_info->fps_text[0] = '\0';
+
+ video_info->width = video_driver_width;
+ video_info->height = video_driver_height;
+
+ video_info->use_rgba = video_driver_use_rgba;
+
+ video_info->libretro_running = false;
+ video_info->msg_bgcolor_enable = settings->bools.video_msg_bgcolor_enable;
+
+#ifdef HAVE_MENU
+ video_info->menu_is_alive = menu_driver_is_alive();
+ video_info->menu_footer_opacity = settings->floats.menu_footer_opacity;
+ video_info->menu_header_opacity = settings->floats.menu_header_opacity;
+ video_info->materialui_color_theme = settings->uints.menu_materialui_color_theme;
+ video_info->ozone_color_theme = settings->uints.menu_ozone_color_theme;
+ video_info->menu_shader_pipeline = settings->uints.menu_xmb_shader_pipeline;
+ video_info->xmb_theme = settings->uints.menu_xmb_theme;
+ video_info->xmb_color_theme = settings->uints.menu_xmb_color_theme;
+ video_info->timedate_enable = settings->bools.menu_timedate_enable;
+ video_info->battery_level_enable = settings->bools.menu_battery_level_enable;
+ video_info->xmb_shadows_enable = settings->bools.menu_xmb_shadows_enable;
+ video_info->xmb_alpha_factor = settings->uints.menu_xmb_alpha_factor;
+ video_info->menu_wallpaper_opacity = settings->floats.menu_wallpaper_opacity;
+ video_info->menu_framebuffer_opacity = settings->floats.menu_framebuffer_opacity;
+
+ video_info->libretro_running = core_is_game_loaded();
+#else
+ video_info->menu_is_alive = false;
+ video_info->menu_footer_opacity = 0.0f;
+ video_info->menu_header_opacity = 0.0f;
+ video_info->materialui_color_theme = 0;
+ video_info->menu_shader_pipeline = 0;
+ video_info->xmb_color_theme = 0;
+ video_info->xmb_theme = 0;
+ video_info->timedate_enable = false;
+ video_info->battery_level_enable = false;
+ video_info->xmb_shadows_enable = false;
+ video_info->xmb_alpha_factor = 0.0f;
+ video_info->menu_framebuffer_opacity = 0.0f;
+ video_info->menu_wallpaper_opacity = 0.0f;
+#endif
+
+ runloop_get_status(&is_paused, &is_idle, &is_slowmotion, &is_perfcnt_enable);
+
+ video_info->is_perfcnt_enable = is_perfcnt_enable;
+ video_info->runloop_is_paused = is_paused;
+ video_info->runloop_is_idle = is_idle;
+ video_info->runloop_is_slowmotion = is_slowmotion;
+
+ video_info->input_driver_nonblock_state = input_driver_is_nonblock_state();
+
+ video_info->context_data = video_context_data;
+ video_info->shader_driver = current_shader;
+ video_info->shader_data = current_shader_data;
+
+ video_info->cb_update_window_title = current_video_context.update_window_title;
+ video_info->cb_swap_buffers = current_video_context.swap_buffers;
+ video_info->cb_get_metrics = current_video_context.get_metrics;
+ video_info->cb_set_resize = current_video_context.set_resize;
+
+ video_info->cb_set_mvp = video_driver_cb_shader_set_mvp;
+
+ video_info->userdata = video_driver_get_ptr(false);
+
+#ifdef HAVE_THREADS
+ video_driver_threaded_unlock(is_threaded);
+#endif
+}
+
+/**
+ * video_driver_translate_coord_viewport:
+ * @mouse_x : Pointer X coordinate.
+ * @mouse_y : Pointer Y coordinate.
+ * @res_x : Scaled X coordinate.
+ * @res_y : Scaled Y coordinate.
+ * @res_screen_x : Scaled screen X coordinate.
+ * @res_screen_y : Scaled screen Y coordinate.
+ *
+ * Translates pointer [X,Y] coordinates into scaled screen
+ * coordinates based on viewport info.
+ *
+ * Returns: true (1) if successful, false if video driver doesn't support
+ * viewport info.
+ **/
+bool video_driver_translate_coord_viewport(
+ struct video_viewport *vp,
+ int mouse_x, int mouse_y,
+ int16_t *res_x, int16_t *res_y,
+ int16_t *res_screen_x, int16_t *res_screen_y)
+{
+ int scaled_screen_x, scaled_screen_y, scaled_x, scaled_y;
+ int norm_vp_width = (int)vp->width;
+ int norm_vp_height = (int)vp->height;
+ int norm_full_vp_width = (int)vp->full_width;
+ int norm_full_vp_height = (int)vp->full_height;
+
+ if (norm_full_vp_width <= 0 || norm_full_vp_height <= 0)
+ return false;
+
+ if (mouse_x >= 0 && mouse_x <= norm_full_vp_width)
+ scaled_screen_x = ((2 * mouse_x * 0x7fff)
+ / norm_full_vp_width) - 0x7fff;
+ else
+ scaled_screen_x = -0x8000; /* OOB */
+
+ if (mouse_y >= 0 && mouse_y <= norm_full_vp_height)
+ scaled_screen_y = ((2 * mouse_y * 0x7fff)
+ / norm_full_vp_height) - 0x7fff;
+ else
+ scaled_screen_y = -0x8000; /* OOB */
+
+ mouse_x -= vp->x;
+ mouse_y -= vp->y;
+
+ if (mouse_x >= 0 && mouse_x <= norm_vp_width)
+ scaled_x = ((2 * mouse_x * 0x7fff)
+ / norm_vp_width) - 0x7fff;
+ else
+ scaled_x = -0x8000; /* OOB */
+
+ if (mouse_y >= 0 && mouse_y <= norm_vp_height)
+ scaled_y = ((2 * mouse_y * 0x7fff)
+ / norm_vp_height) - 0x7fff;
+ else
+ scaled_y = -0x8000; /* OOB */
+
+ *res_x = scaled_x;
+ *res_y = scaled_y;
+ *res_screen_x = scaled_screen_x;
+ *res_screen_y = scaled_screen_y;
+
+ return true;
+}
+
+void video_driver_get_window_title(char *buf, unsigned len)
+{
+ if (buf && video_driver_window_title_update)
+ {
+ strlcpy(buf, video_driver_window_title, len);
+ video_driver_window_title_update = false;
+ }
+}
+
+void video_driver_get_status(uint64_t *frame_count, bool * is_alive,
+ bool *is_focused)
+{
+ *frame_count = video_driver_frame_count;
+ *is_alive = current_video ?
+ current_video->alive(video_driver_data) : true;
+ *is_focused = video_driver_cb_has_focus();
+}
+
+/**
+ * find_video_context_driver_driver_index:
+ * @ident : Identifier of resampler driver to find.
+ *
+ * Finds graphics context driver index by @ident name.
+ *
+ * Returns: graphics context driver index if driver was found, otherwise
+ * -1.
+ **/
+static int find_video_context_driver_index(const char *ident)
+{
+ unsigned i;
+ for (i = 0; gfx_ctx_drivers[i]; i++)
+ if (string_is_equal_noncase(ident, gfx_ctx_drivers[i]->ident))
+ return i;
+ return -1;
+}
+
+/**
+ * find_prev_context_driver:
+ *
+ * Finds previous driver in graphics context driver array.
+ **/
+bool video_context_driver_find_prev_driver(void)
+{
+ settings_t *settings = config_get_ptr();
+ int i = find_video_context_driver_index(
+ settings->arrays.video_context_driver);
+
+ if (i > 0)
+ {
+ strlcpy(settings->arrays.video_context_driver,
+ gfx_ctx_drivers[i - 1]->ident,
+ sizeof(settings->arrays.video_context_driver));
+ return true;
+ }
+
+ RARCH_WARN("Couldn't find any previous video context driver.\n");
+ return false;
+}
+
+/**
+ * find_next_context_driver:
+ *
+ * Finds next driver in graphics context driver array.
+ **/
+bool video_context_driver_find_next_driver(void)
+{
+ settings_t *settings = config_get_ptr();
+ int i = find_video_context_driver_index(
+ settings->arrays.video_context_driver);
+
+ if (i >= 0 && gfx_ctx_drivers[i + 1])
+ {
+ strlcpy(settings->arrays.video_context_driver,
+ gfx_ctx_drivers[i + 1]->ident,
+ sizeof(settings->arrays.video_context_driver));
+ return true;
+ }
+
+ RARCH_WARN("Couldn't find any next video context driver.\n");
+ return false;
+}
+
+/**
+ * video_context_driver_init:
+ * @data : Input data.
+ * @ctx : Graphics context driver to initialize.
+ * @ident : Identifier of graphics context driver to find.
+ * @api : API of higher-level graphics API.
+ * @major : Major version number of higher-level graphics API.
+ * @minor : Minor version number of higher-level graphics API.
+ * @hw_render_ctx : Request a graphics context driver capable of
+ * hardware rendering?
+ *
+ * Initialize graphics context driver.
+ *
+ * Returns: graphics context driver if successfully initialized,
+ * otherwise NULL.
+ **/
+static const gfx_ctx_driver_t *video_context_driver_init(
+ void *data,
+ const gfx_ctx_driver_t *ctx,
+ const char *ident,
+ enum gfx_ctx_api api, unsigned major,
+ unsigned minor, bool hw_render_ctx,
+ void **ctx_data)
+{
+ video_frame_info_t video_info;
+
+ if (!ctx->bind_api(data, api, major, minor))
+ {
+ RARCH_WARN("Failed to bind API (#%u, version %u.%u)"
+ " on context driver \"%s\".\n",
+ (unsigned)api, major, minor, ctx->ident);
+
+ return NULL;
+ }
+
+ video_driver_build_info(&video_info);
+
+ if (!(*ctx_data = ctx->init(&video_info, data)))
+ return NULL;
+
+ if (ctx->bind_hw_render)
+ ctx->bind_hw_render(*ctx_data,
+ video_info.shared_context && hw_render_ctx);
+
+ return ctx;
+}
+
+/**
+ * video_context_driver_init_first:
+ * @data : Input data.
+ * @ident : Identifier of graphics context driver to find.
+ * @api : API of higher-level graphics API.
+ * @major : Major version number of higher-level graphics API.
+ * @minor : Minor version number of higher-level graphics API.
+ * @hw_render_ctx : Request a graphics context driver capable of
+ * hardware rendering?
+ *
+ * Finds first suitable graphics context driver and initializes.
+ *
+ * Returns: graphics context driver if found, otherwise NULL.
+ **/
+const gfx_ctx_driver_t *video_context_driver_init_first(void *data,
+ const char *ident, enum gfx_ctx_api api, unsigned major,
+ unsigned minor, bool hw_render_ctx, void **ctx_data)
+{
+ int i = find_video_context_driver_index(ident);
+
+ if (i >= 0)
+ {
+ const gfx_ctx_driver_t *ctx = video_context_driver_init(data, gfx_ctx_drivers[i], ident,
+ api, major, minor, hw_render_ctx, ctx_data);
+ if (ctx)
+ {
+ video_context_data = *ctx_data;
+ return ctx;
+ }
+ }
+
+ for (i = 0; gfx_ctx_drivers[i]; i++)
+ {
+ const gfx_ctx_driver_t *ctx =
+ video_context_driver_init(data, gfx_ctx_drivers[i], ident,
+ api, major, minor, hw_render_ctx, ctx_data);
+
+ if (ctx)
+ {
+ video_context_data = *ctx_data;
+ return ctx;
+ }
+ }
+
+ return NULL;
+}
+
+bool video_context_driver_init_image_buffer(const video_info_t *data)
+{
+ if (
+ current_video_context.image_buffer_init
+ && current_video_context.image_buffer_init(
+ video_context_data, data))
+ return true;
+ return false;
+}
+
+bool video_context_driver_write_to_image_buffer(gfx_ctx_image_t *img)
+{
+ if (
+ current_video_context.image_buffer_write
+ && current_video_context.image_buffer_write(video_context_data,
+ img->frame, img->width, img->height, img->pitch,
+ img->rgb32, img->index, img->handle))
+ return true;
+ return false;
+}
+
+bool video_context_driver_get_video_output_prev(void)
+{
+ if (!current_video_context.get_video_output_prev)
+ return false;
+ current_video_context.get_video_output_prev(video_context_data);
+ return true;
+}
+
+bool video_context_driver_get_video_output_next(void)
+{
+ if (!current_video_context.get_video_output_next)
+ return false;
+ current_video_context.get_video_output_next(video_context_data);
+ return true;
+}
+
+void video_context_driver_make_current(bool release)
+{
+ if (current_video_context.make_current)
+ current_video_context.make_current(release);
+}
+
+bool video_context_driver_translate_aspect(gfx_ctx_aspect_t *aspect)
+{
+ if (!video_context_data || !aspect)
+ return false;
+ if (!current_video_context.translate_aspect)
+ return false;
+ *aspect->aspect = current_video_context.translate_aspect(
+ video_context_data, aspect->width, aspect->height);
+ return true;
+}
+
+void video_context_driver_free(void)
+{
+ if (current_video_context.destroy)
+ current_video_context.destroy(video_context_data);
+ video_context_driver_destroy();
+ video_context_data = NULL;
+}
+
+bool video_context_driver_get_video_output_size(gfx_ctx_size_t *size_data)
+{
+ if (!size_data)
+ return false;
+ if (!current_video_context.get_video_output_size)
+ return false;
+ current_video_context.get_video_output_size(video_context_data,
+ size_data->width, size_data->height);
+ return true;
+}
+
+bool video_context_driver_swap_interval(int *interval)
+{
+ gfx_ctx_flags_t flags;
+ int current_interval = *interval;
+ settings_t *settings = config_get_ptr();
+ bool adaptive_vsync_enabled = video_driver_get_all_flags(&flags, GFX_CTX_FLAGS_ADAPTIVE_VSYNC) && settings->bools.video_adaptive_vsync;
+
+ if (!current_video_context.swap_interval)
+ return false;
+ if (adaptive_vsync_enabled && current_interval == 1)
+ current_interval = -1;
+ current_video_context.swap_interval(video_context_data, current_interval);
+ return true;
+}
+
+bool video_context_driver_get_proc_address(gfx_ctx_proc_address_t *proc)
+{
+ if (!current_video_context.get_proc_address)
+ return false;
+
+ proc->addr = current_video_context.get_proc_address(proc->sym);
+
+ return true;
+}
+
+bool video_context_driver_get_metrics(gfx_ctx_metrics_t *metrics)
+{
+ if (
+ current_video_context.get_metrics(video_context_data,
+ metrics->type,
+ metrics->value))
+ return true;
+ return false;
+}
+
+bool video_context_driver_get_refresh_rate(float *refresh_rate)
+{
+ float refresh_holder = 0;
+
+ if (!current_video_context.get_refresh_rate || !refresh_rate)
+ return false;
+ if (!video_context_data)
+ return false;
+
+ if (!video_driver_crt_switching_active)
+ if (refresh_rate)
+ *refresh_rate =
+ current_video_context.get_refresh_rate(video_context_data);
+
+ if (video_driver_crt_switching_active)
+ {
+ if (refresh_rate)
+ refresh_holder =
+ current_video_context.get_refresh_rate(video_context_data);
+ if (refresh_holder != video_driver_core_hz) /* Fix for incorrect interlace detsction -- HARD SET VSNC TO REQUIRED REFRESH FOR CRT*/
+ *refresh_rate = video_driver_core_hz;
+ }
+
+ return true;
+}
+
+bool video_context_driver_input_driver(gfx_ctx_input_t *inp)
+{
+ settings_t *settings = config_get_ptr();
+ const char *joypad_name = settings ?
+ settings->arrays.input_joypad_driver : NULL;
+
+ if (!current_video_context.input_driver)
+ return false;
+ current_video_context.input_driver(
+ video_context_data, joypad_name,
+ inp->input, inp->input_data);
+ return true;
+}
+
+bool video_context_driver_suppress_screensaver(bool *bool_data)
+{
+ if ( video_context_data
+ && current_video_context.suppress_screensaver(
+ video_context_data, *bool_data))
+ return true;
+ return false;
+}
+
+bool video_context_driver_get_ident(gfx_ctx_ident_t *ident)
+{
+ if (!ident)
+ return false;
+ ident->ident = current_video_context.ident;
+ return true;
+}
+
+bool video_context_driver_set_video_mode(gfx_ctx_mode_t *mode_info)
+{
+ video_frame_info_t video_info;
+
+ if (!current_video_context.set_video_mode)
+ return false;
+
+ video_driver_build_info(&video_info);
+
+ if (!current_video_context.set_video_mode(
+ video_context_data, &video_info, mode_info->width,
+ mode_info->height, mode_info->fullscreen))
+ return false;
+ return true;
+}
+
+bool video_context_driver_get_video_size(gfx_ctx_mode_t *mode_info)
+{
+ if (!current_video_context.get_video_size)
+ return false;
+ current_video_context.get_video_size(video_context_data,
+ &mode_info->width, &mode_info->height);
+ return true;
+}
+
+bool video_context_driver_show_mouse(bool *bool_data)
+{
+ if (!current_video_context.show_mouse)
+ return false;
+ current_video_context.show_mouse(video_context_data, *bool_data);
+ return true;
+}
+
+static bool video_context_driver_get_flags(gfx_ctx_flags_t *flags)
+{
+ if (!current_video_context.get_flags)
+ return false;
+
+ if (deferred_video_context_driver_set_flags)
+ {
+ flags->flags = deferred_flag_data.flags;
+ deferred_video_context_driver_set_flags = false;
+ return true;
+ }
+
+ flags->flags = current_video_context.get_flags(video_context_data);
+ return true;
+}
+
+static bool video_driver_get_flags(gfx_ctx_flags_t *flags)
+{
+ if (!video_driver_poke || !video_driver_poke->get_flags)
+ return false;
+ flags->flags = video_driver_poke->get_flags(video_driver_data);
+ return true;
+}
+
+bool video_driver_get_all_flags(gfx_ctx_flags_t *flags, enum display_flags flag)
+{
+ if (!flags)
+ return false;
+
+ if (video_driver_get_flags(flags))
+ if (BIT32_GET(flags->flags, flag))
+ return true;
+
+ flags->flags = 0;
+
+ if (video_context_driver_get_flags(flags))
+ if (BIT32_GET(flags->flags, flag))
+ return true;
+
+ return false;
+}
+
+bool video_context_driver_set_flags(gfx_ctx_flags_t *flags)
+{
+ if (!flags)
+ return false;
+ if (!current_video_context.set_flags)
+ {
+ deferred_flag_data.flags = flags->flags;
+ deferred_video_context_driver_set_flags = true;
+ return false;
+ }
+
+ current_video_context.set_flags(video_context_data, flags->flags);
+ return true;
+}
+
+enum gfx_ctx_api video_context_driver_get_api(void)
+{
+ enum gfx_ctx_api ctx_api = video_context_data ?
+ current_video_context.get_api(video_context_data) : GFX_CTX_NONE;
+
+ if (ctx_api == GFX_CTX_NONE)
+ {
+ const char *video_driver = video_driver_get_ident();
+ if (string_is_equal(video_driver, "d3d9"))
+ return GFX_CTX_DIRECT3D9_API;
+ else if (string_is_equal(video_driver, "d3d10"))
+ return GFX_CTX_DIRECT3D10_API;
+ else if (string_is_equal(video_driver, "d3d11"))
+ return GFX_CTX_DIRECT3D11_API;
+ else if (string_is_equal(video_driver, "d3d12"))
+ return GFX_CTX_DIRECT3D12_API;
+ else if (string_is_equal(video_driver, "gx2"))
+ return GFX_CTX_GX2_API;
+ else if (string_is_equal(video_driver, "gx"))
+ return GFX_CTX_GX_API;
+ else if (string_is_equal(video_driver, "gl"))
+ return GFX_CTX_OPENGL_API;
+ else if (string_is_equal(video_driver, "vulkan"))
+ return GFX_CTX_VULKAN_API;
+ else if (string_is_equal(video_driver, "metal"))
+ return GFX_CTX_METAL_API;
+
+ return GFX_CTX_NONE;
+ }
+
+ return ctx_api;
+}
+
+bool video_driver_has_windowed(void)
+{
+#if !(defined(RARCH_CONSOLE) || defined(RARCH_MOBILE))
+ if (video_driver_data && current_video->has_windowed)
+ return current_video->has_windowed(video_driver_data);
+ else if (video_context_data && current_video_context.has_windowed)
+ return current_video_context.has_windowed(video_context_data);
+#endif
+ return false;
+}
+
+bool video_driver_cached_frame_has_valid_framebuffer(void)
+{
+ if (frame_cache_data)
+ return (frame_cache_data == RETRO_HW_FRAME_BUFFER_VALID);
+ return false;
+}
+
+static const shader_backend_t *video_shader_set_backend(
+ enum rarch_shader_type type)
+{
+ switch (type)
+ {
+ case RARCH_SHADER_CG:
+ {
+#ifdef HAVE_CG
+ gfx_ctx_flags_t flags;
+ flags.flags = 0;
+ video_context_driver_get_flags(&flags);
+
+ if (BIT32_GET(flags.flags, GFX_CTX_FLAGS_GL_CORE_CONTEXT))
+ {
+ RARCH_ERR("[Shader driver]: Cg cannot be used with core"
+ " GL context. Trying to fall back to GLSL...\n");
+ return video_shader_set_backend(RARCH_SHADER_GLSL);
+ }
+
+ RARCH_LOG("[Shader driver]: Using Cg shader backend.\n");
+ return &gl_cg_backend;
+#else
+ break;
+#endif
+ }
+ case RARCH_SHADER_GLSL:
+#ifdef HAVE_GLSL
+ RARCH_LOG("[Shader driver]: Using GLSL shader backend.\n");
+ return &gl_glsl_backend;
+#else
+ break;
+#endif
+ case RARCH_SHADER_HLSL:
+ case RARCH_SHADER_NONE:
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+void video_shader_driver_use(video_shader_ctx_info_t *shader_info)
+{
+ if (current_shader && current_shader->use)
+ current_shader->use(shader_info->data, current_shader_data,
+ shader_info->idx, shader_info->set_active);
+}
+
+void video_shader_driver_set_parameter(struct uniform_info *param)
+{
+ if (current_shader && current_shader->set_uniform_parameter)
+ current_shader->set_uniform_parameter(current_shader_data,
+ param, NULL);
+}
+
+void video_shader_driver_set_parameters(video_shader_ctx_params_t *params)
+{
+ if (current_shader && current_shader->set_params)
+ current_shader->set_params(params, current_shader_data);
+}
+
+bool video_shader_driver_get_prev_textures(
+ video_shader_ctx_texture_t *texture)
+{
+ if (!texture || !current_shader)
+ return false;
+ texture->id = current_shader->get_prev_textures(current_shader_data);
+
+ return true;
+}
+
+bool video_shader_driver_get_ident(video_shader_ctx_ident_t *ident)
+{
+ if (!ident || !current_shader)
+ return false;
+ ident->ident = current_shader->ident;
+ return true;
+}
+
+bool video_shader_driver_get_current_shader(video_shader_ctx_t *shader)
+{
+ void *video_driver = video_driver_get_ptr(true);
+ const video_poke_interface_t *video_poke = video_driver_get_poke();
+
+ shader->data = NULL;
+ if (!video_poke || !video_driver || !video_poke->get_current_shader)
+ return false;
+ shader->data = video_poke->get_current_shader(video_driver);
+ return true;
+}
+
+bool video_shader_driver_direct_get_current_shader(
+ video_shader_ctx_t *shader)
+{
+ if (!current_shader)
+ return false;
+
+ shader->data = current_shader->get_current_shader(current_shader_data);
+
+ return true;
+}
+
+bool video_shader_driver_deinit(void)
+{
+ if (!current_shader)
+ return false;
+
+ if (current_shader->deinit)
+ current_shader->deinit(current_shader_data);
+
+ current_shader_data = NULL;
+ current_shader = NULL;
+ return true;
+}
+
+static enum gfx_wrap_type video_shader_driver_wrap_type_null(
+ void *data, unsigned index)
+{
+ return RARCH_WRAP_BORDER;
+}
+
+static bool video_driver_cb_set_mvp(void *data,
+ void *shader_data, const void *mat_data)
+{
+ video_shader_ctx_mvp_t mvp;
+ mvp.data = data;
+ mvp.matrix = mat_data;
+
+ video_driver_set_mvp(&mvp);
+ return true;
+}
+
+static struct video_shader *
+video_shader_driver_get_current_shader_null(void *data)
+{
+ return NULL;
+}
+
+static void video_shader_driver_set_params_null(
+ void *data, void *shader_data)
+{
+}
+
+static void video_shader_driver_scale_null(void *data,
+ unsigned idx, struct gfx_fbo_scale *scale)
+{
+ (void)idx;
+ (void)scale;
+}
+
+static bool video_shader_driver_mipmap_input_null(
+ void *data, unsigned idx)
+{
+ (void)idx;
+ return false;
+}
+
+static bool video_shader_driver_filter_type_null(
+ void *data, unsigned idx, bool *smooth)
+{
+ (void)idx;
+ (void)smooth;
+ return false;
+}
+
+static unsigned video_shader_driver_num_null(void *data)
+{
+ return 0;
+}
+
+static bool video_shader_driver_get_feedback_pass_null(
+ void *data, unsigned *idx)
+{
+ (void)idx;
+ return false;
+}
+
+static void video_shader_driver_reset_to_defaults(void)
+{
+ if (!current_shader)
+ return;
+
+ if (!current_shader->wrap_type)
+ current_shader->wrap_type = video_shader_driver_wrap_type_null;
+ if (current_shader->set_mvp)
+ video_driver_cb_shader_set_mvp = current_shader->set_mvp;
+ else
+ {
+ current_shader->set_mvp = video_driver_cb_set_mvp;
+ video_driver_cb_shader_set_mvp = video_driver_cb_set_mvp;
+ }
+ if (!current_shader->set_coords)
+ current_shader->set_coords = video_driver_cb_set_coords;
+
+ if (current_shader->use)
+ video_driver_cb_shader_use = current_shader->use;
+ else
+ {
+ current_shader->use = video_shader_driver_use_null;
+ video_driver_cb_shader_use = video_shader_driver_use_null;
+ }
+ if (!current_shader->set_params)
+ current_shader->set_params = video_shader_driver_set_params_null;
+ if (!current_shader->shader_scale)
+ current_shader->shader_scale = video_shader_driver_scale_null;
+ if (!current_shader->mipmap_input)
+ current_shader->mipmap_input = video_shader_driver_mipmap_input_null;
+ if (!current_shader->filter_type)
+ current_shader->filter_type = video_shader_driver_filter_type_null;
+ if (!current_shader->num_shaders)
+ current_shader->num_shaders = video_shader_driver_num_null;
+ if (!current_shader->get_current_shader)
+ current_shader->get_current_shader= video_shader_driver_get_current_shader_null;
+ if (!current_shader->get_feedback_pass)
+ current_shader->get_feedback_pass = video_shader_driver_get_feedback_pass_null;
+}
+
+/* Finds first suitable shader context driver. */
+bool video_shader_driver_init_first(void)
+{
+ current_shader = (shader_backend_t*)shader_ctx_drivers[0];
+ video_shader_driver_reset_to_defaults();
+ return true;
+}
+
+bool video_shader_driver_init(video_shader_ctx_init_t *init)
+{
+ void *tmp = NULL;
+ settings_t *settings = config_get_ptr();
+
+ if (!init->shader || !init->shader->init)
+ {
+ init->shader = video_shader_set_backend(init->shader_type);
+
+ if (!init->shader)
+ return false;
+ }
+
+ tmp = init->shader->init(init->data, init->path);
+
+ if (!tmp)
+ return false;
+
+ if (string_is_equal(settings->arrays.menu_driver, "xmb")
+ && init->shader->init_menu_shaders)
+ {
+ RARCH_LOG("Setting up menu pipeline shaders for XMB ... \n");
+ init->shader->init_menu_shaders(tmp);
+ }
+
+ current_shader_data = tmp;
+
+ RARCH_LOG("Resetting shader to defaults ... \n");
+
+ current_shader = (shader_backend_t*)init->shader;
+ video_shader_driver_reset_to_defaults();
+
+ return true;
+}
+
+bool video_shader_driver_get_feedback_pass(unsigned *data)
+{
+ return current_shader->get_feedback_pass(current_shader_data, data);
+}
+
+bool video_shader_driver_mipmap_input(unsigned *index)
+{
+ return current_shader->mipmap_input(current_shader_data, *index);
+}
+
+bool video_shader_driver_scale(video_shader_ctx_scale_t *scaler)
+{
+ if (!scaler || !scaler->scale)
+ return false;
+
+ scaler->scale->valid = false;
+
+ current_shader->shader_scale(current_shader_data,
+ scaler->idx, scaler->scale);
+ return true;
+}
+
+bool video_shader_driver_info(video_shader_ctx_info_t *shader_info)
+{
+ if (!shader_info)
+ return false;
+
+ shader_info->num = current_shader->num_shaders(current_shader_data);
+
+ return true;
+}
+
+bool video_shader_driver_filter_type(video_shader_ctx_filter_t *filter)
+{
+ if (filter)
+ return current_shader->filter_type(current_shader_data,
+ filter->index, filter->smooth);
+ return false;
+}
+
+bool video_shader_driver_compile_program(
+ struct shader_program_info *program_info)
+{
+ if (program_info)
+ return current_shader->compile_program(program_info->data,
+ program_info->idx, NULL, program_info);
+ return false;
+}
+
+bool video_shader_driver_wrap_type(video_shader_ctx_wrap_t *wrap)
+{
+ wrap->type = current_shader->wrap_type(
+ current_shader_data, wrap->idx);
+ return true;
+}
+
+void video_driver_set_coords(video_shader_ctx_coords_t *coords)
+{
+ if (current_shader && current_shader->set_coords)
+ current_shader->set_coords(coords->handle_data,
+ current_shader_data,
+ (const struct video_coords*)coords->data);
+ else
+ {
+ if (video_driver_poke && video_driver_poke->set_coords)
+ video_driver_poke->set_coords(coords->handle_data,
+ current_shader_data,
+ (const struct video_coords*)coords->data);
+ }
+}
+
+void video_driver_set_mvp(video_shader_ctx_mvp_t *mvp)
+{
+ if (!mvp || !mvp->matrix)
+ return;
+
+ if (current_shader && current_shader->set_mvp)
+ current_shader->set_mvp(mvp->data,
+ current_shader_data, mvp->matrix);
+ else
+ {
+ if (video_driver_poke && video_driver_poke->set_mvp)
+ video_driver_poke->set_mvp(mvp->data,
+ current_shader_data, mvp->matrix);
+ }
+}
+
+float video_driver_get_refresh_rate(void)
+{
+ if (video_driver_poke && video_driver_poke->get_refresh_rate)
+ return video_driver_poke->get_refresh_rate(video_driver_data);
+
+ return 0.0f;
+}
diff --git a/gfx/video_driver.h b/gfx/video_driver.h
index eea9ef14e4..2e5340ffbf 100644
--- a/gfx/video_driver.h
+++ b/gfx/video_driver.h
@@ -1,1308 +1,1310 @@
-/* RetroArch - A frontend for libretro.
- * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
- * Copyright (C) 2011-2017 - Daniel De Matteis
- *
- * RetroArch is free software: you can redistribute it and/or modify it under the terms
- * of the GNU General Public License as published by the Free Software Found-
- * ation, either version 3 of the License, or (at your option) any later version.
- *
- * RetroArch 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with RetroArch.
- * If not, see .
- */
-
-#ifndef __VIDEO_DRIVER__H
-#define __VIDEO_DRIVER__H
-
-#include
-#include
-#include
-
-#include
-#include
-#include
-
-#ifdef HAVE_CONFIG_H
-#include "../config.h"
-#endif
-
-#ifdef HAVE_OVERLAY
-#include "../input/input_overlay.h"
-#endif
-
-#include "video_defines.h"
-#include "video_coord_array.h"
-#include "video_filter.h"
-#include "video_shader_parse.h"
-#include "video_state_tracker.h"
-
-#include "../input/input_driver.h"
-
-#define RARCH_SCALE_BASE 256
-
-#include "video_shader_parse.h"
-
-#define VIDEO_SHADER_STOCK_BLEND (GFX_MAX_SHADERS - 1)
-#define VIDEO_SHADER_MENU (GFX_MAX_SHADERS - 2)
-#define VIDEO_SHADER_MENU_2 (GFX_MAX_SHADERS - 3)
-#define VIDEO_SHADER_MENU_3 (GFX_MAX_SHADERS - 4)
-#define VIDEO_SHADER_MENU_4 (GFX_MAX_SHADERS - 5)
-#define VIDEO_SHADER_MENU_5 (GFX_MAX_SHADERS - 6)
-#define VIDEO_SHADER_MENU_6 (GFX_MAX_SHADERS - 7)
-
-#if defined(_XBOX360)
-#define DEFAULT_SHADER_TYPE RARCH_SHADER_HLSL
-#elif defined(__PSL1GHT__) || defined(HAVE_OPENGLES2) || defined(HAVE_GLSL)
-#define DEFAULT_SHADER_TYPE RARCH_SHADER_GLSL
-#elif defined(__CELLOS_LV2__) || defined(HAVE_CG)
-#define DEFAULT_SHADER_TYPE RARCH_SHADER_CG
-#else
-#define DEFAULT_SHADER_TYPE RARCH_SHADER_NONE
-#endif
-
-RETRO_BEGIN_DECLS
-
-#ifndef MAX_EGLIMAGE_TEXTURES
-#define MAX_EGLIMAGE_TEXTURES 32
-#endif
-
-#define MAX_VARIABLES 64
-
-enum
-{
- TEXTURES = 8,
- TEXTURESMASK = TEXTURES - 1
-};
-
-struct LinkInfo
-{
- unsigned tex_w, tex_h;
- struct video_shader_pass *pass;
-};
-
-enum gfx_ctx_api
-{
- GFX_CTX_NONE = 0,
- GFX_CTX_OPENGL_API,
- GFX_CTX_OPENGL_ES_API,
- GFX_CTX_DIRECT3D8_API,
- GFX_CTX_DIRECT3D9_API,
- GFX_CTX_DIRECT3D10_API,
- GFX_CTX_DIRECT3D11_API,
- GFX_CTX_DIRECT3D12_API,
- GFX_CTX_OPENVG_API,
- GFX_CTX_VULKAN_API,
- GFX_CTX_SIXEL_API,
- GFX_CTX_METAL_API,
- GFX_CTX_GDI_API,
- GFX_CTX_GX_API,
- GFX_CTX_GX2_API
-};
-
-enum display_metric_types
-{
- DISPLAY_METRIC_NONE = 0,
- DISPLAY_METRIC_MM_WIDTH,
- DISPLAY_METRIC_MM_HEIGHT,
- DISPLAY_METRIC_DPI
-};
-
-enum display_flags
-{
- GFX_CTX_FLAGS_NONE = 0,
- GFX_CTX_FLAGS_GL_CORE_CONTEXT,
- GFX_CTX_FLAGS_MULTISAMPLING,
- GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES,
- GFX_CTX_FLAGS_HARD_SYNC,
- GFX_CTX_FLAGS_BLACK_FRAME_INSERTION,
- GFX_CTX_FLAGS_MENU_FRAME_FILTERING,
- GFX_CTX_FLAGS_ADAPTIVE_VSYNC
-};
-
-enum shader_uniform_type
-{
- UNIFORM_1F = 0,
- UNIFORM_2F,
- UNIFORM_3F,
- UNIFORM_4F,
- UNIFORM_1FV,
- UNIFORM_2FV,
- UNIFORM_3FV,
- UNIFORM_4FV,
- UNIFORM_1I
-};
-
-enum shader_program_type
-{
- SHADER_PROGRAM_VERTEX = 0,
- SHADER_PROGRAM_FRAGMENT,
- SHADER_PROGRAM_COMBINED
-};
-
-struct shader_program_info
-{
- bool is_file;
- const char *vertex;
- const char *fragment;
- const char *combined;
- unsigned idx;
- void *data;
-};
-
-struct uniform_info
-{
- bool enabled;
-
- int32_t location;
- int32_t count;
- unsigned type; /* shader uniform type */
-
- struct
- {
- enum shader_program_type type;
- const char *ident;
- uint32_t idx;
- bool add_prefix;
- bool enable;
- } lookup;
-
- struct
- {
- struct
- {
- intptr_t v0;
- intptr_t v1;
- intptr_t v2;
- intptr_t v3;
- } integer;
-
- intptr_t *integerv;
-
- struct
- {
- uintptr_t v0;
- uintptr_t v1;
- uintptr_t v2;
- uintptr_t v3;
- } unsigned_integer;
-
- uintptr_t *unsigned_integerv;
-
- struct
- {
- float v0;
- float v1;
- float v2;
- float v3;
- } f;
-
- float *floatv;
- } result;
-};
-
-typedef struct shader_backend
-{
- void *(*init)(void *data, const char *path);
- void (*init_menu_shaders)(void *data);
- void (*deinit)(void *data);
-
- /* Set shader parameters. */
- void (*set_params)(void *data, void *shader_data);
-
- void (*set_uniform_parameter)(void *data, struct uniform_info *param,
- void *uniform_data);
-
- /* Compile a shader program. */
- bool (*compile_program)(void *data, unsigned idx,
- void *program_data, struct shader_program_info *program_info);
-
- /* Use a shader program specified by variable 'index'. */
- void (*use)(void *data, void *shader_data, unsigned index, bool set_active);
-
- /* Returns the number of currently loaded shaders. */
- unsigned (*num_shaders)(void *data);
-
- bool (*filter_type)(void *data, unsigned index, bool *smooth);
- enum gfx_wrap_type (*wrap_type)(void *data, unsigned index);
- void (*shader_scale)(void *data,
- unsigned index, struct gfx_fbo_scale *scale);
- bool (*set_coords)(void *handle_data,
- void *shader_data, const struct video_coords *coords);
- bool (*set_mvp)(void *data, void *shader_data,
- const void *mat_data);
- unsigned (*get_prev_textures)(void *data);
- bool (*get_feedback_pass)(void *data, unsigned *pass);
- bool (*mipmap_input)(void *data, unsigned index);
-
- struct video_shader *(*get_current_shader)(void *data);
-
- enum rarch_shader_type type;
-
- /* Human readable string. */
- const char *ident;
-} shader_backend_t;
-
-typedef struct video_shader_ctx_init
-{
- enum rarch_shader_type shader_type;
- const char *path;
- const shader_backend_t *shader;
- void *data;
- struct
- {
- bool core_context_enabled;
- } gl;
-} video_shader_ctx_init_t;
-
-typedef struct video_shader_ctx_params
-{
- unsigned width;
- unsigned height;
- unsigned tex_width;
- unsigned tex_height;
- unsigned out_width;
- unsigned out_height;
- unsigned frame_counter;
- unsigned fbo_info_cnt;
- void *data;
- const void *info;
- const void *prev_info;
- const void *feedback_info;
- const void *fbo_info;
-} video_shader_ctx_params_t;
-
-typedef struct video_shader_ctx_coords
-{
- void *handle_data;
- const void *data;
-} video_shader_ctx_coords_t;
-
-typedef struct video_shader_ctx_scale
-{
- unsigned idx;
- struct gfx_fbo_scale *scale;
-} video_shader_ctx_scale_t;
-
-typedef struct video_shader_ctx_info
-{
- bool set_active;
- unsigned num;
- unsigned idx;
- void *data;
-} video_shader_ctx_info_t;
-
-typedef struct video_shader_ctx_mvp
-{
- void *data;
- const void *matrix;
-} video_shader_ctx_mvp_t;
-
-typedef struct video_shader_ctx_filter
-{
- unsigned index;
- bool *smooth;
-} video_shader_ctx_filter_t;
-
-typedef struct video_shader_ctx_wrap
-{
- unsigned idx;
- enum gfx_wrap_type type;
-} video_shader_ctx_wrap_t;
-
-typedef struct video_shader_ctx
-{
- struct video_shader *data;
-} video_shader_ctx_t;
-
-typedef struct video_shader_ctx_ident
-{
- const char *ident;
-} video_shader_ctx_ident_t;
-
-typedef struct video_shader_ctx_texture
-{
- unsigned id;
-} video_shader_ctx_texture_t;
-
-typedef void (*gfx_ctx_proc_t)(void);
-
-typedef struct video_info
-{
- /* Launch in fullscreen mode instead of windowed mode. */
- bool fullscreen;
-
- /* Start with V-Sync enabled. */
- bool vsync;
-
- /* If true, the output image should have the aspect ratio
- * as set in aspect_ratio. */
- bool force_aspect;
-
- bool font_enable;
-
- /* Width of window.
- * If fullscreen mode is requested,
- * a width of 0 means the resolution of the
- * desktop should be used. */
- unsigned width;
-
- /* Height of window.
- * If fullscreen mode is requested,
- * a height of 0 means the resolutiof the desktop should be used.
- */
- unsigned height;
-
- int swap_interval;
-
-#ifdef GEKKO
- bool vfilter;
-#endif
-
- /* If true, applies bilinear filtering to the image,
- * otherwise nearest filtering. */
- bool smooth;
-
- bool is_threaded;
-
- /* Use 32bit RGBA rather than native RGB565/XBGR1555.
- *
- * XRGB1555 format is 16-bit and has byte ordering: 0RRRRRGGGGGBBBBB,
- * in native endian.
- *
- * ARGB8888 is AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB, native endian.
- * Alpha channel should be disregarded.
- * */
- bool rgb32;
-
-#ifdef GEKKO
- /* TODO - we can't really have driver system-specific
- * variables in here. There should be some
- * kind of publicly accessible driver implementation
- * video struct for specific things like this.
- */
-
- /* Wii-specific settings. Ignored for everything else. */
- unsigned viwidth;
-#endif
-
- /*
- * input_scale defines the maximum size of the picture that will
- * ever be used with the frame callback.
- *
- * The maximum resolution is a multiple of 256x256 size (RARCH_SCALE_BASE),
- * so an input scale of 2 means you should allocate a texture or of 512x512.
- *
- * Maximum input size: RARCH_SCALE_BASE * input_scale
- */
- unsigned input_scale;
-
- uintptr_t parent;
-} video_info_t;
-
-typedef struct video_frame_info
-{
- bool input_menu_swap_ok_cancel_buttons;
- bool input_driver_nonblock_state;
- bool shared_context;
- bool black_frame_insertion;
- bool hard_sync;
- bool fps_show;
- bool statistics_show;
- bool framecount_show;
- bool scale_integer;
- bool post_filter_record;
- bool windowed_fullscreen;
- bool fullscreen;
- bool font_enable;
- bool use_rgba;
- bool libretro_running;
- bool xmb_shadows_enable;
- bool battery_level_enable;
- bool timedate_enable;
- bool runloop_is_slowmotion;
- bool runloop_is_idle;
- bool runloop_is_paused;
- bool is_perfcnt_enable;
- bool menu_is_alive;
- bool msg_bgcolor_enable;
-
- int custom_vp_x;
- int custom_vp_y;
- int crt_switch_center_adjust;
-
- unsigned hard_sync_frames;
- unsigned aspect_ratio_idx;
- unsigned max_swapchain_images;
- unsigned monitor_index;
- unsigned crt_switch_resolution;
- unsigned crt_switch_resolution_super;
- unsigned width;
- unsigned height;
- unsigned xmb_theme;
- unsigned xmb_color_theme;
- unsigned menu_shader_pipeline;
- unsigned materialui_color_theme;
- unsigned ozone_color_theme;
- unsigned custom_vp_width;
- unsigned custom_vp_height;
- unsigned custom_vp_full_width;
- unsigned custom_vp_full_height;
-
- float menu_wallpaper_opacity;
- float menu_framebuffer_opacity;
- float menu_header_opacity;
- float menu_footer_opacity;
- float refresh_rate;
- float font_msg_pos_x;
- float font_msg_pos_y;
- float font_msg_color_r;
- float font_msg_color_g;
- float font_msg_color_b;
- float xmb_alpha_factor;
-
- char fps_text[128];
- char stat_text[512];
- char chat_text[256];
-
- uint64_t frame_count;
- float frame_time;
- float frame_rate;
-
- struct
- {
- float x;
- float y;
- float scale;
- /* Drop shadow color multiplier. */
- float drop_mod;
- /* Drop shadow offset.
- * If both are 0, no drop shadow will be rendered. */
- int drop_x, drop_y;
- /* Drop shadow alpha */
- float drop_alpha;
- /* ABGR. Use the macros. */
- uint32_t color;
- bool full_screen;
- enum text_alignment text_align;
- } osd_stat_params;
-
- void (*cb_update_window_title)(void*, void *);
- void (*cb_swap_buffers)(void*, void *);
- bool (*cb_get_metrics)(void *data, enum display_metric_types type,
- float *value);
- bool (*cb_set_resize)(void*, unsigned, unsigned);
-
- bool (*cb_set_mvp)(void *data, void *shader_data,
- const void *mat_data);
-
- void *context_data;
- void *shader_data;
- void *userdata;
- const shader_backend_t *shader_driver;
-} video_frame_info_t;
-
-typedef void (*update_window_title_cb)(void*, void*);
-typedef bool (*get_metrics_cb)(void *data, enum display_metric_types type,
- float *value);
-typedef bool (*set_resize_cb)(void*, unsigned, unsigned);
-
-typedef struct gfx_ctx_driver
-{
- /* The opaque pointer is the underlying video driver data (e.g. gl_t for
- * OpenGL contexts). Although not advised, the context driver is allowed
- * to hold a pointer to it as the context never outlives the video driver.
- *
- * The context driver is responsible for it's own data.*/
- void* (*init)(video_frame_info_t *video_info, void *video_driver);
- void (*destroy)(void *data);
-
- enum gfx_ctx_api (*get_api)(void *data);
-
- /* Which API to bind to. */
- bool (*bind_api)(void *video_driver, enum gfx_ctx_api,
- unsigned major, unsigned minor);
-
- /* Sets the swap interval. */
- void (*swap_interval)(void *data, int);
-
- /* Sets video mode. Creates a window, etc. */
- bool (*set_video_mode)(void*, video_frame_info_t *video_info, unsigned, unsigned, bool);
-
- /* Gets current window size.
- * If not initialized yet, it returns current screen size. */
- void (*get_video_size)(void*, unsigned*, unsigned*);
-
- float (*get_refresh_rate)(void*);
-
- void (*get_video_output_size)(void*, unsigned*, unsigned*);
-
- void (*get_video_output_prev)(void*);
-
- void (*get_video_output_next)(void*);
-
- get_metrics_cb get_metrics;
-
- /* Translates a window size to an aspect ratio.
- * In most cases this will be just width / height, but
- * some contexts will better know which actual aspect ratio is used.
- * This can be NULL to assume the default behavior.
- */
- float (*translate_aspect)(void*, unsigned, unsigned);
-
- /* Asks driver to update window title (FPS, etc). */
- update_window_title_cb update_window_title;
-
- /* Queries for resize and quit events.
- * Also processes events. */
- void (*check_window)(void*, bool*, bool*,
- unsigned*, unsigned*, bool);
-
- /* Acknowledge a resize event. This is needed for some APIs.
- * Most backends will ignore this. */
- set_resize_cb set_resize;
-
- /* Checks if window has input focus. */
- bool (*has_focus)(void*);
-
- /* Should the screensaver be suppressed? */
- bool (*suppress_screensaver)(void *data, bool enable);
-
- /* Checks if context driver has windowed support. */
- bool (*has_windowed)(void*);
-
- /* Swaps buffers. VBlank sync depends on
- * earlier calls to swap_interval. */
- void (*swap_buffers)(void*, void *);
-
- /* Most video backends will want to use a certain input driver.
- * Checks for it here. */
- void (*input_driver)(void*, const char *, const input_driver_t**, void**);
-
- /* Wraps whatever gl_proc_address() there is.
- * Does not take opaque, to avoid lots of ugly wrapper code. */
- gfx_ctx_proc_t (*get_proc_address)(const char*);
-
- /* Returns true if this context supports EGLImage buffers for
- * screen drawing and was initalized correctly. */
- bool (*image_buffer_init)(void*, const video_info_t*);
-
- /* Writes the frame to the EGLImage and sets image_handle to it.
- * Returns true if a new image handle is created.
- * Always returns true the first time it's called for a new index.
- * The graphics core must handle a change in the handle correctly. */
- bool (*image_buffer_write)(void*, const void *frame, unsigned width,
- unsigned height, unsigned pitch, bool rgb32,
- unsigned index, void **image_handle);
-
- /* Shows or hides mouse. Can be NULL if context doesn't
- * have a concept of mouse pointer. */
- void (*show_mouse)(void *data, bool state);
-
- /* Human readable string. */
- const char *ident;
-
- uint32_t (*get_flags)(void *data);
-
- void (*set_flags)(void *data, uint32_t flags);
-
- /* Optional. Binds HW-render offscreen context. */
- void (*bind_hw_render)(void *data, bool enable);
-
- /* Optional. Gets base data for the context which is used by the driver.
- * This is mostly relevant for graphics APIs such as Vulkan
- * which do not have global context state. */
- void *(*get_context_data)(void *data);
-
- /* Optional. Makes driver context (only GLX right now)
- * active for this thread. */
- void (*make_current)(bool release);
-} gfx_ctx_driver_t;
-
-typedef struct gfx_ctx_flags
-{
- uint32_t flags;
-} gfx_ctx_flags_t;
-
-typedef struct gfx_ctx_size
-{
- bool *quit;
- bool *resize;
- unsigned *width;
- unsigned *height;
-} gfx_ctx_size_t;
-
-typedef struct gfx_ctx_mode
-{
- unsigned width;
- unsigned height;
- bool fullscreen;
-} gfx_ctx_mode_t;
-
-typedef struct gfx_ctx_metrics
-{
- enum display_metric_types type;
- float *value;
-} gfx_ctx_metrics_t;
-
-typedef struct gfx_ctx_aspect
-{
- float *aspect;
- unsigned width;
- unsigned height;
-} gfx_ctx_aspect_t;
-
-typedef struct gfx_ctx_image
-{
- const void *frame;
- unsigned width;
- unsigned height;
- unsigned pitch;
- unsigned index;
- bool rgb32;
- void **handle;
-} gfx_ctx_image_t;
-
-typedef struct gfx_ctx_input
-{
- const input_driver_t **input;
- void **input_data;
-} gfx_ctx_input_t;
-
-typedef struct gfx_ctx_proc_address
-{
- const char *sym;
- retro_proc_address_t addr;
-} gfx_ctx_proc_address_t;
-
-typedef struct gfx_ctx_ident
-{
- const char *ident;
-} gfx_ctx_ident_t;
-
-typedef struct video_viewport
-{
- int x;
- int y;
- unsigned width;
- unsigned height;
- unsigned full_width;
- unsigned full_height;
-} video_viewport_t;
-
-struct aspect_ratio_elem
-{
- char name[64];
- float value;
-};
-
-/* Optionally implemented interface to poke more
- * deeply into video driver. */
-
-typedef struct video_poke_interface
-{
- uint32_t (*get_flags)(void *data);
- void (*set_coords)(void *handle_data, void *shader_data,
- const struct video_coords *coords);
- void (*set_mvp)(void *data, void *shader_data,
- const void *mat_data);
- uintptr_t (*load_texture)(void *video_data, void *data,
- bool threaded, enum texture_filter_type filter_type);
- void (*unload_texture)(void *data, uintptr_t id);
- void (*set_video_mode)(void *data, unsigned width,
- unsigned height, bool fullscreen);
- float (*get_refresh_rate)(void *data);
- void (*set_filtering)(void *data, unsigned index, bool smooth);
- void (*get_video_output_size)(void *data,
- unsigned *width, unsigned *height);
-
- /* Move index to previous resolution */
- void (*get_video_output_prev)(void *data);
-
- /* Move index to next resolution */
- void (*get_video_output_next)(void *data);
-
- uintptr_t (*get_current_framebuffer)(void *data);
- retro_proc_address_t (*get_proc_address)(void *data, const char *sym);
- void (*set_aspect_ratio)(void *data, unsigned aspectratio_index);
- void (*apply_state_changes)(void *data);
-
- /* Update texture. */
- void (*set_texture_frame)(void *data, const void *frame, bool rgb32,
- unsigned width, unsigned height, float alpha);
- /* Enable or disable rendering. */
- void (*set_texture_enable)(void *data, bool enable, bool full_screen);
- void (*set_osd_msg)(void *data, video_frame_info_t *video_info,
- const char *msg,
- const void *params, void *font);
-
- void (*show_mouse)(void *data, bool state);
- void (*grab_mouse_toggle)(void *data);
-
- struct video_shader *(*get_current_shader)(void *data);
- bool (*get_current_software_framebuffer)(void *data,
- struct retro_framebuffer *framebuffer);
- bool (*get_hw_render_interface)(void *data,
- const struct retro_hw_render_interface **iface);
-} video_poke_interface_t;
-
-/* msg is for showing a message on the screen
- * along with the video frame. */
-typedef bool (*video_driver_frame_t)(void *data,
- const void *frame, unsigned width,
- unsigned height, uint64_t frame_count,
- unsigned pitch, const char *msg, video_frame_info_t *video_info);
-
-typedef struct video_driver
-{
- /* Should the video driver act as an input driver as well?
- * The video initialization might preinitialize an input driver
- * to override the settings in case the video driver relies on
- * input driver for event handling. */
- void *(*init)(const video_info_t *video,
- const input_driver_t **input,
- void **input_data);
-
- /* Updates frame on the screen.
- * Frame can be either XRGB1555, RGB565 or ARGB32 format
- * depending on rgb32 setting in video_info_t.
- * Pitch is the distance in bytes between two scanlines in memory.
- *
- * When msg is non-NULL,
- * it's a message that should be displayed to the user. */
- video_driver_frame_t frame;
-
- /* Should we care about syncing to vblank? Fast forwarding.
- *
- * Requests nonblocking operation.
- *
- * True = VSync is turned off.
- * False = VSync is turned on.
- * */
- void (*set_nonblock_state)(void *data, bool toggle);
-
- /* Is the window still active? */
- bool (*alive)(void *data);
-
- /* Does the window have focus? */
- bool (*focus)(void *data);
-
- /* Should the screensaver be suppressed? */
- bool (*suppress_screensaver)(void *data, bool enable);
-
- /* Does the graphics context support windowed mode? */
- bool (*has_windowed)(void *data);
-
- /* Sets shader. Might not be implemented. Will be moved to
- * poke_interface later. */
- bool (*set_shader)(void *data, enum rarch_shader_type type,
- const char *path);
-
- /* Frees driver. */
- void (*free)(void *data);
-
- /* Human-readable identifier. */
- const char *ident;
-
- void (*set_viewport)(void *data, unsigned width, unsigned height,
- bool force_full, bool allow_rotate);
-
- void (*set_rotation)(void *data, unsigned rotation);
- void (*viewport_info)(void *data, struct video_viewport *vp);
-
- /* Reads out in BGR byte order (24bpp). */
- bool (*read_viewport)(void *data, uint8_t *buffer, bool is_idle);
-
- /* Returns a pointer to a newly allocated buffer that can
- * (and must) be passed to free() by the caller, containing a
- * copy of the current raw frame in the active pixel format
- * and sets width, height and pitch to the correct values. */
- void* (*read_frame_raw)(void *data, unsigned *width,
- unsigned *height, size_t *pitch);
-
-#ifdef HAVE_OVERLAY
- void (*overlay_interface)(void *data,
- const video_overlay_interface_t **iface);
-#endif
- void (*poke_interface)(void *data, const video_poke_interface_t **iface);
- unsigned (*wrap_type_to_enum)(enum gfx_wrap_type type);
-} video_driver_t;
-
-extern struct aspect_ratio_elem aspectratio_lut[ASPECT_RATIO_END];
-
-bool video_driver_has_windowed(void);
-
-bool video_driver_cached_frame_has_valid_framebuffer(void);
-
-void video_driver_destroy(void);
-void video_driver_set_cached_frame_ptr(const void *data);
-void video_driver_set_stub_frame(void);
-void video_driver_unset_stub_frame(void);
-bool video_driver_is_stub_frame(void);
-bool video_driver_supports_recording(void);
-bool video_driver_supports_viewport_read(void);
-bool video_driver_supports_read_frame_raw(void);
-void video_driver_set_viewport_config(void);
-void video_driver_set_viewport_square_pixel(void);
-void video_driver_set_viewport_core(void);
-void video_driver_reset_custom_viewport(void);
-void video_driver_set_rgba(void);
-void video_driver_unset_rgba(void);
-bool video_driver_supports_rgba(void);
-bool video_driver_get_next_video_out(void);
-bool video_driver_get_prev_video_out(void);
-bool video_driver_init(bool *video_is_threaded);
-void video_driver_destroy_data(void);
-void video_driver_free(void);
-void video_driver_free_hw_context(void);
-void video_driver_monitor_reset(void);
-void video_driver_set_aspect_ratio(void);
-void video_driver_update_viewport(struct video_viewport* vp, bool force_full, bool keep_aspect);
-void video_driver_show_mouse(void);
-void video_driver_hide_mouse(void);
-void video_driver_set_nonblock_state(bool toggle);
-bool video_driver_find_driver(void);
-void video_driver_apply_state_changes(void);
-bool video_driver_read_viewport(uint8_t *buffer, bool is_idle);
-bool video_driver_cached_frame(void);
-bool video_driver_frame_filter_alive(void);
-bool video_driver_frame_filter_is_32bit(void);
-void video_driver_default_settings(void);
-void video_driver_load_settings(config_file_t *conf);
-void video_driver_save_settings(config_file_t *conf);
-void video_driver_set_own_driver(void);
-void video_driver_unset_own_driver(void);
-bool video_driver_owns_driver(void);
-bool video_driver_is_hw_context(void);
-struct retro_hw_render_callback *video_driver_get_hw_context(void);
-const struct retro_hw_render_context_negotiation_interface
-*video_driver_get_context_negotiation_interface(void);
-void video_driver_set_context_negotiation_interface(const struct
- retro_hw_render_context_negotiation_interface *iface);
-bool video_driver_is_video_cache_context(void);
-void video_driver_set_video_cache_context_ack(void);
-bool video_driver_is_video_cache_context_ack(void);
-void video_driver_set_active(void);
-void video_driver_unset_active(void);
-bool video_driver_is_active(void);
-bool video_driver_gpu_record_init(unsigned size);
-void video_driver_gpu_record_deinit(void);
-bool video_driver_get_current_software_framebuffer(struct
- retro_framebuffer *fb);
-bool video_driver_get_hw_render_interface(const struct
- retro_hw_render_interface **iface);
-bool video_driver_get_viewport_info(struct video_viewport *viewport);
-void video_driver_set_title_buf(void);
-void video_driver_monitor_adjust_system_rates(void);
-
-/**
- * video_driver_find_handle:
- * @index : index of driver to get handle to.
- *
- * Returns: handle to video driver at index. Can be NULL
- * if nothing found.
- **/
-const void *video_driver_find_handle(int index);
-
-/**
- * video_driver_find_ident:
- * @index : index of driver to get handle to.
- *
- * Returns: Human-readable identifier of video driver at index.
- * Can be NULL if nothing found.
- **/
-const char *video_driver_find_ident(int index);
-
-/**
- * config_get_video_driver_options:
- *
- * Get an enumerated list of all video driver names, separated by '|'.
- *
- * Returns: string listing of all video driver names, separated by '|'.
- **/
-const char* config_get_video_driver_options(void);
-
-/**
- * video_driver_get_ptr:
- *
- * Use this if you need the real video driver
- * and driver data pointers.
- *
- * Returns: video driver's userdata.
- **/
-void *video_driver_get_ptr(bool force_nonthreaded_data);
-
-/**
- * video_driver_get_current_framebuffer:
- *
- * Gets pointer to current hardware renderer framebuffer object.
- * Used by RETRO_ENVIRONMENT_SET_HW_RENDER.
- *
- * Returns: pointer to hardware framebuffer object, otherwise 0.
- **/
-uintptr_t video_driver_get_current_framebuffer(void);
-
-retro_proc_address_t video_driver_get_proc_address(const char *sym);
-
-bool video_driver_set_shader(enum rarch_shader_type type,
- const char *shader);
-
-bool video_driver_set_rotation(unsigned rotation);
-
-bool video_driver_set_video_mode(unsigned width,
- unsigned height, bool fullscreen);
-
-bool video_driver_get_video_output_size(
- unsigned *width, unsigned *height);
-
-void video_driver_set_osd_msg(const char *msg,
- const void *params, void *font);
-
-void video_driver_set_texture_enable(bool enable, bool full_screen);
-
-void video_driver_set_texture_frame(const void *frame, bool rgb32,
- unsigned width, unsigned height, float alpha);
-
-#ifdef HAVE_OVERLAY
-bool video_driver_overlay_interface(
- const video_overlay_interface_t **iface);
-#endif
-
-void * video_driver_read_frame_raw(unsigned *width,
- unsigned *height, size_t *pitch);
-
-void video_driver_set_filtering(unsigned index, bool smooth);
-
-const char *video_driver_get_ident(void);
-
-bool video_driver_set_viewport(unsigned width, unsigned height,
- bool force_fullscreen, bool allow_rotate);
-
-void video_driver_get_size(unsigned *width, unsigned *height);
-
-void video_driver_set_size(unsigned *width, unsigned *height);
-
-void video_driver_unset_video_cache_context_ack(void);
-
-float video_driver_get_aspect_ratio(void);
-
-void video_driver_set_aspect_ratio_value(float value);
-
-rarch_softfilter_t *video_driver_frame_filter_get_ptr(void);
-
-enum retro_pixel_format video_driver_get_pixel_format(void);
-
-void video_driver_set_pixel_format(enum retro_pixel_format fmt);
-
-void video_driver_cached_frame_set(const void *data, unsigned width,
- unsigned height, size_t pitch);
-
-void video_driver_cached_frame_get(const void **data, unsigned *width,
- unsigned *height, size_t *pitch);
-
-void video_driver_menu_settings(void **list_data, void *list_info_data,
- void *group_data, void *subgroup_data, const char *parent_group);
-
-/**
- * video_viewport_get_scaled_integer:
- * @vp : Viewport handle
- * @width : Width.
- * @height : Height.
- * @aspect_ratio : Aspect ratio (in float).
- * @keep_aspect : Preserve aspect ratio?
- *
- * Gets viewport scaling dimensions based on
- * scaled integer aspect ratio.
- **/
-void video_viewport_get_scaled_integer(struct video_viewport *vp,
- unsigned width, unsigned height,
- float aspect_ratio, bool keep_aspect);
-
-struct retro_system_av_info *video_viewport_get_system_av_info(void);
-
-struct video_viewport *video_viewport_get_custom(void);
-
-/**
- * video_monitor_set_refresh_rate:
- * @hz : New refresh rate for monitor.
- *
- * Sets monitor refresh rate to new value.
- **/
-void video_monitor_set_refresh_rate(float hz);
-
-/**
- * video_monitor_fps_statistics
- * @refresh_rate : Monitor refresh rate.
- * @deviation : Deviation from measured refresh rate.
- * @sample_points : Amount of sampled points.
- *
- * Gets the monitor FPS statistics based on the current
- * runtime.
- *
- * Returns: true (1) on success.
- * false (0) if:
- * a) threaded video mode is enabled
- * b) less than 2 frame time samples.
- * c) FPS monitor enable is off.
- **/
-bool video_monitor_fps_statistics(double *refresh_rate,
- double *deviation, unsigned *sample_points);
-
-unsigned video_pixel_get_alignment(unsigned pitch);
-
-const video_poke_interface_t *video_driver_get_poke(void);
-
-/**
- * video_driver_frame:
- * @data : pointer to data of the video frame.
- * @width : width of the video frame.
- * @height : height of the video frame.
- * @pitch : pitch of the video frame.
- *
- * Video frame render callback function.
- **/
-void video_driver_frame(const void *data, unsigned width,
- unsigned height, size_t pitch);
-
-#define video_driver_translate_coord_viewport_wrap(vp, mouse_x, mouse_y, res_x, res_y, res_screen_x, res_screen_y) \
- (video_driver_get_viewport_info(vp) ? video_driver_translate_coord_viewport(vp, mouse_x, mouse_y, res_x, res_y, res_screen_x, res_screen_y) : false)
-
-/**
- * video_driver_translate_coord_viewport:
- * @mouse_x : Pointer X coordinate.
- * @mouse_y : Pointer Y coordinate.
- * @res_x : Scaled X coordinate.
- * @res_y : Scaled Y coordinate.
- * @res_screen_x : Scaled screen X coordinate.
- * @res_screen_y : Scaled screen Y coordinate.
- *
- * Translates pointer [X,Y] coordinates into scaled screen
- * coordinates based on viewport info.
- *
- * Returns: true (1) if successful, false if video driver doesn't support
- * viewport info.
- **/
-bool video_driver_translate_coord_viewport(
- struct video_viewport *vp,
- int mouse_x, int mouse_y,
- int16_t *res_x, int16_t *res_y, int16_t *res_screen_x,
- int16_t *res_screen_y);
-
-uintptr_t video_driver_display_get(void);
-
-enum rarch_display_type video_driver_display_type_get(void);
-
-uintptr_t video_driver_window_get(void);
-
-void video_driver_display_type_set(enum rarch_display_type type);
-
-void video_driver_display_set(uintptr_t idx);
-
-void video_driver_window_set(uintptr_t idx);
-
-bool video_driver_texture_load(void *data,
- enum texture_filter_type filter_type,
- uintptr_t *id);
-
-bool video_driver_texture_unload(uintptr_t *id);
-
-void video_driver_build_info(video_frame_info_t *video_info);
-
-void video_driver_reinit(void);
-
-void video_driver_get_window_title(char *buf, unsigned len);
-
-void video_driver_get_record_status(
- bool *has_gpu_record,
- uint8_t **gpu_buf);
-
-bool *video_driver_get_threaded(void);
-
-void video_driver_set_threaded(bool val);
-
-void video_driver_get_status(uint64_t *frame_count, bool * is_alive,
- bool *is_focused);
-
-/**
- * video_context_driver_init_first:
- * @data : Input data.
- * @ident : Identifier of graphics context driver to find.
- * @api : API of higher-level graphics API.
- * @major : Major version number of higher-level graphics API.
- * @minor : Minor version number of higher-level graphics API.
- * @hw_render_ctx : Request a graphics context driver capable of
- * hardware rendering?
- *
- * Finds first suitable graphics context driver and initializes.
- *
- * Returns: graphics context driver if found, otherwise NULL.
- **/
-const gfx_ctx_driver_t *video_context_driver_init_first(
- void *data, const char *ident,
- enum gfx_ctx_api api, unsigned major, unsigned minor,
- bool hw_render_ctx, void **ctx_data);
-
-bool video_context_driver_find_prev_driver(void);
-
-bool video_context_driver_find_next_driver(void);
-
-bool video_context_driver_init_image_buffer(const video_info_t *data);
-
-bool video_context_driver_write_to_image_buffer(gfx_ctx_image_t *img);
-
-bool video_context_driver_get_video_output_prev(void);
-
-bool video_context_driver_get_video_output_next(void);
-
-bool video_context_driver_bind_hw_render(bool *enable);
-
-void video_context_driver_make_current(bool restore);
-
-bool video_context_driver_set(const gfx_ctx_driver_t *data);
-
-void video_context_driver_destroy(void);
-
-bool video_context_driver_get_video_output_size(gfx_ctx_size_t *size_data);
-
-bool video_context_driver_swap_interval(int *interval);
-
-bool video_context_driver_get_proc_address(gfx_ctx_proc_address_t *proc);
-
-bool video_context_driver_suppress_screensaver(bool *bool_data);
-
-bool video_context_driver_get_ident(gfx_ctx_ident_t *ident);
-
-bool video_context_driver_set_video_mode(gfx_ctx_mode_t *mode_info);
-
-bool video_context_driver_get_video_size(gfx_ctx_mode_t *mode_info);
-
-bool video_context_driver_get_refresh_rate(float *refresh_rate);
-
-bool video_context_driver_show_mouse(bool *bool_data);
-
-bool video_context_driver_set_flags(gfx_ctx_flags_t *flags);
-
-bool video_context_driver_get_metrics(gfx_ctx_metrics_t *metrics);
-
-bool video_context_driver_translate_aspect(gfx_ctx_aspect_t *aspect);
-
-bool video_context_driver_input_driver(gfx_ctx_input_t *inp);
-
-enum gfx_ctx_api video_context_driver_get_api(void);
-
-void video_context_driver_free(void);
-
-bool video_shader_driver_get_prev_textures(video_shader_ctx_texture_t *texture);
-
-bool video_shader_driver_get_ident(video_shader_ctx_ident_t *ident);
-
-bool video_shader_driver_get_current_shader(video_shader_ctx_t *shader);
-
-bool video_shader_driver_direct_get_current_shader(video_shader_ctx_t *shader);
-
-bool video_shader_driver_deinit(void);
-
-void video_shader_driver_set_parameter(struct uniform_info *param);
-
-void video_shader_driver_set_parameters(video_shader_ctx_params_t *params);
-
-bool video_shader_driver_init_first(void);
-
-bool video_shader_driver_init(video_shader_ctx_init_t *init);
-
-bool video_shader_driver_get_feedback_pass(unsigned *data);
-
-bool video_shader_driver_mipmap_input(unsigned *index);
-
-void video_driver_set_coords(video_shader_ctx_coords_t *coords);
-
-bool video_shader_driver_scale(video_shader_ctx_scale_t *scaler);
-
-bool video_shader_driver_info(video_shader_ctx_info_t *shader_info);
-
-void video_driver_set_mvp(video_shader_ctx_mvp_t *mvp);
-
-bool video_shader_driver_filter_type(video_shader_ctx_filter_t *filter);
-
-bool video_shader_driver_compile_program(struct shader_program_info *program_info);
-
-void video_shader_driver_use(video_shader_ctx_info_t *shader_info);
-
-bool video_shader_driver_wrap_type(video_shader_ctx_wrap_t *wrap);
-
-float video_driver_get_refresh_rate(void);
-
-extern bool (*video_driver_cb_has_focus)(void);
-
-bool video_driver_started_fullscreen(void);
-
-bool video_driver_is_threaded(void);
-
-bool video_driver_get_all_flags(gfx_ctx_flags_t *flags,
- enum display_flags flag);
-
-extern video_driver_t video_gl;
-extern video_driver_t video_vulkan;
-extern video_driver_t video_metal;
-extern video_driver_t video_psp1;
-extern video_driver_t video_vita2d;
-extern video_driver_t video_ps2;
-extern video_driver_t video_ctr;
-extern video_driver_t video_switch;
-extern video_driver_t video_d3d8;
-extern video_driver_t video_d3d9;
-extern video_driver_t video_d3d10;
-extern video_driver_t video_d3d11;
-extern video_driver_t video_d3d12;
-extern video_driver_t video_gx;
-extern video_driver_t video_wiiu;
-extern video_driver_t video_xenon360;
-extern video_driver_t video_xvideo;
-extern video_driver_t video_sdl;
-extern video_driver_t video_sdl2;
-extern video_driver_t video_vg;
-extern video_driver_t video_omap;
-extern video_driver_t video_exynos;
-extern video_driver_t video_dispmanx;
-extern video_driver_t video_sunxi;
-extern video_driver_t video_drm;
-extern video_driver_t video_xshm;
-extern video_driver_t video_caca;
-extern video_driver_t video_gdi;
-extern video_driver_t video_vga;
-extern video_driver_t video_sixel;
-extern video_driver_t video_null;
-
-extern const gfx_ctx_driver_t gfx_ctx_osmesa;
-extern const gfx_ctx_driver_t gfx_ctx_sdl_gl;
-extern const gfx_ctx_driver_t gfx_ctx_x_egl;
-extern const gfx_ctx_driver_t gfx_ctx_wayland;
-extern const gfx_ctx_driver_t gfx_ctx_x;
-extern const gfx_ctx_driver_t gfx_ctx_drm;
-extern const gfx_ctx_driver_t gfx_ctx_mali_fbdev;
-extern const gfx_ctx_driver_t gfx_ctx_vivante_fbdev;
-extern const gfx_ctx_driver_t gfx_ctx_android;
-extern const gfx_ctx_driver_t gfx_ctx_ps3;
-extern const gfx_ctx_driver_t gfx_ctx_wgl;
-extern const gfx_ctx_driver_t gfx_ctx_videocore;
-extern const gfx_ctx_driver_t gfx_ctx_qnx;
-extern const gfx_ctx_driver_t gfx_ctx_cgl;
-extern const gfx_ctx_driver_t gfx_ctx_cocoagl;
-extern const gfx_ctx_driver_t gfx_ctx_emscripten;
-extern const gfx_ctx_driver_t gfx_ctx_opendingux_fbdev;
-extern const gfx_ctx_driver_t gfx_ctx_khr_display;
-extern const gfx_ctx_driver_t gfx_ctx_gdi;
-extern const gfx_ctx_driver_t gfx_ctx_sixel;
-extern const gfx_ctx_driver_t switch_ctx;
-extern const gfx_ctx_driver_t orbis_ctx;
-extern const gfx_ctx_driver_t gfx_ctx_null;
-
-extern const shader_backend_t gl_glsl_backend;
-extern const shader_backend_t gl_cg_backend;
-extern const shader_backend_t shader_null_backend;
-
-RETRO_END_DECLS
-
-#endif
+/* RetroArch - A frontend for libretro.
+ * Copyright (C) 2010-2014 - Hans-Kristian Arntzen
+ * Copyright (C) 2011-2017 - Daniel De Matteis
+ *
+ * RetroArch is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Found-
+ * ation, either version 3 of the License, or (at your option) any later version.
+ *
+ * RetroArch 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with RetroArch.
+ * If not, see .
+ */
+
+#ifndef __VIDEO_DRIVER__H
+#define __VIDEO_DRIVER__H
+
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#ifdef HAVE_CONFIG_H
+#include "../config.h"
+#endif
+
+#ifdef HAVE_OVERLAY
+#include "../input/input_overlay.h"
+#endif
+
+#include "video_defines.h"
+#include "video_coord_array.h"
+#include "video_filter.h"
+#include "video_shader_parse.h"
+#include "video_state_tracker.h"
+
+#include "../input/input_driver.h"
+
+#define RARCH_SCALE_BASE 256
+
+#include "video_shader_parse.h"
+
+#define VIDEO_SHADER_STOCK_BLEND (GFX_MAX_SHADERS - 1)
+#define VIDEO_SHADER_MENU (GFX_MAX_SHADERS - 2)
+#define VIDEO_SHADER_MENU_2 (GFX_MAX_SHADERS - 3)
+#define VIDEO_SHADER_MENU_3 (GFX_MAX_SHADERS - 4)
+#define VIDEO_SHADER_MENU_4 (GFX_MAX_SHADERS - 5)
+#define VIDEO_SHADER_MENU_5 (GFX_MAX_SHADERS - 6)
+#define VIDEO_SHADER_MENU_6 (GFX_MAX_SHADERS - 7)
+
+#if defined(_XBOX360)
+#define DEFAULT_SHADER_TYPE RARCH_SHADER_HLSL
+#elif defined(__PSL1GHT__) || defined(HAVE_OPENGLES2) || defined(HAVE_GLSL)
+#define DEFAULT_SHADER_TYPE RARCH_SHADER_GLSL
+#elif defined(__CELLOS_LV2__) || defined(HAVE_CG)
+#define DEFAULT_SHADER_TYPE RARCH_SHADER_CG
+#else
+#define DEFAULT_SHADER_TYPE RARCH_SHADER_NONE
+#endif
+
+RETRO_BEGIN_DECLS
+
+#ifndef MAX_EGLIMAGE_TEXTURES
+#define MAX_EGLIMAGE_TEXTURES 32
+#endif
+
+#define MAX_VARIABLES 64
+
+enum
+{
+ TEXTURES = 8,
+ TEXTURESMASK = TEXTURES - 1
+};
+
+struct LinkInfo
+{
+ unsigned tex_w, tex_h;
+ struct video_shader_pass *pass;
+};
+
+enum gfx_ctx_api
+{
+ GFX_CTX_NONE = 0,
+ GFX_CTX_OPENGL_API,
+ GFX_CTX_OPENGL_ES_API,
+ GFX_CTX_DIRECT3D8_API,
+ GFX_CTX_DIRECT3D9_API,
+ GFX_CTX_DIRECT3D10_API,
+ GFX_CTX_DIRECT3D11_API,
+ GFX_CTX_DIRECT3D12_API,
+ GFX_CTX_OPENVG_API,
+ GFX_CTX_VULKAN_API,
+ GFX_CTX_SIXEL_API,
+ GFX_CTX_METAL_API,
+ GFX_CTX_GDI_API,
+ GFX_CTX_GX_API,
+ GFX_CTX_GX2_API
+};
+
+enum display_metric_types
+{
+ DISPLAY_METRIC_NONE = 0,
+ DISPLAY_METRIC_MM_WIDTH,
+ DISPLAY_METRIC_MM_HEIGHT,
+ DISPLAY_METRIC_DPI
+};
+
+enum display_flags
+{
+ GFX_CTX_FLAGS_NONE = 0,
+ GFX_CTX_FLAGS_GL_CORE_CONTEXT,
+ GFX_CTX_FLAGS_MULTISAMPLING,
+ GFX_CTX_FLAGS_CUSTOMIZABLE_SWAPCHAIN_IMAGES,
+ GFX_CTX_FLAGS_HARD_SYNC,
+ GFX_CTX_FLAGS_BLACK_FRAME_INSERTION,
+ GFX_CTX_FLAGS_MENU_FRAME_FILTERING,
+ GFX_CTX_FLAGS_ADAPTIVE_VSYNC
+};
+
+enum shader_uniform_type
+{
+ UNIFORM_1F = 0,
+ UNIFORM_2F,
+ UNIFORM_3F,
+ UNIFORM_4F,
+ UNIFORM_1FV,
+ UNIFORM_2FV,
+ UNIFORM_3FV,
+ UNIFORM_4FV,
+ UNIFORM_1I
+};
+
+enum shader_program_type
+{
+ SHADER_PROGRAM_VERTEX = 0,
+ SHADER_PROGRAM_FRAGMENT,
+ SHADER_PROGRAM_COMBINED
+};
+
+struct shader_program_info
+{
+ bool is_file;
+ const char *vertex;
+ const char *fragment;
+ const char *combined;
+ unsigned idx;
+ void *data;
+};
+
+struct uniform_info
+{
+ bool enabled;
+
+ int32_t location;
+ int32_t count;
+ unsigned type; /* shader uniform type */
+
+ struct
+ {
+ enum shader_program_type type;
+ const char *ident;
+ uint32_t idx;
+ bool add_prefix;
+ bool enable;
+ } lookup;
+
+ struct
+ {
+ struct
+ {
+ intptr_t v0;
+ intptr_t v1;
+ intptr_t v2;
+ intptr_t v3;
+ } integer;
+
+ intptr_t *integerv;
+
+ struct
+ {
+ uintptr_t v0;
+ uintptr_t v1;
+ uintptr_t v2;
+ uintptr_t v3;
+ } unsigned_integer;
+
+ uintptr_t *unsigned_integerv;
+
+ struct
+ {
+ float v0;
+ float v1;
+ float v2;
+ float v3;
+ } f;
+
+ float *floatv;
+ } result;
+};
+
+typedef struct shader_backend
+{
+ void *(*init)(void *data, const char *path);
+ void (*init_menu_shaders)(void *data);
+ void (*deinit)(void *data);
+
+ /* Set shader parameters. */
+ void (*set_params)(void *data, void *shader_data);
+
+ void (*set_uniform_parameter)(void *data, struct uniform_info *param,
+ void *uniform_data);
+
+ /* Compile a shader program. */
+ bool (*compile_program)(void *data, unsigned idx,
+ void *program_data, struct shader_program_info *program_info);
+
+ /* Use a shader program specified by variable 'index'. */
+ void (*use)(void *data, void *shader_data, unsigned index, bool set_active);
+
+ /* Returns the number of currently loaded shaders. */
+ unsigned (*num_shaders)(void *data);
+
+ bool (*filter_type)(void *data, unsigned index, bool *smooth);
+ enum gfx_wrap_type (*wrap_type)(void *data, unsigned index);
+ void (*shader_scale)(void *data,
+ unsigned index, struct gfx_fbo_scale *scale);
+ bool (*set_coords)(void *handle_data,
+ void *shader_data, const struct video_coords *coords);
+ bool (*set_mvp)(void *data, void *shader_data,
+ const void *mat_data);
+ unsigned (*get_prev_textures)(void *data);
+ bool (*get_feedback_pass)(void *data, unsigned *pass);
+ bool (*mipmap_input)(void *data, unsigned index);
+
+ struct video_shader *(*get_current_shader)(void *data);
+
+ enum rarch_shader_type type;
+
+ /* Human readable string. */
+ const char *ident;
+} shader_backend_t;
+
+typedef struct video_shader_ctx_init
+{
+ enum rarch_shader_type shader_type;
+ const char *path;
+ const shader_backend_t *shader;
+ void *data;
+ struct
+ {
+ bool core_context_enabled;
+ } gl;
+} video_shader_ctx_init_t;
+
+typedef struct video_shader_ctx_params
+{
+ unsigned width;
+ unsigned height;
+ unsigned tex_width;
+ unsigned tex_height;
+ unsigned out_width;
+ unsigned out_height;
+ unsigned frame_counter;
+ unsigned fbo_info_cnt;
+ void *data;
+ const void *info;
+ const void *prev_info;
+ const void *feedback_info;
+ const void *fbo_info;
+} video_shader_ctx_params_t;
+
+typedef struct video_shader_ctx_coords
+{
+ void *handle_data;
+ const void *data;
+} video_shader_ctx_coords_t;
+
+typedef struct video_shader_ctx_scale
+{
+ unsigned idx;
+ struct gfx_fbo_scale *scale;
+} video_shader_ctx_scale_t;
+
+typedef struct video_shader_ctx_info
+{
+ bool set_active;
+ unsigned num;
+ unsigned idx;
+ void *data;
+} video_shader_ctx_info_t;
+
+typedef struct video_shader_ctx_mvp
+{
+ void *data;
+ const void *matrix;
+} video_shader_ctx_mvp_t;
+
+typedef struct video_shader_ctx_filter
+{
+ unsigned index;
+ bool *smooth;
+} video_shader_ctx_filter_t;
+
+typedef struct video_shader_ctx_wrap
+{
+ unsigned idx;
+ enum gfx_wrap_type type;
+} video_shader_ctx_wrap_t;
+
+typedef struct video_shader_ctx
+{
+ struct video_shader *data;
+} video_shader_ctx_t;
+
+typedef struct video_shader_ctx_ident
+{
+ const char *ident;
+} video_shader_ctx_ident_t;
+
+typedef struct video_shader_ctx_texture
+{
+ unsigned id;
+} video_shader_ctx_texture_t;
+
+typedef void (*gfx_ctx_proc_t)(void);
+
+typedef struct video_info
+{
+ /* Launch in fullscreen mode instead of windowed mode. */
+ bool fullscreen;
+
+ /* Start with V-Sync enabled. */
+ bool vsync;
+
+ /* If true, the output image should have the aspect ratio
+ * as set in aspect_ratio. */
+ bool force_aspect;
+
+ bool font_enable;
+
+ /* Width of window.
+ * If fullscreen mode is requested,
+ * a width of 0 means the resolution of the
+ * desktop should be used. */
+ unsigned width;
+
+ /* Height of window.
+ * If fullscreen mode is requested,
+ * a height of 0 means the resolutiof the desktop should be used.
+ */
+ unsigned height;
+
+ int swap_interval;
+
+#ifdef GEKKO
+ bool vfilter;
+#endif
+
+ /* If true, applies bilinear filtering to the image,
+ * otherwise nearest filtering. */
+ bool smooth;
+
+ bool is_threaded;
+
+ /* Use 32bit RGBA rather than native RGB565/XBGR1555.
+ *
+ * XRGB1555 format is 16-bit and has byte ordering: 0RRRRRGGGGGBBBBB,
+ * in native endian.
+ *
+ * ARGB8888 is AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB, native endian.
+ * Alpha channel should be disregarded.
+ * */
+ bool rgb32;
+
+#ifdef GEKKO
+ /* TODO - we can't really have driver system-specific
+ * variables in here. There should be some
+ * kind of publicly accessible driver implementation
+ * video struct for specific things like this.
+ */
+
+ /* Wii-specific settings. Ignored for everything else. */
+ unsigned viwidth;
+#endif
+
+ /*
+ * input_scale defines the maximum size of the picture that will
+ * ever be used with the frame callback.
+ *
+ * The maximum resolution is a multiple of 256x256 size (RARCH_SCALE_BASE),
+ * so an input scale of 2 means you should allocate a texture or of 512x512.
+ *
+ * Maximum input size: RARCH_SCALE_BASE * input_scale
+ */
+ unsigned input_scale;
+
+ uintptr_t parent;
+} video_info_t;
+
+typedef struct video_frame_info
+{
+ bool input_menu_swap_ok_cancel_buttons;
+ bool input_driver_nonblock_state;
+ bool shared_context;
+ bool black_frame_insertion;
+ bool hard_sync;
+ bool fps_show;
+ bool statistics_show;
+ bool framecount_show;
+ bool scale_integer;
+ bool post_filter_record;
+ bool windowed_fullscreen;
+ bool fullscreen;
+ bool font_enable;
+ bool use_rgba;
+ bool libretro_running;
+ bool xmb_shadows_enable;
+ bool battery_level_enable;
+ bool timedate_enable;
+ bool runloop_is_slowmotion;
+ bool runloop_is_idle;
+ bool runloop_is_paused;
+ bool is_perfcnt_enable;
+ bool menu_is_alive;
+ bool msg_bgcolor_enable;
+
+ int custom_vp_x;
+ int custom_vp_y;
+ int crt_switch_center_adjust;
+
+ unsigned hard_sync_frames;
+ unsigned aspect_ratio_idx;
+ unsigned max_swapchain_images;
+ unsigned monitor_index;
+ unsigned crt_switch_resolution;
+ unsigned crt_switch_resolution_super;
+ unsigned width;
+ unsigned height;
+ unsigned xmb_theme;
+ unsigned xmb_color_theme;
+ unsigned menu_shader_pipeline;
+ unsigned materialui_color_theme;
+ unsigned ozone_color_theme;
+ unsigned custom_vp_width;
+ unsigned custom_vp_height;
+ unsigned custom_vp_full_width;
+ unsigned custom_vp_full_height;
+
+ float menu_wallpaper_opacity;
+ float menu_framebuffer_opacity;
+ float menu_header_opacity;
+ float menu_footer_opacity;
+ float refresh_rate;
+ float font_msg_pos_x;
+ float font_msg_pos_y;
+ float font_msg_color_r;
+ float font_msg_color_g;
+ float font_msg_color_b;
+ float xmb_alpha_factor;
+
+ char fps_text[128];
+ char stat_text[512];
+ char chat_text[256];
+
+ uint64_t frame_count;
+ float frame_time;
+ float frame_rate;
+
+ struct
+ {
+ float x;
+ float y;
+ float scale;
+ /* Drop shadow color multiplier. */
+ float drop_mod;
+ /* Drop shadow offset.
+ * If both are 0, no drop shadow will be rendered. */
+ int drop_x, drop_y;
+ /* Drop shadow alpha */
+ float drop_alpha;
+ /* ABGR. Use the macros. */
+ uint32_t color;
+ bool full_screen;
+ enum text_alignment text_align;
+ } osd_stat_params;
+
+ void (*cb_update_window_title)(void*, void *);
+ void (*cb_swap_buffers)(void*, void *);
+ bool (*cb_get_metrics)(void *data, enum display_metric_types type,
+ float *value);
+ bool (*cb_set_resize)(void*, unsigned, unsigned);
+
+ bool (*cb_set_mvp)(void *data, void *shader_data,
+ const void *mat_data);
+
+ void *context_data;
+ void *shader_data;
+ void *userdata;
+ const shader_backend_t *shader_driver;
+} video_frame_info_t;
+
+typedef void (*update_window_title_cb)(void*, void*);
+typedef bool (*get_metrics_cb)(void *data, enum display_metric_types type,
+ float *value);
+typedef bool (*set_resize_cb)(void*, unsigned, unsigned);
+
+typedef struct gfx_ctx_driver
+{
+ /* The opaque pointer is the underlying video driver data (e.g. gl_t for
+ * OpenGL contexts). Although not advised, the context driver is allowed
+ * to hold a pointer to it as the context never outlives the video driver.
+ *
+ * The context driver is responsible for it's own data.*/
+ void* (*init)(video_frame_info_t *video_info, void *video_driver);
+ void (*destroy)(void *data);
+
+ enum gfx_ctx_api (*get_api)(void *data);
+
+ /* Which API to bind to. */
+ bool (*bind_api)(void *video_driver, enum gfx_ctx_api,
+ unsigned major, unsigned minor);
+
+ /* Sets the swap interval. */
+ void (*swap_interval)(void *data, int);
+
+ /* Sets video mode. Creates a window, etc. */
+ bool (*set_video_mode)(void*, video_frame_info_t *video_info, unsigned, unsigned, bool);
+
+ /* Gets current window size.
+ * If not initialized yet, it returns current screen size. */
+ void (*get_video_size)(void*, unsigned*, unsigned*);
+
+ float (*get_refresh_rate)(void*);
+
+ void (*get_video_output_size)(void*, unsigned*, unsigned*);
+
+ void (*get_video_output_prev)(void*);
+
+ void (*get_video_output_next)(void*);
+
+ get_metrics_cb get_metrics;
+
+ /* Translates a window size to an aspect ratio.
+ * In most cases this will be just width / height, but
+ * some contexts will better know which actual aspect ratio is used.
+ * This can be NULL to assume the default behavior.
+ */
+ float (*translate_aspect)(void*, unsigned, unsigned);
+
+ /* Asks driver to update window title (FPS, etc). */
+ update_window_title_cb update_window_title;
+
+ /* Queries for resize and quit events.
+ * Also processes events. */
+ void (*check_window)(void*, bool*, bool*,
+ unsigned*, unsigned*, bool);
+
+ /* Acknowledge a resize event. This is needed for some APIs.
+ * Most backends will ignore this. */
+ set_resize_cb set_resize;
+
+ /* Checks if window has input focus. */
+ bool (*has_focus)(void*);
+
+ /* Should the screensaver be suppressed? */
+ bool (*suppress_screensaver)(void *data, bool enable);
+
+ /* Checks if context driver has windowed support. */
+ bool (*has_windowed)(void*);
+
+ /* Swaps buffers. VBlank sync depends on
+ * earlier calls to swap_interval. */
+ void (*swap_buffers)(void*, void *);
+
+ /* Most video backends will want to use a certain input driver.
+ * Checks for it here. */
+ void (*input_driver)(void*, const char *, const input_driver_t**, void**);
+
+ /* Wraps whatever gl_proc_address() there is.
+ * Does not take opaque, to avoid lots of ugly wrapper code. */
+ gfx_ctx_proc_t (*get_proc_address)(const char*);
+
+ /* Returns true if this context supports EGLImage buffers for
+ * screen drawing and was initalized correctly. */
+ bool (*image_buffer_init)(void*, const video_info_t*);
+
+ /* Writes the frame to the EGLImage and sets image_handle to it.
+ * Returns true if a new image handle is created.
+ * Always returns true the first time it's called for a new index.
+ * The graphics core must handle a change in the handle correctly. */
+ bool (*image_buffer_write)(void*, const void *frame, unsigned width,
+ unsigned height, unsigned pitch, bool rgb32,
+ unsigned index, void **image_handle);
+
+ /* Shows or hides mouse. Can be NULL if context doesn't
+ * have a concept of mouse pointer. */
+ void (*show_mouse)(void *data, bool state);
+
+ /* Human readable string. */
+ const char *ident;
+
+ uint32_t (*get_flags)(void *data);
+
+ void (*set_flags)(void *data, uint32_t flags);
+
+ /* Optional. Binds HW-render offscreen context. */
+ void (*bind_hw_render)(void *data, bool enable);
+
+ /* Optional. Gets base data for the context which is used by the driver.
+ * This is mostly relevant for graphics APIs such as Vulkan
+ * which do not have global context state. */
+ void *(*get_context_data)(void *data);
+
+ /* Optional. Makes driver context (only GLX right now)
+ * active for this thread. */
+ void (*make_current)(bool release);
+} gfx_ctx_driver_t;
+
+typedef struct gfx_ctx_flags
+{
+ uint32_t flags;
+} gfx_ctx_flags_t;
+
+typedef struct gfx_ctx_size
+{
+ bool *quit;
+ bool *resize;
+ unsigned *width;
+ unsigned *height;
+} gfx_ctx_size_t;
+
+typedef struct gfx_ctx_mode
+{
+ unsigned width;
+ unsigned height;
+ bool fullscreen;
+} gfx_ctx_mode_t;
+
+typedef struct gfx_ctx_metrics
+{
+ enum display_metric_types type;
+ float *value;
+} gfx_ctx_metrics_t;
+
+typedef struct gfx_ctx_aspect
+{
+ float *aspect;
+ unsigned width;
+ unsigned height;
+} gfx_ctx_aspect_t;
+
+typedef struct gfx_ctx_image
+{
+ const void *frame;
+ unsigned width;
+ unsigned height;
+ unsigned pitch;
+ unsigned index;
+ bool rgb32;
+ void **handle;
+} gfx_ctx_image_t;
+
+typedef struct gfx_ctx_input
+{
+ const input_driver_t **input;
+ void **input_data;
+} gfx_ctx_input_t;
+
+typedef struct gfx_ctx_proc_address
+{
+ const char *sym;
+ retro_proc_address_t addr;
+} gfx_ctx_proc_address_t;
+
+typedef struct gfx_ctx_ident
+{
+ const char *ident;
+} gfx_ctx_ident_t;
+
+typedef struct video_viewport
+{
+ int x;
+ int y;
+ unsigned width;
+ unsigned height;
+ unsigned full_width;
+ unsigned full_height;
+} video_viewport_t;
+
+struct aspect_ratio_elem
+{
+ char name[64];
+ float value;
+};
+
+/* Optionally implemented interface to poke more
+ * deeply into video driver. */
+
+typedef struct video_poke_interface
+{
+ uint32_t (*get_flags)(void *data);
+ void (*set_coords)(void *handle_data, void *shader_data,
+ const struct video_coords *coords);
+ void (*set_mvp)(void *data, void *shader_data,
+ const void *mat_data);
+ uintptr_t (*load_texture)(void *video_data, void *data,
+ bool threaded, enum texture_filter_type filter_type);
+ void (*unload_texture)(void *data, uintptr_t id);
+ void (*set_video_mode)(void *data, unsigned width,
+ unsigned height, bool fullscreen);
+ float (*get_refresh_rate)(void *data);
+ void (*set_filtering)(void *data, unsigned index, bool smooth);
+ void (*get_video_output_size)(void *data,
+ unsigned *width, unsigned *height);
+
+ /* Move index to previous resolution */
+ void (*get_video_output_prev)(void *data);
+
+ /* Move index to next resolution */
+ void (*get_video_output_next)(void *data);
+
+ uintptr_t (*get_current_framebuffer)(void *data);
+ retro_proc_address_t (*get_proc_address)(void *data, const char *sym);
+ void (*set_aspect_ratio)(void *data, unsigned aspectratio_index);
+ void (*apply_state_changes)(void *data);
+
+ /* Update texture. */
+ void (*set_texture_frame)(void *data, const void *frame, bool rgb32,
+ unsigned width, unsigned height, float alpha);
+ /* Enable or disable rendering. */
+ void (*set_texture_enable)(void *data, bool enable, bool full_screen);
+ void (*set_osd_msg)(void *data, video_frame_info_t *video_info,
+ const char *msg,
+ const void *params, void *font);
+
+ void (*show_mouse)(void *data, bool state);
+ void (*grab_mouse_toggle)(void *data);
+
+ struct video_shader *(*get_current_shader)(void *data);
+ bool (*get_current_software_framebuffer)(void *data,
+ struct retro_framebuffer *framebuffer);
+ bool (*get_hw_render_interface)(void *data,
+ const struct retro_hw_render_interface **iface);
+} video_poke_interface_t;
+
+/* msg is for showing a message on the screen
+ * along with the video frame. */
+typedef bool (*video_driver_frame_t)(void *data,
+ const void *frame, unsigned width,
+ unsigned height, uint64_t frame_count,
+ unsigned pitch, const char *msg, video_frame_info_t *video_info);
+
+typedef struct video_driver
+{
+ /* Should the video driver act as an input driver as well?
+ * The video initialization might preinitialize an input driver
+ * to override the settings in case the video driver relies on
+ * input driver for event handling. */
+ void *(*init)(const video_info_t *video,
+ const input_driver_t **input,
+ void **input_data);
+
+ /* Updates frame on the screen.
+ * Frame can be either XRGB1555, RGB565 or ARGB32 format
+ * depending on rgb32 setting in video_info_t.
+ * Pitch is the distance in bytes between two scanlines in memory.
+ *
+ * When msg is non-NULL,
+ * it's a message that should be displayed to the user. */
+ video_driver_frame_t frame;
+
+ /* Should we care about syncing to vblank? Fast forwarding.
+ *
+ * Requests nonblocking operation.
+ *
+ * True = VSync is turned off.
+ * False = VSync is turned on.
+ * */
+ void (*set_nonblock_state)(void *data, bool toggle);
+
+ /* Is the window still active? */
+ bool (*alive)(void *data);
+
+ /* Does the window have focus? */
+ bool (*focus)(void *data);
+
+ /* Should the screensaver be suppressed? */
+ bool (*suppress_screensaver)(void *data, bool enable);
+
+ /* Does the graphics context support windowed mode? */
+ bool (*has_windowed)(void *data);
+
+ /* Sets shader. Might not be implemented. Will be moved to
+ * poke_interface later. */
+ bool (*set_shader)(void *data, enum rarch_shader_type type,
+ const char *path);
+
+ /* Frees driver. */
+ void (*free)(void *data);
+
+ /* Human-readable identifier. */
+ const char *ident;
+
+ void (*set_viewport)(void *data, unsigned width, unsigned height,
+ bool force_full, bool allow_rotate);
+
+ void (*set_rotation)(void *data, unsigned rotation);
+ void (*viewport_info)(void *data, struct video_viewport *vp);
+
+ /* Reads out in BGR byte order (24bpp). */
+ bool (*read_viewport)(void *data, uint8_t *buffer, bool is_idle);
+
+ /* Returns a pointer to a newly allocated buffer that can
+ * (and must) be passed to free() by the caller, containing a
+ * copy of the current raw frame in the active pixel format
+ * and sets width, height and pitch to the correct values. */
+ void* (*read_frame_raw)(void *data, unsigned *width,
+ unsigned *height, size_t *pitch);
+
+#ifdef HAVE_OVERLAY
+ void (*overlay_interface)(void *data,
+ const video_overlay_interface_t **iface);
+#endif
+ void (*poke_interface)(void *data, const video_poke_interface_t **iface);
+ unsigned (*wrap_type_to_enum)(enum gfx_wrap_type type);
+} video_driver_t;
+
+extern struct aspect_ratio_elem aspectratio_lut[ASPECT_RATIO_END];
+
+bool video_driver_has_windowed(void);
+
+bool video_driver_cached_frame_has_valid_framebuffer(void);
+
+void video_driver_destroy(void);
+void video_driver_set_cached_frame_ptr(const void *data);
+void video_driver_set_stub_frame(void);
+void video_driver_unset_stub_frame(void);
+bool video_driver_is_stub_frame(void);
+bool video_driver_supports_recording(void);
+bool video_driver_supports_viewport_read(void);
+bool video_driver_supports_read_frame_raw(void);
+void video_driver_set_viewport_config(void);
+void video_driver_set_viewport_square_pixel(void);
+void video_driver_set_viewport_core(void);
+void video_driver_reset_custom_viewport(void);
+void video_driver_set_rgba(void);
+void video_driver_unset_rgba(void);
+bool video_driver_supports_rgba(void);
+bool video_driver_get_next_video_out(void);
+bool video_driver_get_prev_video_out(void);
+bool video_driver_init(bool *video_is_threaded);
+void video_driver_destroy_data(void);
+void video_driver_free(void);
+void video_driver_free_hw_context(void);
+void video_driver_monitor_reset(void);
+void video_driver_set_aspect_ratio(void);
+void video_driver_update_viewport(struct video_viewport* vp, bool force_full, bool keep_aspect);
+void video_driver_show_mouse(void);
+void video_driver_hide_mouse(void);
+void video_driver_set_nonblock_state(bool toggle);
+bool video_driver_find_driver(void);
+void video_driver_apply_state_changes(void);
+bool video_driver_read_viewport(uint8_t *buffer, bool is_idle);
+bool video_driver_cached_frame(void);
+bool video_driver_frame_filter_alive(void);
+bool video_driver_frame_filter_is_32bit(void);
+void video_driver_default_settings(void);
+void video_driver_load_settings(config_file_t *conf);
+void video_driver_save_settings(config_file_t *conf);
+void video_driver_set_own_driver(void);
+void video_driver_unset_own_driver(void);
+bool video_driver_owns_driver(void);
+bool video_driver_is_hw_context(void);
+struct retro_hw_render_callback *video_driver_get_hw_context(void);
+const struct retro_hw_render_context_negotiation_interface
+*video_driver_get_context_negotiation_interface(void);
+void video_driver_set_context_negotiation_interface(const struct
+ retro_hw_render_context_negotiation_interface *iface);
+bool video_driver_is_video_cache_context(void);
+void video_driver_set_video_cache_context_ack(void);
+bool video_driver_is_video_cache_context_ack(void);
+void video_driver_set_active(void);
+void video_driver_unset_active(void);
+bool video_driver_is_active(void);
+bool video_driver_gpu_record_init(unsigned size);
+void video_driver_gpu_record_deinit(void);
+bool video_driver_get_current_software_framebuffer(struct
+ retro_framebuffer *fb);
+bool video_driver_get_hw_render_interface(const struct
+ retro_hw_render_interface **iface);
+bool video_driver_get_viewport_info(struct video_viewport *viewport);
+void video_driver_set_title_buf(void);
+void video_driver_monitor_adjust_system_rates(void);
+
+/**
+ * video_driver_find_handle:
+ * @index : index of driver to get handle to.
+ *
+ * Returns: handle to video driver at index. Can be NULL
+ * if nothing found.
+ **/
+const void *video_driver_find_handle(int index);
+
+/**
+ * video_driver_find_ident:
+ * @index : index of driver to get handle to.
+ *
+ * Returns: Human-readable identifier of video driver at index.
+ * Can be NULL if nothing found.
+ **/
+const char *video_driver_find_ident(int index);
+
+/**
+ * config_get_video_driver_options:
+ *
+ * Get an enumerated list of all video driver names, separated by '|'.
+ *
+ * Returns: string listing of all video driver names, separated by '|'.
+ **/
+const char* config_get_video_driver_options(void);
+
+/**
+ * video_driver_get_ptr:
+ *
+ * Use this if you need the real video driver
+ * and driver data pointers.
+ *
+ * Returns: video driver's userdata.
+ **/
+void *video_driver_get_ptr(bool force_nonthreaded_data);
+
+/**
+ * video_driver_get_current_framebuffer:
+ *
+ * Gets pointer to current hardware renderer framebuffer object.
+ * Used by RETRO_ENVIRONMENT_SET_HW_RENDER.
+ *
+ * Returns: pointer to hardware framebuffer object, otherwise 0.
+ **/
+uintptr_t video_driver_get_current_framebuffer(void);
+
+retro_proc_address_t video_driver_get_proc_address(const char *sym);
+
+bool video_driver_set_shader(enum rarch_shader_type type,
+ const char *shader);
+
+bool video_driver_set_rotation(unsigned rotation);
+
+bool video_driver_set_video_mode(unsigned width,
+ unsigned height, bool fullscreen);
+
+bool video_driver_get_video_output_size(
+ unsigned *width, unsigned *height);
+
+void video_driver_set_osd_msg(const char *msg,
+ const void *params, void *font);
+
+void video_driver_set_texture_enable(bool enable, bool full_screen);
+
+void video_driver_set_texture_frame(const void *frame, bool rgb32,
+ unsigned width, unsigned height, float alpha);
+
+#ifdef HAVE_OVERLAY
+bool video_driver_overlay_interface(
+ const video_overlay_interface_t **iface);
+#endif
+
+void * video_driver_read_frame_raw(unsigned *width,
+ unsigned *height, size_t *pitch);
+
+void video_driver_set_filtering(unsigned index, bool smooth);
+
+const char *video_driver_get_ident(void);
+
+bool video_driver_set_viewport(unsigned width, unsigned height,
+ bool force_fullscreen, bool allow_rotate);
+
+void video_driver_get_size(unsigned *width, unsigned *height);
+
+void video_driver_set_size(unsigned *width, unsigned *height);
+
+void video_driver_unset_video_cache_context_ack(void);
+
+float video_driver_get_aspect_ratio(void);
+
+void video_driver_set_aspect_ratio_value(float value);
+
+rarch_softfilter_t *video_driver_frame_filter_get_ptr(void);
+
+enum retro_pixel_format video_driver_get_pixel_format(void);
+
+void video_driver_set_pixel_format(enum retro_pixel_format fmt);
+
+void video_driver_cached_frame_set(const void *data, unsigned width,
+ unsigned height, size_t pitch);
+
+void video_driver_cached_frame_get(const void **data, unsigned *width,
+ unsigned *height, size_t *pitch);
+
+void video_driver_menu_settings(void **list_data, void *list_info_data,
+ void *group_data, void *subgroup_data, const char *parent_group);
+
+/**
+ * video_viewport_get_scaled_integer:
+ * @vp : Viewport handle
+ * @width : Width.
+ * @height : Height.
+ * @aspect_ratio : Aspect ratio (in float).
+ * @keep_aspect : Preserve aspect ratio?
+ *
+ * Gets viewport scaling dimensions based on
+ * scaled integer aspect ratio.
+ **/
+void video_viewport_get_scaled_integer(struct video_viewport *vp,
+ unsigned width, unsigned height,
+ float aspect_ratio, bool keep_aspect);
+
+struct retro_system_av_info *video_viewport_get_system_av_info(void);
+
+struct video_viewport *video_viewport_get_custom(void);
+
+/**
+ * video_monitor_set_refresh_rate:
+ * @hz : New refresh rate for monitor.
+ *
+ * Sets monitor refresh rate to new value.
+ **/
+void video_monitor_set_refresh_rate(float hz);
+
+/**
+ * video_monitor_fps_statistics
+ * @refresh_rate : Monitor refresh rate.
+ * @deviation : Deviation from measured refresh rate.
+ * @sample_points : Amount of sampled points.
+ *
+ * Gets the monitor FPS statistics based on the current
+ * runtime.
+ *
+ * Returns: true (1) on success.
+ * false (0) if:
+ * a) threaded video mode is enabled
+ * b) less than 2 frame time samples.
+ * c) FPS monitor enable is off.
+ **/
+bool video_monitor_fps_statistics(double *refresh_rate,
+ double *deviation, unsigned *sample_points);
+
+unsigned video_pixel_get_alignment(unsigned pitch);
+
+const video_poke_interface_t *video_driver_get_poke(void);
+
+/**
+ * video_driver_frame:
+ * @data : pointer to data of the video frame.
+ * @width : width of the video frame.
+ * @height : height of the video frame.
+ * @pitch : pitch of the video frame.
+ *
+ * Video frame render callback function.
+ **/
+void video_driver_frame(const void *data, unsigned width,
+ unsigned height, size_t pitch);
+
+void crt_switch_driver_reinit(void);
+
+#define video_driver_translate_coord_viewport_wrap(vp, mouse_x, mouse_y, res_x, res_y, res_screen_x, res_screen_y) \
+ (video_driver_get_viewport_info(vp) ? video_driver_translate_coord_viewport(vp, mouse_x, mouse_y, res_x, res_y, res_screen_x, res_screen_y) : false)
+
+/**
+ * video_driver_translate_coord_viewport:
+ * @mouse_x : Pointer X coordinate.
+ * @mouse_y : Pointer Y coordinate.
+ * @res_x : Scaled X coordinate.
+ * @res_y : Scaled Y coordinate.
+ * @res_screen_x : Scaled screen X coordinate.
+ * @res_screen_y : Scaled screen Y coordinate.
+ *
+ * Translates pointer [X,Y] coordinates into scaled screen
+ * coordinates based on viewport info.
+ *
+ * Returns: true (1) if successful, false if video driver doesn't support
+ * viewport info.
+ **/
+bool video_driver_translate_coord_viewport(
+ struct video_viewport *vp,
+ int mouse_x, int mouse_y,
+ int16_t *res_x, int16_t *res_y, int16_t *res_screen_x,
+ int16_t *res_screen_y);
+
+uintptr_t video_driver_display_get(void);
+
+enum rarch_display_type video_driver_display_type_get(void);
+
+uintptr_t video_driver_window_get(void);
+
+void video_driver_display_type_set(enum rarch_display_type type);
+
+void video_driver_display_set(uintptr_t idx);
+
+void video_driver_window_set(uintptr_t idx);
+
+bool video_driver_texture_load(void *data,
+ enum texture_filter_type filter_type,
+ uintptr_t *id);
+
+bool video_driver_texture_unload(uintptr_t *id);
+
+void video_driver_build_info(video_frame_info_t *video_info);
+
+void video_driver_reinit(void);
+
+void video_driver_get_window_title(char *buf, unsigned len);
+
+void video_driver_get_record_status(
+ bool *has_gpu_record,
+ uint8_t **gpu_buf);
+
+bool *video_driver_get_threaded(void);
+
+void video_driver_set_threaded(bool val);
+
+void video_driver_get_status(uint64_t *frame_count, bool * is_alive,
+ bool *is_focused);
+
+/**
+ * video_context_driver_init_first:
+ * @data : Input data.
+ * @ident : Identifier of graphics context driver to find.
+ * @api : API of higher-level graphics API.
+ * @major : Major version number of higher-level graphics API.
+ * @minor : Minor version number of higher-level graphics API.
+ * @hw_render_ctx : Request a graphics context driver capable of
+ * hardware rendering?
+ *
+ * Finds first suitable graphics context driver and initializes.
+ *
+ * Returns: graphics context driver if found, otherwise NULL.
+ **/
+const gfx_ctx_driver_t *video_context_driver_init_first(
+ void *data, const char *ident,
+ enum gfx_ctx_api api, unsigned major, unsigned minor,
+ bool hw_render_ctx, void **ctx_data);
+
+bool video_context_driver_find_prev_driver(void);
+
+bool video_context_driver_find_next_driver(void);
+
+bool video_context_driver_init_image_buffer(const video_info_t *data);
+
+bool video_context_driver_write_to_image_buffer(gfx_ctx_image_t *img);
+
+bool video_context_driver_get_video_output_prev(void);
+
+bool video_context_driver_get_video_output_next(void);
+
+bool video_context_driver_bind_hw_render(bool *enable);
+
+void video_context_driver_make_current(bool restore);
+
+bool video_context_driver_set(const gfx_ctx_driver_t *data);
+
+void video_context_driver_destroy(void);
+
+bool video_context_driver_get_video_output_size(gfx_ctx_size_t *size_data);
+
+bool video_context_driver_swap_interval(int *interval);
+
+bool video_context_driver_get_proc_address(gfx_ctx_proc_address_t *proc);
+
+bool video_context_driver_suppress_screensaver(bool *bool_data);
+
+bool video_context_driver_get_ident(gfx_ctx_ident_t *ident);
+
+bool video_context_driver_set_video_mode(gfx_ctx_mode_t *mode_info);
+
+bool video_context_driver_get_video_size(gfx_ctx_mode_t *mode_info);
+
+bool video_context_driver_get_refresh_rate(float *refresh_rate);
+
+bool video_context_driver_show_mouse(bool *bool_data);
+
+bool video_context_driver_set_flags(gfx_ctx_flags_t *flags);
+
+bool video_context_driver_get_metrics(gfx_ctx_metrics_t *metrics);
+
+bool video_context_driver_translate_aspect(gfx_ctx_aspect_t *aspect);
+
+bool video_context_driver_input_driver(gfx_ctx_input_t *inp);
+
+enum gfx_ctx_api video_context_driver_get_api(void);
+
+void video_context_driver_free(void);
+
+bool video_shader_driver_get_prev_textures(video_shader_ctx_texture_t *texture);
+
+bool video_shader_driver_get_ident(video_shader_ctx_ident_t *ident);
+
+bool video_shader_driver_get_current_shader(video_shader_ctx_t *shader);
+
+bool video_shader_driver_direct_get_current_shader(video_shader_ctx_t *shader);
+
+bool video_shader_driver_deinit(void);
+
+void video_shader_driver_set_parameter(struct uniform_info *param);
+
+void video_shader_driver_set_parameters(video_shader_ctx_params_t *params);
+
+bool video_shader_driver_init_first(void);
+
+bool video_shader_driver_init(video_shader_ctx_init_t *init);
+
+bool video_shader_driver_get_feedback_pass(unsigned *data);
+
+bool video_shader_driver_mipmap_input(unsigned *index);
+
+void video_driver_set_coords(video_shader_ctx_coords_t *coords);
+
+bool video_shader_driver_scale(video_shader_ctx_scale_t *scaler);
+
+bool video_shader_driver_info(video_shader_ctx_info_t *shader_info);
+
+void video_driver_set_mvp(video_shader_ctx_mvp_t *mvp);
+
+bool video_shader_driver_filter_type(video_shader_ctx_filter_t *filter);
+
+bool video_shader_driver_compile_program(struct shader_program_info *program_info);
+
+void video_shader_driver_use(video_shader_ctx_info_t *shader_info);
+
+bool video_shader_driver_wrap_type(video_shader_ctx_wrap_t *wrap);
+
+float video_driver_get_refresh_rate(void);
+
+extern bool (*video_driver_cb_has_focus)(void);
+
+bool video_driver_started_fullscreen(void);
+
+bool video_driver_is_threaded(void);
+
+bool video_driver_get_all_flags(gfx_ctx_flags_t *flags,
+ enum display_flags flag);
+
+extern video_driver_t video_gl;
+extern video_driver_t video_vulkan;
+extern video_driver_t video_metal;
+extern video_driver_t video_psp1;
+extern video_driver_t video_vita2d;
+extern video_driver_t video_ps2;
+extern video_driver_t video_ctr;
+extern video_driver_t video_switch;
+extern video_driver_t video_d3d8;
+extern video_driver_t video_d3d9;
+extern video_driver_t video_d3d10;
+extern video_driver_t video_d3d11;
+extern video_driver_t video_d3d12;
+extern video_driver_t video_gx;
+extern video_driver_t video_wiiu;
+extern video_driver_t video_xenon360;
+extern video_driver_t video_xvideo;
+extern video_driver_t video_sdl;
+extern video_driver_t video_sdl2;
+extern video_driver_t video_vg;
+extern video_driver_t video_omap;
+extern video_driver_t video_exynos;
+extern video_driver_t video_dispmanx;
+extern video_driver_t video_sunxi;
+extern video_driver_t video_drm;
+extern video_driver_t video_xshm;
+extern video_driver_t video_caca;
+extern video_driver_t video_gdi;
+extern video_driver_t video_vga;
+extern video_driver_t video_sixel;
+extern video_driver_t video_null;
+
+extern const gfx_ctx_driver_t gfx_ctx_osmesa;
+extern const gfx_ctx_driver_t gfx_ctx_sdl_gl;
+extern const gfx_ctx_driver_t gfx_ctx_x_egl;
+extern const gfx_ctx_driver_t gfx_ctx_wayland;
+extern const gfx_ctx_driver_t gfx_ctx_x;
+extern const gfx_ctx_driver_t gfx_ctx_drm;
+extern const gfx_ctx_driver_t gfx_ctx_mali_fbdev;
+extern const gfx_ctx_driver_t gfx_ctx_vivante_fbdev;
+extern const gfx_ctx_driver_t gfx_ctx_android;
+extern const gfx_ctx_driver_t gfx_ctx_ps3;
+extern const gfx_ctx_driver_t gfx_ctx_wgl;
+extern const gfx_ctx_driver_t gfx_ctx_videocore;
+extern const gfx_ctx_driver_t gfx_ctx_qnx;
+extern const gfx_ctx_driver_t gfx_ctx_cgl;
+extern const gfx_ctx_driver_t gfx_ctx_cocoagl;
+extern const gfx_ctx_driver_t gfx_ctx_emscripten;
+extern const gfx_ctx_driver_t gfx_ctx_opendingux_fbdev;
+extern const gfx_ctx_driver_t gfx_ctx_khr_display;
+extern const gfx_ctx_driver_t gfx_ctx_gdi;
+extern const gfx_ctx_driver_t gfx_ctx_sixel;
+extern const gfx_ctx_driver_t switch_ctx;
+extern const gfx_ctx_driver_t orbis_ctx;
+extern const gfx_ctx_driver_t gfx_ctx_null;
+
+extern const shader_backend_t gl_glsl_backend;
+extern const shader_backend_t gl_cg_backend;
+extern const shader_backend_t shader_null_backend;
+
+RETRO_END_DECLS
+
+#endif
From c43a93e544ac00b14d8dbba60d58cc7bad5899df Mon Sep 17 00:00:00 2001
From: alphanu1 <37101891+alphanu1@users.noreply.github.com>
Date: Wed, 30 Jan 2019 20:47:36 +0000
Subject: [PATCH 02/18] Adding userland for RPi
---
gfx/include/userland/.gitignore | 32 +
gfx/include/userland/CMakeLists.txt | 132 +
gfx/include/userland/LICENCE | 26 +
gfx/include/userland/README.md | 8 +
gfx/include/userland/buildme | 44 +
.../userland/containers/CMakeLists.txt | 124 +
.../userland/containers/asf/CMakeLists.txt | 19 +
.../userland/containers/asf/asf_reader.c | 2247 +++++++
.../userland/containers/asf/asf_writer.c | 577 ++
.../userland/containers/avi/CMakeLists.txt | 19 +
.../userland/containers/avi/avi_reader.c | 1521 +++++
.../userland/containers/avi/avi_writer.c | 1171 ++++
.../userland/containers/binary/CMakeLists.txt | 19 +
.../containers/binary/binary_reader.c | 267 +
.../containers/binary/binary_writer.c | 160 +
gfx/include/userland/containers/containers.h | 746 +++
.../userland/containers/containers_codecs.h | 214 +
.../userland/containers/containers_types.h | 100 +
.../userland/containers/core/containers.c | 637 ++
.../containers/core/containers_bits.c | 490 ++
.../containers/core/containers_bits.h | 344 +
.../containers/core/containers_bytestream.h | 389 ++
.../containers/core/containers_codecs.c | 209 +
.../containers/core/containers_common.h | 73 +
.../containers/core/containers_filters.c | 222 +
.../containers/core/containers_filters.h | 110 +
.../containers/core/containers_index.c | 192 +
.../containers/core/containers_index.h | 82 +
.../userland/containers/core/containers_io.c | 1088 ++++
.../userland/containers/core/containers_io.h | 229 +
.../containers/core/containers_io_helpers.c | 244 +
.../containers/core/containers_io_helpers.h | 716 ++
.../containers/core/containers_list.c | 221 +
.../containers/core/containers_list.h | 102 +
.../containers/core/containers_loader.c | 436 ++
.../containers/core/containers_loader.h | 41 +
.../containers/core/containers_logging.c | 111 +
.../containers/core/containers_logging.h | 77 +
.../containers/core/containers_private.h | 183 +
.../containers/core/containers_time.h | 103 +
.../userland/containers/core/containers_uri.c | 1120 ++++
.../userland/containers/core/containers_uri.h | 241 +
.../containers/core/containers_utils.c | 366 ++
.../containers/core/containers_utils.h | 86 +
.../containers/core/containers_waveformat.h | 180 +
.../containers/core/containers_writer_utils.c | 127 +
.../containers/core/containers_writer_utils.h | 96 +
.../userland/containers/core/packetizers.c | 233 +
.../containers/core/packetizers_private.h | 111 +
.../userland/containers/dummy/CMakeLists.txt | 12 +
.../userland/containers/dummy/dummy_writer.c | 137 +
.../userland/containers/flash/CMakeLists.txt | 13 +
.../userland/containers/flash/flv_reader.c | 1174 ++++
.../containers/h264/avc1_packetizer.c | 343 +
gfx/include/userland/containers/io/io_file.c | 154 +
gfx/include/userland/containers/io/io_http.c | 898 +++
gfx/include/userland/containers/io/io_net.c | 379 ++
gfx/include/userland/containers/io/io_null.c | 91 +
.../userland/containers/io/io_pktfile.c | 261 +
.../containers/metadata/id3/CMakeLists.txt | 13 +
.../metadata/id3/id3_metadata_reader.c | 450 ++
.../metadata/id3/id3_metadata_strings.h | 179 +
.../userland/containers/mkv/CMakeLists.txt | 13 +
.../userland/containers/mkv/matroska_reader.c | 2323 +++++++
.../userland/containers/mp4/CMakeLists.txt | 19 +
.../userland/containers/mp4/mp4_common.h | 128 +
.../userland/containers/mp4/mp4_reader.c | 1879 ++++++
.../userland/containers/mp4/mp4_writer.c | 1441 +++++
.../userland/containers/mpeg/CMakeLists.txt | 13 +
.../userland/containers/mpeg/ps_reader.c | 1268 ++++
.../userland/containers/mpga/CMakeLists.txt | 13 +
.../userland/containers/mpga/mpga_common.h | 147 +
.../containers/mpga/mpga_packetizer.c | 288 +
.../userland/containers/mpga/mpga_reader.c | 618 ++
.../containers/mpgv/mpgv_packetizer.c | 431 ++
.../userland/containers/net/net_sockets.h | 263 +
.../userland/containers/net/net_sockets_bsd.c | 117 +
.../userland/containers/net/net_sockets_bsd.h | 43 +
.../containers/net/net_sockets_common.c | 619 ++
.../containers/net/net_sockets_null.c | 175 +
.../containers/net/net_sockets_priv.h | 85 +
.../containers/net/net_sockets_win32.c | 140 +
.../containers/net/net_sockets_win32.h | 38 +
gfx/include/userland/containers/packetizers.h | 159 +
.../userland/containers/pcm/pcm_packetizer.c | 269 +
.../userland/containers/qsynth/CMakeLists.txt | 13 +
.../containers/qsynth/qsynth_reader.c | 482 ++
.../userland/containers/raw/CMakeLists.txt | 18 +
.../containers/raw/raw_video_common.h | 66 +
.../containers/raw/raw_video_reader.c | 465 ++
.../containers/raw/raw_video_writer.c | 264 +
.../userland/containers/rcv/CMakeLists.txt | 13 +
.../userland/containers/rcv/rcv_reader.c | 358 +
.../userland/containers/rtp/CMakeLists.txt | 17 +
.../userland/containers/rtp/rtp_base64.c | 165 +
.../userland/containers/rtp/rtp_base64.h | 49 +
.../userland/containers/rtp/rtp_h264.c | 803 +++
.../userland/containers/rtp/rtp_h264.h | 42 +
.../userland/containers/rtp/rtp_mpeg4.c | 788 +++
.../userland/containers/rtp/rtp_mpeg4.h | 42 +
.../userland/containers/rtp/rtp_priv.h | 111 +
.../userland/containers/rtp/rtp_reader.c | 1158 ++++
.../userland/containers/rtsp/CMakeLists.txt | 14 +
.../userland/containers/rtsp/rtsp_reader.c | 1983 ++++++
.../userland/containers/rv9/CMakeLists.txt | 13 +
.../userland/containers/rv9/rv9_reader.c | 335 +
.../userland/containers/simple/CMakeLists.txt | 18 +
.../containers/simple/simple_common.h | 42 +
.../containers/simple/simple_reader.c | 599 ++
.../containers/simple/simple_writer.c | 348 +
.../userland/containers/test/CMakeLists.txt | 66 +
.../userland/containers/test/autotest.cpp | 1958 ++++++
.../containers/test/check_frame_int.c | 348 +
gfx/include/userland/containers/test/crc_32.c | 227 +
.../containers/test/datagram_receiver.c | 80 +
.../containers/test/datagram_sender.c | 77 +
.../userland/containers/test/dump_pktfile.c | 147 +
gfx/include/userland/containers/test/nb_io.h | 47 +
.../userland/containers/test/nb_io_unix.c | 80 +
.../userland/containers/test/nb_io_win32.c | 53 +
.../userland/containers/test/rtp_decoder.c | 449 ++
.../userland/containers/test/stream_client.c | 145 +
.../userland/containers/test/stream_server.c | 148 +
gfx/include/userland/containers/test/test.c | 623 ++
.../userland/containers/test/test_bits.c | 600 ++
.../userland/containers/test/test_uri.c | 824 +++
.../userland/containers/test/uri_pipe.c | 116 +
.../userland/containers/wav/CMakeLists.txt | 13 +
.../userland/containers/wav/wav_reader.c | 366 ++
.../userland/helpers/dtoverlay/CMakeLists.txt | 25 +
.../userland/helpers/dtoverlay/dtoverlay.c | 2039 ++++++
.../userland/helpers/dtoverlay/dtoverlay.h | 208 +
gfx/include/userland/helpers/v3d/v3d_common.h | 39 +
gfx/include/userland/helpers/v3d/v3d_ver.h | 66 +
.../helpers/vc_image/metadata_fourcc.h | 107 +
.../userland/helpers/vc_image/vc_image.h | 804 +++
.../helpers/vc_image/vc_image_helper.h | 261 +
.../helpers/vc_image/vc_image_metadata.h | 99 +
.../android/apps/vidtex/CMakeLists.txt | 18 +
.../android/apps/vidtex/applog.h | 48 +
.../android/apps/vidtex/launcher.h | 87 +
.../android/apps/vidtex/launcher_rpi.c | 87 +
.../android/apps/vidtex/launcher_rpi.h | 61 +
.../android/apps/vidtex/main.cpp | 115 +
.../android/apps/vidtex/svp.c | 637 ++
.../android/apps/vidtex/svp.h | 146 +
.../android/apps/vidtex/vidtex.c | 555 ++
.../android/apps/vidtex/vidtex.h | 89 +
.../framework/common/host_ilcore.h | 79 +
.../framework/common/ilcore.c | 353 +
.../host_applications/linux/CMakeLists.txt | 23 +
.../linux/apps/dtmerge/CMakeLists.txt | 20 +
.../linux/apps/dtmerge/dtmerge.c | 172 +
.../linux/apps/dtoverlay/CMakeLists.txt | 25 +
.../linux/apps/dtoverlay/dtoverlay-post | 17 +
.../linux/apps/dtoverlay/dtoverlay-pre | 17 +
.../linux/apps/dtoverlay/dtoverlay_main.c | 1086 ++++
.../linux/apps/dtoverlay/utils.c | 463 ++
.../linux/apps/dtoverlay/utils.h | 74 +
.../linux/apps/gencmd/CMakeLists.txt | 20 +
.../linux/apps/gencmd/gencmd.c | 147 +
.../linux/apps/hello_pi/CMakeLists.txt | 31 +
.../linux/apps/hello_pi/Makefile.include | 28 +
.../linux/apps/hello_pi/README | 21 +
.../apps/hello_pi/hello_audio/CMakeLists.txt | 8 +
.../linux/apps/hello_pi/hello_audio/Makefile | 6 +
.../linux/apps/hello_pi/hello_audio/audio.c | 426 ++
.../apps/hello_pi/hello_audio/audioplay.h | 159 +
.../apps/hello_pi/hello_audio/sinewave.c | 160 +
.../hello_pi/hello_dispmanx/CMakeLists.txt | 8 +
.../apps/hello_pi/hello_dispmanx/Makefile | 5 +
.../apps/hello_pi/hello_dispmanx/dispmanx.c | 163 +
.../apps/hello_pi/hello_encode/CMakeLists.txt | 8 +
.../linux/apps/hello_pi/hello_encode/Makefile | 6 +
.../linux/apps/hello_pi/hello_encode/encode.c | 315 +
.../linux/apps/hello_pi/hello_fft/gpu_fft.c | 135 +
.../linux/apps/hello_pi/hello_fft/gpu_fft.h | 101 +
.../linux/apps/hello_pi/hello_fft/gpu_fft.txt | 159 +
.../apps/hello_pi/hello_fft/gpu_fft_base.c | 190 +
.../apps/hello_pi/hello_fft/gpu_fft_shaders.c | 102 +
.../apps/hello_pi/hello_fft/gpu_fft_trans.c | 95 +
.../apps/hello_pi/hello_fft/gpu_fft_trans.h | 45 +
.../hello_pi/hello_fft/gpu_fft_twiddles.c | 315 +
.../linux/apps/hello_pi/hello_fft/hello_fft.c | 109 +
.../apps/hello_pi/hello_fft/hello_fft_2d.c | 135 +
.../hello_pi/hello_fft/hello_fft_2d_bitmap.h | 53 +
.../hello_pi/hello_fft/hex/shader_1024k.hex | 948 +++
.../hello_pi/hello_fft/hex/shader_128k.hex | 735 +++
.../hello_pi/hello_fft/hex/shader_16k.hex | 688 ++
.../apps/hello_pi/hello_fft/hex/shader_1k.hex | 523 ++
.../hello_pi/hello_fft/hex/shader_2048k.hex | 1353 ++++
.../hello_pi/hello_fft/hex/shader_256.hex | 359 ++
.../hello_pi/hello_fft/hex/shader_256k.hex | 861 +++
.../apps/hello_pi/hello_fft/hex/shader_2k.hex | 765 +++
.../hello_pi/hello_fft/hex/shader_32k.hex | 697 ++
.../hello_pi/hello_fft/hex/shader_4096k.hex | 1523 +++++
.../apps/hello_pi/hello_fft/hex/shader_4k.hex | 514 ++
.../hello_pi/hello_fft/hex/shader_512.hex | 494 ++
.../hello_pi/hello_fft/hex/shader_512k.hex | 983 +++
.../hello_pi/hello_fft/hex/shader_64k.hex | 940 +++
.../apps/hello_pi/hello_fft/hex/shader_8k.hex | 603 ++
.../hello_pi/hello_fft/hex/shader_trans.hex | 126 +
.../linux/apps/hello_pi/hello_fft/mailbox.c | 262 +
.../linux/apps/hello_pi/hello_fft/mailbox.h | 47 +
.../linux/apps/hello_pi/hello_fft/makefile | 36 +
.../apps/hello_pi/hello_fft/qasm/gpu_fft.qinc | 509 ++
.../hello_fft/qasm/gpu_fft_1024k.qasm | 319 +
.../hello_pi/hello_fft/qasm/gpu_fft_128k.qasm | 319 +
.../hello_pi/hello_fft/qasm/gpu_fft_16k.qasm | 282 +
.../hello_pi/hello_fft/qasm/gpu_fft_1k.qasm | 231 +
.../hello_fft/qasm/gpu_fft_2048k.qasm | 336 +
.../hello_fft/qasm/gpu_fft_2048k.qinc | 91 +
.../hello_pi/hello_fft/qasm/gpu_fft_256.qasm | 233 +
.../hello_pi/hello_fft/qasm/gpu_fft_256k.qasm | 326 +
.../hello_pi/hello_fft/qasm/gpu_fft_2k.qasm | 265 +
.../hello_pi/hello_fft/qasm/gpu_fft_32k.qasm | 271 +
.../hello_fft/qasm/gpu_fft_4096k.qasm | 356 +
.../hello_pi/hello_fft/qasm/gpu_fft_4k.qasm | 276 +
.../hello_pi/hello_fft/qasm/gpu_fft_512.qasm | 240 +
.../hello_pi/hello_fft/qasm/gpu_fft_512k.qasm | 329 +
.../hello_pi/hello_fft/qasm/gpu_fft_64k.qasm | 306 +
.../hello_pi/hello_fft/qasm/gpu_fft_8k.qasm | 276 +
.../hello_pi/hello_fft/qasm/gpu_fft_ex.qinc | 112 +
.../hello_fft/qasm/gpu_fft_trans.qasm | 133 +
.../apps/hello_pi/hello_font/CMakeLists.txt | 9 +
.../linux/apps/hello_pi/hello_font/Makefile | 7 +
.../linux/apps/hello_pi/hello_font/Vera.ttf | Bin 0 -> 65932 bytes
.../linux/apps/hello_pi/hello_font/main.c | 138 +
.../apps/hello_pi/hello_jpeg/CMakeLists.txt | 8 +
.../linux/apps/hello_pi/hello_jpeg/Makefile | 6 +
.../linux/apps/hello_pi/hello_jpeg/jpeg.c | 696 ++
.../linux/apps/hello_pi/hello_jpeg/jpeg.h | 68 +
.../apps/hello_pi/hello_mmal_encode/Makefile | 10 +
.../hello_pi/hello_mmal_encode/mmal_encode.c | 261 +
.../apps/hello_pi/hello_teapot/CMakeLists.txt | 8 +
.../linux/apps/hello_pi/hello_teapot/Makefile | 7 +
.../apps/hello_pi/hello_teapot/README.md | 4 +
.../hello_teapot/cube_texture_and_coords.h | 100 +
.../linux/apps/hello_pi/hello_teapot/models.c | 515 ++
.../linux/apps/hello_pi/hello_teapot/models.h | 36 +
.../apps/hello_pi/hello_teapot/teapot.obj.dat | Bin 0 -> 635016 bytes
.../apps/hello_pi/hello_teapot/triangle.c | 468 ++
.../apps/hello_pi/hello_teapot/triangle.h | 30 +
.../linux/apps/hello_pi/hello_teapot/video.c | 266 +
.../apps/hello_pi/hello_tiger/CMakeLists.txt | 9 +
.../linux/apps/hello_pi/hello_tiger/Makefile | 8 +
.../apps/hello_pi/hello_tiger/license.txt | 53 +
.../linux/apps/hello_pi/hello_tiger/main.c | 533 ++
.../apps/hello_pi/hello_tiger/readme.txt | 263 +
.../linux/apps/hello_pi/hello_tiger/tiger.c | 1952 ++++++
.../linux/apps/hello_pi/hello_tiger/tiger.h | 45 +
.../hello_pi/hello_triangle/CMakeLists.txt | 8 +
.../apps/hello_pi/hello_triangle/Makefile | 5 +
.../hello_triangle/cube_texture_and_coords.h | 100 +
.../apps/hello_pi/hello_triangle/triangle.c | 551 ++
.../hello_pi/hello_triangle2/CMakeLists.txt | 8 +
.../apps/hello_pi/hello_triangle2/Makefile | 5 +
.../apps/hello_pi/hello_triangle2/triangle2.c | 512 ++
.../apps/hello_pi/hello_video/CMakeLists.txt | 8 +
.../linux/apps/hello_pi/hello_video/Makefile | 6 +
.../linux/apps/hello_pi/hello_video/README | 1 +
.../linux/apps/hello_pi/hello_video/video.c | 222 +
.../hello_pi/hello_videocube/CMakeLists.txt | 8 +
.../apps/hello_pi/hello_videocube/Makefile | 7 +
.../apps/hello_pi/hello_videocube/README.md | 4 +
.../hello_videocube/cube_texture_and_coords.h | 100 +
.../apps/hello_pi/hello_videocube/triangle.c | 481 ++
.../apps/hello_pi/hello_videocube/triangle.h | 30 +
.../apps/hello_pi/hello_videocube/video.c | 266 +
.../apps/hello_pi/hello_world/CMakeLists.txt | 8 +
.../linux/apps/hello_pi/hello_world/Makefile | 5 +
.../linux/apps/hello_pi/hello_world/world.c | 36 +
.../apps/hello_pi/libs/ilclient/Makefile | 5 +
.../apps/hello_pi/libs/ilclient/ilclient.c | 1836 ++++++
.../apps/hello_pi/libs/ilclient/ilclient.h | 1039 +++
.../apps/hello_pi/libs/ilclient/ilcore.c | 308 +
.../linux/apps/hello_pi/libs/vgfont/Makefile | 7 +
.../linux/apps/hello_pi/libs/vgfont/font.c | 355 +
.../apps/hello_pi/libs/vgfont/graphics.c | 1608 +++++
.../hello_pi/libs/vgfont/graphics_x_private.h | 366 ++
.../linux/apps/hello_pi/libs/vgfont/vgfont.h | 136 +
.../linux/apps/hello_pi/libs/vgfont/vgft.c | 424 ++
.../linux/apps/hello_pi/libs/vgfont/vgft.h | 70 +
.../linux/apps/hello_pi/rebuild.sh | 34 +
.../linux/apps/raspicam/CMakeLists.txt | 62 +
.../linux/apps/raspicam/Doxyfile | 1869 ++++++
.../linux/apps/raspicam/Makefile | 6 +
.../linux/apps/raspicam/README.md | 477 ++
.../linux/apps/raspicam/RaspiCLI.c | 155 +
.../linux/apps/raspicam/RaspiCLI.h | 56 +
.../linux/apps/raspicam/RaspiCamControl.c | 1826 ++++++
.../linux/apps/raspicam/RaspiCamControl.h | 247 +
.../linux/apps/raspicam/RaspiCommonSettings.c | 226 +
.../linux/apps/raspicam/RaspiCommonSettings.h | 59 +
.../linux/apps/raspicam/RaspiGPS.c | 241 +
.../linux/apps/raspicam/RaspiGPS.h | 47 +
.../linux/apps/raspicam/RaspiHelpers.c | 296 +
.../linux/apps/raspicam/RaspiHelpers.h | 43 +
.../linux/apps/raspicam/RaspiPreview.c | 284 +
.../linux/apps/raspicam/RaspiPreview.h | 67 +
.../linux/apps/raspicam/RaspiStill.c | 2063 ++++++
.../linux/apps/raspicam/RaspiStillYUV.c | 1363 ++++
.../linux/apps/raspicam/RaspiTex.c | 757 +++
.../linux/apps/raspicam/RaspiTex.h | 193 +
.../linux/apps/raspicam/RaspiTexUtil.c | 601 ++
.../linux/apps/raspicam/RaspiTexUtil.h | 116 +
.../linux/apps/raspicam/RaspiVid.c | 2958 +++++++++
.../linux/apps/raspicam/RaspiVidYUV.c | 1508 +++++
.../gl_scenes/cube_texture_and_coords.h | 100 +
.../linux/apps/raspicam/gl_scenes/mirror.c | 123 +
.../linux/apps/raspicam/gl_scenes/mirror.h | 37 +
.../linux/apps/raspicam/gl_scenes/models.c | 521 ++
.../linux/apps/raspicam/gl_scenes/models.h | 36 +
.../linux/apps/raspicam/gl_scenes/sobel.c | 192 +
.../linux/apps/raspicam/gl_scenes/sobel.h | 37 +
.../linux/apps/raspicam/gl_scenes/square.c | 121 +
.../linux/apps/raspicam/gl_scenes/square.h | 37 +
.../linux/apps/raspicam/gl_scenes/teapot.c | 332 +
.../linux/apps/raspicam/gl_scenes/teapot.h | 35 +
.../apps/raspicam/gl_scenes/vcsm_square.c | 332 +
.../apps/raspicam/gl_scenes/vcsm_square.h | 37 +
.../linux/apps/raspicam/gl_scenes/yuv.c | 145 +
.../linux/apps/raspicam/gl_scenes/yuv.h | 37 +
.../linux/apps/raspicam/gps.h | 2171 +++++++
.../apps/raspicam/imv_examples/README.md | 39 +
.../apps/raspicam/imv_examples/imv2pgm.c | 88 +
.../apps/raspicam/imv_examples/imv2txt.c | 86 +
.../linux/apps/raspicam/imv_examples/plot.par | 353 +
.../linux/apps/raspicam/libgps_loader.c | 175 +
.../linux/apps/raspicam/libgps_loader.h | 74 +
.../linux/apps/raspicam/tga.c | 113 +
.../linux/apps/raspicam/tga.h | 73 +
.../linux/apps/smem/CMakeLists.txt | 20 +
.../host_applications/linux/apps/smem/smem.c | 449 ++
.../linux/apps/tvservice/CMakeLists.txt | 5 +
.../linux/apps/tvservice/tvservice.c | 1156 ++++
.../linux/apps/vcmailbox/CMakeLists.txt | 5 +
.../linux/apps/vcmailbox/vcmailbox.c | 107 +
.../linux/kernel_headers/vmcs_sm_ioctl.h | 292 +
.../linux/libs/bcm_host/CMakeLists.txt | 25 +
.../linux/libs/bcm_host/bcm_host.c | 168 +
.../linux/libs/bcm_host/include/bcm_host.h | 61 +
.../linux/libs/debug_sym/CMakeLists.txt | 15 +
.../linux/libs/debug_sym/debug_sym.c | 834 +++
.../linux/libs/debug_sym/debug_sym.h | 215 +
.../linux/libs/sm/CMakeLists.txt | 18 +
.../linux/libs/sm/user-vcsm.c | 1753 +++++
.../linux/libs/sm/user-vcsm.h | 472 ++
.../vmcs/test_apps/mmalcam/mmalcam.c | 299 +
.../vmcs/test_apps/mmalcam/mmalcam.h | 98 +
.../vmcs/test_apps/mmalcam/viewfinder.c | 1479 +++++
.../vmcs/test_apps/mmalplay/mmalplay.c | 512 ++
.../vmcs/test_apps/mmalplay/mmalplay.h | 116 +
.../vmcs/test_apps/mmalplay/playback.c | 1249 ++++
.../host_applications/vmcs/vmcs_config.h.in | 29 +
.../host_support/include/vc_debug_sym.h | 186 +
.../userland/host_support/include/vc_mem.h | 52 +
.../userland/interface/khronos/CMakeLists.txt | 95 +
.../khrn_client_platform_filler_abstract.h | 113 +
.../khrn_client_platform_filler_direct.h | 113 +
.../interface/khronos/common/khrn_client.c | 612 ++
.../interface/khronos/common/khrn_client.h | 485 ++
.../khronos/common/khrn_client_cache.c | 382 ++
.../khronos/common/khrn_client_cache.h | 92 +
.../khronos/common/khrn_client_check_types.h | 87 +
.../interface/khronos/common/khrn_client_cr.c | 140 +
.../common/khrn_client_global_image_map.c | 55 +
.../common/khrn_client_global_image_map.h | 47 +
.../khronos/common/khrn_client_mangle.h | 467 ++
.../khronos/common/khrn_client_platform.h | 331 +
.../khronos/common/khrn_client_pointermap.c | 37 +
.../khronos/common/khrn_client_pointermap.h | 47 +
.../khronos/common/khrn_client_rpc.h | 838 +++
.../khronos/common/khrn_client_unmangle.h | 467 ++
.../khronos/common/khrn_client_vector.c | 82 +
.../khronos/common/khrn_client_vector.h | 43 +
.../interface/khronos/common/khrn_int_color.h | 89 +
.../khronos/common/khrn_int_common.h | 177 +
.../khronos/common/khrn_int_generic_map.c | 341 +
.../khronos/common/khrn_int_generic_map.h | 205 +
.../interface/khronos/common/khrn_int_hash.c | 310 +
.../interface/khronos/common/khrn_int_hash.h | 161 +
.../khronos/common/khrn_int_hash_asm.s | 229 +
.../interface/khronos/common/khrn_int_ids.h | 440 ++
.../interface/khronos/common/khrn_int_image.c | 299 +
.../interface/khronos/common/khrn_int_image.h | 383 ++
.../interface/khronos/common/khrn_int_math.h | 182 +
.../khronos/common/khrn_int_misc_impl.h | 40 +
.../interface/khronos/common/khrn_int_util.c | 163 +
.../interface/khronos/common/khrn_int_util.h | 511 ++
.../khronos/common/khrn_int_util_cr.h | 141 +
.../interface/khronos/common/khrn_options.c | 98 +
.../interface/khronos/common/khrn_options.h | 56 +
.../common/linux/khrn_client_platform_linux.c | 829 +++
.../common/linux/khrn_client_rpc_linux.c | 528 ++
.../openwfc/khrn_client_platform_openwfc.c | 328 +
.../vcos/khrn_client_platform_filler_vcos.h | 122 +
.../khrn_client_platform_filler_vcos_vchiq.h | 116 +
.../interface/khronos/egl/egl_client.c | 2511 ++++++++
.../interface/khronos/egl/egl_client_config.c | 410 ++
.../interface/khronos/egl/egl_client_config.h | 146 +
.../khronos/egl/egl_client_config_cr.c | 776 +++
.../khronos/egl/egl_client_context.c | 341 +
.../khronos/egl/egl_client_context.h | 81 +
.../interface/khronos/egl/egl_client_cr.c | 579 ++
.../khronos/egl/egl_client_get_proc.c | 262 +
.../khronos/egl/egl_client_surface.c | 918 +++
.../khronos/egl/egl_client_surface.h | 346 +
.../userland/interface/khronos/egl/egl_int.h | 57 +
.../interface/khronos/egl/egl_int_config.h | 34 +
.../interface/khronos/egl/egl_int_impl.h | 181 +
.../ext/egl_brcm_driver_monitor_client.c | 148 +
.../ext/egl_brcm_driver_monitor_client.h | 29 +
.../khronos/ext/egl_brcm_flush_client.c | 50 +
.../ext/egl_brcm_global_image_client.c | 221 +
.../ext/egl_brcm_perf_monitor_client.c | 149 +
.../ext/egl_brcm_perf_monitor_client.h | 29 +
.../khronos/ext/egl_khr_image_client.c | 530 ++
.../khronos/ext/egl_khr_lock_surface_client.c | 190 +
.../khronos/ext/egl_khr_sync_client.c | 405 ++
.../khronos/ext/egl_khr_sync_client.h | 35 +
.../khronos/ext/egl_openmaxil_client.c | 45 +
.../khronos/ext/egl_openmaxil_client.h | 31 +
.../khronos/ext/ext_gl_debug_marker.c | 71 +
.../khronos/ext/gl_oes_draw_texture_client.c | 95 +
.../khronos/ext/gl_oes_egl_image_client.c | 152 +
.../khronos/ext/gl_oes_framebuffer_object.c | 134 +
.../interface/khronos/ext/gl_oes_map_buffer.c | 195 +
.../ext/gl_oes_matrix_palette_client.c | 132 +
.../khronos/ext/gl_oes_query_matrix_client.c | 60 +
.../interface/khronos/glxx/gl11_int_config.h | 49 +
.../interface/khronos/glxx/gl11_int_impl.h | 129 +
.../interface/khronos/glxx/gl20_int_impl.h | 118 +
.../interface/khronos/glxx/glxx_client.c | 5734 +++++++++++++++++
.../interface/khronos/glxx/glxx_client.h | 164 +
.../interface/khronos/glxx/glxx_int_attrib.h | 143 +
.../interface/khronos/glxx/glxx_int_config.h | 42 +
.../interface/khronos/glxx/glxx_int_impl.h | 142 +
.../interface/khronos/include/EGL/egl.h | 329 +
.../interface/khronos/include/EGL/eglext.h | 205 +
.../khronos/include/EGL/eglext_android.h | 99 +
.../khronos/include/EGL/eglext_brcm.h | 205 +
.../khronos/include/EGL/eglext_nvidia.h | 54 +
.../khronos/include/EGL/eglplatform.h | 205 +
.../interface/khronos/include/GLES/gl.h | 798 +++
.../interface/khronos/include/GLES/glext.h | 1147 ++++
.../khronos/include/GLES/glplatform.h | 64 +
.../interface/khronos/include/GLES2/gl2.h | 649 ++
.../interface/khronos/include/GLES2/gl2ext.h | 1218 ++++
.../khronos/include/GLES2/gl2platform.h | 64 +
.../khronos/include/KHR/khrplatform.h | 295 +
.../interface/khronos/include/VG/openvg.h | 745 +++
.../interface/khronos/include/VG/vgext.h | 233 +
.../interface/khronos/include/VG/vgplatform.h | 86 +
.../interface/khronos/include/VG/vgu.h | 131 +
.../interface/khronos/include/WF/wfc.h | 275 +
.../khronos/include/WF/wfcplatform.h | 71 +
.../userland/interface/khronos/vg/vg_client.c | 5666 ++++++++++++++++
.../userland/interface/khronos/vg/vg_client.h | 229 +
.../userland/interface/khronos/vg/vg_int.h | 47 +
.../interface/khronos/vg/vg_int_config.h | 57 +
.../interface/khronos/vg/vg_int_impl.h | 115 +
.../interface/khronos/vg/vg_int_mat3x3.c | 646 ++
.../interface/khronos/vg/vg_int_mat3x3.h | 156 +
.../interface/khronos/vg/vg_int_util.h | 280 +
.../interface/khronos/wf/wfc_client.c | 2597 ++++++++
.../interface/khronos/wf/wfc_client_ipc.c | 521 ++
.../interface/khronos/wf/wfc_client_ipc.h | 82 +
.../khronos/wf/wfc_client_server_api.c | 673 ++
.../interface/khronos/wf/wfc_client_stream.c | 1043 +++
.../interface/khronos/wf/wfc_client_stream.h | 135 +
.../userland/interface/khronos/wf/wfc_int.h | 253 +
.../userland/interface/khronos/wf/wfc_ipc.h | 350 +
.../interface/khronos/wf/wfc_server_api.h | 302 +
.../userland/interface/mmal/CMakeLists.txt | 45 +
.../interface/mmal/client/CMakeLists.txt | 1 +
.../mmal/client/brcmjpeg/CMakeLists.txt | 6 +
.../interface/mmal/client/brcmjpeg/brcmjpeg.c | 914 +++
.../interface/mmal/client/brcmjpeg/brcmjpeg.h | 152 +
.../mmal/client/brcmjpeg/brcmjpeg_test.c | 236 +
.../interface/mmal/components/CMakeLists.txt | 34 +
.../mmal/components/aaf_audio_render.cpp | 504 ++
.../interface/mmal/components/aggregator.c | 188 +
.../mmal/components/android_media_codec.cpp | 861 +++
.../mmal/components/artificial_camera.c | 287 +
.../mmal/components/avcodec_audio_decoder.c | 607 ++
.../mmal/components/avcodec_video_decoder.c | 586 ++
.../interface/mmal/components/clock.c | 900 +++
.../mmal/components/container_reader.c | 1000 +++
.../userland/interface/mmal/components/copy.c | 327 +
.../interface/mmal/components/null_sink.c | 125 +
.../interface/mmal/components/passthrough.c | 284 +
.../interface/mmal/components/scheduler.c | 485 ++
.../mmal/components/sdl_audio_render.c | 278 +
.../mmal/components/sdl_video_render.c | 394 ++
.../interface/mmal/components/spdif.c | 496 ++
.../interface/mmal/components/splitter.c | 345 +
.../interface/mmal/core/CMakeLists.txt | 25 +
.../interface/mmal/core/mmal_buffer.c | 188 +
.../interface/mmal/core/mmal_buffer_private.h | 86 +
.../userland/interface/mmal/core/mmal_clock.c | 892 +++
.../interface/mmal/core/mmal_clock_private.h | 204 +
.../interface/mmal/core/mmal_component.c | 780 +++
.../mmal/core/mmal_component_private.h | 169 +
.../interface/mmal/core/mmal_core_private.h | 40 +
.../interface/mmal/core/mmal_events.c | 138 +
.../interface/mmal/core/mmal_events_private.h | 67 +
.../interface/mmal/core/mmal_format.c | 183 +
.../interface/mmal/core/mmal_logging.c | 43 +
.../userland/interface/mmal/core/mmal_pool.c | 303 +
.../userland/interface/mmal/core/mmal_port.c | 1509 +++++
.../interface/mmal/core/mmal_port_clock.c | 803 +++
.../interface/mmal/core/mmal_port_private.h | 213 +
.../userland/interface/mmal/core/mmal_queue.c | 198 +
gfx/include/userland/interface/mmal/mmal.h | 381 ++
.../userland/interface/mmal/mmal_buffer.h | 262 +
.../userland/interface/mmal/mmal_clock.h | 202 +
.../userland/interface/mmal/mmal_common.h | 83 +
.../userland/interface/mmal/mmal_component.h | 148 +
.../userland/interface/mmal/mmal_encodings.h | 272 +
.../userland/interface/mmal/mmal_events.h | 109 +
.../userland/interface/mmal/mmal_format.h | 223 +
.../userland/interface/mmal/mmal_logging.h | 71 +
.../userland/interface/mmal/mmal_parameters.h | 194 +
.../interface/mmal/mmal_parameters_audio.h | 66 +
.../interface/mmal/mmal_parameters_camera.h | 996 +++
.../interface/mmal/mmal_parameters_clock.h | 88 +
.../interface/mmal/mmal_parameters_common.h | 191 +
.../interface/mmal/mmal_parameters_video.h | 528 ++
.../userland/interface/mmal/mmal_pool.h | 167 +
.../userland/interface/mmal/mmal_port.h | 286 +
.../userland/interface/mmal/mmal_queue.h | 116 +
.../userland/interface/mmal/mmal_types.h | 100 +
.../interface/mmal/openmaxil/CMakeLists.txt | 20 +
.../interface/mmal/openmaxil/README.md | 10 +
.../interface/mmal/openmaxil/mmalomx.h | 130 +
.../interface/mmal/openmaxil/mmalomx_buffer.c | 235 +
.../interface/mmal/openmaxil/mmalomx_buffer.h | 39 +
.../mmal/openmaxil/mmalomx_commands.c | 462 ++
.../mmal/openmaxil/mmalomx_commands.h | 85 +
.../interface/mmal/openmaxil/mmalomx_core.c | 1577 +++++
.../mmal/openmaxil/mmalomx_logging.c | 176 +
.../mmal/openmaxil/mmalomx_logging.h | 46 +
.../interface/mmal/openmaxil/mmalomx_marks.c | 101 +
.../interface/mmal/openmaxil/mmalomx_marks.h | 36 +
.../mmal/openmaxil/mmalomx_parameters.c | 578 ++
.../mmal/openmaxil/mmalomx_parameters.h | 37 +
.../mmal/openmaxil/mmalomx_registry.c | 159 +
.../mmal/openmaxil/mmalomx_registry.h | 41 +
.../interface/mmal/openmaxil/mmalomx_roles.c | 210 +
.../interface/mmal/openmaxil/mmalomx_roles.h | 61 +
.../mmal/openmaxil/mmalomx_util_params.c | 193 +
.../mmal/openmaxil/mmalomx_util_params.h | 114 +
.../openmaxil/mmalomx_util_params_audio.c | 34 +
.../openmaxil/mmalomx_util_params_camera.c | 556 ++
.../openmaxil/mmalomx_util_params_common.h | 133 +
.../mmal/openmaxil/mmalomx_util_params_misc.c | 157 +
.../openmaxil/mmalomx_util_params_video.c | 277 +
.../interface/mmal/test/CMakeLists.txt | 29 +
.../mmal/test/examples/example_basic_1.c | 241 +
.../mmal/test/examples/example_basic_2.c | 375 ++
.../mmal/test/examples/example_connections.c | 196 +
.../mmal/test/examples/example_graph.c | 98 +
.../interface/mmal/util/CMakeLists.txt | 28 +
.../mmal/util/mmal_component_wrapper.c | 368 ++
.../mmal/util/mmal_component_wrapper.h | 157 +
.../interface/mmal/util/mmal_connection.c | 532 ++
.../interface/mmal/util/mmal_connection.h | 232 +
.../mmal/util/mmal_default_components.h | 92 +
.../userland/interface/mmal/util/mmal_graph.c | 1560 +++++
.../userland/interface/mmal/util/mmal_graph.h | 243 +
.../userland/interface/mmal/util/mmal_il.c | 1056 +++
.../userland/interface/mmal/util/mmal_il.h | 229 +
.../userland/interface/mmal/util/mmal_list.c | 221 +
.../userland/interface/mmal/util/mmal_list.h | 127 +
.../interface/mmal/util/mmal_param_convert.c | 176 +
.../interface/mmal/util/mmal_param_convert.h | 92 +
.../userland/interface/mmal/util/mmal_util.c | 474 ++
.../userland/interface/mmal/util/mmal_util.h | 191 +
.../interface/mmal/util/mmal_util_params.c | 223 +
.../interface/mmal/util/mmal_util_params.h | 210 +
.../interface/mmal/util/mmal_util_rational.c | 158 +
.../interface/mmal/util/mmal_util_rational.h | 127 +
.../userland/interface/mmal/vc/CMakeLists.txt | 26 +
.../userland/interface/mmal/vc/mmal_vc_api.c | 1505 +++++
.../userland/interface/mmal/vc/mmal_vc_api.h | 239 +
.../interface/mmal/vc/mmal_vc_api_drm.c | 77 +
.../interface/mmal/vc/mmal_vc_api_drm.h | 55 +
.../interface/mmal/vc/mmal_vc_client.c | 827 +++
.../interface/mmal/vc/mmal_vc_client_priv.h | 80 +
.../interface/mmal/vc/mmal_vc_dbglog.h | 160 +
.../userland/interface/mmal/vc/mmal_vc_diag.c | 880 +++
.../interface/mmal/vc/mmal_vc_msgnames.c | 78 +
.../interface/mmal/vc/mmal_vc_msgnames.h | 37 +
.../userland/interface/mmal/vc/mmal_vc_msgs.h | 535 ++
.../interface/mmal/vc/mmal_vc_opaque_alloc.c | 87 +
.../interface/mmal/vc/mmal_vc_opaque_alloc.h | 73 +
.../userland/interface/mmal/vc/mmal_vc_shm.c | 241 +
.../userland/interface/mmal/vc/mmal_vc_shm.h | 62 +
.../interface/peer/vc_vchi_dispmanx_common.h | 85 +
.../userland/interface/vchi/common/endian.h | 44 +
.../interface/vchi/connections/connection.h | 324 +
.../interface/vchi/message_drivers/message.h | 197 +
gfx/include/userland/interface/vchi/vchi.h | 382 ++
.../userland/interface/vchi/vchi_cfg.h | 222 +
.../interface/vchi/vchi_cfg_internal.h | 65 +
.../userland/interface/vchi/vchi_common.h | 170 +
gfx/include/userland/interface/vchi/vchi_mh.h | 36 +
.../interface/vchiq_arm/CMakeLists.txt | 20 +
.../userland/interface/vchiq_arm/vchiq.h | 36 +
.../userland/interface/vchiq_arm/vchiq_cfg.h | 63 +
.../userland/interface/vchiq_arm/vchiq_if.h | 200 +
.../interface/vchiq_arm/vchiq_ioctl.h | 116 +
.../userland/interface/vchiq_arm/vchiq_lib.c | 1764 +++++
.../userland/interface/vchiq_arm/vchiq_test.c | 1766 +++++
.../userland/interface/vchiq_arm/vchiq_test.h | 135 +
.../interface/vchiq_arm/vchiq_test_if.h | 35 +
.../userland/interface/vchiq_arm/vchiq_util.c | 111 +
.../userland/interface/vchiq_arm/vchiq_util.h | 65 +
.../userland/interface/vcos/CMakeLists.txt | 68 +
.../interface/vcos/generic/CMakeLists.txt | 21 +
.../interface/vcos/generic/vcos_abort.c | 85 +
.../interface/vcos/generic/vcos_cmd.c | 722 +++
.../interface/vcos/generic/vcos_common.h | 96 +
.../interface/vcos/generic/vcos_deprecated.h | 36 +
.../vcos/generic/vcos_generic_blockpool.c | 568 ++
.../vcos/generic/vcos_generic_blockpool.h | 294 +
.../vcos/generic/vcos_generic_event_flags.c | 320 +
.../vcos/generic/vcos_generic_event_flags.h | 127 +
.../vcos/generic/vcos_generic_named_sem.c | 268 +
.../vcos/generic/vcos_generic_named_sem.h | 101 +
.../generic/vcos_generic_quickslow_mutex.h | 95 +
.../vcos/generic/vcos_generic_reentrant_mtx.c | 76 +
.../vcos/generic/vcos_generic_reentrant_mtx.h | 95 +
.../vcos/generic/vcos_generic_safe_string.c | 92 +
.../interface/vcos/generic/vcos_generic_tls.h | 164 +
.../interface/vcos/generic/vcos_init.c | 84 +
.../generic/vcos_joinable_thread_from_plain.h | 229 +
.../vcos/generic/vcos_latch_from_sem.h | 68 +
.../interface/vcos/generic/vcos_logcat.c | 577 ++
.../vcos/generic/vcos_mem_from_malloc.c | 98 +
.../vcos/generic/vcos_mem_from_malloc.h | 80 +
.../interface/vcos/generic/vcos_msgqueue.c | 389 ++
.../vcos/generic/vcos_mutexes_are_reentrant.h | 88 +
.../vcos/generic/vcos_thread_reaper.h | 55 +
.../interface/vcos/glibc/vcos_backtrace.c | 56 +
.../interface/vcos/pthreads/CMakeLists.txt | 46 +
.../interface/vcos/pthreads/vcos_dlfcn.c | 71 +
.../vcos/pthreads/vcos_futex_mutex.h | 102 +
.../interface/vcos/pthreads/vcos_platform.h | 828 +++
.../vcos/pthreads/vcos_platform_types.h | 71 +
.../interface/vcos/pthreads/vcos_pthreads.c | 897 +++
.../userland/interface/vcos/user_nodefs.h | 47 +
gfx/include/userland/interface/vcos/vcos.h | 226 +
.../userland/interface/vcos/vcos_assert.h | 324 +
.../interface/vcos/vcos_atomic_flags.h | 92 +
.../userland/interface/vcos/vcos_attr.h | 153 +
.../userland/interface/vcos/vcos_blockpool.h | 171 +
.../userland/interface/vcos/vcos_build_info.h | 32 +
.../userland/interface/vcos/vcos_cfg.h | 126 +
.../userland/interface/vcos/vcos_cmd.h | 120 +
.../userland/interface/vcos/vcos_ctype.h | 49 +
.../userland/interface/vcos/vcos_dlfcn.h | 86 +
.../userland/interface/vcos/vcos_event.h | 117 +
.../interface/vcos/vcos_event_flags.h | 118 +
.../userland/interface/vcos/vcos_init.h | 110 +
.../userland/interface/vcos/vcos_inttypes.h | 49 +
.../userland/interface/vcos/vcos_isr.h | 90 +
.../userland/interface/vcos/vcos_legacy_isr.h | 102 +
.../userland/interface/vcos/vcos_logging.h | 324 +
.../interface/vcos/vcos_logging_control.h | 28 +
.../interface/vcos/vcos_lowlevel_thread.h | 129 +
.../userland/interface/vcos/vcos_mem.h | 101 +
.../userland/interface/vcos/vcos_mempool.h | 109 +
.../userland/interface/vcos/vcos_msgqueue.h | 280 +
.../userland/interface/vcos/vcos_mutex.h | 112 +
.../interface/vcos/vcos_named_semaphore.h | 113 +
.../userland/interface/vcos/vcos_once.h | 62 +
.../userland/interface/vcos/vcos_queue.h | 105 +
.../interface/vcos/vcos_quickslow_mutex.h | 101 +
.../interface/vcos/vcos_reentrant_mutex.h | 86 +
.../userland/interface/vcos/vcos_semaphore.h | 158 +
.../userland/interface/vcos/vcos_stdbool.h | 47 +
.../userland/interface/vcos/vcos_stdint.h | 107 +
.../userland/interface/vcos/vcos_string.h | 129 +
.../userland/interface/vcos/vcos_thread.h | 282 +
.../interface/vcos/vcos_thread_attr.h | 96 +
.../userland/interface/vcos/vcos_timer.h | 117 +
.../userland/interface/vcos/vcos_tls.h | 84 +
.../userland/interface/vcos/vcos_types.h | 279 +
.../interface/vctypes/vc_display_types.h | 116 +
.../interface/vctypes/vc_image_structs.h | 246 +
.../interface/vctypes/vc_image_types.h | 174 +
.../interface/vmcs_host/CMakeLists.txt | 35 +
.../vmcs_host/khronos/IL/OMX_Audio.h | 1391 ++++
.../vmcs_host/khronos/IL/OMX_Broadcom.h | 2739 ++++++++
.../vmcs_host/khronos/IL/OMX_Component.h | 579 ++
.../interface/vmcs_host/khronos/IL/OMX_Core.h | 1467 +++++
.../interface/vmcs_host/khronos/IL/OMX_ILCS.h | 65 +
.../vmcs_host/khronos/IL/OMX_IVCommon.h | 1101 ++++
.../vmcs_host/khronos/IL/OMX_Image.h | 347 +
.../vmcs_host/khronos/IL/OMX_Index.h | 551 ++
.../vmcs_host/khronos/IL/OMX_Other.h | 347 +
.../vmcs_host/khronos/IL/OMX_Types.h | 372 ++
.../vmcs_host/khronos/IL/OMX_Video.h | 1082 ++++
.../vmcs_host/linux/vcfiled/CMakeLists.txt | 33 +
.../vmcs_host/linux/vcfiled/vcfiled.c | 220 +
.../vmcs_host/linux/vcfiled/vcfiled_check.c | 169 +
.../vmcs_host/linux/vcfiled/vcfiled_check.h | 48 +
.../linux/vcfiled/vcfiled_lock_test.c | 82 +
.../interface/vmcs_host/linux/vcfilesys.c | 1131 ++++
.../interface/vmcs_host/linux/vchost_config.h | 61 +
.../interface/vmcs_host/linux/vcmisc.c | 33 +
.../userland/interface/vmcs_host/vc_cec.h | 513 ++
.../interface/vmcs_host/vc_cecservice.h | 515 ++
.../interface/vmcs_host/vc_cecservice_defs.h | 182 +
.../userland/interface/vmcs_host/vc_cma.h | 70 +
.../interface/vmcs_host/vc_dispmanx.h | 142 +
.../interface/vmcs_host/vc_dispmanx_types.h | 229 +
.../interface/vmcs_host/vc_dispservice_defs.h | 250 +
.../vmcs_host/vc_dispservice_x_defs.h | 276 +
.../interface/vmcs_host/vc_fileservice_defs.h | 119 +
.../interface/vmcs_host/vc_gencmd_defs.h | 36 +
.../userland/interface/vmcs_host/vc_hdmi.h | 545 ++
.../interface/vmcs_host/vc_hdmi_property.h | 137 +
.../interface/vmcs_host/vc_ilcs_defs.h | 288 +
.../interface/vmcs_host/vc_imageconv_defs.h | 51 +
.../userland/interface/vmcs_host/vc_sdtv.h | 147 +
.../interface/vmcs_host/vc_service_common.c | 57 +
.../interface/vmcs_host/vc_service_common.h | 46 +
.../interface/vmcs_host/vc_tvservice.h | 524 ++
.../interface/vmcs_host/vc_tvservice_defs.h | 357 +
.../vmcs_host/vc_vchi_audioserv_defs.h | 165 +
.../interface/vmcs_host/vc_vchi_bufman.h | 116 +
.../interface/vmcs_host/vc_vchi_bufman_defs.h | 136 +
.../interface/vmcs_host/vc_vchi_cecservice.c | 1365 ++++
.../interface/vmcs_host/vc_vchi_dispmanx.c | 1321 ++++
.../interface/vmcs_host/vc_vchi_dispmanx.h | 69 +
.../vmcs_host/vc_vchi_fileservice_defs.h | 75 +
.../interface/vmcs_host/vc_vchi_filesys.c | 2509 ++++++++
.../interface/vmcs_host/vc_vchi_filesys.h | 184 +
.../interface/vmcs_host/vc_vchi_gencmd.c | 532 ++
.../interface/vmcs_host/vc_vchi_gencmd.h | 88 +
.../interface/vmcs_host/vc_vchi_gpuserv.c | 261 +
.../interface/vmcs_host/vc_vchi_gpuserv.h | 94 +
.../interface/vmcs_host/vc_vchi_tvservice.c | 1639 +++++
.../userland/interface/vmcs_host/vcfilesys.h | 166 +
.../interface/vmcs_host/vcfilesys_defs.h | 88 +
.../userland/interface/vmcs_host/vcgencmd.h | 95 +
.../userland/interface/vmcs_host/vchost.h | 273 +
.../vmcs_host/vchost_platform_config.h | 32 +
.../userland/interface/vmcs_host/vcilcs.c | 1071 +++
.../userland/interface/vmcs_host/vcilcs.h | 103 +
.../interface/vmcs_host/vcilcs_common.c | 122 +
.../interface/vmcs_host/vcilcs_common.h | 83 +
.../userland/interface/vmcs_host/vcilcs_in.c | 271 +
.../userland/interface/vmcs_host/vcilcs_out.c | 917 +++
.../userland/makefiles/cmake/arm-linux.cmake | 130 +
.../makefiles/cmake/cmake_config.h.in | 15 +
.../makefiles/cmake/global_settings.cmake | 83 +
.../makefiles/cmake/srcs/test-mtrace.c | 35 +
.../toolchains/arm-linux-gnueabihf.cmake | 22 +
.../toolchains/bcm2708-glibc-linux.cmake | 21 +
.../userland/makefiles/cmake/vmcs.cmake | 81 +
.../userland/middleware/dlloader/dlfcn.h | 99 +
.../userland/middleware/imageconv/imageconv.h | 448 ++
.../common/2708/khrn_interlock_filler_4.h | 50 +
.../khronos/common/2708/khrn_prod_4.h | 545 ++
.../middleware/khronos/common/khrn_hw.h | 236 +
.../middleware/khronos/common/khrn_image.h | 339 +
.../khronos/common/khrn_interlock.h | 74 +
.../middleware/khronos/common/khrn_map.h | 49 +
.../middleware/khronos/common/khrn_mem.h | 30 +
.../middleware/khronos/common/khrn_misc.h | 90 +
.../middleware/khronos/common/khrn_pid_map.h | 51 +
.../khronos/common/khrn_pid_map_value.h | 64 +
.../khronos/common/khrn_server_pointermap.h | 48 +
.../khronos/dispatch/khrn_dispatch.h | 51 +
.../middleware/khronos/egl/egl_disp.h | 123 +
.../middleware/khronos/egl/egl_server.h | 255 +
.../middleware/khronos/ext/egl_khr_image.h | 68 +
.../khronos/vg/2708/vg_config_filler_4.h | 33 +
.../userland/middleware/khronos/vg/vg_image.h | 132 +
.../middleware/khronos/wf/wfc_server_stream.h | 95 +
.../middleware/openmaxil/CMakeLists.txt | 52 +
.../opensrc/helpers/libfdt/CMakeLists.txt | 13 +
.../userland/opensrc/helpers/libfdt/fdt.c | 251 +
.../userland/opensrc/helpers/libfdt/fdt.h | 111 +
.../opensrc/helpers/libfdt/fdt_empty_tree.c | 84 +
.../userland/opensrc/helpers/libfdt/fdt_ro.c | 679 ++
.../userland/opensrc/helpers/libfdt/fdt_rw.c | 494 ++
.../opensrc/helpers/libfdt/fdt_strerror.c | 96 +
.../userland/opensrc/helpers/libfdt/fdt_sw.c | 288 +
.../userland/opensrc/helpers/libfdt/fdt_wip.c | 118 +
.../userland/opensrc/helpers/libfdt/libfdt.h | 1653 +++++
.../opensrc/helpers/libfdt/libfdt_env.h | 118 +
.../opensrc/helpers/libfdt/libfdt_internal.h | 95 +
gfx/include/userland/pkgconfig/bcm_host.pc.in | 10 +
gfx/include/userland/pkgconfig/brcmegl.pc.in | 13 +
.../userland/pkgconfig/brcmglesv2.pc.in | 12 +
gfx/include/userland/pkgconfig/brcmvg.pc.in | 11 +
gfx/include/userland/pkgconfig/egl.pc.in | 13 +
gfx/include/userland/pkgconfig/glesv2.pc.in | 12 +
gfx/include/userland/pkgconfig/mmal.pc.in | 11 +
gfx/include/userland/pkgconfig/vcsm.pc.in | 10 +
gfx/include/userland/pkgconfig/vg.pc.in | 11 +
gfx/include/userland/vcfw/drivers/driver.h | 159 +
gfx/include/userland/vcfw/logging/logging.h | 350 +
.../vcfw/rtos/common/rtos_common_mem.h | 1360 ++++
gfx/include/userland/vcfw/rtos/rtos.h | 540 ++
gfx/include/userland/vcfw/vclib/vclib.h | 239 +
gfx/include/userland/vcinclude/common.h | 140 +
.../userland/vcinclude/vc_image_types.h | 34 +
gfx/include/userland/vcinclude/vcore.h | 64 +
814 files changed, 258409 insertions(+)
create mode 100644 gfx/include/userland/.gitignore
create mode 100644 gfx/include/userland/CMakeLists.txt
create mode 100644 gfx/include/userland/LICENCE
create mode 100644 gfx/include/userland/README.md
create mode 100644 gfx/include/userland/buildme
create mode 100644 gfx/include/userland/containers/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/asf/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/asf/asf_reader.c
create mode 100644 gfx/include/userland/containers/asf/asf_writer.c
create mode 100644 gfx/include/userland/containers/avi/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/avi/avi_reader.c
create mode 100644 gfx/include/userland/containers/avi/avi_writer.c
create mode 100644 gfx/include/userland/containers/binary/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/binary/binary_reader.c
create mode 100644 gfx/include/userland/containers/binary/binary_writer.c
create mode 100644 gfx/include/userland/containers/containers.h
create mode 100644 gfx/include/userland/containers/containers_codecs.h
create mode 100644 gfx/include/userland/containers/containers_types.h
create mode 100644 gfx/include/userland/containers/core/containers.c
create mode 100644 gfx/include/userland/containers/core/containers_bits.c
create mode 100644 gfx/include/userland/containers/core/containers_bits.h
create mode 100644 gfx/include/userland/containers/core/containers_bytestream.h
create mode 100644 gfx/include/userland/containers/core/containers_codecs.c
create mode 100644 gfx/include/userland/containers/core/containers_common.h
create mode 100644 gfx/include/userland/containers/core/containers_filters.c
create mode 100644 gfx/include/userland/containers/core/containers_filters.h
create mode 100644 gfx/include/userland/containers/core/containers_index.c
create mode 100644 gfx/include/userland/containers/core/containers_index.h
create mode 100644 gfx/include/userland/containers/core/containers_io.c
create mode 100644 gfx/include/userland/containers/core/containers_io.h
create mode 100644 gfx/include/userland/containers/core/containers_io_helpers.c
create mode 100644 gfx/include/userland/containers/core/containers_io_helpers.h
create mode 100644 gfx/include/userland/containers/core/containers_list.c
create mode 100644 gfx/include/userland/containers/core/containers_list.h
create mode 100644 gfx/include/userland/containers/core/containers_loader.c
create mode 100644 gfx/include/userland/containers/core/containers_loader.h
create mode 100644 gfx/include/userland/containers/core/containers_logging.c
create mode 100644 gfx/include/userland/containers/core/containers_logging.h
create mode 100644 gfx/include/userland/containers/core/containers_private.h
create mode 100644 gfx/include/userland/containers/core/containers_time.h
create mode 100644 gfx/include/userland/containers/core/containers_uri.c
create mode 100644 gfx/include/userland/containers/core/containers_uri.h
create mode 100644 gfx/include/userland/containers/core/containers_utils.c
create mode 100644 gfx/include/userland/containers/core/containers_utils.h
create mode 100644 gfx/include/userland/containers/core/containers_waveformat.h
create mode 100644 gfx/include/userland/containers/core/containers_writer_utils.c
create mode 100644 gfx/include/userland/containers/core/containers_writer_utils.h
create mode 100644 gfx/include/userland/containers/core/packetizers.c
create mode 100644 gfx/include/userland/containers/core/packetizers_private.h
create mode 100644 gfx/include/userland/containers/dummy/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/dummy/dummy_writer.c
create mode 100644 gfx/include/userland/containers/flash/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/flash/flv_reader.c
create mode 100644 gfx/include/userland/containers/h264/avc1_packetizer.c
create mode 100644 gfx/include/userland/containers/io/io_file.c
create mode 100644 gfx/include/userland/containers/io/io_http.c
create mode 100644 gfx/include/userland/containers/io/io_net.c
create mode 100644 gfx/include/userland/containers/io/io_null.c
create mode 100644 gfx/include/userland/containers/io/io_pktfile.c
create mode 100644 gfx/include/userland/containers/metadata/id3/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/metadata/id3/id3_metadata_reader.c
create mode 100644 gfx/include/userland/containers/metadata/id3/id3_metadata_strings.h
create mode 100644 gfx/include/userland/containers/mkv/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/mkv/matroska_reader.c
create mode 100644 gfx/include/userland/containers/mp4/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/mp4/mp4_common.h
create mode 100644 gfx/include/userland/containers/mp4/mp4_reader.c
create mode 100644 gfx/include/userland/containers/mp4/mp4_writer.c
create mode 100644 gfx/include/userland/containers/mpeg/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/mpeg/ps_reader.c
create mode 100644 gfx/include/userland/containers/mpga/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/mpga/mpga_common.h
create mode 100644 gfx/include/userland/containers/mpga/mpga_packetizer.c
create mode 100644 gfx/include/userland/containers/mpga/mpga_reader.c
create mode 100644 gfx/include/userland/containers/mpgv/mpgv_packetizer.c
create mode 100644 gfx/include/userland/containers/net/net_sockets.h
create mode 100644 gfx/include/userland/containers/net/net_sockets_bsd.c
create mode 100644 gfx/include/userland/containers/net/net_sockets_bsd.h
create mode 100644 gfx/include/userland/containers/net/net_sockets_common.c
create mode 100644 gfx/include/userland/containers/net/net_sockets_null.c
create mode 100644 gfx/include/userland/containers/net/net_sockets_priv.h
create mode 100644 gfx/include/userland/containers/net/net_sockets_win32.c
create mode 100644 gfx/include/userland/containers/net/net_sockets_win32.h
create mode 100644 gfx/include/userland/containers/packetizers.h
create mode 100644 gfx/include/userland/containers/pcm/pcm_packetizer.c
create mode 100644 gfx/include/userland/containers/qsynth/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/qsynth/qsynth_reader.c
create mode 100644 gfx/include/userland/containers/raw/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/raw/raw_video_common.h
create mode 100644 gfx/include/userland/containers/raw/raw_video_reader.c
create mode 100644 gfx/include/userland/containers/raw/raw_video_writer.c
create mode 100644 gfx/include/userland/containers/rcv/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/rcv/rcv_reader.c
create mode 100644 gfx/include/userland/containers/rtp/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/rtp/rtp_base64.c
create mode 100644 gfx/include/userland/containers/rtp/rtp_base64.h
create mode 100644 gfx/include/userland/containers/rtp/rtp_h264.c
create mode 100644 gfx/include/userland/containers/rtp/rtp_h264.h
create mode 100644 gfx/include/userland/containers/rtp/rtp_mpeg4.c
create mode 100644 gfx/include/userland/containers/rtp/rtp_mpeg4.h
create mode 100644 gfx/include/userland/containers/rtp/rtp_priv.h
create mode 100644 gfx/include/userland/containers/rtp/rtp_reader.c
create mode 100644 gfx/include/userland/containers/rtsp/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/rtsp/rtsp_reader.c
create mode 100644 gfx/include/userland/containers/rv9/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/rv9/rv9_reader.c
create mode 100644 gfx/include/userland/containers/simple/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/simple/simple_common.h
create mode 100644 gfx/include/userland/containers/simple/simple_reader.c
create mode 100644 gfx/include/userland/containers/simple/simple_writer.c
create mode 100644 gfx/include/userland/containers/test/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/test/autotest.cpp
create mode 100644 gfx/include/userland/containers/test/check_frame_int.c
create mode 100644 gfx/include/userland/containers/test/crc_32.c
create mode 100644 gfx/include/userland/containers/test/datagram_receiver.c
create mode 100644 gfx/include/userland/containers/test/datagram_sender.c
create mode 100644 gfx/include/userland/containers/test/dump_pktfile.c
create mode 100644 gfx/include/userland/containers/test/nb_io.h
create mode 100644 gfx/include/userland/containers/test/nb_io_unix.c
create mode 100644 gfx/include/userland/containers/test/nb_io_win32.c
create mode 100644 gfx/include/userland/containers/test/rtp_decoder.c
create mode 100644 gfx/include/userland/containers/test/stream_client.c
create mode 100644 gfx/include/userland/containers/test/stream_server.c
create mode 100644 gfx/include/userland/containers/test/test.c
create mode 100644 gfx/include/userland/containers/test/test_bits.c
create mode 100644 gfx/include/userland/containers/test/test_uri.c
create mode 100644 gfx/include/userland/containers/test/uri_pipe.c
create mode 100644 gfx/include/userland/containers/wav/CMakeLists.txt
create mode 100644 gfx/include/userland/containers/wav/wav_reader.c
create mode 100644 gfx/include/userland/helpers/dtoverlay/CMakeLists.txt
create mode 100644 gfx/include/userland/helpers/dtoverlay/dtoverlay.c
create mode 100644 gfx/include/userland/helpers/dtoverlay/dtoverlay.h
create mode 100644 gfx/include/userland/helpers/v3d/v3d_common.h
create mode 100644 gfx/include/userland/helpers/v3d/v3d_ver.h
create mode 100644 gfx/include/userland/helpers/vc_image/metadata_fourcc.h
create mode 100644 gfx/include/userland/helpers/vc_image/vc_image.h
create mode 100644 gfx/include/userland/helpers/vc_image/vc_image_helper.h
create mode 100644 gfx/include/userland/helpers/vc_image/vc_image_metadata.h
create mode 100644 gfx/include/userland/host_applications/android/apps/vidtex/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/android/apps/vidtex/applog.h
create mode 100644 gfx/include/userland/host_applications/android/apps/vidtex/launcher.h
create mode 100644 gfx/include/userland/host_applications/android/apps/vidtex/launcher_rpi.c
create mode 100644 gfx/include/userland/host_applications/android/apps/vidtex/launcher_rpi.h
create mode 100644 gfx/include/userland/host_applications/android/apps/vidtex/main.cpp
create mode 100644 gfx/include/userland/host_applications/android/apps/vidtex/svp.c
create mode 100644 gfx/include/userland/host_applications/android/apps/vidtex/svp.h
create mode 100644 gfx/include/userland/host_applications/android/apps/vidtex/vidtex.c
create mode 100644 gfx/include/userland/host_applications/android/apps/vidtex/vidtex.h
create mode 100644 gfx/include/userland/host_applications/framework/common/host_ilcore.h
create mode 100644 gfx/include/userland/host_applications/framework/common/ilcore.c
create mode 100644 gfx/include/userland/host_applications/linux/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/dtmerge/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/dtmerge/dtmerge.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/dtoverlay/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/dtoverlay/dtoverlay-post
create mode 100644 gfx/include/userland/host_applications/linux/apps/dtoverlay/dtoverlay-pre
create mode 100644 gfx/include/userland/host_applications/linux/apps/dtoverlay/dtoverlay_main.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/dtoverlay/utils.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/dtoverlay/utils.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/gencmd/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/gencmd/gencmd.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/Makefile.include
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/README
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_audio/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_audio/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_audio/audio.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_audio/audioplay.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_audio/sinewave.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_dispmanx/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_dispmanx/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_dispmanx/dispmanx.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_encode/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_encode/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_encode/encode.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_base.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_shaders.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_trans.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/gpu_fft_twiddles.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hello_fft.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hello_fft_2d.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hello_fft_2d_bitmap.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_1024k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_128k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_16k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_1k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2048k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_256k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_2k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_32k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4096k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_4k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_512k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_64k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_8k.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/hex/shader_trans.hex
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/mailbox.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/mailbox.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft.qinc
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1024k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_128k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_16k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_1k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2048k.qinc
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_256k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_2k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_32k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4096k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_4k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_512k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_64k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_8k.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_ex.qinc
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_fft/qasm/gpu_fft_trans.qasm
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_font/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_font/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_font/Vera.ttf
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_font/main.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_jpeg/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_jpeg/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_jpeg/jpeg.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_mmal_encode/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_mmal_encode/mmal_encode.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_teapot/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_teapot/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_teapot/README.md
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_teapot/cube_texture_and_coords.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_teapot/models.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_teapot/models.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_teapot/teapot.obj.dat
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_teapot/triangle.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_teapot/triangle.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_teapot/video.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_tiger/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_tiger/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_tiger/license.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_tiger/main.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_tiger/readme.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_tiger/tiger.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_tiger/tiger.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_triangle/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_triangle/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_triangle/cube_texture_and_coords.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_triangle/triangle.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_triangle2/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_triangle2/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_triangle2/triangle2.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_video/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_video/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_video/README
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_video/video.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_videocube/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_videocube/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_videocube/README.md
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_videocube/cube_texture_and_coords.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_videocube/triangle.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_videocube/triangle.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_videocube/video.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_world/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_world/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/hello_world/world.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/libs/ilclient/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/libs/ilclient/ilclient.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/libs/ilclient/ilclient.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/libs/ilclient/ilcore.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/libs/vgfont/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/libs/vgfont/font.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/libs/vgfont/graphics.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/libs/vgfont/graphics_x_private.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/libs/vgfont/vgfont.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/libs/vgfont/vgft.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/hello_pi/rebuild.sh
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/Doxyfile
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/Makefile
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/README.md
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiCLI.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiCLI.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiCamControl.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiCamControl.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiCommonSettings.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiCommonSettings.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiGPS.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiGPS.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiHelpers.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiHelpers.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiPreview.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiPreview.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiStill.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiStillYUV.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiTex.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiTex.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiTexUtil.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiTexUtil.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiVid.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/RaspiVidYUV.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/cube_texture_and_coords.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/mirror.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/mirror.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/models.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/models.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/sobel.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/sobel.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/square.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/square.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/teapot.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/teapot.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/vcsm_square.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/yuv.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gl_scenes/yuv.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/gps.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/imv_examples/README.md
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/imv_examples/imv2pgm.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/imv_examples/imv2txt.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/imv_examples/plot.par
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/libgps_loader.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/libgps_loader.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/tga.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/raspicam/tga.h
create mode 100644 gfx/include/userland/host_applications/linux/apps/smem/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/smem/smem.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/tvservice/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/tvservice/tvservice.c
create mode 100644 gfx/include/userland/host_applications/linux/apps/vcmailbox/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/apps/vcmailbox/vcmailbox.c
create mode 100644 gfx/include/userland/host_applications/linux/kernel_headers/vmcs_sm_ioctl.h
create mode 100644 gfx/include/userland/host_applications/linux/libs/bcm_host/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/libs/bcm_host/bcm_host.c
create mode 100644 gfx/include/userland/host_applications/linux/libs/bcm_host/include/bcm_host.h
create mode 100644 gfx/include/userland/host_applications/linux/libs/debug_sym/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/libs/debug_sym/debug_sym.c
create mode 100644 gfx/include/userland/host_applications/linux/libs/debug_sym/debug_sym.h
create mode 100644 gfx/include/userland/host_applications/linux/libs/sm/CMakeLists.txt
create mode 100644 gfx/include/userland/host_applications/linux/libs/sm/user-vcsm.c
create mode 100644 gfx/include/userland/host_applications/linux/libs/sm/user-vcsm.h
create mode 100644 gfx/include/userland/host_applications/vmcs/test_apps/mmalcam/mmalcam.c
create mode 100644 gfx/include/userland/host_applications/vmcs/test_apps/mmalcam/mmalcam.h
create mode 100644 gfx/include/userland/host_applications/vmcs/test_apps/mmalcam/viewfinder.c
create mode 100644 gfx/include/userland/host_applications/vmcs/test_apps/mmalplay/mmalplay.c
create mode 100644 gfx/include/userland/host_applications/vmcs/test_apps/mmalplay/mmalplay.h
create mode 100644 gfx/include/userland/host_applications/vmcs/test_apps/mmalplay/playback.c
create mode 100644 gfx/include/userland/host_applications/vmcs/vmcs_config.h.in
create mode 100644 gfx/include/userland/host_support/include/vc_debug_sym.h
create mode 100644 gfx/include/userland/host_support/include/vc_mem.h
create mode 100644 gfx/include/userland/interface/khronos/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/khronos/common/abstract/khrn_client_platform_filler_abstract.h
create mode 100644 gfx/include/userland/interface/khronos/common/direct/khrn_client_platform_filler_direct.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client.c
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_cache.c
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_cache.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_check_types.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_cr.c
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_global_image_map.c
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_global_image_map.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_mangle.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_platform.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_pointermap.c
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_pointermap.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_rpc.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_unmangle.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_vector.c
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_client_vector.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_color.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_common.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_generic_map.c
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_generic_map.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_hash.c
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_hash.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_hash_asm.s
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_ids.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_image.c
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_image.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_math.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_misc_impl.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_util.c
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_util.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_int_util_cr.h
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_options.c
create mode 100644 gfx/include/userland/interface/khronos/common/khrn_options.h
create mode 100644 gfx/include/userland/interface/khronos/common/linux/khrn_client_platform_linux.c
create mode 100644 gfx/include/userland/interface/khronos/common/linux/khrn_client_rpc_linux.c
create mode 100644 gfx/include/userland/interface/khronos/common/openwfc/khrn_client_platform_openwfc.c
create mode 100644 gfx/include/userland/interface/khronos/common/vcos/khrn_client_platform_filler_vcos.h
create mode 100644 gfx/include/userland/interface/khronos/common/vcos_vchiq/khrn_client_platform_filler_vcos_vchiq.h
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_client.c
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_client_config.c
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_client_config.h
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_client_config_cr.c
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_client_context.c
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_client_context.h
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_client_cr.c
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_client_get_proc.c
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_client_surface.c
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_client_surface.h
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_int.h
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_int_config.h
create mode 100644 gfx/include/userland/interface/khronos/egl/egl_int_impl.h
create mode 100644 gfx/include/userland/interface/khronos/ext/egl_brcm_driver_monitor_client.c
create mode 100644 gfx/include/userland/interface/khronos/ext/egl_brcm_driver_monitor_client.h
create mode 100644 gfx/include/userland/interface/khronos/ext/egl_brcm_flush_client.c
create mode 100644 gfx/include/userland/interface/khronos/ext/egl_brcm_global_image_client.c
create mode 100644 gfx/include/userland/interface/khronos/ext/egl_brcm_perf_monitor_client.c
create mode 100644 gfx/include/userland/interface/khronos/ext/egl_brcm_perf_monitor_client.h
create mode 100644 gfx/include/userland/interface/khronos/ext/egl_khr_image_client.c
create mode 100644 gfx/include/userland/interface/khronos/ext/egl_khr_lock_surface_client.c
create mode 100644 gfx/include/userland/interface/khronos/ext/egl_khr_sync_client.c
create mode 100644 gfx/include/userland/interface/khronos/ext/egl_khr_sync_client.h
create mode 100644 gfx/include/userland/interface/khronos/ext/egl_openmaxil_client.c
create mode 100644 gfx/include/userland/interface/khronos/ext/egl_openmaxil_client.h
create mode 100644 gfx/include/userland/interface/khronos/ext/ext_gl_debug_marker.c
create mode 100644 gfx/include/userland/interface/khronos/ext/gl_oes_draw_texture_client.c
create mode 100644 gfx/include/userland/interface/khronos/ext/gl_oes_egl_image_client.c
create mode 100644 gfx/include/userland/interface/khronos/ext/gl_oes_framebuffer_object.c
create mode 100644 gfx/include/userland/interface/khronos/ext/gl_oes_map_buffer.c
create mode 100644 gfx/include/userland/interface/khronos/ext/gl_oes_matrix_palette_client.c
create mode 100644 gfx/include/userland/interface/khronos/ext/gl_oes_query_matrix_client.c
create mode 100644 gfx/include/userland/interface/khronos/glxx/gl11_int_config.h
create mode 100644 gfx/include/userland/interface/khronos/glxx/gl11_int_impl.h
create mode 100644 gfx/include/userland/interface/khronos/glxx/gl20_int_impl.h
create mode 100644 gfx/include/userland/interface/khronos/glxx/glxx_client.c
create mode 100644 gfx/include/userland/interface/khronos/glxx/glxx_client.h
create mode 100644 gfx/include/userland/interface/khronos/glxx/glxx_int_attrib.h
create mode 100644 gfx/include/userland/interface/khronos/glxx/glxx_int_config.h
create mode 100644 gfx/include/userland/interface/khronos/glxx/glxx_int_impl.h
create mode 100644 gfx/include/userland/interface/khronos/include/EGL/egl.h
create mode 100644 gfx/include/userland/interface/khronos/include/EGL/eglext.h
create mode 100644 gfx/include/userland/interface/khronos/include/EGL/eglext_android.h
create mode 100644 gfx/include/userland/interface/khronos/include/EGL/eglext_brcm.h
create mode 100644 gfx/include/userland/interface/khronos/include/EGL/eglext_nvidia.h
create mode 100644 gfx/include/userland/interface/khronos/include/EGL/eglplatform.h
create mode 100644 gfx/include/userland/interface/khronos/include/GLES/gl.h
create mode 100644 gfx/include/userland/interface/khronos/include/GLES/glext.h
create mode 100644 gfx/include/userland/interface/khronos/include/GLES/glplatform.h
create mode 100644 gfx/include/userland/interface/khronos/include/GLES2/gl2.h
create mode 100644 gfx/include/userland/interface/khronos/include/GLES2/gl2ext.h
create mode 100644 gfx/include/userland/interface/khronos/include/GLES2/gl2platform.h
create mode 100644 gfx/include/userland/interface/khronos/include/KHR/khrplatform.h
create mode 100644 gfx/include/userland/interface/khronos/include/VG/openvg.h
create mode 100644 gfx/include/userland/interface/khronos/include/VG/vgext.h
create mode 100644 gfx/include/userland/interface/khronos/include/VG/vgplatform.h
create mode 100644 gfx/include/userland/interface/khronos/include/VG/vgu.h
create mode 100644 gfx/include/userland/interface/khronos/include/WF/wfc.h
create mode 100644 gfx/include/userland/interface/khronos/include/WF/wfcplatform.h
create mode 100644 gfx/include/userland/interface/khronos/vg/vg_client.c
create mode 100644 gfx/include/userland/interface/khronos/vg/vg_client.h
create mode 100644 gfx/include/userland/interface/khronos/vg/vg_int.h
create mode 100644 gfx/include/userland/interface/khronos/vg/vg_int_config.h
create mode 100644 gfx/include/userland/interface/khronos/vg/vg_int_impl.h
create mode 100644 gfx/include/userland/interface/khronos/vg/vg_int_mat3x3.c
create mode 100644 gfx/include/userland/interface/khronos/vg/vg_int_mat3x3.h
create mode 100644 gfx/include/userland/interface/khronos/vg/vg_int_util.h
create mode 100644 gfx/include/userland/interface/khronos/wf/wfc_client.c
create mode 100644 gfx/include/userland/interface/khronos/wf/wfc_client_ipc.c
create mode 100644 gfx/include/userland/interface/khronos/wf/wfc_client_ipc.h
create mode 100644 gfx/include/userland/interface/khronos/wf/wfc_client_server_api.c
create mode 100644 gfx/include/userland/interface/khronos/wf/wfc_client_stream.c
create mode 100644 gfx/include/userland/interface/khronos/wf/wfc_client_stream.h
create mode 100644 gfx/include/userland/interface/khronos/wf/wfc_int.h
create mode 100644 gfx/include/userland/interface/khronos/wf/wfc_ipc.h
create mode 100644 gfx/include/userland/interface/khronos/wf/wfc_server_api.h
create mode 100644 gfx/include/userland/interface/mmal/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/mmal/client/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/mmal/client/brcmjpeg/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/mmal/client/brcmjpeg/brcmjpeg.c
create mode 100644 gfx/include/userland/interface/mmal/client/brcmjpeg/brcmjpeg.h
create mode 100644 gfx/include/userland/interface/mmal/client/brcmjpeg/brcmjpeg_test.c
create mode 100644 gfx/include/userland/interface/mmal/components/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/mmal/components/aaf_audio_render.cpp
create mode 100644 gfx/include/userland/interface/mmal/components/aggregator.c
create mode 100644 gfx/include/userland/interface/mmal/components/android_media_codec.cpp
create mode 100644 gfx/include/userland/interface/mmal/components/artificial_camera.c
create mode 100644 gfx/include/userland/interface/mmal/components/avcodec_audio_decoder.c
create mode 100644 gfx/include/userland/interface/mmal/components/avcodec_video_decoder.c
create mode 100644 gfx/include/userland/interface/mmal/components/clock.c
create mode 100644 gfx/include/userland/interface/mmal/components/container_reader.c
create mode 100644 gfx/include/userland/interface/mmal/components/copy.c
create mode 100644 gfx/include/userland/interface/mmal/components/null_sink.c
create mode 100644 gfx/include/userland/interface/mmal/components/passthrough.c
create mode 100644 gfx/include/userland/interface/mmal/components/scheduler.c
create mode 100644 gfx/include/userland/interface/mmal/components/sdl_audio_render.c
create mode 100644 gfx/include/userland/interface/mmal/components/sdl_video_render.c
create mode 100644 gfx/include/userland/interface/mmal/components/spdif.c
create mode 100644 gfx/include/userland/interface/mmal/components/splitter.c
create mode 100644 gfx/include/userland/interface/mmal/core/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_buffer.c
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_buffer_private.h
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_clock.c
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_clock_private.h
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_component.c
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_component_private.h
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_core_private.h
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_events.c
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_events_private.h
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_format.c
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_logging.c
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_pool.c
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_port.c
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_port_clock.c
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_port_private.h
create mode 100644 gfx/include/userland/interface/mmal/core/mmal_queue.c
create mode 100644 gfx/include/userland/interface/mmal/mmal.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_buffer.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_clock.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_common.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_component.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_encodings.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_events.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_format.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_logging.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_parameters.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_parameters_audio.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_parameters_camera.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_parameters_clock.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_parameters_common.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_parameters_video.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_pool.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_port.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_queue.h
create mode 100644 gfx/include/userland/interface/mmal/mmal_types.h
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/README.md
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx.h
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_buffer.c
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_buffer.h
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_commands.c
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_commands.h
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_core.c
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_logging.c
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_logging.h
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_marks.c
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_marks.h
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_parameters.c
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_parameters.h
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_registry.c
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_registry.h
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_roles.c
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_roles.h
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_util_params.c
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_util_params.h
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_util_params_audio.c
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_util_params_camera.c
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_util_params_common.h
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_util_params_misc.c
create mode 100644 gfx/include/userland/interface/mmal/openmaxil/mmalomx_util_params_video.c
create mode 100644 gfx/include/userland/interface/mmal/test/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/mmal/test/examples/example_basic_1.c
create mode 100644 gfx/include/userland/interface/mmal/test/examples/example_basic_2.c
create mode 100644 gfx/include/userland/interface/mmal/test/examples/example_connections.c
create mode 100644 gfx/include/userland/interface/mmal/test/examples/example_graph.c
create mode 100644 gfx/include/userland/interface/mmal/util/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_component_wrapper.c
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_component_wrapper.h
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_connection.c
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_connection.h
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_default_components.h
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_graph.c
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_graph.h
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_il.c
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_il.h
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_list.c
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_list.h
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_param_convert.c
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_param_convert.h
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_util.c
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_util.h
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_util_params.c
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_util_params.h
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_util_rational.c
create mode 100644 gfx/include/userland/interface/mmal/util/mmal_util_rational.h
create mode 100644 gfx/include/userland/interface/mmal/vc/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_api.c
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_api.h
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_api_drm.c
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_api_drm.h
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_client.c
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_client_priv.h
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_dbglog.h
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_diag.c
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_msgnames.c
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_msgnames.h
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_msgs.h
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_opaque_alloc.c
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_opaque_alloc.h
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_shm.c
create mode 100644 gfx/include/userland/interface/mmal/vc/mmal_vc_shm.h
create mode 100644 gfx/include/userland/interface/peer/vc_vchi_dispmanx_common.h
create mode 100644 gfx/include/userland/interface/vchi/common/endian.h
create mode 100644 gfx/include/userland/interface/vchi/connections/connection.h
create mode 100644 gfx/include/userland/interface/vchi/message_drivers/message.h
create mode 100644 gfx/include/userland/interface/vchi/vchi.h
create mode 100644 gfx/include/userland/interface/vchi/vchi_cfg.h
create mode 100644 gfx/include/userland/interface/vchi/vchi_cfg_internal.h
create mode 100644 gfx/include/userland/interface/vchi/vchi_common.h
create mode 100644 gfx/include/userland/interface/vchi/vchi_mh.h
create mode 100644 gfx/include/userland/interface/vchiq_arm/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/vchiq_arm/vchiq.h
create mode 100644 gfx/include/userland/interface/vchiq_arm/vchiq_cfg.h
create mode 100644 gfx/include/userland/interface/vchiq_arm/vchiq_if.h
create mode 100644 gfx/include/userland/interface/vchiq_arm/vchiq_ioctl.h
create mode 100644 gfx/include/userland/interface/vchiq_arm/vchiq_lib.c
create mode 100644 gfx/include/userland/interface/vchiq_arm/vchiq_test.c
create mode 100644 gfx/include/userland/interface/vchiq_arm/vchiq_test.h
create mode 100644 gfx/include/userland/interface/vchiq_arm/vchiq_test_if.h
create mode 100644 gfx/include/userland/interface/vchiq_arm/vchiq_util.c
create mode 100644 gfx/include/userland/interface/vchiq_arm/vchiq_util.h
create mode 100644 gfx/include/userland/interface/vcos/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/vcos/generic/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_abort.c
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_cmd.c
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_common.h
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_deprecated.h
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_generic_blockpool.c
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_generic_blockpool.h
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_generic_event_flags.c
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_generic_event_flags.h
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_generic_named_sem.c
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_generic_named_sem.h
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_generic_quickslow_mutex.h
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_generic_reentrant_mtx.c
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_generic_reentrant_mtx.h
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_generic_safe_string.c
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_generic_tls.h
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_init.c
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_joinable_thread_from_plain.h
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_latch_from_sem.h
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_logcat.c
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_mem_from_malloc.c
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_mem_from_malloc.h
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_msgqueue.c
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_mutexes_are_reentrant.h
create mode 100644 gfx/include/userland/interface/vcos/generic/vcos_thread_reaper.h
create mode 100644 gfx/include/userland/interface/vcos/glibc/vcos_backtrace.c
create mode 100644 gfx/include/userland/interface/vcos/pthreads/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/vcos/pthreads/vcos_dlfcn.c
create mode 100644 gfx/include/userland/interface/vcos/pthreads/vcos_futex_mutex.h
create mode 100644 gfx/include/userland/interface/vcos/pthreads/vcos_platform.h
create mode 100644 gfx/include/userland/interface/vcos/pthreads/vcos_platform_types.h
create mode 100644 gfx/include/userland/interface/vcos/pthreads/vcos_pthreads.c
create mode 100644 gfx/include/userland/interface/vcos/user_nodefs.h
create mode 100644 gfx/include/userland/interface/vcos/vcos.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_assert.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_atomic_flags.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_attr.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_blockpool.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_build_info.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_cfg.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_cmd.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_ctype.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_dlfcn.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_event.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_event_flags.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_init.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_inttypes.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_isr.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_legacy_isr.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_logging.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_logging_control.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_lowlevel_thread.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_mem.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_mempool.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_msgqueue.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_mutex.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_named_semaphore.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_once.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_queue.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_quickslow_mutex.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_reentrant_mutex.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_semaphore.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_stdbool.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_stdint.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_string.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_thread.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_thread_attr.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_timer.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_tls.h
create mode 100644 gfx/include/userland/interface/vcos/vcos_types.h
create mode 100644 gfx/include/userland/interface/vctypes/vc_display_types.h
create mode 100644 gfx/include/userland/interface/vctypes/vc_image_structs.h
create mode 100644 gfx/include/userland/interface/vctypes/vc_image_types.h
create mode 100644 gfx/include/userland/interface/vmcs_host/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/vmcs_host/khronos/IL/OMX_Audio.h
create mode 100644 gfx/include/userland/interface/vmcs_host/khronos/IL/OMX_Broadcom.h
create mode 100644 gfx/include/userland/interface/vmcs_host/khronos/IL/OMX_Component.h
create mode 100644 gfx/include/userland/interface/vmcs_host/khronos/IL/OMX_Core.h
create mode 100644 gfx/include/userland/interface/vmcs_host/khronos/IL/OMX_ILCS.h
create mode 100644 gfx/include/userland/interface/vmcs_host/khronos/IL/OMX_IVCommon.h
create mode 100644 gfx/include/userland/interface/vmcs_host/khronos/IL/OMX_Image.h
create mode 100644 gfx/include/userland/interface/vmcs_host/khronos/IL/OMX_Index.h
create mode 100644 gfx/include/userland/interface/vmcs_host/khronos/IL/OMX_Other.h
create mode 100644 gfx/include/userland/interface/vmcs_host/khronos/IL/OMX_Types.h
create mode 100644 gfx/include/userland/interface/vmcs_host/khronos/IL/OMX_Video.h
create mode 100644 gfx/include/userland/interface/vmcs_host/linux/vcfiled/CMakeLists.txt
create mode 100644 gfx/include/userland/interface/vmcs_host/linux/vcfiled/vcfiled.c
create mode 100644 gfx/include/userland/interface/vmcs_host/linux/vcfiled/vcfiled_check.c
create mode 100644 gfx/include/userland/interface/vmcs_host/linux/vcfiled/vcfiled_check.h
create mode 100644 gfx/include/userland/interface/vmcs_host/linux/vcfiled/vcfiled_lock_test.c
create mode 100644 gfx/include/userland/interface/vmcs_host/linux/vcfilesys.c
create mode 100644 gfx/include/userland/interface/vmcs_host/linux/vchost_config.h
create mode 100644 gfx/include/userland/interface/vmcs_host/linux/vcmisc.c
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_cec.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_cecservice.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_cecservice_defs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_cma.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_dispmanx.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_dispmanx_types.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_dispservice_defs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_dispservice_x_defs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_fileservice_defs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_gencmd_defs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_hdmi.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_hdmi_property.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_ilcs_defs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_imageconv_defs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_sdtv.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_service_common.c
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_service_common.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_tvservice.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_tvservice_defs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_audioserv_defs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_bufman.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_bufman_defs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_cecservice.c
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_dispmanx.c
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_dispmanx.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_fileservice_defs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_filesys.c
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_filesys.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_gencmd.c
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_gencmd.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_gpuserv.c
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_gpuserv.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vc_vchi_tvservice.c
create mode 100644 gfx/include/userland/interface/vmcs_host/vcfilesys.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vcfilesys_defs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vcgencmd.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vchost.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vchost_platform_config.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vcilcs.c
create mode 100644 gfx/include/userland/interface/vmcs_host/vcilcs.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vcilcs_common.c
create mode 100644 gfx/include/userland/interface/vmcs_host/vcilcs_common.h
create mode 100644 gfx/include/userland/interface/vmcs_host/vcilcs_in.c
create mode 100644 gfx/include/userland/interface/vmcs_host/vcilcs_out.c
create mode 100644 gfx/include/userland/makefiles/cmake/arm-linux.cmake
create mode 100644 gfx/include/userland/makefiles/cmake/cmake_config.h.in
create mode 100644 gfx/include/userland/makefiles/cmake/global_settings.cmake
create mode 100644 gfx/include/userland/makefiles/cmake/srcs/test-mtrace.c
create mode 100644 gfx/include/userland/makefiles/cmake/toolchains/arm-linux-gnueabihf.cmake
create mode 100644 gfx/include/userland/makefiles/cmake/toolchains/bcm2708-glibc-linux.cmake
create mode 100644 gfx/include/userland/makefiles/cmake/vmcs.cmake
create mode 100644 gfx/include/userland/middleware/dlloader/dlfcn.h
create mode 100644 gfx/include/userland/middleware/imageconv/imageconv.h
create mode 100644 gfx/include/userland/middleware/khronos/common/2708/khrn_interlock_filler_4.h
create mode 100644 gfx/include/userland/middleware/khronos/common/2708/khrn_prod_4.h
create mode 100644 gfx/include/userland/middleware/khronos/common/khrn_hw.h
create mode 100644 gfx/include/userland/middleware/khronos/common/khrn_image.h
create mode 100644 gfx/include/userland/middleware/khronos/common/khrn_interlock.h
create mode 100644 gfx/include/userland/middleware/khronos/common/khrn_map.h
create mode 100644 gfx/include/userland/middleware/khronos/common/khrn_mem.h
create mode 100644 gfx/include/userland/middleware/khronos/common/khrn_misc.h
create mode 100644 gfx/include/userland/middleware/khronos/common/khrn_pid_map.h
create mode 100644 gfx/include/userland/middleware/khronos/common/khrn_pid_map_value.h
create mode 100644 gfx/include/userland/middleware/khronos/common/khrn_server_pointermap.h
create mode 100644 gfx/include/userland/middleware/khronos/dispatch/khrn_dispatch.h
create mode 100644 gfx/include/userland/middleware/khronos/egl/egl_disp.h
create mode 100644 gfx/include/userland/middleware/khronos/egl/egl_server.h
create mode 100644 gfx/include/userland/middleware/khronos/ext/egl_khr_image.h
create mode 100644 gfx/include/userland/middleware/khronos/vg/2708/vg_config_filler_4.h
create mode 100644 gfx/include/userland/middleware/khronos/vg/vg_image.h
create mode 100644 gfx/include/userland/middleware/khronos/wf/wfc_server_stream.h
create mode 100644 gfx/include/userland/middleware/openmaxil/CMakeLists.txt
create mode 100644 gfx/include/userland/opensrc/helpers/libfdt/CMakeLists.txt
create mode 100644 gfx/include/userland/opensrc/helpers/libfdt/fdt.c
create mode 100644 gfx/include/userland/opensrc/helpers/libfdt/fdt.h
create mode 100644 gfx/include/userland/opensrc/helpers/libfdt/fdt_empty_tree.c
create mode 100644 gfx/include/userland/opensrc/helpers/libfdt/fdt_ro.c
create mode 100644 gfx/include/userland/opensrc/helpers/libfdt/fdt_rw.c
create mode 100644 gfx/include/userland/opensrc/helpers/libfdt/fdt_strerror.c
create mode 100644 gfx/include/userland/opensrc/helpers/libfdt/fdt_sw.c
create mode 100644 gfx/include/userland/opensrc/helpers/libfdt/fdt_wip.c
create mode 100644 gfx/include/userland/opensrc/helpers/libfdt/libfdt.h
create mode 100644 gfx/include/userland/opensrc/helpers/libfdt/libfdt_env.h
create mode 100644 gfx/include/userland/opensrc/helpers/libfdt/libfdt_internal.h
create mode 100644 gfx/include/userland/pkgconfig/bcm_host.pc.in
create mode 100644 gfx/include/userland/pkgconfig/brcmegl.pc.in
create mode 100644 gfx/include/userland/pkgconfig/brcmglesv2.pc.in
create mode 100644 gfx/include/userland/pkgconfig/brcmvg.pc.in
create mode 100644 gfx/include/userland/pkgconfig/egl.pc.in
create mode 100644 gfx/include/userland/pkgconfig/glesv2.pc.in
create mode 100644 gfx/include/userland/pkgconfig/mmal.pc.in
create mode 100644 gfx/include/userland/pkgconfig/vcsm.pc.in
create mode 100644 gfx/include/userland/pkgconfig/vg.pc.in
create mode 100644 gfx/include/userland/vcfw/drivers/driver.h
create mode 100644 gfx/include/userland/vcfw/logging/logging.h
create mode 100644 gfx/include/userland/vcfw/rtos/common/rtos_common_mem.h
create mode 100644 gfx/include/userland/vcfw/rtos/rtos.h
create mode 100644 gfx/include/userland/vcfw/vclib/vclib.h
create mode 100644 gfx/include/userland/vcinclude/common.h
create mode 100644 gfx/include/userland/vcinclude/vc_image_types.h
create mode 100644 gfx/include/userland/vcinclude/vcore.h
diff --git a/gfx/include/userland/.gitignore b/gfx/include/userland/.gitignore
new file mode 100644
index 0000000000..63570f12a0
--- /dev/null
+++ b/gfx/include/userland/.gitignore
@@ -0,0 +1,32 @@
+# Build directory
+build/
+
+# Compiled Object files
+*.slo
+*.lo
+*.o
+
+# Compiled Dynamic libraries
+*.so
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+
+*.raw
+*.rgb
+*.bgr
+*.h264
+*.264
+*.yuyv
+*.uyvy
+*.yvyu
+*.vyuy
+*.i420
+*.yuv
+*.jpg
+*.avi
+*.pts
+*.ppm
+*.mkv
diff --git a/gfx/include/userland/CMakeLists.txt b/gfx/include/userland/CMakeLists.txt
new file mode 100644
index 0000000000..cfc8ae54f2
--- /dev/null
+++ b/gfx/include/userland/CMakeLists.txt
@@ -0,0 +1,132 @@
+cmake_minimum_required(VERSION 2.8)
+
+project(vmcs_host_apps)
+
+SET(PROJECT_VER_MAJOR 1)
+SET(PROJECT_VER_MINOR 0)
+SET(PROJECT_VER_PATCH 0)
+SET(PROJECT_VER "${PROJECT_VER_MAJOR}.${PROJECT_VER_MINOR}.${PROJECT_VER_PATCH}")
+SET(PROJECT_APIVER "${PROJECT_VER}")
+
+if(ARM64)
+ set(BUILD_MMAL FALSE)
+ set(BUILD_MMAL_APPS FALSE)
+else()
+ set(BUILD_MMAL TRUE)
+ set(BUILD_MMAL_APPS TRUE)
+endif()
+set(vmcs_root ${PROJECT_SOURCE_DIR})
+get_filename_component(VIDEOCORE_ROOT . ABSOLUTE)
+
+set(VCOS_PTHREADS_BUILD_SHARED TRUE)
+
+include(makefiles/cmake/global_settings.cmake)
+include(makefiles/cmake/arm-linux.cmake)
+include(makefiles/cmake/vmcs.cmake)
+
+enable_language(ASM)
+
+# Global include paths
+include_directories(host_applications/framework)
+include_directories(${PROJECT_SOURCE_DIR})
+include_directories(interface/vcos/pthreads)
+include_directories(interface/vmcs_host/linux)
+include_directories(interface/vmcs_host)
+include_directories(interface/vmcs_host/khronos)
+include_directories(interface/khronos/include)
+include_directories(${PROJECT_BINARY_DIR})
+include_directories(interface/vchiq_arm)
+#include_directories(tools/inet_transport)
+include_directories(host_support/include)
+
+# Global compiler flags
+if(CMAKE_COMPILER_IS_GNUCC)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-multichar -Wall -Wno-unused-but-set-variable -fPIC")
+endif()
+
+add_definitions(-D_REENTRANT)
+add_definitions(-DUSE_VCHIQ_ARM -DVCHI_BULK_ALIGN=1 -DVCHI_BULK_GRANULARITY=1)
+add_definitions(-DOMX_SKIP64BIT)
+add_definitions(-DEGL_SERVER_DISPMANX)
+add_definitions(-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64)
+add_definitions(-D_GNU_SOURCE)
+
+# do we actually need this?
+add_definitions(-D__VIDEOCORE4__)
+add_definitions(-DTV_SUPPORTED_MODE_NO_DEPRECATED)
+
+# add_definitions(-DKHRONOS_CLIENT_LOGGING)
+
+# Check for OpenWF-C value set via command line
+if(KHRONOS_EGL_PLATFORM MATCHES "openwfc")
+ add_definitions(-DKHRONOS_EGL_PLATFORM_OPENWFC)
+endif()
+
+# List of subsidiary CMakeLists
+add_subdirectory(interface/vcos)
+add_subdirectory(interface/vmcs_host)
+add_subdirectory(interface/vchiq_arm)
+if(NOT ARM64)
+ add_subdirectory(interface/khronos)
+endif()
+
+#add_subdirectory(opensrc/tools/lua)
+if(BUILD_MMAL)
+ include_directories(interface/mmal)
+ add_subdirectory(interface/mmal)
+ add_subdirectory(containers)
+endif()
+
+# VidTex supports Android and Linux
+if(BUILD_MMAL_APPS)
+add_subdirectory(host_applications/android/apps/vidtex)
+endif(BUILD_MMAL_APPS)
+
+if(NOT ARM64)
+ add_subdirectory(middleware/openmaxil)
+endif()
+
+# 3d demo code
+#if(NOT ANDROID)
+# add_subdirectory(thirdparty/applications/demos)
+# add_subdirectory(opensrc/applications/demos)
+#endif()
+
+#if(ENABLE_3D_TESTS)
+# add_subdirectory(thirdparty/applications/test)
+#endif()
+
+# FIXME: we should use a pre-packaged version of freetype
+# rather than the one included in the repo.
+#add_subdirectory(opensrc/helpers/freetype)
+#add_subdirectory(${PROJECT_SOURCE_DIR}/opensrc/helpers/fonts/ttf-bitstream-vera)
+
+# VMCS Host Applications
+#add_subdirectory(host_applications/framework)
+
+# add_subdirectory(interface/vchiq/test/win32)
+
+# Apps and libraries supporting Camera Tuning Tool
+#add_subdirectory(tools/inet_transport/linux)
+#add_subdirectory(host_support/vcstandalone)
+
+# add linux apps
+add_subdirectory(host_applications/linux)
+add_subdirectory(opensrc/helpers/libfdt)
+add_subdirectory(helpers/dtoverlay)
+
+set(vmcs_host_apps_VERSION_MAJOR 1)
+set(vmcs_host_apps_VERSION_MINOR 0)
+
+include_directories("${PROJECT_BINARY_DIR}")
+include(FindPkgConfig QUIET)
+if(PKG_CONFIG_FOUND)
+ # Produce a pkg-config file
+ foreach(PCFILE bcm_host.pc egl.pc glesv2.pc vg.pc brcmegl.pc brcmglesv2.pc brcmvg.pc vcsm.pc mmal.pc )
+ configure_file("pkgconfig/${PCFILE}.in" "${PCFILE}" @ONLY)
+ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PCFILE}"
+ DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/pkgconfig")
+ endforeach()
+endif()
+# Remove cache entry, if one added by command line
+unset(KHRONOS_EGL_PLATFORM CACHE)
diff --git a/gfx/include/userland/LICENCE b/gfx/include/userland/LICENCE
new file mode 100644
index 0000000000..dea4c26015
--- /dev/null
+++ b/gfx/include/userland/LICENCE
@@ -0,0 +1,26 @@
+Copyright (c) 2012, Broadcom Europe Ltd
+Copyright (c) 2015, Raspberry Pi (Trading) Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/gfx/include/userland/README.md b/gfx/include/userland/README.md
new file mode 100644
index 0000000000..de5754d784
--- /dev/null
+++ b/gfx/include/userland/README.md
@@ -0,0 +1,8 @@
+This repository contains the source code for the ARM side libraries used on Raspberry Pi.
+These typically are installed in /opt/vc/lib and includes source for the ARM side code to interface to:
+EGL, mmal, GLESv2, vcos, openmaxil, vchiq_arm, bcm_host, WFC, OpenVG.
+
+Use buildme to build. It requires cmake to be installed and an arm cross compiler. It is set up to use this one:
+https://github.com/raspberrypi/tools/tree/master/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian
+
+Note that this repository does not contain the source for the edidparser and vcdbg binaries due to licensing restrictions.
diff --git a/gfx/include/userland/buildme b/gfx/include/userland/buildme
new file mode 100644
index 0000000000..b8fd4408ef
--- /dev/null
+++ b/gfx/include/userland/buildme
@@ -0,0 +1,44 @@
+#!/bin/bash
+BUILDTYPE=Release
+
+if [ "$1" = "--debug" ]; then
+ BUILDTYPE=Debug
+ shift
+fi
+
+BUILDSUBDIR=`echo $BUILDTYPE | tr '[A-Z]' '[a-z]'`;
+
+if [ "armv6l" = `arch` ] || [ "armv7l" = `arch` ]; then
+ # Native compile on the Raspberry Pi
+ mkdir -p build/raspberry/$BUILDSUBDIR
+ pushd build/raspberry/$BUILDSUBDIR
+ cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE ../../..
+ if [ "armv6l" = `arch` ]; then
+ make
+ else
+ make -j4
+ fi
+ if [ "$1" != "" ]; then
+ sudo make install DESTDIR=$1
+ else
+ sudo make install
+ fi
+elif [ "$1" = "--native" ]; then
+ # Build natively on the host
+ mkdir -p build/native/$BUILDSUBDIR
+ pushd build/native/$BUILDSUBDIR
+ cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE ../../..
+ shift
+ make -j `nproc` $*
+else
+ # Cross compile on a more capable machine
+ mkdir -p build/arm-linux/$BUILDSUBDIR
+ pushd build/arm-linux/$BUILDSUBDIR
+ cmake -DCMAKE_TOOLCHAIN_FILE=../../../makefiles/cmake/toolchains/arm-linux-gnueabihf.cmake -DCMAKE_BUILD_TYPE=$BUILDTYPE ../../..
+ make -j `nproc`
+
+ if [ "$1" != "" ]; then
+ sudo make install DESTDIR=$1
+ fi
+fi
+popd
diff --git a/gfx/include/userland/containers/CMakeLists.txt b/gfx/include/userland/containers/CMakeLists.txt
new file mode 100644
index 0000000000..5570038c9a
--- /dev/null
+++ b/gfx/include/userland/containers/CMakeLists.txt
@@ -0,0 +1,124 @@
+SET( SOURCE_DIR . )
+
+# We support building both static and shared libraries
+if (NOT DEFINED LIBRARY_TYPE)
+set(LIBRARY_TYPE SHARED)
+endif (NOT DEFINED LIBRARY_TYPE)
+
+# Make sure the compiler can find the necessary include files
+include_directories (${SOURCE_DIR}/.. ${SOURCE_DIR}/../interface/vcos)
+
+# Needed for the container loader
+add_definitions(-DDL_PATH_PREFIX="${VMCS_PLUGIN_DIR}/")
+
+SET( GCC_COMPILER_FLAGS -Wall -g -O2 -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wcast-qual -Wwrite-strings -Wundef )
+SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -Wextra )#-Wno-missing-field-initializers )
+SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -std=c99 -D_POSIX_C_SOURCE=200112L )
+SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -Wno-missing-field-initializers )
+SET( GCC_COMPILER_FLAGS ${GCC_COMPILER_FLAGS} -Wno-unused-value )
+
+add_definitions( ${GCC_COMPILER_FLAGS} )
+
+# Containers core library
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers.c)
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_io.c)
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_io_helpers.c)
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_codecs.c)
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_utils.c)
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_writer_utils.c)
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_loader.c)
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_filters.c)
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_logging.c)
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_uri.c)
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_bits.c)
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_list.c)
+set(core_SRCS ${core_SRCS} ${SOURCE_DIR}/core/containers_index.c)
+
+# Containers io library
+set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_file.c)
+set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_null.c)
+set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_net.c)
+set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_pktfile.c)
+set(io_SRCS ${io_SRCS} ${SOURCE_DIR}/io/io_http.c)
+add_definitions( -DENABLE_CONTAINER_IO_HTTP )
+
+# Containers net library
+if (DEFINED MSVC)
+set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_common.c)
+set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_win32.c)
+elseif (DEFINED LINUX OR DEFINED UNIX)
+set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_common.c)
+set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_bsd.c)
+else (DEFINED MSVC)
+set(net_SRCS ${net_SRCS} ${SOURCE_DIR}/net/net_sockets_null.c)
+endif (DEFINED MSVC)
+set(extra_net_SRCS net_sockets_win32.c net_sockets_win32.h net_sockets_null.c)
+add_custom_target(containers_net_extra ALL
+ COMMAND touch ${extra_net_SRCS}
+ WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/containers/net)
+
+# Packetizers library
+set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/core/packetizers.c)
+set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/mpga/mpga_packetizer.c)
+set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/mpgv/mpgv_packetizer.c)
+set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/pcm/pcm_packetizer.c)
+set(packetizers_SRCS ${packetizers_SRCS} ${SOURCE_DIR}/h264/avc1_packetizer.c)
+
+add_library(containers ${LIBRARY_TYPE} ${core_SRCS} ${io_SRCS} ${net_SRCS} ${packetizers_SRCS})
+target_link_libraries(containers vcos)
+install(TARGETS containers DESTINATION lib)
+
+set(container_readers)
+set(container_writers)
+
+# Container modules
+add_subdirectory(mp4)
+set(container_readers ${container_readers} reader_mp4)
+set(container_writers ${container_writers} writer_mp4)
+add_subdirectory(mpeg)
+set(container_readers ${container_readers} reader_ps)
+add_subdirectory(mpga)
+set(container_readers ${container_readers} reader_mpga)
+add_subdirectory(binary)
+set(container_readers ${container_readers} reader_binary)
+set(container_writers ${container_writers} writer_binary)
+add_subdirectory(mkv)
+set(container_readers ${container_readers} reader_mkv)
+add_subdirectory(wav)
+set(container_readers ${container_readers} reader_wav)
+add_subdirectory(asf)
+set(container_readers ${container_readers} reader_asf)
+set(container_writers ${container_writers} writer_asf)
+add_subdirectory(flash)
+set(container_readers ${container_readers} reader_flv)
+add_subdirectory(avi)
+set(container_readers ${container_readers} reader_avi)
+set(container_writers ${container_writers} writer_avi)
+add_subdirectory(rtp)
+set(container_readers ${container_readers} reader_rtp)
+add_subdirectory(rtsp)
+set(container_readers ${container_readers} reader_rtsp)
+add_subdirectory(rcv)
+set(container_readers ${container_readers} reader_rcv)
+add_subdirectory(rv9)
+set(container_readers ${container_readers} reader_rv9)
+add_subdirectory(qsynth)
+set(container_readers ${container_readers} reader_qsynth)
+add_subdirectory(simple)
+set(container_readers ${container_readers} reader_simple)
+set(container_writers ${container_writers} writer_simple)
+add_subdirectory(raw)
+set(container_readers ${container_readers} reader_raw_video)
+set(container_writers ${container_writers} writer_raw_video)
+add_subdirectory(dummy)
+set(container_writers ${container_writers} writer_dummy)
+
+add_subdirectory(metadata/id3)
+set(container_readers ${container_readers} reader_metadata_id3)
+
+if (${LIBRARY_TYPE} STREQUAL STATIC)
+target_link_libraries(containers ${container_readers} ${container_writers})
+endif (${LIBRARY_TYPE} STREQUAL STATIC)
+
+# Test apps
+add_subdirectory(test)
diff --git a/gfx/include/userland/containers/asf/CMakeLists.txt b/gfx/include/userland/containers/asf/CMakeLists.txt
new file mode 100644
index 0000000000..2cd7ac2372
--- /dev/null
+++ b/gfx/include/userland/containers/asf/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(reader_asf ${LIBRARY_TYPE} asf_reader.c)
+
+target_link_libraries(reader_asf containers)
+
+install(TARGETS reader_asf DESTINATION ${VMCS_PLUGIN_DIR})
+
+add_library(writer_asf ${LIBRARY_TYPE} asf_writer.c)
+
+target_link_libraries(writer_asf containers)
+
+install(TARGETS writer_asf DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/asf/asf_reader.c b/gfx/include/userland/containers/asf/asf_reader.c
new file mode 100644
index 0000000000..c36ed5e544
--- /dev/null
+++ b/gfx/include/userland/containers/asf/asf_reader.c
@@ -0,0 +1,2247 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+/* prevent double-defines when it is defined on the command line - as in the test app */
+#ifndef ENABLE_CONTAINERS_LOG_FORMAT
+//#define ENABLE_CONTAINERS_LOG_FORMAT
+#endif
+#ifndef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+#endif
+
+#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->object_level
+
+/* For the sanity of the Visual Studio debugger make local names for structures */
+#define VC_CONTAINER_TRACK_MODULE_T ASF_VC_CONTAINER_TRACK_MODULE_T
+#define VC_CONTAINER_MODULE_T ASF_VC_CONTAINER_MODULE_T
+#define VC_CONTAINER_T ASF_VC_CONTAINER_T
+
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+#define ASF_TRACKS_MAX 2
+#define ASF_EXTRADATA_MAX 256
+
+#define ASF_MAX_OBJECT_LEVEL 4
+#define ASF_MAX_CONSECUTIVE_UNKNOWN_OBJECTS 5
+#define ASF_MAX_OBJECT_SIZE (1<<29) /* Does not apply to the data object */
+#define ASF_OBJECT_HEADER_SIZE (16+8)
+#define ASF_UNKNOWN_PTS ((uint32_t)(-1))
+#define ASF_MAX_CONSECUTIVE_CORRUPTED_PACKETS 100
+#define ASF_MAX_SEARCH_PACKETS 1000
+
+#define ASF_SKIP_GUID(ctx, size, n) (size -= 16, SKIP_GUID(ctx,n))
+#define ASF_SKIP_U8(ctx, size, n) (size -= 1, SKIP_U8(ctx,n))
+#define ASF_SKIP_U16(ctx, size, n) (size -= 2, SKIP_U16(ctx,n))
+#define ASF_SKIP_U24(ctx, size, n) (size -= 3, SKIP_U24(ctx,n))
+#define ASF_SKIP_U32(ctx, size, n) (size -= 4, SKIP_U32(ctx,n))
+#define ASF_SKIP_U64(ctx, size, n) (size -= 8, SKIP_U64(ctx,n))
+#define ASF_READ_GUID(ctx, size, buffer, n) (size -= 16, READ_GUID(ctx,(uint8_t *)buffer,n))
+#define ASF_READ_U8(ctx, size, n) (size -= 1, READ_U8(ctx,n))
+#define ASF_READ_U16(ctx, size, n) (size -= 2, READ_U16(ctx,n))
+#define ASF_READ_U24(ctx, size, n) (size -= 3, READ_U24(ctx,n))
+#define ASF_READ_U32(ctx, size, n) (size -= 4, READ_U32(ctx,n))
+#define ASF_READ_U64(ctx, size, n) (size -= 8, READ_U64(ctx,n))
+#define ASF_READ_STRING(ctx, size, buffer, to_read, n) (size -= to_read, READ_STRING_UTF16(ctx,buffer,to_read,n))
+#define ASF_SKIP_STRING(ctx, size, to_read, n) (size -= to_read, SKIP_STRING_UTF16(ctx,to_read,n))
+#define ASF_READ_BYTES(ctx, size, buffer, to_read) (size -= to_read, READ_BYTES(ctx,buffer,to_read))
+#define ASF_SKIP_BYTES(ctx, size, to_read) (size -= to_read, SKIP_BYTES(ctx,to_read))
+
+/* Read variable length field from p_context. */
+#define READ_VLC(p_context, length, value_if_missing, txt) \
+ (length) == 1 ? READ_U8(p_context, txt) : \
+ (length) == 2 ? READ_U16(p_context, txt) : \
+ (length) == 3 ? READ_U32(p_context, txt) : value_if_missing
+
+#define CHECK_POINT(p_context, amount_to_read) do { \
+ if(amount_to_read < 0) return VC_CONTAINER_ERROR_CORRUPTED; \
+ if(STREAM_STATUS(p_context)) return STREAM_STATUS(p_context); } while(0)
+
+/******************************************************************************
+Type definitions.
+******************************************************************************/
+
+/** Context for our reader
+ */
+typedef struct
+{
+ uint64_t start; /* The byte offset start of the current packet in the file */
+ uint32_t size;
+ uint32_t padding_size;
+ uint64_t send_time; /* read in mS, stored in uS */
+ bool eos;
+ bool corrupted;
+ uint16_t bad_packets;
+
+ /* All the different Length Types for the VLC codes */
+ unsigned int replicated_data_lt;
+ unsigned int offset_into_media_object_lt;
+ unsigned int media_object_number_lt;
+ unsigned int payload_lt;
+
+ unsigned int multiple_payloads;
+ unsigned int compressed_payloads;
+
+ uint8_t num_payloads;
+ uint8_t current_payload;
+ uint32_t current_offset; /* The offset in the current packet for the next byte to be read */
+
+ /* Info already read */
+ uint32_t stream_num; /* Stream number and key-frame flag */
+ uint32_t media_object_num;
+ uint32_t media_object_off;
+ uint32_t payload_size;
+ uint32_t subpayload_size;
+
+ /* Info read from the replicated data */
+ uint32_t media_object_size;
+ uint64_t media_object_pts; /**< Presentation timestamp in microseconds */
+ uint64_t media_object_pts_delta; /**< Presentation timestamp delta in microseconds */
+
+} ASF_PACKET_STATE;
+
+typedef struct VC_CONTAINER_TRACK_MODULE_T
+{
+ /* The ID of the stream (the index in the containing array need not be the ID) */
+ unsigned int stream_id;
+ bool b_valid;
+
+ uint8_t extradata[ASF_EXTRADATA_MAX];
+
+ ASF_PACKET_STATE *p_packet_state;
+ ASF_PACKET_STATE local_packet_state;
+
+ /* Simple index structure. Corresponds to the simple index in 6.1 of the spec
+ * This index has locations in packets, not in bytes, and relies on the
+ * file having fixed-length packets - as is required */
+ struct
+ {
+ uint64_t offset; /**< Offset to the start of the simple index data */
+ uint32_t num_entries;
+ int64_t time_interval; /* in uS */
+ bool incomplete; /* The index does not go to the end of the file */
+ } simple_index;
+
+} VC_CONTAINER_TRACK_MODULE_T;
+
+typedef struct VC_CONTAINER_MODULE_T
+{
+ int object_level;
+
+ uint32_t packet_size; /**< Size of a data packet */
+ uint64_t packets_num; /**< Number of packets contained in the data object */
+
+ bool broadcast; /**< Specifies if we are dealing with a broadcast stream */
+ int64_t duration; /**< Duration of the stream in microseconds */
+ int64_t preroll; /**< Duration of the preroll in microseconds. */
+ /* This is the PTS of the first packet; all are offset by this amount. */
+ uint64_t time_offset; /**< Offset added to timestamps in microseconds */
+
+ uint64_t data_offset; /**< Offset to the start of the data packets */
+ int64_t data_size; /**< Size of the data contained in the data object */
+
+ /* The track objects. There's a count of these in VC_CONTAINER_T::tracks_num */
+ VC_CONTAINER_TRACK_T *tracks[ASF_TRACKS_MAX];
+
+ /* Translation table from stream_number to index in the tracks array */
+ unsigned char stream_number_to_index[128];
+
+ /* Data for a top-level index structure as defined in 6.2 of the spec */
+ struct
+ {
+ uint64_t entry_time_interval; /* The time interval between specifiers, scaled to uS */
+ uint32_t specifiers_count; /* The number of specifiers in the file, 0 if no index */
+ uint64_t active_specifiers[ASF_TRACKS_MAX]; /* the specifier in use for each track,
+ * or >=specifiers_count if none */
+ uint64_t specifiers_offset; /* The file address of the first specifier. */
+ uint32_t block_count; /* The number of index blocks */
+ uint64_t blocks_offset; /* The file address of the first block */
+ } top_level_index;
+
+ /* A pointer to the track (in the tracks array) which is to be used with a simple index.
+ * null if there is no such track */
+ ASF_VC_CONTAINER_TRACK_MODULE_T *simple_index_track;
+
+ /* Shared packet state. This is used when the tracks are in sync,
+ and for the track at the earliest position in the file when they are not in sync */
+ ASF_PACKET_STATE packet_state;
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Prototypes for local functions
+******************************************************************************/
+static VC_CONTAINER_STATUS_T asf_read_object( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_header( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_header_ext( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_file_properties( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_ext_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_simple_index( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_index( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_index_parameters( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_data( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_codec_list( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_content_description( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_ext_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_read_object_adv_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T asf_skip_unprocessed_object( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T seek_to_positions(VC_CONTAINER_T *p_ctx,
+ uint64_t track_positions[ASF_TRACKS_MAX], int64_t *p_time,
+ VC_CONTAINER_SEEK_FLAGS_T flags, unsigned int start_track,
+ bool seek_on_start_track);
+
+/******************************************************************************
+GUID list for the different ASF objects
+******************************************************************************/
+static const GUID_T asf_guid_header = {0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
+static const GUID_T asf_guid_file_props = {0x8CABDCA1, 0xA947, 0x11CF, {0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
+static const GUID_T asf_guid_stream_props = {0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
+static const GUID_T asf_guid_ext_stream_props = {0x14E6A5CB, 0xC672, 0x4332, {0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A}};
+static const GUID_T asf_guid_data = {0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
+static const GUID_T asf_guid_simple_index = {0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}};
+static const GUID_T asf_guid_index = {0xD6E229D3, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}};
+static const GUID_T asf_guid_index_parameters = {0xD6E229DF, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}};
+static const GUID_T asf_guid_header_ext = {0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
+static const GUID_T asf_guid_codec_list = {0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}};
+static const GUID_T asf_guid_content_description = {0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
+static const GUID_T asf_guid_ext_content_description = {0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}};
+static const GUID_T asf_guid_stream_bitrate_props = {0x7BF875CE, 0x468D, 0x11D1, {0x8D, 0x82, 0x00, 0x60, 0x97, 0xC9, 0xA2, 0xB2}};
+static const GUID_T asf_guid_language_list = {0x7C4346A9, 0xEFE0, 0x4BFC, {0xB2, 0x29, 0x39, 0x3E, 0xDE, 0x41, 0x5C, 0x85}};
+static const GUID_T asf_guid_metadata = {0xC5F8CBEA, 0x5BAF, 0x4877, {0x84, 0x67, 0xAA, 0x8C, 0x44, 0xFA, 0x4C, 0xCA}};
+static const GUID_T asf_guid_padding = {0x1806D474, 0xCADF, 0x4509, {0xA4, 0xBA, 0x9A, 0xAB, 0xCB, 0x96, 0xAA, 0xE8}};
+static const GUID_T asf_guid_content_encryption = {0x2211B3FB, 0xBD23, 0x11D2, {0xB4, 0xB7, 0x00, 0xA0, 0xC9, 0x55, 0xFC, 0x6E}};
+static const GUID_T asf_guid_ext_content_encryption = {0x298AE614, 0x2622, 0x4C17, {0xB9, 0x35, 0xDA, 0xE0, 0x7E, 0xE9, 0x28, 0x9C}};
+static const GUID_T asf_guid_adv_content_encryption = {0x43058533, 0x6981, 0x49E6, {0x9B, 0x74, 0xAD, 0x12, 0xCB, 0x86, 0xD5, 0x8C}};
+static const GUID_T asf_guid_compatibility = {0x26F18B5D, 0x4584, 0x47EC, {0x9F, 0x5F, 0x0E, 0x65, 0x1F, 0x04, 0x52, 0xC9}};
+static const GUID_T asf_guid_script_command = {0x1EFB1A30, 0x0B62, 0x11D0, {0xA3, 0x9B, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}};
+static const GUID_T asf_guid_mutual_exclusion = {0xD6E229DC, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}};
+
+static const GUID_T asf_guid_stream_type_video = {0xBC19EFC0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
+static const GUID_T asf_guid_stream_type_audio = {0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
+
+/******************************************************************************
+List of GUIDs and their associated processing functions
+******************************************************************************/
+static struct {
+ const GUID_T *guid;
+ const char *psz_name;
+ VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T *, int64_t );
+
+} asf_object_list[] =
+{
+ {&asf_guid_header, "header", asf_read_object_header},
+ {&asf_guid_file_props, "file properties", asf_read_object_file_properties},
+ {&asf_guid_stream_props, "stream properties", asf_read_object_stream_properties},
+ {&asf_guid_ext_stream_props, "extended stream properties", asf_read_object_ext_stream_properties},
+ {&asf_guid_data, "data", asf_read_object_data},
+ {&asf_guid_simple_index, "simple index", asf_read_object_simple_index},
+ {&asf_guid_index, "index", asf_read_object_index},
+ {&asf_guid_index_parameters, "index parameters", asf_read_object_index_parameters},
+ {&asf_guid_header_ext, "header extension", asf_read_object_header_ext},
+ {&asf_guid_codec_list, "codec list", asf_read_object_codec_list},
+ {&asf_guid_content_description, "content description", asf_read_object_content_description},
+ {&asf_guid_ext_content_description, "extended content description", asf_skip_unprocessed_object},
+ {&asf_guid_stream_bitrate_props, "stream bitrate properties", asf_read_object_stream_bitrate_props},
+ {&asf_guid_language_list, "language list", asf_skip_unprocessed_object},
+ {&asf_guid_metadata, "metadata", asf_skip_unprocessed_object},
+ {&asf_guid_padding, "padding", asf_skip_unprocessed_object},
+ {&asf_guid_compatibility, "compatibility", asf_skip_unprocessed_object},
+ {&asf_guid_script_command, "script command", asf_skip_unprocessed_object},
+ {&asf_guid_mutual_exclusion, "mutual exclusion", asf_skip_unprocessed_object},
+ {&asf_guid_content_encryption, "content encryption", &asf_read_object_content_encryption},
+ {&asf_guid_ext_content_encryption, "extended content encryption", &asf_read_object_ext_content_encryption},
+ {&asf_guid_adv_content_encryption, "advanced content encryption", &asf_read_object_adv_content_encryption},
+ {0, "unknown", asf_skip_unprocessed_object}
+};
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+/** Find the track associated with an ASF stream id */
+static VC_CONTAINER_TRACK_T *asf_reader_find_track( VC_CONTAINER_T *p_ctx, unsigned int stream_id,
+ bool b_create)
+{
+ VC_CONTAINER_TRACK_T *p_track = 0;
+ VC_CONTAINER_MODULE_T * module = p_ctx->priv->module;
+ unsigned int i;
+
+ /* discard the key-frame flag */
+ stream_id &= 0x7f;
+
+ /* look to see if we have already allocated the stream */
+ i = module->stream_number_to_index[stream_id];
+
+ if(i < p_ctx->tracks_num) /* We found it */
+ p_track = p_ctx->tracks[i];
+
+ if(!p_track && b_create && p_ctx->tracks_num < ASF_TRACKS_MAX)
+ {
+ /* Allocate and initialise a new track */
+ p_ctx->tracks[p_ctx->tracks_num] = p_track =
+ vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
+ if(p_track)
+ {
+ /* store the stream ID */
+ p_track->priv->module->stream_id = stream_id;
+
+ /* Store the translation table value */
+ module->stream_number_to_index[stream_id] = p_ctx->tracks_num;
+
+ /* count the track */
+ p_ctx->tracks_num++;
+ }
+ }
+
+ if(!p_track && b_create)
+ LOG_DEBUG(p_ctx, "could not create track for stream id: %i", stream_id);
+
+ return p_track;
+}
+
+/** Base function used to read an ASF object from the ASF header.
+ * This will read the object header do lots of sanity checking and pass on the rest
+ * of the reading to the object specific reading function */
+static VC_CONTAINER_STATUS_T asf_read_object( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t object_size, offset = STREAM_POSITION(p_ctx);
+ unsigned int i, unknown_objects = 0, is_data_object;
+ GUID_T guid;
+
+ /* Sanity check the size of the data */
+ if(size && size < ASF_OBJECT_HEADER_SIZE)
+ {
+ LOG_DEBUG(p_ctx, "invalid object header (too small)");
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ if(READ_GUID(p_ctx, &guid, "Object ID") != sizeof(guid))
+ return STREAM_STATUS(p_ctx);
+
+ /* Find out which GUID we are dealing with */
+ for( i = 0; asf_object_list[i].guid; i++ )
+ {
+ if(guid.word0 != asf_object_list[i].guid->word0) continue;
+ if(!memcmp(&guid, asf_object_list[i].guid, sizeof(guid))) break;
+ }
+
+ LOG_FORMAT(p_ctx, "Object Name: %s", asf_object_list[i].psz_name);
+
+ /* Bail out if we find too many consecutive unknown objects */
+ if(!asf_object_list[i].guid) unknown_objects++;
+ else unknown_objects = 0;
+ if(unknown_objects >= ASF_MAX_CONSECUTIVE_UNKNOWN_OBJECTS)
+ {
+ LOG_DEBUG(p_ctx, "too many unknown objects");
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ is_data_object = asf_object_list[i].pf_func == asf_read_object_data;
+
+ object_size = READ_U64(p_ctx, "Object Size");
+
+ /* Sanity check the object size */
+ if(object_size < 0 /* Shouldn't ever get that big */ ||
+ /* Minimum size check (data object can have a size == 0) */
+ (object_size < ASF_OBJECT_HEADER_SIZE && !(is_data_object && !object_size)) ||
+ /* Only the data object can really be massive */
+ (!is_data_object && object_size > ASF_MAX_OBJECT_SIZE))
+ {
+ LOG_DEBUG(p_ctx, "object %s has an invalid size (%"PRIi64")",
+ asf_object_list[i].psz_name, object_size);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+ if(size && object_size > size)
+ {
+ LOG_DEBUG(p_ctx, "object %s is bigger than it should (%"PRIi64" > %"PRIi64")",
+ asf_object_list[i].psz_name, object_size, size);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+ size = object_size;
+
+ if(module->object_level >= 2 * ASF_MAX_OBJECT_LEVEL)
+ {
+ LOG_DEBUG(p_ctx, "object %s is too deep. skipping", asf_object_list[i].psz_name);
+ status = asf_skip_unprocessed_object(p_ctx, size - ASF_OBJECT_HEADER_SIZE);
+ /* Just bail out, hoping we have enough data */
+ }
+ else
+ {
+ module->object_level++;
+
+ /* Call the object specific parsing function */
+ status = asf_object_list[i].pf_func(p_ctx, size - ASF_OBJECT_HEADER_SIZE);
+
+ module->object_level--;
+
+ if(status != VC_CONTAINER_SUCCESS)
+ LOG_DEBUG(p_ctx, "object %s appears to be corrupted (%i)", asf_object_list[i].psz_name, status);
+ }
+
+ /* The stream position should be exactly at the end of the object */
+ {
+ int64_t bytes_processed = STREAM_POSITION(p_ctx) - offset;
+
+ /* fail with overruns */
+ if (bytes_processed > size)
+ {
+ /* Things have gone really bad here and we ended up reading past the end of the
+ * object. We could maybe try to be clever and recover by seeking back to the end
+ * of the object. However if we get there, the file is clearly corrupted so there's
+ * no guarantee it would work anyway. */
+ LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of object %s",
+ bytes_processed-size, asf_object_list[i].psz_name);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ /* Handle underruns by throwing away the data (this should never happen, but we don't really care if it does) */
+ if (bytes_processed < size)
+ {
+ size -= bytes_processed;
+ LOG_DEBUG(p_ctx, "%"PRIi64" bytes left unread in object %s", size, asf_object_list[i].psz_name);
+
+ if(size < ASF_MAX_OBJECT_SIZE)
+ SKIP_BYTES(p_ctx, size); /* read a small amount */
+ else
+ SEEK(p_ctx, STREAM_POSITION(p_ctx) + size); /* seek a large distance */
+ }
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an ASF header object */
+static VC_CONTAINER_STATUS_T asf_read_object_header( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t offset = STREAM_POSITION(p_ctx);
+
+ /* Sanity check the size of the data */
+ if((size -= 6) < 0) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ SKIP_U32(p_ctx, "Number of Header Objects"); /* FIXME: could use that */
+ SKIP_U8(p_ctx, "Reserved1");
+ SKIP_U8(p_ctx, "Reserved2");
+
+ /* Read contained objects */
+ module->object_level++;
+ while(status == VC_CONTAINER_SUCCESS && size >= ASF_OBJECT_HEADER_SIZE)
+ {
+ offset = STREAM_POSITION(p_ctx);
+ status = asf_read_object(p_ctx, size);
+ size -= (STREAM_POSITION(p_ctx) - offset);
+ }
+ module->object_level--;
+
+ return status;
+}
+
+/** Reads an ASF extended header object */
+static VC_CONTAINER_STATUS_T asf_read_object_header_ext( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t data_size, offset;
+
+ ASF_SKIP_GUID(p_ctx, size, "Reserved Field 1");
+ ASF_SKIP_U16(p_ctx, size, "Reserved Field 2");
+ data_size = ASF_READ_U32(p_ctx, size, "Header Extension Data Size");
+
+ if(data_size != size)
+ LOG_DEBUG(p_ctx, "invalid header extension data size (%"PRIi64",%"PRIi64")", data_size, size);
+
+ CHECK_POINT(p_ctx, size);
+
+ /* Read contained objects */
+ module->object_level++;
+ while(status == VC_CONTAINER_SUCCESS && size >= ASF_OBJECT_HEADER_SIZE)
+ {
+ offset = STREAM_POSITION(p_ctx);
+ status = asf_read_object(p_ctx, size);
+ size -= (STREAM_POSITION(p_ctx) - offset);
+ }
+ module->object_level--;
+
+ return status;
+}
+
+/** Reads an ASF file properties object */
+static VC_CONTAINER_STATUS_T asf_read_object_file_properties( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint32_t max_packet_size;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ ASF_SKIP_GUID(p_ctx, size, "File ID");
+ ASF_SKIP_U64(p_ctx, size, "File Size");
+ ASF_SKIP_U64(p_ctx, size, "Creation Date");
+ ASF_SKIP_U64(p_ctx, size, "Data Packets Count");
+ module->duration = ASF_READ_U64(p_ctx, size, "Play Duration") / UINT64_C(10); /* read in 100nS units, stored in uS */
+ ASF_SKIP_U64(p_ctx, size, "Send Duration");
+ module->preroll = ASF_READ_U64(p_ctx, size, "Preroll") * UINT64_C(1000); /* read in mS, storedin uS */
+ module->broadcast = ASF_READ_U32(p_ctx, size, "Flags") & 0x1;
+ module->packet_size = ASF_READ_U32(p_ctx, size, "Minimum Data Packet Size");
+ max_packet_size = ASF_READ_U32(p_ctx, size, "Maximum Data Packet Size");
+ ASF_SKIP_U32(p_ctx, size, "Maximum Bitrate");
+
+ if(module->preroll < module->duration) module->duration -= module->preroll;
+ else module->duration = 0;
+
+ /* Sanity check the packet size */
+ if(!module->packet_size)
+ {
+ LOG_DEBUG(p_ctx, "packet size cannot be 0");
+ return VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED;
+ }
+
+ if(max_packet_size != module->packet_size)
+ {
+ LOG_DEBUG(p_ctx, "asf stream not supported (min packet size: %i != max packet size: %i)",
+ module->packet_size, max_packet_size);
+ return VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED;
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads the bitmapinfoheader structure contained in a stream properties object */
+static VC_CONTAINER_STATUS_T asf_read_bitmapinfoheader( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *p_track, int64_t size )
+{
+ uint32_t bmih_size, formatdata_size;
+ uint32_t fourcc;
+
+ /* Sanity check the size of the data */
+ if(size < 40 + 11) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ /* Read the preamble to the BITMAPINFOHEADER */
+ ASF_SKIP_U32(p_ctx, size, "Encoded Image Width");
+ ASF_SKIP_U32(p_ctx, size, "Encoded Image Height");
+ ASF_SKIP_U8(p_ctx, size, "Reserved Flags");
+ formatdata_size = ASF_READ_U16(p_ctx, size, "Format Data Size");
+
+ /* Sanity check the size of the data */
+ if(formatdata_size < 40 || size < formatdata_size) return VC_CONTAINER_ERROR_CORRUPTED;
+ bmih_size = ASF_READ_U32(p_ctx, size, "Format Data Size");
+ if(bmih_size < 40 || bmih_size > formatdata_size) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ /* Read BITMAPINFOHEADER structure */
+ p_track->format->type->video.width = ASF_READ_U32(p_ctx, size, "Image Width");
+ p_track->format->type->video.height = ASF_READ_U32(p_ctx, size, "Image Height"); /* Signed */
+ ASF_SKIP_U16(p_ctx, size, "Reserved");
+ ASF_SKIP_U16(p_ctx, size, "Bits Per Pixel Count");
+ ASF_READ_BYTES(p_ctx, size, (char *)&fourcc, 4); /* Compression ID */
+ LOG_FORMAT(p_ctx, "Compression ID: %4.4s", (char *)&fourcc);
+ p_track->format->codec = vfw_fourcc_to_codec(fourcc);
+ if(p_track->format->codec == VC_CONTAINER_CODEC_UNKNOWN)
+ p_track->format->codec = fourcc;
+ ASF_SKIP_U32(p_ctx, size, "Image Size");
+ ASF_SKIP_U32(p_ctx, size, "Horizontal Pixels Per Meter");
+ ASF_SKIP_U32(p_ctx, size, "Vertical Pixels Per Meter");
+ ASF_SKIP_U32(p_ctx, size, "Colors Used Count");
+ ASF_SKIP_U32(p_ctx, size, "Important Colors Count");
+
+ if(!(bmih_size -= 40))return VC_CONTAINER_SUCCESS;
+
+ if(bmih_size > ASF_EXTRADATA_MAX)
+ {
+ LOG_DEBUG(p_ctx, "extradata truncated");
+ bmih_size = ASF_EXTRADATA_MAX;
+ }
+ p_track->format->extradata = p_track->priv->module->extradata;
+ p_track->format->extradata_size = ASF_READ_BYTES(p_ctx, size, p_track->format->extradata, bmih_size);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads the waveformatex structure contained in a stream properties object */
+static VC_CONTAINER_STATUS_T asf_read_waveformatex( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *p_track, int64_t size)
+{
+ uint16_t extradata_size;
+
+ /* Read WAVEFORMATEX structure */
+ p_track->format->codec = waveformat_to_codec(ASF_READ_U16(p_ctx, size, "Codec ID"));
+ p_track->format->type->audio.channels = ASF_READ_U16(p_ctx, size, "Number of Channels");
+ p_track->format->type->audio.sample_rate = ASF_READ_U32(p_ctx, size, "Samples per Second");
+ p_track->format->bitrate = ASF_READ_U32(p_ctx, size, "Average Number of Bytes Per Second") * 8;
+ p_track->format->type->audio.block_align = ASF_READ_U16(p_ctx, size, "Block Alignment");
+ p_track->format->type->audio.bits_per_sample = ASF_READ_U16(p_ctx, size, "Bits Per Sample");
+ extradata_size = ASF_READ_U16(p_ctx, size, "Codec Specific Data Size");
+
+ CHECK_POINT(p_ctx, size);
+
+ if(!extradata_size) return VC_CONTAINER_SUCCESS;
+
+ /* Sanity check the size of the data */
+ if(extradata_size > size) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ if(extradata_size > ASF_EXTRADATA_MAX)
+ {
+ LOG_DEBUG(p_ctx, "extradata truncated");
+ extradata_size = ASF_EXTRADATA_MAX;
+ }
+ p_track->format->extradata = p_track->priv->module->extradata;
+ p_track->format->extradata_size = ASF_READ_BYTES(p_ctx, size, p_track->format->extradata, extradata_size);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an ASF stream properties object */
+static VC_CONTAINER_STATUS_T asf_read_object_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *p_track;
+ unsigned int ts_length, flags;
+ VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_UNKNOWN;
+ GUID_T stream_type;
+ int64_t offset;
+
+ ASF_READ_GUID(p_ctx, size, &stream_type, "Stream Type");
+ ASF_SKIP_GUID(p_ctx, size, "Error Correction Type");
+
+ /* The time_offset field is in 100nS units. Scale back to uS */
+ module->time_offset = ASF_READ_U64(p_ctx, size, "Time Offset") / UINT64_C(10);
+ ts_length = ASF_READ_U32(p_ctx, size, "Type-Specific Data Length");
+ ASF_SKIP_U32(p_ctx, size, "Error Correction Data Length");
+ flags = ASF_READ_U16(p_ctx, size, "Flags");
+ ASF_SKIP_U32(p_ctx, size, "Reserved");
+
+ CHECK_POINT(p_ctx, size);
+
+ /* Zero is not a valid stream id */
+ if(!(flags & 0x7F)) goto skip;
+
+ if(!memcmp(&stream_type, &asf_guid_stream_type_video, sizeof(GUID_T)))
+ type = VC_CONTAINER_ES_TYPE_VIDEO;
+ else if(!memcmp(&stream_type, &asf_guid_stream_type_audio, sizeof(GUID_T)))
+ type = VC_CONTAINER_ES_TYPE_AUDIO;
+
+ /* Check we know what to do with this track */
+ if(type == VC_CONTAINER_ES_TYPE_UNKNOWN) goto skip;
+
+ /* Sanity check sizes */
+ if(ts_length > size) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ p_track = asf_reader_find_track( p_ctx, flags, true);
+ if(!p_track) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+
+ p_track->format->es_type = type;
+
+ offset = STREAM_POSITION(p_ctx);
+ if(type == VC_CONTAINER_ES_TYPE_AUDIO)
+ status = asf_read_waveformatex(p_ctx, p_track, (int64_t)ts_length);
+ else if(type == VC_CONTAINER_ES_TYPE_VIDEO)
+ status = asf_read_bitmapinfoheader(p_ctx, p_track, (int64_t)ts_length);
+ size -= STREAM_POSITION(p_ctx) - offset;
+
+ if(status) return status;
+
+ p_track->priv->module->b_valid = true;
+ p_track->is_enabled = true;
+ p_track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+
+ /* Codec specific work-arounds */
+ switch(p_track->format->codec)
+ {
+ case VC_CONTAINER_CODEC_MPGA:
+ /* Can't guarantee that the data is framed */
+ p_track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+ break;
+ default: break;
+ }
+
+ skip:
+ if(size) SKIP_BYTES(p_ctx, size);
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an ASF extended stream properties object */
+static VC_CONTAINER_STATUS_T asf_read_object_ext_stream_properties( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_TRACK_T *p_track;
+ unsigned int i, name_count, pes_count, length, stream_id;
+
+ ASF_SKIP_U64(p_ctx, size, "Start Time");
+ ASF_SKIP_U64(p_ctx, size, "End Time");
+ ASF_SKIP_U32(p_ctx, size, "Data Bitrate");
+ ASF_SKIP_U32(p_ctx, size, "Buffer Size");
+ ASF_SKIP_U32(p_ctx, size, "Initial Buffer Fullness");
+ ASF_SKIP_U32(p_ctx, size, "Alternate Data Bitrate");
+ ASF_SKIP_U32(p_ctx, size, "Alternate Buffer Size");
+ ASF_SKIP_U32(p_ctx, size, "Alternate Initial Buffer Fullness");
+ ASF_SKIP_U32(p_ctx, size, "Maximum Object Size");
+ ASF_SKIP_U32(p_ctx, size, "Flags");
+ stream_id = ASF_READ_U16(p_ctx, size, "Stream Number");
+ ASF_SKIP_U16(p_ctx, size, "Stream Language ID Index");
+ ASF_SKIP_U64(p_ctx, size, "Average Time Per Frame");
+ name_count = ASF_READ_U16(p_ctx, size, "Stream Name Count");
+ pes_count = ASF_READ_U16(p_ctx, size, "Payload Extension System Count");
+
+ CHECK_POINT(p_ctx, size);
+
+ p_track = asf_reader_find_track( p_ctx, stream_id, true);
+ if(!p_track) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+
+ /* Stream Names */
+ for(i = 0; i < name_count; i++)
+ {
+ if(size < 4) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_U16(p_ctx, size, "Language ID Index");
+ length = ASF_READ_U16(p_ctx, size, "Stream Name Length");
+ if(size < length) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_BYTES(p_ctx, size, length); /* Stream Name */
+ }
+
+ CHECK_POINT(p_ctx, size);
+
+ /* Payload Extension Systems */
+ for(i = 0; i < pes_count; i++)
+ {
+ if(size < 22) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_GUID(p_ctx, size, "Extension System ID");
+ ASF_SKIP_U16(p_ctx, size, "Extension Data Size");
+ length = ASF_READ_U32(p_ctx, size, "Extension System Info Length");
+ if(size < length) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_BYTES(p_ctx, size, length); /* Extension System Info */
+ }
+
+ CHECK_POINT(p_ctx, size);
+
+ /* Optional Stream Properties Object */
+ if(size >= ASF_OBJECT_HEADER_SIZE)
+ status = asf_read_object(p_ctx, size);
+
+ return status;
+}
+
+/** Reads an ASF simple index object */
+static VC_CONTAINER_STATUS_T asf_read_object_simple_index( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = 0;
+ uint64_t time_interval, index_duration;
+ uint32_t count;
+ unsigned int i;
+
+ ASF_SKIP_GUID(p_ctx, size, "File ID");
+
+ /* time in 100nS units, converted to uS */
+ time_interval = ASF_READ_U64(p_ctx, size, "Index Entry Time Interval") / UINT64_C(10);
+ ASF_SKIP_U32(p_ctx, size, "Maximum Packet Count");
+ count = ASF_READ_U32(p_ctx, size, "Index Entries Count");
+
+ CHECK_POINT(p_ctx, size);
+
+ if(count > size / 6)
+ {
+ LOG_DEBUG(p_ctx, "invalid number of entries in the index (%i, %"PRIi64")", count, size / 6);
+ count = (uint32_t)(size / 6);
+ }
+
+ /* Find the track corresponding to this index */
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ if(p_ctx->tracks[i]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) continue;
+ if(p_ctx->tracks[i]->priv->module->simple_index.offset) continue;
+ break;
+ }
+
+ /* Skip the index if we can't find the associated track */
+ if(i == p_ctx->tracks_num || !count || !time_interval) return VC_CONTAINER_SUCCESS;
+ track_module = p_ctx->tracks[i]->priv->module;
+
+ track_module->simple_index.offset = STREAM_POSITION(p_ctx);
+ track_module->simple_index.time_interval = time_interval;
+ track_module->simple_index.num_entries = count;
+
+ /* Check that the index covers the whole duration of the stream */
+ index_duration = (count * time_interval);
+ if(module->preroll + module->time_offset < index_duration)
+ index_duration -= module->preroll + module->time_offset;
+ else
+ index_duration = 0;
+
+ if((uint64_t)module->duration > index_duration + time_interval)
+ {
+ track_module->simple_index.incomplete = true;
+ }
+
+ LOG_DEBUG(p_ctx, "index covers %fS on %fS",
+ (float)index_duration / 1E6, (float)module->duration / 1E6);
+
+#if defined(ENABLE_CONTAINERS_LOG_FORMAT) && defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE)
+ for(i = 0; i < count; i++)
+ {
+ LOG_FORMAT(p_ctx, "Entry: %u", i);
+ ASF_SKIP_U32(p_ctx, size, "Packet Number");
+ ASF_SKIP_U16(p_ctx, size, "Packet Count");
+ if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) break;
+ }
+ size = i * 6;
+#else
+ size = CACHE_BYTES(p_ctx, count * 6);
+#endif
+
+ /* Check that the index is complete */
+ if(size / 6 != count )
+ {
+ LOG_DEBUG(p_ctx, "index is incomplete (%i entries on %i)", (int)size / 6, count);
+ track_module->simple_index.num_entries = (uint32_t)(size / 6);
+ track_module->simple_index.incomplete = true;
+ }
+
+ /* If we haven't had an index before, or this track is enabled, we'll store this one.
+ * (Usually there will only be one video track, and it will be enabled, so both tests
+ * will pass. This check is an attempt to handle content not structured as it should be) */
+ if ((!module->simple_index_track) || (p_ctx->tracks[i]->is_enabled))
+ {
+ /* Save the track so we don't have to look for it in when seeking */
+ module->simple_index_track = track_module;
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an ASF index object */
+static VC_CONTAINER_STATUS_T asf_read_object_index( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint32_t i, specifiers_count, blocks_count;
+ uint32_t best_specifier_type[ASF_TRACKS_MAX] = {0};
+
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ /* Read the time interval and scale to microseconds */
+ module->top_level_index.entry_time_interval
+ = (uint64_t)ASF_READ_U32(p_ctx, size, "Index Entry Time Interval") * INT64_C(1000);
+
+ module->top_level_index.specifiers_count
+ = specifiers_count
+ = (uint32_t)ASF_READ_U16(p_ctx, size, "Index Specifiers Count");
+
+ module->top_level_index.block_count
+ = blocks_count
+ = ASF_READ_U32(p_ctx, size, "Index Blocks Count");
+
+ CHECK_POINT(p_ctx, size);
+
+ /* Index specifiers. Search for the one we like best */
+ if(size < specifiers_count * 4) return VC_CONTAINER_ERROR_CORRUPTED;
+ for(i = 0; i < specifiers_count; i++)
+ {
+ uint32_t stream_id = (uint32_t)ASF_READ_U16(p_ctx, size, "Stream Number");
+ uint32_t index_type = (uint32_t)ASF_READ_U16(p_ctx, size, "Index Type");
+
+ /* Find the track index for this stream */
+ unsigned track = module->stream_number_to_index[stream_id];
+
+ if ((track < ASF_TRACKS_MAX) &&
+ (index_type > best_specifier_type[track]))
+ {
+ /* We like this better than any we have seen before. Note - if we don't like any
+ * the file must be subtly corrupt - best we say nothing, and attempt a seek with
+ * the data for the first specifier, it will be better than nothing. At worst it
+ * will play until a seek is attempted */
+ module->top_level_index.active_specifiers[track] = i;
+ best_specifier_type[track] = index_type;
+ }
+ }
+
+ for (i = 0; i < p_ctx->tracks_num; i++)
+ {
+ LOG_DEBUG(p_ctx, "indexing track %"PRIu32" with specifier %"PRIu32,
+ i, module->top_level_index.active_specifiers[i]);
+ }
+
+ CHECK_POINT(p_ctx, size);
+
+ /* The blocks start here */
+ module->top_level_index.blocks_offset = STREAM_POSITION(p_ctx);
+
+ /* Index blocks */
+#if !(defined(ENABLE_CONTAINERS_LOG_FORMAT) && defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE))
+ blocks_count = 0; /* Don't log the index. Note we'll get a warning on unprocessed data */
+#endif
+ /* coverity[dead_error_condition] Code needs to stay there for debugging purposes */
+ for(i = 0; i < blocks_count; i++)
+ {
+ uint32_t j, k, count = ASF_READ_U32(p_ctx, size, "Index Entry Count");
+
+ for(j = 0; j < specifiers_count; j++)
+ {
+ ASF_SKIP_U64(p_ctx, size, "Block Positions");
+ }
+ for(j = 0; j < count; j++)
+ {
+ for(k = 0; k < specifiers_count; k++)
+ {
+ ASF_SKIP_U32(p_ctx, size, "Offsets");
+ }
+ }
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an ASF index parameters object */
+static VC_CONTAINER_STATUS_T asf_read_object_index_parameters( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+#ifdef ENABLE_CONTAINERS_LOG_FORMAT
+ /* This is added for debugging only. The spec (section 4.9) states that this is also enclosed in
+ * the index object (see above) and that they are identical. I think they aren't always... */
+ uint32_t i, specifiers_count;
+
+ /* Read the time interval in milliseconds */
+ ASF_SKIP_U32(p_ctx, size, "Index Entry Time Interval");
+
+ specifiers_count = (uint32_t)ASF_READ_U16(p_ctx, size, "Index Specifiers Count");
+
+ CHECK_POINT(p_ctx, size);
+
+ /* Index specifiers. Search for the one we like best */
+ if(size < specifiers_count * 4) return VC_CONTAINER_ERROR_CORRUPTED;
+ for(i = 0; i < specifiers_count; i++)
+ {
+ ASF_SKIP_U16(p_ctx, size, "Stream Number");
+ ASF_SKIP_U16(p_ctx, size, "Index Type");
+ }
+#endif
+ CHECK_POINT(p_ctx, size);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an ASF codec list object */
+static VC_CONTAINER_STATUS_T asf_read_object_codec_list( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint32_t i, count, length;
+
+ ASF_SKIP_GUID(p_ctx, size, "Reserved");
+ count = ASF_READ_U32(p_ctx, size, "Codec Entries Count");
+
+ CHECK_POINT(p_ctx, size);
+
+ /* Codec entries */
+ for(i = 0; i < count; i++)
+ {
+ ASF_SKIP_U16(p_ctx, size, "Type");
+ length = ASF_READ_U16(p_ctx, size, "Codec Name Length");
+ if(size < length) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_STRING(p_ctx, size, length * 2, "Codec Name");
+ length = ASF_READ_U16(p_ctx, size, "Codec Description Length");
+ if(size < length) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_STRING(p_ctx, size, length * 2, "Codec Description");
+ length = ASF_READ_U16(p_ctx, size, "Codec Information Length");
+ if(size < length) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_BYTES(p_ctx, size, length);
+
+ CHECK_POINT(p_ctx, size);
+ }
+
+ return status;
+}
+
+/** Reads an ASF content description object */
+static VC_CONTAINER_STATUS_T asf_read_object_content_description( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint16_t t_length, a_length, c_length, d_length, r_length;
+
+ t_length = ASF_READ_U16(p_ctx, size, "Title Length");
+ a_length = ASF_READ_U16(p_ctx, size, "Author Length");
+ c_length = ASF_READ_U16(p_ctx, size, "Copyright Length");
+ d_length = ASF_READ_U16(p_ctx, size, "Description Length");
+ r_length = ASF_READ_U16(p_ctx, size, "Rating Length");
+
+ CHECK_POINT(p_ctx, size);
+
+ if(size < t_length) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_STRING(p_ctx, size, t_length, "Title");
+ if(size < a_length) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_STRING(p_ctx, size, a_length, "Author");
+ if(size < c_length) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_STRING(p_ctx, size, c_length, "Copyright");
+ if(size < d_length) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_STRING(p_ctx, size, d_length, "Description");
+ if(size < r_length) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_STRING(p_ctx, size, r_length, "Rating");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an ASF stream bitrate properties object */
+static VC_CONTAINER_STATUS_T asf_read_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint16_t i, count;
+
+ count = ASF_READ_U16(p_ctx, size, "Bitrate Records Count");
+
+ /* Bitrate records */
+ if(size < count * 6) return VC_CONTAINER_ERROR_CORRUPTED;
+ for(i = 0; i < count; i++)
+ {
+ ASF_SKIP_U16(p_ctx, size, "Flags");
+ ASF_SKIP_U32(p_ctx, size, "Average Bitrate");
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an ASF content encryption object */
+static VC_CONTAINER_STATUS_T asf_read_object_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint32_t length;
+
+ length = ASF_READ_U32(p_ctx, size, "Secret Data Length");
+ ASF_SKIP_BYTES(p_ctx, size, length);
+
+ length = ASF_READ_U32(p_ctx, size, "Protection Type Length");
+ ASF_SKIP_BYTES(p_ctx, size, length);
+
+ length = ASF_READ_U32(p_ctx, size, "Key ID Length");
+ ASF_SKIP_BYTES(p_ctx, size, length);
+
+ length = ASF_READ_U32(p_ctx, size, "License URL Length");
+ ASF_SKIP_BYTES(p_ctx, size, length); /* null-terminated ASCII string */
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an ASF extended content encryption object */
+static VC_CONTAINER_STATUS_T asf_read_object_ext_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint32_t length;
+
+ length = ASF_READ_U32(p_ctx, size, "Data Size");
+ ASF_SKIP_BYTES(p_ctx, size, length);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an ASF advanced content encryption object */
+static VC_CONTAINER_STATUS_T asf_read_object_adv_content_encryption( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint32_t i, count;
+
+ count = ASF_READ_U16(p_ctx, size, "Content Encryption Records Count");
+
+ for(i = 0; i < count; i++)
+ {
+ uint32_t j, rec_count, data_size, length;
+
+ ASF_SKIP_GUID(p_ctx, size, "System ID");
+ ASF_SKIP_U32(p_ctx, size, "System Version");
+ rec_count = ASF_READ_U16(p_ctx, size, "Encrypted Object Record Count");
+
+ CHECK_POINT(p_ctx, size);
+
+ for(j = 0; j < rec_count; j++)
+ {
+ ASF_SKIP_U16(p_ctx, size, "Encrypted Object ID Type");
+ length = ASF_READ_U16(p_ctx, size, "Encrypted Object ID Length");
+ if(length > size) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_BYTES(p_ctx, size, length);
+ CHECK_POINT(p_ctx, size);
+ }
+
+ data_size = ASF_READ_U32(p_ctx, size, "Data Size");
+ if(data_size > size) return VC_CONTAINER_ERROR_CORRUPTED;
+ ASF_SKIP_BYTES(p_ctx, size, data_size);
+ CHECK_POINT(p_ctx, size);
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Skips over an object that is if a type we don't handle, or is nested too deep */
+static VC_CONTAINER_STATUS_T asf_skip_unprocessed_object( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ LOG_DEBUG(p_ctx, "%"PRIi64" bytes ignored in unhandled object", size);
+
+ if(size < ASF_MAX_OBJECT_SIZE)
+ SKIP_BYTES(p_ctx, size); /* read a small amount */
+ else
+ SEEK(p_ctx, STREAM_POSITION(p_ctx) + size); /* seek a large distance */
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_find_packet_header( VC_CONTAINER_T *p_ctx,
+ ASF_PACKET_STATE *p_state )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int search_size = 64*1024; /* should be max packet size according to spec */
+#ifdef ENABLE_CONTAINER_LOG_DEBUG
+ uint64_t offset = STREAM_POSITION(p_ctx);
+#endif
+ uint8_t h[3];
+ VC_CONTAINER_PARAM_UNUSED(p_state);
+
+ /* Limit the search up to what should theoretically be the packet boundary */
+ if(module->packet_size)
+ search_size = module->packet_size -
+ (STREAM_POSITION(p_ctx) - module->data_offset) % module->packet_size;
+
+ for(; search_size > sizeof(h); search_size--)
+ {
+ if(PEEK_BYTES(p_ctx, h, sizeof(h)) != sizeof(h))
+ return STREAM_STATUS(p_ctx);
+
+ if(!h[0] && !h[1] && h[2] == 0x82)
+ {
+ search_size = 2;
+ break; /* Got it */
+ }
+
+ SKIP_BYTES(p_ctx, 1);
+ }
+
+ /* If we failed, we just skip to the theoretical packet boundary */
+ SKIP_BYTES(p_ctx, search_size);
+
+ LOG_DEBUG(p_ctx, "found potential sync, discarded %"PRIu64" bytes)",
+ STREAM_POSITION(p_ctx) - offset);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_read_packet_header( VC_CONTAINER_T *p_ctx,
+ ASF_PACKET_STATE *p_state, uint64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ uint64_t offset = STREAM_POSITION(p_ctx);
+ uint8_t flags, property_flags, length;
+ VC_CONTAINER_PARAM_UNUSED(size);
+
+ p_state->start = offset;
+
+ LOG_FORMAT(p_ctx, "Packet Offset: %"PRIu64, offset);
+
+ if ((module->data_size > 0) && (offset >= (module->data_size + module->data_offset)))
+ {
+ return VC_CONTAINER_ERROR_EOS;
+ }
+
+ /* Find out whether we are dealing with error correction data or payload parsing info */
+ if( PEEK_U8(p_ctx) >> 7 )
+ {
+ /* We have error correction data */
+ flags = READ_U8(p_ctx, "Error Correction Flags");
+ length = flags & 0xF;
+ SKIP_BYTES(p_ctx, length); /* Error correction data */
+ }
+
+ /* Payload parsing information */
+ flags = READ_U8(p_ctx, "Length Type Flags");
+ p_state->multiple_payloads = flags & 1;
+ property_flags = READ_U8(p_ctx, "Property Flags");
+ p_state->replicated_data_lt = (property_flags >> 0) & 0x3;
+ p_state->offset_into_media_object_lt = (property_flags >> 2) & 0x3;
+ p_state->media_object_number_lt = (property_flags >> 4) & 0x3;
+
+ /* Sanity check stream number length type */
+ if(((property_flags >> 6) & 0x3) != 1)
+ goto error;
+
+ /* If there's no packet size field we default to the size in the file header. */
+ p_state->size = READ_VLC(p_ctx, (flags >> 5) & 0x3 /* Packet length type */,
+ module->packet_size, "Packet Length");
+
+ READ_VLC(p_ctx, (flags>>1)&0x3 /* Sequence type */, 0, "Sequence");
+ p_state->padding_size = READ_VLC(p_ctx, (flags>>3)&0x3 /* Padding length type */, 0, "Padding Length");
+ p_state->send_time = READ_U32(p_ctx, "Send Time") * UINT64_C(1000); /* Read in millisecond units, stored in uS */
+ SKIP_U16(p_ctx, "Duration"); /* in milliseconds */
+
+ p_state->num_payloads = 1;
+ p_state->current_payload = 0;
+ if(p_state->multiple_payloads)
+ {
+ LOG_FORMAT(p_ctx, "Multiple Payloads");
+ flags = READ_U8(p_ctx, "Payload Flags");
+ p_state->num_payloads = flags & 0x3F;
+ LOG_FORMAT(p_ctx, "Number of Payloads: %i", p_state->num_payloads);
+ p_state->payload_lt = (flags >> 6) & 3;
+
+ /* Sanity check */
+ if(!p_state->num_payloads) goto error;
+ }
+
+ /* Update the current offset in the packet. */
+ p_state->current_offset = STREAM_POSITION(p_ctx) - offset;
+
+ /* Sanity check offset */
+ if(p_state->current_offset > p_state->size) goto error;
+
+ /* Sanity check padding size */
+ if(p_state->padding_size + p_state->current_offset > p_state->size) goto error;
+
+ /* Sanity check packet size */
+ if(!module->broadcast &&
+ (p_state->size != module->packet_size)) goto error;
+
+ return STREAM_STATUS(p_ctx);
+
+ error:
+ LOG_FORMAT(p_ctx, "Invalid payload parsing information (offset %"PRIu64")", STREAM_POSITION(p_ctx));
+ return STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS ?
+ VC_CONTAINER_ERROR_CORRUPTED : STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_read_payload_header( VC_CONTAINER_T *p_ctx,
+ ASF_PACKET_STATE *p_state /* uint64_t size */ )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ uint32_t rep_data_length;
+
+ if(p_state->current_payload >= p_state->num_payloads)
+ return VC_CONTAINER_ERROR_CORRUPTED;
+
+ p_state->stream_num = READ_U8(p_ctx, "Stream Number");
+ if(!(p_state->stream_num & 0x7F)) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ p_state->media_object_num = READ_VLC(p_ctx, p_state->media_object_number_lt, 0, "Media Object Number");
+
+ /* For a compressed packet this field is a timestamp, and is moved to p_state->media_object_pts later */
+ p_state->media_object_off = READ_VLC(p_ctx, p_state->offset_into_media_object_lt, 0, "Offset Into Media Object");
+ rep_data_length = READ_VLC(p_ctx, p_state->replicated_data_lt, 0, "Replicated Data Length");
+
+ /* Sanity check the replicated data length */
+ if(rep_data_length && rep_data_length != 1 &&
+ (rep_data_length < 8 ||
+ STREAM_POSITION(p_ctx) - p_state->start + p_state->padding_size + rep_data_length > p_state->size))
+ {
+ LOG_FORMAT(p_ctx, "invalid replicated data length");
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ /* Read what we need from the replicated data */
+ if(rep_data_length > 1)
+ {
+ p_state->media_object_size = READ_U32(p_ctx, "Media Object Size");
+ p_state->media_object_pts = READ_U32(p_ctx, "Presentation Time") * UINT64_C(1000);
+ p_state->compressed_payloads = 0;
+ SKIP_BYTES(p_ctx, rep_data_length - 8); /* Rest of replicated data */
+ }
+ else if(rep_data_length == 1)
+ {
+ LOG_FORMAT(p_ctx, "Compressed Payload Data");
+ p_state->media_object_pts_delta = READ_U8(p_ctx, "Presentation Time Delta") * UINT64_C(1000);
+ p_state->compressed_payloads = 1;
+
+ /* Move the pts from media_object_off where it was read, and adjust it */
+ p_state->media_object_off *= UINT64_C(1000);
+ p_state->media_object_pts = p_state->media_object_off - p_state->media_object_pts_delta;
+ p_state->media_object_off = 0;
+ p_state->media_object_size = 0;
+ }
+ else
+ {
+ p_state->media_object_size = 0;
+ p_state->media_object_pts = p_state->send_time;
+ p_state->compressed_payloads = 0;
+ }
+
+ if(p_state->media_object_pts > module->preroll + module->time_offset)
+ p_state->media_object_pts -= (module->preroll + module->time_offset);
+ else p_state->media_object_pts = 0;
+
+ p_state->payload_size = p_state->size - p_state->padding_size - (STREAM_POSITION(p_ctx) - p_state->start);
+ if(p_state->multiple_payloads)
+ {
+ p_state->payload_size = READ_VLC(p_ctx, p_state->payload_lt, 0, "Payload Length");
+ if(!p_state->payload_size) return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+ else
+ LOG_FORMAT(p_ctx, "Payload Length: %i", p_state->payload_size);
+
+ if(p_state->payload_size >= p_state->size) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ p_state->subpayload_size = p_state->payload_size;
+
+ /* Update current_offset to reflect the variable number of bytes we just read */
+ p_state->current_offset = STREAM_POSITION(p_ctx) - p_state->start;
+
+ /* Sanity check offset */
+ if(p_state->current_offset > p_state->size) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_read_object_data( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int i;
+ VC_CONTAINER_PARAM_UNUSED(size);
+
+ SKIP_GUID(p_ctx, "File ID");
+ SKIP_U64(p_ctx, "Total Data Packets");
+ SKIP_U16(p_ctx, "Reserved");
+ module->data_offset = STREAM_POSITION(p_ctx);
+
+ /* Initialise state for all tracks */
+ module->packet_state.start = module->data_offset;
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i];
+ p_track->priv->module->p_packet_state = &module->packet_state;
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+/* Read the next sub-payload or next payload */
+static VC_CONTAINER_STATUS_T asf_read_next_payload_header( VC_CONTAINER_T *p_ctx,
+ ASF_PACKET_STATE *p_state, uint32_t *pi_track, uint32_t *pi_length)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+
+ if(p_state->subpayload_size)
+ {
+ /* We still haven't read the current subpayload, return the info we already have */
+ goto end;
+ }
+
+ /* Check if we're done reading a packet */
+ if(p_state->current_payload >= p_state->num_payloads)
+ {
+ /* Skip the padding at the end */
+ if(p_state->size)
+ {
+ int32_t pad_length = p_state->size - (STREAM_POSITION(p_ctx) - p_state->start);
+ if(pad_length < 0) return VC_CONTAINER_ERROR_CORRUPTED;
+ SKIP_BYTES(p_ctx, pad_length); /* Padding */
+ }
+
+ /* Read the header for the next packet */
+ module->object_level = 0; /* For debugging */
+ status = asf_read_packet_header( p_ctx, p_state, (uint64_t)0/*size???*/ );
+ module->object_level = 1; /* For debugging */
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ }
+
+ /* Check if we're done reading a payload */
+ if(!p_state->payload_size)
+ {
+ /* Read the payload header */
+ status = asf_read_payload_header( p_ctx, p_state );
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ }
+
+ /* For compressed payloads, payload_size != subpayload_size */
+ if(p_state->compressed_payloads && p_state->payload_size)
+ {
+ p_state->payload_size--;
+ p_state->subpayload_size = READ_U8(p_ctx, "Sub-Payload Data Length");
+ if(p_state->subpayload_size > p_state->payload_size)
+ {
+ /* TODO: do something ? */
+ LOG_DEBUG(p_ctx, "subpayload is too big");
+ p_state->subpayload_size = p_state->payload_size;
+ }
+ p_state->media_object_off = 0;
+ p_state->media_object_size = p_state->subpayload_size;
+ p_state->media_object_pts += p_state->media_object_pts_delta;
+ }
+
+ end:
+ /* We've read the payload header, return the requested info */
+ if(pi_track) *pi_track = module->stream_number_to_index[p_state->stream_num & 0x7F];
+ if(pi_length) *pi_length = p_state->subpayload_size;
+
+ p_state->current_offset = STREAM_POSITION(p_ctx) - p_state->start;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+/* read the next payload (not sub-payload) */
+static VC_CONTAINER_STATUS_T asf_read_next_payload( VC_CONTAINER_T *p_ctx,
+ ASF_PACKET_STATE *p_state, uint8_t *p_data, uint32_t *pi_size )
+{
+ uint32_t subpayload_size = p_state->subpayload_size;
+
+ if(p_data && *pi_size < subpayload_size) subpayload_size = *pi_size;
+
+ if(!p_state->subpayload_size)
+ return VC_CONTAINER_SUCCESS;
+
+ p_state->payload_size -= subpayload_size;
+ if(!p_state->payload_size) p_state->current_payload++;
+ p_state->subpayload_size -= subpayload_size;
+ p_state->media_object_off += subpayload_size;
+
+ if(p_data) *pi_size = READ_BYTES(p_ctx, p_data, subpayload_size);
+ else *pi_size = SKIP_BYTES(p_ctx, subpayload_size);
+
+ p_state->current_offset += subpayload_size;
+
+ if(*pi_size!= subpayload_size)
+ return STREAM_STATUS(p_ctx);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/******************************************************************************
+Functions exported as part of the Container Module API
+ *****************************************************************************/
+/*****************************************************************************/
+/** Read data from the ASF file
+
+@param p_ctx Context for the file being read from
+
+@param p_packet Packet information. Includes data buffer and stream ID as aprropriate.
+
+@param flags Flags controlling the read.
+ May request reading only, skipping a packet or force access to a set track.
+
+******************************************************************************/
+static VC_CONTAINER_STATUS_T asf_reader_read( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *p_packet, uint32_t flags )
+{
+ VC_CONTAINER_MODULE_T *global_module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ ASF_PACKET_STATE *p_state;
+ uint32_t buffer_size = 0, track, data_size;
+ uint64_t state_pos;
+
+ LOG_DEBUG(p_ctx, "asf_reader_read track %"PRIu32" flags %u", p_packet->track, flags);
+
+ /* If a specific track has been selected, we need to use the track packet state */
+ if(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK)
+ {
+ vc_container_assert(p_packet->track < p_ctx->tracks_num);
+ /* The state to use is the one referred to by the track we selected */
+ p_state = p_ctx->tracks[p_packet->track]->priv->module->p_packet_state;
+ }
+ else
+ {
+ /* No specific track was selected. Read the next data from the global position. */
+ p_state = &global_module->packet_state;
+ }
+
+ /* Stop if the stream can't be read */
+ if(p_state->eos) return VC_CONTAINER_ERROR_EOS;
+ if(p_state->corrupted) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ /* If we aren't in the right position in the file go there now. */
+ state_pos = p_state->start + p_state->current_offset;
+ if ((uint64_t)STREAM_POSITION(p_ctx) != state_pos)
+ {
+ LOG_DEBUG(p_ctx, "seeking from %"PRIu64" to %"PRIu64, STREAM_POSITION(p_ctx), state_pos);
+ SEEK(p_ctx, state_pos);
+ }
+
+ /* Look at the next payload header */
+ status = asf_read_next_payload_header( p_ctx, p_state, &track, &data_size );
+ if((status == VC_CONTAINER_ERROR_CORRUPTED)
+ && (p_state->bad_packets < ASF_MAX_CONSECUTIVE_CORRUPTED_PACKETS))
+ {
+ /* If the current packet is corrupted we will try to search for the next packet */
+ uint32_t corrupted = p_state->bad_packets;
+ LOG_DEBUG(p_ctx, "packet offset %"PRIi64" is corrupted", p_state->start);
+ memset(p_state, 0, sizeof(*p_state));
+ p_state->bad_packets = corrupted + 1;
+
+ /* TODO: flag discontinuity */
+
+ if(asf_find_packet_header(p_ctx, p_state) == VC_CONTAINER_SUCCESS)
+ {
+ p_state->start = STREAM_POSITION(p_ctx);
+ return VC_CONTAINER_ERROR_CONTINUE;
+ }
+ }
+ if(status == VC_CONTAINER_ERROR_EOS) p_state->eos = true;
+ if(status == VC_CONTAINER_ERROR_CORRUPTED) p_state->corrupted = true;
+ if(status != VC_CONTAINER_SUCCESS)
+ {
+ return status;
+ }
+
+ p_state->bad_packets = 0;
+
+ /* bad track number or track is disabled */
+ if(track >= p_ctx->tracks_num || !p_ctx->tracks[track]->is_enabled)
+ {
+ LOG_DEBUG(p_ctx, "skipping packet because track %u is invalid or disabled", track);
+
+ /* Skip payload by reading with a null buffer */
+ status = asf_read_next_payload(p_ctx, p_state, 0, &data_size );
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ return VC_CONTAINER_ERROR_CONTINUE;
+ }
+
+ track_module = p_ctx->tracks[track]->priv->module;
+
+ /* If we are reading from the global state, and the track we found is not on the global state,
+ * either skip the data or reconnect it to the global state */
+ if ((p_state == &global_module->packet_state) &&
+ (track_module->p_packet_state != &global_module->packet_state))
+ {
+ uint64_t track_pos =
+ track_module->p_packet_state->start
+ + track_module->p_packet_state->current_offset;
+
+ /* Check if the end of the current packet is beyond the track's position */
+ if (track_pos > state_pos + track_module->p_packet_state->size)
+ {
+ LOG_DEBUG(p_ctx, "skipping packet from track %u as it has already been read", track);
+ status = asf_read_next_payload(p_ctx, p_state, 0, &data_size);
+
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ return VC_CONTAINER_ERROR_CONTINUE;
+ }
+ else
+ {
+ LOG_DEBUG(p_ctx, "switching track index %u location %"PRIu64" back to global state", track, track_pos);
+ track_module->p_packet_state = &global_module->packet_state;
+
+ /* Update the global state to the precise position */
+ global_module->packet_state = track_module->local_packet_state;
+ return VC_CONTAINER_ERROR_CONTINUE;
+ }
+ }
+
+ /* If we are forcing, and the data is from a different track, skip it.
+ * We may need to move the track we want onto a local state. */
+ if ((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK)
+ && (track != p_packet->track))
+ {
+ track_module = p_ctx->tracks[p_packet->track]->priv->module;
+
+ /* If the track we found is on the same state as the track we want they must both be on the global state */
+ if (p_ctx->tracks[track]->priv->module->p_packet_state == p_state)
+ {
+ LOG_DEBUG(p_ctx, "switching track index %u location %"PRIu64" away from global state", p_packet->track, state_pos);
+
+ /* Change the track we want onto a local state */
+ track_module->p_packet_state = &track_module->local_packet_state;
+
+ /* Copy the global state into the local state for the track we are forcing */
+ track_module->local_packet_state = global_module->packet_state;
+ }
+
+ LOG_DEBUG(p_ctx, "skipping packet from track %u while forcing %u", track, p_packet->track);
+ status = asf_read_next_payload(p_ctx, track_module->p_packet_state, 0, &data_size );
+ return VC_CONTAINER_ERROR_CONTINUE;
+ }
+
+ /* If we arrive here either the data is from the track we are forcing, or we are not forcing
+ * and we haven't already read the data while forcing that track */
+
+ /* If skip, and no info required, skip over it and return now. */
+ if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO))
+ return asf_read_next_payload(p_ctx, p_state, 0, &data_size );
+
+ /* Fill-in the packet information */
+ if(p_state->media_object_pts == ASF_UNKNOWN_PTS || p_state->media_object_off)
+ p_packet->dts = p_packet->pts = VC_CONTAINER_TIME_UNKNOWN;
+ else
+ p_packet->dts = p_packet->pts = p_state->media_object_pts;
+
+ p_packet->flags = 0;
+
+ if(p_state->stream_num >> 7) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME;
+
+ if(!p_state->media_object_off) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
+
+ if(p_state->media_object_size &&
+ p_state->media_object_off + data_size >= p_state->media_object_size)
+ p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
+
+ if(!p_state->media_object_size)
+ p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
+
+ p_packet->track = track;
+
+ p_packet->frame_size = p_state->media_object_size;
+
+ p_packet->size = data_size;
+
+ /* If the skip flag is set (Info must have been too) skip the data and return */
+ if(flags & VC_CONTAINER_READ_FLAG_SKIP)
+ {
+ /* Skip payload by reading with a null buffer */
+ return asf_read_next_payload(p_ctx, p_state, 0, &data_size );
+ }
+ else if(flags & VC_CONTAINER_READ_FLAG_INFO)
+ {
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ /* Read the payload data */
+ buffer_size = p_packet->buffer_size;
+ status = asf_read_next_payload(p_ctx, p_state, p_packet->data, &buffer_size );
+ if(status != VC_CONTAINER_SUCCESS)
+ {
+ /* FIXME */
+ return status;
+ }
+
+ p_packet->size = buffer_size;
+ if(buffer_size != data_size)
+ p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END;
+ LOG_DEBUG(p_ctx, "asf_reader_read exit %u PTS %"PRIi64" track %"PRIu32, status, p_packet->pts, track);
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_reader_index_find_time( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_MODULE_T* track_module, int64_t time, uint32_t *packet_num, bool forward )
+{
+ VC_CONTAINER_STATUS_T status;
+ uint32_t entry, previous_packet_num;
+ bool eos = false;
+
+ /* Default to beginning of file in case of error */
+ *packet_num = 0;
+
+ /* Special case - time zero is beginning of file */
+ if(time == 0) {return VC_CONTAINER_SUCCESS;}
+
+ /* Sanity checking */
+ if(!track_module->simple_index.num_entries) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ if(!track_module->simple_index.time_interval) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ entry = time / track_module->simple_index.time_interval;
+ LOG_DEBUG(p_ctx, "entry: %i, offset: %"PRIi64", interv: %"PRIi64, entry,
+ track_module->simple_index.offset, track_module->simple_index.time_interval);
+ if(entry >= track_module->simple_index.num_entries)
+ {
+ entry = track_module->simple_index.num_entries - 1;
+ eos = true;
+ }
+
+ /* Fetch the entry from the index */
+ status = SEEK(p_ctx, track_module->simple_index.offset + 6 * entry);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ *packet_num = READ_U32(p_ctx, "Packet Number");
+ if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) return STREAM_STATUS(p_ctx);
+
+ /* When asking for the following keyframe we need to find the next entry with a greater
+ * packet number */
+ previous_packet_num = *packet_num;
+ while(!eos && forward && previous_packet_num == *packet_num)
+ {
+ if(++entry == track_module->simple_index.num_entries) {eos = true; break;}
+ status = SEEK(p_ctx, track_module->simple_index.offset + 6 * entry);
+ if(status != VC_CONTAINER_SUCCESS) break;
+ *packet_num = READ_U32(p_ctx, "Packet Number");
+ if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) break;
+ }
+
+ if(eos && track_module->simple_index.incomplete) return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
+ else if(eos) return VC_CONTAINER_ERROR_EOS;
+ else return STREAM_STATUS(p_ctx);
+}
+
+#if 0
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_reader_index_find_packet( VC_CONTAINER_T *p_ctx,
+ unsigned int track, uint32_t *packet_num, bool forward )
+{
+ VC_CONTAINER_STATUS_T status;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = 0;
+ uint32_t i, prev_packet_num = 0, next_packet_num;
+ bool eos = false;
+
+ /* Sanity checking */
+ if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_FAILED;
+ track_module = p_ctx->tracks[track]->priv->module;
+ if(!track_module->num_index_entries) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ if(!track_module->index_time_interval) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ status = SEEK(p_ctx, track_module->index_offset);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ /* Loop through all the entries in the index */
+ for(i = 0; i < track_module->num_index_entries; i++)
+ {
+ next_packet_num = READ_U32(p_ctx, "Packet Number");
+ SKIP_U16(p_ctx, "Packet Count");
+ if(next_packet_num > *packet_num) break;
+ if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) break;
+ prev_packet_num = next_packet_num;
+ }
+ if(i == track_module->num_index_entries ) eos = true;
+
+ if(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS && !eos)
+ {
+ if(forward) *packet_num = next_packet_num;
+ else *packet_num = prev_packet_num;
+ }
+
+ if(eos && track_module->index_incomplete) return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
+ else if(eos) return VC_CONTAINER_ERROR_EOS;
+ else return STREAM_STATUS(p_ctx);
+}
+#endif
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_reader_find_next_frame( VC_CONTAINER_T *p_ctx,
+ unsigned int track, ASF_PACKET_STATE *p_state, bool keyframe, bool timeout )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint32_t data_track, data_size;
+ unsigned int packets = 0;
+
+ if(p_ctx->tracks[track]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO)
+ keyframe = false;
+
+ /* We still need to go to the right payload */
+ while(status == VC_CONTAINER_SUCCESS &&
+ (!timeout || packets++ < ASF_MAX_SEARCH_PACKETS))
+ {
+ status = asf_read_next_payload_header( p_ctx, p_state, &data_track, &data_size );
+ if(status != VC_CONTAINER_SUCCESS) break;
+
+ if(data_track == track && ((p_state->stream_num >> 7) || !keyframe) &&
+ !p_state->media_object_off) break;
+
+ /* Skip payload */
+ status = asf_read_next_payload(p_ctx, p_state, 0, &data_size );
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+/* Helper for asf_reader_seek - seek when there is a top-level index (spec section 6.2) */
+static VC_CONTAINER_STATUS_T seek_by_top_level_index(
+ VC_CONTAINER_T *p_ctx,
+ int64_t *p_time,
+ VC_CONTAINER_SEEK_MODE_T mode,
+ VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned index;
+ uint64_t time = 0;
+ uint64_t block_address = module->top_level_index.blocks_offset;
+ uint64_t track_positions[ASF_TRACKS_MAX];
+
+ VC_CONTAINER_PARAM_UNUSED(mode);
+ LOG_DEBUG(p_ctx, "seek_by_top_level_index");
+
+ for (index = 0; index < ASF_TRACKS_MAX; ++index)
+ {
+ /* Set all to a stupid value */
+ track_positions[index] = UINT64_MAX;
+ }
+
+ /* Loop through the index blocks to find the one(s) that deal with the time(s) in question.
+ * Note that most ASF files only have one index block. */
+ for (index = 0; index < module->top_level_index.block_count; ++index)
+ {
+ uint64_t block_duration, block_position;
+ uint32_t index_entry_count, stream;
+ LOG_DEBUG(p_ctx, "looking for index blocks at offset %"PRIu64, block_address);
+ status = SEEK(p_ctx, block_address);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ /* Read the number of entries for this index block. */
+ index_entry_count = READ_U32(p_ctx, "Index Entry Count");
+
+ /* Turn into a duration */
+ block_duration = (uint64_t)index_entry_count * module->top_level_index.entry_time_interval;
+
+ /* Go through each stream */
+ for (stream = 0; stream < p_ctx->tracks_num; ++stream)
+ {
+ /* Work out the track's target time */
+ uint64_t track_time = *p_time + module->preroll + module->time_offset;
+
+ /* Have we the correct index block for the seek time? */
+ if ((time <= track_time) && (track_time < time + block_duration))
+ {
+ /* We have the correct index block for the seek time. Work out where in it. */
+ uint32_t block_index = (track_time - time) / module->top_level_index.entry_time_interval;
+ uint64_t active_specifier = module->top_level_index.active_specifiers[stream];
+ uint64_t new_position;
+
+ /* Read the Block Positions value for the correct specifier */
+ status = SEEK(p_ctx,
+ block_address + INT64_C(4)
+ + active_specifier * INT64_C(8));
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ return status;
+ }
+ block_position = READ_U32(p_ctx, "Block Position");
+
+ /* Read the target address for the stream */
+ status = SEEK(p_ctx, block_address + 4 /* skip index entry count */
+ + (UINT64_C(8) * module->top_level_index.specifiers_count) /* block positions */
+ + (UINT64_C(4) * module->top_level_index.specifiers_count * block_index) /* prior index entries */
+ + (UINT64_C(4) * active_specifier)); /* correct specifier */
+ LOG_DEBUG(p_ctx, "reading at %"PRIu64, STREAM_POSITION(p_ctx));
+
+ new_position = module->data_offset + block_position + (uint64_t)READ_U32(p_ctx, "Offset");
+ LOG_DEBUG(p_ctx, "actual address for stream %"PRIu32" = %"PRIu64, stream, new_position);
+ track_positions[stream] = new_position;
+ }
+ }
+
+ /* Work out where the next block is */
+ block_address += (UINT64_C(8) * module->top_level_index.specifiers_count)
+ + (UINT64_C(4) * module->top_level_index.specifiers_count * index_entry_count);
+ }
+
+ return seek_to_positions(p_ctx, track_positions, p_time, flags, 0, 0);
+}
+
+/* Helper for asf_reader_seek -
+ * Given a set of positions seek the tracks. The status is the result of physically seeking each one.
+ * It is expected that the positions will be before *p_time; if the flags require it search
+ * for the next keyframe that is at or above *p_time. */
+static VC_CONTAINER_STATUS_T seek_to_positions(VC_CONTAINER_T *p_ctx, uint64_t track_positions[ASF_TRACKS_MAX],
+ int64_t *p_time, VC_CONTAINER_SEEK_FLAGS_T flags,
+ unsigned int start_track, bool seek_on_start_track)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ uint64_t global_position = UINT64_MAX;
+ unsigned int lowest_track, index, tracks;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ int64_t track_best_pts[ASF_TRACKS_MAX];
+
+ if (*p_time == 0)
+ {
+ // Special case: Time 0 means beginning of file. Don't search for the matching packet(s).
+ memset(&module->packet_state, 0, sizeof(module->packet_state));
+ module->packet_state.start = track_positions[0];
+ status = SEEK(p_ctx, module->packet_state.start);
+
+ // Set each track to using the global state
+ for(index = 0; index < p_ctx->tracks_num; index++)
+ {
+ p_ctx->tracks[index]->priv->module->p_packet_state = &module->packet_state;
+ }
+
+ return status;
+ }
+
+ for(tracks = 0, index = start_track; tracks < p_ctx->tracks_num;
+ tracks++, index = (index+1) % p_ctx->tracks_num)
+ {
+ uint32_t data_size;
+
+ /* Use an on-stack packet state. We can't use the global state, as we must leave it at
+ * the lowest position. We can't use any track's private state, as we will move it past
+ * the desired location. */
+ ASF_PACKET_STATE private_state;
+ memset(&private_state, 0, sizeof(private_state));
+
+ track_best_pts[index] = INT64_MAX;
+
+ status = SEEK(p_ctx, track_positions[index]);
+
+ /* loop until we find the packet we're looking for.
+ * stop when we've seen a big enough PTS, and are on a key frame */
+ while(status == VC_CONTAINER_SUCCESS)
+ {
+ /* Get the next key-frame */
+ status = asf_reader_find_next_frame(p_ctx, index, &private_state, true, true);
+ if(status == VC_CONTAINER_SUCCESS)
+ {
+ /* Get the PTS, if any */
+ int64_t pts = (int64_t)private_state.media_object_pts;
+
+ if(pts != ASF_UNKNOWN_PTS)
+ {
+ if ((track_best_pts[index] == INT64_MAX) /* we don't have a time yet */
+ || (pts <= *p_time) /* it's before our target */
+ || (flags & VC_CONTAINER_SEEK_FLAG_FORWARD)) /* we want after target */
+ {
+ /* Store this time. It's the best yet. */
+ track_best_pts[index] = pts;
+
+ /* Update the desired position */
+ track_positions[index] = private_state.start + private_state.current_offset;
+
+ /* Copy the local state into this track's private state */
+ p_ctx->tracks[index]->priv->module->local_packet_state = private_state;
+
+ LOG_DEBUG(p_ctx, "seek forward track %u to pts %"PRIu64,
+ index, track_best_pts[index]);
+ }
+
+ /* If we've got to our target time we can stop. */
+ if (pts >= *p_time)
+ {
+ /* Then stop. */
+ break;
+ }
+ }
+
+ status = asf_read_next_payload(p_ctx, &private_state, 0, &data_size );
+ }
+ }
+
+ /* If we are seeking using a specific track, usually this is the video track
+ * and we want all the other tracks to start at the same time or later */
+ if (seek_on_start_track && start_track == index)
+ {
+ flags |= VC_CONTAINER_SEEK_FLAG_FORWARD;
+ *p_time = track_best_pts[index];
+ }
+
+ {
+ ASF_PACKET_STATE *p_state = &p_ctx->tracks[index]->priv->module->local_packet_state;
+
+ LOG_DEBUG(p_ctx, "seek track %u to pts %"PRIu64" (key:%i,moo:%i)",
+ index, track_best_pts[index], p_state->stream_num >> 7, p_state->media_object_off);
+ }
+ }
+
+ /* Find the smallest track address in track_positions. This will be the global position */
+ /* Also the lowest PTS in track_best_pts, this will be the new global PTS */
+ for (index = 0, lowest_track = 0; index < p_ctx->tracks_num; ++index)
+ {
+ /* If it is smaller, remember it */
+ if (track_positions[index] < global_position)
+ {
+ global_position = track_positions[index];
+ lowest_track = index;
+ }
+
+ /* Put the lowest PTS into entry 0 of the array */
+ if ((track_best_pts[index] != INT64_MAX) && (track_best_pts[index] < track_best_pts[0]))
+ {
+ track_best_pts[0] = track_best_pts[index];
+ }
+ }
+
+ /* Update the caller with the lowest real PTS, if any. (we may have already done this above) */
+ if (track_best_pts[0] != INT64_MAX)
+ {
+ *p_time = track_best_pts[0];
+ }
+ else
+ {
+ LOG_DEBUG(p_ctx, "no PTS suitable to update the caller");
+ }
+
+ /* As we did an extra read on the index track past the desired location seek back to it */
+ status = SEEK(p_ctx, global_position);
+
+ /* Copy the packet state for the stream with the lowest address into the global state */
+ module->packet_state = p_ctx->tracks[lowest_track]->priv->module->local_packet_state;
+
+ for(index = 0; index < p_ctx->tracks_num; index++)
+ {
+ VC_CONTAINER_TRACK_MODULE_T* track_mod = p_ctx->tracks[index]->priv->module;
+
+ /* If the track position is the global position, or it is invalid, use the global state */
+ if ((track_positions[index] <= global_position) || (track_positions[index] == UINT64_MAX))
+ {
+ track_mod->p_packet_state = &module->packet_state;
+ }
+ else
+ {
+ /* Track is not at the global position. Use the local state. */
+ LOG_DEBUG(p_ctx, "track %u local position %"PRIu64, index, track_positions[index]);
+ track_mod->p_packet_state = &track_mod->local_packet_state;
+ }
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+/* Seek to a location in the file, using whatever indices are available
+ * If flags bit VC_CONTAINER_SEEK_FLAG_FORWARD is set the position is guaranteed to
+ * be a keyframe at or after the requested location. Conversely if it is not set
+ * the position is guaranteed to be at or before the request. */
+static VC_CONTAINER_STATUS_T asf_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_time,
+ VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_EOS; /* initialised to known fail state */
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int stream;
+
+ VC_CONTAINER_PARAM_UNUSED(mode);
+
+ LOG_DEBUG(p_ctx, "asf_reader_seek");
+
+ /* Prefer the top-level index to the simple index - it has byte offsets not packet offsets,
+ * and is likely to have separate tables for every track */
+ if (module->top_level_index.block_count)
+ {
+ status = seek_by_top_level_index(p_ctx, p_time, mode, flags);
+ }
+ else
+ {
+ uint64_t track_positions[ASF_TRACKS_MAX];
+ int seek_track = -1;
+ uint32_t packet_num;
+ uint64_t new_position;
+
+ if (*p_time == 0)
+ {
+ // Special optimisation - for time zero just go to the beginning.
+ packet_num = 0;
+ }
+ /* If there is a simple index use the packet number from it */
+ else if (module->simple_index_track)
+ {
+ /* Correct time desired */
+ uint64_t track_time = *p_time + module->preroll + module->time_offset;
+
+ LOG_DEBUG(p_ctx, "using simple index");
+
+ /* Search the index for the correct packet */
+ status = asf_reader_index_find_time(p_ctx, module->simple_index_track, track_time,
+ &packet_num, flags & VC_CONTAINER_SEEK_FLAG_FORWARD);
+ }
+ else
+ {
+ /* No index at all. Use arithmetic to guess the packet number. */
+ LOG_DEBUG(p_ctx, "index not usable %u", (unsigned)status);
+
+ if (module->packets_num == 0)
+ {
+ /* This is a broadcast stream, and we can't do the arithmetic.
+ * Set it to a value that will guarantee a seek fail. */
+ LOG_DEBUG(p_ctx, "no packets in file");
+ packet_num = UINT32_MAX;
+ }
+ else
+ {
+ packet_num = *p_time * module->packets_num / module->duration;
+ }
+ }
+
+ /* calculate the byte address of the packet, relative to the start of data */
+ new_position = (uint64_t)packet_num * (uint64_t)module->packet_size;
+
+ LOG_DEBUG(p_ctx, "packet number %"PRIu32" approx byte offset %"PRIu64 , packet_num, new_position + module->data_offset);
+ if (new_position > (uint64_t)module->data_size)
+ {
+ new_position = module->data_size;
+ LOG_DEBUG(p_ctx, "arithmetic error, seeking to end of file %" PRIu64 , new_position + module->data_offset);
+ }
+
+ new_position += module->data_offset;
+
+ for(stream = 0; stream < p_ctx->tracks_num; stream++)
+ {
+ /* Use the 1st enabled video track as the seek stream */
+ if(p_ctx->tracks[stream]->format->es_type ==
+ VC_CONTAINER_ES_TYPE_VIDEO &&
+ p_ctx->tracks[stream]->is_enabled && seek_track < 0)
+ seek_track = stream;
+
+ track_positions[stream] = new_position;
+ } /* repeat for all tracks */
+
+ /* Work out if we actually got anywhere. If so, save the positions for the subsequent reads */
+ status = seek_to_positions(p_ctx, track_positions, p_time, flags,
+ seek_track < 0 ? 0 : seek_track, seek_track >= 0);
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_reader_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int i;
+
+/* FIXME: metadata is currently shared across all readers so freeing
+ it is left to the common layer but this isn't necessarily
+ the best solution.
+ for(i = 0; i meta_num; i++)
+ free(p_ctx->meta[i]);
+ if(p_ctx->meta_num) free(p_ctx->meta);
+ p_ctx->meta_num = 0;
+*/
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ vc_container_free_track(p_ctx, p_ctx->tracks[i]);
+ p_ctx->tracks_num = 0;
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = 0;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ unsigned int i;
+ GUID_T guid;
+
+ /* Check for an ASF top-level header object */
+ if(PEEK_BYTES(p_ctx, (uint8_t *)&guid, sizeof(guid)) < sizeof(guid) ||
+ memcmp(&guid, &asf_guid_header, sizeof(guid)))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /*
+ * We are dealing with an ASF file
+ */
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+
+ /* Set the translation table to all error values */
+ memset(&module->stream_number_to_index, 0xff, sizeof(module->stream_number_to_index));
+ p_ctx->priv->module = module;
+ p_ctx->tracks = module->tracks;
+
+ /* Read the top level header object */
+ status = asf_read_object(p_ctx, INT64_C(0));
+ if(status != VC_CONTAINER_SUCCESS)
+ goto error;
+
+ /* Bail out if we didn't find a track */
+ if(!p_ctx->tracks_num) {status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE; goto error;}
+
+ /*
+ * The top level data object must come next
+ */
+ if(READ_GUID(p_ctx, &guid, "Object ID") != sizeof(guid) ||
+ memcmp(&guid, &asf_guid_data, sizeof(guid)))
+ goto error;
+
+ LOG_FORMAT(p_ctx, "Object Name: data");
+ module->data_size = READ_U64(p_ctx, "Object Size");
+
+ /* If the data size was supplied remove the size of the common object header and the local header for this object */
+ if(module->data_size) module->data_size -= ASF_OBJECT_HEADER_SIZE + 16 + 8 + 2;
+
+ /* Sanity check the data object size */
+ if(module->data_size < 0)
+ goto error;
+
+ module->object_level++;
+ SKIP_GUID(p_ctx, "File ID");
+ module->packets_num = READ_U64(p_ctx, "Total Data Packets");
+ if(module->broadcast) module->packets_num = 0;
+ SKIP_U16(p_ctx, "Reserved");
+
+ if (module->packet_size)
+ {
+ LOG_DEBUG(p_ctx, "object size %"PRIu64" means %f packets",
+ module->data_size, (float)(module->data_size) / (float)(module->packet_size));
+ }
+
+ module->data_offset = STREAM_POSITION(p_ctx);
+ LOG_DEBUG(p_ctx, "expect end of data at address %"PRIu64, module->data_size + module->data_offset);
+
+ module->object_level--;
+
+ /*
+ * We now have all the information we really need to start playing the stream
+ */
+
+ /* Initialise state for all tracks */
+ module->packet_state.start = module->data_offset;
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i];
+ p_track->priv->module->p_packet_state = &module->packet_state;
+ }
+
+ p_ctx->priv->pf_close = asf_reader_close;
+ p_ctx->priv->pf_read = asf_reader_read;
+ p_ctx->priv->pf_seek = asf_reader_seek;
+
+ if(STREAM_SEEKABLE(p_ctx))
+ {
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_FORCE_TRACK;
+ }
+
+ p_ctx->duration = module->duration;
+
+ /* Check if we're done */
+ if(!module->data_size || !STREAM_SEEKABLE(p_ctx))
+ return VC_CONTAINER_SUCCESS;
+
+ /* If the stream is seekable and not a broadcast stream, we want to use any index there
+ * might be at the end of the stream */
+
+ /* Seek back to the end of the data object */
+ if( SEEK(p_ctx, module->data_offset + module->data_size) == VC_CONTAINER_SUCCESS)
+ {
+ /* This will catch the simple index object if it is there */
+ do {
+ status = asf_read_object(p_ctx, INT64_C(0));
+ } while(status == VC_CONTAINER_SUCCESS);
+ }
+
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ if(p_ctx->tracks[i]->priv->module->simple_index.offset)
+ LOG_DEBUG(p_ctx, "track %i has an index", i);
+ }
+
+ /* Seek back to the start of the data */
+ return SEEK(p_ctx, module->data_offset);
+
+ error:
+ if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ LOG_DEBUG(p_ctx, "asf: error opening stream (%i)", status);
+ if(module) asf_reader_close(p_ctx);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open asf_reader_open
+#endif
diff --git a/gfx/include/userland/containers/asf/asf_writer.c b/gfx/include/userland/containers/asf/asf_writer.c
new file mode 100644
index 0000000000..60da7ce410
--- /dev/null
+++ b/gfx/include/userland/containers/asf/asf_writer.c
@@ -0,0 +1,577 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+//#define ENABLE_CONTAINERS_LOG_FORMAT
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_writer_utils.h"
+#include "containers/core/containers_logging.h"
+#undef CONTAINER_HELPER_LOG_INDENT
+#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->object_level
+
+VC_CONTAINER_STATUS_T asf_writer_open( VC_CONTAINER_T *p_ctx );
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+#define ASF_TRACKS_MAX 16
+#define ASF_OBJECT_HEADER_SIZE (16+8)
+
+/******************************************************************************
+Type definitions.
+******************************************************************************/
+typedef enum {
+ ASF_OBJECT_TYPE_UNKNOWN = 0,
+ ASF_OBJECT_TYPE_HEADER,
+ ASF_OBJECT_TYPE_FILE_PROPS,
+ ASF_OBJECT_TYPE_STREAM_PROPS,
+ ASF_OBJECT_TYPE_EXT_STREAM_PROPS,
+ ASF_OBJECT_TYPE_DATA,
+ ASF_OBJECT_TYPE_SIMPLE_INDEX,
+ ASF_OBJECT_TYPE_INDEX,
+ ASF_OBJECT_TYPE_HEADER_EXT,
+ ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL,
+ ASF_OBJECT_TYPE_CODEC_LIST,
+ ASF_OBJECT_TYPE_CONTENT_DESCRIPTION,
+ ASF_OBJECT_TYPE_EXT_CONTENT_DESCRIPTION,
+ ASF_OBJECT_TYPE_STREAM_BITRATE_PROPS,
+ ASF_OBJECT_TYPE_LANGUAGE_LIST,
+ ASF_OBJECT_TYPE_METADATA,
+ ASF_OBJECT_TYPE_PADDING,
+} ASF_OBJECT_TYPE_T;
+
+typedef struct VC_CONTAINER_TRACK_MODULE_T
+{
+ unsigned int stream_id;
+ uint64_t time_offset;
+ bool b_valid;
+
+ uint64_t index_offset;
+ uint32_t num_index_entries;
+ int64_t index_time_interval;
+} VC_CONTAINER_TRACK_MODULE_T;
+
+typedef struct VC_CONTAINER_MODULE_T
+{
+ int object_level;
+ uint32_t packet_size;
+
+ VC_CONTAINER_TRACK_T *tracks[ASF_TRACKS_MAX];
+
+ VC_CONTAINER_WRITER_EXTRAIO_T null;
+ bool b_header_done;
+
+ unsigned int current_track;
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Static functions within this file.
+******************************************************************************/
+static VC_CONTAINER_STATUS_T asf_write_object( VC_CONTAINER_T *p_ctx, ASF_OBJECT_TYPE_T object_type );
+static VC_CONTAINER_STATUS_T asf_write_object_header( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T asf_write_object_header_ext( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T asf_write_object_header_ext_internal( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T asf_write_object_file_properties( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T asf_write_object_stream_properties( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T asf_write_object_ext_stream_properties( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T asf_write_object_simple_index( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T asf_write_object_index( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T asf_write_object_data( VC_CONTAINER_T *p_ctx );
+#if 0
+static VC_CONTAINER_STATUS_T asf_write_object_codec_list( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T asf_write_object_content_description( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T asf_write_object_stream_bitrate_props( VC_CONTAINER_T *p_ctx );
+#endif
+
+static const GUID_T asf_guid_header = {0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
+static const GUID_T asf_guid_file_props = {0x8CABDCA1, 0xA947, 0x11CF, {0x8E, 0xE4, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
+static const GUID_T asf_guid_stream_props = {0xB7DC0791, 0xA9B7, 0x11CF, {0x8E, 0xE6, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
+static const GUID_T asf_guid_ext_stream_props = {0x14E6A5CB, 0xC672, 0x4332, {0x83, 0x99, 0xA9, 0x69, 0x52, 0x06, 0x5B, 0x5A}};
+static const GUID_T asf_guid_data = {0x75B22636, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
+static const GUID_T asf_guid_simple_index = {0x33000890, 0xE5B1, 0x11CF, {0x89, 0xF4, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xCB}};
+static const GUID_T asf_guid_index = {0xD6E229D3, 0x35DA, 0x11D1, {0x90, 0x34, 0x00, 0xA0, 0xC9, 0x03, 0x49, 0xBE}};
+static const GUID_T asf_guid_header_ext = {0x5FBF03B5, 0xA92E, 0x11CF, {0x8E, 0xE3, 0x00, 0xC0, 0x0C, 0x20, 0x53, 0x65}};
+static const GUID_T asf_guid_codec_list = {0x86D15240, 0x311D, 0x11D0, {0xA3, 0xA4, 0x00, 0xA0, 0xC9, 0x03, 0x48, 0xF6}};
+static const GUID_T asf_guid_content_description = {0x75B22633, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
+static const GUID_T asf_guid_ext_content_description = {0xD2D0A440, 0xE307, 0x11D2, {0x97, 0xF0, 0x00, 0xA0, 0xC9, 0x5E, 0xA8, 0x50}};
+static const GUID_T asf_guid_stream_bitrate_props = {0x7BF875CE, 0x468D, 0x11D1, {0x8D, 0x82, 0x00, 0x60, 0x97, 0xC9, 0xA2, 0xB2}};
+static const GUID_T asf_guid_language_list = {0x7C4346A9, 0xEFE0, 0x4BFC, {0xB2, 0x29, 0x39, 0x3E, 0xDE, 0x41, 0x5C, 0x85}};
+static const GUID_T asf_guid_metadata = {0xC5F8CBEA, 0x5BAF, 0x4877, {0x84, 0x67, 0xAA, 0x8C, 0x44, 0xFA, 0x4C, 0xCA}};
+static const GUID_T asf_guid_padding = {0x1806D474, 0xCADF, 0x4509, {0xA4, 0xBA, 0x9A, 0xAB, 0xCB, 0x96, 0xAA, 0xE8}};
+
+static const GUID_T asf_guid_stream_type_video = {0xBC19EFC0, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
+static const GUID_T asf_guid_stream_type_audio = {0xF8699E40, 0x5B4D, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
+static const GUID_T asf_guid_error_correction = {0x20FB5700, 0x5B55, 0x11CF, {0xA8, 0xFD, 0x00, 0x80, 0x5F, 0x5C, 0x44, 0x2B}};
+
+static struct {
+ const ASF_OBJECT_TYPE_T type;
+ const GUID_T *guid;
+ const char *psz_name;
+ VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T * );
+
+} asf_object_list[] =
+{
+ {ASF_OBJECT_TYPE_HEADER, &asf_guid_header, "header", asf_write_object_header},
+ {ASF_OBJECT_TYPE_FILE_PROPS, &asf_guid_file_props, "file properties", asf_write_object_file_properties},
+ {ASF_OBJECT_TYPE_STREAM_PROPS, &asf_guid_stream_props, "stream properties", asf_write_object_stream_properties},
+ {ASF_OBJECT_TYPE_EXT_STREAM_PROPS, &asf_guid_ext_stream_props, "extended stream properties", asf_write_object_ext_stream_properties},
+ {ASF_OBJECT_TYPE_DATA, &asf_guid_data, "data", asf_write_object_data},
+ {ASF_OBJECT_TYPE_SIMPLE_INDEX, &asf_guid_simple_index, "simple index", asf_write_object_simple_index},
+ {ASF_OBJECT_TYPE_INDEX, &asf_guid_index, "index", asf_write_object_index},
+ {ASF_OBJECT_TYPE_HEADER_EXT, &asf_guid_header_ext, "header extension", asf_write_object_header_ext},
+ {ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL, &asf_guid_header_ext, "header extension", asf_write_object_header_ext_internal},
+#if 0
+ {ASF_OBJECT_TYPE_CODEC_LIST, &asf_guid_codec_list, "codec list", asf_write_object_codec_list},
+ {ASF_OBJECT_TYPE_CONTENT_DESCRIPTION, &asf_guid_content_description, "content description", asf_write_object_content_description},
+ {ASF_OBJECT_TYPE_EXT_CONTENT_DESCRIPTION, &asf_guid_ext_content_description, "extended content description", 0},
+ {ASF_OBJECT_TYPE_STREAM_BITRATE_PROPS, &asf_guid_stream_bitrate_props, "stream bitrate properties", asf_write_object_stream_bitrate_props},
+#endif
+ {ASF_OBJECT_TYPE_UNKNOWN, 0, "unknown", 0}
+};
+
+static GUID_T guid_null;
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_write_object( VC_CONTAINER_T *p_ctx, ASF_OBJECT_TYPE_T type )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t object_size = 0;
+ unsigned int i;
+
+ /* Find out which object we want to write */
+ for( i = 0; asf_object_list[i].type && asf_object_list[i].type != type; i++ );
+
+ /* Check we found the requested type */
+ if(!asf_object_list[i].type)
+ {
+ vc_container_assert(0);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ /* We need to find out the size of the object we're going to write.
+ * Because we want to be streamable, we can't just come back later to update the size field.
+ * The easiest way to find out the size of the data we're going to write is to write a dummy
+ * version of it and get the size from that. It is a bit wasteful but is so much easier and
+ * shouldn't really impact performance as there's no actual i/o involved. */
+ if(!vc_container_writer_extraio_enable(p_ctx, &module->null))
+ {
+ asf_object_list[i].pf_func(p_ctx);
+ object_size = STREAM_POSITION(p_ctx);
+ }
+ vc_container_writer_extraio_disable(p_ctx, &module->null);
+
+ /* Special case for header extension internal function */
+ if(type == ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL)
+ {
+ WRITE_U32(p_ctx, object_size, "Header Extension Data Size");
+ /* Call the object specific writing function */
+ status = asf_object_list[i].pf_func(p_ctx);
+ return status;
+ }
+
+ /* Write the object header */
+
+ if(WRITE_GUID(p_ctx, asf_object_list[i].guid, "Object ID") != sizeof(GUID_T))
+ return VC_CONTAINER_ERROR_EOS;
+
+ LOG_FORMAT(p_ctx, "Object Name: %s", asf_object_list[i].psz_name);
+
+ WRITE_U64(p_ctx, object_size + ASF_OBJECT_HEADER_SIZE, "Object Size");
+
+ module->object_level++;
+
+ /* Call the object specific writing function */
+ status = asf_object_list[i].pf_func(p_ctx);
+
+ module->object_level--;
+
+ if(status != VC_CONTAINER_SUCCESS)
+ LOG_DEBUG(p_ctx, "object %s appears to be corrupted", asf_object_list[i].psz_name);
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T asf_write_object_header( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ WRITE_U32(p_ctx, 0, "Number of Header Objects"); /* FIXME: could use that */
+ WRITE_U8(p_ctx, 0, "Reserved1");
+ WRITE_U8(p_ctx, 0, "Reserved2");
+
+ status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_FILE_PROPS);
+ status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER_EXT);
+
+ for(module->current_track = 0; module->current_track < p_ctx->tracks_num;
+ module->current_track++)
+ {
+ status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_STREAM_PROPS);
+ }
+
+ /* Codec List */
+ /* Content Description */
+ /* Stream Bitrate Properties */
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T asf_write_object_header_ext( VC_CONTAINER_T *p_ctx )
+{
+ WRITE_GUID(p_ctx, &guid_null, "Reserved Field 1");
+ WRITE_U16(p_ctx, 0, "Reserved Field 2");
+
+ return asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER_EXT_INTERNAL);
+}
+
+static VC_CONTAINER_STATUS_T asf_write_object_header_ext_internal( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ for(module->current_track = 0; module->current_track < p_ctx->tracks_num;
+ module->current_track++)
+ {
+ status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_EXT_STREAM_PROPS);
+ }
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T asf_write_object_file_properties( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ WRITE_GUID(p_ctx, &guid_null, "File ID");
+ WRITE_U64(p_ctx, 0, "File Size");
+ WRITE_U64(p_ctx, 0, "Creation Date");
+ WRITE_U64(p_ctx, 0, "Data Packets Count");
+ WRITE_U64(p_ctx, 0, "Play Duration");
+ WRITE_U64(p_ctx, 0, "Send Duration");
+ WRITE_U64(p_ctx, 0, "Preroll");
+ WRITE_U32(p_ctx, 0, "Flags");
+ WRITE_U32(p_ctx, module->packet_size, "Minimum Data Packet Size");
+ WRITE_U32(p_ctx, module->packet_size, "Maximum Data Packet Size");
+ WRITE_U32(p_ctx, 0, "Maximum Bitrate");
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T asf_write_bitmapinfoheader( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *p_track )
+{
+ uint32_t fourcc;
+
+ /* Write the preamble to the BITMAPINFOHEADER */
+ WRITE_U32(p_ctx, p_track->format->type->video.width, "Encoded Image Width");
+ WRITE_U32(p_ctx, p_track->format->type->video.height, "Encoded Image Height");
+ WRITE_U8(p_ctx, 0, "Reserved Flags");
+ WRITE_U16(p_ctx, 40 + p_track->format->extradata_size, "Format Data Size");
+
+ /* Write BITMAPINFOHEADER structure */
+ WRITE_U32(p_ctx, 40 + p_track->format->extradata_size, "Format Data Size");
+ WRITE_U32(p_ctx, p_track->format->type->video.width, "Image Width");
+ WRITE_U32(p_ctx, p_track->format->type->video.height, "Image Height");
+ WRITE_U16(p_ctx, 0, "Reserved");
+ WRITE_U16(p_ctx, 0, "Bits Per Pixel Count");
+ fourcc = codec_to_fourcc(p_track->format->codec);
+ WRITE_BYTES(p_ctx, (char *)&fourcc, 4); /* Compression ID */
+ LOG_FORMAT(p_ctx, "Compression ID: %4.4s", (char *)&fourcc);
+ WRITE_U32(p_ctx, 0, "Image Size");
+ WRITE_U32(p_ctx, 0, "Horizontal Pixels Per Meter");
+ WRITE_U32(p_ctx, 0, "Vertical Pixels Per Meter");
+ WRITE_U32(p_ctx, 0, "Colors Used Count");
+ WRITE_U32(p_ctx, 0, "Important Colors Count");
+
+ WRITE_BYTES(p_ctx, p_track->format->extradata, p_track->format->extradata_size);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T asf_write_waveformatex( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *p_track)
+{
+ /* Write WAVEFORMATEX structure */
+ WRITE_U16(p_ctx, codec_to_waveformat(p_track->format->codec), "Codec ID");
+ WRITE_U16(p_ctx, p_track->format->type->audio.channels, "Number of Channels");
+ WRITE_U32(p_ctx, p_track->format->type->audio.sample_rate, "Samples per Second");
+ WRITE_U32(p_ctx, p_track->format->bitrate, "Average Number of Bytes Per Second");
+ WRITE_U16(p_ctx, p_track->format->type->audio.block_align, "Block Alignment");
+ WRITE_U16(p_ctx, p_track->format->type->audio.bits_per_sample, "Bits Per Sample");
+ WRITE_U16(p_ctx, p_track->format->extradata_size, "Codec Specific Data Size");
+ WRITE_BYTES(p_ctx, p_track->format->extradata, p_track->format->extradata_size);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T asf_write_object_stream_properties( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int track = module->current_track, ts_size = 0;
+ const GUID_T *p_guid = &guid_null;
+
+ if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ {
+ p_guid = &asf_guid_stream_type_video;
+ ts_size = 11 + 40 + p_ctx->tracks[track]->format->extradata_size;
+ }
+ else if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
+ {
+ p_guid = &asf_guid_stream_type_audio;
+ ts_size = 18 + p_ctx->tracks[track]->format->extradata_size;
+ }
+
+ WRITE_GUID(p_ctx, p_guid, "Stream Type");
+ WRITE_GUID(p_ctx, &asf_guid_error_correction, "Error Correction Type");
+ WRITE_U64(p_ctx, 0, "Time Offset");
+ WRITE_U32(p_ctx, ts_size, "Type-Specific Data Length");
+ WRITE_U32(p_ctx, 0, "Error Correction Data Length");
+ WRITE_U16(p_ctx, track + 1, "Flags");
+ WRITE_U32(p_ctx, 0, "Reserved");
+
+ /* Type-Specific Data */
+ if(ts_size)
+ {
+ if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ status = asf_write_bitmapinfoheader( p_ctx, p_ctx->tracks[track]);
+ else if(p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
+ status = asf_write_waveformatex( p_ctx, p_ctx->tracks[track]);
+ }
+
+ /* Error Correction Data */
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T asf_write_object_ext_stream_properties( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ WRITE_U64(p_ctx, 0, "Start Time");
+ WRITE_U64(p_ctx, 0, "End Time");
+ WRITE_U32(p_ctx, 0, "Data Bitrate");
+ WRITE_U32(p_ctx, 0, "Buffer Size");
+ WRITE_U32(p_ctx, 0, "Initial Buffer Fullness");
+ WRITE_U32(p_ctx, 0, "Alternate Data Bitrate");
+ WRITE_U32(p_ctx, 0, "Alternate Buffer Size");
+ WRITE_U32(p_ctx, 0, "Alternate Initial Buffer Fullness");
+ WRITE_U32(p_ctx, 0, "Maximum Object Size");
+ WRITE_U32(p_ctx, 0, "Flags");
+ WRITE_U16(p_ctx, module->current_track + 1, "Stream Number");
+ WRITE_U16(p_ctx, 0, "Stream Language ID Index");
+ WRITE_U64(p_ctx, 0, "Average Time Per Frame");
+ WRITE_U16(p_ctx, 0, "Stream Name Count");
+ WRITE_U16(p_ctx, 0, "Payload Extension System Count");
+ /* Stream Names */
+ /* Payload Extension Systems */
+ /* Stream Properties Object */
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T asf_write_object_index( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T asf_write_object_simple_index( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T asf_write_object_data( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_write_header( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status;
+
+ /* TODO Sanity check the tracks */
+
+ status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_HEADER);
+ status = asf_write_object(p_ctx, ASF_OBJECT_TYPE_DATA);
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_writer_write( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *p_packet )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_PARAM_UNUSED(p_packet);
+
+ if(!module->b_header_done)
+ {
+ module->b_header_done = true;
+ status = asf_write_header(p_ctx);
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_writer_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--)
+ vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]);
+
+ vc_container_writer_extraio_delete(p_ctx, &module->null);
+ free(module);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format )
+{
+ VC_CONTAINER_STATUS_T status;
+ VC_CONTAINER_TRACK_T *track;
+
+ /* TODO check we support this format */
+
+ if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED))
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ /* Allocate and initialise track data */
+ if(p_ctx->tracks_num >= ASF_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ p_ctx->tracks[p_ctx->tracks_num] = track =
+ vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
+ if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ if(format->extradata_size)
+ {
+ status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size );
+ if(status) goto error;
+ }
+
+ vc_container_format_copy(track->format, format, format->extradata_size);
+ p_ctx->tracks_num++;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ vc_container_free_track(p_ctx, track);
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T asf_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+
+ switch(operation)
+ {
+ case VC_CONTAINER_CONTROL_TRACK_ADD:
+ {
+ VC_CONTAINER_ES_FORMAT_T *p_format =
+ (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * );
+ return asf_writer_add_track(p_ctx, p_format);
+ }
+
+ case VC_CONTAINER_CONTROL_TRACK_ADD_DONE:
+ if(!module->b_header_done)
+ {
+ status = asf_write_header(p_ctx);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ module->b_header_done = true;
+ }
+ return VC_CONTAINER_SUCCESS;
+
+ default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ }
+}
+
+/******************************************************************************
+Global function definitions.
+******************************************************************************/
+
+VC_CONTAINER_STATUS_T asf_writer_open( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ const char *extension = vc_uri_path_extension(p_ctx->priv->uri);
+ VC_CONTAINER_MODULE_T *module = 0;
+ unsigned int i;
+
+ /* Check if the user has specified a container */
+ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
+
+ /* Check we're the right writer for this */
+ if(!extension)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if(strcasecmp(extension, "asf") && strcasecmp(extension, "wmv") &&
+ strcasecmp(extension, "wma"))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks = module->tracks;
+
+ /* Create a null i/o writer to help us out in writing our data */
+ status = vc_container_writer_extraio_create_null(p_ctx, &module->null);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ /* We'll only write the header once we've got all our tracks */
+
+ p_ctx->priv->pf_close = asf_writer_close;
+ p_ctx->priv->pf_write = asf_writer_write;
+ p_ctx->priv->pf_control = asf_writer_control;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ LOG_DEBUG(p_ctx, "asf: error opening stream");
+ for(i = 0; i < ASF_TRACKS_MAX && p_ctx->tracks && p_ctx->tracks[i]; i++)
+ vc_container_free_track(p_ctx, p_ctx->tracks[i]);
+ free(module);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak writer_open asf_writer_open
+#endif
diff --git a/gfx/include/userland/containers/avi/CMakeLists.txt b/gfx/include/userland/containers/avi/CMakeLists.txt
new file mode 100644
index 0000000000..f8589c1ba5
--- /dev/null
+++ b/gfx/include/userland/containers/avi/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(reader_avi ${LIBRARY_TYPE} avi_reader.c)
+
+target_link_libraries(reader_avi containers)
+
+install(TARGETS reader_avi DESTINATION ${VMCS_PLUGIN_DIR})
+
+add_library(writer_avi ${LIBRARY_TYPE} avi_writer.c)
+
+target_link_libraries(writer_avi containers)
+
+install(TARGETS writer_avi DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/avi/avi_reader.c b/gfx/include/userland/containers/avi/avi_reader.c
new file mode 100644
index 0000000000..4c4c3f4dc9
--- /dev/null
+++ b/gfx/include/userland/containers/avi/avi_reader.c
@@ -0,0 +1,1521 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+#define CONTAINER_IS_LITTLE_ENDIAN
+//#define ENABLE_CONTAINERS_LOG_FORMAT
+//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+#define CONTAINER_HELPER_LOG_INDENT(a) 0
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+#include "containers/core/containers_waveformat.h"
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+#define AVISF_DISABLED 0x00000001 /*< If set stream should not be enabled by default. */
+#define AVIF_MUSTUSEINDEX 0x00000020
+#define AVIF_TRUSTCKTYPE 0x00000800 /*< (OpenDML) keyframe information reliable. */
+
+#define AVIIF_LIST 0x00000001
+#define AVIIF_KEYFRAME 0x00000010
+#define AVIIF_NOTIME 0x00000100
+
+#define AVI_INDEX_OF_INDEXES 0x00
+#define AVI_INDEX_OF_CHUNKS 0x01
+#define AVI_INDEX_2FIELD 0x01
+#define AVI_INDEX_DELTAFRAME 0x80000000
+
+#define AVI_TRACKS_MAX 16 /*< We won't try to handle streams with more tracks than this */
+
+#define AVI_TWOCC(a,b) ((a) | (b << 8))
+
+#define AVI_SYNC_CHUNK(ctx) \
+ while(STREAM_POSITION(ctx) & 1) \
+ { \
+ if (SKIP_BYTES(ctx, 1) != 1) break; \
+ }
+
+#define AVI_SKIP_CHUNK(ctx, size) \
+ do { \
+ SKIP_BYTES(ctx, size); \
+ AVI_SYNC_CHUNK(ctx); \
+ } while(0)
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+typedef struct AVI_TRACK_STREAM_STATE_T
+{
+ unsigned current_track_num; /**< Number of track currently being read */
+ int64_t data_offset; /**< Offset within the stream to find the track data */
+ uint32_t chunk_size; /**< Size of the current chunk being read */
+ uint32_t chunk_data_left; /**< Data left from the current chunk being read */
+
+ unsigned extra_chunk_track_num; /**< Temporary storage for in-band data e.g. 'dd'
+ chunks */
+ uint32_t extra_chunk_data[4];
+ uint32_t extra_chunk_data_offs;
+ uint32_t extra_chunk_data_len;
+} AVI_TRACK_STREAM_STATE_T;
+
+typedef struct AVI_TRACK_CHUNK_STATE_T
+{
+ uint64_t index;
+ uint64_t offs; /**< offset into bytestream consisting of all chunks for this track */
+ int64_t time_pos; /**< pts of chunk (if known) */
+ uint32_t flags; /**< flags associated with chunk */
+ AVI_TRACK_STREAM_STATE_T local_state;
+ AVI_TRACK_STREAM_STATE_T *state;
+} AVI_TRACK_CHUNK_STATE_T;
+
+typedef struct VC_CONTAINER_TRACK_MODULE_T
+{
+ int64_t time_start; /**< i.e. 'dwStart' in 'strh' (converted to microseconds) */
+ int64_t duration; /**< i.e. 'dwLength' in 'strh' (converted to microseconds) */
+ uint32_t time_num; /**< i.e. 'dwScale' in 'strh' */
+ uint32_t time_den; /**< i.e. 'dwRate' in 'strh', time_num / time_den =
+ samples (or frames) / second for audio (or video) */
+ uint32_t sample_size; /**< i.e. 'dwSampleSize' in 'strh' */
+
+ uint64_t index_offset; /**< Offset to the start of an OpenDML index i.e. 'indx'
+ (if available) */
+ uint32_t index_size; /**< Size of the OpenDML index chunk */
+ AVI_TRACK_CHUNK_STATE_T chunk;
+} VC_CONTAINER_TRACK_MODULE_T;
+
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *tracks[AVI_TRACKS_MAX];
+ uint64_t data_offset; /**< Offset to the start of data packets i.e.
+ the data in the 'movi' list */
+ uint64_t data_size; /**< Size of the chunk containing data packets */
+ uint64_t index_offset; /**< Offset to the start of index data e.g.
+ the data in a 'idx1' list */
+ uint32_t index_size; /**< Size of the chunk containing index data */
+ AVI_TRACK_STREAM_STATE_T state;
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+static VC_CONTAINER_STATUS_T avi_find_chunk(VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T id, uint32_t *size)
+{
+ VC_CONTAINER_STATUS_T status;
+ VC_CONTAINER_FOURCC_T chunk_id;
+ uint32_t chunk_size;
+
+ do {
+ chunk_id = READ_FOURCC(p_ctx, "Chunk ID");
+ chunk_size = READ_U32(p_ctx, "Chunk size");
+ if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
+
+ if(chunk_id == id)
+ {
+ *size = chunk_size;
+ return VC_CONTAINER_SUCCESS;
+ }
+ /* Not interested in this chunk, skip it. */
+ AVI_SKIP_CHUNK(p_ctx, chunk_size);
+ } while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS);
+
+ return status; /* chunk not found */
+}
+
+static VC_CONTAINER_STATUS_T avi_find_list(VC_CONTAINER_T *p_ctx, VC_CONTAINER_FOURCC_T fourcc, uint32_t *size)
+{
+ VC_CONTAINER_STATUS_T status;
+ VC_CONTAINER_FOURCC_T chunk_id;
+ uint32_t chunk_size;
+ uint32_t peek_buf[1];
+
+ do {
+ chunk_id = READ_FOURCC(p_ctx, "Chunk ID");
+ chunk_size = READ_U32(p_ctx, "Chunk size");
+ if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
+
+ if(chunk_id == VC_FOURCC('L','I','S','T'))
+ {
+ if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if (peek_buf[0] == fourcc)
+ {
+ *size = chunk_size;
+ return VC_CONTAINER_SUCCESS;
+ }
+ }
+ /* Not interested in this chunk, skip it. */
+ AVI_SKIP_CHUNK(p_ctx, chunk_size);
+ } while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS);
+
+ return status; /* list not found */
+}
+
+static int64_t avi_stream_ticks_to_us(VC_CONTAINER_TRACK_MODULE_T *track_module, uint64_t ticks)
+{
+ int64_t time;
+ vc_container_assert(track_module->time_den != 0);
+ time = INT64_C(1000000) * track_module->time_num * ticks / track_module->time_den;
+ return time;
+}
+
+static int64_t avi_calculate_chunk_time(VC_CONTAINER_TRACK_MODULE_T *track_module)
+{
+ if (track_module->sample_size == 0)
+ return track_module->time_start + avi_stream_ticks_to_us(track_module, track_module->chunk.index);
+ else
+ return track_module->time_start + avi_stream_ticks_to_us(track_module,
+ ((track_module->chunk.offs + (track_module->sample_size >> 1)) / track_module->sample_size));
+}
+
+static VC_CONTAINER_STATUS_T avi_read_stream_header_list(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track,
+ VC_CONTAINER_TRACK_MODULE_T *track_module)
+{
+ VC_CONTAINER_STATUS_T status;
+ int64_t list_offset;
+ uint32_t list_size, chunk_id, chunk_size;
+
+ int stream_header_chunk_read = 0, stream_format_chunk_read = 0;
+
+ /* Look for a 'strl' LIST (sub)chunk */
+ status = avi_find_list(p_ctx, VC_FOURCC('s','t','r','l'), &chunk_size);
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "'strl' LIST not found for stream");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ list_offset = STREAM_POSITION(p_ctx);
+ list_size = chunk_size;
+ SKIP_FOURCC(p_ctx, "strl");
+
+ while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS && STREAM_POSITION(p_ctx) < list_offset + list_size)
+ {
+ int64_t offset = STREAM_POSITION(p_ctx);
+ chunk_id = READ_FOURCC(p_ctx, "Chunk ID");
+ chunk_size = READ_U32(p_ctx, "Chunk size");
+ LOG_FORMAT(p_ctx, "chunk %4.4s, offset: %"PRIi64", size: %i", (char *)&chunk_id, offset, chunk_size);
+
+ if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
+
+ if(chunk_id == VC_FOURCC('s','t','r','h'))
+ {
+ VC_CONTAINER_FOURCC_T fourcc_type, fourcc_handler;
+ uint32_t flags, scale, rate, div, start, length, sample_size;
+
+ /* We won't accept more than one 'strh' per stream */
+ if (stream_header_chunk_read)
+ {
+ LOG_DEBUG(p_ctx, "rejecting invalid 'strl', found more than one 'strh'");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ fourcc_type = READ_FOURCC(p_ctx, "fccType");
+ fourcc_handler = READ_FOURCC(p_ctx, "fccHandler");
+ flags = READ_U32(p_ctx, "dwFlags");
+ SKIP_U16(p_ctx, "wPriority");
+ SKIP_U16(p_ctx, "wLanguage");
+ SKIP_U32(p_ctx, "dwInitialFrames");
+ scale = READ_U32(p_ctx, "dwScale");
+ rate = READ_U32(p_ctx, "dwRate");
+ start = READ_U32(p_ctx, "dwStart");
+ length = READ_U32(p_ctx, "dwLength");
+ SKIP_U32(p_ctx, "dwSuggestedBufferSize");
+ SKIP_U32(p_ctx, "dwQuality");
+ sample_size = READ_U32(p_ctx, "dwSampleSize");
+ SKIP_U16(p_ctx, "rcFrame.left");
+ SKIP_U16(p_ctx, "rcFrame.top");
+ SKIP_U16(p_ctx, "rcFrame.right");
+ SKIP_U16(p_ctx, "rcFrame.bottom");
+
+ /* In AVI, sec/frame = scale/rate and frames/sec = rate/scale */
+ if (rate == 0)
+ {
+ LOG_DEBUG(p_ctx, "invalid dwRate: 0, using 1 as a guess");
+ LOG_DEBUG(p_ctx, "timestamps will almost certainly be wrong");
+ rate = 1;
+ }
+
+ div = vc_container_maths_gcd((int64_t)scale, (int64_t)rate);
+ scale = scale / div;
+ rate = rate / div;
+
+ track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+ if(fourcc_type == VC_FOURCC('v','i','d','s'))
+ {
+ track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO;
+ track->format->type->video.frame_rate_num = rate;
+ track->format->type->video.frame_rate_den = scale;
+
+ if (sample_size != 0)
+ {
+ LOG_DEBUG(p_ctx, "ignoring dwSampleSize (%d) for video stream", sample_size);
+ sample_size = 0;
+ }
+ }
+ else if(fourcc_type == VC_FOURCC('a','u','d','s'))
+ {
+ track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO;
+ /* VBR audio is going to be non-framed */
+ track->format->flags &= ~VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+ }
+ else if(fourcc_type == VC_FOURCC('t','x','t','s'))
+ track->format->es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE;
+
+ /* Don't overwrite any existing value (i.e. in the unlikely case where we
+ see 'strf' before 'strh') */
+ if(!track->format->codec) track->format->codec = vfw_fourcc_to_codec(fourcc_handler);
+
+ /* FIXME: enable this once read_media does the right thing */
+ if (!(flags & AVISF_DISABLED) || 1)
+ track->is_enabled = 1;
+
+ track_module->time_num = scale;
+ track_module->time_den = rate;
+ track_module->time_start = avi_stream_ticks_to_us(track_module, (uint64_t)start);
+ track_module->duration = avi_stream_ticks_to_us(track_module, (uint64_t)length);
+ track_module->sample_size = sample_size;
+
+ p_ctx->duration = MAX(p_ctx->duration, track_module->duration);
+
+ stream_header_chunk_read = 1;
+ }
+ else if(chunk_id == VC_FOURCC('s','t','r','f'))
+ {
+ uint8_t *buffer;
+ unsigned extra_offset = 0, extra_size = 0;
+
+ /* We won't accept more than one 'strf' per stream */
+ if (stream_format_chunk_read)
+ {
+ LOG_DEBUG(p_ctx, "rejecting invalid 'strl', found more than one 'strf'");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ /* Use the extradata buffer for reading in the entire 'strf' (should not be a large chunk) */
+ if ((status = vc_container_track_allocate_extradata(p_ctx, track, chunk_size)) != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "failed to allocate memory for 'strf' (%d bytes)", chunk_size);
+ return status;
+ }
+
+ buffer = track->priv->extradata;
+ if(READ_BYTES(p_ctx, buffer, chunk_size) != chunk_size)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ AVI_SYNC_CHUNK(p_ctx);
+
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ {
+ status = vc_container_bitmapinfoheader_to_es_format(buffer, chunk_size, &extra_offset, &extra_size, track->format);
+ }
+ else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
+ {
+ status = vc_container_waveformatex_to_es_format(buffer, chunk_size, &extra_offset, &extra_size, track->format);
+ if (track_module->sample_size != 0 && track_module->sample_size != track->format->type->audio.block_align)
+ {
+ LOG_DEBUG(p_ctx, "invalid dwSampleSize (%d), should match nBlockAlign (%d) for audio streams.",
+ track_module->sample_size, track->format->type->audio.block_align);
+
+ /* Note that if nBlockAlign really is 0, strf is seriously broken... */
+ if (track->format->type->audio.block_align != 0)
+ track_module->sample_size = track->format->type->audio.block_align;
+ }
+ else
+ {
+ /* Flawed muxers might only set nBlockAlign (i.e. not set dwSampleSize correctly). */
+ if (track->format->type->audio.block_align == 1)
+ track_module->sample_size = 1;
+ }
+ }
+
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ if (extra_size)
+ {
+ track->format->extradata = buffer + extra_offset;
+ track->format->extradata_size = extra_size;
+ }
+
+ /* Codec specific fix-up */
+ if (track->format->codec == VC_CONTAINER_CODEC_MP4A &&
+ track->format->extradata_size)
+ {
+ /* This is going to be raw AAC so it will be framed */
+ track_module->sample_size = 0;
+ track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+ }
+
+ /* WMA specific fix-up */
+ if ((track->format->codec == VC_CONTAINER_CODEC_WMA1 ||
+ track->format->codec == VC_CONTAINER_CODEC_WMA2 ||
+ track->format->codec == VC_CONTAINER_CODEC_WMAP ||
+ track->format->codec == VC_CONTAINER_CODEC_WMAL ||
+ track->format->codec == VC_CONTAINER_CODEC_WMAV) &&
+ track->format->extradata_size)
+ {
+ track_module->sample_size = 0;
+ track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+ }
+
+ stream_format_chunk_read = 1;
+ }
+ else if(chunk_id == VC_FOURCC('s','t','r','d'))
+ {
+ /* The data in a 'strd' chunk is either codec configuration data or DRM information,
+ we can safely assume it might be either as long as we don't overwrite any config
+ data read previously from a 'strf' chunk */
+ if ((status = vc_container_track_allocate_drmdata(p_ctx, track, chunk_size)) != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "failed to allocate memory for 'strd' (%d bytes)", chunk_size);
+ return status;
+ }
+
+ if(READ_BYTES(p_ctx, track->priv->drmdata, chunk_size) != chunk_size)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ AVI_SYNC_CHUNK(p_ctx);
+
+ if (!track->format->extradata)
+ {
+ if (vc_container_track_allocate_extradata(p_ctx, track, chunk_size) == VC_CONTAINER_SUCCESS)
+ {
+ memcpy(track->format->extradata, track->priv->drmdata, chunk_size);
+
+ track->format->extradata = track->priv->extradata;
+ track->format->extradata_size = chunk_size;
+ }
+ else
+ {
+ LOG_DEBUG(p_ctx, "failed to allocate memory for 'strd' (%d bytes)", chunk_size);
+ LOG_DEBUG(p_ctx, "no codec configuration data set");
+ }
+ }
+ }
+ else if(chunk_id == VC_FOURCC('i','n','d','x'))
+ {
+ track_module->index_offset = STREAM_POSITION(p_ctx);
+ track_module->index_size = chunk_size;
+ }
+ else
+ {
+ /* Not interested in this chunk, skip it. */
+ }
+
+ /* Skip any left-over data */
+ AVI_SKIP_CHUNK(p_ctx, offset + chunk_size + 8 - STREAM_POSITION(p_ctx) );
+ }
+
+ if (!stream_header_chunk_read || !stream_format_chunk_read)
+ {
+ LOG_DEBUG(p_ctx, "invalid 'strl', 'strh' and 'strf' are both required");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T avi_find_next_data_chunk(VC_CONTAINER_T *p_ctx, uint32_t *id, uint32_t *size)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_FOURCC_T chunk_id;
+ uint32_t chunk_size = 0;
+ uint32_t peek_buf[1];
+
+ do
+ {
+ chunk_id = READ_FOURCC(p_ctx, "Chunk ID");
+ chunk_size = READ_U32(p_ctx, "Chunk size");
+ if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
+ break;
+
+ /* Check if this is a 'rec ' or a 'movi' LIST instead of a plain data chunk */
+ if(chunk_id == VC_FOURCC('L','I','S','T'))
+ {
+ if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4)
+ return VC_CONTAINER_ERROR_EOS;
+ if (peek_buf[0] == VC_FOURCC('r','e','c',' '))
+ SKIP_FOURCC(p_ctx, "rec ");
+ else if (peek_buf[0] == VC_FOURCC('m','o','v','i'))
+ SKIP_FOURCC(p_ctx, "movi");
+ else
+ AVI_SKIP_CHUNK(p_ctx, chunk_size); /* Not interested in this LIST chunk, skip it. */
+ continue;
+ }
+
+ /* Check if this is a 'AVIX' RIFF header instead of a data chunk */
+ if(chunk_id == VC_FOURCC('R','I','F','F'))
+ {
+ if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4)
+ return VC_CONTAINER_ERROR_EOS;
+ if (peek_buf[0] == VC_FOURCC('A','V','I','X'))
+ SKIP_FOURCC(p_ctx, "AVIX");
+ else
+ AVI_SKIP_CHUNK(p_ctx, chunk_size); /* Not interested in this RIFF header, skip it. */
+ continue;
+ }
+
+ /* We treat only db/dc/dd or wb chunks as data */
+ if((uint32_t)chunk_id >> 16 == AVI_TWOCC('d','c') ||
+ (uint32_t)chunk_id >> 16 == AVI_TWOCC('d','b') ||
+ (uint32_t)chunk_id >> 16 == AVI_TWOCC('d','d') ||
+ (uint32_t)chunk_id >> 16 == AVI_TWOCC('w','b'))
+ {
+ *id = chunk_id;
+ *size = chunk_size;
+ break;
+ }
+
+ /* Need to exit if a zero sized chunk encountered so we don't loop forever. */
+ if(chunk_size == 0 && chunk_id == 0) return VC_CONTAINER_ERROR_EOS;
+
+ /* Not interested in this chunk, skip it */
+ AVI_SKIP_CHUNK(p_ctx, chunk_size);
+ } while ((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS);
+
+ return status;
+}
+
+static void avi_track_from_chunk_id(VC_CONTAINER_FOURCC_T chunk_id, uint16_t *data_type, uint16_t *track_num)
+{
+ *track_num = (((uint8_t*)&chunk_id)[0] - 48) * 10 + ((uint8_t*)&chunk_id)[1] - 48;
+ *data_type = (uint32_t)chunk_id >> 16;
+}
+
+static VC_CONTAINER_STATUS_T avi_check_track(VC_CONTAINER_T *p_ctx, uint16_t data_type, uint16_t track_num)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ if (track_num < p_ctx->tracks_num)
+ {
+ if (data_type == AVI_TWOCC('w','b') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_AUDIO)
+ {
+ LOG_DEBUG(p_ctx, "suspicious track type ('wb'), track %d is not an audio track", track_num);
+ status = VC_CONTAINER_ERROR_FAILED;
+ }
+ if (data_type == AVI_TWOCC('d','b') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO)
+ {
+ LOG_DEBUG(p_ctx, "suspicious track type ('db'), track %d is not a video track", track_num);
+ status = VC_CONTAINER_ERROR_FAILED;
+ }
+ if (data_type == AVI_TWOCC('d','c') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO)
+ {
+ LOG_DEBUG(p_ctx, "suspicious track type ('dc'), track %d is not a video track", track_num);
+ status = VC_CONTAINER_ERROR_FAILED;
+ }
+ if (data_type == AVI_TWOCC('d','d') && p_ctx->tracks[track_num]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO)
+ {
+ LOG_DEBUG(p_ctx, "suspicious track type ('dd'), track %d is not a video track", track_num);
+ status = VC_CONTAINER_ERROR_FAILED;
+ }
+ }
+ else
+ {
+ LOG_DEBUG(p_ctx, "invalid track number %d (no more than %d tracks expected)",
+ track_num, p_ctx->tracks_num);
+ status = VC_CONTAINER_ERROR_FAILED;
+ }
+
+ return status;
+}
+
+static int avi_compare_seek_time(int64_t chunk_time, int64_t seek_time,
+ int chunk_is_keyframe, VC_CONTAINER_SEEK_FLAGS_T seek_flags)
+{
+ if (chunk_time == seek_time && chunk_is_keyframe && !(seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
+ return 0;
+
+ if (chunk_time > seek_time && chunk_is_keyframe && (seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
+ return 0;
+
+ if (chunk_time > seek_time && !(seek_flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
+ return 1; /* Chunk time is past seek time, caller should use the previous keyframe */
+
+ return -1;
+}
+
+static VC_CONTAINER_STATUS_T avi_scan_legacy_index_chunk(VC_CONTAINER_T *p_ctx, int seek_track_num,
+ int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ VC_CONTAINER_TRACK_MODULE_T *track_module;
+ AVI_TRACK_CHUNK_STATE_T selected_chunk;
+ int64_t base_offset = module->data_offset;
+ int64_t selected_chunk_offset = base_offset + 4;
+ int32_t extra_offset = 0;
+ int first_chunk_offset = 1;
+ uint64_t position;
+
+ SEEK(p_ctx, module->index_offset);
+ memset(&selected_chunk, 0, sizeof(selected_chunk));
+
+ while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS &&
+ (uint64_t)STREAM_POSITION(p_ctx) < module->index_offset + module->index_size)
+ {
+ VC_CONTAINER_FOURCC_T chunk_id;
+ uint16_t data_type, track_num;
+ uint32_t chunk_flags, offset, size;
+
+ chunk_id = READ_FOURCC(p_ctx, "Chunk ID");
+ chunk_flags = READ_U32(p_ctx, "dwFlags");
+ offset = READ_U32(p_ctx, "dwOffset");
+ size = READ_U32(p_ctx, "dwSize");
+
+ if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) break;
+
+ /* Although it's rare, the offsets might be given from the start of the file
+ instead of the data chunk, we have to handle both cases. */
+ if (first_chunk_offset)
+ {
+ if (offset > module->data_offset) base_offset = INT64_C(0);
+ selected_chunk_offset = base_offset + 4;
+ first_chunk_offset = 0;
+ }
+
+ avi_track_from_chunk_id(chunk_id, &data_type, &track_num);
+ LOG_DEBUG(p_ctx, "reading track %"PRIu16, track_num);
+
+ if (avi_check_track(p_ctx, data_type, track_num) != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "skipping index entry for track %d/%d", track_num, p_ctx->tracks_num);
+ continue;
+ }
+
+ track_module = p_ctx->tracks[track_num]->priv->module;
+
+ if (data_type == AVI_TWOCC('d','d'))
+ {
+ if (track_num == seek_track_num)
+ track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_ENCRYPTED;
+ extra_offset = -(size + 8);
+ }
+
+ /* If this entry does not affect timing, skip it */
+ if ((chunk_flags & (AVIIF_LIST | AVIIF_NOTIME)) || data_type == AVI_TWOCC('d','d'))
+ continue;
+
+ position = base_offset + offset + extra_offset;
+ extra_offset = INT64_C(0);
+
+ /* Check validity of position */
+ if (position <= module->data_offset /* || (*pos > module->data_offset + module->data_size*/)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ if (track_num == seek_track_num)
+ {
+ bool is_keyframe = true;
+ int res;
+
+ if (p_ctx->tracks[track_num]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ is_keyframe = chunk_flags & AVIIF_KEYFRAME;
+
+ if (is_keyframe)
+ track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME;
+ else
+ track_module->chunk.flags &= ~(VC_CONTAINER_PACKET_FLAG_KEYFRAME);
+
+ res = avi_compare_seek_time(track_module->chunk.time_pos, *time, is_keyframe, flags);
+ if (res > 0)
+ break; /* We've found the keyframe we wanted */
+
+ if (is_keyframe)
+ {
+ selected_chunk_offset = position;
+ selected_chunk = track_module->chunk;
+ }
+
+ if (res == 0)
+ break; /* We've found the keyframe we wanted */
+
+ track_module->chunk.index++;
+ track_module->chunk.offs += size;
+ track_module->chunk.time_pos = avi_calculate_chunk_time(track_module);
+
+ LOG_DEBUG(p_ctx, "index %"PRIu64", offs %"PRIu64", time %"PRIi64"us", track_module->chunk.index,
+ track_module->chunk.offs, track_module->chunk.time_pos);
+ }
+ }
+
+ if (status == VC_CONTAINER_SUCCESS ||
+ /* When seeking backwards, always return the last good position */
+ !(flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
+ {
+ *pos = selected_chunk_offset;
+ track_module = p_ctx->tracks[seek_track_num]->priv->module;
+ track_module->chunk.index = selected_chunk.index;
+ track_module->chunk.offs = selected_chunk.offs;
+ track_module->chunk.flags = selected_chunk.flags;
+ track_module->chunk.time_pos = selected_chunk.time_pos;
+ *time = track_module->chunk.time_pos;
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ return VC_CONTAINER_ERROR_NOT_FOUND;
+}
+
+static VC_CONTAINER_STATUS_T avi_scan_standard_index_chunk(VC_CONTAINER_T *p_ctx, uint64_t index_offset,
+ unsigned seek_track_num, int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = NULL;
+ VC_CONTAINER_FOURCC_T chunk_id;
+ uint32_t chunk_size;
+ uint16_t data_type, track_num;
+ uint8_t index_type, index_sub_type;
+ uint32_t entry, entry_count = 0;
+ uint16_t entry_size;
+ uint64_t base_offset = UINT64_C(0);
+ uint64_t position = UINT64_C(0);
+ uint64_t prev_keyframe_offs = INT64_C(0);
+ AVI_TRACK_CHUNK_STATE_T prev_keyframe_chunk = { 0 };
+
+ SEEK(p_ctx, index_offset);
+
+ chunk_id = READ_FOURCC(p_ctx, "Chunk ID");
+ chunk_size = READ_U32(p_ctx, "Chunk Size");
+
+ entry_size = READ_U16(p_ctx, "wLongsPerEntry");
+ index_sub_type = READ_U8(p_ctx, "bIndexSubType");
+ index_type = READ_U8(p_ctx, "bIndexType");
+ entry_count = READ_U32(p_ctx, "nEntriesInUse");
+ chunk_id = READ_FOURCC(p_ctx, "dwChunkId");
+ base_offset = READ_U64(p_ctx, "qwBaseOffset");
+ SKIP_U32(p_ctx, "dwReserved");
+
+ if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
+ return status;
+
+ avi_track_from_chunk_id(chunk_id, &data_type, &track_num);
+ status = avi_check_track(p_ctx, data_type, track_num);
+ if (status || chunk_size < 24 || track_num != seek_track_num)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ if (entry_size != 2 || index_sub_type != 0 || index_type != AVI_INDEX_OF_CHUNKS)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ entry_count = MIN(entry_count, (chunk_size - 24) / (entry_size * 4));
+
+ track_module = p_ctx->tracks[seek_track_num]->priv->module;
+
+ for (entry = 0; entry < entry_count; ++entry)
+ {
+ uint32_t chunk_offset;
+ int key_frame = 0;
+
+ chunk_offset = READ_U32(p_ctx, "dwOffset");
+ chunk_size = READ_U32(p_ctx, "dwSize");
+
+ if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
+ break;
+
+ status = VC_CONTAINER_ERROR_NOT_FOUND;
+
+ if (!(chunk_size & AVI_INDEX_DELTAFRAME))
+ key_frame = 1;
+ chunk_size &= ~AVI_INDEX_DELTAFRAME;
+
+ position = base_offset + chunk_offset - 8;
+
+ if (key_frame)
+ track_module->chunk.flags = VC_CONTAINER_PACKET_FLAG_KEYFRAME;
+ else
+ track_module->chunk.flags = 0;
+
+ if (time != NULL)
+ {
+ int res;
+ status = VC_CONTAINER_ERROR_NOT_FOUND;
+ res = avi_compare_seek_time(track_module->chunk.time_pos, *time, key_frame, flags);
+
+ if (res == 0)
+ {
+ *pos = position;
+ *time = track_module->chunk.time_pos;
+ status = VC_CONTAINER_SUCCESS;
+ break;
+ }
+ else if (res > 0)
+ {
+ if (prev_keyframe_offs)
+ {
+ *pos = prev_keyframe_offs;
+ track_module->chunk = prev_keyframe_chunk;
+ *time = track_module->chunk.time_pos;
+ status = VC_CONTAINER_SUCCESS;
+ }
+ break;
+ }
+
+ if (key_frame)
+ {
+ prev_keyframe_offs = position;
+ prev_keyframe_chunk = track_module->chunk;
+ }
+ }
+ else
+ {
+ /* Not seeking to a time position, but scanning
+ track chunk state up to a certain file position
+ instead */
+ if (position >= *pos)
+ {
+ status = VC_CONTAINER_SUCCESS;
+ break;
+ }
+ }
+
+ track_module->chunk.index++;
+ track_module->chunk.offs += chunk_size;
+ track_module->chunk.time_pos = avi_calculate_chunk_time(track_module);
+ }
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T avi_scan_super_index_chunk(VC_CONTAINER_T *p_ctx, unsigned index_track_num,
+ int64_t *time, VC_CONTAINER_SEEK_FLAGS_T flags, uint64_t *pos)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND;
+ VC_CONTAINER_FOURCC_T chunk_id;
+ uint64_t index_offset;
+ uint32_t index_size;
+ uint16_t data_type, track_num;
+ uint32_t entry, entry_count;
+ uint16_t entry_size;
+ uint8_t index_sub_type, index_type;
+
+ index_offset = p_ctx->tracks[index_track_num]->priv->module->index_offset;
+ index_size = p_ctx->tracks[index_track_num]->priv->module->index_size;
+
+ SEEK(p_ctx, index_offset);
+
+ entry_size = READ_U16(p_ctx, "wLongsPerEntry");
+ index_sub_type = READ_U8(p_ctx, "bIndexSubType");
+ index_type = READ_U8(p_ctx, "bIndexType");
+ entry_count = READ_U32(p_ctx, "nEntriesInUse");
+ chunk_id = READ_FOURCC(p_ctx, "dwChunkId");
+ SKIP_U32(p_ctx, "dwReserved0");
+ SKIP_U32(p_ctx, "dwReserved1");
+ SKIP_U32(p_ctx, "dwReserved2");
+
+ if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
+ return status;
+
+ if (index_type == AVI_INDEX_OF_INDEXES)
+ {
+ avi_track_from_chunk_id(chunk_id, &data_type, &track_num);
+ status = avi_check_track(p_ctx, data_type, track_num);
+ if (status || index_size < 24 || track_num != index_track_num) return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ /* FIXME: We should probably support AVI_INDEX_2FIELD as well */
+ if (entry_size != 4 || index_sub_type != 0)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ entry_count = MIN(entry_count, (index_size - 24) / entry_size);
+
+ for (entry = 0; entry < entry_count; ++entry)
+ {
+ uint64_t entry_offset, standard_index_offset;
+ standard_index_offset = READ_U64(p_ctx, "qwOffset");
+ SKIP_U32(p_ctx, "dwSize");
+ SKIP_U32(p_ctx, "dwDuration");
+
+ if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
+ break;
+
+ if (standard_index_offset == UINT64_C(0))
+ {
+ status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* Not plausible */
+ break;
+ }
+
+ entry_offset = STREAM_POSITION(p_ctx);
+ status = avi_scan_standard_index_chunk(p_ctx, standard_index_offset, index_track_num, time, flags, pos);
+ if (status != VC_CONTAINER_ERROR_NOT_FOUND) break;
+ SEEK(p_ctx, entry_offset); /* Move to next entry ('ix' chunk); */
+ }
+ }
+ else if (index_type == AVI_INDEX_OF_CHUNKS)
+ {
+ /* It seems we are dealing with a standard index instead... */
+ status = avi_scan_standard_index_chunk(p_ctx, index_offset, index_track_num, time, flags, pos);
+ }
+ else
+ {
+ status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T avi_read_dd_chunk( VC_CONTAINER_T *p_ctx,
+ AVI_TRACK_STREAM_STATE_T *p_state, uint16_t data_type, uint32_t chunk_size,
+ uint16_t track_num )
+{
+ if (data_type == AVI_TWOCC('d','d'))
+ {
+ if (p_state->extra_chunk_data_len ||
+ chunk_size > sizeof(p_state->extra_chunk_data))
+ {
+ LOG_DEBUG(p_ctx, "cannot handle multiple consecutive 'dd' chunks");
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+ if(READ_BYTES(p_ctx, p_state->extra_chunk_data, chunk_size) != chunk_size)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ AVI_SYNC_CHUNK(p_ctx);
+ p_state->extra_chunk_track_num = track_num;
+ p_state->extra_chunk_data_len = chunk_size;
+ p_state->extra_chunk_data_offs = 0;
+
+ return VC_CONTAINER_ERROR_CONTINUE;
+ }
+ else if (p_state->extra_chunk_data_len &&
+ p_state->extra_chunk_track_num != track_num)
+ {
+ LOG_DEBUG(p_ctx, "dropping data from '%02ddd' chunk, not for this track (%d)",
+ p_state->extra_chunk_track_num, track_num);
+ p_state->extra_chunk_data_len = 0;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+ *****************************************************************************/
+
+static VC_CONTAINER_STATUS_T avi_reader_read( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *p_packet, uint32_t flags )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = NULL;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ AVI_TRACK_STREAM_STATE_T *p_state = &module->state;
+
+ if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK)
+ {
+ p_state = p_ctx->tracks[p_packet->track]->priv->module->chunk.state;
+ }
+
+ LOG_DEBUG(p_ctx, "seeking to %"PRIi64, p_state->data_offset);
+ SEEK(p_ctx, p_state->data_offset);
+
+ if (p_state->chunk_data_left == 0)
+ {
+ VC_CONTAINER_FOURCC_T chunk_id;
+ uint32_t chunk_size;
+ uint16_t data_type, track_num;
+
+ if ((status = avi_find_next_data_chunk(p_ctx, &chunk_id, &chunk_size)) != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "unable to find the next data chunk %d", status);
+ p_state->data_offset = STREAM_POSITION(p_ctx);
+ return status;
+ }
+
+ avi_track_from_chunk_id(chunk_id, &data_type, &track_num);
+
+ if (avi_check_track(p_ctx, data_type, track_num) != VC_CONTAINER_SUCCESS)
+ {
+ AVI_SKIP_CHUNK(p_ctx, chunk_size);
+ LOG_DEBUG(p_ctx, "skipping data for track %d/%d", track_num, p_ctx->tracks_num);
+
+ p_state->data_offset = STREAM_POSITION(p_ctx);
+ return VC_CONTAINER_ERROR_CONTINUE;
+ }
+
+ /* If we are reading from the global state (i.e. normal read or forced
+ read from the track on the global state), and the track we found is
+ not on the global state, connect the two */
+ if (p_state == &module->state &&
+ p_ctx->tracks[track_num]->priv->module->chunk.state != &module->state)
+ {
+ int64_t next_chunk;
+
+ /* The track's offset is past the current position, skip it as we are
+ not interested in track data from before the track's offset. If we
+ were to read it we would return the same data multiple times. */
+ next_chunk = (STREAM_POSITION(p_ctx) + chunk_size + 1) & ~1;
+ if (p_ctx->tracks[track_num]->priv->module->chunk.state->data_offset > next_chunk)
+ {
+ AVI_SKIP_CHUNK(p_ctx, chunk_size);
+ LOG_DEBUG(p_ctx, "skipping track %d/%d as we have already read it", track_num, p_ctx->tracks_num);
+ p_state->data_offset = STREAM_POSITION(p_ctx);
+ return VC_CONTAINER_ERROR_CONTINUE;
+ }
+
+ /* The track state must be pointing to the current chunk. We need to
+ reconnect the track to the global state. */
+ LOG_DEBUG(p_ctx, "reconnect track %u to the global state", track_num);
+
+ p_ctx->tracks[track_num]->priv->module->chunk.state = &module->state;
+
+ module->state = p_ctx->tracks[track_num]->priv->module->chunk.local_state;
+
+ vc_container_assert(chunk_size >= p_state->chunk_data_left);
+ vc_container_assert(!p_state->chunk_data_left ||
+ ((p_state->data_offset + p_state->chunk_data_left + 1) & ~1) == next_chunk);
+ vc_container_assert(p_state->current_track_num == track_num);
+
+ return VC_CONTAINER_ERROR_CONTINUE;
+ }
+
+ /* If we are not forcing, or if we are and found the track we are
+ interested in, check for dd data and set the track module for the later code */
+ if (!(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) ||
+ (track_num == p_packet->track))
+ {
+ if ((status = avi_read_dd_chunk(p_ctx, p_state, data_type, chunk_size, track_num)) != VC_CONTAINER_SUCCESS)
+ {
+ p_state->data_offset = STREAM_POSITION(p_ctx);
+ return status;
+ }
+ }
+
+ p_state->chunk_size = p_state->chunk_data_left = chunk_size;
+ p_state->current_track_num = track_num;
+ }
+
+ /* If there is data from another track skip past it */
+ if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK &&
+ p_state->current_track_num != p_packet->track)
+ {
+ p_state->data_offset = STREAM_POSITION(p_ctx);
+
+ AVI_SKIP_CHUNK(p_ctx, p_state->chunk_data_left);
+ LOG_DEBUG(p_ctx, "skipping track %d/%d as we are ignoring it",
+ p_state->current_track_num, p_ctx->tracks_num);
+
+ track_module = p_ctx->tracks[p_packet->track]->priv->module;
+
+ /* Handle disconnection from global state */
+ if (p_state == &module->state &&
+ p_ctx->tracks[p_state->current_track_num]->priv->module->chunk.state == &module->state)
+ {
+ /* Make a copy of the global state */
+ LOG_DEBUG(p_ctx, "using local state on track %d", p_packet->track);
+ track_module->chunk.local_state = module->state;
+ track_module->chunk.state = &track_module->chunk.local_state;
+ }
+
+ track_module->chunk.state->data_offset = STREAM_POSITION(p_ctx);
+ track_module->chunk.state->chunk_data_left = 0;
+
+ return VC_CONTAINER_ERROR_CONTINUE;
+ }
+
+ track_module = p_ctx->tracks[p_state->current_track_num]->priv->module;
+
+ if (flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK)
+ {
+ vc_container_assert(p_state->current_track_num == p_packet->track);
+ }
+
+ LOG_DEBUG(p_ctx, "reading track %u chunk at time %"PRIi64"us, offset %"PRIu64,
+ p_state->current_track_num, track_module->chunk.time_pos,
+ track_module->chunk.offs);
+ if (p_state->extra_chunk_data_len)
+ track_module->chunk.flags |= VC_CONTAINER_PACKET_FLAG_ENCRYPTED;
+ else
+ track_module->chunk.flags &= ~VC_CONTAINER_PACKET_FLAG_ENCRYPTED;
+
+ if (p_packet)
+ {
+ p_packet->track = p_state->current_track_num;
+ p_packet->size = p_state->chunk_data_left +
+ p_state->extra_chunk_data_len;
+ p_packet->flags = track_module->chunk.flags;
+
+ if (p_state->chunk_data_left == p_state->chunk_size)
+ {
+ p_packet->pts = track_module->chunk.time_pos;
+ if (track_module->sample_size == 0)
+ p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME;
+ }
+ else
+ {
+ p_packet->pts = VC_CONTAINER_TIME_UNKNOWN;
+ if (track_module->sample_size == 0)
+ p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
+ }
+
+ p_packet->dts = VC_CONTAINER_TIME_UNKNOWN;
+ }
+
+ if (flags & VC_CONTAINER_READ_FLAG_SKIP)
+ {
+ SKIP_BYTES(p_ctx, p_state->chunk_data_left);
+ AVI_SYNC_CHUNK(p_ctx);
+ p_state->chunk_data_left = 0;
+ p_state->extra_chunk_data_len = 0;
+ }
+
+ if (flags & VC_CONTAINER_READ_FLAG_INFO)
+ {
+ p_state->data_offset = STREAM_POSITION(p_ctx);
+
+ LOG_DEBUG(p_ctx, "data position %"PRIi64, p_state->data_offset);
+
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ if (p_packet)
+ {
+ uint8_t *data = p_packet->data;
+ uint32_t buffer_size = p_packet->buffer_size;
+ uint32_t size = 0;
+ uint32_t len;
+
+ /* See if we need to insert extra data */
+ if (p_state->extra_chunk_data_len)
+ {
+ len = MIN(buffer_size, p_state->extra_chunk_data_len);
+ memcpy(data, p_state->extra_chunk_data + p_state->extra_chunk_data_offs, len);
+ data += len;
+ buffer_size -= len;
+ size = len;
+ p_state->extra_chunk_data_len -= len;
+ p_state->extra_chunk_data_offs += len;
+ }
+
+ /* Now try to read data into buffer */
+ len = MIN(buffer_size, p_state->chunk_data_left);
+ READ_BYTES(p_ctx, data, len);
+ size += len;
+ p_state->chunk_data_left -= len;
+ p_packet->size = size;
+
+ if (p_state->chunk_data_left)
+ p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END;
+ }
+
+ if (p_state->chunk_data_left == 0)
+ {
+ AVI_SYNC_CHUNK(p_ctx);
+ track_module->chunk.index++;
+ track_module->chunk.offs += p_state->chunk_size;
+ track_module->chunk.flags = 0;
+ track_module->chunk.time_pos = avi_calculate_chunk_time(track_module);
+ }
+
+ /* Update the track's position */
+ p_state->data_offset = STREAM_POSITION(p_ctx);
+
+ LOG_DEBUG(p_ctx, "data position %"PRIi64, p_state->data_offset);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset,
+ VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ uint64_t position, pos;
+ AVI_TRACK_CHUNK_STATE_T chunk_state[AVI_TRACKS_MAX];
+ AVI_TRACK_STREAM_STATE_T global_state;
+ unsigned seek_track_num, i;
+
+ if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(p_ctx))
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ LOG_DEBUG(p_ctx, "AVI seeking to %"PRIi64"us", *p_offset);
+
+ /* Save current position and chunk state so we can restore it if we
+ hit an error whilst scanning index data */
+ position = STREAM_POSITION(p_ctx);
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ chunk_state[i] = p_ctx->tracks[i]->priv->module->chunk;
+ global_state = p_ctx->priv->module->state;
+
+ /* Clear track state affected by a seek operation of any kind */
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ p_ctx->tracks[i]->priv->module->chunk.index = INT64_C(0);
+ p_ctx->tracks[i]->priv->module->chunk.offs = INT64_C(0);
+ p_ctx->tracks[i]->priv->module->chunk.flags = 0;
+ p_ctx->tracks[i]->priv->module->chunk.time_pos = p_ctx->tracks[i]->priv->module->time_start;
+ p_ctx->tracks[i]->priv->module->chunk.state = &p_ctx->tracks[i]->priv->module->chunk.local_state;
+ p_ctx->tracks[i]->priv->module->chunk.local_state.chunk_data_left = UINT64_C(0);
+ p_ctx->tracks[i]->priv->module->chunk.local_state.chunk_size = UINT64_C(0);
+ p_ctx->tracks[i]->priv->module->chunk.local_state.extra_chunk_data_len = 0;
+ p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = module->data_offset + 4;
+ }
+
+ /* Clear the global state */
+ p_ctx->priv->module->state.chunk_data_left = UINT64_C(0);
+ p_ctx->priv->module->state.chunk_size = UINT64_C(0);
+ p_ctx->priv->module->state.extra_chunk_data_len = 0;
+ p_ctx->priv->module->state.data_offset = module->data_offset + 4;
+
+ /* Choose track to use for seeking, favor video tracks and tracks
+ that are enabled */
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ if(p_ctx->tracks[i]->is_enabled &&
+ p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break;
+ if(i == p_ctx->tracks_num)
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ if(p_ctx->tracks[i]->is_enabled) break;
+ if(i == p_ctx->tracks_num) i = 0;
+
+ LOG_DEBUG(p_ctx, "seek on track %d/%d", i, p_ctx->tracks_num);
+ seek_track_num = i;
+
+ if (p_ctx->tracks[seek_track_num]->priv->module->index_offset)
+ {
+ LOG_DEBUG(p_ctx, "seeking using the super index");
+ status = avi_scan_super_index_chunk(p_ctx, seek_track_num, p_offset, flags, &pos);
+ if (status != VC_CONTAINER_SUCCESS) goto error;
+
+ /* As AVI chunks don't convey timestamp information, we need to scan all tracks
+ to the seek file position */
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ if (p_ctx->tracks[i]->priv->module->index_offset && i != seek_track_num)
+ {
+ uint64_t track_pos;
+ int64_t track_time = *p_offset;
+
+ status = avi_scan_super_index_chunk(p_ctx, i, &track_time, flags, &track_pos);
+ if (status != VC_CONTAINER_SUCCESS) goto error;
+ p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = track_pos;
+ }
+ }
+ }
+ else
+ {
+ LOG_DEBUG(p_ctx, "seeking using the legacy index");
+
+ /* The legacy index comes after data so it might not have been available at the
+ time the container was opened; if this is the case, see if we can find an index
+ now, if we can't, then there's no way we can proceed with the seek. */
+ if(!module->index_offset)
+ {
+ uint32_t chunk_size;
+
+ LOG_DEBUG(p_ctx, "no index offset, searching for one");
+
+ /* Locate data chunk and skip it */
+ SEEK(p_ctx, module->data_offset);
+ AVI_SKIP_CHUNK(p_ctx, module->data_size);
+ /* Now search for the index */
+ status = avi_find_chunk(p_ctx, VC_FOURCC('i','d','x','1'), &chunk_size);
+ if (status == VC_CONTAINER_SUCCESS)
+ {
+ /* Store offset to index data */
+ module->index_offset = STREAM_POSITION(p_ctx);
+ module->index_size = chunk_size;
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX;
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG;
+ }
+ }
+ /* Check again, we may or may not have an index */
+ if (!module->index_offset)
+ {
+ /* If there is no index and we are seeking to 0 we can assume the
+ correct location is the start of the data. Otherwise we are unable
+ to seek to a specified non-zero location without an index */
+ if (*p_offset != INT64_C(0))
+ {
+ LOG_DEBUG(p_ctx, "failed to find the legacy index, unable to seek");
+ status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ goto error;
+ }
+ pos = module->data_offset;
+ }
+ else
+ {
+ LOG_DEBUG(p_ctx, "scanning the legacy index chunk");
+ status = avi_scan_legacy_index_chunk(p_ctx, seek_track_num, p_offset, flags, &pos);
+ if (status != VC_CONTAINER_SUCCESS) goto error;
+
+ for (i = 0; i < p_ctx->tracks_num; i++)
+ {
+ if (i != seek_track_num)
+ {
+ uint64_t track_pos = pos;
+ int64_t track_time = *p_offset;
+
+ status = avi_scan_legacy_index_chunk(p_ctx, i, &track_time, flags, &track_pos);
+ if (status != VC_CONTAINER_SUCCESS) goto error;
+ p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset = track_pos;
+ p_ctx->tracks[i]->priv->module->chunk.local_state.current_track_num = i;
+ }
+ }
+ }
+ }
+
+ position = pos;
+
+ /* Set the seek track's data offset */
+ p_ctx->tracks[seek_track_num]->priv->module->chunk.local_state.data_offset = position;
+ p_ctx->tracks[seek_track_num]->priv->module->chunk.local_state.current_track_num = seek_track_num;
+
+ /* Connect the earlier track(s) to the global state. Needs 2 passes */
+ module->state.data_offset = INT64_MAX;
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ if(p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset <
+ module->state.data_offset)
+ module->state = p_ctx->tracks[i]->priv->module->chunk.local_state;
+ }
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ if(p_ctx->tracks[i]->priv->module->chunk.local_state.data_offset ==
+ module->state.data_offset)
+ p_ctx->tracks[i]->priv->module->chunk.state = &module->state;
+ }
+
+ LOG_DEBUG(p_ctx, "seek to %"PRIi64", position %"PRIu64, *p_offset, pos);
+
+ return SEEK(p_ctx, position);
+
+error:
+ p_ctx->priv->module->state = global_state;
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ p_ctx->tracks[i]->priv->module->chunk = chunk_state[i];
+ SEEK(p_ctx, position);
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_reader_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int i;
+
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ vc_container_free_track(p_ctx, p_ctx->tracks[i]);
+ p_ctx->tracks = NULL;
+ p_ctx->tracks_num = 0;
+ free(module);
+ p_ctx->priv->module = 0;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = 0;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ uint32_t chunk_size;
+ uint32_t peek_buf[3];
+ unsigned int i;
+ uint32_t flags, num_streams;
+ int64_t offset;
+
+ /* Check the RIFF chunk descriptor */
+ if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 12) != 12)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if( peek_buf[0] != VC_FOURCC('R','I','F','F') )
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if( peek_buf[2] != VC_FOURCC('A','V','I',' ') )
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /*
+ * We now know we are dealing with an AVI file
+ */
+ SKIP_FOURCC(p_ctx, "RIFF ID");
+ SKIP_U32(p_ctx, "fileSize");
+ SKIP_FOURCC(p_ctx, "fileType");
+
+ /* Look for the 'hdrl' LIST (sub)chunk */
+ status = avi_find_list(p_ctx, VC_FOURCC('h','d','r','l'), &chunk_size);
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "'hdrl' LIST not found");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ SKIP_FOURCC(p_ctx, "hdrl");
+
+ /* Now look for the 'avih' sub-chunk */
+ status = avi_find_chunk(p_ctx, VC_FOURCC('a','v','i','h'), &chunk_size);
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "'avih' not found");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ /* Parse the 'avih' sub-chunk */
+ SKIP_U32(p_ctx, "dwMicroSecPerFrame");
+ SKIP_U32(p_ctx, "dwMaxBytesPerSec");
+ SKIP_U32(p_ctx, "dwPaddingGranularity");
+ flags = READ_U32(p_ctx, "dwFlags");
+ SKIP_U32(p_ctx, "dwTotalFrames");
+ SKIP_U32(p_ctx, "dwInitialFrames");
+ num_streams = READ_U32(p_ctx, "dwStreams");
+ SKIP_U32(p_ctx, "dwSuggestedBufferSize");
+ SKIP_U32(p_ctx, "dwWidth");
+ SKIP_U32(p_ctx, "dwHeight");
+ SKIP_U32(p_ctx, "dwReserved0");
+ SKIP_U32(p_ctx, "dwReserved1");
+ SKIP_U32(p_ctx, "dwReserved2");
+ SKIP_U32(p_ctx, "dwReserved3");
+
+ if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
+ goto error;
+
+ /* Allocate our context and tracks */
+ if ((module = malloc(sizeof(*module))) == NULL)
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks = module->tracks;
+
+ if (num_streams > AVI_TRACKS_MAX)
+ {
+ LOG_DEBUG(p_ctx, "cannot handle %u tracks, restricted to %d", num_streams, AVI_TRACKS_MAX);
+ num_streams = AVI_TRACKS_MAX;
+ }
+
+ for (p_ctx->tracks_num = 0; p_ctx->tracks_num != num_streams; p_ctx->tracks_num++)
+ {
+ p_ctx->tracks[p_ctx->tracks_num] = vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
+ if(!p_ctx->tracks[p_ctx->tracks_num]) break;
+ }
+ if(p_ctx->tracks_num != num_streams)
+ { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+
+ /* Try to read stream header list chunks ('strl') */
+ for (i = 0; i != num_streams; ++i)
+ {
+ status = avi_read_stream_header_list(p_ctx, p_ctx->tracks[i], p_ctx->tracks[i]->priv->module);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+ }
+
+ /* Look for the 'movi' LIST (sub)chunk */
+ status = avi_find_list(p_ctx, VC_FOURCC('m','o','v','i'), &chunk_size);
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "'movi' LIST not found");
+ status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ goto error;
+ }
+
+ /* Store offset to the start and size of data (the 'movi' LIST) */
+ module->data_offset = STREAM_POSITION(p_ctx);
+ module->data_size = chunk_size;
+
+ p_ctx->priv->pf_close = avi_reader_close;
+ p_ctx->priv->pf_read = avi_reader_read;
+ p_ctx->priv->pf_seek = avi_reader_seek;
+
+ if (flags & AVIF_MUSTUSEINDEX)
+ {
+ LOG_DEBUG(p_ctx, "AVIF_MUSTUSEINDEX not supported, playback might not work properly");
+ }
+
+ /* If the stream is seekable, see if we can find an index (for at
+ least one of the tracks); even if we cannot find an index now,
+ one might become available later (e.g. when the stream grows
+ run-time), in that case we might want to report that we can seek
+ and re-search for the index again if or when we're requested to
+ seek. */
+ if(STREAM_SEEKABLE(p_ctx))
+ {
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_FORCE_TRACK;
+
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ if(p_ctx->tracks[i]->priv->module->index_offset) break;
+
+ if (i < p_ctx->tracks_num)
+ {
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX;
+ if (flags & AVIF_TRUSTCKTYPE)
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG;
+ }
+ else
+ {
+ /* Skip data first */
+ AVI_SKIP_CHUNK(p_ctx, module->data_size);
+ /* Now search for the index */
+ status = avi_find_chunk(p_ctx, VC_FOURCC('i','d','x','1'), &chunk_size);
+ if (status == VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "'idx1' found");
+ /* Store offset to index data */
+ module->index_offset = STREAM_POSITION(p_ctx);
+ module->index_size = chunk_size;
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_HAS_INDEX;
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG;
+ }
+
+ /* Seek back to the start of the data */
+ SEEK(p_ctx, module->data_offset);
+ }
+ }
+
+ SKIP_FOURCC(p_ctx, "movi");
+
+ for (i = 0; i != num_streams; i++)
+ {
+ p_ctx->tracks[i]->priv->module->chunk.state = &p_ctx->priv->module->state;
+ }
+ p_ctx->priv->module->state.data_offset = STREAM_POSITION(p_ctx);
+
+ /* Update the tracks to set their data offsets. This help with bad
+ interleaving, for example when there is all the video tracks followed
+ by all the audio tracks. It means we don't have to read through the
+ tracks we are not interested in when forcing a read from a given track,
+ as could be the case in the above example. If this fails we will fall
+ back to skipping track data. */
+ offset = INT64_C(0);
+ avi_reader_seek(p_ctx, &offset, VC_CONTAINER_SEEK_MODE_TIME, VC_CONTAINER_SEEK_FLAG_PRECISE);
+
+ if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error;
+ return VC_CONTAINER_SUCCESS;
+
+error:
+ LOG_DEBUG(p_ctx, "error opening stream (%i)", status);
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ vc_container_free_track(p_ctx, p_ctx->tracks[i]);
+ p_ctx->tracks = NULL;
+ p_ctx->tracks_num = 0;
+ if (module) free(module);
+ p_ctx->priv->module = NULL;
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open avi_reader_open
+#endif
diff --git a/gfx/include/userland/containers/avi/avi_writer.c b/gfx/include/userland/containers/avi/avi_writer.c
new file mode 100644
index 0000000000..589ee13ec7
--- /dev/null
+++ b/gfx/include/userland/containers/avi/avi_writer.c
@@ -0,0 +1,1171 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+#include
+#include
+
+#define CONTAINER_IS_LITTLE_ENDIAN
+//#define ENABLE_CONTAINERS_LOG_FORMAT
+//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+#define CONTAINER_HELPER_LOG_INDENT(a) 0
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_writer_utils.h"
+#include "containers/core/containers_logging.h"
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx );
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+#define AVISF_DISABLED 0x00000001 /*< If set stream should not be enabled by default. */
+#define AVIF_HASINDEX 0x00000010
+#define AVIF_TRUSTCKTYPE 0x00000800
+#define AVIIF_KEYFRAME 0x00000010
+
+#define AVI_INDEX_OF_INDEXES 0x00
+#define AVI_INDEX_OF_CHUNKS 0x01
+#define AVI_INDEX_DELTAFRAME 0x80000000
+
+#define AVI_INDEX_ENTRY_SIZE 16
+#define AVI_SUPER_INDEX_ENTRY_SIZE 16
+#define AVI_STD_INDEX_ENTRY_SIZE 8
+#define AVI_FRAME_BUFFER_SIZE 100000
+
+#define AVI_TRACKS_MAX 3
+
+#define AVI_AUDIO_CHUNK_SIZE_LIMIT 16384 /*< Watermark limit for data chunks when 'dwSampleSize'
+ is non-zero */
+
+#define AVI_END_CHUNK(ctx) \
+ do { \
+ if(STREAM_POSITION(ctx) & 1) WRITE_U8(ctx, 0, "AVI_END_CHUNK"); \
+ } while(0)
+
+#define AVI_PACKET_KEYFRAME (VC_CONTAINER_PACKET_FLAG_KEYFRAME | VC_CONTAINER_PACKET_FLAG_FRAME_END)
+#define AVI_PACKET_IS_KEYFRAME(flags) (((flags) & AVI_PACKET_KEYFRAME) == AVI_PACKET_KEYFRAME)
+
+/******************************************************************************
+Type definitions.
+******************************************************************************/
+typedef struct VC_CONTAINER_TRACK_MODULE_T
+{
+ uint32_t chunk_index; /**< index of current chunk */
+ uint32_t chunk_offs; /**< current offset into bytestream consisting of all
+ chunks for this track */
+ uint32_t sample_size; /**< i.e. 'dwSampleSize' in 'strh' */
+ uint32_t max_chunk_size; /**< largest chunk written so far */
+ uint64_t index_offset; /**< Offset to the start of an OpenDML index for this track
+ i.e. 'indx' */
+ uint32_t index_size; /**< Size of the OpenDML index for this track i.e. 'indx' */
+} VC_CONTAINER_TRACK_MODULE_T;
+
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *tracks[AVI_TRACKS_MAX];
+ VC_CONTAINER_WRITER_EXTRAIO_T null_io; /**< Null I/O for calculating chunk sizes, etc. */
+ VC_CONTAINER_WRITER_EXTRAIO_T temp_io; /**< I/O for temporary storage of index data */
+ int headers_written;
+
+ uint32_t header_list_offset; /**< Offset to the header list chunk ('hdrl') */
+ uint32_t header_list_size; /**< Size of the header list chunk ('hdrl') */
+ uint32_t data_offset; /**< Offset to the start of data packets i.e.
+ the data in the AVI RIFF 'movi' list */
+ uint64_t data_size; /**< Size of the chunk containing data packets */
+ uint32_t index_offset; /**< Offset to the start of index data e.g.
+ the data in an 'idx1' list */
+ unsigned current_track_num; /**< Number of track currently being written */
+ uint32_t chunk_size; /**< Final size of the current chunk being written (if known) */
+ uint32_t chunk_data_written; /**< Data written to the current chunk so far */
+ uint8_t *avi_frame_buffer; /**< For accumulating whole frames when seeking isn't available. */
+ VC_CONTAINER_PACKET_T frame_packet; /**< Packet header for whole frame. */
+
+ VC_CONTAINER_STATUS_T index_status;
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+static void avi_chunk_id_from_track_num( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_FOURCC_T *p_chunk_id, unsigned int track_num )
+{
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[track_num];
+ VC_CONTAINER_FOURCC_T chunk_id = 0;
+ char track_num_buf[3];
+
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ chunk_id = VC_FOURCC('0','0','d','c');
+ else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
+ chunk_id = VC_FOURCC('0','0','w','b');
+ else
+ {
+ /* Note that avi_writer_add_track should ensure this
+ can't happen */
+ *p_chunk_id = VC_FOURCC('J','U','N','K'); return;
+ }
+
+ snprintf(track_num_buf, sizeof(track_num_buf), "%02d", track_num);
+ memcpy(&chunk_id, track_num_buf, 2);
+
+ *p_chunk_id = chunk_id;
+}
+
+/*****************************************************************************/
+static void avi_index_chunk_id_from_track_num(VC_CONTAINER_FOURCC_T *p_chunk_id,
+ unsigned int track_num )
+{
+ VC_CONTAINER_FOURCC_T chunk_id = 0;
+ char track_num_buf[3];
+
+ chunk_id = VC_FOURCC('i','x','0','0');
+
+ snprintf(track_num_buf, sizeof(track_num_buf), "%02d", track_num);
+ memcpy(((uint8_t*)&chunk_id) + 2, track_num_buf, 2);
+
+ *p_chunk_id = chunk_id;
+}
+
+/*****************************************************************************/
+static uint32_t avi_num_chunks( VC_CONTAINER_T *p_ctx )
+{
+ unsigned int i;
+ uint32_t num_chunks = 0;
+ for (i = 0; i < p_ctx->tracks_num; i++)
+ num_chunks += p_ctx->tracks[i]->priv->module->chunk_index;
+
+ return num_chunks;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_finish_data_chunk( VC_CONTAINER_T *p_ctx, uint32_t chunk_size )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ if (chunk_size)
+ {
+ /* Rewrite the chunk size, this won't be efficient if it happens often */
+ if (STREAM_SEEKABLE(p_ctx))
+ {
+ SEEK(p_ctx, STREAM_POSITION(p_ctx) - chunk_size - 4);
+ WRITE_U32(p_ctx, chunk_size, "Chunk Size");
+ SKIP_BYTES(p_ctx, chunk_size);
+ }
+ else
+ {
+ LOG_DEBUG(p_ctx, "warning, can't rewrite chunk size, data will be malformed");
+ status = VC_CONTAINER_ERROR_FAILED;
+ }
+ }
+
+ AVI_END_CHUNK(p_ctx);
+
+ if (status != VC_CONTAINER_SUCCESS) status = STREAM_STATUS(p_ctx);
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_write_index_entry( VC_CONTAINER_T *p_ctx, uint8_t track_num,
+ uint32_t chunk_size, int keyframe )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ uint32_t deltaframe = keyframe ? 0 : AVI_INDEX_DELTAFRAME;
+
+ vc_container_io_write_uint8(module->temp_io.io, track_num);
+ vc_container_io_write_be_uint32(module->temp_io.io, chunk_size | deltaframe);
+
+ if (module->temp_io.io->status != VC_CONTAINER_SUCCESS)
+ {
+ module->index_status = module->temp_io.io->status;
+ LOG_DEBUG(p_ctx, "warning, couldn't store index data, index data will be incorrect");
+ }
+
+ return module->temp_io.io->status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_read_index_entry( VC_CONTAINER_T *p_ctx,
+ unsigned int *p_track_num, uint32_t *p_chunk_size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ uint32_t chunk_size;
+ uint8_t track_num;
+
+ track_num = vc_container_io_read_uint8(module->temp_io.io);
+ chunk_size = vc_container_io_read_be_uint32(module->temp_io.io);
+
+ /* This shouldn't really happen if the temporary I/O is reliable */
+ if (track_num >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_FAILED;
+
+ *p_track_num = track_num;
+ *p_chunk_size = chunk_size;
+
+ return module->temp_io.io->status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_write_stream_format_chunk(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track, uint32_t chunk_size)
+{
+ VC_CONTAINER_STATUS_T status;
+
+ WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','f'), "Chunk ID");
+ WRITE_U32(p_ctx, chunk_size, "Chunk Size");
+
+ if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
+
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ status = vc_container_write_bitmapinfoheader(p_ctx, track->format);
+ else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
+ status = vc_container_write_waveformatex(p_ctx, track->format);
+
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ AVI_END_CHUNK(p_ctx);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_write_stream_header_chunk(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track)
+{
+ VC_CONTAINER_FOURCC_T fourcc_type = 0, fourcc_handler = 0;
+ uint32_t flags, scale = 0, rate = 0, div, start = 0, sample_size = 0;
+ uint16_t left = 0, right = 0, top = 0, bottom = 0;
+ uint32_t max_chunk_size, length = 0;
+
+ WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','h'), "Chunk ID");
+ WRITE_U32(p_ctx, 56, "Chunk Size");
+
+ if (!track->is_enabled)
+ flags = 0; /* AVISF_DISABLED; FIXME: write_media should set this correctly! */
+ else
+ flags = 0;
+
+ if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ {
+ fourcc_type = VC_FOURCC('v','i','d','s');
+ sample_size = 0;
+ scale = track->format->type->video.frame_rate_den;
+ rate = track->format->type->video.frame_rate_num;
+ if (rate == 0 || scale == 0)
+ {
+ LOG_DEBUG(p_ctx, "invalid video framerate (%d/%d)", rate, scale);
+ LOG_DEBUG(p_ctx, "using 30/1 (playback timing will almost certainly be incorrect)");
+ scale = 1; rate = 30;
+ }
+
+ top = track->format->type->video.y_offset;
+ left = track->format->type->video.x_offset;
+ bottom = track->format->type->video.y_offset + track->format->type->video.visible_height;
+ right = track->format->type->video.x_offset + track->format->type->video.visible_width;
+ }
+ else if (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
+ {
+ fourcc_type = VC_FOURCC('a','u','d','s');
+ sample_size = track->format->type->audio.block_align;
+ scale = 1;
+
+ if (track->format->type->audio.block_align)
+ rate = (track->format->bitrate / track->format->type->audio.block_align) >> 3;
+
+ if (rate == 0)
+ {
+ rate = track->format->type->audio.sample_rate ? track->format->type->audio.sample_rate : 32000;
+ LOG_DEBUG(p_ctx, "invalid audio rate, using %d (playback timing will almost certainly be incorrect)",
+ rate);
+ }
+ }
+ else
+ {
+ /* avi_writer_add_track should ensure this can't happen */
+ vc_container_assert(0);
+ }
+
+ fourcc_handler = codec_to_vfw_fourcc(track->format->codec);
+
+ div = vc_container_maths_gcd((int64_t)scale, (int64_t)rate);
+ scale /= div;
+ rate /= div;
+
+ length = sample_size ? track->priv->module->chunk_offs : track->priv->module->chunk_index;
+ max_chunk_size = track->priv->module->max_chunk_size;
+ track->priv->module->sample_size = sample_size;
+
+ WRITE_FOURCC(p_ctx, fourcc_type, "fccType");
+ WRITE_FOURCC(p_ctx, fourcc_handler, "fccHandler");
+ WRITE_U32(p_ctx, flags, "dwFlags");
+ WRITE_U16(p_ctx, 0, "wPriority");
+ WRITE_U16(p_ctx, 0, "wLanguage");
+ WRITE_U32(p_ctx, 0, "dwInitialFrames");
+ WRITE_U32(p_ctx, scale, "dwScale");
+ WRITE_U32(p_ctx, rate, "dwRate");
+ WRITE_U32(p_ctx, start, "dwStart");
+ WRITE_U32(p_ctx, length, "dwLength");
+ WRITE_U32(p_ctx, max_chunk_size, "dwSuggestedBufferSize");
+ WRITE_U32(p_ctx, 0, "dwQuality");
+ WRITE_U32(p_ctx, sample_size, "dwSampleSize");
+ WRITE_U16(p_ctx, left, "rcFrame.left");
+ WRITE_U16(p_ctx, top, "rcFrame.top");
+ WRITE_U16(p_ctx, right, "rcFrame.right");
+ WRITE_U16(p_ctx, bottom, "rcFrame.bottom");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_write_super_index_chunk(VC_CONTAINER_T *p_ctx, unsigned int index_track_num,
+ uint32_t index_size)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[index_track_num]->priv->module;
+ VC_CONTAINER_FOURCC_T chunk_id;
+ uint32_t num_indices = 1; /* FIXME: support for multiple RIFF chunks (AVIX) */
+ unsigned int i;
+
+ if(module->null_io.refcount)
+ {
+ /* Assume that we're not actually writing the data, just want know the index chunk size */
+ WRITE_BYTES(p_ctx, NULL, 8 + 24 + num_indices * (int64_t)AVI_SUPER_INDEX_ENTRY_SIZE);
+ return STREAM_STATUS(p_ctx);
+ }
+
+ if (track_module->index_offset)
+ WRITE_FOURCC(p_ctx, VC_FOURCC('i','n','d','x'), "Chunk ID");
+ else
+ WRITE_FOURCC(p_ctx, VC_FOURCC('J','U','N','K'), "Chunk ID");
+
+ WRITE_U32(p_ctx, index_size, "Chunk Size");
+
+ avi_chunk_id_from_track_num(p_ctx, &chunk_id, index_track_num);
+ WRITE_U16(p_ctx, 4, "wLongsPerEntry");
+ WRITE_U8(p_ctx, 0, "bIndexSubType");
+ WRITE_U8(p_ctx, AVI_INDEX_OF_INDEXES, "bIndexType");
+ WRITE_U32(p_ctx, num_indices, "nEntriesInUse");
+ WRITE_FOURCC(p_ctx, chunk_id, "dwChunkId");
+ WRITE_U32(p_ctx, 0, "dwReserved0");
+ WRITE_U32(p_ctx, 0, "dwReserved1");
+ WRITE_U32(p_ctx, 0, "dwReserved2");
+
+ for (i = 0; i < num_indices; ++i)
+ {
+ uint64_t index_offset = track_module->index_offset;
+ uint32_t chunk_size = track_module->index_size;
+ uint32_t length = track_module->sample_size ?
+ track_module->chunk_offs : track_module->chunk_index;
+ WRITE_U64(p_ctx, index_offset, "qwOffset");
+ WRITE_U32(p_ctx, chunk_size, "dwSize");
+ WRITE_U32(p_ctx, length, "dwDuration");
+ }
+
+ AVI_END_CHUNK(p_ctx);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_write_stream_header_list(VC_CONTAINER_T *p_ctx,
+ unsigned int track_num, uint32_t list_size)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[track_num];
+ VC_CONTAINER_STATUS_T status;
+ uint32_t chunk_size = 0;
+
+ WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID");
+ WRITE_U32(p_ctx, list_size, "LIST Size");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','l'), "Chunk ID");
+
+ if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
+
+ /* Write the stream header chunk ('strh') */
+ status = avi_write_stream_header_chunk(p_ctx, track);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ /* Write the stream format chunk ('strf') */
+ if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
+ {
+ status = avi_write_stream_format_chunk(p_ctx, track, 0);
+ chunk_size = STREAM_POSITION(p_ctx) - 8;
+ }
+ vc_container_writer_extraio_disable(p_ctx, &module->null_io);
+
+ status = avi_write_stream_format_chunk(p_ctx, track, chunk_size);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ /* If the track has DRM data, write it into the 'strd' chunk (we don't write
+ write codec configuration data into 'strd') */
+ if (track->priv->drmdata && track->priv->drmdata_size)
+ {
+ WRITE_FOURCC(p_ctx, VC_FOURCC('s','t','r','d'), "Chunk ID");
+ WRITE_U32(p_ctx, track->priv->drmdata_size, "Chunk Size");
+ WRITE_BYTES(p_ctx, track->priv->drmdata, track->priv->drmdata_size);
+ AVI_END_CHUNK(p_ctx);
+ if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
+ }
+
+ /* Write the super index chunk ('indx') */
+ if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
+ {
+ status = avi_write_super_index_chunk(p_ctx, track_num, 0);
+ chunk_size = STREAM_POSITION(p_ctx) - 8;
+ }
+ vc_container_writer_extraio_disable(p_ctx, &module->null_io);
+
+ status = avi_write_super_index_chunk(p_ctx, track_num, chunk_size);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ AVI_END_CHUNK(p_ctx);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_write_avi_header_chunk(VC_CONTAINER_T *p_ctx)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ uint32_t bitrate = 0, width = 0, height = 0, frame_interval = 0;
+ uint32_t flags, num_chunks = 0, max_video_chunk_size = 0;
+ uint32_t num_streams = p_ctx->tracks_num;
+ unsigned int i;
+
+ for (i = 0; i < p_ctx->tracks_num; i++)
+ {
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i];
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[i]->priv->module;
+ if (track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ {
+ width = track->format->type->video.width;
+ height = track->format->type->video.height;
+ if (track->format->type->video.frame_rate_num)
+ frame_interval = track->format->type->video.frame_rate_den * UINT64_C(1000000) /
+ track->format->type->video.frame_rate_num;
+ num_chunks = track_module->chunk_index;
+ max_video_chunk_size = track_module->max_chunk_size;
+ break;
+ }
+ }
+
+ flags = (module->index_offset && module->index_status == VC_CONTAINER_SUCCESS) ?
+ (AVIF_HASINDEX | AVIF_TRUSTCKTYPE) : 0;
+
+ WRITE_FOURCC(p_ctx, VC_FOURCC('a','v','i','h'), "Chunk ID");
+ WRITE_U32(p_ctx, 56, "Chunk Size");
+ WRITE_U32(p_ctx, frame_interval, "dwMicroSecPerFrame");
+ WRITE_U32(p_ctx, bitrate >> 3, "dwMaxBytesPerSec");
+ WRITE_U32(p_ctx, 0, "dwPaddingGranularity");
+ WRITE_U32(p_ctx, flags, "dwFlags");
+ WRITE_U32(p_ctx, num_chunks, "dwTotalFrames");
+ WRITE_U32(p_ctx, 0, "dwInitialFrames");
+ WRITE_U32(p_ctx, num_streams, "dwStreams");
+ WRITE_U32(p_ctx, max_video_chunk_size, "dwSuggestedBufferSize");
+ WRITE_U32(p_ctx, width, "dwWidth");
+ WRITE_U32(p_ctx, height, "dwHeight");
+ WRITE_U32(p_ctx, 0, "dwReserved0");
+ WRITE_U32(p_ctx, 0, "dwReserved1");
+ WRITE_U32(p_ctx, 0, "dwReserved2");
+ WRITE_U32(p_ctx, 0, "dwReserved3");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_write_header_list( VC_CONTAINER_T *p_ctx, uint32_t header_list_size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ unsigned int i;
+
+ WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID");
+ WRITE_U32(p_ctx, header_list_size, "LIST Size");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('h','d','r','l'), "Chunk ID");
+ if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
+
+ /* Write the main AVI header chunk ('avih') */
+ if ((status = avi_write_avi_header_chunk(p_ctx)) != VC_CONTAINER_SUCCESS)
+ return status;
+
+ for (i = 0; i < p_ctx->tracks_num; i++)
+ {
+ uint32_t list_size = 0;
+
+ /* Write a stream header list chunk ('strl') */
+ if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
+ {
+ status = avi_write_stream_header_list(p_ctx, i, 0);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+ list_size = STREAM_POSITION(p_ctx) - 8;
+ }
+ vc_container_writer_extraio_disable(p_ctx, &module->null_io);
+
+ status = avi_write_stream_header_list(p_ctx, i, list_size);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_write_headers( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ uint32_t header_list_offset, header_list_size = 0;
+
+ /* Write the header list chunk ('hdrl') */
+ if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
+ {
+ status = avi_write_header_list(p_ctx, 0);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+ header_list_size = STREAM_POSITION(p_ctx) - 8;
+ }
+ vc_container_writer_extraio_disable(p_ctx, &module->null_io);
+
+ header_list_offset = STREAM_POSITION(p_ctx);
+ status = avi_write_header_list(p_ctx, header_list_size);
+ if (status == VC_CONTAINER_SUCCESS && !module->header_list_offset)
+ {
+ module->header_list_offset = header_list_offset;
+ module->header_list_size = header_list_size;
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_write_legacy_index_chunk( VC_CONTAINER_T *p_ctx, uint32_t index_size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ uint32_t chunk_offset = 4;
+ unsigned int track_num;
+
+ vc_container_assert(8 + avi_num_chunks(p_ctx) * INT64_C(16) <= (int64_t)ULONG_MAX);
+
+ if(module->null_io.refcount)
+ {
+ /* Assume that we're not actually writing the data,
+ just want know the index size */
+ WRITE_BYTES(p_ctx, NULL, 8 + avi_num_chunks(p_ctx) * (int64_t)AVI_INDEX_ENTRY_SIZE);
+ return STREAM_STATUS(p_ctx);
+ }
+
+ module->index_offset = STREAM_POSITION(p_ctx);
+
+ WRITE_FOURCC(p_ctx, VC_FOURCC('i','d','x','1'), "Chunk ID");
+ WRITE_U32(p_ctx, index_size, "Chunk Size");
+
+ /* Scan through all written entries, convert to appropriate index format */
+ vc_container_io_seek(module->temp_io.io, INT64_C(0));
+
+ while((status = STREAM_STATUS(p_ctx)) == VC_CONTAINER_SUCCESS)
+ {
+ VC_CONTAINER_FOURCC_T chunk_id;
+ uint32_t chunk_size, flags;
+
+ status = avi_read_index_entry(p_ctx, &track_num, &chunk_size);
+ if (status != VC_CONTAINER_SUCCESS) break;
+
+ avi_chunk_id_from_track_num(p_ctx, &chunk_id, track_num);
+ flags = (chunk_size & AVI_INDEX_DELTAFRAME) ? 0 : AVIIF_KEYFRAME;
+ chunk_size &= ~AVI_INDEX_DELTAFRAME;
+
+ WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID");
+ WRITE_U32(p_ctx, flags, "dwFlags");
+ WRITE_U32(p_ctx, chunk_offset, "dwOffset");
+ WRITE_U32(p_ctx, chunk_size, "dwSize");
+
+ chunk_offset += ((chunk_size + 1) & ~1) + 8;
+ }
+
+ AVI_END_CHUNK(p_ctx);
+
+ /* Note that currently, we might write a partial index but still set AVIF_HASINDEX */
+ /* if ( STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS ) module->index_offset = 0 */
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_write_standard_index_chunk( VC_CONTAINER_T *p_ctx, unsigned int index_track_num,
+ uint32_t index_size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[index_track_num]->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ VC_CONTAINER_FOURCC_T chunk_id;
+ int64_t base_offset = module->data_offset + 12;
+ uint32_t num_chunks = track_module->chunk_index;
+ uint32_t chunk_offset = 4;
+
+ vc_container_assert(32 + num_chunks * (int64_t)AVI_STD_INDEX_ENTRY_SIZE <= (int64_t)ULONG_MAX);
+
+ if(module->null_io.refcount)
+ {
+ /* Assume that we're not actually writing the data, just want know the index chunk size */
+ WRITE_BYTES(p_ctx, NULL, 8 + 24 + num_chunks * INT64_C(8));
+ return STREAM_STATUS(p_ctx);
+ }
+
+ track_module->index_offset = STREAM_POSITION(p_ctx);
+ track_module->index_size = index_size ? (index_size - 8) : 0;
+
+ avi_index_chunk_id_from_track_num(&chunk_id, index_track_num);
+ WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID");
+ WRITE_U32(p_ctx, index_size, "Chunk Size");
+
+ avi_chunk_id_from_track_num(p_ctx, &chunk_id, index_track_num);
+ WRITE_U16(p_ctx, 2, "wLongsPerEntry");
+ WRITE_U8(p_ctx, 0, "bIndexSubType");
+ WRITE_U8(p_ctx, AVI_INDEX_OF_CHUNKS, "bIndexType");
+ WRITE_U32(p_ctx, num_chunks, "nEntriesInUse");
+ WRITE_FOURCC(p_ctx, chunk_id, "dwChunkId");
+ WRITE_U64(p_ctx, base_offset, "qwBaseOffset");
+ WRITE_U32(p_ctx, 0, "dwReserved");
+
+ /* Scan through all written entries, convert to appropriate index format */
+ vc_container_io_seek(module->temp_io.io, INT64_C(0));
+
+ while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS)
+ {
+ uint32_t chunk_size;
+ unsigned int track_num;
+
+ status = avi_read_index_entry(p_ctx, &track_num, &chunk_size);
+ if (status != VC_CONTAINER_SUCCESS) break;
+
+ if(track_num != index_track_num) continue;
+
+ WRITE_U32(p_ctx, chunk_offset, "dwOffset");
+ WRITE_U32(p_ctx, chunk_size, "dwSize");
+
+ chunk_offset += ((chunk_size + 1) & ~(1 | AVI_INDEX_DELTAFRAME)) + 12;
+ }
+
+ AVI_END_CHUNK(p_ctx);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_write_legacy_index_data( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint32_t chunk_size = 0;
+
+ /* Write the legacy index chunk ('idx1') */
+ if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
+ {
+ status = avi_write_legacy_index_chunk(p_ctx, 0);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+ chunk_size = STREAM_POSITION(p_ctx) - 8;
+ }
+ vc_container_writer_extraio_disable(p_ctx, &module->null_io);
+
+ status = avi_write_legacy_index_chunk(p_ctx, chunk_size);
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_write_standard_index_data( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint32_t chunk_size = 0;
+ unsigned int i;
+
+ /* Write the standard index chunks ('ix00') */
+ for (i = 0; i < p_ctx->tracks_num; i++)
+ {
+ if(!vc_container_writer_extraio_enable(p_ctx, &module->null_io))
+ {
+ status = avi_write_standard_index_chunk(p_ctx, i, 0);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+ chunk_size = STREAM_POSITION(p_ctx) - 8;
+ }
+ vc_container_writer_extraio_disable(p_ctx, &module->null_io);
+
+ status = avi_write_standard_index_chunk(p_ctx, i, chunk_size);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+static int64_t avi_calculate_file_size( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *p_packet )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t filesize = 0;
+ int refcount;
+
+ /* Start from current file position */
+ filesize = STREAM_POSITION(p_ctx);
+
+ refcount = vc_container_writer_extraio_enable(p_ctx, &module->null_io);
+ vc_container_assert(refcount == 0); /* Although perfectly harmless, we should
+ not be called with the null i/o enabled. */
+ VC_CONTAINER_PARAM_UNUSED(refcount);
+
+ do {
+ /* If we know what the final size of the chunk is going to be,
+ we can use that here to avoid writing a partial final packet */
+ WRITE_BYTES(p_ctx, NULL, p_packet->frame_size ? p_packet->frame_size : p_packet->size);
+ AVI_END_CHUNK(p_ctx);
+
+ /* Index entries for the chunk */
+ WRITE_BYTES(p_ctx, NULL, AVI_INDEX_ENTRY_SIZE + AVI_STD_INDEX_ENTRY_SIZE);
+
+ /* Current standard index data */
+ if (avi_write_standard_index_data(p_ctx) != VC_CONTAINER_SUCCESS) break;
+
+ /* Current legacy index data */
+ status = avi_write_legacy_index_data(p_ctx);
+ if (status != VC_CONTAINER_SUCCESS) break;
+ } while(0);
+
+ filesize += STREAM_POSITION(p_ctx);
+
+ vc_container_writer_extraio_disable(p_ctx, &module->null_io);
+
+ return filesize;
+}
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+ *****************************************************************************/
+
+static VC_CONTAINER_STATUS_T avi_writer_write( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *p_packet )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_TRACK_T *track = NULL;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = NULL;
+
+ /* Check we have written headers before any data */
+ if(!module->headers_written)
+ {
+ if ((status = avi_write_headers(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
+ module->headers_written = 1;
+ }
+
+ /* Check that we have started the 'movi' list */
+ if (!module->data_offset)
+ {
+ module->data_offset = STREAM_POSITION(p_ctx);
+ vc_container_assert(module->data_offset != INT64_C(0));
+
+ WRITE_FOURCC(p_ctx, VC_FOURCC('L','I','S','T'), "Chunk ID");
+ WRITE_U32(p_ctx, 0, "LIST Size");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('m','o','v','i'), "Chunk ID");
+ if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
+ }
+
+ /* If the container is passing in a frame from a new track but we
+ arent't finished with a chunk from another track we need to finish
+ that chunk first */
+ if (module->chunk_data_written && p_packet->track != module->current_track_num)
+ {
+ track_module = p_ctx->tracks[module->current_track_num]->priv->module;
+ status = avi_finish_data_chunk(p_ctx, module->chunk_data_written);
+ avi_write_index_entry(p_ctx, module->current_track_num, module->chunk_data_written, 0);
+ track_module->chunk_index++;
+ track_module->chunk_offs += module->chunk_data_written;
+ track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written);
+ module->chunk_data_written = 0;
+ if (status != VC_CONTAINER_SUCCESS) return status;
+ }
+
+ /* Check we are not about to go over the limit of total number of chunks */
+ if (avi_num_chunks(p_ctx) == (uint32_t)ULONG_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+
+ if(STREAM_SEEKABLE(p_ctx))
+ {
+ /* Check we are not about to go over the maximum file size */
+ if (avi_calculate_file_size(p_ctx, p_packet) >= (int64_t)ULONG_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ }
+
+ /* FIXME: are we expected to handle this case or should it be picked up by the above layer? */
+ vc_container_assert(!(module->chunk_data_written && (p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)));
+
+ track = p_ctx->tracks[p_packet->track];
+ track_module = p_ctx->tracks[p_packet->track]->priv->module;
+ module->current_track_num = p_packet->track;
+
+ if (module->chunk_data_written == 0)
+ {
+ /* This is the first fragment of the chunk */
+ VC_CONTAINER_FOURCC_T chunk_id;
+ uint32_t chunk_size;
+
+ avi_chunk_id_from_track_num(p_ctx, &chunk_id, p_packet->track);
+
+ if (p_packet->frame_size)
+ {
+ /* We know what the final size of the chunk is going to be */
+ chunk_size = module->chunk_size = p_packet->frame_size;
+ }
+ else
+ {
+ chunk_size = p_packet->size;
+ module->chunk_size = 0;
+ }
+
+ WRITE_FOURCC(p_ctx, chunk_id, "Chunk ID");
+ if(STREAM_SEEKABLE(p_ctx) || p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END)
+ {
+ /* If the output stream can seek we can fix up the frame size later, and if the
+ * packet holds the whole frame we won't need to, so write data straight out. */
+ WRITE_U32(p_ctx, chunk_size, "Chunk Size");
+ WRITE_BYTES(p_ctx, p_packet->data, p_packet->size);
+ }
+ else
+ {
+ vc_container_assert(module->avi_frame_buffer);
+ if(p_packet->size > AVI_FRAME_BUFFER_SIZE)
+ return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ module->frame_packet = *p_packet;
+ module->frame_packet.data = module->avi_frame_buffer;
+ memcpy(module->frame_packet.data,
+ p_packet->data, module->frame_packet.size);
+ }
+
+ module->chunk_data_written = p_packet->size;
+ }
+ else
+ {
+ if(module->frame_packet.size > 0 && module->avi_frame_buffer)
+ {
+ if(module->frame_packet.size > 0)
+ {
+ if(module->frame_packet.size + p_packet->size > AVI_FRAME_BUFFER_SIZE)
+ return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ memcpy(module->frame_packet.data + module->frame_packet.size,
+ p_packet->data, p_packet->size);
+ module->frame_packet.size += p_packet->size;
+ }
+ }
+ else
+ {
+ WRITE_BYTES(p_ctx, p_packet->data, p_packet->size);
+ }
+ module->chunk_data_written += p_packet->size;
+ }
+
+ if ((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS)
+ return status;
+
+ if ((p_packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END) ||
+ (track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO &&
+ track->format->type->audio.block_align &&
+ module->chunk_data_written > AVI_AUDIO_CHUNK_SIZE_LIMIT))
+ {
+ if(module->frame_packet.size > 0)
+ {
+ WRITE_U32(p_ctx, module->frame_packet.size, "Chunk Size");
+ WRITE_BYTES(p_ctx, module->frame_packet.data, module->frame_packet.size);
+ p_packet->size = module->frame_packet.size;
+ module->frame_packet.size = 0;
+ }
+
+ if (!module->chunk_size && module->chunk_data_written > p_packet->size)
+ {
+ /* The chunk size needs to be rewritten */
+ status = avi_finish_data_chunk(p_ctx, module->chunk_data_written);
+ }
+ else
+ {
+ status = avi_finish_data_chunk(p_ctx, 0);
+ }
+
+ if(!STREAM_SEEKABLE(p_ctx))
+ {
+ /* If we are streaming then flush to avoid delaying data transport. */
+ vc_container_control(p_ctx, VC_CONTAINER_CONTROL_IO_FLUSH);
+ }
+
+ if(STREAM_SEEKABLE(p_ctx))
+ {
+ /* Keep track of data written so we can check we don't exceed file size and also for doing
+ * index fix-ups, but only do this if we are writing to a seekable IO. */
+ avi_write_index_entry(p_ctx, p_packet->track, module->chunk_data_written, AVI_PACKET_IS_KEYFRAME(p_packet->flags));
+ }
+ track_module->chunk_index++;
+ track_module->chunk_offs += module->chunk_data_written;
+ track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written);
+ module->chunk_data_written = 0;
+
+ if (status != VC_CONTAINER_SUCCESS) return status;
+ }
+
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_writer_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int i;
+
+ /* If we arent't finished with a chunk we need to finish it first */
+ if (module->chunk_data_written)
+ {
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track_num]->priv->module;
+ status = avi_finish_data_chunk(p_ctx, module->chunk_data_written);
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "warning, writing failed, last chunk truncated");
+ }
+ avi_write_index_entry(p_ctx, module->current_track_num, module->chunk_data_written, 0);
+ track_module->chunk_index++;
+ track_module->chunk_offs += module->chunk_data_written;
+ track_module->max_chunk_size = MAX(track_module->max_chunk_size, module->chunk_data_written);
+ module->chunk_data_written = 0;
+ }
+
+ if(STREAM_SEEKABLE(p_ctx))
+ {
+ uint32_t filesize;
+
+ /* Write standard index data before finalising the size of the 'movi' list */
+ status = avi_write_standard_index_data(p_ctx);
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ module->index_status = status;
+ LOG_DEBUG(p_ctx, "warning, writing standard index data failed, file will be malformed");
+ }
+
+ /* FIXME: support for multiple RIFF chunks (AVIX) */
+ module->data_size = STREAM_POSITION(p_ctx) - module->data_offset - 8;
+
+ /* Now write the legacy index */
+ status = avi_write_legacy_index_data(p_ctx);
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ module->index_status = status;
+ LOG_DEBUG(p_ctx, "warning, writing legacy index data failed, file will be malformed");
+ }
+
+ /* If we can, do the necessary fixups for values not know at the
+ time of writing chunk headers */
+
+ /* Rewrite the AVI RIFF chunk size */
+ filesize = (uint32_t)STREAM_POSITION(p_ctx);
+ SEEK(p_ctx, 4);
+ WRITE_U32(p_ctx, filesize, "fileSize");
+ if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "warning, rewriting 'fileSize' failed, file will be malformed");
+ }
+
+ /* Rewrite the header list chunk ('hdrl') */
+ SEEK(p_ctx, module->header_list_offset);
+ status = avi_write_header_list(p_ctx, module->header_list_size);
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "warning, rewriting 'hdrl' failed, file will be malformed");
+ }
+
+ /* Rewrite the AVI RIFF 'movi' list size */
+ SEEK(p_ctx, module->data_offset + 4);
+ WRITE_U32(p_ctx, module->data_size, "Chunk Size");
+ if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "warning, rewriting 'movi' list size failed, file will be malformed");
+ }
+ }
+
+ vc_container_writer_extraio_delete(p_ctx, &module->null_io);
+ if(module->temp_io.io) vc_container_writer_extraio_delete(p_ctx, &module->temp_io);
+
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ vc_container_free_track(p_ctx, p_ctx->tracks[i]);
+ p_ctx->tracks_num = 0;
+ p_ctx->tracks = NULL;
+
+ if(module->avi_frame_buffer) free(module->avi_frame_buffer);
+ free(module);
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_TRACK_T *track = NULL;
+
+ if (module->headers_written) return VC_CONTAINER_ERROR_FAILED;
+
+ /* FIXME: should we check the format in more detail? */
+ if((format->es_type != VC_CONTAINER_ES_TYPE_VIDEO && format->es_type != VC_CONTAINER_ES_TYPE_AUDIO) ||
+ format->codec == VC_CONTAINER_CODEC_UNKNOWN)
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED))
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ /* Allocate new track */
+ if(p_ctx->tracks_num >= AVI_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ p_ctx->tracks[p_ctx->tracks_num] = track =
+ vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
+ if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ if(format->extradata_size)
+ {
+ status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size );
+ if(status) goto error;
+ }
+
+ status = vc_container_format_copy(track->format, format, format->extradata_size);
+ if(status) goto error;
+
+ p_ctx->tracks_num++;
+ return VC_CONTAINER_SUCCESS;
+
+error:
+ vc_container_free_track(p_ctx, track);
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avi_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+
+ switch(operation)
+ {
+ case VC_CONTAINER_CONTROL_TRACK_ADD:
+ {
+ VC_CONTAINER_ES_FORMAT_T *format =
+ (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * );
+ return avi_writer_add_track(p_ctx, format);
+ }
+ case VC_CONTAINER_CONTROL_TRACK_ADD_DONE:
+ {
+ if(!module->headers_written)
+ {
+ if ((status = avi_write_headers(p_ctx)) != VC_CONTAINER_SUCCESS) return status;
+ module->headers_written = 1;
+ return VC_CONTAINER_SUCCESS;
+ }
+ else
+ return VC_CONTAINER_ERROR_FAILED;
+ }
+ default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ }
+}
+
+/******************************************************************************
+Global function definitions.
+******************************************************************************/
+VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T *p_ctx )
+{
+ const char *extension = vc_uri_path_extension(p_ctx->priv->uri);
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = 0;
+
+ /* Check if the user has specified a container */
+ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
+
+ /* Check we're the right writer for this */
+ if(!extension)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if(strcasecmp(extension, "avi") && strcasecmp(extension, "divx"))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+
+ /* Create a null i/o writer to help us out in writing our data */
+ status = vc_container_writer_extraio_create_null(p_ctx, &module->null_io);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ if(STREAM_SEEKABLE(p_ctx))
+ {
+ /* Create a temporary i/o writer for storage of index data while we are writing */
+ status = vc_container_writer_extraio_create_temp(p_ctx, &module->temp_io);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+ }
+ else
+ {
+ module->avi_frame_buffer = malloc(AVI_FRAME_BUFFER_SIZE);
+ if(!module->avi_frame_buffer)
+ { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ }
+ module->frame_packet.size = 0;
+
+ p_ctx->tracks = module->tracks;
+
+ /* Write the RIFF chunk descriptor */
+ WRITE_FOURCC(p_ctx, VC_FOURCC('R','I','F','F'), "RIFF ID");
+ WRITE_U32(p_ctx, 0, "fileSize");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('A','V','I',' '), "fileType");
+
+ if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error;
+
+ p_ctx->priv->pf_close = avi_writer_close;
+ p_ctx->priv->pf_write = avi_writer_write;
+ p_ctx->priv->pf_control = avi_writer_control;
+
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ LOG_DEBUG(p_ctx, "error opening stream");
+ p_ctx->tracks_num = 0;
+ p_ctx->tracks = NULL;
+ if(module)
+ {
+ if(module->avi_frame_buffer) free(module->avi_frame_buffer);
+ free(module);
+ }
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak writer_open avi_writer_open
+#endif
diff --git a/gfx/include/userland/containers/binary/CMakeLists.txt b/gfx/include/userland/containers/binary/CMakeLists.txt
new file mode 100644
index 0000000000..c159b4051c
--- /dev/null
+++ b/gfx/include/userland/containers/binary/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(reader_binary ${LIBRARY_TYPE} binary_reader.c)
+
+target_link_libraries(reader_binary containers)
+
+install(TARGETS reader_binary DESTINATION ${VMCS_PLUGIN_DIR})
+
+add_library(writer_binary ${LIBRARY_TYPE} binary_writer.c)
+
+target_link_libraries(writer_binary containers)
+
+install(TARGETS writer_binary DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/binary/binary_reader.c b/gfx/include/userland/containers/binary/binary_reader.c
new file mode 100644
index 0000000000..0bcf389ec5
--- /dev/null
+++ b/gfx/include/userland/containers/binary/binary_reader.c
@@ -0,0 +1,267 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+#define DEFAULT_BLOCK_SIZE (1024*16)
+/* Work-around for JPEG because our decoder expects that much at the start */
+#define DEFAULT_JPEG_BLOCK_SIZE (1024*80)
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *track;
+ unsigned int default_block_size;
+ unsigned int block_size;
+ bool init;
+
+ VC_CONTAINER_STATUS_T status;
+
+} VC_CONTAINER_MODULE_T;
+
+static struct
+{
+ const char *ext;
+ VC_CONTAINER_ES_TYPE_T type;
+ VC_CONTAINER_FOURCC_T codec;
+
+} extension_to_format_table[] =
+{
+ /* Audio */
+ {"mp3", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MPGA},
+ {"aac", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A},
+ {"adts", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A},
+ {"ac3", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_AC3},
+ {"ec3", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_EAC3},
+ {"amr", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_AMRNB},
+ {"awb", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_AMRWB},
+ {"evrc", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_EVRC},
+ {"dts", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_DTS},
+
+ /* Video */
+ {"m1v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP1V},
+ {"m2v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP2V},
+ {"m4v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V},
+ {"mp4v", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V},
+ {"h263", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H263},
+ {"263", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H263},
+ {"h264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264},
+ {"264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264},
+ {"mvc", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MVC},
+ {"vc1l", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_WVC1},
+
+ /* Image */
+ {"gif", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_GIF},
+ {"jpg", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_JPEG},
+ {"jpeg", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_JPEG},
+ {"png", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_PNG},
+ {"ppm", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_PPM},
+ {"tga", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_TGA},
+ {"bmp", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_BMP},
+
+ {"bin", 0, 0},
+ {0, 0, 0}
+};
+
+static struct
+{
+ const char *ext;
+ VC_CONTAINER_ES_TYPE_T type;
+ VC_CONTAINER_FOURCC_T codec;
+
+} bin_extension_to_format_table[] =
+{
+ {"m4v.bin", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V},
+ {"263.bin", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H263},
+ {"264.bin", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264},
+ {0, 0, 0}
+};
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+ *****************************************************************************/
+static VC_CONTAINER_STATUS_T binary_reader_read( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *packet, uint32_t flags )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int size;
+
+ if(module->status != VC_CONTAINER_SUCCESS)
+ return module->status;
+
+ if(!module->block_size)
+ {
+ module->block_size = module->default_block_size;
+ module->init = 0;
+ }
+
+ packet->size = module->block_size;
+ packet->dts = packet->pts = VC_CONTAINER_TIME_UNKNOWN;
+ if(module->init) packet->dts = packet->pts = 0;
+ packet->track = 0;
+ packet->flags = 0;
+
+ if(flags & VC_CONTAINER_READ_FLAG_SKIP)
+ {
+ size = SKIP_BYTES(p_ctx, module->block_size);
+ module->block_size -= size;
+ module->status = STREAM_STATUS(p_ctx);
+ return module->status;
+ }
+
+ if(flags & VC_CONTAINER_READ_FLAG_INFO)
+ return VC_CONTAINER_SUCCESS;
+
+ size = MIN(module->block_size, packet->buffer_size);
+ size = READ_BYTES(p_ctx, packet->data, size);
+ module->block_size -= size;
+ packet->size = size;
+
+ module->status = size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx);
+ return module->status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T binary_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *offset,
+ VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_PARAM_UNUSED(module);
+ VC_CONTAINER_PARAM_UNUSED(offset);
+ VC_CONTAINER_PARAM_UNUSED(mode);
+ VC_CONTAINER_PARAM_UNUSED(flags);
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T binary_reader_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--)
+ vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]);
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T *p_ctx )
+{
+ const char *extension = vc_uri_path_extension(p_ctx->priv->uri);
+ VC_CONTAINER_MODULE_T *module = 0;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ VC_CONTAINER_ES_TYPE_T es_type = 0;
+ VC_CONTAINER_FOURCC_T codec = 0;
+ unsigned int i;
+
+ /* Check if the user has specified a container */
+ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
+
+ /* Check if the extension is supported */
+ if(!extension || !vc_uri_path(p_ctx->priv->uri))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ for(i = 0; extension_to_format_table[i].ext; i++)
+ {
+ if(strcasecmp(extension, extension_to_format_table[i].ext))
+ continue;
+
+ es_type = extension_to_format_table[i].type;
+ codec = extension_to_format_table[i].codec;
+ break;
+ }
+ if(!extension_to_format_table[i].ext) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* If this a .bin file we look in our bin list */
+ for(i = 0; !codec && bin_extension_to_format_table[i].ext; i++)
+ {
+ if(!strstr(vc_uri_path(p_ctx->priv->uri), bin_extension_to_format_table[i].ext) &&
+ !strstr(extension, bin_extension_to_format_table[i].ext))
+ continue;
+
+ es_type = bin_extension_to_format_table[i].type;
+ codec = bin_extension_to_format_table[i].codec;
+ break;
+ }
+ if(!codec) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks_num = 1;
+ p_ctx->tracks = &module->track;
+ p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0);
+ if(!p_ctx->tracks[0]) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ p_ctx->tracks[0]->format->es_type = es_type;
+ p_ctx->tracks[0]->format->codec = codec;
+ p_ctx->tracks[0]->is_enabled = true;
+ module->default_block_size = DEFAULT_BLOCK_SIZE;
+ if(codec == VC_CONTAINER_CODEC_JPEG)
+ module->default_block_size = DEFAULT_JPEG_BLOCK_SIZE;
+ module->block_size = module->default_block_size;
+ module->init = 1;
+
+ /*
+ * We now have all the information we really need to start playing the stream
+ */
+
+ p_ctx->priv->pf_close = binary_reader_close;
+ p_ctx->priv->pf_read = binary_reader_read;
+ p_ctx->priv->pf_seek = binary_reader_seek;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ LOG_DEBUG(p_ctx, "binary: error opening stream (%i)", status);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open binary_reader_open
+#endif
diff --git a/gfx/include/userland/containers/binary/binary_writer.c b/gfx/include/userland/containers/binary/binary_writer.c
new file mode 100644
index 0000000000..b4580ead13
--- /dev/null
+++ b/gfx/include/userland/containers/binary/binary_writer.c
@@ -0,0 +1,160 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+
+/******************************************************************************
+Supported extensions
+******************************************************************************/
+static const char *extensions[] =
+{ "mp3", "aac", "adts", "ac3", "ec3", "amr", "awb", "evrc", "dts",
+ "m1v", "m2v", "mp4v", "h263", "263", "h264", "264", "mvc",
+ "bin", 0
+};
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *track;
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T * );
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+ *****************************************************************************/
+static VC_CONTAINER_STATUS_T binary_writer_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--)
+ vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]);
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T binary_writer_write( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *packet )
+{
+ WRITE_BYTES(p_ctx, packet->data, packet->size);
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T binary_writer_control( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_CONTROL_T operation, va_list args )
+{
+ VC_CONTAINER_ES_FORMAT_T *format;
+ VC_CONTAINER_TRACK_T *track;
+ VC_CONTAINER_STATUS_T status;
+
+ switch(operation)
+ {
+ case VC_CONTAINER_CONTROL_TRACK_ADD:
+ format = (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * );
+
+ /* Allocate and initialise track data */
+ if(p_ctx->tracks_num >= 1) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, 0);
+ if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ if(format->extradata_size)
+ {
+ status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size );
+ if(status != VC_CONTAINER_SUCCESS)
+ {
+ vc_container_free_track(p_ctx, track);
+ return status;
+ }
+ WRITE_BYTES(p_ctx, format->extradata, format->extradata_size);
+ }
+
+ vc_container_format_copy(track->format, format, format->extradata_size);
+ p_ctx->tracks_num++;
+ return VC_CONTAINER_SUCCESS;
+
+ case VC_CONTAINER_CONTROL_TRACK_ADD_DONE:
+ return VC_CONTAINER_SUCCESS;
+
+ default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ }
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T *p_ctx )
+{
+ const char *extension = vc_uri_path_extension(p_ctx->priv->uri);
+ VC_CONTAINER_MODULE_T *module = 0;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ unsigned int i;
+
+ /* Check if the user has specified a container */
+ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
+
+ /* Check we're the right writer for this */
+ if(!extension)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ for(i = 0; extensions[i]; i++)
+ if(!strcasecmp(extension, extensions[i])) break;
+ if(!extensions[i])
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks = &module->track;
+
+ p_ctx->priv->pf_close = binary_writer_close;
+ p_ctx->priv->pf_write = binary_writer_write;
+ p_ctx->priv->pf_control = binary_writer_control;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ LOG_DEBUG(p_ctx, "binary: error opening stream (%i)", status);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak writer_open binary_writer_open
+#endif
diff --git a/gfx/include/userland/containers/containers.h b/gfx/include/userland/containers/containers.h
new file mode 100644
index 0000000000..4b090c9e4a
--- /dev/null
+++ b/gfx/include/userland/containers/containers.h
@@ -0,0 +1,746 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_H
+#define VC_CONTAINERS_H
+
+/** \file containers.h
+ * Public API for container readers and writers
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "containers/containers_types.h"
+
+/** \defgroup VcContainerApi Container API
+ * API for container readers and writers */
+/* @{ */
+
+/** Status codes returned by the container API */
+typedef enum
+{
+ VC_CONTAINER_SUCCESS = 0, /**< No error */
+ VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED, /**< Format of container is not supported */
+ VC_CONTAINER_ERROR_FORMAT_FEATURE_NOT_SUPPORTED, /**< Format of container uses unsupported features */
+ VC_CONTAINER_ERROR_FORMAT_INVALID, /**< Format of container is invalid */
+ VC_CONTAINER_ERROR_CORRUPTED, /**< Container is corrupted */
+ VC_CONTAINER_ERROR_URI_NOT_FOUND, /**< URI could not be found */
+ VC_CONTAINER_ERROR_URI_OPEN_FAILED, /**< URI could not be opened */
+ VC_CONTAINER_ERROR_OUT_OF_MEMORY, /**< Out of memory */
+ VC_CONTAINER_ERROR_OUT_OF_SPACE, /**< Out of disk space (used when writing) */
+ VC_CONTAINER_ERROR_OUT_OF_RESOURCES, /**< Out of resources (other than memory) */
+ VC_CONTAINER_ERROR_EOS, /**< End of stream reached */
+ VC_CONTAINER_ERROR_LIMIT_REACHED, /**< User defined limit reached (used when writing) */
+ VC_CONTAINER_ERROR_BUFFER_TOO_SMALL, /**< Given buffer is too small for data to be copied */
+ VC_CONTAINER_ERROR_INCOMPLETE_DATA, /**< Requested data is incomplete */
+ VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE, /**< Container doesn't have any track */
+ VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED, /**< Format of the track is not supported */
+ VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION, /**< The requested operation is not supported */
+ VC_CONTAINER_ERROR_INVALID_ARGUMENT, /**< The argument provided is invalid */
+ VC_CONTAINER_ERROR_CONTINUE, /**< The requested operation was interrupted and needs to be tried again */
+ VC_CONTAINER_ERROR_ABORTED, /**< The requested operation was aborted */
+ VC_CONTAINER_ERROR_NOT_FOUND, /**< The requested data was not found */
+ VC_CONTAINER_ERROR_DRM_NOT_AUTHORIZED, /**< The DRM was not authorized */
+ VC_CONTAINER_ERROR_DRM_EXPIRED, /**< The DRM has expired */
+ VC_CONTAINER_ERROR_DRM_FAILED, /**< Generic DRM error */
+ VC_CONTAINER_ERROR_FAILED, /**< Generic error */
+ VC_CONTAINER_ERROR_NOT_READY /**< The container was not yet able to carry out the operation. */
+} VC_CONTAINER_STATUS_T;
+
+/** Four Character Code type used to identify codecs, etc. */
+typedef uint32_t VC_CONTAINER_FOURCC_T;
+
+/** Type definition for language codes.
+ * Language are defined as ISO639 Alpha-3 codes (http://en.wikipedia.org/wiki/List_of_ISO_639-2_codes) */
+typedef uint8_t VC_CONTAINER_LANGUAGE_T[3];
+
+/** Enumeration of the character encodings supported. */
+typedef enum {
+ VC_CONTAINER_CHAR_ENCODING_UNKNOWN = 0, /**< Encoding is unknown */
+ VC_CONTAINER_CHAR_ENCODING_UTF8 /**< UTF8 encoding */
+} VC_CONTAINER_CHAR_ENCODING_T;
+
+/** \name Container Capabilities
+ * The following flags are exported by containers to describe their capabilities */
+/* @{ */
+/** Type definition for container capabilities */
+typedef uint32_t VC_CONTAINER_CAPABILITIES_T;
+/** The container can seek */
+#define VC_CONTAINER_CAPS_CAN_SEEK 0x1
+/** Seeking is fast. The absence of this flag can be used as a hint to avoid seeking as much as possible */
+#define VC_CONTAINER_CAPS_SEEK_IS_FAST 0x2
+/** The container controls the pace at which it is reading the data */
+#define VC_CONTAINER_CAPS_CAN_CONTROL_PACE 0x4
+/** The container provides an index. This basically means that seeking will be precise and fast */
+#define VC_CONTAINER_CAPS_HAS_INDEX 0x8
+/** The container provides keyframe information */
+#define VC_CONTAINER_CAPS_DATA_HAS_KEYFRAME_FLAG 0x10
+/** The container supports adding tracks after TRACK_ADD_DONE control message has been sent */
+#define VC_CONTAINER_CAPS_DYNAMIC_TRACK_ADD 0x20
+/** The container supports forcing reading of a given track */
+#define VC_CONTAINER_CAPS_FORCE_TRACK 0x40
+/* @} */
+
+/** \defgroup VcContainerMetadata Container Metadata
+ * Container metadata contains descriptive information which is associated with the multimedia data */
+/* @{ */
+
+/** Enumeration of the different metadata keys available. */
+typedef enum {
+ /* Metadata of global scope */
+ VC_CONTAINER_METADATA_KEY_TITLE = VC_FOURCC('t','i','t','l'),
+ VC_CONTAINER_METADATA_KEY_ARTIST = VC_FOURCC('a','r','t','i'),
+ VC_CONTAINER_METADATA_KEY_ALBUM = VC_FOURCC('a','l','b','m'),
+ VC_CONTAINER_METADATA_KEY_DESCRIPTION = VC_FOURCC('d','e','s','c'),
+ VC_CONTAINER_METADATA_KEY_YEAR = VC_FOURCC('y','e','a','r'),
+ VC_CONTAINER_METADATA_KEY_GENRE = VC_FOURCC('g','e','n','r'),
+ VC_CONTAINER_METADATA_KEY_TRACK = VC_FOURCC('t','r','a','k'),
+ VC_CONTAINER_METADATA_KEY_LYRICS = VC_FOURCC('l','y','r','x'),
+
+ VC_CONTAINER_METADATA_KEY_UNKNOWN = 0
+
+} VC_CONTAINER_METADATA_KEY_T;
+
+/** Definition of the metadata type.
+ * This type is used to store one element of metadata */
+typedef struct VC_CONTAINER_METADATA_T
+{
+ /** Identifier for the type of metadata the value refers to.
+ * Using an enum for the id will mean that a list of possible values will have to be
+ * defined and maintained. This might limit extensibility and customisation.\n
+ * Maybe it would be better to use a FOURCC or even a string here. */
+ VC_CONTAINER_METADATA_KEY_T key;
+
+ VC_CONTAINER_LANGUAGE_T language; /**< Language code for the metadata */
+ VC_CONTAINER_CHAR_ENCODING_T encoding; /**< Encoding of the metadata */
+
+ /** Metadata value. This value is defined as null-terminated UTF-8 string.\n
+ * Do we want to support other types than strings (e.g. integer) ?\n
+ * We need an encoding conversion library! */
+ char *value;
+
+ /** Size of the memory area reserved for metadata value (including any
+ * terminating characters). */
+ unsigned int size;
+} VC_CONTAINER_METADATA_T;
+/* @} */
+
+/** \defgroup VcContainerESFormat Container Elementary Stream Format
+ * This describes the format of an elementary stream associated with a track */
+/* @{ */
+
+/** Enumeration of the different types of elementary streams.
+ * This divides elementary streams into 4 big categories. */
+typedef enum
+{
+ VC_CONTAINER_ES_TYPE_UNKNOWN, /**< Unknown elementary stream type */
+ VC_CONTAINER_ES_TYPE_AUDIO, /**< Audio elementary stream */
+ VC_CONTAINER_ES_TYPE_VIDEO, /**< Video elementary stream */
+ VC_CONTAINER_ES_TYPE_SUBPICTURE /**< Sub-picture elementary stream (e.g. subtitles, overlays) */
+
+} VC_CONTAINER_ES_TYPE_T;
+
+/** Definition of a video format.
+ * This describes the properties specific to a video stream */
+typedef struct VC_CONTAINER_VIDEO_FORMAT_T
+{
+ uint32_t width; /**< Width of the frame */
+ uint32_t height; /**< Height of the frame */
+ uint32_t visible_width; /**< Width of the visible area of the frame */
+ uint32_t visible_height; /**< Height of the visible area of the frame */
+ uint32_t x_offset; /**< Offset to the start of the visible width */
+ uint32_t y_offset; /**< Offset to the start of the visible height */
+ uint32_t frame_rate_num; /**< Frame rate numerator */
+ uint32_t frame_rate_den; /**< Frame rate denominator */
+ uint32_t par_num; /**< Pixel aspect ratio numerator */
+ uint32_t par_den; /**< Pixel aspect ratio denominator */
+} VC_CONTAINER_VIDEO_FORMAT_T;
+
+/** Enumeration for the different channel locations */
+typedef enum
+{
+ VC_CONTAINER_AUDIO_CHANNEL_LEFT = 0, /**< Left channel */
+ VC_CONTAINER_AUDIO_CHANNEL_RIGHT, /**< Right channel */
+ VC_CONTAINER_AUDIO_CHANNEL_CENTER, /**< Center channel */
+ VC_CONTAINER_AUDIO_CHANNEL_LOW_FREQUENCY, /**< Low frequency channel */
+ VC_CONTAINER_AUDIO_CHANNEL_BACK_LEFT, /**< Back left channel */
+ VC_CONTAINER_AUDIO_CHANNEL_BACK_RIGHT, /**< Back right channel */
+ VC_CONTAINER_AUDIO_CHANNEL_BACK_CENTER, /**< Back center channel */
+ VC_CONTAINER_AUDIO_CHANNEL_SIDE_LEFT, /**< Side left channel */
+ VC_CONTAINER_AUDIO_CHANNEL_SIDE_RIGHT, /**< Side right channel */
+
+ VC_CONTAINER_AUDIO_CHANNELS_MAX = 32 /**< Maximum number of channels supported */
+
+} VC_CONTAINER_AUDIO_CHANNEL_T;
+
+/** \name Audio format flags
+ * \anchor audioformatflags
+ * The following flags describe properties of an audio stream */
+/* @{ */
+#define VC_CONTAINER_AUDIO_FORMAT_FLAG_CHANNEL_MAPPING 0x1 /**< Channel mapping available */
+/* @} */
+
+/** Definition of an audio format.
+ * This describes the properties specific to an audio stream */
+typedef struct VC_CONTAINER_AUDIO_FORMAT_T
+{
+ uint32_t channels; /**< Number of audio channels */
+ uint32_t sample_rate; /**< Sample rate */
+
+ uint32_t bits_per_sample; /**< Bits per sample */
+ uint32_t block_align; /**< Size of a block of data */
+
+ uint32_t flags; /**< Flags describing the audio format.
+ * See \ref audioformatflags "Audio format flags". */
+
+ /** Mapping of the channels in order of appearance */
+ VC_CONTAINER_AUDIO_CHANNEL_T channel_mapping[VC_CONTAINER_AUDIO_CHANNELS_MAX];
+
+ uint16_t gap_delay; /**< Delay introduced by the encoder. Used for gapless playback */
+ uint16_t gap_padding; /**< Padding introduced by the encoder. Used for gapless playback */
+
+ /** Replay gain information. First element is the track information and the second
+ * is the album information. */
+ struct {
+ float peak; /**< Peak value (full range is 1.0) */
+ float gain; /**< Gain value in dB */
+ } replay_gain[2];
+
+} VC_CONTAINER_AUDIO_FORMAT_T;
+
+/** Definition of a subpicture format.
+ * This describes the properties specific to a subpicture stream */
+typedef struct VC_CONTAINER_SUBPICTURE_FORMAT_T
+{
+ VC_CONTAINER_CHAR_ENCODING_T encoding; /**< Encoding for text based subpicture formats */
+ uint32_t x_offset; /**< Width offset to the start of the subpicture */
+ uint32_t y_offset; /**< Height offset to the start of the subpicture */
+} VC_CONTAINER_SUBPICTURE_FORMAT_T;
+
+/** \name Elementary stream format flags
+ * \anchor esformatflags
+ * The following flags describe properties of an elementary stream */
+/* @{ */
+#define VC_CONTAINER_ES_FORMAT_FLAG_FRAMED 0x1 /**< Elementary stream is framed */
+/* @} */
+
+/** Definition of the type specific format.
+ * This describes the type specific information of the elementary stream. */
+typedef union
+{
+ VC_CONTAINER_AUDIO_FORMAT_T audio; /**< Audio specific information */
+ VC_CONTAINER_VIDEO_FORMAT_T video; /**< Video specific information */
+ VC_CONTAINER_SUBPICTURE_FORMAT_T subpicture; /**< Subpicture specific information */
+} VC_CONTAINER_ES_SPECIFIC_FORMAT_T;
+
+/** Definition of an elementary stream format */
+typedef struct VC_CONTAINER_ES_FORMAT_T
+{
+ VC_CONTAINER_ES_TYPE_T es_type; /**< Type of the elementary stream */
+ VC_CONTAINER_FOURCC_T codec; /**< Coding of the elementary stream */
+ VC_CONTAINER_FOURCC_T codec_variant; /**< If set, indicates a variant of the coding */
+
+ VC_CONTAINER_ES_SPECIFIC_FORMAT_T *type; /**< Type specific information for the elementary stream */
+
+ uint32_t bitrate; /**< Bitrate */
+
+ VC_CONTAINER_LANGUAGE_T language; /**< Language code for the elementary stream */
+ uint32_t group_id; /**< ID of the group this elementary stream belongs to */
+
+ uint32_t flags; /**< Flags describing the properties of an elementary stream.
+ * See \ref esformatflags "Elementary stream format flags". */
+
+ unsigned int extradata_size; /**< Size of the codec specific data */
+ uint8_t *extradata; /**< Codec specific data */
+
+} VC_CONTAINER_ES_FORMAT_T;
+/* @} */
+
+/** \defgroup VcContainerPacket Container Packet
+ * A container packet is the unit of data that is being read from or written to a container */
+/* @{ */
+
+/** Structure describing a data packet */
+typedef struct VC_CONTAINER_PACKET_T
+{
+ struct VC_CONTAINER_PACKET_T *next; /**< Used to build lists of packets */
+ uint8_t *data; /**< Pointer to the buffer containing the actual data for the packet */
+ unsigned int buffer_size; /**< Size of the p_data buffer. This is used to indicate how much data can be read in p_data */
+ unsigned int size; /**< Size of the data contained in p_data */
+ unsigned int frame_size; /**< If set, indicates the size of the frame this packet belongs to */
+ int64_t pts; /**< Presentation Timestamp of the packet */
+ int64_t dts; /**< Decoding Timestamp of the packet */
+ uint64_t num; /**< Number of this packet */
+ uint32_t track; /**< Track associated with this packet */
+ uint32_t flags; /**< Flags associated with this packet */
+
+ void *user_data; /**< Field reserved for use by the client */
+ void *framework_data; /**< Field reserved for use by the framework */
+
+} VC_CONTAINER_PACKET_T;
+
+/** \name Container Packet Flags
+ * The following flags describe properties of the data packet */
+/* @{ */
+#define VC_CONTAINER_PACKET_FLAG_KEYFRAME 0x01 /**< Packet is a keyframe */
+#define VC_CONTAINER_PACKET_FLAG_FRAME_START 0x02 /**< Packet starts a frame */
+#define VC_CONTAINER_PACKET_FLAG_FRAME_END 0x04 /**< Packet ends a frame */
+#define VC_CONTAINER_PACKET_FLAG_FRAME 0x06 /**< Packet contains only complete frames */
+#define VC_CONTAINER_PACKET_FLAG_DISCONTINUITY 0x08 /**< Packet comes after a discontinuity in the stream. Decoders might have to be flushed */
+#define VC_CONTAINER_PACKET_FLAG_ENCRYPTED 0x10 /**< Packet contains DRM encrypted data */
+#define VC_CONTAINER_PACKET_FLAG_CONFIG 0x20 /**< Packet contains stream specific config data */
+/* @} */
+
+/** \name Special Unknown Time Value
+ * This is the special value used to signal that a timestamp is not known */
+/* @{ */
+#define VC_CONTAINER_TIME_UNKNOWN (INT64_C(1)<<63) /**< Special value for signalling that time is not known */
+/* @} */
+
+/* @} */
+
+/** \name Track flags
+ * \anchor trackflags
+ * The following flags describe properties of a track */
+/* @{ */
+#define VC_CONTAINER_TRACK_FLAG_CHANGED 0x1 /**< Track definition has changed */
+/* @} */
+
+/** Definition of the track type */
+typedef struct VC_CONTAINER_TRACK_T
+{
+ struct VC_CONTAINER_TRACK_PRIVATE_T *priv; /**< Private member used by the implementation */
+ uint32_t is_enabled; /**< Flag to specify if the track is enabled */
+ uint32_t flags; /**< Flags describing the properties of a track.
+ * See \ref trackflags "Track flags". */
+
+ VC_CONTAINER_ES_FORMAT_T *format; /**< Format of the elementary stream contained in the track */
+
+ unsigned int meta_num; /**< Number of metadata elements associated with the track */
+ VC_CONTAINER_METADATA_T **meta; /**< Array of metadata elements associated with the track */
+
+} VC_CONTAINER_TRACK_T;
+
+/** Definition of the DRM type */
+typedef struct VC_CONTAINER_DRM_T
+{
+ VC_CONTAINER_FOURCC_T format; /**< Four character code describing the format of the DRM in use */
+ unsigned int views_max; /**< Maximum number of views allowed */
+ unsigned int views_current; /**< Current number of views */
+
+} VC_CONTAINER_DRM_T;
+
+/** Type definition for the progress reporting function. This function will be called regularly
+ * by the container during a call which blocks for too long and will report the progress of the
+ * operation as an estimated total length in microseconds and a percentage done.
+ * Returning anything else than VC_CONTAINER_SUCCESS in this function will abort the current
+ * operation. */
+typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_PROGRESS_REPORT_FUNC_T)(void *userdata,
+ int64_t length, unsigned int percentage_done);
+
+/** \name Container Events
+ * The following flags are exported by containers to notify the application of events */
+/* @{ */
+/** Type definition for container events */
+typedef uint32_t VC_CONTAINER_EVENTS_T;
+#define VC_CONTAINER_EVENT_TRACKS_CHANGE 1 /**< Track information has changed */
+#define VC_CONTAINER_EVENT_METADATA_CHANGE 2 /**< Metadata has changed */
+/* @} */
+
+/** Definition of the container context */
+typedef struct VC_CONTAINER_T
+{
+ struct VC_CONTAINER_PRIVATE_T *priv; /**< Private member used by the implementation */
+
+ VC_CONTAINER_EVENTS_T events; /**< Events generated by the container */
+ VC_CONTAINER_CAPABILITIES_T capabilities; /**< Capabilities exported by the container */
+
+ VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress; /**< Progress report function pointer */
+ void *progress_userdata; /**< Progress report user data */
+
+ int64_t duration; /**< Duration of the media in microseconds */
+ int64_t position; /**< Current time position into the media */
+ int64_t size; /**< Size of the media in bytes */
+
+ unsigned int tracks_num; /**< Number of tracks available */
+ /** Pointer to an array of pointers to track elements.
+ * The reasoning for using a pointer to pointers here is to allow us to extend
+ * VC_CONTAINER_TRACK_T without losing binary backward compatibility. */
+ VC_CONTAINER_TRACK_T **tracks;
+
+ unsigned int meta_num; /**< Number of metadata elements associated with the container */
+ VC_CONTAINER_METADATA_T **meta; /**< Array of metadata elements associated with the container */
+
+ VC_CONTAINER_DRM_T *drm; /**< Description used for DRM protected content */
+
+} VC_CONTAINER_T;
+
+/** Forward declaration of a container input / output context.
+ * This structure defines the context for a container io instance */
+typedef struct VC_CONTAINER_IO_T VC_CONTAINER_IO_T;
+
+/** Opens the media container pointed to by the URI for reading.
+ * This will create an an instance of a container reader and its associated context.
+ * The context returned will also be filled with the information retrieved from the media.
+ *
+ * If the media isn't accessible or recognized, this will return a null pointer as well as
+ * an error code indicating why this failed.
+ *
+ * \param psz_uri Unified Resource Identifier pointing to the media container
+ * \param status Returns the status of the operation
+ * \param pf_progress User provided function pointer to a progress report function. Can be set to
+ * null if no progress report is wanted. This function will be used during
+ * the whole lifetime of the instance (i.e. it will be used during
+ * open / seek / close)
+ * \param progress_userdata User provided pointer that will be passed during the progress report
+ * function call.
+ * \return A pointer to the context of the new instance of the
+ * container reader. Returns NULL on failure.
+ */
+VC_CONTAINER_T *vc_container_open_reader( const char *psz_uri, VC_CONTAINER_STATUS_T *status,
+ VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress, void *progress_userdata);
+
+/** Opens for reading the media container pointed to by the container i/o.
+ * This will create an an instance of a container reader and its associated context.
+ * The context returned will also be filled with the information retrieved from the media.
+ *
+ * If the media isn't accessible or recognized, this will return a null pointer as well as
+ * an error code indicating why this failed.
+ *
+ * \param p_io Instance of the container i/o to use
+ * \param psz_uri Unified Resource Identifier pointing to the media container (optional)
+ * \param status Returns the status of the operation
+ * \param pf_progress User provided function pointer to a progress report function. Can be set to
+ * null if no progress report is wanted. This function will be used during
+ * the whole lifetime of the instance (i.e. it will be used during
+ * open / seek / close)
+ * \param progress_userdata User provided pointer that will be passed during the progress report
+ * function call.
+ * \return A pointer to the context of the new instance of the
+ * container reader. Returns NULL on failure.
+ */
+VC_CONTAINER_T *vc_container_open_reader_with_io( VC_CONTAINER_IO_T *p_io,
+ const char *psz_uri, VC_CONTAINER_STATUS_T *status,
+ VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress, void *progress_userdata);
+
+/** Opens the media container pointed to by the URI for writing.
+ * This will create an an instance of a container writer and its associated context.
+ * The context returned will be initialised to sensible values.
+ *
+ * The application will need to add all the media tracks using \ref vc_container_control before
+ * it starts writing data using \ref vc_container_write.
+ *
+ * If the media isn't accessible or recognized, this will return a null pointer as well as
+ * an error code indicating why this failed.
+ *
+ * \param psz_uri Unified Resource Identifier pointing to the media container
+ * \param status Returns the status of the operation
+ * \param pf_progress User provided function pointer to a progess report function. Can be set to
+ * null if no progress report is wanted.
+ * \param progress_userdata User provided pointer that will be passed during the progress report
+ * function call.
+ * \return A pointer to the context of the new instance of the
+ * container writer. Returns NULL on failure.
+ */
+VC_CONTAINER_T *vc_container_open_writer( const char *psz_uri, VC_CONTAINER_STATUS_T *status,
+ VC_CONTAINER_PROGRESS_REPORT_FUNC_T pf_progress, void *progress_userdata);
+
+/** Closes an instance of a container reader / writer.
+ * This will free all the resources associated with the context.
+ *
+ * \param context Pointer to the context of the instance to close
+ * \return the status of the operation
+ */
+VC_CONTAINER_STATUS_T vc_container_close( VC_CONTAINER_T *context );
+
+/** \name Container read flags
+ * The following flags can be passed during a read call */
+/* @{ */
+/** Type definition for the read flags */
+typedef uint32_t VC_CONTAINER_READ_FLAGS_T;
+/** Ask the container to only return information on the next packet without reading it */
+#define VC_CONTAINER_READ_FLAG_INFO 1
+/** Ask the container to skip the next packet */
+#define VC_CONTAINER_READ_FLAG_SKIP 2
+/** Force the container to read data from the specified track */
+#define VC_CONTAINER_READ_FLAG_FORCE_TRACK 4
+/* @} */
+
+/** Reads a data packet from a container reader.
+ * By default, the reader will read whatever packet comes next in the container and update the
+ * given \ref VC_CONTAINER_PACKET_T structure with this packet's information.
+ * This behaviour can be changed using the \ref VC_CONTAINER_READ_FLAGS_T.\n
+ * \ref VC_CONTAINER_READ_FLAG_INFO will instruct the reader to only return information on the
+ * following packet but not its actual data. The data can be retreived later by issuing another
+ * read request.\n
+ * \ref VC_CONTAINER_READ_FLAG_FORCE_TRACK will force the reader to read the next packet for the
+ * selected track (as present in the \ref VC_CONTAINER_PACKET_T structure) instead of defaulting
+ * to reading the packet which comes next in the container.\n
+ * \ref VC_CONTAINER_READ_FLAG_SKIP will instruct the reader to skip the next packet. In this case
+ * it isn't necessary for the caller to pass a pointer to a \ref VC_CONTAINER_PACKET_T structure
+ * unless the \ref VC_CONTAINER_READ_FLAG_INFO is also given.\n
+ * A combination of all these flags can be used.
+ *
+ * \param context Pointer to the context of the reader to use
+ * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet
+ * This needs to be partially filled before the call (buffer, buffer_size)
+ * \param flags Flags controlling the read operation
+ * \return the status of the operation
+ */
+VC_CONTAINER_STATUS_T vc_container_read( VC_CONTAINER_T *context,
+ VC_CONTAINER_PACKET_T *packet, VC_CONTAINER_READ_FLAGS_T flags );
+
+/** Writes a data packet to a container writer.
+ *
+ * \param context Pointer to the context of the writer to use
+ * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet
+ * \return the status of the operation
+ */
+VC_CONTAINER_STATUS_T vc_container_write( VC_CONTAINER_T *context,
+ VC_CONTAINER_PACKET_T *packet );
+
+/** Definition of the different seek modes */
+typedef enum
+{
+ /** The offset provided for seeking is an absolute time offset in microseconds */
+ VC_CONTAINER_SEEK_MODE_TIME = 0,
+ /** The offset provided for seeking is a percentage (Q32 ?) */
+ VC_CONTAINER_SEEK_MODE_PERCENT
+
+} VC_CONTAINER_SEEK_MODE_T;
+
+/** \name Container Seek Flags
+ * The following flags control seek operations */
+/* @{ */
+/** Type definition for the seek flags */
+typedef uint32_t VC_CONTAINER_SEEK_FLAGS_T;
+/** Choose precise seeking even if slower */
+#define VC_CONTAINER_SEEK_FLAG_PRECISE 0x1
+/** By default a seek will always seek to the keyframe which comes just before the requested
+ * position. This flag allows the caller to force the container to seek to the keyframe which
+ * comes just after the requested position. */
+#define VC_CONTAINER_SEEK_FLAG_FORWARD 0x2
+/* @} */
+
+/** Seek into a container reader.
+ *
+ * \param context Pointer to the context of the reader to use
+ * \param offset Offset to seek to. Used as an input as well as output value.
+ * \param mode Seeking mode requested.
+ * \param flags Flags affecting the seeking operation.
+ * \return the status of the operation
+ */
+VC_CONTAINER_STATUS_T vc_container_seek( VC_CONTAINER_T *context, int64_t *offset,
+ VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags);
+
+/** Performance statistics.
+ */
+/** The maximum number of bins a statistics value is held in */
+#define VC_CONTAINER_STATS_BINS 10
+
+/** This type is used to represent multiple values of a statistic.
+ */
+typedef struct VC_CONTAINER_STATS_T
+{
+ /** The number of places to right shift count before using. Resulting values
+ * of zero are rounded to 1. */
+ uint32_t shift;
+
+ /** We store VC_CONTAINER_STATS_BINS+1 records, in sorted order of numpc.
+ * At least one will be invalid and all zero. We combine adjacent records
+ * as necessary. */
+ struct {
+ /** Sum of count. For single value statistics this is the freqency, for paired statistics
+ * this is the number of bytes written. */
+ uint32_t count;
+ /** Number of count. For single value statistics this is the total value, for paired statistics
+ * this is the total length of time. */
+ uint32_t num;
+ /** Number>>shift per count. Stored to save recalculation. */
+ uint32_t numpc;
+ } record[VC_CONTAINER_STATS_BINS+1];
+} VC_CONTAINER_STATS_T;
+
+/** This type represents the statistics saved by the io layer. */
+typedef struct VC_CONTAINER_WRITE_STATS_T
+{
+ /** This logs the number of bytes written in count, and the microseconds taken to write
+ * in num. */
+ VC_CONTAINER_STATS_T write;
+ /** This logs the length of time the write function has to wait for the asynchronous task. */
+ VC_CONTAINER_STATS_T wait;
+ /** This logs the length of time that we wait for a flush command to complete. */
+ VC_CONTAINER_STATS_T flush;
+} VC_CONTAINER_WRITE_STATS_T;
+
+
+/** Control operations which can be done on containers. */
+typedef enum
+{
+ /** Adds a new track to the list of tracks. This should be used by writers to create
+ * their list of tracks.\n
+ * Arguments:\n
+ * arg1= VC_CONTAINER_ES_FORMAT_T *: format of the track to add\n
+ * return= VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED if the format is not supported */
+ VC_CONTAINER_CONTROL_TRACK_ADD = 0,
+
+ /** Specifies that we're done adding new tracks. This is optional but can be used by writers
+ * to trigger the writing of the container header early. If this isn't used, the header will be
+ * written when the first data packet is received.\n
+ * No arguments.\n
+ * return= VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED if the format is not supported */
+ VC_CONTAINER_CONTROL_TRACK_ADD_DONE,
+
+ /** Change the format of a track in the list of tracks. This should be used by writers to modify
+ * the format of a track at run-time.\n
+ * Arguments:\n
+ * arg1= unsigned int: index of track to change\n
+ * arg2= VC_CONTAINER_ES_FORMAT_T *: format of the track to add\n
+ * return= VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED if the format is not supported */
+ VC_CONTAINER_CONTROL_TRACK_CHANGE,
+
+ /** Deletes a track from the list of tracks. This should be used by writers to delete tracks
+ * during run-time. Note that vc_container_close will automatically delete all track so it
+ * isn't necessary to call this before closing a writer.\n
+ * Arguments:\n
+ * arg1= index of the track to delete */
+ VC_CONTAINER_CONTROL_TRACK_DEL,
+
+ /** Activate the playback of DRM protected content.\n
+ * No arguments.\n
+ * return= one of the VC_CONTAINER_ERROR_DRM error codes if content can't be played */
+ VC_CONTAINER_CONTROL_DRM_PLAY,
+
+ /** TBD */
+ VC_CONTAINER_CONTROL_METADATA_ADD,
+ /** TBD */
+ VC_CONTAINER_CONTROL_METADATA_CHANGE,
+ /** TBD */
+ VC_CONTAINER_CONTROL_METADATA_DEL,
+
+ /** TBD */
+ VC_CONTAINER_CONTROL_CHAPTER_ADD,
+ /** TBD */
+ VC_CONTAINER_CONTROL_CHAPTER_DEL,
+
+ /** Set a maximum size for files generated by writers.\n
+ * Arguments:\n
+ * arg1= uint64_t: maximum size */
+ VC_CONTAINER_CONTROL_SET_MAXIMUM_SIZE,
+
+ /** Enables/disabled performance statistic gathering.\n
+ * Arguments:\n
+ * arg1= bool: enable or disable */
+ VC_CONTAINER_CONTROL_SET_IO_PERF_STATS,
+
+ /** Collects performance statistics.\n
+ * Arguments:\n
+ * arg1= VC_CONTAINER_WRITE_STATS_T *: */
+ VC_CONTAINER_CONTROL_GET_IO_PERF_STATS,
+
+ /** HACK.\n
+ * Arguments:\n
+ * arg1= void (*)(void *): callback function
+ * arg1= void *: opaque pointer to pass during the callback */
+ VC_CONTAINER_CONTROL_SET_IO_BUFFER_FULL_CALLBACK,
+
+ /** Set the I/O read buffer size to be used.\n
+ * Arguments:\n
+ * arg1= uint32_t: New buffer size in bytes*/
+ VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE,
+
+ /** Set the timeout on I/O read operations, if applicable.\n
+ * Arguments:\n
+ * arg1= uint32_t: New timeout in milliseconds, or VC_CONTAINER_READ_TIMEOUT_BLOCK */
+ VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS,
+
+ /** Set the timestamp base.\n
+ * The timestamp passed equates to time zero for the stream.\n
+ * Arguments:\n
+ * arg1= uint32_t: Timestamp base in stream clock units. */
+ VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE,
+
+ /** Set the next expected sequence number for the stream.\n
+ * Arguments:\n
+ * arg1= uint32_t: Next expected sequence number. */
+ VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER,
+
+ /** Set the source ID for the container.\n
+ * Arguments:\n
+ * arg1= uint32_t: Source identifier. */
+ VC_CONTAINER_CONTROL_SET_SOURCE_ID,
+
+ /** Arguments:\n
+ * arg1= void *: metadata buffer
+ * arg2= unsigned long: length of metadata in bytes */
+ VC_CONTAINER_CONTROL_GET_DRM_METADATA,
+
+ /** Arguments:\n
+ * arg1= unsigned long: track number
+ * arg2= VC_CONTAINER_FOURCC_T : drm type
+ * arg3= void *: encryption configuration parameters.
+ * arg4= unsigned long: configuration data length */
+ VC_CONTAINER_CONTROL_ENCRYPT_TRACK,
+
+ /** Causes the io to be flushed.\n
+ * Arguments: none */
+ VC_CONTAINER_CONTROL_IO_FLUSH,
+
+ /** Request the container reader to packetize data for the specified track.
+ * Arguments:\n
+ * arg1= unsigned long: track number
+ * arg2= VC_CONTAINER_FOURCC_T: codec variant to output */
+ VC_CONTAINER_CONTROL_TRACK_PACKETIZE,
+
+ /** Private user extensions must be above this number */
+ VC_CONTAINER_CONTROL_USER_EXTENSIONS = 0x1000
+
+} VC_CONTAINER_CONTROL_T;
+
+/** Used with the VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS control to indicate the read shall
+ * block until either data is available, or an error occurs.
+ */
+#define VC_CONTAINER_READ_TIMEOUT_BLOCK (uint32_t)(-1)
+
+/** Extensible control function for container readers and writers.
+ * This function takes a variable number of arguments which will depend on the specific operation.
+ *
+ * \param context Pointer to the VC_CONTAINER_T context to use
+ * \param operation The requested operation
+ * \return the status of the operation. Can be \ref VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION
+ * if the operation is not supported or implemented by the container.
+ */
+VC_CONTAINER_STATUS_T vc_container_control( VC_CONTAINER_T *context, VC_CONTAINER_CONTROL_T operation, ... );
+
+/* @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VC_CONTAINERS_H */
diff --git a/gfx/include/userland/containers/containers_codecs.h b/gfx/include/userland/containers/containers_codecs.h
new file mode 100644
index 0000000000..3a6bc4bab4
--- /dev/null
+++ b/gfx/include/userland/containers/containers_codecs.h
@@ -0,0 +1,214 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_CODECS_H
+#define VC_CONTAINERS_CODECS_H
+
+/** \file containers_codecs.h
+ * Codec helpers
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "containers/containers_types.h"
+
+/* Video */
+#define VC_CONTAINER_CODEC_MP1V VC_FOURCC('m','p','1','v')
+#define VC_CONTAINER_CODEC_MP2V VC_FOURCC('m','p','2','v')
+#define VC_CONTAINER_CODEC_MP4V VC_FOURCC('m','p','4','v')
+#define VC_CONTAINER_CODEC_DIV3 VC_FOURCC('d','i','v','3')
+#define VC_CONTAINER_CODEC_DIV4 VC_FOURCC('d','i','v','4')
+#define VC_CONTAINER_CODEC_H263 VC_FOURCC('h','2','6','3')
+#define VC_CONTAINER_CODEC_H264 VC_FOURCC('h','2','6','4')
+#define VC_CONTAINER_CODEC_MVC VC_FOURCC('m','v','c',' ')
+#define VC_CONTAINER_CODEC_WMV1 VC_FOURCC('w','m','v','1')
+#define VC_CONTAINER_CODEC_WMV2 VC_FOURCC('w','m','v','2')
+#define VC_CONTAINER_CODEC_WMV3 VC_FOURCC('w','m','v','3')
+#define VC_CONTAINER_CODEC_WVC1 VC_FOURCC('w','v','c','1')
+#define VC_CONTAINER_CODEC_WMVA VC_FOURCC('w','m','v','a')
+#define VC_CONTAINER_CODEC_MJPEG VC_FOURCC('m','j','p','g')
+#define VC_CONTAINER_CODEC_MJPEGA VC_FOURCC('m','j','p','a')
+#define VC_CONTAINER_CODEC_MJPEGB VC_FOURCC('m','j','p','b')
+#define VC_CONTAINER_CODEC_THEORA VC_FOURCC('t','h','e','o')
+#define VC_CONTAINER_CODEC_VP3 VC_FOURCC('v','p','3',' ')
+#define VC_CONTAINER_CODEC_VP6 VC_FOURCC('v','p','6',' ')
+#define VC_CONTAINER_CODEC_VP7 VC_FOURCC('v','p','7',' ')
+#define VC_CONTAINER_CODEC_VP8 VC_FOURCC('v','p','8',' ')
+#define VC_CONTAINER_CODEC_RV10 VC_FOURCC('r','v','1','0')
+#define VC_CONTAINER_CODEC_RV20 VC_FOURCC('r','v','2','0')
+#define VC_CONTAINER_CODEC_RV30 VC_FOURCC('r','v','3','0')
+#define VC_CONTAINER_CODEC_RV40 VC_FOURCC('r','v','4','0')
+#define VC_CONTAINER_CODEC_AVS VC_FOURCC('a','v','s',' ')
+#define VC_CONTAINER_CODEC_SPARK VC_FOURCC('s','p','r','k')
+#define VC_CONTAINER_CODEC_DIRAC VC_FOURCC('d','r','a','c')
+
+#define VC_CONTAINER_CODEC_YUV VC_FOURCC('y','u','v',' ')
+#define VC_CONTAINER_CODEC_I420 VC_FOURCC('I','4','2','0')
+#define VC_CONTAINER_CODEC_YV12 VC_FOURCC('Y','V','1','2')
+#define VC_CONTAINER_CODEC_I422 VC_FOURCC('I','4','2','2')
+#define VC_CONTAINER_CODEC_YUYV VC_FOURCC('Y','U','Y','V')
+#define VC_CONTAINER_CODEC_YVYU VC_FOURCC('Y','V','Y','U')
+#define VC_CONTAINER_CODEC_UYVY VC_FOURCC('U','Y','V','Y')
+#define VC_CONTAINER_CODEC_VYUY VC_FOURCC('V','Y','U','Y')
+#define VC_CONTAINER_CODEC_NV12 VC_FOURCC('N','V','1','2')
+#define VC_CONTAINER_CODEC_NV21 VC_FOURCC('N','V','2','1')
+#define VC_CONTAINER_CODEC_ARGB VC_FOURCC('A','R','G','B')
+#define VC_CONTAINER_CODEC_RGBA VC_FOURCC('R','G','B','A')
+#define VC_CONTAINER_CODEC_ABGR VC_FOURCC('A','B','G','R')
+#define VC_CONTAINER_CODEC_BGRA VC_FOURCC('B','G','R','A')
+#define VC_CONTAINER_CODEC_RGB16 VC_FOURCC('R','G','B','2')
+#define VC_CONTAINER_CODEC_RGB24 VC_FOURCC('R','G','B','3')
+#define VC_CONTAINER_CODEC_RGB32 VC_FOURCC('R','G','B','4')
+#define VC_CONTAINER_CODEC_BGR16 VC_FOURCC('B','G','R','2')
+#define VC_CONTAINER_CODEC_BGR24 VC_FOURCC('B','G','R','3')
+#define VC_CONTAINER_CODEC_BGR32 VC_FOURCC('B','G','R','4')
+#define VC_CONTAINER_CODEC_YUVUV128 VC_FOURCC('S','A','N','D')
+
+#define VC_CONTAINER_CODEC_JPEG VC_FOURCC('j','p','e','g')
+#define VC_CONTAINER_CODEC_PNG VC_FOURCC('p','n','g',' ')
+#define VC_CONTAINER_CODEC_GIF VC_FOURCC('g','i','f',' ')
+#define VC_CONTAINER_CODEC_PPM VC_FOURCC('p','p','m',' ')
+#define VC_CONTAINER_CODEC_TGA VC_FOURCC('t','g','a',' ')
+#define VC_CONTAINER_CODEC_BMP VC_FOURCC('b','m','p',' ')
+
+/* Audio */
+#define VC_CONTAINER_CODEC_PCM_UNSIGNED_BE VC_FOURCC('P','C','M','U')
+#define VC_CONTAINER_CODEC_PCM_UNSIGNED_LE VC_FOURCC('p','c','m','u')
+#define VC_CONTAINER_CODEC_PCM_SIGNED_BE VC_FOURCC('P','C','M','S')
+#define VC_CONTAINER_CODEC_PCM_SIGNED_LE VC_FOURCC('p','c','m','s')
+#define VC_CONTAINER_CODEC_PCM_FLOAT_BE VC_FOURCC('P','C','M','F')
+#define VC_CONTAINER_CODEC_PCM_FLOAT_LE VC_FOURCC('p','c','m','f')
+/* Defines for native endianness */
+#ifdef VC_CONTAINER_IS_BIG_ENDIAN
+#define VC_CONTAINER_CODEC_PCM_UNSIGNED VC_CONTAINER_CODEC_PCM_UNSIGNED_BE
+#define VC_CONTAINER_CODEC_PCM_SIGNED VC_CONTAINER_CODEC_PCM_SIGNED_BE
+#define VC_CONTAINER_CODEC_PCM_FLOAT VC_CONTAINER_CODEC_PCM_FLOAT_BE
+#else
+#define VC_CONTAINER_CODEC_PCM_UNSIGNED VC_CONTAINER_CODEC_PCM_UNSIGNED_LE
+#define VC_CONTAINER_CODEC_PCM_SIGNED VC_CONTAINER_CODEC_PCM_SIGNED_LE
+#define VC_CONTAINER_CODEC_PCM_FLOAT VC_CONTAINER_CODEC_PCM_FLOAT_LE
+#endif
+
+#define VC_CONTAINER_CODEC_MPGA VC_FOURCC('m','p','g','a')
+#define VC_CONTAINER_CODEC_MP4A VC_FOURCC('m','p','4','a')
+#define VC_CONTAINER_CODEC_ALAW VC_FOURCC('a','l','a','w')
+#define VC_CONTAINER_CODEC_MULAW VC_FOURCC('u','l','a','w')
+#define VC_CONTAINER_CODEC_ADPCM_MS VC_FOURCC('m','s',0x0,0x2)
+#define VC_CONTAINER_CODEC_ADPCM_IMA_MS VC_FOURCC('m','s',0x0,0x1)
+#define VC_CONTAINER_CODEC_ADPCM_SWF VC_FOURCC('a','s','w','f')
+#define VC_CONTAINER_CODEC_WMA1 VC_FOURCC('w','m','a','1')
+#define VC_CONTAINER_CODEC_WMA2 VC_FOURCC('w','m','a','2')
+#define VC_CONTAINER_CODEC_WMAP VC_FOURCC('w','m','a','p')
+#define VC_CONTAINER_CODEC_WMAL VC_FOURCC('w','m','a','l')
+#define VC_CONTAINER_CODEC_WMAV VC_FOURCC('w','m','a','v')
+#define VC_CONTAINER_CODEC_AMRNB VC_FOURCC('a','m','r','n')
+#define VC_CONTAINER_CODEC_AMRWB VC_FOURCC('a','m','r','w')
+#define VC_CONTAINER_CODEC_AMRWBP VC_FOURCC('a','m','r','p')
+#define VC_CONTAINER_CODEC_AC3 VC_FOURCC('a','c','3',' ')
+#define VC_CONTAINER_CODEC_EAC3 VC_FOURCC('e','a','c','3')
+#define VC_CONTAINER_CODEC_DTS VC_FOURCC('d','t','s',' ')
+#define VC_CONTAINER_CODEC_MLP VC_FOURCC('m','l','p',' ')
+#define VC_CONTAINER_CODEC_FLAC VC_FOURCC('f','l','a','c')
+#define VC_CONTAINER_CODEC_VORBIS VC_FOURCC('v','o','r','b')
+#define VC_CONTAINER_CODEC_SPEEX VC_FOURCC('s','p','x',' ')
+#define VC_CONTAINER_CODEC_ATRAC3 VC_FOURCC('a','t','r','3')
+#define VC_CONTAINER_CODEC_ATRACX VC_FOURCC('a','t','r','x')
+#define VC_CONTAINER_CODEC_ATRACL VC_FOURCC('a','t','r','l')
+#define VC_CONTAINER_CODEC_MIDI VC_FOURCC('m','i','d','i')
+#define VC_CONTAINER_CODEC_EVRC VC_FOURCC('e','v','r','c')
+#define VC_CONTAINER_CODEC_NELLYMOSER VC_FOURCC('n','e','l','y')
+#define VC_CONTAINER_CODEC_QCELP VC_FOURCC('q','c','e','l')
+
+/* Text */
+#define VC_CONTAINER_CODEC_TEXT VC_FOURCC('t','e','x','t')
+#define VC_CONTAINER_CODEC_SSA VC_FOURCC('s','s','a',' ')
+#define VC_CONTAINER_CODEC_USF VC_FOURCC('u','s','f',' ')
+#define VC_CONTAINER_CODEC_VOBSUB VC_FOURCC('v','s','u','b')
+
+#define VC_CONTAINER_CODEC_UNKNOWN VC_FOURCC('u','n','k','n')
+
+/* Codec variants */
+
+/** ISO 14496-10 Annex B byte stream format */
+#define VC_CONTAINER_VARIANT_H264_DEFAULT 0
+/** ISO 14496-15 AVC format (used in mp4/mkv and other containers) */
+#define VC_CONTAINER_VARIANT_H264_AVC1 VC_FOURCC('a','v','c','C')
+/** Implicitly delineated NAL units without emulation prevention */
+#define VC_CONTAINER_VARIANT_H264_RAW VC_FOURCC('r','a','w',' ')
+
+/** MPEG 1/2 Audio - Layer unknown */
+#define VC_CONTAINER_VARIANT_MPGA_DEFAULT 0
+/** MPEG 1/2 Audio - Layer 1 */
+#define VC_CONTAINER_VARIANT_MPGA_L1 VC_FOURCC('l','1',' ',' ')
+/** MPEG 1/2 Audio - Layer 2 */
+#define VC_CONTAINER_VARIANT_MPGA_L2 VC_FOURCC('l','2',' ',' ')
+/** MPEG 1/2 Audio - Layer 3 */
+#define VC_CONTAINER_VARIANT_MPGA_L3 VC_FOURCC('l','3',' ',' ')
+
+/** Converts a WaveFormat ID into a VC_CONTAINER_FOURCC_T.
+ *
+ * \param waveformat_id WaveFormat ID to convert
+ * \return a valid VC_CONTAINER_FOURCC_T or VC_CONTAINER_CODEC_UNKNOWN if no mapping was found.
+ */
+VC_CONTAINER_FOURCC_T waveformat_to_codec(uint16_t waveformat_id);
+
+/** Converts a VC_CONTAINER_FOURCC_T into a WaveFormat ID.
+ *
+ * \param codec VC_CONTAINER_FOURCC_T to convert
+ * \return a valid WaveFormat ID of 0 if no mapping was found.
+ */
+uint16_t codec_to_waveformat(VC_CONTAINER_FOURCC_T codec);
+
+/** Tries to convert a generic fourcc into a VC_CONTAINER_FOURCC_T.
+ *
+ * \param fourcc fourcc to convert
+ * \return a valid VC_CONTAINER_FOURCC_T or VC_CONTAINER_CODEC_UNKNOWN if no mapping was found.
+ */
+VC_CONTAINER_FOURCC_T fourcc_to_codec(uint32_t fourcc);
+
+uint32_t codec_to_fourcc(VC_CONTAINER_FOURCC_T codec);
+
+/** Tries to convert VideoForWindows fourcc into a VC_CONTAINER_FOURCC_T.
+ *
+ * \param fourcc vfw fourcc to convert
+ * \return a valid VC_CONTAINER_FOURCC_T or VC_CONTAINER_CODEC_UNKNOWN if no mapping was found.
+ */
+VC_CONTAINER_FOURCC_T vfw_fourcc_to_codec(uint32_t fourcc);
+
+/** Tries to convert a VC_CONTAINER_FOURCC_T into a VideoForWindows fourcc.
+ *
+ * \param codec VC_CONTAINER_FOURCC_T to convert
+ * \return a valid vfw fourcc or 0 if no mapping was found.
+ */
+uint32_t codec_to_vfw_fourcc(VC_CONTAINER_FOURCC_T codec);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VC_CONTAINERS_CODECS_H */
diff --git a/gfx/include/userland/containers/containers_types.h b/gfx/include/userland/containers/containers_types.h
new file mode 100644
index 0000000000..4cf666685a
--- /dev/null
+++ b/gfx/include/userland/containers/containers_types.h
@@ -0,0 +1,100 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_TYPES_H
+#define VC_CONTAINERS_TYPES_H
+
+/** \file containers_types.h
+ * Definition of types used by the containers API
+ */
+
+#include
+#include
+#include
+#include
+
+#if defined( __STDC__ ) && defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+#include
+#include
+
+#elif defined( __GNUC__ )
+#include
+#include
+
+#elif defined(_MSC_VER)
+#include
+#if (_MSC_VER < 1700)
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#endif
+#define PRIu16 "u"
+#define PRIu32 "u"
+#define PRId64 "I64d"
+#define PRIi64 "I64i"
+#define PRIo64 "I64o"
+#define PRIu64 "I64u"
+#define PRIx64 "I64x"
+
+#else
+typedef signed char int8_t;
+typedef unsigned char uint8_t;
+typedef signed short int16_t;
+typedef unsigned short uint16_t;
+typedef signed long int32_t;
+typedef unsigned long uint32_t;
+typedef signed long long int64_t;
+typedef unsigned long long uint64_t;
+#endif
+
+/* C99 64bits integers */
+#ifndef INT64_C
+# define INT64_C(value) value##LL
+# define UINT64_C(value) value##ULL
+#endif
+
+/* C99 boolean */
+#ifndef __cplusplus
+#ifndef bool
+# define bool int
+#endif
+#ifndef true
+# define true 1
+#endif
+#ifndef false
+# define false 0
+#endif
+#endif /* __cplusplus */
+
+/* FIXME: should be different for big endian */
+#define VC_FOURCC(a,b,c,d) ((a) | (b << 8) | (c << 16) | (d << 24))
+
+#endif /* VC_CONTAINERS_TYPES_H */
diff --git a/gfx/include/userland/containers/core/containers.c b/gfx/include/userland/containers/core/containers.c
new file mode 100644
index 0000000000..3693e354cd
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers.c
@@ -0,0 +1,637 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io.h"
+#include "containers/core/containers_filters.h"
+#include "containers/core/containers_loader.h"
+#include "containers/core/containers_logging.h"
+#include "containers/core/containers_utils.h"
+
+#define WRITER_SPACE_SAFETY_MARGIN (10*1024)
+#define PACKETIZER_BUFFER_SIZE (32*1024)
+
+/*****************************************************************************/
+VC_CONTAINER_T *vc_container_open_reader_with_io( struct VC_CONTAINER_IO_T *io,
+ const char *uri, VC_CONTAINER_STATUS_T *p_status,
+ VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_T *p_ctx = 0;
+ const char *extension;
+
+ VC_CONTAINER_PARAM_UNUSED(pfn_progress);
+ VC_CONTAINER_PARAM_UNUSED(progress_userdata);
+ VC_CONTAINER_PARAM_UNUSED(uri);
+
+ /* Sanity check the i/o */
+ if (!io || !io->pf_read || !io->pf_seek)
+ {
+ LOG_ERROR(0, "invalid i/o instance: %p", io);
+ status = VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ goto error;
+ }
+
+ /* Allocate our context before trying out the different readers / writers */
+ p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->drm));
+ if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->drm));
+ p_ctx->priv = (VC_CONTAINER_PRIVATE_T *)(p_ctx + 1);
+ p_ctx->priv->verbosity = vc_container_log_get_default_verbosity();
+ p_ctx->drm = (VC_CONTAINER_DRM_T *)(p_ctx->priv + 1);
+ p_ctx->size = io->size;
+ p_ctx->priv->io = io;
+ p_ctx->priv->uri = io->uri_parts;
+
+ /* If the uri has an extension, use it as a hint when loading the container */
+ extension = vc_uri_path_extension(p_ctx->priv->uri);
+ /* If the user has specified a container, then use that instead */
+ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
+
+ status = vc_container_load_reader(p_ctx, extension);
+ if (status != VC_CONTAINER_SUCCESS)
+ goto error;
+
+ p_ctx->priv->drm_filter = vc_container_filter_open(VC_FOURCC('d','r','m',' '),
+ VC_FOURCC('u','n','k','n'), p_ctx, &status);
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ /* Some other problem occurred aside from the filter not being appropriate or
+ the stream not needing it. */
+ if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error;
+
+ /* Report no DRM and continue as normal */
+ p_ctx->drm = NULL;
+ status = VC_CONTAINER_SUCCESS;
+ }
+
+end:
+ if(p_status) *p_status = status;
+ return p_ctx;
+
+error:
+ if (p_ctx)
+ {
+ p_ctx->priv->io = NULL; /* The i/o doesn't belong to us */
+ vc_container_close(p_ctx);
+ p_ctx = NULL;
+ }
+ goto end;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_T *vc_container_open_reader( const char *uri, VC_CONTAINER_STATUS_T *p_status,
+ VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata)
+{
+ VC_CONTAINER_IO_T *io;
+ VC_CONTAINER_T *ctx;
+
+ /* Start by opening the URI */
+ io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_READ, p_status );
+ if (!io)
+ return 0;
+
+ ctx = vc_container_open_reader_with_io( io, uri, p_status, pfn_progress, progress_userdata);
+ if (!ctx)
+ vc_container_io_close(io);
+ return ctx;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_T *vc_container_open_writer( const char *uri, VC_CONTAINER_STATUS_T *p_status,
+ VC_CONTAINER_PROGRESS_REPORT_FUNC_T pfn_progress, void *progress_userdata)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_T *p_ctx = 0;
+ VC_CONTAINER_IO_T *io;
+ const char *extension;
+ VC_CONTAINER_PARAM_UNUSED(pfn_progress);
+ VC_CONTAINER_PARAM_UNUSED(progress_userdata);
+
+ /* Start by opening the URI */
+ io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_WRITE, &status );
+ if(!io) goto error;
+
+ /* Make sure we have enough space available to start writing */
+ if(io->max_size && io->max_size < WRITER_SPACE_SAFETY_MARGIN)
+ {
+ LOG_DEBUG(p_ctx, "not enough space available to open a writer");
+ status = VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ goto error;
+ }
+
+ /* Allocate our context before trying out the different readers / writers */
+ p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv));
+ if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv));
+ p_ctx->priv = (VC_CONTAINER_PRIVATE_T *)(p_ctx + 1);
+ p_ctx->priv->verbosity = vc_container_log_get_default_verbosity();
+ p_ctx->priv->io = io;
+ p_ctx->priv->uri = io->uri_parts;
+ io = NULL; /* io now owned by the context */
+
+ /* If the uri has an extension, use it as a hint when loading the container */
+ extension = vc_uri_path_extension(p_ctx->priv->uri);
+ /* If the user has specified a container, then use that instead */
+ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
+
+ status = vc_container_load_writer(p_ctx, extension);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ end:
+ if(p_status) *p_status = status;
+ return p_ctx;
+
+error:
+ if(io) vc_container_io_close(io);
+ if (p_ctx) vc_container_close(p_ctx);
+ p_ctx = NULL;
+ goto end;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_close( VC_CONTAINER_T *p_ctx )
+{
+ unsigned int i;
+
+ if(!p_ctx)
+ return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ if(p_ctx->tracks[i]->priv->packetizer)
+ vc_packetizer_close(p_ctx->tracks[i]->priv->packetizer);
+ if(p_ctx->priv->packetizer_buffer) free(p_ctx->priv->packetizer_buffer);
+ if(p_ctx->priv->drm_filter) vc_container_filter_close(p_ctx->priv->drm_filter);
+ if(p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx);
+ if(p_ctx->priv->io) vc_container_io_close(p_ctx->priv->io);
+ if(p_ctx->priv->module_handle) vc_container_unload(p_ctx);
+ for(i = 0; i < p_ctx->meta_num; i++) free(p_ctx->meta[i]);
+ if(p_ctx->meta_num) free(p_ctx->meta);
+ p_ctx->meta_num = 0;
+ free(p_ctx);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T container_read_packet( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *p_packet, uint32_t flags )
+{
+ VC_CONTAINER_STATUS_T status;
+
+ while(1)
+ {
+ status = p_ctx->priv->pf_read(p_ctx, p_packet, flags);
+ if(status == VC_CONTAINER_ERROR_CONTINUE)
+ continue;
+
+ if(!p_packet || (flags & VC_CONTAINER_READ_FLAG_SKIP))
+ return status; /* We've just been requested to skip the data */
+
+ if(status != VC_CONTAINER_SUCCESS)
+ return status;
+
+ /* Skip data from out of bounds tracks, disabled tracks or packets that are encrypted
+ and cannot be decrypted */
+ if(p_packet->track >= p_ctx->tracks_num ||
+ !p_ctx->tracks[p_packet->track]->is_enabled ||
+ ((p_packet->flags & VC_CONTAINER_PACKET_FLAG_ENCRYPTED) && !p_ctx->priv->drm_filter))
+ {
+ if(flags & VC_CONTAINER_READ_FLAG_INFO)
+ status = p_ctx->priv->pf_read(p_ctx, p_packet, VC_CONTAINER_READ_FLAG_SKIP);
+ if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_CONTINUE)
+ continue;
+ }
+ if(status != VC_CONTAINER_SUCCESS)
+ return status;
+
+ if(p_ctx->priv->drm_filter)
+ status = vc_container_filter_process(p_ctx->priv->drm_filter, p_packet);
+
+ break;
+ }
+ return status;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_read( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_CONTINUE;
+ VC_PACKETIZER_FLAGS_T packetizer_flags = 0;
+ VC_PACKETIZER_T *packetizer;
+ uint32_t force = flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK;
+ unsigned int i;
+
+ if(!p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP))
+ return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ if(!p_packet && (flags & VC_CONTAINER_READ_FLAG_INFO))
+ return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ if(p_packet && !p_packet->data && !(flags & (VC_CONTAINER_READ_FLAG_INFO | VC_CONTAINER_READ_FLAG_SKIP)))
+ return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ if((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) &&
+ (!p_packet || p_packet->track >= p_ctx->tracks_num || !p_ctx->tracks[p_packet->track]->is_enabled))
+ return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+
+ /* Always having a packet structure to work with simplifies things */
+ if(!p_packet)
+ p_packet = &p_ctx->priv->packetizer_packet;
+
+ /* Simple/Fast case first */
+ if(!p_ctx->priv->packetizing)
+ {
+ status = container_read_packet( p_ctx, p_packet, flags );
+ goto end;
+ }
+
+ if(flags & VC_CONTAINER_READ_FLAG_INFO)
+ packetizer_flags |= VC_PACKETIZER_FLAG_INFO;
+ if(flags & VC_CONTAINER_READ_FLAG_SKIP)
+ packetizer_flags |= VC_PACKETIZER_FLAG_SKIP;
+
+ /* Loop through all the packetized tracks first to see if we've got any
+ * data to consume there */
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ VC_PACKETIZER_T *packetizer = p_ctx->tracks[i]->priv->packetizer;
+ if(!p_ctx->tracks[i]->is_enabled || !packetizer ||
+ (force && i != p_packet->track))
+ continue;
+
+ status = vc_packetizer_read(packetizer, p_packet, packetizer_flags);
+ p_packet->track = i;
+ if(status == VC_CONTAINER_SUCCESS)
+ break;
+ }
+ if(i < p_ctx->tracks_num) /* We've got some data */
+ goto end;
+
+ /* Let's go and read some data from the actual container */
+ while(1)
+ {
+ VC_CONTAINER_PACKET_T *tmp = &p_ctx->priv->packetizer_packet;
+ tmp->track = p_packet->track;
+
+ /* Let's check what the container has to offer */
+ status = container_read_packet( p_ctx, tmp, force|VC_PACKETIZER_FLAG_INFO );
+ if(status != VC_CONTAINER_SUCCESS)
+ return status;
+
+ if(!p_ctx->tracks[tmp->track]->priv->packetizer)
+ {
+ status = container_read_packet( p_ctx, p_packet, flags );
+ break;
+ }
+
+ /* Feed data from the container onto the packetizer */
+ packetizer = p_ctx->tracks[tmp->track]->priv->packetizer;
+
+ tmp->data = p_ctx->priv->packetizer_buffer;
+ tmp->buffer_size = PACKETIZER_BUFFER_SIZE;
+ tmp->size = 0;
+ status = container_read_packet( p_ctx, tmp, force );
+ if(status != VC_CONTAINER_SUCCESS)
+ return status;
+
+ p_packet->track = tmp->track;
+ vc_packetizer_push(packetizer, tmp);
+ vc_packetizer_pop(packetizer, &tmp, VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT);
+
+ status = vc_packetizer_read(packetizer, p_packet, packetizer_flags);
+ if(status == VC_CONTAINER_SUCCESS)
+ break;
+ }
+
+ end:
+ if(status != VC_CONTAINER_SUCCESS)
+ return status;
+
+ if(p_packet && p_packet->dts > p_ctx->position)
+ p_ctx->position = p_packet->dts;
+ if(p_packet && p_packet->pts > p_ctx->position)
+ p_ctx->position = p_packet->pts;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_write( VC_CONTAINER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet )
+{
+ VC_CONTAINER_STATUS_T status;
+ void * p_metadata_buffer = NULL;
+ uint32_t metadata_length = 0;
+
+ /* TODO: check other similar argument errors and non-stateless errors */
+ if (!p_packet || !p_packet->data || p_packet->track >= p_ctx->tracks_num)
+ return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+
+ /* Check for a previous error */
+ if(p_ctx->priv->status != VC_CONTAINER_SUCCESS && p_ctx->priv->status != VC_CONTAINER_ERROR_NOT_READY)
+ return p_ctx->priv->status;
+
+ /* Check we have enough space to write the data */
+ if(p_ctx->priv->max_size &&
+ p_ctx->size + p_packet->size + WRITER_SPACE_SAFETY_MARGIN > p_ctx->priv->max_size)
+ {status = VC_CONTAINER_ERROR_LIMIT_REACHED; goto end;}
+ if(p_ctx->priv->io->max_size &&
+ p_ctx->size + p_packet->size + WRITER_SPACE_SAFETY_MARGIN +
+ (p_ctx->priv->tmp_io ? p_ctx->priv->tmp_io->offset : 0) > p_ctx->priv->io->max_size)
+ {status = VC_CONTAINER_ERROR_OUT_OF_SPACE; goto end;}
+
+ /* If a filter is created, then send the packet to the filter for encryption. */
+ if(p_ctx->priv->drm_filter)
+ {
+ status = vc_container_filter_process(p_ctx->priv->drm_filter, p_packet);
+
+ if(status == VC_CONTAINER_SUCCESS)
+ {
+ /* Get the encryption metadata and send it to the output first. */
+ if(vc_container_control(p_ctx, VC_CONTAINER_CONTROL_GET_DRM_METADATA,
+ &p_metadata_buffer, &metadata_length) == VC_CONTAINER_SUCCESS && metadata_length > 0)
+ {
+ /* Make a packet up with the metadata in the payload and write it. */
+ VC_CONTAINER_PACKET_T metadata_packet;
+ metadata_packet.data = p_metadata_buffer;
+ metadata_packet.buffer_size = metadata_length;
+ metadata_packet.size = metadata_length;
+ metadata_packet.frame_size = p_packet->frame_size + metadata_length;
+ metadata_packet.pts = p_packet->pts;
+ metadata_packet.dts = p_packet->dts;
+ metadata_packet.num = p_packet->num;
+ metadata_packet.track = p_packet->track;
+ /* As this packet is written first, we must transfer any frame start
+ flag from the following packet. Also, this packet can never have the end flag set. */
+ metadata_packet.flags = p_packet->flags & ~VC_CONTAINER_PACKET_FLAG_FRAME_END;
+
+ p_packet->pts = p_packet->dts = VC_CONTAINER_TIME_UNKNOWN;
+ p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_START;
+ if(p_ctx->priv->pf_write(p_ctx, &metadata_packet) != VC_CONTAINER_SUCCESS) goto end;
+ }
+ }
+ else if (status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION)
+ {
+ /* Encryption was appropriate but a problem has occurred. Skip the write of data
+ to the io and return the status to the caller. */
+ goto end;
+ }
+ }
+
+ status = p_ctx->priv->pf_write(p_ctx, p_packet);
+
+ end:
+ p_ctx->priv->status = status;
+ return status;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_seek( VC_CONTAINER_T *p_ctx, int64_t *p_offset,
+ VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ VC_CONTAINER_STATUS_T status;
+ int64_t offset = *p_offset;
+ unsigned int i;
+
+ /* Reset all packetizers */
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ if(p_ctx->tracks[i]->priv->packetizer)
+ vc_packetizer_reset(p_ctx->tracks[i]->priv->packetizer);
+
+ status = p_ctx->priv->pf_seek(p_ctx, p_offset, mode, flags);
+
+ /* */
+ if(status == VC_CONTAINER_SUCCESS && (flags & VC_CONTAINER_SEEK_FLAG_FORWARD) &&
+ *p_offset < offset)
+ {
+ LOG_DEBUG(p_ctx, "container didn't seek forward as requested (%"PRIi64"/%"PRIi64")",
+ *p_offset, offset);
+ for(i = 1; i <= 5 && *p_offset < offset; i++)
+ {
+ *p_offset = offset + i * i * 100000;
+ status = p_ctx->priv->pf_seek(p_ctx, p_offset, mode, flags);
+ if(status != VC_CONTAINER_SUCCESS) break;
+ }
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, ... )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ va_list args;
+
+ va_start( args, operation );
+
+ if(operation == VC_CONTAINER_CONTROL_ENCRYPT_TRACK)
+ {
+ VC_CONTAINER_FOURCC_T type = va_arg(args, VC_CONTAINER_FOURCC_T);
+
+ uint32_t track_num = va_arg(args, uint32_t);
+ void *p_drm_data = va_arg(args, void *);
+ int drm_data_size = va_arg(args, uint32_t);
+
+ if(!p_ctx->priv->drm_filter && track_num < p_ctx->tracks_num)
+ {
+ VC_CONTAINER_TRACK_T * p_track = p_ctx->tracks[track_num];
+
+ if ((status = vc_container_track_allocate_drmdata(p_ctx, p_track, drm_data_size)) != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "failed to allocate memory for 'drm data' (%d bytes)", drm_data_size);
+ goto end;
+ }
+ memcpy(p_track->priv->drmdata, p_drm_data, drm_data_size);
+
+ /* Track encryption enabled and no filter - create one now. */
+ p_ctx->priv->drm_filter = vc_container_filter_open(VC_FOURCC('d','r','m',' '), type, p_ctx, &status);
+ if(status != VC_CONTAINER_SUCCESS)
+ goto end;
+
+ status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, track_num);
+ }
+ }
+ else if(operation == VC_CONTAINER_CONTROL_GET_DRM_METADATA)
+ {
+ void *p_drm_data = va_arg(args, void *);
+ int *drm_data_size = va_arg(args, int *);
+ status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, p_drm_data, drm_data_size);
+ }
+
+ if(status == VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION && p_ctx->priv->pf_control)
+ status = p_ctx->priv->pf_control(p_ctx, operation, args);
+
+ /* If the request has already been handled then we're done */
+ if(status != VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION)
+ goto end;
+
+ switch(operation)
+ {
+ case VC_CONTAINER_CONTROL_DRM_PLAY:
+ if (p_ctx->priv->drm_filter)
+ status = vc_container_filter_control(p_ctx->priv->drm_filter, operation, args);
+ break;
+
+ case VC_CONTAINER_CONTROL_SET_MAXIMUM_SIZE:
+ p_ctx->priv->max_size = (uint64_t)va_arg( args, uint64_t );
+ if(p_ctx->priv->io->max_size &&
+ p_ctx->priv->max_size > p_ctx->priv->io->max_size)
+ p_ctx->priv->max_size = p_ctx->priv->io->max_size;
+ status = VC_CONTAINER_SUCCESS;
+ break;
+
+ case VC_CONTAINER_CONTROL_TRACK_PACKETIZE:
+ {
+ unsigned int track_num = va_arg(args, unsigned int);
+ VC_CONTAINER_FOURCC_T fourcc = va_arg(args, VC_CONTAINER_FOURCC_T);
+ VC_CONTAINER_TRACK_T *p_track;
+
+ if(track_num >= p_ctx->tracks_num)
+ {
+ status = VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ break;
+ }
+ if(p_ctx->tracks[track_num]->priv->packetizer)
+ {
+ status = VC_CONTAINER_SUCCESS;
+ break;
+ }
+
+ p_track = p_ctx->tracks[track_num];
+ p_track->priv->packetizer = vc_packetizer_open( p_track->format, fourcc, &status );
+ if(!p_track->priv->packetizer)
+ break;
+
+ if(!p_ctx->priv->packetizer_buffer)
+ {
+ p_ctx->priv->packetizer_buffer = malloc(PACKETIZER_BUFFER_SIZE);
+ if(!p_ctx->priv->packetizer_buffer)
+ {
+ status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ vc_packetizer_close(p_track->priv->packetizer);
+ p_track->priv->packetizer = NULL;
+ break;
+ }
+ }
+
+ vc_container_format_copy(p_track->format, p_track->priv->packetizer->out,
+ p_track->format->extradata_size);
+ p_track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+ p_ctx->priv->packetizing = true;
+ }
+ break;
+
+ default: break;
+ }
+
+ if (status == VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION)
+ status = vc_container_io_control_list(p_ctx->priv->io, operation, args);
+
+ end:
+ va_end( args );
+ return status;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_TRACK_T *vc_container_allocate_track( VC_CONTAINER_T *context, unsigned int extra_size )
+{
+ VC_CONTAINER_TRACK_T *p_ctx;
+ unsigned int size;
+ VC_CONTAINER_PARAM_UNUSED(context);
+
+ size = sizeof(*p_ctx) + sizeof(*p_ctx->priv) + sizeof(*p_ctx->format) +
+ sizeof(*p_ctx->format->type) + extra_size;
+
+ p_ctx = malloc(size);
+ if(!p_ctx) return 0;
+
+ memset(p_ctx, 0, size);
+ p_ctx->priv = (VC_CONTAINER_TRACK_PRIVATE_T *)(p_ctx + 1);
+ p_ctx->format = (VC_CONTAINER_ES_FORMAT_T *)(p_ctx->priv + 1);
+ p_ctx->format->type = (void *)(p_ctx->format + 1);
+ p_ctx->priv->module = (struct VC_CONTAINER_TRACK_MODULE_T *)(p_ctx->format->type + 1);
+ return p_ctx;
+}
+
+/*****************************************************************************/
+void vc_container_free_track( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *p_track )
+{
+ VC_CONTAINER_PARAM_UNUSED(context);
+ if(p_track)
+ {
+ if(p_track->priv->extradata) free(p_track->priv->extradata);
+ if(p_track->priv->drmdata) free(p_track->priv->drmdata);
+ free(p_track);
+ }
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_track_allocate_extradata( VC_CONTAINER_T *context,
+ VC_CONTAINER_TRACK_T *p_track, unsigned int extra_size )
+{
+ VC_CONTAINER_PARAM_UNUSED(context);
+
+ /* Sanity check the size of the extra data */
+ if(extra_size > 100*1024) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ /* Check if we need to allocate a buffer */
+ if(extra_size > p_track->priv->extradata_size)
+ {
+ p_track->priv->extradata_size = 0;
+ if(p_track->priv->extradata) free(p_track->priv->extradata);
+ p_track->priv->extradata = malloc(extra_size);
+ p_track->format->extradata = p_track->priv->extradata;
+ if(!p_track->priv->extradata) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ p_track->priv->extradata_size = extra_size;
+ }
+ p_track->format->extradata = p_track->priv->extradata;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_track_allocate_drmdata( VC_CONTAINER_T *context,
+ VC_CONTAINER_TRACK_T *p_track, unsigned int size )
+{
+ VC_CONTAINER_PARAM_UNUSED(context);
+
+ /* Sanity check the size of the drm data */
+ if(size > 200*1024) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ /* Check if we need to allocate a buffer */
+ if(size > p_track->priv->drmdata_size)
+ {
+ p_track->priv->drmdata_size = 0;
+ if(p_track->priv->drmdata) free(p_track->priv->drmdata);
+ p_track->priv->drmdata = malloc(size);
+ if(!p_track->priv->drmdata) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ p_track->priv->drmdata_size = size;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
diff --git a/gfx/include/userland/containers/core/containers_bits.c b/gfx/include/userland/containers/core/containers_bits.c
new file mode 100644
index 0000000000..30f8650180
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_bits.c
@@ -0,0 +1,490 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "containers/core/containers_bits.h"
+#include "containers/core/containers_common.h"
+
+#ifdef ENABLE_CONTAINERS_LOG_FORMAT
+#include "containers/core/containers_logging.h"
+#endif
+
+/******************************************************************************
+Defines and constants.
+******************************************************************************/
+
+#ifdef ENABLE_CONTAINERS_LOG_FORMAT
+/** String used for indentation. If more spaces are needed, just add them. */
+#define INDENT_SPACES_STRING "> "
+#define INDENT_SPACES_LENGTH (sizeof(INDENT_SPACES_STRING) - 1)
+#endif /* ENABLE_CONTAINERS_LOG_FORMAT */
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+#ifdef ENABLE_CONTAINERS_LOG_FORMAT
+
+/**************************************************************************//**
+ * Returns a string that indicates whether the bit stream is valid or not.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object.
+ * \return A string indicating the validity of the stream.
+ */
+static const char * vc_container_bits_valid_str( VC_CONTAINER_BITS_T *bit_stream )
+{
+ return vc_container_bits_valid(bit_stream) ? "" : " - stream invalid";
+}
+
+/**************************************************************************//**
+ * Returns a string of spaces the length of which is determined by the
+ * parameter.
+ * The length is limited to a certain size, above which a greater than symbol
+ * prefixes the maximum number of spaces.
+ *
+ * \param length The required length of the string.
+ * \return A string indicating the validity of the stream.
+ */
+static const char * vc_container_bits_indent_str(uint32_t length)
+{
+ uint32_t str_length = length;
+
+ if (str_length > INDENT_SPACES_LENGTH)
+ str_length = INDENT_SPACES_LENGTH;
+
+ return INDENT_SPACES_STRING + (INDENT_SPACES_LENGTH - str_length);
+}
+
+#endif /* ENABLE_CONTAINERS_LOG_FORMAT */
+
+/**************************************************************************//**
+ * Returns the number of consecutive zero bits in the stream.
+ * the zero bits are terminated either by a one bit, or the end of the stream.
+ * In the former case, the zero bits and the terminating one bit are removed
+ * from the stream.
+ * In the latter case, the stream becomes invalid. The stream also becomes
+ * invalid if there are not as many bits after the one bit as zero bits before
+ * it.
+ * If the stream is already or becomes invalid, zero is returned.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object.
+ * \return The number of consecutive zero bits, or zero if the stream is
+ * invalid.
+ */
+static uint32_t vc_container_bits_get_leading_zero_bits( VC_CONTAINER_BITS_T *bit_stream )
+{
+ uint32_t leading_zero_bits;
+ uint32_t bits_left = vc_container_bits_available(bit_stream);
+ uint32_t bits;
+ uint8_t mask, current_byte;
+
+ if (!bits_left)
+ return vc_container_bits_invalidate(bit_stream);
+
+ /* Cache 'bits' field to avoid repeated pointer access */
+ bits = bit_stream->bits;
+ if (bits)
+ {
+ current_byte = *bit_stream->buffer;
+ mask = 1 << (bits - 1);
+ } else {
+ /* Initialize variables to placate the compiler */
+ current_byte = 0;
+ mask = 0;
+ }
+
+ /* Scan for the first one bit, counting the number of zeroes. This gives the
+ * number of further bits after the one that are part of the value. See
+ * section 9.1 of ITU-T REC H.264 201003 for more details. */
+
+ for (leading_zero_bits = 0; leading_zero_bits < bits_left; leading_zero_bits++)
+ {
+ if (!bits)
+ {
+ if (!bit_stream->bytes)
+ return vc_container_bits_invalidate(bit_stream);
+ bit_stream->bytes--;
+ current_byte = *(++bit_stream->buffer);
+ bits = 8;
+ mask = 0x80;
+ }
+
+ bits--;
+ bits_left--;
+ if (current_byte & mask)
+ break; /* Found the marker bit */
+
+ mask >>= 1;
+ }
+
+ /* Check enough bits are left in the stream for the value. */
+ if (leading_zero_bits > bits_left)
+ return vc_container_bits_invalidate(bit_stream);
+
+ /* Return cached value of bits to the stream */
+ bit_stream->bits = bits;
+
+ return leading_zero_bits;
+}
+
+/*****************************************************************************
+Functions exported as part of the bit stream API
+ *****************************************************************************/
+
+/*****************************************************************************/
+void vc_container_bits_init(VC_CONTAINER_BITS_T *bit_stream,
+ const uint8_t *buffer,
+ uint32_t available)
+{
+ vc_container_assert(buffer && (buffer != (const uint8_t *)1));
+
+ /* Start with buffer pointing at the previous byte with no bits available
+ * to make the mathematics easier */
+ bit_stream->buffer = buffer - 1;
+ bit_stream->bytes = available;
+ bit_stream->bits = 0;
+}
+
+/*****************************************************************************/
+uint32_t vc_container_bits_invalidate( VC_CONTAINER_BITS_T *bit_stream )
+{
+ bit_stream->buffer = NULL;
+ return 0;
+}
+
+/*****************************************************************************/
+bool vc_container_bits_valid(VC_CONTAINER_BITS_T *bit_stream)
+{
+ return (bit_stream->buffer != NULL);
+}
+
+/*****************************************************************************/
+void vc_container_bits_reset(VC_CONTAINER_BITS_T *bit_stream)
+{
+ bit_stream->bytes = 0;
+ bit_stream->bits = 0;
+}
+
+/*****************************************************************************/
+const uint8_t *vc_container_bits_current_pointer(const VC_CONTAINER_BITS_T *bit_stream)
+{
+ const uint8_t *buffer = bit_stream->buffer;
+
+ /* Only valid on byte boundaries, where buffer pointer has not been moved yet */
+ vc_container_assert(!bit_stream->bits);
+
+ return buffer ? (buffer + 1) : NULL;
+}
+
+/*****************************************************************************/
+void vc_container_bits_copy_stream(VC_CONTAINER_BITS_T *dst,
+ const VC_CONTAINER_BITS_T *src)
+{
+ memcpy(dst, src, sizeof(VC_CONTAINER_BITS_T));
+}
+
+/*****************************************************************************/
+uint32_t vc_container_bits_available(const VC_CONTAINER_BITS_T *bit_stream)
+{
+ if (!bit_stream->buffer)
+ return 0;
+ return (bit_stream->bytes << 3) + bit_stream->bits;
+}
+
+/*****************************************************************************/
+uint32_t vc_container_bits_bytes_available(const VC_CONTAINER_BITS_T *bit_stream)
+{
+ if (!bit_stream->buffer)
+ return 0;
+
+ vc_container_assert(!bit_stream->bits);
+
+ return vc_container_bits_available(bit_stream) >> 3;
+}
+
+/*****************************************************************************/
+void vc_container_bits_skip(VC_CONTAINER_BITS_T *bit_stream,
+ uint32_t bits_to_skip)
+{
+ uint32_t have_bits;
+ uint32_t new_bytes;
+
+ have_bits = vc_container_bits_available(bit_stream);
+ if (have_bits < bits_to_skip)
+ {
+ vc_container_bits_invalidate(bit_stream);
+ return;
+ }
+
+ have_bits -= bits_to_skip;
+ new_bytes = have_bits >> 3;
+ bit_stream->bits = have_bits & 7;
+ bit_stream->buffer += (bit_stream->bytes - new_bytes);
+ bit_stream->bytes = new_bytes;
+}
+
+/*****************************************************************************/
+void vc_container_bits_skip_bytes(VC_CONTAINER_BITS_T *bit_stream,
+ uint32_t bytes_to_skip)
+{
+ /* Only valid on byte boundaries */
+ vc_container_assert(!bit_stream->bits);
+
+ vc_container_bits_skip(bit_stream, bytes_to_skip << 3);
+}
+
+/*****************************************************************************/
+void vc_container_bits_reduce_bytes(VC_CONTAINER_BITS_T *bit_stream,
+ uint32_t bytes_to_reduce)
+{
+ if (bit_stream->bytes >= bytes_to_reduce)
+ bit_stream->bytes -= bytes_to_reduce;
+ else
+ vc_container_bits_invalidate(bit_stream);
+}
+
+/*****************************************************************************/
+void vc_container_bits_copy_bytes(VC_CONTAINER_BITS_T *bit_stream,
+ uint32_t bytes_to_copy,
+ uint8_t *dst)
+{
+ vc_container_assert(!bit_stream->bits);
+
+ if (bit_stream->bytes < bytes_to_copy)
+ {
+ /* Not enough data */
+ vc_container_bits_invalidate(bit_stream);
+ return;
+ }
+
+ /* When the number of bits is zero, the next byte to take is at buffer + 1 */
+ memcpy(dst, bit_stream->buffer + 1, bytes_to_copy);
+ bit_stream->buffer += bytes_to_copy;
+ bit_stream->bytes -= bytes_to_copy;
+}
+
+/*****************************************************************************/
+uint32_t vc_container_bits_read_u32(VC_CONTAINER_BITS_T *bit_stream,
+ uint32_t value_bits)
+{
+ uint32_t value = 0;
+ uint32_t needed = value_bits;
+ uint32_t bits;
+
+ vc_container_assert(value_bits <= 32);
+
+ if (needed > vc_container_bits_available(bit_stream))
+ return vc_container_bits_invalidate(bit_stream);
+
+ bits = bit_stream->bits;
+ while (needed)
+ {
+ uint32_t take;
+
+ if (!bits)
+ {
+ bit_stream->bytes--;
+ bit_stream->buffer++;
+ bits = 8;
+ }
+
+ take = bits;
+ if (needed < take) take = needed;
+
+ bits -= take;
+ needed -= take;
+
+ value <<= take;
+ if (take == 8)
+ value |= *bit_stream->buffer; /* optimize whole byte case */
+ else
+ value |= (*bit_stream->buffer >> bits) & ((1 << take) - 1);
+ }
+
+ bit_stream->bits = bits;
+ return value;
+}
+
+/*****************************************************************************/
+void vc_container_bits_skip_exp_golomb(VC_CONTAINER_BITS_T *bit_stream)
+{
+ vc_container_bits_skip(bit_stream, vc_container_bits_get_leading_zero_bits(bit_stream));
+}
+
+/*****************************************************************************/
+uint32_t vc_container_bits_read_u32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream)
+{
+ uint32_t leading_zero_bits;
+ uint32_t codeNum;
+
+ leading_zero_bits = vc_container_bits_get_leading_zero_bits(bit_stream);
+
+ /* Anything bigger than 32 bits is definitely overflow */
+ if (leading_zero_bits > 32)
+ return vc_container_bits_invalidate(bit_stream);
+
+ codeNum = vc_container_bits_read_u32(bit_stream, leading_zero_bits);
+
+ if (leading_zero_bits == 32)
+ {
+ /* If codeNum is non-zero, it would need 33 bits, so is also overflow */
+ if (codeNum)
+ return vc_container_bits_invalidate(bit_stream);
+
+ return 0xFFFFFFFF;
+ }
+
+ return codeNum + (1 << leading_zero_bits) - 1;
+}
+
+/*****************************************************************************/
+int32_t vc_container_bits_read_s32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream)
+{
+ uint32_t uval;
+
+ uval = vc_container_bits_read_u32_exp_golomb(bit_stream);
+
+ /* The signed Exp-Golomb code 0xFFFFFFFF cannot be represented as a signed 32-bit
+ * integer, because it should be one larger than the largest positive value. */
+ if (uval == 0xFFFFFFFF)
+ return vc_container_bits_invalidate(bit_stream);
+
+ /* Definition of conversion is
+ * s = ((-1)^(u + 1)) * Ceil(u / 2)
+ * where '^' is power, but this should be equivalent */
+ return ((int32_t)((uval & 1) << 1) - 1) * (int32_t)((uval >> 1) + (uval & 1));
+}
+
+#ifdef ENABLE_CONTAINERS_LOG_FORMAT
+
+/*****************************************************************************/
+void vc_container_bits_log(VC_CONTAINER_T *p_ctx,
+ uint32_t indent,
+ const char *txt,
+ VC_CONTAINER_BITS_T *bit_stream,
+ VC_CONTAINER_BITS_LOG_OP_T op,
+ uint32_t length)
+{
+ const char *valid_str = vc_container_bits_valid_str(bit_stream);
+ const char *indent_str = vc_container_bits_indent_str(indent);
+
+ switch (op)
+ {
+ case VC_CONTAINER_BITS_LOG_SKIP:
+ vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bits skipped%s", indent_str, txt, length, valid_str);
+ break;
+ case VC_CONTAINER_BITS_LOG_SKIP_BYTES:
+ vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes skipped%s", indent_str, txt, length, valid_str);
+ break;
+ case VC_CONTAINER_BITS_LOG_COPY_BYTES:
+ vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes copied%s", indent_str, txt, length, valid_str);
+ break;
+ case VC_CONTAINER_BITS_LOG_REDUCE_BYTES:
+ vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: %u bytes reduced%s", indent_str, txt, length, valid_str);
+ break;
+ case VC_CONTAINER_BITS_LOG_EG_SKIP:
+ vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: Exp-Golomb value skipped%s", indent_str, txt, valid_str);
+ break;
+ default:
+ /* Unexpected operation. Check bit stream logging macros */
+ vc_container_assert(0);
+ }
+}
+
+/*****************************************************************************/
+uint32_t vc_container_bits_log_u32(VC_CONTAINER_T *p_ctx,
+ uint32_t indent,
+ const char *txt,
+ VC_CONTAINER_BITS_T *bit_stream,
+ VC_CONTAINER_BITS_LOG_OP_T op,
+ uint32_t length,
+ uint32_t value)
+{
+ const char *valid_str = vc_container_bits_valid_str(bit_stream);
+ const char *indent_str = vc_container_bits_indent_str(indent);
+
+ switch (op)
+ {
+ case VC_CONTAINER_BITS_LOG_U8:
+ vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%02x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str);
+ break;
+ case VC_CONTAINER_BITS_LOG_U16:
+ vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%04x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str);
+ break;
+ case VC_CONTAINER_BITS_LOG_U32:
+ vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%u) in %u bits%s", indent_str, txt, value, value, length, valid_str);
+ break;
+ case VC_CONTAINER_BITS_LOG_EG_U32:
+ vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%u) unsigned Exp-Golomb%s", indent_str, txt, value, value, valid_str);
+ break;
+ default:
+ /* Unexpected operation. Check bit stream logging macros */
+ vc_container_assert(0);
+ }
+
+ return value;
+}
+
+/*****************************************************************************/
+int32_t vc_container_bits_log_s32(VC_CONTAINER_T *p_ctx,
+ uint32_t indent,
+ const char *txt,
+ VC_CONTAINER_BITS_T *bit_stream,
+ VC_CONTAINER_BITS_LOG_OP_T op,
+ uint32_t length,
+ int32_t value)
+{
+ const char *valid_str = vc_container_bits_valid_str(bit_stream);
+ const char *indent_str = vc_container_bits_indent_str(indent);
+
+ VC_CONTAINER_PARAM_UNUSED(length);
+
+ switch (op)
+ {
+ case VC_CONTAINER_BITS_LOG_EG_S32:
+ vc_container_log(p_ctx, VC_CONTAINER_LOG_FORMAT, "%s%s: 0x%08x (%d) signed Exp-Golomb%s", indent_str, txt, value, value, valid_str);
+ break;
+ default:
+ /* Unexpected operation. Check bit stream logging macros */
+ vc_container_assert(0);
+ }
+
+ return value;
+}
+
+#endif /* ENABLE_CONTAINERS_LOG_FORMAT */
diff --git a/gfx/include/userland/containers/core/containers_bits.h b/gfx/include/userland/containers/core/containers_bits.h
new file mode 100644
index 0000000000..10f1cd1d61
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_bits.h
@@ -0,0 +1,344 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef VC_CONTAINERS_BITS_H
+#define VC_CONTAINERS_BITS_H
+
+#include "containers/containers.h"
+
+/** Bit stream structure
+ * Value are read from the buffer, taking bits from MSB to LSB in sequential
+ * bytes until the number of bit and the number of bytes runs out. */
+typedef struct vc_container_bits_tag
+{
+ const uint8_t *buffer; /**< Buffer from which to take bits */
+ uint32_t bytes; /**< Number of bytes available from buffer */
+ uint32_t bits; /**< Number of bits available at current pointer */
+} VC_CONTAINER_BITS_T;
+
+/** Initialise a bit stream object.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object to initialise.
+ * \param buffer Pointer to the start of the byte buffer.
+ * \param available Number of bytes in the bit stream.
+ */
+void vc_container_bits_init(VC_CONTAINER_BITS_T *bit_stream, const uint8_t *buffer, uint32_t available);
+
+/** Invalidates the bit stream.
+ * Also returns zero, because it allows callers that need to invalidate and
+ * immediately return zero to do so in a single statement.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object.
+ * \return Zero, always.
+ */
+uint32_t vc_container_bits_invalidate( VC_CONTAINER_BITS_T *bit_stream );
+
+/** Returns true if the bit stream is currently valid.
+ * The stream becomes invalid when a read or skip operation goe beyond the end
+ * of the stream.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object.
+ * \return True if the stream is valid, false if it is invalid.
+ */
+bool vc_container_bits_valid(VC_CONTAINER_BITS_T *bit_stream);
+
+/** Reset a valid bit stream object to appear empty.
+ * Once a stream has become invalid, reset has no effect.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object.
+ */
+void vc_container_bits_reset(VC_CONTAINER_BITS_T *bit_stream);
+
+/** Return the current byte pointer for the bit stream.
+ *
+ * \pre bit_stream is not NULL.
+ * \pre The stream is on a byte boundary.
+ *
+ * \param bit_stream The bit stream object.
+ * \return The current byte pointer, or NULL if the stream is invalid.
+ */
+const uint8_t *vc_container_bits_current_pointer(const VC_CONTAINER_BITS_T *bit_stream);
+
+/** Copy one bit stream to another.
+ * If the source stream is invalid, the destination one will become so as well.
+ *
+ * \pre Neither bit stream is NULL.
+ *
+ * \param dst The destination bit stream object.
+ * \param src The source bit stream object.
+ */
+void vc_container_bits_copy_stream(VC_CONTAINER_BITS_T *dst, const VC_CONTAINER_BITS_T *src);
+
+/** Return the number of bits left to take from the stream.
+ * If the stream is invalid, zero is returned.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object.
+ * \return The number of bits left to take.
+ */
+uint32_t vc_container_bits_available(const VC_CONTAINER_BITS_T *bit_stream);
+
+/** Return the number of bytes left to take from the stream.
+ * If the stream is invalid, zero is returned.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object.
+ * \return The number of bytes left to take.
+ */
+uint32_t vc_container_bits_bytes_available(const VC_CONTAINER_BITS_T *bit_stream);
+
+/** Skip past a number of bits in the stream.
+ * If bits_to_skip is greater than the number of bits available in the stream,
+ * the stream becomes invalid.
+ * If the stream is already invalid, this has no effect.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object.
+ * \param bits_to_skip The number of bits to skip.
+ */
+void vc_container_bits_skip(VC_CONTAINER_BITS_T *bit_stream, uint32_t bits_to_skip);
+
+/** Skip past a number of bytes in the stream.
+ * If bytes_to_skip is greater than the number of bytes available in the stream,
+ * the stream becomes invalid.
+ * If the stream is already invalid, this has no effect.
+ *
+ * \pre bit_stream is not NULL.
+ * \pre The stream is on a byte boundary.
+ *
+ * \param bit_stream The bit stream object.
+ * \param bytes_to_skip The number of bytes to skip.
+ */
+void vc_container_bits_skip_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_skip);
+
+/** Reduce the length of the bit stream by a number of bytes.
+ * This reduces the number of bits/bytes available without changing the current
+ * position in the stream. If bytes_to_reduce is greater than the number of
+ * bytes available in the stream, the stream becomes invalid.
+ * If the stream is already invalid, this has no effect.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object.
+ * \param bytes_to_reduce The number of bytes by which to reduce the stream.
+ */
+void vc_container_bits_reduce_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_reduce);
+
+/** Copies a number of bytes from the stream to a byte buffer.
+ * If the stream is or becomes invalid, no data is copied.
+ *
+ * \pre bit_stream is not NULL.
+ * \pre The stream is on a byte boundary.
+ *
+ * \param bit_stream The bit stream object.
+ * \param bytes_to_copy The number of bytes to copy.
+ * \param dst The byte buffer destination.
+ */
+void vc_container_bits_copy_bytes(VC_CONTAINER_BITS_T *bit_stream, uint32_t bytes_to_copy, uint8_t *dst);
+
+/** Returns the next value_bits from the stream. The last bit will be the least
+ * significant bit in the returned value.
+ * If value_bits is greater than the number of bits available in the stream,
+ * the stream becomes invalid.
+ * If the stream is invalid, or becomes invalid while reading the value, zero
+ * is returned.
+ *
+ * \pre bit_stream is not NULL.
+ * \pre value_bits is not larger than 32.
+ *
+ * \param bit_stream The bit stream object.
+ * \param value_bits The number of bits to retrieve.
+ * \return The value read from the stream, or zero if the stream is invalid.
+ */
+uint32_t vc_container_bits_read_u32(VC_CONTAINER_BITS_T *bit_stream, uint32_t value_bits);
+
+/** Skips the next Exp-Golomb value in the stream.
+ * See section 9.1 of ITU-T REC H.264 201003 for details.
+ * If there are not enough bits in the stream to complete an Exp-Golomb value,
+ * the stream becomes invalid.
+ * If the stream is already invalid, this has no effect.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object.
+ */
+void vc_container_bits_skip_exp_golomb(VC_CONTAINER_BITS_T *bit_stream);
+
+/** Returns the next unsigned Exp-Golomb value from the stream.
+ * See section 9.1 of ITU-T REC H.264 201003 for details.
+ * If there are not enough bits in the stream to complete an Exp-Golomb value,
+ * the stream becomes invalid.
+ * If the next unsigned Exp-Golomb value in the stream is larger than 32 bits,
+ * or the stream is or becomes invalid, zero is returned.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object.
+ * \return The next unsigned value from the stream, or zero on error.
+ */
+uint32_t vc_container_bits_read_u32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream);
+
+/** Returns the next signed Exp-Golomb value from the stream.
+ * See section 9.1.1 of ITU-T REC H.264 201003 for details.
+ * If there are not enough bits in the stream to complete an Exp-Golomb value,
+ * the stream becomes invalid.
+ * If the next signed Exp-Golomb value in the stream is larger than 32 bits,
+ * or the stream is or becomes invalid, zero is returned.
+ *
+ * \pre bit_stream is not NULL.
+ *
+ * \param bit_stream The bit stream object.
+ * \return The next signed value from the stream, or zero on error.
+ */
+int32_t vc_container_bits_read_s32_exp_golomb(VC_CONTAINER_BITS_T *bit_stream);
+
+/******************************************************************************
+ * Macros reduce function name length and enable logging of some operations *
+ ******************************************************************************/
+#define BITS_INIT(ctx, bits, buffer, available) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_init(bits, buffer, available))
+#define BITS_INVALIDATE(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_invalidate(bits))
+#define BITS_VALID(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_valid(bits))
+#define BITS_RESET(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_reset(bits))
+#define BITS_AVAILABLE(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_available(bits))
+#define BITS_BYTES_AVAILABLE(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_bytes_available(bits))
+#define BITS_CURRENT_POINTER(ctx, bits) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_current_pointer(bits))
+#define BITS_COPY_STREAM(ctx, dst, src) (VC_CONTAINER_PARAM_UNUSED(ctx), vc_container_bits_copy_stream(dst, src))
+
+#ifdef ENABLE_CONTAINERS_LOG_FORMAT
+
+typedef enum {
+ VC_CONTAINER_BITS_LOG_SKIP,
+ VC_CONTAINER_BITS_LOG_SKIP_BYTES,
+ VC_CONTAINER_BITS_LOG_U8,
+ VC_CONTAINER_BITS_LOG_U16,
+ VC_CONTAINER_BITS_LOG_U32,
+ VC_CONTAINER_BITS_LOG_COPY_BYTES,
+ VC_CONTAINER_BITS_LOG_REDUCE_BYTES,
+ VC_CONTAINER_BITS_LOG_EG_SKIP,
+ VC_CONTAINER_BITS_LOG_EG_U32,
+ VC_CONTAINER_BITS_LOG_EG_S32,
+} VC_CONTAINER_BITS_LOG_OP_T;
+
+/** Logs an operation with void return.
+ *
+ * \pre None of p_ctx, txt or bit_stream are NULL.
+ *
+ * \param p_ctx Container context.
+ * \param indent Indent level.
+ * \param txt Description of what is being read.
+ * \param bit_stream The bit stream object.
+ * \param op The operation just performed.
+ * \param length The length of the operation.
+ */
+void vc_container_bits_log(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length);
+
+/** Logs an operation with unsigned 32-bit integer return.
+ *
+ * \pre None of p_ctx, txt or bit_stream are NULL.
+ *
+ * \param p_ctx Container context.
+ * \param indent Indent level.
+ * \param txt Description of what is being read.
+ * \param bit_stream The bit stream object.
+ * \param op The operation just performed.
+ * \param length The length of the operation.
+ * \param value The value returned by the operation.
+ * \return The unsigned 32-bit integer value passed in.
+ */
+uint32_t vc_container_bits_log_u32(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length, uint32_t value);
+
+/** Logs an operation with signed 32-bit integer return.
+ *
+ * \pre None of p_ctx, txt or bit_stream are NULL.
+ *
+ * \param p_ctx Container context.
+ * \param indent Indent level.
+ * \param txt Description of what is being read.
+ * \param bit_stream The bit stream object.
+ * \param op The operation just performed.
+ * \param length The length of the operation.
+ * \param value The value returned by the operation.
+ * \return The signed 32-bit integer value passed in.
+ */
+int32_t vc_container_bits_log_s32(VC_CONTAINER_T *p_ctx, uint32_t indent, const char *txt, VC_CONTAINER_BITS_T *bit_stream, VC_CONTAINER_BITS_LOG_OP_T op, uint32_t length, int32_t value);
+
+#ifndef BITS_LOG_INDENT
+# ifndef CONTAINER_HELPER_LOG_INDENT
+# define BITS_LOG_INDENT(ctx) 0
+# else
+# define BITS_LOG_INDENT(ctx) ((ctx)->priv->io->module ? CONTAINER_HELPER_LOG_INDENT(a) : 0)
+# endif
+#endif
+
+#define BITS_SKIP(ctx, bits, length, txt) (vc_container_bits_skip(bits, length), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_SKIP, length))
+#define BITS_SKIP_BYTES(ctx, bits, length, txt) (vc_container_bits_skip_bytes(bits, length), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_SKIP_BYTES, length))
+
+#define BITS_READ_U8(ctx, bits, length, txt) (uint8_t)vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_U8, length, vc_container_bits_read_u32(bits, length))
+#define BITS_READ_U16(ctx, bits, length, txt) (uint16_t)vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_U16, length, vc_container_bits_read_u32(bits, length))
+#define BITS_READ_U32(ctx, bits, length, txt) vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_U32, length, vc_container_bits_read_u32(bits, length))
+
+#define BITS_COPY_BYTES(ctx, bits, length, dst, txt) (vc_container_bits_copy_bytes(bits, length, dst), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_COPY_BYTES, length))
+
+#define BITS_REDUCE_BYTES(ctx, bits, length, txt) (vc_container_bits_reduce_bytes(bits, length), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_REDUCE_BYTES, length))
+
+#define BITS_SKIP_EXP(ctx, bits, txt) (vc_container_bits_skip_exp_golomb(bits), vc_container_bits_log(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_EG_SKIP, 0))
+
+#define BITS_READ_S32_EXP(ctx, bits, txt) vc_container_bits_log_s32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_EG_S32, 0, vc_container_bits_read_s32_exp_golomb(bits))
+#define BITS_READ_U32_EXP(ctx, bits, txt) vc_container_bits_log_u32(ctx, BITS_LOG_INDENT(ctx), txt, bits, VC_CONTAINER_BITS_LOG_EG_U32, 0, vc_container_bits_read_u32_exp_golomb(bits))
+
+#else /* ENABLE_CONTAINERS_LOG_FORMAT */
+
+#define BITS_SKIP(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_skip(bits, length))
+#define BITS_SKIP_BYTES(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_skip_bytes(bits, length))
+
+#define BITS_READ_U8(ctx, bits, length, txt) (uint8_t)(VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32(bits, length))
+#define BITS_READ_U16(ctx, bits, length, txt) (uint16_t)(VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32(bits, length))
+#define BITS_READ_U32(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32(bits, length))
+
+#define BITS_COPY_BYTES(ctx, bits, length, dst, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_copy_bytes(bits, length, dst))
+
+#define BITS_REDUCE_BYTES(ctx, bits, length, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_reduce_bytes(bits, length))
+
+#define BITS_SKIP_EXP(ctx, bits, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_skip_exp_golomb(bits))
+
+#define BITS_READ_S32_EXP(ctx, bits, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_s32_exp_golomb(bits))
+#define BITS_READ_U32_EXP(ctx, bits, txt) (VC_CONTAINER_PARAM_UNUSED(ctx), VC_CONTAINER_PARAM_UNUSED(txt), vc_container_bits_read_u32_exp_golomb(bits))
+
+#endif /* ENABLE_CONTAINERS_LOG_FORMAT */
+
+#endif /* VC_CONTAINERS_BITS_H */
diff --git a/gfx/include/userland/containers/core/containers_bytestream.h b/gfx/include/userland/containers/core/containers_bytestream.h
new file mode 100644
index 0000000000..95b3a6172d
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_bytestream.h
@@ -0,0 +1,389 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_BYTESTREAM_H
+#define VC_CONTAINERS_BYTESTREAM_H
+
+/** \file
+ * Utility functions to provide a byte stream out of a list of container packets
+ */
+
+typedef struct VC_CONTAINER_BYTESTREAM_T
+{
+ VC_CONTAINER_PACKET_T *first; /**< first packet in the chain */
+ VC_CONTAINER_PACKET_T **last; /**< last packet in the chain */
+
+ VC_CONTAINER_PACKET_T *current; /**< packet containing the current read pointer */
+ size_t current_offset; /**< position of current packet (in bytes) */
+ size_t offset; /**< position within the current packet */
+
+ size_t bytes; /**< Number of bytes available in the bytestream */
+
+} VC_CONTAINER_BYTESTREAM_T;
+
+/*****************************************************************************/
+STATIC_INLINE void bytestream_init( VC_CONTAINER_BYTESTREAM_T *stream )
+{
+ stream->first = stream->current = NULL;
+ stream->last = &stream->first;
+ stream->offset = stream->current_offset = stream->bytes = 0;
+}
+
+STATIC_INLINE void bytestream_push( VC_CONTAINER_BYTESTREAM_T *stream,
+ VC_CONTAINER_PACKET_T *packet )
+{
+ *stream->last = packet;
+ stream->last = &packet->next;
+ packet->next = NULL;
+ if( !stream->current ) stream->current = packet;
+ stream->bytes += packet->size;
+}
+
+STATIC_INLINE VC_CONTAINER_PACKET_T *bytestream_pop( VC_CONTAINER_BYTESTREAM_T *stream )
+{
+ VC_CONTAINER_PACKET_T *packet = stream->first;
+
+ if( stream->current == packet )
+ return NULL;
+ vc_container_assert(packet);
+
+ stream->bytes -= packet->size;
+ stream->current_offset -= packet->size;
+ stream->first = packet->next;
+ if( !stream->first )
+ stream->last = &stream->first;
+ return packet;
+}
+
+STATIC_INLINE VC_CONTAINER_PACKET_T *bytestream_get_packet( VC_CONTAINER_BYTESTREAM_T *stream, size_t *offset )
+{
+ VC_CONTAINER_PACKET_T *packet = stream->current;
+ size_t off = stream->offset;
+
+ while(packet && packet->size == off)
+ {
+ packet = packet->next;
+ off = 0;
+ }
+
+ if (offset)
+ *offset = off;
+
+ return packet;
+}
+
+STATIC_INLINE bool bytestream_skip_packet( VC_CONTAINER_BYTESTREAM_T *stream )
+{
+ VC_CONTAINER_PACKET_T *packet = stream->current;
+
+ if( packet )
+ {
+ stream->current = packet->next;
+ stream->current_offset += (packet->size - stream->offset);
+ stream->offset = 0;
+ }
+
+ return !!packet;
+}
+
+STATIC_INLINE void bytestream_get_timestamps( VC_CONTAINER_BYTESTREAM_T *stream, int64_t *pts, int64_t *dts, bool b_same )
+{
+ VC_CONTAINER_PACKET_T *packet = bytestream_get_packet( stream, 0 );
+
+ if(packet)
+ {
+ if(b_same && packet->pts == VC_CONTAINER_TIME_UNKNOWN) packet->pts = packet->dts;
+ if(pts) *pts = packet->pts;
+ if(dts) *dts = packet->dts;
+
+ packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN;
+ return;
+ }
+
+ if(pts) *pts = VC_CONTAINER_TIME_UNKNOWN;
+ if(dts) *dts = VC_CONTAINER_TIME_UNKNOWN;
+}
+
+STATIC_INLINE void bytestream_get_timestamps_and_offset( VC_CONTAINER_BYTESTREAM_T *stream,
+ int64_t *pts, int64_t *dts, size_t *offset, bool b_same )
+{
+ VC_CONTAINER_PACKET_T *packet = bytestream_get_packet( stream, offset );
+
+ if(packet)
+ {
+ if(b_same && packet->pts == VC_CONTAINER_TIME_UNKNOWN) packet->pts = packet->dts;
+ if(pts) *pts = packet->pts;
+ if(dts) *dts = packet->dts;
+
+ packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN;
+ return;
+ }
+
+ if(pts) *pts = VC_CONTAINER_TIME_UNKNOWN;
+ if(dts) *dts = VC_CONTAINER_TIME_UNKNOWN;
+}
+
+STATIC_INLINE size_t bytestream_size( VC_CONTAINER_BYTESTREAM_T *stream )
+{
+ return stream->bytes - stream->current_offset - stream->offset;
+}
+
+STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_skip( VC_CONTAINER_BYTESTREAM_T *stream, size_t size )
+{
+ VC_CONTAINER_PACKET_T *packet;
+ size_t offset, bytes = 0, skip;
+
+ if( !size )
+ return VC_CONTAINER_SUCCESS; /* Nothing to do */
+ if( stream->bytes - stream->current_offset - stream->offset < size )
+ return VC_CONTAINER_ERROR_EOS; /* Not enough data */
+
+ for( packet = stream->current, offset = stream->offset; ;
+ packet = packet->next, offset = 0 )
+ {
+ if( packet->size - offset >= size)
+ break;
+
+ skip = packet->size - offset;
+ bytes += skip;
+ size -= skip;
+ }
+
+ stream->current = packet;
+ stream->current_offset += stream->offset - offset + bytes;
+ stream->offset = offset + size;
+ return VC_CONTAINER_SUCCESS;
+}
+
+STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_get( VC_CONTAINER_BYTESTREAM_T *stream, uint8_t *data, size_t size )
+{
+ VC_CONTAINER_PACKET_T *packet;
+ size_t offset, bytes = 0, copy;
+
+ if( !size )
+ return VC_CONTAINER_SUCCESS; /* Nothing to do */
+ if( stream->bytes - stream->current_offset - stream->offset < size )
+ return VC_CONTAINER_ERROR_EOS; /* Not enough data */
+
+ for( packet = stream->current, offset = stream->offset; ;
+ packet = packet->next, offset = 0 )
+ {
+ if( packet->size - offset >= size)
+ break;
+
+ copy = packet->size - offset;
+ memcpy( data, packet->data + offset, copy );
+ bytes += copy;
+ data += copy;
+ size -= copy;
+ }
+
+ memcpy( data, packet->data + offset, size );
+ stream->current = packet;
+ stream->current_offset += stream->offset - offset + bytes;
+ stream->offset = offset + size;
+ return VC_CONTAINER_SUCCESS;
+}
+
+STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_peek( VC_CONTAINER_BYTESTREAM_T *stream, uint8_t *data, size_t size )
+{
+ VC_CONTAINER_PACKET_T *packet;
+ size_t offset, copy;
+
+ if( !size )
+ return VC_CONTAINER_SUCCESS; /* Nothing to do */
+ if( stream->bytes - stream->current_offset - stream->offset < size )
+ return VC_CONTAINER_ERROR_EOS; /* Not enough data */
+
+ for( packet = stream->current, offset = stream->offset; ;
+ packet = packet->next, offset = 0 )
+ {
+ if( packet->size - offset >= size)
+ break;
+
+ copy = packet->size - offset;
+ memcpy( data, packet->data + offset, copy );
+ data += copy;
+ size -= copy;
+ }
+
+ memcpy( data, packet->data + offset, size );
+ return VC_CONTAINER_SUCCESS;
+}
+
+STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_peek_at( VC_CONTAINER_BYTESTREAM_T *stream,
+ size_t peek_offset, uint8_t *data, size_t size )
+{
+ VC_CONTAINER_PACKET_T *packet;
+ size_t copy;
+
+ if( !size )
+ return VC_CONTAINER_SUCCESS; /* Nothing to do */
+ if( stream->bytes - stream->current_offset - stream->offset < peek_offset + size )
+ return VC_CONTAINER_ERROR_EOS; /* Not enough data */
+
+ peek_offset += stream->offset;
+
+ /* Find the right place */
+ for( packet = stream->current; ; packet = packet->next )
+ {
+ if( packet->size > peek_offset )
+ break;
+
+ peek_offset -= packet->size;
+ }
+
+ /* Copy the data */
+ for( ; ; packet = packet->next, peek_offset = 0 )
+ {
+ if( packet->size - peek_offset >= size)
+ break;
+
+ copy = packet->size - peek_offset;
+ memcpy( data, packet->data + peek_offset, copy );
+ data += copy;
+ size -= copy;
+ }
+
+ memcpy( data, packet->data + peek_offset, size );
+ return VC_CONTAINER_SUCCESS;
+}
+
+STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_skip_byte( VC_CONTAINER_BYTESTREAM_T *stream )
+{
+ VC_CONTAINER_PACKET_T *packet = stream->current;
+
+ if( !packet )
+ return VC_CONTAINER_ERROR_EOS;
+
+ /* Fast path first */
+ if( packet->size - stream->offset )
+ {
+ stream->offset++;
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ return bytestream_skip( stream, 1 );
+}
+
+STATIC_INLINE VC_CONTAINER_STATUS_T packet_peek_byte( VC_CONTAINER_BYTESTREAM_T *stream,
+ uint8_t *data )
+{
+ VC_CONTAINER_PACKET_T *packet = stream->current;
+
+ if( !packet )
+ return VC_CONTAINER_ERROR_EOS;
+
+ /* Fast path first */
+ if( packet->size - stream->offset )
+ {
+ *data = packet->data[stream->offset];
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ return bytestream_peek( stream, data, 1 );
+}
+
+STATIC_INLINE VC_CONTAINER_STATUS_T packet_get_byte( VC_CONTAINER_BYTESTREAM_T *stream,
+ uint8_t *data )
+{
+ VC_CONTAINER_PACKET_T *packet = stream->current;
+
+ if( !packet )
+ return VC_CONTAINER_ERROR_EOS;
+
+ /* Fast path first */
+ if( packet->size - stream->offset )
+ {
+ *data = packet->data[stream->offset];
+ stream->offset++;
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ return bytestream_get( stream, data, 1 );
+}
+
+STATIC_INLINE VC_CONTAINER_STATUS_T bytestream_find_startcode( VC_CONTAINER_BYTESTREAM_T *stream,
+ size_t *search_offset, const uint8_t *startcode, unsigned int length )
+{
+ VC_CONTAINER_PACKET_T *packet, *backup_packet = NULL;
+ size_t position, start_offset = position = *search_offset;
+ size_t offset, backup_offset = 0;
+ unsigned int match = 0;
+
+ if( stream->bytes - stream->current_offset - stream->offset < start_offset + length )
+ return VC_CONTAINER_ERROR_EOS; /* Not enough data */
+
+ /* Find the right place */
+ for( packet = stream->current, offset = stream->offset;
+ packet != NULL; packet = packet->next, offset = 0 )
+ {
+ if( packet->size - offset > start_offset)
+ break;
+
+ start_offset -= (packet->size - offset);
+ }
+
+ /* Start the search for the start code.
+ * To make things simple we try to find a match one byte at a time. */
+ for( offset += start_offset;
+ packet != NULL; packet = packet->next, offset = 0 )
+ {
+ for( ; offset < packet->size; offset++ )
+ {
+ if( packet->data[offset] != startcode[match] )
+ {
+ if ( match ) /* False positive */
+ {
+ packet = backup_packet;
+ offset = backup_offset;
+ match = 0;
+ }
+ position++;
+ continue;
+ }
+
+ /* We've got a match */
+ if( !match++ )
+ {
+ backup_packet = packet;
+ backup_offset = offset;
+ }
+
+ if( match == length )
+ {
+ /* We have the full start code it */
+ *search_offset = position;
+ return VC_CONTAINER_SUCCESS;
+ }
+ }
+ }
+
+ *search_offset = position;
+ return VC_CONTAINER_ERROR_EOS; /* No luck in finding the start code */
+}
+
+#endif /* VC_CONTAINERS_BYTESTREAM_H */
diff --git a/gfx/include/userland/containers/core/containers_codecs.c b/gfx/include/userland/containers/core/containers_codecs.c
new file mode 100644
index 0000000000..16e1593a54
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_codecs.c
@@ -0,0 +1,209 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+
+#include "containers/containers.h"
+#include "containers/containers_codecs.h"
+#include "containers/core/containers_utils.h"
+
+/*****************************************************************************/
+static struct {
+ VC_CONTAINER_FOURCC_T codec;
+ uint16_t id;
+} codec_to_wf_table[] =
+{
+ {VC_CONTAINER_CODEC_PCM_SIGNED_LE, WAVE_FORMAT_PCM},
+ {VC_CONTAINER_CODEC_ALAW, WAVE_FORMAT_ALAW},
+ {VC_CONTAINER_CODEC_MULAW, WAVE_FORMAT_MULAW},
+ {VC_CONTAINER_CODEC_ADPCM_MS, WAVE_FORMAT_ADPCM},
+ {VC_CONTAINER_CODEC_MPGA, WAVE_FORMAT_MPEG},
+ {VC_CONTAINER_CODEC_MPGA, WAVE_FORMAT_MPEGLAYER3},
+ {VC_CONTAINER_CODEC_WMA1, WAVE_FORMAT_WMAUDIO1},
+ {VC_CONTAINER_CODEC_WMA2, WAVE_FORMAT_WMAUDIO2},
+ {VC_CONTAINER_CODEC_WMAP, WAVE_FORMAT_WMAUDIOPRO},
+ {VC_CONTAINER_CODEC_WMAL, WAVE_FORMAT_WMAUDIO_LOSSLESS},
+ {VC_CONTAINER_CODEC_WMAV, WAVE_FORMAT_WMAUDIO_VOICE},
+ {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_DVM},
+ {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_DOLBY_AC3_SPDIF}, /**< AC-3 padded for S/PDIF */
+ {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_RAW_SPORT}, /**< AC-3 padded for S/PDIF */
+ {VC_CONTAINER_CODEC_AC3, WAVE_FORMAT_ESST_AC3}, /**< AC-3 padded for S/PDIF */
+ {VC_CONTAINER_CODEC_EAC3, WAVE_FORMAT_DVM},
+ {VC_CONTAINER_CODEC_DTS, WAVE_FORMAT_DTS},
+#if 0
+ {CODEC_G726, WAVE_FORMAT_G726_ADPCM},
+ {CODEC_G726, WAVE_FORMAT_DF_G726},
+ {CODEC_G726, WAVE_FORMAT_G726ADPCM},
+ {CODEC_G726, WAVE_FORMAT_PANASONIC_G726},
+#endif
+ {VC_CONTAINER_CODEC_MP4A, WAVE_FORMAT_AAC},
+ {VC_CONTAINER_CODEC_MP4A, WAVE_FORMAT_MP4A},
+ {VC_CONTAINER_CODEC_ATRAC3, WAVE_FORMAT_SONY_SCX},
+ {VC_CONTAINER_CODEC_UNKNOWN, WAVE_FORMAT_UNKNOWN}
+};
+
+VC_CONTAINER_FOURCC_T waveformat_to_codec(uint16_t waveformat_id)
+{
+ unsigned int i;
+ for(i = 0; codec_to_wf_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++)
+ if(codec_to_wf_table[i].id == waveformat_id) break;
+ return codec_to_wf_table[i].codec;
+}
+
+uint16_t codec_to_waveformat(VC_CONTAINER_FOURCC_T codec)
+{
+ unsigned int i;
+ for(i = 0; codec_to_wf_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++)
+ if(codec_to_wf_table[i].codec == codec) break;
+ return codec_to_wf_table[i].id;
+}
+
+static struct {
+ VC_CONTAINER_FOURCC_T codec;
+ uint32_t fourcc;
+} codec_to_vfw_table[] =
+{
+#if defined(ENABLE_CONTAINERS_STANDALONE) || !defined(NDEBUG)
+ /* We are legally required to not play DivX in RELEASE mode. See Jira SW-3138 */
+ {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('D','I','V','3')},
+ {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('d','i','v','3')},
+ {VC_CONTAINER_CODEC_DIV4, VC_FOURCC('D','I','V','4')},
+ {VC_CONTAINER_CODEC_DIV4, VC_FOURCC('d','i','v','4')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('D','X','5','0')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('D','I','V','X')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('d','i','v','x')},
+#endif /* ENABLE_CONTAINERS_STANDALONE || !NDEBUG */
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','V')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','v')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','S')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','s')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','4','S','2')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','4','s','2')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('F','M','P','4')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('X','V','I','D')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('x','v','i','d')},
+ {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('M','P','4','3')},
+ {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('m','p','4','3')},
+ {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('m','p','g','1')},
+ {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('M','P','G','1')},
+ {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('m','p','g','2')},
+ {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('M','P','G','2')},
+ {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('M','J','P','G')},
+ {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('m','j','p','g')},
+ {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('W','M','V','1')},
+ {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('w','m','v','1')},
+ {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('W','M','V','2')},
+ {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('w','m','v','2')},
+ {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('W','M','V','3')},
+ {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('w','m','v','3')},
+ {VC_CONTAINER_CODEC_WVC1, VC_FOURCC('W','V','C','1')},
+ {VC_CONTAINER_CODEC_WVC1, VC_FOURCC('w','v','c','1')},
+ {VC_CONTAINER_CODEC_WMVA, VC_FOURCC('w','m','v','a')},
+ {VC_CONTAINER_CODEC_WMVA, VC_FOURCC('W','M','V','A')},
+ {VC_CONTAINER_CODEC_VP6, VC_FOURCC('V','P','6','F')},
+ {VC_CONTAINER_CODEC_VP6, VC_FOURCC('v','p','6','f')},
+ {VC_CONTAINER_CODEC_VP7, VC_FOURCC('V','P','7','0')},
+ {VC_CONTAINER_CODEC_VP7, VC_FOURCC('v','p','7','0')},
+ {VC_CONTAINER_CODEC_H263, VC_FOURCC('H','2','6','3')},
+ {VC_CONTAINER_CODEC_H263, VC_FOURCC('h','2','6','3')},
+ {VC_CONTAINER_CODEC_H264, VC_FOURCC('H','2','6','4')},
+ {VC_CONTAINER_CODEC_H264, VC_FOURCC('h','2','6','4')},
+ {VC_CONTAINER_CODEC_H264, VC_FOURCC('A','V','C','1')},
+ {VC_CONTAINER_CODEC_H264, VC_FOURCC('a','v','c','1')},
+ {VC_CONTAINER_CODEC_SPARK, VC_FOURCC('F','L','V','1')},
+ {VC_CONTAINER_CODEC_SPARK, VC_FOURCC('f','l','v','1')},
+ {VC_CONTAINER_CODEC_UNKNOWN, 0}
+};
+
+VC_CONTAINER_FOURCC_T vfw_fourcc_to_codec(uint32_t fourcc)
+{
+ unsigned int i;
+ for(i = 0; codec_to_vfw_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++)
+ if(codec_to_vfw_table[i].fourcc == fourcc) break;
+
+ if(codec_to_vfw_table[i].codec == VC_CONTAINER_CODEC_UNKNOWN)
+ return fourcc;
+
+ return codec_to_vfw_table[i].codec;
+}
+
+uint32_t codec_to_vfw_fourcc(VC_CONTAINER_FOURCC_T codec)
+{
+ unsigned int i;
+ for(i = 0; codec_to_vfw_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++)
+ if(codec_to_vfw_table[i].codec == codec) break;
+
+ return codec_to_vfw_table[i].fourcc;
+}
+
+static struct {
+ VC_CONTAINER_FOURCC_T codec;
+ uint32_t fourcc;
+} codec_to_fourcc_table[] =
+{
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','S')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','4','S','2')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','s')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','4','s','2')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('M','P','4','V')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('m','p','4','v')},
+ {VC_CONTAINER_CODEC_MP4V, VC_FOURCC('F','M','P','4')},
+ {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('M','P','4','3')},
+ {VC_CONTAINER_CODEC_DIV3, VC_FOURCC('m','p','4','3')},
+ {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('W','M','V','1')},
+ {VC_CONTAINER_CODEC_WMV1, VC_FOURCC('w','m','v','1')},
+ {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('W','M','V','2')},
+ {VC_CONTAINER_CODEC_WMV2, VC_FOURCC('w','m','v','2')},
+ {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('W','M','V','3')},
+ {VC_CONTAINER_CODEC_WMV3, VC_FOURCC('w','m','v','3')},
+ {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('m','p','g','1')},
+ {VC_CONTAINER_CODEC_MP1V, VC_FOURCC('M','P','G','1')},
+ {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('m','p','g','2')},
+ {VC_CONTAINER_CODEC_MP2V, VC_FOURCC('M','P','G','2')},
+ {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('M','J','P','G')},
+ {VC_CONTAINER_CODEC_MJPEG, VC_FOURCC('m','j','p','g')},
+ {VC_CONTAINER_CODEC_UNKNOWN, 0}
+};
+
+VC_CONTAINER_FOURCC_T fourcc_to_codec(uint32_t fourcc)
+{
+ unsigned int i;
+ for(i = 0; codec_to_fourcc_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++)
+ if(codec_to_fourcc_table[i].fourcc == fourcc) break;
+
+ return codec_to_fourcc_table[i].codec;
+}
+
+uint32_t codec_to_fourcc(VC_CONTAINER_FOURCC_T codec)
+{
+ unsigned int i;
+ for(i = 0; codec_to_fourcc_table[i].codec != VC_CONTAINER_CODEC_UNKNOWN; i++)
+ if(codec_to_fourcc_table[i].codec == codec) break;
+
+ return codec_to_fourcc_table[i].fourcc;
+}
diff --git a/gfx/include/userland/containers/core/containers_common.h b/gfx/include/userland/containers/core/containers_common.h
new file mode 100644
index 0000000000..eb18671c3e
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_common.h
@@ -0,0 +1,73 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_COMMON_H
+#define VC_CONTAINERS_COMMON_H
+
+/** \file containers_common.h
+ * Common definitions for containers infrastructure
+ */
+
+#ifndef ENABLE_CONTAINERS_STANDALONE
+# include "vcos.h"
+# define vc_container_assert(a) vcos_assert(a)
+#else
+# include "assert.h"
+# define vc_container_assert(a) assert(a)
+#endif /* ENABLE_CONTAINERS_STANDALONE */
+
+#ifndef countof
+# define countof(a) (sizeof(a) / sizeof(a[0]))
+#endif
+
+#ifndef MIN
+# define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef MAX
+# define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifdef _MSC_VER
+# define strcasecmp stricmp
+# define strncasecmp strnicmp
+#endif
+
+#define STATIC_INLINE static __inline
+#define VC_CONTAINER_PARAM_UNUSED(a) (void)(a)
+
+#if defined(__HIGHC__) && !defined(strcasecmp)
+# define strcasecmp(a,b) _stricmp(a,b)
+#endif
+
+#if defined(__GNUC__) && (__GNUC__ > 2)
+# define VC_CONTAINER_CONSTRUCTOR(func) void __attribute__((constructor,used)) func(void)
+# define VC_CONTAINER_DESTRUCTOR(func) void __attribute__((destructor,used)) func(void)
+#else
+# define VC_CONTAINER_CONSTRUCTOR(func) void func(void)
+# define VC_CONTAINER_DESTRUCTOR(func) void func(void)
+#endif
+
+#endif /* VC_CONTAINERS_COMMON_H */
diff --git a/gfx/include/userland/containers/core/containers_filters.c b/gfx/include/userland/containers/core/containers_filters.c
new file mode 100644
index 0000000000..a5bc0b7c12
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_filters.c
@@ -0,0 +1,222 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+#include
+#include
+
+#include "containers/containers.h"
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_filters.h"
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE)
+ #include "vcos_dlfcn.h"
+ #define DL_SUFFIX VCOS_SO_EXT
+ #ifndef DL_PATH_PREFIX
+ #define DL_PATH_PREFIX ""
+ #endif
+#endif
+
+typedef struct VC_CONTAINER_FILTER_PRIVATE_T
+{
+ /** Pointer to the container filter code and symbols */
+ void *handle;
+} VC_CONTAINER_FILTER_PRIVATE_T;
+
+typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_FILTER_OPEN_FUNC_T)(VC_CONTAINER_FILTER_T*, VC_CONTAINER_FOURCC_T);
+
+static VC_CONTAINER_FILTER_OPEN_FUNC_T load_library(void **handle, VC_CONTAINER_FOURCC_T filter, const char *name);
+static void unload_library(void *handle);
+
+static struct {
+ VC_CONTAINER_FOURCC_T filter;
+ const char *name;
+} filter_to_name_table[] =
+{
+ {VC_FOURCC('d','r','m',' '), "divx"},
+ {VC_FOURCC('d','r','m',' '), "hdcp2"},
+ {0, NULL}
+};
+
+static VC_CONTAINER_STATUS_T vc_container_filter_load(VC_CONTAINER_FILTER_T *p_ctx,
+ VC_CONTAINER_FOURCC_T filter,
+ VC_CONTAINER_FOURCC_T type)
+{
+ void *handle = NULL;
+ VC_CONTAINER_FILTER_OPEN_FUNC_T func;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ unsigned int i;
+
+ for(i = 0; filter_to_name_table[i].filter; ++i)
+ {
+ if (filter_to_name_table[i].filter == filter)
+ {
+ if ((func = load_library(&handle, filter, filter_to_name_table[i].name)) != NULL)
+ {
+ status = (*func)(p_ctx, type);
+ if(status == VC_CONTAINER_SUCCESS) break;
+ unload_library(handle);
+ if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) break;
+ }
+ }
+ }
+
+ p_ctx->priv->handle = handle;
+ return status;
+}
+
+static void vc_container_filter_unload(VC_CONTAINER_FILTER_T *p_ctx)
+{
+ unload_library(p_ctx->priv->handle);
+ p_ctx->priv->handle = NULL;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_FILTER_T *vc_container_filter_open(VC_CONTAINER_FOURCC_T filter,
+ VC_CONTAINER_FOURCC_T type,
+ VC_CONTAINER_T *p_container,
+ VC_CONTAINER_STATUS_T *p_status )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND;
+ VC_CONTAINER_FILTER_T *p_ctx = 0;
+ VC_CONTAINER_FILTER_PRIVATE_T *priv = 0;
+
+ /* Allocate our context before trying out the different filter modules */
+ p_ctx = malloc(sizeof(*p_ctx) + sizeof(*priv));
+ if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*priv));
+ p_ctx->priv = priv = (VC_CONTAINER_FILTER_PRIVATE_T *)&p_ctx[1];
+ p_ctx->container = p_container;
+
+ status = vc_container_filter_load(p_ctx, filter, type);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ end:
+ if(p_status) *p_status = status;
+ return p_ctx;
+
+ error:
+ if(p_ctx) free(p_ctx);
+ p_ctx = 0;
+ goto end;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_filter_close( VC_CONTAINER_FILTER_T *p_ctx )
+{
+ if (p_ctx)
+ {
+ if(p_ctx->pf_close) p_ctx->pf_close(p_ctx);
+ if(p_ctx->priv && p_ctx->priv->handle) vc_container_filter_unload(p_ctx);
+ free(p_ctx);
+ }
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_filter_process( VC_CONTAINER_FILTER_T *p_ctx, VC_CONTAINER_PACKET_T *p_packet )
+{
+ VC_CONTAINER_STATUS_T status;
+ status = p_ctx->pf_process(p_ctx, p_packet);
+ return status;
+}
+
+VC_CONTAINER_STATUS_T vc_container_filter_control(VC_CONTAINER_FILTER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, ... )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ va_list args;
+
+ va_start( args, operation );
+ if(p_ctx->pf_control)
+ status = p_ctx->pf_control(p_ctx, operation, args);
+ va_end( args );
+
+ return status;
+}
+
+static VC_CONTAINER_FILTER_OPEN_FUNC_T load_library(void **handle, VC_CONTAINER_FOURCC_T filter, const char *name)
+{
+ VC_CONTAINER_FILTER_OPEN_FUNC_T func = NULL;
+#ifdef ENABLE_CONTAINERS_STANDALONE
+ VC_CONTAINER_PARAM_UNUSED(handle);
+ VC_CONTAINER_PARAM_UNUSED(filter);
+ VC_CONTAINER_PARAM_UNUSED(name);
+#else
+ char *dl_name, *entrypt_name;
+ const char *entrypt_name_short = "filter_open";
+ char filter_[6], *ptr;
+ void *dl_handle;
+ int dl_name_len;
+ int entrypt_name_len;
+
+ snprintf(filter_, sizeof(filter_), "%4.4s", (const char*)&filter);
+ ptr = strchr(filter_, '\0');
+ while (ptr > filter_ && isspace(*--ptr)) *ptr = '\0';
+ strncat(filter_, "_", 1);
+
+ dl_name_len = strlen(DL_PATH_PREFIX) + strlen(filter_) + strlen(name) + strlen(DL_SUFFIX) + 1;
+ dl_name = malloc(dl_name_len);
+ if (!dl_name) return NULL;
+
+ entrypt_name_len = strlen(name) + 1 + strlen(filter_) + strlen(entrypt_name_short) + 1;
+ entrypt_name = malloc(entrypt_name_len);
+ if (!entrypt_name)
+ {
+ free(dl_name);
+ return NULL;
+ }
+
+ snprintf(dl_name, dl_name_len, "%s%s%s%s", DL_PATH_PREFIX, filter_, name, DL_SUFFIX);
+ snprintf(entrypt_name, entrypt_name_len, "%s_%s%s", name, filter_, entrypt_name_short);
+
+ if ((dl_handle = vcos_dlopen(dl_name, VCOS_DL_NOW)) != NULL)
+ {
+ /* Try generic entrypoint name before the mangled, full name */
+ func = (VC_CONTAINER_FILTER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name_short);
+ if (!func) func = (VC_CONTAINER_FILTER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name);
+ /* Only return handle if symbol found */
+ if (func)
+ *handle = dl_handle;
+ else
+ vcos_dlclose(dl_handle);
+ }
+
+ free(dl_name);
+ free(entrypt_name);
+#endif
+ return func;
+}
+
+static void unload_library(void *handle)
+{
+#ifdef ENABLE_CONTAINERS_STANDALONE
+ VC_CONTAINER_PARAM_UNUSED(handle);
+#else
+ vcos_dlclose(handle);
+#endif
+}
diff --git a/gfx/include/userland/containers/core/containers_filters.h b/gfx/include/userland/containers/core/containers_filters.h
new file mode 100644
index 0000000000..9026274e4f
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_filters.h
@@ -0,0 +1,110 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_FILTERS_H
+#define VC_CONTAINERS_FILTERS_H
+
+/** \file containers_filters.h
+ * Interface definition for the filter abstraction used by the container
+ * common layer */
+#include
+
+#include "containers/containers.h"
+
+/** \defgroup VcContainerFilterApi Container Filter API */
+/* @{ */
+
+/** Container Filter Context.
+ * This structure defines the context for a container filter instance */
+typedef struct VC_CONTAINER_FILTER_T
+{
+ /** Pointer to container instance */
+ struct VC_CONTAINER_T *container;
+ /** Pointer to information private to the container filter instance */
+ struct VC_CONTAINER_FILTER_PRIVATE_T *priv;
+ /** Pointer to information private to the container filter module */
+ struct VC_CONTAINER_FILTER_MODULE_T *module;
+
+ /** \note the following list of function pointers should not be used directly.
+ * They defines the interface for implementing container filter modules and are
+ * filled in by the container filter modules themselves. */
+
+ /** \private
+ * Function pointer to close and free all resources allocated by a
+ * container filter module */
+ VC_CONTAINER_STATUS_T (*pf_close)(struct VC_CONTAINER_FILTER_T *filter);
+
+ /** \private
+ * Function pointer to filter a data packet using a container filter module */
+ VC_CONTAINER_STATUS_T (*pf_process)(struct VC_CONTAINER_FILTER_T *filter, VC_CONTAINER_PACKET_T *p_packet);
+
+ /** \private
+ * Function pointer to control container filter module */
+ VC_CONTAINER_STATUS_T (*pf_control)( struct VC_CONTAINER_FILTER_T *filter, VC_CONTAINER_CONTROL_T operation, va_list args );
+
+} VC_CONTAINER_FILTER_T;
+
+/** Opens a container filter using a four character code describing the filter.
+ * This will create an instance of the container filter.
+ *
+ * \param filter Four Character Code describing the filter
+ * \param type Four Character Code describing the subtype - indicated whether filter is encrypt or decrypt
+ * \param container Pointer to the container instance
+ * \param status Returns the status of the operation
+ * \return If successful, this returns a pointer to the new instance
+ * of the container filter. Returns NULL on failure.
+ */
+VC_CONTAINER_FILTER_T *vc_container_filter_open(VC_CONTAINER_FOURCC_T filter,
+ VC_CONTAINER_FOURCC_T type,
+ VC_CONTAINER_T *container,
+ VC_CONTAINER_STATUS_T *status );
+
+/** Closes an instance of a container filter.
+ * \param context Pointer to the VC_CONTAINER_FILTER_T context of the instance to close
+ * \return VC_CONTAINER_SUCCESS on success.
+ */
+VC_CONTAINER_STATUS_T vc_container_filter_close( VC_CONTAINER_FILTER_T *context );
+
+/** Filter a data packet using a container filter module.
+ * \param context Pointer to the VC_CONTAINER_FILTER_T instance to use
+ * \param packet Pointer to the VC_CONTAINER_PACKET_T structure to process
+ * \return the status of the operation
+ */
+VC_CONTAINER_STATUS_T vc_container_filter_process(VC_CONTAINER_FILTER_T *context, VC_CONTAINER_PACKET_T *p_packet);
+
+/** Extensible control function for container filter modules.
+* This function takes a variable number of arguments which will depend on the specific operation.
+*
+* \param context Pointer to the VC_CONTAINER_FILTER_T instance to use
+* \param operation The requested operation
+* \param args Arguments for the operation
+* \return the status of the operation
+*/
+VC_CONTAINER_STATUS_T vc_container_filter_control(VC_CONTAINER_FILTER_T *context, VC_CONTAINER_CONTROL_T operation, ... );
+
+/* @} */
+
+#endif /* VC_CONTAINERS_FILTERS_H */
diff --git a/gfx/include/userland/containers/core/containers_index.c b/gfx/include/userland/containers/core/containers_index.c
new file mode 100644
index 0000000000..7edebfa20f
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_index.c
@@ -0,0 +1,192 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+
+#include "containers/containers.h"
+#include "containers/core/containers_index.h"
+
+typedef struct {
+ int64_t file_offset;
+ int64_t time;
+} VC_CONTAINER_INDEX_POS_T;
+
+struct VC_CONTAINER_INDEX_T {
+ int len; // log2 of length of entry array
+ int next; // next array entry to write into
+ int gap; // log2 of the passes through entry array to build the full list
+ int mgap; // len-gap, stored for convenience
+ int count; // number of calls to index_add since last entry added
+ int max_count; // log2 of the number of calls to discard between each entry added
+ int64_t max_time; // time of the latest entry
+ VC_CONTAINER_INDEX_POS_T entry[0]; // array of position/time pairs
+};
+
+// We have a fixed length list, and when it is full we want to discard half the entries.
+// This is done without coping data by mapping the entry number to the index to the array,
+// in the following way:
+// Length is a power of two, so the entry number is a fixed constant bit width. The highest gap
+// bits are used as a direct offset into the array (o), the lowest mgap bits are right shifted by
+// gap to increment this (S). Each time we double the number of passes through the actual array.
+// So if len=3, we start off with mgap=3, gap=0, we have a single pass with the trivial mapping:
+// |S|S|S| [0 1 2 3 4 5 6 7]
+// when this is full we change to mgap=2, gap=1, so we iterate this way:
+// |o|S|S| [0 2 4 6] [1 3 5 7]
+// when this is full we change to mgap=1, gap=2
+// |o|o|S| [0 4] [1 5] [2 6] [3 7]
+// when this is full we change to this, which is equivalent to where we started
+// |o|o|o| [0] [1] [2] [3] [4] [5] [6] [7]
+
+#define ENTRY(x, i) ((x)->gap == 0 ? (i) : ((i)>>(x)->mgap) + (((i) & ((1<<(x)->mgap)-1)) << (x)->gap))
+
+VC_CONTAINER_STATUS_T vc_container_index_create( VC_CONTAINER_INDEX_T **index, int length )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ VC_CONTAINER_INDEX_T *id = NULL;
+ int len = 0;
+
+ if(length < 16) length = 16;
+ if(length > 4096) length = 4096;
+ while((length >>= 1) != 0)
+ len++;
+
+ id = malloc(sizeof(VC_CONTAINER_INDEX_T) + (sizeof(VC_CONTAINER_INDEX_POS_T)<len = id->mgap = len;
+
+ *index = id;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ return status;
+}
+
+VC_CONTAINER_STATUS_T vc_container_index_free( VC_CONTAINER_INDEX_T *index )
+{
+ if(index == NULL)
+ return VC_CONTAINER_ERROR_FAILED;
+
+ free(index);
+ return VC_CONTAINER_SUCCESS;
+}
+
+VC_CONTAINER_STATUS_T vc_container_index_add( VC_CONTAINER_INDEX_T *index, int64_t time, int64_t file_offset )
+{
+ if(index == NULL)
+ return VC_CONTAINER_ERROR_FAILED;
+
+ // reject entries if they are in part of the time covered
+ if(index->next != 0 && time <= index->max_time)
+ return VC_CONTAINER_SUCCESS;
+
+ index->count++;
+ if(index->count == (1<max_count))
+ {
+ int entry;
+ if(index->next == (1<len))
+ {
+ // New entry doesn't fit, we discard every other index record
+ // by changing how we map index positions to array entry indexes.
+ index->next >>= 1;
+ index->gap++;
+ index->mgap--;
+ index->max_count++;
+
+ if(index->gap == index->len)
+ {
+ index->gap = 0;
+ index->mgap = index->len;
+ }
+ }
+
+ entry = ENTRY(index, index->next);
+
+ index->entry[entry].file_offset = file_offset;
+ index->entry[entry].time = time;
+ index->count = 0;
+ index->next++;
+ index->max_time = time;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+VC_CONTAINER_STATUS_T vc_container_index_get( VC_CONTAINER_INDEX_T *index, int later, int64_t *time, int64_t *file_offset, int *past )
+{
+ int guess, start, end, entry;
+
+ if(index == NULL || index->next == 0)
+ return VC_CONTAINER_ERROR_FAILED;
+
+ guess = start = 0;
+ end = index->next-1;
+
+ *past = *time > index->max_time;
+
+ while(end-start > 1)
+ {
+ int64_t gtime;
+ guess = (start+end)>>1;
+ gtime = index->entry[ENTRY(index, guess)].time;
+
+ if(*time < gtime)
+ end = guess;
+ else if(*time > gtime)
+ start = guess;
+ else
+ break;
+ }
+
+ if (*time != index->entry[ENTRY(index, guess)].time)
+ {
+ if(later)
+ {
+ if(*time <= index->entry[ENTRY(index, start)].time)
+ guess = start;
+ else
+ guess = end;
+ }
+ else
+ {
+ if(*time >= index->entry[ENTRY(index, end)].time)
+ guess = end;
+ else
+ guess = start;
+ }
+ }
+
+ entry = ENTRY(index, guess);
+ *time = index->entry[entry].time;
+ *file_offset = index->entry[entry].file_offset;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
diff --git a/gfx/include/userland/containers/core/containers_index.h b/gfx/include/userland/containers/core/containers_index.h
new file mode 100644
index 0000000000..f0c49c3d92
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_index.h
@@ -0,0 +1,82 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_INDEX_H
+#define VC_CONTAINERS_INDEX_H
+
+/** \file containers_index.h
+ * Definition of index utilitie for containers. Creates and maintains an
+ * index of file offsets and times, and is able to suggest a file position
+ * to seek to achieve a given time target. Useful for container formats
+ * that don't include an index.
+ */
+
+#include "containers/containers.h"
+
+struct VC_CONTAINER_INDEX_T;
+typedef struct VC_CONTAINER_INDEX_T VC_CONTAINER_INDEX_T;
+
+/**
+ * Creates an index with a suggested number of entries.
+ * @param index Pointer to created index will be filled here on success.
+ * @param length Suggested length of index.
+ * @return Status code
+ */
+VC_CONTAINER_STATUS_T vc_container_index_create( VC_CONTAINER_INDEX_T **index, int length );
+
+
+/**
+ * Frees an index.
+ * @param index Pointer to valid index.
+ * @return Status code.
+ */
+VC_CONTAINER_STATUS_T vc_container_index_free( VC_CONTAINER_INDEX_T *index );
+
+
+/**
+ * Adds an entry to the index. If the index is full then some stored records will be
+ * discarded.
+ * @param index Pointer to a valid index.
+ * @param time Timestamp of new index entry.
+ * @param file_offset File offset for new index entry.
+ * @return Status code
+ */
+VC_CONTAINER_STATUS_T vc_container_index_add( VC_CONTAINER_INDEX_T *index, int64_t time, int64_t file_offset );
+
+
+/**
+ * Retrieves the best entry for the supplied time offset.
+ * @param index Pointer to valid index.
+ * @param later If true, the selected entry is the earliest retained entry with a greater or equal timestamp.
+ * If false, the selected entry is the latest retained entry with an earlier or equal timestamp.
+ * @param time The requested time. On success, this is filled in with the time of the selected entry.
+ * @param file_offset On success, this is filled in with the file offset of the selected entry.
+ * @param past Set if the requested time is after the last entry in the index.
+ * @return Status code.
+ */
+VC_CONTAINER_STATUS_T vc_container_index_get( VC_CONTAINER_INDEX_T *index, int later, int64_t *time, int64_t *file_offset, int *past );
+
+#endif /* VC_CONTAINERS_WRITER_UTILS_H */
diff --git a/gfx/include/userland/containers/core/containers_io.c b/gfx/include/userland/containers/core/containers_io.c
new file mode 100644
index 0000000000..907a2a06d4
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_io.c
@@ -0,0 +1,1088 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+#include
+
+#include "containers/containers.h"
+#include "containers/core/containers_io.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_uri.h"
+
+#define MAX_NUM_CACHED_AREAS 16
+#define MAX_NUM_MEMORY_AREAS 4
+#define NUM_TMP_MEMORY_AREAS 2
+#define MEM_CACHE_READ_MAX_SIZE (32*1024) /* Needs to be a power of 2 */
+#define MEM_CACHE_WRITE_MAX_SIZE (128*1024) /* Needs to be a power of 2 */
+#define MEM_CACHE_TMP_MAX_SIZE (32*1024) /* Needs to be a power of 2 */
+#define MEM_CACHE_ALIGNMENT (1*1024) /* Needs to be a power of 2 */
+#define MEM_CACHE_AREA_READ_MAX_SIZE (4*1024*1024) /* Needs to be a power of 2 */
+
+typedef struct VC_CONTAINER_IO_PRIVATE_CACHE_T
+{
+ int64_t start; /**< Offset to the start of the cached area in the stream */
+ int64_t end; /**< Offset to the end of the cached area in the stream */
+
+ int64_t offset; /**< Offset of the currently cached data in the stream */
+ size_t size; /**< Size of the cached area */
+ bool dirty; /**< Whether the cache is dirty and needs to be written back */
+
+ size_t position; /**< Current position in the cache */
+
+ uint8_t *buffer; /**< Pointer to the start of the valid cache area */
+ uint8_t *buffer_end; /**< Pointer to the end of the cache */
+
+ unsigned int mem_max_size; /**< Maximum size of the memory cache */
+ unsigned int mem_size; /**< Size of the memory cache */
+ uint8_t *mem; /**< Pointer to the memory cache */
+
+ VC_CONTAINER_IO_T *io;
+
+} VC_CONTAINER_IO_PRIVATE_CACHE_T;
+
+typedef struct VC_CONTAINER_IO_PRIVATE_T
+{
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache; /**< Current cache */
+
+ unsigned int caches_num;
+ VC_CONTAINER_IO_PRIVATE_CACHE_T caches;
+
+ unsigned int cached_areas_num;
+ VC_CONTAINER_IO_PRIVATE_CACHE_T cached_areas[MAX_NUM_CACHED_AREAS];
+
+ int64_t actual_offset;
+
+ struct VC_CONTAINER_IO_ASYNC_T *async_io;
+
+} VC_CONTAINER_IO_PRIVATE_T;
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_io_file_open( VC_CONTAINER_IO_T *p_ctx, const char *uri,
+ VC_CONTAINER_IO_MODE_T mode );
+VC_CONTAINER_STATUS_T vc_container_io_null_open( VC_CONTAINER_IO_T *p_ctx, const char *uri,
+ VC_CONTAINER_IO_MODE_T mode );
+VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *p_ctx, const char *uri,
+ VC_CONTAINER_IO_MODE_T mode );
+VC_CONTAINER_STATUS_T vc_container_io_pktfile_open( VC_CONTAINER_IO_T *p_ctx, const char *uri,
+ VC_CONTAINER_IO_MODE_T mode );
+VC_CONTAINER_STATUS_T vc_container_io_http_open( VC_CONTAINER_IO_T *p_ctx, const char *uri,
+ VC_CONTAINER_IO_MODE_T mode );
+static VC_CONTAINER_STATUS_T io_seek_not_seekable(VC_CONTAINER_IO_T *p_ctx, int64_t offset);
+
+static size_t vc_container_io_cache_read( VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, uint8_t *data, size_t size );
+static int32_t vc_container_io_cache_write( VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, const uint8_t *data, size_t size );
+static VC_CONTAINER_STATUS_T vc_container_io_cache_seek( VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int64_t offset );
+static size_t vc_container_io_cache_refill( VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache );
+static size_t vc_container_io_cache_flush( VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete );
+
+static struct VC_CONTAINER_IO_ASYNC_T *async_io_start( VC_CONTAINER_IO_T *io, int num_areas, VC_CONTAINER_STATUS_T * );
+static VC_CONTAINER_STATUS_T async_io_stop( struct VC_CONTAINER_IO_ASYNC_T *ctx );
+static int async_io_write( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache );
+static VC_CONTAINER_STATUS_T async_io_wait_complete( struct VC_CONTAINER_IO_ASYNC_T *ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete );
+static void async_io_stats_initialise( struct VC_CONTAINER_IO_ASYNC_T *ctx, int enable );
+static void async_io_stats_get( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_WRITE_STATS_T *stats );
+
+/*****************************************************************************/
+static VC_CONTAINER_IO_T *vc_container_io_open_core( const char *uri, VC_CONTAINER_IO_MODE_T mode,
+ VC_CONTAINER_IO_CAPABILITIES_T capabilities,
+ bool b_open, VC_CONTAINER_STATUS_T *p_status )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_IO_T *p_ctx = 0;
+ VC_CONTAINER_IO_PRIVATE_T *private = 0;
+ unsigned int uri_length, caches = 0, cache_max_size, num_areas = MAX_NUM_MEMORY_AREAS;
+
+ /* XXX */
+ uri_length = strlen(uri) + 1;
+
+ /* Allocate our context before trying out the different io modules */
+ p_ctx = malloc( sizeof(*p_ctx) + sizeof(*private) + uri_length);
+ if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*private) + uri_length );
+ p_ctx->priv = private = (VC_CONTAINER_IO_PRIVATE_T *)&p_ctx[1];
+ p_ctx->uri = (char *)&private[1];
+ memcpy((char *)p_ctx->uri, uri, uri_length);
+ p_ctx->uri_parts = vc_uri_create();
+ if(!p_ctx->uri_parts) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ vc_uri_parse(p_ctx->uri_parts, uri);
+
+ if (b_open)
+ {
+ /* Open the actual i/o module */
+ status = vc_container_io_null_open(p_ctx, uri, mode);
+ if(status) status = vc_container_io_net_open(p_ctx, uri, mode);
+ if(status) status = vc_container_io_pktfile_open(p_ctx, uri, mode);
+#ifdef ENABLE_CONTAINER_IO_HTTP
+ if(status) status = vc_container_io_http_open(p_ctx, uri, mode);
+#endif
+ if(status) status = vc_container_io_file_open(p_ctx, uri, mode);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ if(!p_ctx->pf_seek || (p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK))
+ {
+ p_ctx->capabilities |= VC_CONTAINER_IO_CAPS_CANT_SEEK;
+ p_ctx->pf_seek = io_seek_not_seekable;
+ }
+ }
+ else
+ {
+ /* We're only creating an empty container i/o */
+ p_ctx->capabilities = capabilities;
+ }
+
+ if(p_ctx->capabilities & VC_CONTAINER_IO_CAPS_NO_CACHING)
+ caches = 1;
+
+ if(mode == VC_CONTAINER_IO_MODE_WRITE) cache_max_size = MEM_CACHE_WRITE_MAX_SIZE;
+ else cache_max_size = MEM_CACHE_READ_MAX_SIZE;
+
+ if(mode == VC_CONTAINER_IO_MODE_WRITE &&
+ vc_uri_path_extension(p_ctx->uri_parts) &&
+ !strcasecmp(vc_uri_path_extension(p_ctx->uri_parts), "tmp"))
+ {
+ caches = 1;
+ cache_max_size = MEM_CACHE_TMP_MAX_SIZE;
+ num_areas = NUM_TMP_MEMORY_AREAS;
+ }
+
+ /* Check if the I/O needs caching */
+ if(caches)
+ {
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache = &p_ctx->priv->caches;
+ cache->mem_max_size = cache_max_size;
+ cache->mem_size = cache->mem_max_size;
+ cache->io = p_ctx;
+ cache->mem = malloc(p_ctx->priv->caches.mem_size);
+ if(cache->mem)
+ {
+ cache->buffer = cache->mem;
+ cache->buffer_end = cache->mem + cache->mem_size;
+ p_ctx->priv->caches_num = 1;
+ }
+ }
+
+ if(p_ctx->priv->caches_num)
+ p_ctx->priv->cache = &p_ctx->priv->caches;
+
+
+ /* Try to start an asynchronous io if we're in write mode and we've got at least 2 cache memory areas */
+ if(mode == VC_CONTAINER_IO_MODE_WRITE && p_ctx->priv->cache && num_areas >= 2)
+ p_ctx->priv->async_io = async_io_start( p_ctx, num_areas, 0 );
+
+ end:
+ if(p_status) *p_status = status;
+ return p_ctx;
+
+ error:
+ if(p_ctx) vc_uri_release(p_ctx->uri_parts);
+ if(p_ctx) free(p_ctx);
+ p_ctx = 0;
+ goto end;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_IO_T *vc_container_io_open( const char *uri, VC_CONTAINER_IO_MODE_T mode,
+ VC_CONTAINER_STATUS_T *p_status )
+{
+ return vc_container_io_open_core( uri, mode, 0, true, p_status );
+}
+
+/*****************************************************************************/
+VC_CONTAINER_IO_T *vc_container_io_create( const char *uri, VC_CONTAINER_IO_MODE_T mode,
+ VC_CONTAINER_IO_CAPABILITIES_T capabilities,
+ VC_CONTAINER_STATUS_T *p_status )
+{
+ return vc_container_io_open_core( uri, mode, capabilities, false, p_status );
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_io_close( VC_CONTAINER_IO_T *p_ctx )
+{
+ unsigned int i;
+
+ if(p_ctx)
+ {
+ if(p_ctx->priv)
+ {
+ if(p_ctx->priv->caches_num)
+ {
+ if(p_ctx->priv->caches.dirty)
+ vc_container_io_cache_flush( p_ctx, &p_ctx->priv->caches, 1 );
+ }
+
+ if(p_ctx->priv->async_io)
+ async_io_stop( p_ctx->priv->async_io );
+ else if(p_ctx->priv->caches_num)
+ free(p_ctx->priv->caches.mem);
+
+ for(i = 0; i < p_ctx->priv->cached_areas_num; i++)
+ free(p_ctx->priv->cached_areas[i].mem);
+
+ if(p_ctx->pf_close)
+ p_ctx->pf_close(p_ctx);
+ }
+ vc_uri_release(p_ctx->uri_parts);
+ free(p_ctx);
+ }
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+size_t vc_container_io_peek(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size)
+{
+ size_t ret;
+
+ if(p_ctx->priv->cache)
+ {
+ /* FIXME: do something a bit more clever than this */
+ int64_t offset = p_ctx->offset;
+ ret = vc_container_io_read(p_ctx, buffer, size);
+ vc_container_io_seek(p_ctx, offset);
+ return ret;
+ }
+
+ if (p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK)
+ return 0;
+
+ ret = p_ctx->pf_read(p_ctx, buffer, size);
+ p_ctx->pf_seek(p_ctx, p_ctx->offset);
+ return ret;
+}
+
+/*****************************************************************************/
+size_t vc_container_io_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size)
+{
+ size_t ret;
+
+ if(p_ctx->priv->cache)
+ ret = vc_container_io_cache_read( p_ctx, p_ctx->priv->cache, (uint8_t*)buffer, size );
+ else
+ {
+ ret = p_ctx->pf_read(p_ctx, buffer, size);
+ p_ctx->priv->actual_offset += ret;
+ }
+
+ p_ctx->offset += ret;
+ return ret;
+}
+
+/*****************************************************************************/
+size_t vc_container_io_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size)
+{
+ int32_t ret;
+
+ if(p_ctx->priv->cache)
+ ret = vc_container_io_cache_write( p_ctx, p_ctx->priv->cache, (const uint8_t*)buffer, size );
+ else
+ {
+ ret = p_ctx->pf_write(p_ctx, buffer, size);
+ p_ctx->priv->actual_offset += ret;
+ }
+
+ p_ctx->offset += ret;
+ return ret < 0 ? 0 : ret;
+}
+
+/*****************************************************************************/
+size_t vc_container_io_skip(VC_CONTAINER_IO_T *p_ctx, size_t size)
+{
+ if(!size) return 0;
+
+ if(size < 8)
+ {
+ uint8_t value[8];
+ return vc_container_io_read(p_ctx, value, size);
+ }
+
+ if(p_ctx->priv->cache)
+ {
+ if(vc_container_io_cache_seek(p_ctx, p_ctx->priv->cache, p_ctx->offset + size)) return 0;
+ p_ctx->offset += size;
+ return size;
+ }
+
+ if(vc_container_io_seek(p_ctx, p_ctx->offset + size)) return 0;
+ return size;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_io_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset)
+{
+ VC_CONTAINER_STATUS_T status;
+ unsigned int i;
+
+ /* Check if the requested position is in one of the cached areas */
+ for(i = 0; i < p_ctx->priv->cached_areas_num; i++)
+ {
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache = &p_ctx->priv->cached_areas[i];
+ if(offset >= cache->start && offset < cache->end)
+ {
+ p_ctx->priv->cache = cache;
+ break;
+ }
+ }
+ if(i == p_ctx->priv->cached_areas_num)
+ p_ctx->priv->cache = p_ctx->priv->caches_num ? &p_ctx->priv->caches : 0;
+
+ if(p_ctx->priv->cache)
+ {
+ status = vc_container_io_cache_seek( p_ctx, p_ctx->priv->cache, offset );
+ if(status == VC_CONTAINER_SUCCESS) p_ctx->offset = offset;
+ return status;
+ }
+
+ if(p_ctx->status == VC_CONTAINER_SUCCESS &&
+ offset == p_ctx->offset) return VC_CONTAINER_SUCCESS;
+
+ status = p_ctx->pf_seek(p_ctx, offset);
+ if(status == VC_CONTAINER_SUCCESS) p_ctx->offset = offset;
+ p_ctx->priv->actual_offset = p_ctx->offset;
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_seek_not_seekable(VC_CONTAINER_IO_T *p_ctx, int64_t offset)
+{
+ VC_CONTAINER_IO_PRIVATE_T *private = p_ctx->priv;
+
+ vc_container_assert(offset >= private->actual_offset);
+ if(offset == private->actual_offset) return VC_CONTAINER_SUCCESS;
+
+ if(offset < private->actual_offset)
+ {
+ p_ctx->status = VC_CONTAINER_ERROR_EOS;
+ return p_ctx->status;
+ }
+
+ offset -= private->actual_offset;
+ while(offset && !p_ctx->status)
+ {
+ uint8_t value[64];
+ unsigned int ret, size = MIN(offset, 64);
+ ret = p_ctx->pf_read(p_ctx, value, size);
+ if(ret != size) p_ctx->status = VC_CONTAINER_ERROR_EOS;
+ offset -= ret;
+ }
+ return p_ctx->status;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_io_control_list(VC_CONTAINER_IO_T *context, VC_CONTAINER_CONTROL_T operation, va_list args)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ if (context->pf_control)
+ status = context->pf_control(context, operation, args);
+
+ /* Option to add generic I/O control here */
+
+ if(operation == VC_CONTAINER_CONTROL_IO_FLUSH && context->priv->cache)
+ {
+ status = VC_CONTAINER_SUCCESS;
+ (void)vc_container_io_cache_flush( context, context->priv->cache, 1 );
+ }
+
+ if(operation == VC_CONTAINER_CONTROL_SET_IO_PERF_STATS && context->priv->async_io)
+ {
+ status = VC_CONTAINER_SUCCESS;
+ async_io_stats_initialise(context->priv->async_io, va_arg(args, int));
+ }
+
+ if(operation == VC_CONTAINER_CONTROL_GET_IO_PERF_STATS && context->priv->async_io)
+ {
+ status = VC_CONTAINER_SUCCESS;
+ async_io_stats_get(context->priv->async_io, va_arg(args, VC_CONTAINER_WRITE_STATS_T *));
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_io_control(VC_CONTAINER_IO_T *context, VC_CONTAINER_CONTROL_T operation, ...)
+{
+ VC_CONTAINER_STATUS_T result;
+ va_list args;
+
+ va_start(args, operation);
+ result = vc_container_io_control_list(context, operation, args);
+ va_end(args);
+
+ return result;
+}
+
+/*****************************************************************************/
+size_t vc_container_io_cache(VC_CONTAINER_IO_T *p_ctx, size_t size)
+{
+ VC_CONTAINER_IO_PRIVATE_T *private = p_ctx->priv;
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, *main_cache;
+ VC_CONTAINER_STATUS_T status;
+
+ /* Sanity checking */
+ if(private->cached_areas_num >= MAX_NUM_CACHED_AREAS) return 0;
+
+ cache = &private->cached_areas[private->cached_areas_num];
+ cache->start = p_ctx->offset;
+ cache->end = cache->start + size;
+ cache->offset = p_ctx->offset;
+ cache->position = 0;
+ cache->size = 0;
+ cache->io = p_ctx;
+
+ /* Set the size of the cache area depending on the capabilities of the i/o */
+ if(p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK)
+ cache->mem_max_size = MEM_CACHE_AREA_READ_MAX_SIZE;
+ else if((p_ctx->capabilities & VC_CONTAINER_IO_CAPS_SEEK_SLOW) &&
+ size <= MEM_CACHE_AREA_READ_MAX_SIZE)
+ cache->mem_max_size = MEM_CACHE_AREA_READ_MAX_SIZE;
+ else
+ cache->mem_max_size = MEM_CACHE_READ_MAX_SIZE;
+
+ cache->mem_size = size;
+ if(cache->mem_size > cache->mem_max_size) cache->mem_size = cache->mem_max_size;
+ cache->mem = malloc(cache->mem_size);
+
+ cache->buffer = cache->mem;
+ cache->buffer_end = cache->mem + cache->mem_size;
+
+ if(!cache->mem) return 0;
+ private->cached_areas_num++;
+
+ /* Copy any data we've got in the current cache into the new cache */
+ main_cache = p_ctx->priv->cache;
+ if(main_cache && main_cache->position < main_cache->size)
+ {
+ cache->size = main_cache->size - main_cache->position;
+ if(cache->size > cache->mem_size) cache->size = cache->mem_size;
+ memcpy(cache->buffer, main_cache->buffer + main_cache->position, cache->size);
+ main_cache->position += cache->size;
+ }
+
+ /* Read the rest of the cache directly from the stream */
+ if(cache->mem_size > cache->size)
+ {
+ size_t ret = cache->io->pf_read(cache->io, cache->buffer + cache->size,
+ cache->mem_size - cache->size);
+ cache->size += ret;
+ cache->io->priv->actual_offset = cache->offset + cache->size;
+ }
+
+ status = vc_container_io_seek(p_ctx, cache->end);
+ if(status != VC_CONTAINER_SUCCESS)
+ return 0;
+
+ if(p_ctx->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK)
+ return cache->size;
+ else
+ return size;
+}
+
+/*****************************************************************************/
+static size_t vc_container_io_cache_refill( VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache )
+{
+ size_t ret = vc_container_io_cache_flush( p_ctx, cache, 1 );
+
+ if(ret) return 0; /* TODO what should we do there ? */
+
+ if(p_ctx->priv->actual_offset != cache->offset)
+ {
+ if(cache->io->pf_seek(cache->io, cache->offset) != VC_CONTAINER_SUCCESS)
+ return 0;
+ }
+
+ ret = cache->io->pf_read(cache->io, cache->buffer, cache->buffer_end - cache->buffer);
+ cache->size = ret;
+ cache->position = 0;
+ cache->io->priv->actual_offset = cache->offset + ret;
+ return ret;
+}
+
+/*****************************************************************************/
+static size_t vc_container_io_cache_refill_bypass( VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, uint8_t *buffer, size_t size )
+{
+ size_t ret = vc_container_io_cache_flush( p_ctx, cache, 1 );
+
+ if(ret) return 0; /* TODO what should we do there ? */
+
+ if(p_ctx->priv->actual_offset != cache->offset)
+ {
+ if(cache->io->pf_seek(cache->io, cache->offset) != VC_CONTAINER_SUCCESS)
+ return 0;
+ }
+
+ ret = cache->io->pf_read(cache->io, buffer, size);
+ cache->size = cache->position = 0;
+ cache->offset += ret;
+ cache->io->priv->actual_offset = cache->offset;
+ return ret;
+}
+
+/*****************************************************************************/
+static size_t vc_container_io_cache_read( VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, uint8_t *data, size_t size )
+{
+ size_t read = 0, bytes, ret;
+
+ while(size)
+ {
+ bytes = cache->size - cache->position; /* Bytes left in cache */
+
+#if 1 // FIXME Only if stream is seekable
+ /* Try to read directly from the stream if the cache just gets in the way */
+ if(!bytes && size > cache->mem_size)
+ {
+ bytes = cache->mem_size;
+ ret = vc_container_io_cache_refill_bypass( p_ctx, cache, data + read, bytes);
+ read += ret;
+
+ if(ret != bytes) /* We didn't read as many bytes as we had hoped */
+ goto end;
+
+ size -= bytes;
+ continue;
+ }
+#endif
+
+ /* Refill the cache if it is empty */
+ if(!bytes) bytes = vc_container_io_cache_refill( p_ctx, cache );
+ if(!bytes) goto end;
+
+ /* We do have some data in the cache so override the status */
+ p_ctx->status = VC_CONTAINER_SUCCESS;
+
+ /* Read data directly from the cache */
+ if(bytes > size) bytes = size;
+ memcpy(data + read, cache->buffer + cache->position, bytes);
+ cache->position += bytes;
+ read += bytes;
+ size -= bytes;
+ }
+
+ end:
+ vc_container_assert(cache->offset + cache->position == p_ctx->offset + read);
+ return read;
+}
+
+/*****************************************************************************/
+static int32_t vc_container_io_cache_write( VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, const uint8_t *data, size_t size )
+{
+ int32_t written = 0;
+ size_t bytes, ret;
+
+ /* If we do not have a write cache then we need to flush it */
+ if(cache->size && !cache->dirty)
+ {
+ ret = vc_container_io_cache_flush( p_ctx, cache, 1 );
+ if(ret) return -(int32_t)ret;
+ }
+
+ while(size)
+ {
+ bytes = (cache->buffer_end - cache->buffer) - cache->position; /* Space left in cache */
+
+ /* Flush the cache if it is full */
+ if(!bytes)
+ {
+ /* Cache full, flush it */
+ ret = vc_container_io_cache_flush( p_ctx, cache, 0 );
+ if(ret)
+ {
+ written -= ret;
+ return written;
+ }
+ continue;
+ }
+
+ if(bytes > size) bytes = size;
+
+ if(!p_ctx->priv->async_io && bytes == cache->mem_size)
+ {
+ /* Write directly from the buffer */
+ ret = cache->io->pf_write(cache->io, data + written, bytes);
+ cache->offset += ret;
+ cache->io->priv->actual_offset += ret;
+ }
+ else
+ {
+ /* Write in the cache */
+ memcpy(cache->buffer + cache->position, data + written, bytes);
+ cache->position += bytes;
+ cache->dirty = 1;
+ ret = bytes;
+ }
+
+ written += ret;
+ if(ret != bytes) goto end;
+
+ size -= bytes;
+ }
+
+ end:
+ vc_container_assert(cache->offset + (int64_t)cache->position == p_ctx->offset + written);
+ if(cache->position > cache->size) cache->size = cache->position;
+ return written;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T vc_container_io_cache_seek(VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int64_t offset)
+{
+ VC_CONTAINER_STATUS_T status;
+ size_t shift, ret;
+
+ /* Check if the seek position is within our cache */
+ if(offset >= cache->offset && offset < cache->offset + (int64_t)cache->size)
+ {
+ cache->position = offset - cache->offset;
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ shift = cache->buffer - cache->mem;
+ if(!cache->dirty && shift && cache->size &&
+ offset >= cache->offset - (int64_t)shift && offset < cache->offset)
+ {
+ /* We need to refill the partial bit of the cache that we didn't take care of last time */
+ status = cache->io->pf_seek(cache->io, cache->offset - shift);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ cache->offset -= shift;
+ cache->buffer -= shift;
+
+ ret = cache->io->pf_read(cache->io, cache->buffer, shift);
+ vc_container_assert(ret == shift); /* FIXME: ret must = shift */
+ cache->size += shift;
+ cache->position = offset - cache->offset;
+ cache->io->priv->actual_offset = cache->offset + ret;
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ if(cache->dirty) vc_container_io_cache_flush( p_ctx, cache, 1 );
+ // FIXME: what if all the data couldn't be flushed ?
+
+ if(p_ctx->priv->async_io) async_io_wait_complete( p_ctx->priv->async_io, cache, 1 );
+
+ status = cache->io->pf_seek(cache->io, offset);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ vc_container_io_cache_flush( p_ctx, cache, 1 );
+
+ cache->offset = offset;
+ cache->io->priv->actual_offset = offset;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static size_t vc_container_io_cache_flush( VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete )
+{
+ size_t ret = 0, shift;
+
+ if(cache->position > cache->size) cache->size = cache->position;
+
+ if(cache->dirty && cache->size)
+ {
+ if(p_ctx->priv->actual_offset != cache->offset)
+ {
+ if(p_ctx->priv->async_io) async_io_wait_complete( p_ctx->priv->async_io, cache, complete );
+
+ if(cache->io->pf_seek(cache->io, cache->offset) != VC_CONTAINER_SUCCESS)
+ return 0;
+ }
+
+ if(p_ctx->priv->async_io)
+ {
+ ret = async_io_write( p_ctx->priv->async_io, cache );
+ if(async_io_wait_complete( p_ctx->priv->async_io, cache, complete ) != VC_CONTAINER_SUCCESS)
+ ret = 0;
+ }
+ else
+ ret = cache->io->pf_write(cache->io, cache->buffer, cache->size);
+
+ cache->io->priv->actual_offset = cache->offset + ret;
+ ret = cache->position - ret;
+ }
+ cache->dirty = 0;
+
+ cache->offset += cache->size;
+ if(cache->mem_size == cache->mem_max_size)
+ {
+ shift = cache->offset &(MEM_CACHE_ALIGNMENT-1);
+ cache->buffer = cache->mem + shift;
+ }
+
+ cache->position = cache->size = 0;
+ return ret;
+}
+
+/*****************************************************************************
+ * Asynchronous I/O.
+ * This is here to keep the I/O as busy as possible by allowing the writer
+ * to continue its work while the I/O is taking place in the background.
+ *****************************************************************************/
+
+#ifdef ENABLE_CONTAINERS_ASYNC_IO
+#include "vcos.h"
+
+#define NUMPC(c,n,s) ((c) < (1<<(s)) ? (n) : ((n) / (c >> (s))))
+
+static void stats_initialise(VC_CONTAINER_STATS_T *st, uint32_t shift)
+{
+ memset(st, 0, sizeof(VC_CONTAINER_STATS_T));
+ st->shift = shift;
+}
+
+static void stats_add_value(VC_CONTAINER_STATS_T *st, uint32_t count, uint32_t num)
+{
+ uint32_t numpc;
+ int i, j;
+
+ if(count == 0)
+ return;
+
+ numpc = NUMPC(count, num, st->shift);
+ // insert in the right place
+ i=0;
+ while(i < VC_CONTAINER_STATS_BINS && st->record[i].count != 0 && st->record[i].numpc > numpc)
+ i++;
+
+ if(st->record[i].count != 0 && st->record[i].numpc == numpc)
+ {
+ // equal numpc, can merge now
+ st->record[i].count += count;
+ st->record[i].num += num;
+ }
+ else
+ {
+ // shift higher records up
+ for(j=VC_CONTAINER_STATS_BINS; j>i; j--)
+ st->record[j] = st->record[j-1];
+
+ // write record in
+ st->record[i].count = count;
+ st->record[i].num = num;
+ st->record[i].numpc = numpc;
+
+ // if full, join the two closest records
+ if(st->record[VC_CONTAINER_STATS_BINS].count)
+ {
+ uint32_t min_diff = 0;
+ j = -1;
+
+ // find closest, based on difference between numpc
+ for(i=0; irecord[i].numpc - st->record[i+1].numpc;
+ if(j == -1 || diff < min_diff)
+ {
+ j = i;
+ min_diff = diff;
+ }
+ }
+
+ // merge these records
+ st->record[j].count += st->record[j+1].count;
+ st->record[j].num += st->record[j+1].num;
+ st->record[j].numpc = NUMPC(st->record[j].count, st->record[j].num, st->shift);
+
+ // shift down higher records
+ while(++j < VC_CONTAINER_STATS_BINS)
+ st->record[j] = st->record[j+1];
+
+ // zero the free top record
+ st->record[VC_CONTAINER_STATS_BINS].count = 0;
+ st->record[VC_CONTAINER_STATS_BINS].num = 0;
+ st->record[VC_CONTAINER_STATS_BINS].numpc = 0;
+ }
+ }
+}
+
+typedef struct VC_CONTAINER_IO_ASYNC_T
+{
+ VC_CONTAINER_IO_T *io;
+ VCOS_THREAD_T thread;
+ VCOS_SEMAPHORE_T spare_sema;
+ VCOS_SEMAPHORE_T queue_sema;
+ VCOS_EVENT_T wake_event;
+ int quit;
+
+ unsigned int num_area;
+ uint8_t *mem[MAX_NUM_MEMORY_AREAS]; /**< Base address of memory areas */
+ uint8_t *buffer[MAX_NUM_MEMORY_AREAS]; /**< When queued for writing, pointer to start of valid cache area */
+ size_t size[MAX_NUM_MEMORY_AREAS]; /**< When queued for writing, size of valid area to write */
+ unsigned int cur_area;
+
+ unsigned char stack[3000];
+ int error;
+
+ int stats_enable;
+ VC_CONTAINER_WRITE_STATS_T stats;
+
+} VC_CONTAINER_IO_ASYNC_T;
+
+/*****************************************************************************/
+static void async_io_stats_initialise( struct VC_CONTAINER_IO_ASYNC_T *ctx, int enable )
+{
+ ctx->stats_enable = enable;
+ stats_initialise(&ctx->stats.write, 8);
+ stats_initialise(&ctx->stats.wait, 0);
+ stats_initialise(&ctx->stats.flush, 0);
+}
+
+static void async_io_stats_get( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_WRITE_STATS_T *stats )
+{
+ *stats = ctx->stats;
+}
+
+static void *async_io_thread(VOID *argv)
+{
+ VC_CONTAINER_IO_ASYNC_T *ctx = argv;
+ unsigned int write_area = 0;
+
+ while (1)
+ {
+ unsigned long time = 0;
+
+ vcos_event_wait(&ctx->wake_event);
+ if(ctx->quit) break;
+
+ while(vcos_semaphore_trywait(&ctx->queue_sema) == VCOS_SUCCESS)
+ {
+ uint8_t *buffer = ctx->buffer[write_area];
+ size_t size = ctx->size[write_area];
+
+ if(ctx->stats_enable)
+ time = vcos_getmicrosecs();
+
+ if(ctx->io->pf_write(ctx->io, buffer, size) != size)
+ ctx->error = 1;
+
+ if(ctx->stats_enable)
+ stats_add_value(&ctx->stats.write, size, vcos_getmicrosecs() - time);
+
+ /* Signal that the write is done */
+ vcos_semaphore_post(&ctx->spare_sema);
+
+ if(++write_area == ctx->num_area)
+ write_area = 0;
+ }
+ }
+
+ return NULL;
+}
+
+static int async_io_write( VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache )
+{
+ unsigned long time = 0;
+ unsigned int offset;
+
+ if(ctx->stats_enable)
+ time = vcos_getmicrosecs();
+
+ /* post the current area */
+ ctx->buffer[ctx->cur_area] = cache->buffer;
+ ctx->size[ctx->cur_area] = cache->size;
+ vcos_semaphore_post(&ctx->queue_sema);
+ vcos_event_signal(&ctx->wake_event);
+
+ /* now we need to grab another area */
+ vcos_semaphore_wait(&ctx->spare_sema);
+ if(++ctx->cur_area == ctx->num_area)
+ ctx->cur_area = 0;
+
+ if(ctx->stats_enable)
+ stats_add_value(&ctx->stats.wait, 1, vcos_getmicrosecs() - time);
+
+ /* alter cache mem to point to the new cur_area */
+ offset = cache->buffer - cache->mem;
+ cache->mem = ctx->mem[ctx->cur_area];
+ cache->buffer = cache->mem + offset;
+ cache->buffer_end = cache->mem + cache->mem_size;
+
+ return ctx->error ? 0 : cache->size;
+}
+
+static VC_CONTAINER_STATUS_T async_io_wait_complete( struct VC_CONTAINER_IO_ASYNC_T *ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete )
+{
+ unsigned int time = 0;
+
+ if(ctx->stats_enable)
+ time = vcos_getmicrosecs();
+
+ if(complete)
+ {
+ int num;
+ /* Need to make sure that all memory areas have been written out, so should have num-1 spare */
+ for(num=0; numnum_area-1; num++)
+ vcos_semaphore_wait(&ctx->spare_sema);
+
+ for(num=0; numnum_area-1; num++)
+ vcos_semaphore_post(&ctx->spare_sema);
+ }
+ else
+ {
+ /* Need to make sure we can acquire one memory area */
+ vcos_semaphore_wait(&ctx->spare_sema);
+ vcos_semaphore_post(&ctx->spare_sema);
+ }
+
+ if(ctx->stats_enable)
+ stats_add_value(&ctx->stats.flush, 1, vcos_getmicrosecs() - time);
+
+ return ctx->error ? VC_CONTAINER_ERROR_FAILED : VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_IO_ASYNC_T *async_io_start( VC_CONTAINER_IO_T *io, int num_areas, VC_CONTAINER_STATUS_T *status )
+{
+ VC_CONTAINER_IO_ASYNC_T *ctx = 0;
+ VCOS_UNSIGNED pri = 0;
+
+ /* Allocate our context */
+ ctx = malloc(sizeof(*ctx));
+ if(!ctx) goto error_spare_sema;
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->io = io;
+
+ ctx->mem[0] = io->priv->cache->mem;
+
+ for(ctx->num_area = 1; ctx->num_area < num_areas; ctx->num_area++)
+ {
+ ctx->mem[ctx->num_area] = malloc(io->priv->cache->mem_size);
+ if(!ctx->mem[ctx->num_area])
+ break;
+ }
+
+ if(ctx->num_area == 1) // no real benefit in asynchronous writes
+ goto error_spare_sema;
+
+ async_io_stats_initialise(ctx, 0);
+
+ if(vcos_semaphore_create(&ctx->spare_sema, "async_spare_sem", ctx->num_area-1) != VCOS_SUCCESS)
+ goto error_spare_sema;
+
+ if(vcos_semaphore_create(&ctx->queue_sema, "async_queue_sem", 0) != VCOS_SUCCESS)
+ goto error_queue_sema;
+
+ if (vcos_event_create(&ctx->wake_event, "async_wake_event") != VCOS_SUCCESS)
+ goto error_event;
+
+ // run this thread at a slightly higher priority than the calling thread - that means that
+ // we prefer to write to the SD card rather than filling the memory buffer.
+ pri = vcos_thread_get_priority(vcos_thread_current());
+ if(vcos_thread_create_classic(&ctx->thread, "async_io", async_io_thread, ctx,
+ ctx->stack, sizeof(ctx->stack), pri-1, 10, VCOS_START) != VCOS_SUCCESS)
+ goto error_thread;
+
+ if(status) *status = VC_CONTAINER_SUCCESS;
+ return ctx;
+
+ error_thread:
+ vcos_event_delete(&ctx->wake_event);
+ error_event:
+ vcos_semaphore_delete(&ctx->queue_sema);
+ error_queue_sema:
+ vcos_semaphore_delete(&ctx->spare_sema);
+ error_spare_sema:
+ if(ctx) free(ctx);
+ if(status) *status = VC_CONTAINER_ERROR_FAILED;
+ return 0;
+}
+
+static VC_CONTAINER_STATUS_T async_io_stop( VC_CONTAINER_IO_ASYNC_T *ctx )
+{
+ /* Block if a write operation is already in progress */
+ //vcos_semaphore_wait(&ctx->sema);
+ // XXX block until all done
+
+ ctx->quit = 1;
+ vcos_event_signal(&ctx->wake_event);
+ vcos_thread_join(&ctx->thread,NULL);
+ vcos_event_delete(&ctx->wake_event);
+ vcos_semaphore_delete(&ctx->queue_sema);
+ vcos_semaphore_delete(&ctx->spare_sema);
+
+ while(ctx->num_area > 0)
+ free(ctx->mem[--ctx->num_area]);
+
+ free(ctx);
+ return VC_CONTAINER_SUCCESS;
+}
+#else
+
+static struct VC_CONTAINER_IO_ASYNC_T *async_io_start( VC_CONTAINER_IO_T *io, int num_areas, VC_CONTAINER_STATUS_T *status )
+{
+ VC_CONTAINER_PARAM_UNUSED(io);
+ VC_CONTAINER_PARAM_UNUSED(num_areas);
+ if(status) *status = VC_CONTAINER_ERROR_FAILED;
+ return 0;
+}
+
+static int async_io_write( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_IO_PRIVATE_CACHE_T *cache )
+{
+ VC_CONTAINER_PARAM_UNUSED(ctx);
+ VC_CONTAINER_PARAM_UNUSED(cache);
+ return 0;
+}
+
+static VC_CONTAINER_STATUS_T async_io_wait_complete( struct VC_CONTAINER_IO_ASYNC_T *ctx,
+ VC_CONTAINER_IO_PRIVATE_CACHE_T *cache, int complete )
+{
+ VC_CONTAINER_PARAM_UNUSED(ctx);
+ VC_CONTAINER_PARAM_UNUSED(cache);
+ VC_CONTAINER_PARAM_UNUSED(complete);
+ return 0;
+}
+
+static VC_CONTAINER_STATUS_T async_io_stop( struct VC_CONTAINER_IO_ASYNC_T *ctx )
+{
+ VC_CONTAINER_PARAM_UNUSED(ctx);
+ return VC_CONTAINER_SUCCESS;
+}
+
+static void async_io_stats_initialise( struct VC_CONTAINER_IO_ASYNC_T *ctx, int enable )
+{
+ VC_CONTAINER_PARAM_UNUSED(ctx);
+ VC_CONTAINER_PARAM_UNUSED(enable);
+}
+
+static void async_io_stats_get( struct VC_CONTAINER_IO_ASYNC_T *ctx, VC_CONTAINER_WRITE_STATS_T *stats )
+{
+ VC_CONTAINER_PARAM_UNUSED(ctx);
+ VC_CONTAINER_PARAM_UNUSED(stats);
+}
+
+
+#endif
diff --git a/gfx/include/userland/containers/core/containers_io.h b/gfx/include/userland/containers/core/containers_io.h
new file mode 100644
index 0000000000..8cd4407d58
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_io.h
@@ -0,0 +1,229 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_IO_H
+#define VC_CONTAINERS_IO_H
+
+/** \file containers_io.h
+ * Interface definition for the input / output abstraction layer used by container
+ * readers and writers */
+#include "containers/containers.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \defgroup VcContainerIoApi Container I/O API */
+/* @{ */
+
+/** Container io opening mode.
+ * This is used to specify whether a reader or writer is requested.
+ */
+typedef enum {
+ VC_CONTAINER_IO_MODE_READ = 0, /**< Container io opened in reading mode */
+ VC_CONTAINER_IO_MODE_WRITE = 1 /**< Container io opened in writing mode */
+} VC_CONTAINER_IO_MODE_T;
+
+/** \name Container I/O Capabilities
+ * The following flags are exported by container i/o modules to describe their capabilities */
+/* @{ */
+/** Type definition for container I/O capabilities */
+typedef uint32_t VC_CONTAINER_IO_CAPABILITIES_T;
+/** Seeking is not supported */
+#define VC_CONTAINER_IO_CAPS_CANT_SEEK 0x1
+/** Seeking is slow and should be avoided */
+#define VC_CONTAINER_IO_CAPS_SEEK_SLOW 0x2
+/** The I/O doesn't provide any caching of the data */
+#define VC_CONTAINER_IO_CAPS_NO_CACHING 0x4
+/* @} */
+
+/** Container Input / Output Context.
+ * This structure defines the context for a container io instance */
+struct VC_CONTAINER_IO_T
+{
+ /** Pointer to information private to the container io instance */
+ struct VC_CONTAINER_IO_PRIVATE_T *priv;
+
+ /** Pointer to information private to the container io module */
+ struct VC_CONTAINER_IO_MODULE_T *module;
+
+ /** Uniform Resource Identifier for the stream to open.
+ * This is a string encoded in UTF-8 which follows the syntax defined in
+ * RFC2396 (http://tools.ietf.org/html/rfc2396). */
+ char *uri;
+
+ /** Pre-parsed URI */
+ struct VC_URI_PARTS_T *uri_parts;
+
+ /** Current offset into the i/o stream */
+ int64_t offset;
+
+ /** Current size of the i/o stream (0 if unknown). This size might grow during the
+ * lifetime of the i/o instance (for instance when doing progressive download). */
+ int64_t size;
+
+ /** Capabilities of the i/o stream */
+ VC_CONTAINER_IO_CAPABILITIES_T capabilities;
+
+ /** Status of the i/o stream */
+ VC_CONTAINER_STATUS_T status;
+
+ /** Maximum size allowed for this i/o stream (0 if unknown). This is used during writing
+ * to limit the size of the stream to below this value. */
+ int64_t max_size;
+
+ /** \note the following list of function pointers should not be used directly.
+ * They defines the interface for implementing container io modules and are filled in
+ * by the container modules themselves. */
+
+ /** \private
+ * Function pointer to close and free all resources allocated by a
+ * container io module */
+ VC_CONTAINER_STATUS_T (*pf_close)(struct VC_CONTAINER_IO_T *io);
+
+ /** \private
+ * Function pointer to read or skip data from container io module */
+ size_t (*pf_read)(struct VC_CONTAINER_IO_T *io, void *buffer, size_t size);
+
+ /** \private
+ * Function pointer to write data to a container io module */
+ size_t (*pf_write)(struct VC_CONTAINER_IO_T *io, const void *buffer, size_t size);
+
+ /** \private
+ * Function pointer to seek into a container io module */
+ VC_CONTAINER_STATUS_T (*pf_seek)(struct VC_CONTAINER_IO_T *io, int64_t offset);
+
+ /** \private
+ * Function pointer to perform a control operation on a container io module */
+ VC_CONTAINER_STATUS_T (*pf_control)(struct VC_CONTAINER_IO_T *io,
+ VC_CONTAINER_CONTROL_T operation, va_list args);
+
+};
+
+/** Opens an i/o stream pointed to by a URI.
+ * This will create an instance of the container i/o module.
+ *
+ * \param uri Uniform Resource Identifier pointing to the multimedia container
+ * \param mode Mode in which the i/o stream will be opened
+ * \param status Returns the status of the operation
+ * \return If successful, this returns a pointer to the new instance
+ * of the i/o module. Returns NULL on failure.
+ */
+VC_CONTAINER_IO_T *vc_container_io_open( const char *uri, VC_CONTAINER_IO_MODE_T mode,
+ VC_CONTAINER_STATUS_T *status );
+
+/** Creates an empty i/o stream. The i/o function pointers will have to be set
+ * by the caller before the i/o gets used.
+ * This will create an instance of the container i/o module.
+ *
+ * \param uri Uniform Resource Identifier pointing to the multimedia container
+ * \param mode Mode in which the i/o stream will be opened
+ * \param capabilities Flags indicating the capabilities of the i/o
+ * \param status Returns the status of the operation
+ * \return If successful, this returns a pointer to the new instance
+ * of the i/o module. Returns NULL on failure.
+ */
+VC_CONTAINER_IO_T *vc_container_io_create( const char *uri, VC_CONTAINER_IO_MODE_T mode,
+ VC_CONTAINER_IO_CAPABILITIES_T capabilities,
+ VC_CONTAINER_STATUS_T *p_status );
+
+/** Closes an instance of a container i/o module.
+ * \param context Pointer to the VC_CONTAINER_IO_T context of the instance to close
+ * \return VC_CONTAINER_SUCCESS on success.
+ */
+VC_CONTAINER_STATUS_T vc_container_io_close( VC_CONTAINER_IO_T *context );
+
+/** Read data from an i/o stream without advancing the read position within the stream.
+ * \param context Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param buffer Pointer to the buffer where the data will be read
+ * \param size Number of bytes to read
+ * \return The size of the data actually read.
+ */
+size_t vc_container_io_peek(VC_CONTAINER_IO_T *context, void *buffer, size_t size);
+
+/** Read data from an i/o stream.
+ * \param context Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param buffer Pointer to the buffer where the data will be read
+ * \param size Number of bytes to read
+ * \return The size of the data actually read.
+ */
+size_t vc_container_io_read(VC_CONTAINER_IO_T *context, void *buffer, size_t size);
+
+/** Skip data in an i/o stream without reading it.
+ * \param context Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param size Number of bytes to skip
+ * \return The size of the data actually skipped.
+ */
+size_t vc_container_io_skip(VC_CONTAINER_IO_T *context, size_t size);
+
+/** Write data to an i/o stream.
+ * \param context Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param buffer Pointer to the buffer containing the data to write
+ * \param size Number of bytes to write
+ * \return The size of the data actually written.
+ */
+size_t vc_container_io_write(VC_CONTAINER_IO_T *context, const void *buffer, size_t size);
+
+/** Seek into an i/o stream.
+ * \param context Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param offset Absolute file offset to seek to
+ * \return Status of the operation
+ */
+VC_CONTAINER_STATUS_T vc_container_io_seek(VC_CONTAINER_IO_T *context, int64_t offset);
+
+/** Perform control operation on an i/o stream (va_list).
+ * \param context Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param operation Control operation to be performed
+ * \param args Additional arguments for the operation
+ * \return Status of the operation
+ */
+VC_CONTAINER_STATUS_T vc_container_io_control_list(VC_CONTAINER_IO_T *context,
+ VC_CONTAINER_CONTROL_T operation, va_list args);
+
+/** Perform control operation on an i/o stream (varargs).
+ * \param context Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param operation Control operation to be performed
+ * \param ... Additional arguments for the operation
+ * \return Status of the operation
+ */
+VC_CONTAINER_STATUS_T vc_container_io_control(VC_CONTAINER_IO_T *context,
+ VC_CONTAINER_CONTROL_T operation, ...);
+
+/** Cache the pointed region of the i/o stream (from current position).
+ * This will allow future seeking into the specified region even on non-seekable streams.
+ * \param context Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param size Size of the region to cache
+ * \return Status of the operation
+ */
+size_t vc_container_io_cache(VC_CONTAINER_IO_T *context, size_t size);
+
+/* @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VC_CONTAINERS_HELPERS_H */
diff --git a/gfx/include/userland/containers/core/containers_io_helpers.c b/gfx/include/userland/containers/core/containers_io_helpers.c
new file mode 100644
index 0000000000..88e08cc7ca
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_io_helpers.c
@@ -0,0 +1,244 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+#include
+#include
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_logging.h"
+
+void vc_container_helper_format_debug(VC_CONTAINER_T *ctx, int indent, const char *format, ...)
+{
+ char debug_string[512];
+ va_list args;
+ int result;
+
+ if(indent >= (int)sizeof(debug_string)) return;
+ memset(debug_string, ' ', indent);
+
+ va_start( args, format );
+ result = vsnprintf(debug_string + indent, sizeof(debug_string) - indent, format, args);
+ va_end( args );
+
+ if(result <= 0) return;
+
+ vc_container_log(ctx, VC_CONTAINER_LOG_FORMAT, debug_string);
+ fflush(0);
+}
+
+uint64_t vc_container_helper_int_debug(VC_CONTAINER_T *ctx, int type, uint64_t value, const char *name, int indent)
+{
+ VC_CONTAINER_PARAM_UNUSED(ctx);
+
+ if(type == LOG_FORMAT_TYPE_HEX)
+ vc_container_helper_format_debug(ctx, indent, "%s: 0x%"PRIx64, name, value);
+ else
+ vc_container_helper_format_debug(ctx, indent, "%s: %"PRIi64, name, value);
+ return value;
+}
+
+uint64_t vc_container_helper_read_debug(VC_CONTAINER_T *ctx, int type, int size,
+ const char *name, uint8_t *buffer, int indent, int b_skip)
+{
+ int64_t offset = STREAM_POSITION(ctx);
+ uint64_t value = 0;
+ GUID_T guid;
+
+ if(type == LOG_FORMAT_TYPE_STRING ||
+ type == LOG_FORMAT_TYPE_STRING_UTF16_LE ||
+ type == LOG_FORMAT_TYPE_STRING_UTF16_BE)
+ {
+ uint8_t stringbuf[256];
+ char utf8buf[256];
+ int stringsize = sizeof(stringbuf) - 2;
+
+ if(!buffer)
+ {
+ buffer = stringbuf;
+ if(size < stringsize) stringsize = size;
+ }
+ else stringsize = size;
+
+ value = vc_container_io_read(ctx->priv->io, buffer, stringsize);
+
+ if(!utf8_from_charset(type == LOG_FORMAT_TYPE_STRING ? "UTF8" : "UTF16-LE",
+ utf8buf, sizeof(utf8buf), buffer, stringsize))
+ vc_container_helper_format_debug(ctx, indent, "%s: \"%s\"", name, utf8buf);
+ else
+ vc_container_helper_format_debug(ctx, indent, "%s: (could not read)", name);
+
+ if(size - stringsize)
+ value += vc_container_io_skip(ctx->priv->io, size - stringsize);
+ return value;
+ }
+
+ if(type == LOG_FORMAT_TYPE_UINT_LE)
+ {
+ switch(size)
+ {
+ case 1: value = vc_container_io_read_uint8(ctx->priv->io); break;
+ case 2: value = vc_container_io_read_le_uint16(ctx->priv->io); break;
+ case 3: value = vc_container_io_read_le_uint24(ctx->priv->io); break;
+ case 4: value = vc_container_io_read_le_uint32(ctx->priv->io); break;
+ case 5: value = vc_container_io_read_le_uint40(ctx->priv->io); break;
+ case 6: value = vc_container_io_read_le_uint48(ctx->priv->io); break;
+ case 7: value = vc_container_io_read_le_uint56(ctx->priv->io); break;
+ case 8: value = vc_container_io_read_le_uint64(ctx->priv->io); break;
+ }
+ }
+ else if(type == LOG_FORMAT_TYPE_UINT_BE)
+ {
+ switch(size)
+ {
+ case 1: value = vc_container_io_read_uint8(ctx->priv->io); break;
+ case 2: value = vc_container_io_read_be_uint16(ctx->priv->io); break;
+ case 3: value = vc_container_io_read_be_uint24(ctx->priv->io); break;
+ case 4: value = vc_container_io_read_be_uint32(ctx->priv->io); break;
+ case 5: value = vc_container_io_read_be_uint40(ctx->priv->io); break;
+ case 6: value = vc_container_io_read_be_uint48(ctx->priv->io); break;
+ case 7: value = vc_container_io_read_be_uint56(ctx->priv->io); break;
+ case 8: value = vc_container_io_read_be_uint64(ctx->priv->io); break;
+ }
+ }
+ else if(type == LOG_FORMAT_TYPE_FOURCC)
+ {
+ value = vc_container_io_read_fourcc(ctx->priv->io);
+ }
+ else if(type == LOG_FORMAT_TYPE_GUID)
+ {
+ value = vc_container_io_read(ctx->priv->io, &guid, 16);
+ }
+ else
+ {
+ vc_container_assert(0);
+ return 0;
+ }
+
+ if(type == LOG_FORMAT_TYPE_GUID)
+ {
+ if(value == 16)
+ {
+ vc_container_helper_format_debug(ctx, indent, "%s: 0x%x-0x%x-0x%x-0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
+ name, guid.word0, guid.short0, guid.short1,
+ guid.bytes[0], guid.bytes[1], guid.bytes[2], guid.bytes[3],
+ guid.bytes[4], guid.bytes[5], guid.bytes[6], guid.bytes[7]);
+ if(buffer) memcpy(buffer, &guid, sizeof(guid));
+ }
+ }
+ else if(type == LOG_FORMAT_TYPE_FOURCC)
+ {
+ uint32_t val = value;
+ vc_container_helper_format_debug(ctx, indent, "%s: %4.4s", name, (char *)&val);
+ }
+ else
+ {
+ vc_container_helper_format_debug(ctx, indent, "%s: %"PRIi64, name, value);
+ }
+
+ if(b_skip) value = (STREAM_POSITION(ctx) - offset) != size;
+ return value;
+}
+
+VC_CONTAINER_STATUS_T vc_container_helper_write_debug(VC_CONTAINER_T *ctx, int type, int size,
+ const char *name, uint64_t value, const uint8_t *buffer, int indent, int silent)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ if(type == LOG_FORMAT_TYPE_STRING)
+ {
+ value = vc_container_io_write(ctx->priv->io, buffer, size);
+ if(!silent)
+ vc_container_helper_format_debug(ctx, indent, "%s: \"%ls\"", name, buffer);
+ return value == (uint64_t)size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED;
+ }
+
+ if(type == LOG_FORMAT_TYPE_UINT_LE)
+ {
+ switch(size)
+ {
+ case 1: status = vc_container_io_write_uint8(ctx->priv->io, (uint8_t)value); break;
+ case 2: status = vc_container_io_write_le_uint16(ctx->priv->io, (uint16_t)value); break;
+ case 3: status = vc_container_io_write_le_uint24(ctx->priv->io, (uint32_t)value); break;
+ case 4: status = vc_container_io_write_le_uint32(ctx->priv->io, (uint32_t)value); break;
+ case 8: status = vc_container_io_write_le_uint64(ctx->priv->io, value); break;
+ }
+ }
+ else if(type == LOG_FORMAT_TYPE_UINT_BE)
+ {
+ switch(size)
+ {
+ case 1: status = vc_container_io_write_uint8(ctx->priv->io, (uint8_t)value); break;
+ case 2: status = vc_container_io_write_be_uint16(ctx->priv->io, (uint16_t)value); break;
+ case 3: status = vc_container_io_write_be_uint24(ctx->priv->io, (uint32_t)value); break;
+ case 4: status = vc_container_io_write_be_uint32(ctx->priv->io, (uint32_t)value); break;
+ case 8: status = vc_container_io_write_be_uint64(ctx->priv->io, value); break;
+ }
+ }
+ else if(type == LOG_FORMAT_TYPE_FOURCC)
+ {
+ status = vc_container_io_write_fourcc(ctx->priv->io, (uint32_t)value);
+ }
+ else if(type == LOG_FORMAT_TYPE_GUID)
+ {
+ value = vc_container_io_write(ctx->priv->io, buffer, 16);
+ }
+ else
+ {
+ vc_container_assert(0);
+ return 0;
+ }
+
+ if(status)
+ {
+ vc_container_helper_format_debug(ctx, indent, "write failed for %s", name);
+ return status;
+ }
+
+ if(!silent)
+ {
+ if (type == LOG_FORMAT_TYPE_FOURCC)
+ {
+ vc_container_helper_format_debug(ctx, indent, "%s: %4.4s", name, (char *)&value);
+ }
+ else if(type == LOG_FORMAT_TYPE_GUID)
+ {
+ GUID_T guid;
+ memcpy(&guid, buffer, sizeof(guid));
+ vc_container_helper_format_debug(ctx, indent, "%s: 0x%x-0x%x-0x%x-0x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x",
+ name, guid.word0, guid.short0, guid.short1,
+ guid.bytes[0], guid.bytes[1], guid.bytes[2], guid.bytes[3],
+ guid.bytes[4], guid.bytes[5], guid.bytes[6], guid.bytes[7]);
+ }
+ else
+ {
+ vc_container_helper_format_debug(ctx, indent, "%s: %"PRIi64, name, value);
+ }
+ }
+
+ return status;
+}
diff --git a/gfx/include/userland/containers/core/containers_io_helpers.h b/gfx/include/userland/containers/core/containers_io_helpers.h
new file mode 100644
index 0000000000..1f9b4607f8
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_io_helpers.h
@@ -0,0 +1,716 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_IO_HELPERS_H
+#define VC_CONTAINERS_IO_HELPERS_H
+
+/** \file containers_io_helpers.h
+ * Helper functions and macros which provide functionality which is often used by containers
+ */
+
+#include "containers/core/containers_io.h"
+#include "containers/core/containers_utils.h"
+
+/*****************************************************************************
+ * Helper inline functions to read integers from an i/o stream
+ *****************************************************************************/
+
+/** Reads an unsigned 8 bits integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint8_t vc_container_io_read_uint8(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value;
+ size_t ret = vc_container_io_read(io, &value, 1);
+ return ret == 1 ? value : 0;
+}
+
+/** Reads a FOURCC from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The FOURCC to read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE VC_CONTAINER_FOURCC_T vc_container_io_read_fourcc(VC_CONTAINER_IO_T *io)
+{
+ VC_CONTAINER_FOURCC_T value;
+ size_t ret = vc_container_io_read(io, (int8_t *)&value, 4);
+ return ret == 4 ? value : 0;
+}
+
+/** Reads an unsigned 16 bits big endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint16_t vc_container_io_read_be_uint16(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[2];
+ size_t ret = vc_container_io_read(io, value, 2);
+ return ret == 2 ? (value[0] << 8) | value[1] : 0;
+}
+
+/** Reads an unsigned 24 bits big endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint32_t vc_container_io_read_be_uint24(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[3];
+ size_t ret = vc_container_io_read(io, value, 3);
+ return ret == 3 ? (value[0] << 16) | (value[1] << 8) | value[2] : 0;
+}
+
+/** Reads an unsigned 32 bits big endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint32_t vc_container_io_read_be_uint32(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[4];
+ size_t ret = vc_container_io_read(io, value, 4);
+ return ret == 4 ? (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3] : 0;
+}
+
+/** Reads an unsigned 40 bits big endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint64_t vc_container_io_read_be_uint40(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[5];
+ uint32_t value1, value2;
+ size_t ret = vc_container_io_read(io, value, 5);
+
+ value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3];
+ value2 = value[4];
+
+ return ret == 5 ? (((uint64_t)value1) << 8)|value2 : 0;
+}
+
+/** Reads an unsigned 48 bits big endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint64_t vc_container_io_read_be_uint48(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[6];
+ uint32_t value1, value2;
+ size_t ret = vc_container_io_read(io, value, 6);
+
+ value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3];
+ value2 = (value[4] << 8) | value[5];
+
+ return ret == 6 ? (((uint64_t)value1) << 16)|value2 : 0;
+}
+
+/** Reads an unsigned 56 bits big endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint64_t vc_container_io_read_be_uint56(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[7];
+ uint32_t value1, value2;
+ size_t ret = vc_container_io_read(io, value, 7);
+
+ value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3];
+ value2 = (value[4] << 16) | (value[5] << 8) | value[6];
+
+ return ret == 7 ? (((uint64_t)value1) << 24)|value2 : 0;
+}
+
+/** Reads an unsigned 64 bits big endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint64_t vc_container_io_read_be_uint64(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[8];
+ uint32_t value1, value2;
+ size_t ret = vc_container_io_read(io, value, 8);
+
+ value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3];
+ value2 = (value[4] << 24) | (value[5] << 16) | (value[6] << 8) | value[7];
+
+ return ret == 8 ? (((uint64_t)value1) << 32)|value2 : 0;
+}
+
+/** Reads an unsigned 16 bits little endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint16_t vc_container_io_read_le_uint16(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[2];
+ size_t ret = vc_container_io_read(io, value, 2);
+ return ret == 2 ? (value[1] << 8) | value[0] : 0;
+}
+
+/** Reads an unsigned 24 bits little endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint32_t vc_container_io_read_le_uint24(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[3];
+ size_t ret = vc_container_io_read(io, value, 3);
+ return ret == 3 ? (value[2] << 16) | (value[1] << 8) | value[0] : 0;
+}
+
+/** Reads an unsigned 32 bits little endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint32_t vc_container_io_read_le_uint32(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[4];
+ size_t ret = vc_container_io_read(io, value, 4);
+ return ret == 4 ? (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0] : 0;
+}
+
+/** Reads an unsigned 40 bits little endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint64_t vc_container_io_read_le_uint40(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[5];
+ uint32_t value1, value2;
+ size_t ret = vc_container_io_read(io, value, 5);
+
+ value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0];
+ value2 = value[4];
+
+ return ret == 5 ? (((uint64_t)value2) << 32)|value1 : 0;
+}
+
+/** Reads an unsigned 48 bits little endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint64_t vc_container_io_read_le_uint48(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[6];
+ uint32_t value1, value2;
+ size_t ret = vc_container_io_read(io, value, 6);
+
+ value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0];
+ value2 = (value[5] << 8) | value[4];
+
+ return ret == 6 ? (((uint64_t)value2) << 32)|value1 : 0;
+}
+
+/** Reads an unsigned 56 bits little endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint64_t vc_container_io_read_le_uint56(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[7];
+ uint32_t value1, value2;
+ size_t ret = vc_container_io_read(io, value, 7);
+
+ value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0];
+ value2 = (value[6] << 16) | (value[5] << 8) | value[4];
+
+ return ret == 7 ? (((uint64_t)value2) << 32)|value1 : 0;
+}
+
+/** Reads an unsigned 64 bits little endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint64_t vc_container_io_read_le_uint64(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[8];
+ uint32_t value1, value2;
+ size_t ret = vc_container_io_read(io, value, 8);
+
+ value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0];
+ value2 = (value[7] << 24) | (value[6] << 16) | (value[5] << 8) | value[4];
+
+ return ret == 8 ? (((uint64_t)value2) << 32)|value1 : 0;
+}
+
+/*****************************************************************************
+ * Helper inline functions to peek integers from an i/o stream
+ *****************************************************************************/
+
+/** Peeks an unsigned 8 bits integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint8_t vc_container_io_peek_uint8(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value;
+ size_t ret = vc_container_io_peek(io, &value, 1);
+ return ret == 1 ? value : 0;
+}
+
+/** Peeks an unsigned 16 bits big endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint16_t vc_container_io_peek_be_uint16(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[2];
+ size_t ret = vc_container_io_peek(io, value, 2);
+ return ret == 2 ? (value[0] << 8) | value[1] : 0;
+}
+
+/** Peeks an unsigned 24 bits big endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint32_t vc_container_io_peek_be_uint24(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[3];
+ size_t ret = vc_container_io_peek(io, value, 3);
+ return ret == 3 ? (value[0] << 16) | (value[1] << 8) | value[2] : 0;
+}
+
+/** Peeks an unsigned 32 bits big endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint32_t vc_container_io_peek_be_uint32(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[4];
+ size_t ret = vc_container_io_peek(io, value, 4);
+ return ret == 4 ? (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3] : 0;
+}
+
+/** Peeks an unsigned 64 bits big endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint64_t vc_container_io_peek_be_uint64(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[8];
+ uint32_t value1, value2;
+ size_t ret = vc_container_io_peek(io, value, 8);
+
+ value1 = (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | value[3];
+ value2 = (value[4] << 24) | (value[5] << 16) | (value[6] << 8) | value[7];
+
+ return ret == 8 ? (((uint64_t)value1) << 32)|value2 : 0;
+}
+
+/** Peeks an unsigned 16 bits little endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint16_t vc_container_io_peek_le_uint16(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[2];
+ size_t ret = vc_container_io_peek(io, value, 2);
+ return ret == 2 ? (value[1] << 8) | value[0] : 0;
+}
+
+/** Peeks an unsigned 24 bits little endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint32_t vc_container_io_peek_le_uint24(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[3];
+ size_t ret = vc_container_io_peek(io, value, 3);
+ return ret == 3 ? (value[2] << 16) | (value[1] << 8) | value[0] : 0;
+}
+
+/** Peeks an unsigned 32 bits little endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint32_t vc_container_io_peek_le_uint32(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[4];
+ size_t ret = vc_container_io_peek(io, value, 4);
+ return ret == 4 ? (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0] : 0;
+}
+
+/** Peeks an unsigned 64 bits little endian integer from an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \return The integer read. In case of failure during the read,
+ * this will return a value of 0.
+ */
+STATIC_INLINE uint64_t vc_container_io_peek_le_uint64(VC_CONTAINER_IO_T *io)
+{
+ uint8_t value[8];
+ uint32_t value1, value2;
+ size_t ret = vc_container_io_peek(io, value, 8);
+
+ value1 = (value[3] << 24) | (value[2] << 16) | (value[1] << 8) | value[0];
+ value2 = (value[7] << 24) | (value[6] << 16) | (value[5] << 8) | value[4];
+
+ return ret == 8 ? (((uint64_t)value2) << 32)|value1 : 0;
+}
+
+/*****************************************************************************
+ * Helper inline functions to write integers to an i/o stream
+ *****************************************************************************/
+
+/** Writes an unsigned 8 bits integer to an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param value The integer to write.
+ * \return The status of the operation.
+ */
+STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_uint8(VC_CONTAINER_IO_T *io, uint8_t value)
+{
+ size_t ret = vc_container_io_write(io, &value, 1);
+ return ret == 1 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED;
+}
+
+/** Writes a FOURCC to an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param value The FOURCC to write.
+ * \return The status of the operation.
+ */
+STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_fourcc(VC_CONTAINER_IO_T *io, VC_CONTAINER_FOURCC_T value)
+{
+ size_t ret = vc_container_io_write(io, (uint8_t *)&value, 4);
+ return ret == 4 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED;
+}
+
+/** Writes an unsigned 16 bits big endian integer to an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param value The integer to write.
+ * \return The status of the operation.
+ */
+STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint16(VC_CONTAINER_IO_T *io, uint16_t value)
+{
+ uint8_t bytes[2] = {(uint8_t)(value >> 8), (uint8_t)value};
+ size_t ret = vc_container_io_write(io, bytes, 2);
+ return ret == 2 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED;
+}
+
+/** Writes an unsigned 24 bits big endian integer to an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param value The integer to write.
+ * \return The status of the operation.
+ */
+STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint24(VC_CONTAINER_IO_T *io, uint32_t value)
+{
+ uint8_t bytes[3] = {(uint8_t)(value >> 16), (uint8_t)(value >> 8), (uint8_t)value};
+ size_t ret = vc_container_io_write(io, bytes, 3);
+ return ret == 3 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED;
+}
+
+/** Writes an unsigned 32 bits big endian integer to an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param value The integer to write.
+ * \return The status of the operation.
+ */
+STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint32(VC_CONTAINER_IO_T *io, uint32_t value)
+{
+ uint8_t bytes[4] = {value >> 24, value >> 16, value >> 8, value};
+ size_t ret = vc_container_io_write(io, bytes, 4);
+ return ret == 4 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED;
+}
+
+/** Writes an unsigned 64 bits big endian integer to an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param value The integer to write.
+ * \return The status of the operation.
+ */
+STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_be_uint64(VC_CONTAINER_IO_T *io, uint64_t value)
+{
+ uint8_t bytes[8] =
+ {
+ (uint8_t)(value >> 56),
+ (uint8_t)(value >> 48),
+ (uint8_t)(value >> 40),
+ (uint8_t)(value >> 32),
+ (uint8_t)(value >> 24),
+ (uint8_t)(value >> 16),
+ (uint8_t)(value >> 8),
+ (uint8_t) value
+ };
+ size_t ret = vc_container_io_write(io, bytes, 8);
+ return ret == 8 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED;
+}
+
+/** Writes an unsigned 16 bits little endian integer to an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param value The integer to write.
+ * \return The status of the operation.
+ */
+STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint16(VC_CONTAINER_IO_T *io, uint16_t value)
+{
+ uint8_t bytes[2] = {(uint8_t)value, (uint8_t)(value >> 8)};
+ size_t ret = vc_container_io_write(io, bytes, 2);
+ return ret == 2 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED;
+}
+
+/** Writes an unsigned 24 bits little endian integer to an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param value The integer to write.
+ * \return The status of the operation.
+ */
+STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint24(VC_CONTAINER_IO_T *io, uint32_t value)
+{
+ uint8_t bytes[3] = {value, value >> 8, value >> 16};
+ size_t ret = vc_container_io_write(io, bytes, 3);
+ return ret == 3 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED;
+}
+
+/** Writes an unsigned 32 bits little endian integer to an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param value The integer to write.
+ * \return The status of the operation.
+ */
+STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint32(VC_CONTAINER_IO_T *io, uint32_t value)
+{
+ uint8_t bytes[4] = {value, value >> 8, value >> 16, value >> 24};
+ size_t ret = vc_container_io_write(io, bytes, 4);
+ return ret == 4 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED;
+}
+
+/** Writes an unsigned 64 bits little endian integer to an i/o stream.
+ * \param io Pointer to the VC_CONTAINER_IO_T instance to use
+ * \param value The integer to write.
+ * \return The status of the operation.
+ */
+STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_io_write_le_uint64(VC_CONTAINER_IO_T *io, uint64_t value)
+{
+ uint8_t bytes[8] =
+ {
+ (uint8_t) value,
+ (uint8_t)(value >> 8),
+ (uint8_t)(value >> 16),
+ (uint8_t)(value >> 24),
+ (uint8_t)(value >> 32),
+ (uint8_t)(value >> 40),
+ (uint8_t)(value >> 48),
+ (uint8_t)(value >> 56)
+ };
+ size_t ret = vc_container_io_write(io, bytes, 8);
+ return ret == 8 ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FAILED;
+}
+
+/*****************************************************************************
+ * Helper macros for accessing the i/o stream. These will also call the right
+ * functions depending on the endianness defined.
+ *****************************************************************************/
+
+/** Macro which returns the current position within the stream */
+#define STREAM_POSITION(ctx) (ctx)->priv->io->offset
+/** Macro which returns true if the end of stream has been reached */
+#define STREAM_EOS(ctx) ((ctx)->priv->io->status == VC_CONTAINER_ERROR_EOS)
+/** Macro which returns the status of the stream */
+#define STREAM_STATUS(ctx) (ctx)->priv->io->status
+/** Macro which returns true if an error other than end of stream has occurred */
+#define STREAM_ERROR(ctx) ((ctx)->priv->io->status && (ctx)->priv->io->status != VC_CONTAINER_ERROR_EOS)
+/** Macro which returns true if we can seek into the stream */
+#define STREAM_SEEKABLE(ctx) (!((ctx)->priv->io->capabilities & VC_CONTAINER_IO_CAPS_CANT_SEEK))
+
+#define PEEK_BYTES(ctx, buffer, size) vc_container_io_peek((ctx)->priv->io, buffer, (size_t)(size))
+#define READ_BYTES(ctx, buffer, size) vc_container_io_read((ctx)->priv->io, buffer, (size_t)(size))
+#define SKIP_BYTES(ctx, size) vc_container_io_skip((ctx)->priv->io, (size_t)(size))
+#define SEEK(ctx, off) vc_container_io_seek((ctx)->priv->io, (int64_t)(off))
+#define CACHE_BYTES(ctx, size) vc_container_io_cache((ctx)->priv->io, (size_t)(size))
+
+#define _SKIP_GUID(ctx) vc_container_io_skip((ctx)->priv->io, 16)
+#define _SKIP_U8(ctx) (vc_container_io_skip((ctx)->priv->io, 1) != 1)
+#define _SKIP_U16(ctx) (vc_container_io_skip((ctx)->priv->io, 2) != 2)
+#define _SKIP_U24(ctx) (vc_container_io_skip((ctx)->priv->io, 3) != 3)
+#define _SKIP_U32(ctx) (vc_container_io_skip((ctx)->priv->io, 4) != 4)
+#define _SKIP_U64(ctx) (vc_container_io_skip((ctx)->priv->io, 8) != 8)
+#define _SKIP_FOURCC(ctx) (vc_container_io_skip((ctx)->priv->io, 4) != 4)
+
+#define _READ_GUID(ctx, buffer) vc_container_io_read((ctx)->priv->io, buffer, 16)
+#define _READ_U8(ctx) vc_container_io_read_uint8((ctx)->priv->io)
+#define _READ_FOURCC(ctx) vc_container_io_read_fourcc((ctx)->priv->io)
+#define PEEK_GUID(ctx, buffer) vc_container_io_peek((ctx)->priv->io, buffer, 16)
+#define PEEK_U8(ctx) vc_container_io_peek_uint8((ctx)->priv->io)
+#ifdef CONTAINER_IS_BIG_ENDIAN
+# define _READ_U16(ctx) vc_container_io_read_be_uint16((ctx)->priv->io)
+# define _READ_U24(ctx) vc_container_io_read_be_uint24((ctx)->priv->io)
+# define _READ_U32(ctx) vc_container_io_read_be_uint32((ctx)->priv->io)
+# define _READ_U40(ctx) vc_container_io_read_be_uint40((ctx)->priv->io)
+# define _READ_U48(ctx) vc_container_io_read_be_uint48((ctx)->priv->io)
+# define _READ_U56(ctx) vc_container_io_read_be_uint56((ctx)->priv->io)
+# define _READ_U64(ctx) vc_container_io_read_be_uint64((ctx)->priv->io)
+# define PEEK_U16(ctx) vc_container_io_peek_be_uint16((ctx)->priv->io)
+# define PEEK_U24(ctx) vc_container_io_peek_be_uint24((ctx)->priv->io)
+# define PEEK_U32(ctx) vc_container_io_peek_be_uint32((ctx)->priv->io)
+# define PEEK_U64(ctx) vc_container_io_peek_be_uint64((ctx)->priv->io)
+#else
+# define _READ_U16(ctx) vc_container_io_read_le_uint16((ctx)->priv->io)
+# define _READ_U24(ctx) vc_container_io_read_le_uint24((ctx)->priv->io)
+# define _READ_U32(ctx) vc_container_io_read_le_uint32((ctx)->priv->io)
+# define _READ_U40(ctx) vc_container_io_read_le_uint40((ctx)->priv->io)
+# define _READ_U48(ctx) vc_container_io_read_le_uint48((ctx)->priv->io)
+# define _READ_U56(ctx) vc_container_io_read_le_uint56((ctx)->priv->io)
+# define _READ_U64(ctx) vc_container_io_read_le_uint64((ctx)->priv->io)
+# define PEEK_U16(ctx) vc_container_io_peek_le_uint16((ctx)->priv->io)
+# define PEEK_U24(ctx) vc_container_io_peek_le_uint24((ctx)->priv->io)
+# define PEEK_U32(ctx) vc_container_io_peek_le_uint32((ctx)->priv->io)
+# define PEEK_U64(ctx) vc_container_io_peek_le_uint64((ctx)->priv->io)
+#endif
+
+#define WRITE_BYTES(ctx, buffer, size) vc_container_io_write((ctx)->priv->io, buffer, (size_t)(size))
+#define _WRITE_GUID(ctx, buffer) vc_container_io_write((ctx)->priv->io, buffer, 16)
+#define _WRITE_U8(ctx, v) vc_container_io_write_uint8((ctx)->priv->io, v)
+#define _WRITE_FOURCC(ctx, v) vc_container_io_write_fourcc((ctx)->priv->io, v)
+#ifdef CONTAINER_IS_BIG_ENDIAN
+# define _WRITE_U16(ctx, v) vc_container_io_write_be_uint16((ctx)->priv->io, v)
+# define _WRITE_U24(ctx, v) vc_container_io_write_be_uint24((ctx)->priv->io, v)
+# define _WRITE_U32(ctx, v) vc_container_io_write_be_uint32((ctx)->priv->io, v)
+# define _WRITE_U64(ctx, v) vc_container_io_write_be_uint64((ctx)->priv->io, v)
+#else
+# define _WRITE_U16(ctx, v) vc_container_io_write_le_uint16((ctx)->priv->io, v)
+# define _WRITE_U24(ctx, v) vc_container_io_write_le_uint24((ctx)->priv->io, v)
+# define _WRITE_U32(ctx, v) vc_container_io_write_le_uint32((ctx)->priv->io, v)
+# define _WRITE_U64(ctx, v) vc_container_io_write_le_uint64((ctx)->priv->io, v)
+#endif
+
+#ifndef CONTAINER_HELPER_LOG_INDENT
+# define CONTAINER_HELPER_LOG_INDENT(a) 0
+#endif
+
+#ifdef CONTAINER_IS_BIG_ENDIAN
+# define LOG_FORMAT_TYPE_UINT LOG_FORMAT_TYPE_UINT_BE
+# define LOG_FORMAT_TYPE_STRING_UTF16 LOG_FORMAT_TYPE_STRING_UTF16_BE
+#else
+# define LOG_FORMAT_TYPE_UINT LOG_FORMAT_TYPE_UINT_LE
+# define LOG_FORMAT_TYPE_STRING_UTF16 LOG_FORMAT_TYPE_STRING_UTF16_LE
+#endif
+
+#ifndef ENABLE_CONTAINERS_LOG_FORMAT
+#define SKIP_GUID(ctx,n) _SKIP_GUID(ctx)
+#define SKIP_U8(ctx,n) _SKIP_U8(ctx)
+#define SKIP_U16(ctx,n) _SKIP_U16(ctx)
+#define SKIP_U24(ctx,n) _SKIP_U24(ctx)
+#define SKIP_U32(ctx,n) _SKIP_U32(ctx)
+#define SKIP_U64(ctx,n) _SKIP_U64(ctx)
+#define SKIP_FOURCC(ctx,n) _SKIP_FOURCC(ctx)
+#define READ_GUID(ctx,buffer,n) _READ_GUID(ctx,(uint8_t *)buffer)
+#define READ_U8(ctx,n) _READ_U8(ctx)
+#define READ_U16(ctx,n) _READ_U16(ctx)
+#define READ_U24(ctx,n) _READ_U24(ctx)
+#define READ_U32(ctx,n) _READ_U32(ctx)
+#define READ_U40(ctx,n) _READ_U40(ctx)
+#define READ_U48(ctx,n) _READ_U48(ctx)
+#define READ_U56(ctx,n) _READ_U56(ctx)
+#define READ_U64(ctx,n) _READ_U64(ctx)
+#define READ_FOURCC(ctx,n) _READ_FOURCC(ctx)
+#define READ_STRING(ctx,buffer,sz,n) READ_BYTES(ctx,buffer,sz)
+#define READ_STRING_UTF16(ctx,buffer,sz,n) READ_BYTES(ctx,buffer,sz)
+#define SKIP_STRING(ctx,sz,n) SKIP_BYTES(ctx,sz)
+#define SKIP_STRING_UTF16(ctx,sz,n) SKIP_BYTES(ctx,sz)
+#else
+#define SKIP_GUID(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_GUID, 16, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1)
+#define SKIP_U8(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 1, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1)
+#define SKIP_U16(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 2, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1)
+#define SKIP_U24(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 3, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1)
+#define SKIP_U32(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1)
+#define SKIP_U64(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 8, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1)
+#define SKIP_FOURCC(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_FOURCC, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1)
+#define READ_GUID(ctx,buffer,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_GUID, 16, n, (uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), 0)
+#define READ_U8(ctx,n) (uint8_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 1, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0)
+#define READ_U16(ctx,n) (uint16_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 2, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0)
+#define READ_U24(ctx,n) (uint32_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 3, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0)
+#define READ_U32(ctx,n) (uint32_t)vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0)
+#define READ_U40(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 5, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0)
+#define READ_U48(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 6, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0)
+#define READ_U56(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 7, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0)
+#define READ_U64(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_UINT, 8, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0)
+#define READ_FOURCC(ctx,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_FOURCC, 4, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 0)
+#define READ_STRING_UTF16(ctx,buffer,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING_UTF16, sz, n, (uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), 0)
+#define READ_STRING(ctx,buffer,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING, sz, n, (uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), 0)
+#define SKIP_STRING_UTF16(ctx,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING_UTF16, sz, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1)
+#define SKIP_STRING(ctx,sz,n) vc_container_helper_read_debug(ctx, LOG_FORMAT_TYPE_STRING, sz, n, 0, CONTAINER_HELPER_LOG_INDENT(ctx), 1)
+#endif
+
+#ifndef ENABLE_CONTAINERS_LOG_FORMAT
+#define WRITE_GUID(ctx,buffer,n) _WRITE_GUID(ctx,(const uint8_t *)buffer)
+#define WRITE_U8(ctx,v,n) _WRITE_U8(ctx,(uint8_t)(v))
+#define WRITE_FOURCC(ctx,v,n) _WRITE_FOURCC(ctx,(uint32_t)(v))
+#define WRITE_U16(ctx,v,n) _WRITE_U16(ctx,(uint16_t)(v))
+#define WRITE_U24(ctx,v,n) _WRITE_U24(ctx,(uint32_t)(v))
+#define WRITE_U32(ctx,v,n) _WRITE_U32(ctx,(uint32_t)(v))
+#define WRITE_U64(ctx,v,n) _WRITE_U64(ctx,(uint64_t)(v))
+#define WRITE_STRING(ctx,buffer,size,n) WRITE_BYTES(ctx, buffer, size)
+#else
+#define WRITE_GUID(ctx,buffer,n) (vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_GUID, 16, n, UINT64_C(0), (const uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) ? 0 : 16)
+#define WRITE_U8(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 1, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module)
+#define WRITE_FOURCC(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_FOURCC, 4, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module)
+#define WRITE_U16(ctx,v,n) (uint16_t)vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 2, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module)
+#define WRITE_U24(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 3, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module)
+#define WRITE_U32(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 4, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module)
+#define WRITE_U64(ctx,v,n) vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_UINT, 8, n, (uint64_t)(v), 0, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module)
+#define WRITE_STRING(ctx,buffer,size,n) (vc_container_helper_write_debug(ctx, LOG_FORMAT_TYPE_STRING, size, n, UINT64_C(0), (const uint8_t *)buffer, CONTAINER_HELPER_LOG_INDENT(ctx), !(ctx)->priv->io->module) ? 0 : size)
+#endif
+
+#ifdef ENABLE_CONTAINERS_LOG_FORMAT
+#define LOG_FORMAT(ctx, ...) do { if((ctx)->priv->io->module) vc_container_helper_format_debug(ctx, CONTAINER_HELPER_LOG_INDENT(ctx), __VA_ARGS__); } while(0)
+#else
+#define LOG_FORMAT(ctx, ...) do {} while (0)
+#endif
+
+#define LOG_FORMAT_TYPE_UINT_LE 0
+#define LOG_FORMAT_TYPE_UINT_BE 1
+#define LOG_FORMAT_TYPE_STRING 2
+#define LOG_FORMAT_TYPE_STRING_UTF16_LE 3
+#define LOG_FORMAT_TYPE_STRING_UTF16_BE 4
+#define LOG_FORMAT_TYPE_FOURCC 5
+#define LOG_FORMAT_TYPE_GUID 6
+#define LOG_FORMAT_TYPE_HEX 0x100
+
+uint64_t vc_container_helper_int_debug(VC_CONTAINER_T *ctx, int type, uint64_t value, const char *name, int indent);
+uint64_t vc_container_helper_read_debug(VC_CONTAINER_T *ctx, int type, int size, const char *name,
+ uint8_t *buffer, int indent, int b_skip);
+VC_CONTAINER_STATUS_T vc_container_helper_write_debug(VC_CONTAINER_T *ctx, int type, int size, const char *name,
+ uint64_t value, const uint8_t *buffer, int indent, int silent);
+void vc_container_helper_format_debug(VC_CONTAINER_T *ctx, int indent, const char *format, ...);
+
+#endif /* VC_CONTAINERS_IO_HELPERS_H */
+/* End of file */
+/*-----------------------------------------------------------------------------*/
diff --git a/gfx/include/userland/containers/core/containers_list.c b/gfx/include/userland/containers/core/containers_list.c
new file mode 100644
index 0000000000..333f052765
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_list.c
@@ -0,0 +1,221 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+#include
+
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_list.h"
+
+/******************************************************************************
+Defines and constants.
+******************************************************************************/
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+/** Find an entry in the list, or the insertion point.
+ * Uses binary sub-division to find the search item. If index is not NULL, the
+ * index of the matching entry, or the point at which to insert if not found, is
+ * written to that address.
+ *
+ * \param list The list to be searched.
+ * \param entry The entry for which to search.
+ * \param index Set to index of match, or insertion point if not found. May be NULL.
+ * \return True if a match was found, false if not. */
+static bool vc_containers_list_find_index(const VC_CONTAINERS_LIST_T *list,
+ const void *entry,
+ uint32_t *index)
+{
+ const char *entries = (const char *)list->entries;
+ size_t entry_size = list->entry_size;
+ VC_CONTAINERS_LIST_COMPARATOR_T comparator = list->comparator;
+ uint32_t start = 0, end = list->size;
+ uint32_t mid = end >> 1;
+ bool match = false;
+
+ while (mid < end)
+ {
+ int comparison = comparator(entry, entries + mid * entry_size);
+
+ if (comparison < 0)
+ end = mid;
+ else if (comparison > 0)
+ start = mid + 1;
+ else {
+ match = true;
+ break;
+ }
+
+ mid = (start + end) >> 1;
+ }
+
+ if (index) *index = mid;
+ return match;
+}
+
+/******************************************************************************
+Functions exported as part of the API
+******************************************************************************/
+
+/*****************************************************************************/
+VC_CONTAINERS_LIST_T *vc_containers_list_create(uint32_t capacity,
+ size_t entry_size,
+ VC_CONTAINERS_LIST_COMPARATOR_T comparator)
+{
+ VC_CONTAINERS_LIST_T *list;
+
+ list = (VC_CONTAINERS_LIST_T *)malloc(sizeof(VC_CONTAINERS_LIST_T));
+ if (!list)
+ return NULL;
+
+ /* Ensure non-zero capacity, as that signifies a read-only list */
+ if (!capacity) capacity = 1;
+
+ list->entries = malloc(capacity * entry_size);
+ if (!list->entries)
+ {
+ free(list);
+ return NULL;
+ }
+
+ list->size = 0;
+ list->capacity = capacity;
+ list->entry_size = entry_size;
+ list->comparator = comparator;
+
+ return list;
+}
+
+/*****************************************************************************/
+void vc_containers_list_destroy(VC_CONTAINERS_LIST_T *list)
+{
+ /* Avoid trying to destroy read-only lists */
+ if (list && list->capacity)
+ {
+ if (list->entries)
+ free(list->entries);
+ free(list);
+ }
+}
+
+/*****************************************************************************/
+void vc_containers_list_reset(VC_CONTAINERS_LIST_T *list)
+{
+ /* Avoid trying to reset read-only lists */
+ if (list && list->capacity)
+ list->size = 0;
+}
+
+/*****************************************************************************/
+bool vc_containers_list_insert(VC_CONTAINERS_LIST_T *list,
+ void *new_entry,
+ bool allow_duplicates)
+{
+ uint32_t insert_idx;
+ char *insert_ptr;
+ size_t entry_size;
+ bool match;
+
+ if (!list || !list->capacity) return false;
+
+ entry_size = list->entry_size;
+ match = vc_containers_list_find_index(list, new_entry, &insert_idx);
+ insert_ptr = (char *)list->entries + entry_size * insert_idx;
+
+ if (!match || allow_duplicates)
+ {
+ /* Ensure there is space for the new entry */
+ if (list->size == list->capacity)
+ {
+ void *new_entries = realloc(list->entries, (list->size + 1) * entry_size);
+
+ if (!new_entries)
+ return false;
+ list->entries = new_entries;
+ list->capacity++;
+ }
+
+ /* Move up anything above the insertion point */
+ if (insert_idx < list->size)
+ memmove(insert_ptr + entry_size, insert_ptr, (list->size - insert_idx) * entry_size);
+
+ list->size++;
+ }
+
+ /* Copy in the new entry (overwriting the old one if necessary) */
+ memcpy(insert_ptr, new_entry, list->entry_size);
+
+ return true;
+}
+
+/*****************************************************************************/
+bool vc_containers_list_find_entry(const VC_CONTAINERS_LIST_T *list,
+ void *entry)
+{
+ uint32_t index;
+ size_t entry_size;
+
+ if (!vc_containers_list_find_index(list, entry, &index))
+ return false;
+
+ entry_size = list->entry_size;
+ memcpy(entry, (const char *)list->entries + entry_size * index, entry_size);
+
+ return true;
+}
+
+/*****************************************************************************/
+void vc_containers_list_validate(const VC_CONTAINERS_LIST_T *list)
+{
+ uint32_t ii, entry_size;
+ const uint8_t *entry_ptr;
+
+ vc_container_assert(list);
+ vc_container_assert(!list->capacity || list->size <= list->capacity);
+ vc_container_assert(list->entry_size);
+ vc_container_assert(list->comparator);
+ vc_container_assert(list->entries);
+
+ /* Check all entries are in sorted order */
+ entry_ptr = (const uint8_t *)list->entries;
+ entry_size = list->entry_size;
+ for (ii = 1; ii < list->size; ii++)
+ {
+ vc_container_assert(list->comparator(entry_ptr, entry_ptr + entry_size) <= 0);
+ entry_ptr += entry_size;
+ }
+}
diff --git a/gfx/include/userland/containers/core/containers_list.h b/gfx/include/userland/containers/core/containers_list.h
new file mode 100644
index 0000000000..5b089976a5
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_list.h
@@ -0,0 +1,102 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _VC_CONTAINERS_LIST_H_
+#define _VC_CONTAINERS_LIST_H_
+
+#include "containers/containers.h"
+
+/** List entry comparison prototype.
+ * Returns zero if items at a and b match, positive if a is "bigger" than b and
+ * negative if a is "smaller" than b. */
+typedef int (*VC_CONTAINERS_LIST_COMPARATOR_T)(const void *a, const void *b);
+
+/** Sorted list type.
+ * Storage type providing efficient insertion and search via binary sub-division. */
+typedef struct vc_containers_list_tag
+{
+ uint32_t size; /**< Number of defined entries in list */
+ uint32_t capacity; /**< Capacity of list, in entries, or zero for read-only */
+ size_t entry_size; /**< Size of one entry, in bytes */
+ VC_CONTAINERS_LIST_COMPARATOR_T comparator; /**< Entry comparison function */
+ void *entries; /**< Pointer to array of entries */
+} VC_CONTAINERS_LIST_T;
+
+/** Macro to generate a static, read-only list from an array and comparator */
+#define VC_CONTAINERS_STATIC_LIST(L, A, C) static VC_CONTAINERS_LIST_T L = { countof(A), 0, sizeof(*(A)), (VC_CONTAINERS_LIST_COMPARATOR_T)(C), A }
+
+
+/** Create an empty list.
+ * The list is created based on the details provided, minimum capacity one entry.
+ *
+ * \param The initial capacity in entries.
+ * \param entry_size The size of each entry, in bytes.
+ * \param comparator A function for comparing two entries.
+ * \return The new list or NULL. */
+VC_CONTAINERS_LIST_T *vc_containers_list_create(uint32_t capacity, size_t entry_size, VC_CONTAINERS_LIST_COMPARATOR_T comparator);
+
+/** Destroy a list.
+ * Has no effect on a static list.
+ *
+ * \param list The list to be destroyed. */
+void vc_containers_list_destroy(VC_CONTAINERS_LIST_T *list);
+
+/** Reset a list to be empty.
+ * Has no effect on a static list.
+ *
+ * \param list The list to be reset. */
+void vc_containers_list_reset(VC_CONTAINERS_LIST_T *list);
+
+/** Insert an entry into the list.
+ *
+ * \param list The list.
+ * \param new_entry The new entry to be inserted.
+ * \param allow_duplicates Determines whether to insert or overwrite if there
+ * is an existing matching entry.
+ * \return True if the entry has successfully been inserted, false if the list
+ * needed to be enlarged and the memory allocation failed. */
+bool vc_containers_list_insert(VC_CONTAINERS_LIST_T *list, void *new_entry, bool allow_duplicates);
+
+/** Find an entry in the list and fill in the result.
+ * Searches for an entry in the list using the comparator and if found
+ * overwrites the one passed in with the one found.
+ *
+ * \param list The list to search.
+ * \param entry An entry with enough defined to find it in the list, filled in
+ * with the rest if found.
+ * \return True if found, false if not. */
+bool vc_containers_list_find_entry(const VC_CONTAINERS_LIST_T *list, void *entry);
+
+/** Validates a list pointer.
+ * Fields and contents of a list are checked and asserted to be correct. With a
+ * large list this may be slow, so it is recommended only to call this in debug
+ * builds.
+ *
+ * \param list The list to be validated. */
+void vc_containers_list_validate(const VC_CONTAINERS_LIST_T *list);
+
+#endif /* _VC_CONTAINERS_LIST_H_ */
diff --git a/gfx/include/userland/containers/core/containers_loader.c b/gfx/include/userland/containers/core/containers_loader.c
new file mode 100644
index 0000000000..b59730119a
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_loader.c
@@ -0,0 +1,436 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+#include
+
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_loader.h"
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE)
+ #include "vcos_dlfcn.h"
+ #define DL_SUFFIX VCOS_SO_EXT
+ #ifndef DL_PATH_PREFIX
+ #define DL_PATH_PREFIX ""
+ #endif
+#endif
+
+/******************************************************************************
+Type definitions.
+******************************************************************************/
+
+typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_READER_OPEN_FUNC_T)(VC_CONTAINER_T *);
+typedef VC_CONTAINER_STATUS_T (*VC_CONTAINER_WRITER_OPEN_FUNC_T)(VC_CONTAINER_T *);
+
+/******************************************************************************
+Prototypes for local functions
+******************************************************************************/
+
+static void reset_context(VC_CONTAINER_T *p_ctx);
+static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read);
+static void unload_library(void *handle);
+static VC_CONTAINER_READER_OPEN_FUNC_T load_reader(void **handle, const char *name);
+static VC_CONTAINER_READER_OPEN_FUNC_T load_writer(void **handle, const char *name);
+static VC_CONTAINER_READER_OPEN_FUNC_T load_metadata_reader(void **handle, const char *name);
+static const char* container_for_fileext(const char *fileext);
+
+/********************************************************************************
+ List of supported containers
+ ********************************************************************************/
+
+static const char *readers[] =
+{"mp4", "asf", "avi", "mkv", "wav", "flv", "simple", "rawvideo", "mpga", "ps", "rtp", "rtsp", "rcv", "rv9", "qsynth", "binary", 0};
+static const char *writers[] =
+{"mp4", "asf", "avi", "binary", "simple", "rawvideo", 0};
+static const char *metadata_readers[] =
+{"id3", 0};
+
+#if defined(ENABLE_CONTAINERS_STANDALONE)
+VC_CONTAINER_STATUS_T asf_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T avi_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T avi_writer_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T mkv_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T wav_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T binary_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T binary_writer_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T rv9_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T simple_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T simple_writer_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T * );
+VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T * );
+
+VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T * );
+
+static struct
+{
+ const char *name;
+ VC_CONTAINER_READER_OPEN_FUNC_T func;
+} reader_entry_points[] =
+{
+ {"asf", &asf_reader_open},
+ {"avi", &avi_reader_open},
+ {"mpga", &mpga_reader_open},
+ {"mkv", &mkv_reader_open},
+ {"wav", &wav_reader_open},
+ {"mp4", &mp4_reader_open},
+ {"flv", &flv_reader_open},
+ {"ps", &ps_reader_open},
+ {"binary", &binary_reader_open},
+ {"rtp", &rtp_reader_open},
+ {"rtsp", &rtsp_reader_open},
+ {"rcv", &rcv_reader_open},
+ {"rv9", &rv9_reader_open},
+ {"qsynth", &qsynth_reader_open},
+ {"simple", &simple_reader_open},
+ {"rawvideo", &rawvideo_reader_open},
+ {0, 0}
+};
+
+static struct
+{
+ const char *name;
+ VC_CONTAINER_READER_OPEN_FUNC_T func;
+} metadata_reader_entry_points[] =
+{
+ {"id3", &id3_metadata_reader_open},
+ {0, 0}
+};
+
+static struct
+{
+ const char *name;
+ VC_CONTAINER_WRITER_OPEN_FUNC_T func;
+} writer_entry_points[] =
+{
+ {"avi", &avi_writer_open},
+ {"mp4", &mp4_writer_open},
+ {"binary", &binary_writer_open},
+ {"simple", &simple_writer_open},
+ {"rawvideo", &rawvideo_writer_open},
+ {0, 0}
+};
+#endif /* defined(ENABLE_CONTAINERS_STANDALONE) */
+
+/** Table describing the mapping between file extensions and container name.
+ This is only used as optimisation to decide which container to try first.
+ Entries where the file extension and container have the same name can be omitted. */
+static const struct {
+ const char *extension;
+ const char *container;
+} extension_container_mapping[] =
+{
+ { "wma", "asf" },
+ { "wmv", "asf" },
+ { "mov", "mp4" },
+ { "3gp", "mp4" },
+ { "mp2", "mpga" },
+ { "mp3", "mpga" },
+ { "webm", "mkv" },
+ { "mid", "qsynth" },
+ { "mld", "qsynth" },
+ { "mmf", "qsynth" },
+ { 0, 0 }
+};
+
+/********************************************************************************
+ Public functions
+ ********************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_load_reader(VC_CONTAINER_T *p_ctx, const char *fileext)
+{
+ const char *name;
+ void *handle = NULL;
+ VC_CONTAINER_READER_OPEN_FUNC_T func;
+ VC_CONTAINER_STATUS_T status;
+ unsigned int i;
+ int64_t offset;
+
+ vc_container_assert(p_ctx && !p_ctx->priv->module_handle);
+
+ /* FIXME: the missing part here is code that reads a configuration or
+ searches the filesystem for container libraries. Instead, we currently
+ rely on static arrays i.e. 'readers', 'writers', etc. */
+
+ /* Before trying proper container readers, iterate through metadata
+ readers to parse tags concatenated to start/end of stream */
+ for(i = 0; metadata_readers[i]; i++)
+ {
+ if ((func = load_metadata_reader(&handle, metadata_readers[i])) != NULL)
+ {
+ status = (*func)(p_ctx);
+ if(!status && p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx);
+ reset_context(p_ctx);
+ unload_library(handle);
+ if(status == VC_CONTAINER_SUCCESS) break;
+ if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error;
+ }
+ }
+
+ /* Store the current position, in case any containers don't leave the stream
+ at the start, and the IO layer can cope with the seek */
+ offset = p_ctx->priv->io->offset;
+
+ /* Now move to containers, try to find a readers using the file extension to name
+ mapping first */
+ if (fileext && (name = container_for_fileext(fileext)) != NULL && (func = load_reader(&handle, name)) != NULL)
+ {
+ status = (*func)(p_ctx);
+ if(status == VC_CONTAINER_SUCCESS) goto success;
+ unload_library(handle);
+ if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error;
+ }
+
+ /* If there was no suitable mapping, iterate through all readers. */
+ for(i = 0; readers[i]; i++)
+ {
+ if ((func = load_reader(&handle, readers[i])) != NULL)
+ {
+ if(vc_container_io_seek(p_ctx->priv->io, offset) != VC_CONTAINER_SUCCESS)
+ {
+ unload_library(handle);
+ goto error;
+ }
+
+ status = (*func)(p_ctx);
+ if(status == VC_CONTAINER_SUCCESS) goto success;
+ reset_context(p_ctx);
+ unload_library(handle);
+ if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error;
+ }
+ }
+
+ error:
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ success:
+ p_ctx->priv->module_handle = handle;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_load_writer(VC_CONTAINER_T *p_ctx, const char *fileext)
+{
+ const char *name;
+ void *handle = NULL;
+ VC_CONTAINER_WRITER_OPEN_FUNC_T func;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FAILED;
+ unsigned int i;
+
+ vc_container_assert(p_ctx && !p_ctx->priv->module_handle);
+
+ /* Do we have a container mapping for this file extension? */
+ if ((name = container_for_fileext(fileext)) != NULL && (func = load_writer(&handle, name)) != NULL)
+ {
+ status = (*func)(p_ctx);
+ if(status == VC_CONTAINER_SUCCESS) goto success;
+ unload_library(handle);
+ if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error;
+ }
+
+ /* If there was no suitable mapping, iterate through all writers. */
+ for(i = 0; writers[i]; i++)
+ {
+ if ((func = load_writer(&handle, writers[i])) != NULL)
+ {
+ status = (*func)(p_ctx);
+ if(status == VC_CONTAINER_SUCCESS) goto success;
+ unload_library(handle);
+ if (status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED) goto error;
+ }
+ }
+
+ error:
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ success:
+ p_ctx->priv->module_handle = handle;
+ return status;
+}
+
+/*****************************************************************************/
+void vc_container_unload(VC_CONTAINER_T *p_ctx)
+{
+ if (p_ctx->priv->module_handle)
+ {
+ unload_library(p_ctx->priv->module_handle);
+ p_ctx->priv->module_handle = NULL;
+ }
+}
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+static void reset_context(VC_CONTAINER_T *p_ctx)
+{
+ vc_container_assert(p_ctx);
+
+ p_ctx->capabilities = 0;
+ p_ctx->tracks = NULL;
+ p_ctx->tracks_num = 0;
+ p_ctx->drm = NULL;
+ p_ctx->priv->module = NULL;
+ p_ctx->priv->pf_close = NULL;
+ p_ctx->priv->pf_read = NULL;
+ p_ctx->priv->pf_write = NULL;
+ p_ctx->priv->pf_seek = NULL;
+ p_ctx->priv->pf_control = NULL;
+ p_ctx->priv->tmp_io = NULL;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_READER_OPEN_FUNC_T load_reader(void **handle, const char *name)
+{
+ return load_library(handle, name, NULL, 1);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_READER_OPEN_FUNC_T load_writer(void **handle, const char *name)
+{
+ return load_library(handle, name, NULL, 0);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_READER_OPEN_FUNC_T load_metadata_reader(void **handle, const char *name)
+{
+ #define DL_PREFIX_METADATA "metadata_"
+ return load_library(handle, name, DL_PREFIX_METADATA, 1);
+}
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE)
+
+/*****************************************************************************/
+static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read)
+{
+ #define DL_PREFIX_RD "reader_"
+ #define DL_PREFIX_WR "writer_"
+ const char *entrypt_read = {"reader_open"};
+ const char *entrypt_write = {"writer_open"};
+ char *dl_name, *entrypt_name;
+ void *dl_handle;
+ VC_CONTAINER_READER_OPEN_FUNC_T func = NULL;
+ unsigned dl_size, ep_size, name_len = strlen(name) + (ext ? strlen(ext) : 0);
+
+ vc_container_assert(read == 0 || read == 1);
+
+ dl_size = strlen(DL_PATH_PREFIX) + MAX(strlen(DL_PREFIX_RD), strlen(DL_PREFIX_WR)) + name_len + strlen(DL_SUFFIX) + 1;
+ if ((dl_name = malloc(dl_size)) == NULL)
+ return NULL;
+
+ ep_size = name_len + 1 + MAX(strlen(entrypt_read), strlen(entrypt_write)) + 1;
+ if ((entrypt_name = malloc(ep_size)) == NULL)
+ {
+ free(dl_name);
+ return NULL;
+ }
+
+ snprintf(dl_name, dl_size, "%s%s%s%s%s", DL_PATH_PREFIX, read ? DL_PREFIX_RD : DL_PREFIX_WR, ext ? ext : "", name, DL_SUFFIX);
+ snprintf(entrypt_name, ep_size, "%s_%s%s", name, ext ? ext : "", read ? entrypt_read : entrypt_write);
+
+ if ( (dl_handle = vcos_dlopen(dl_name, VCOS_DL_NOW)) != NULL )
+ {
+ /* Try generic entrypoint name before the mangled, full name */
+ func = (VC_CONTAINER_READER_OPEN_FUNC_T)vcos_dlsym(dl_handle, read ? entrypt_read : entrypt_write);
+#if !defined(__VIDEOCORE__) /* The following would be pointless on MW/VideoCore */
+ if (!func) func = (VC_CONTAINER_READER_OPEN_FUNC_T)vcos_dlsym(dl_handle, entrypt_name);
+#endif
+ /* Only return handle if symbol found */
+ if (func)
+ *handle = dl_handle;
+ else
+ vcos_dlclose(dl_handle);
+ }
+
+ free(entrypt_name);
+ free(dl_name);
+ return func;
+}
+
+/*****************************************************************************/
+static void unload_library(void *handle)
+{
+ vcos_dlclose(handle);
+}
+
+#else /* !defined(ENABLE_CONTAINERS_STANDALONE) */
+
+/*****************************************************************************/
+static VC_CONTAINER_READER_OPEN_FUNC_T load_library(void **handle, const char *name, const char *ext, int read)
+{
+ int i;
+ VC_CONTAINER_PARAM_UNUSED(handle);
+ VC_CONTAINER_PARAM_UNUSED(ext);
+
+ if (read)
+ {
+ for (i = 0; reader_entry_points[i].name; i++)
+ if (!strcasecmp(reader_entry_points[i].name, name))
+ return reader_entry_points[i].func;
+
+ for (i = 0; metadata_reader_entry_points[i].name; i++)
+ if (!strcasecmp(metadata_reader_entry_points[i].name, name))
+ return metadata_reader_entry_points[i].func;
+ }
+ else
+ {
+ for (i = 0; writer_entry_points[i].name; i++)
+ if (!strcasecmp(writer_entry_points[i].name, name))
+ return writer_entry_points[i].func;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+static void unload_library(void *handle)
+{
+ (void)handle;
+}
+
+#endif /* !defined(ENABLE_CONTAINERS_STANDALONE) */
+
+/*****************************************************************************/
+static const char* container_for_fileext(const char *fileext)
+{
+ int i;
+
+ for( i = 0; fileext && extension_container_mapping[i].extension; i++ )
+ {
+ if (!strcasecmp( fileext, extension_container_mapping[i].extension ))
+ return extension_container_mapping[i].container;
+ }
+
+ return fileext;
+}
diff --git a/gfx/include/userland/containers/core/containers_loader.h b/gfx/include/userland/containers/core/containers_loader.h
new file mode 100644
index 0000000000..946eaf62a8
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_loader.h
@@ -0,0 +1,41 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef VC_CONTAINERS_LOADER_H
+#define VC_CONTAINERS_LOADER_H
+
+/** Find and attempt to load & open reader, 'fileext' is a hint that can be used
+ to speed up loading. */
+VC_CONTAINER_STATUS_T vc_container_load_reader(VC_CONTAINER_T *p_ctx, const char *fileext);
+
+/** Find and attempt to load & open writer, 'fileext' is a hint used to help in
+ selecting the appropriate container format. */
+VC_CONTAINER_STATUS_T vc_container_load_writer(VC_CONTAINER_T *p_ctx, const char *fileext);
+
+void vc_container_unload(VC_CONTAINER_T *p_ctx);
+
+#endif /* VC_CONTAINERS_LOADER_H */
diff --git a/gfx/include/userland/containers/core/containers_logging.c b/gfx/include/userland/containers/core/containers_logging.c
new file mode 100644
index 0000000000..5a739a38e6
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_logging.c
@@ -0,0 +1,111 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+#include
+#include
+#include "containers/containers.h"
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_logging.h"
+
+#ifndef ENABLE_CONTAINERS_STANDALONE
+# include "vcos.h"
+#endif
+
+#ifdef __ANDROID__
+#define LOG_TAG "ContainersCore"
+#include
+#endif
+
+/* Default verbosity that will be inherited by containers */
+static uint32_t default_verbosity_mask = VC_CONTAINER_LOG_ALL;
+
+/* By default log everything that's not associated with a container context */
+static uint32_t verbosity_mask = VC_CONTAINER_LOG_ALL;
+
+void vc_container_log_set_default_verbosity(uint32_t mask)
+{
+ default_verbosity_mask = mask;
+}
+
+uint32_t vc_container_log_get_default_verbosity(void)
+{
+ return default_verbosity_mask;
+}
+
+void vc_container_log_set_verbosity(VC_CONTAINER_T *ctx, uint32_t mask)
+{
+ if(!ctx) verbosity_mask = mask;
+ else ctx->priv->verbosity = mask;
+}
+
+void vc_container_log(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, ...)
+{
+ uint32_t verbosity = ctx ? ctx->priv->verbosity : verbosity_mask;
+ va_list args;
+
+ // Optimise out the call to vc_container_log_vargs etc. when it won't do anything.
+ if(!(type & verbosity)) return;
+
+ va_start( args, format );
+ vc_container_log_vargs(ctx, type, format, args);
+ va_end( args );
+}
+
+void vc_container_log_vargs(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, va_list args)
+{
+ uint32_t verbosity = ctx ? ctx->priv->verbosity : verbosity_mask;
+
+ // If the verbosity is such that the type doesn't need logging quit now.
+ if(!(type & verbosity)) return;
+
+#ifdef __ANDROID__
+ {
+ // Default to Android's "verbose" level (doesn't usually come out)
+ android_LogPriority logLevel = ANDROID_LOG_VERBOSE;
+
+ // Where type suggest a higher level is required update logLevel.
+ // (Usually type contains only 1 bit as set by the LOG_DEBUG, LOG_ERROR or LOG_INFO macros)
+ if (type & VC_CONTAINER_LOG_ERROR)
+ logLevel = ANDROID_LOG_ERROR;
+ else if (type & VC_CONTAINER_LOG_INFO)
+ logLevel = ANDROID_LOG_INFO;
+ else if (type & VC_CONTAINER_LOG_DEBUG)
+ logLevel = ANDROID_LOG_DEBUG;
+
+ // Actually put the message out.
+ LOG_PRI_VA(logLevel, LOG_TAG, format, args);
+ }
+#else
+#ifndef ENABLE_CONTAINERS_STANDALONE
+ vcos_vlog(format, args);
+#else
+ vprintf(format, args); printf("\n");
+ fflush(0);
+#endif
+#endif
+}
diff --git a/gfx/include/userland/containers/core/containers_logging.h b/gfx/include/userland/containers/core/containers_logging.h
new file mode 100644
index 0000000000..a7c2bd9a1c
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_logging.h
@@ -0,0 +1,77 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_LOGGING_H
+#define VC_CONTAINERS_LOGGING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** \file containers_logging.h
+ * Logging API used by container readers and writers
+ */
+
+typedef enum {
+ VC_CONTAINER_LOG_ERROR = 0x01,
+ VC_CONTAINER_LOG_INFO = 0x02,
+ VC_CONTAINER_LOG_DEBUG = 0x04,
+ VC_CONTAINER_LOG_FORMAT = 0x08,
+ VC_CONTAINER_LOG_ALL = 0xFF
+} VC_CONTAINER_LOG_TYPE_T;
+
+void vc_container_log(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, ...);
+void vc_container_log_vargs(VC_CONTAINER_T *ctx, VC_CONTAINER_LOG_TYPE_T type, const char *format, va_list args);
+void vc_container_log_set_verbosity(VC_CONTAINER_T *ctx, uint32_t mask);
+void vc_container_log_set_default_verbosity(uint32_t mask);
+uint32_t vc_container_log_get_default_verbosity(void);
+
+#define ENABLE_CONTAINER_LOG_ERROR
+#define ENABLE_CONTAINER_LOG_INFO
+
+#ifdef ENABLE_CONTAINER_LOG_DEBUG
+# define LOG_DEBUG(ctx, ...) vc_container_log(ctx, VC_CONTAINER_LOG_DEBUG, __VA_ARGS__)
+#else
+# define LOG_DEBUG(ctx, ...) VC_CONTAINER_PARAM_UNUSED(ctx)
+#endif
+
+#ifdef ENABLE_CONTAINER_LOG_ERROR
+# define LOG_ERROR(ctx, ...) vc_container_log(ctx, VC_CONTAINER_LOG_ERROR, __VA_ARGS__)
+#else
+# define LOG_ERROR(ctx, ...) VC_CONTAINER_PARAM_UNUSED(ctx)
+#endif
+
+#ifdef ENABLE_CONTAINER_LOG_INFO
+# define LOG_INFO(ctx, ...) vc_container_log(ctx, VC_CONTAINER_LOG_INFO, __VA_ARGS__)
+#else
+# define LOG_INFO(ctx, ...) VC_CONTAINER_PARAM_UNUSED(ctx)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VC_CONTAINERS_LOGGING_H */
diff --git a/gfx/include/userland/containers/core/containers_private.h b/gfx/include/userland/containers/core/containers_private.h
new file mode 100644
index 0000000000..8c379c6735
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_private.h
@@ -0,0 +1,183 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_PRIVATE_H
+#define VC_CONTAINERS_PRIVATE_H
+
+/** \file containers_private.h
+ * Private interface for container readers and writers
+ */
+
+#include
+#include "containers/containers.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_io.h"
+#include "containers/core/containers_filters.h"
+#include "containers/packetizers.h"
+#include "containers/core/containers_uri.h"
+
+#define URI_MAX_LEN 256
+
+/** \defgroup VcContainerModuleApi Container Module API
+ * Private interface for modules implementing container readers and writers */
+/* @{ */
+
+/** Track context private to the container reader / writer instance. This private context is used to
+ * store data which shouldn't be exported by the public API. */
+typedef struct VC_CONTAINER_TRACK_PRIVATE_T
+{
+ /** Pointer to the private data of the container module in use */
+ struct VC_CONTAINER_TRACK_MODULE_T *module;
+
+ /** Pointer to the allocated buffer for the track extradata */
+ uint8_t *extradata;
+ /** Size of the allocated buffer for the track extradata */
+ uint32_t extradata_size;
+
+ /** Pointer to the allocated buffer for the track DRM data*/
+ uint8_t *drmdata;
+ /** Size of the allocated buffer for the track DRM data */
+ uint32_t drmdata_size;
+
+ /** Packetizer used by this track */
+ VC_PACKETIZER_T *packetizer;
+
+} VC_CONTAINER_TRACK_PRIVATE_T;
+
+/** Context private to the container reader / writer instance. This private context is used to
+ * store data which shouldn't be exported by the public API. */
+typedef struct VC_CONTAINER_PRIVATE_T
+{
+ /** Pointer to the container i/o instance used to read / write to the container */
+ struct VC_CONTAINER_IO_T *io;
+ /** Pointer to the private data of the container module in use */
+ struct VC_CONTAINER_MODULE_T *module;
+
+ /** Reads a data packet from a container reader.
+ * By default, the reader will read whatever packet comes next in the container and update the
+ * given \ref VC_CONTAINER_PACKET_T structure with this packet's information.
+ * This behaviour can be changed using the \ref VC_CONTAINER_READ_FLAGS_T.\n
+ * \ref VC_CONTAINER_READ_FLAG_INFO will instruct the reader to only return information on the
+ * following packet but not its actual data. The data can be retreived later by issuing another
+ * read request.
+ * \ref VC_CONTAINER_READ_FLAG_FORCE_TRACK will force the reader to read the next packet for the
+ * selected track (as present in the \ref VC_CONTAINER_PACKET_T structure) instead of defaulting
+ * to reading the packet which comes next in the container.
+ * \ref VC_CONTAINER_READ_FLAG_SKIP will instruct the reader to skip the next packet. In this case
+ * it isn't necessary for the caller to pass a pointer to a \ref VC_CONTAINER_PACKET_T structure
+ * unless the \ref VC_CONTAINER_READ_FLAG_INFO is also given.
+ * A combination of all these flags can be used.
+ *
+ * \param context Pointer to the context of the reader to use
+ * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet
+ * This needs to be partially filled before the call (buffer, buffer_size)
+ * \param flags Flags controlling the read operation
+ * \return the status of the operation
+ */
+ VC_CONTAINER_STATUS_T (*pf_read)( VC_CONTAINER_T *context,
+ VC_CONTAINER_PACKET_T *packet, VC_CONTAINER_READ_FLAGS_T flags );
+
+ /** Writes a data packet to a container writer.
+ *
+ * \param context Pointer to the context of the writer to use
+ * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet
+ * \return the status of the operation
+ */
+ VC_CONTAINER_STATUS_T (*pf_write)( struct VC_CONTAINER_T *context,
+ VC_CONTAINER_PACKET_T *packet );
+
+ /** Seek into a container reader.
+ *
+ * \param context Pointer to the context of the reader to use
+ * \param offset Offset to seek to. Used as an input as well as output value.
+ * \param mode Seeking mode requested.
+ * \param flags Flags affecting the seeking operation.
+ * \return the status of the operation
+ */
+ VC_CONTAINER_STATUS_T (*pf_seek)( VC_CONTAINER_T *context, int64_t *offset,
+ VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags);
+
+ /** Extensible control function for container readers and writers.
+ * This function takes a variable number of arguments which will depend on the specific operation.
+ *
+ * \param context Pointer to the VC_CONTAINER_T context to use
+ * \param operation The requested operation
+ * \return the status of the operation
+ */
+ VC_CONTAINER_STATUS_T (*pf_control)( VC_CONTAINER_T *context, VC_CONTAINER_CONTROL_T operation, va_list args );
+
+ /** Closes a container reader / writer module.
+ *
+ * \param context Pointer to the context of the instance to close
+ * \return the status of the operation
+ */
+ VC_CONTAINER_STATUS_T (*pf_close)( struct VC_CONTAINER_T *context );
+
+ /** Pointer to container filter instance used for DRM */
+ struct VC_CONTAINER_FILTER_T *drm_filter;
+
+ /** Pointer to the container module code and symbols*/
+ void *module_handle;
+
+ /** Maximum size of a stream that is being written.
+ * This is set by the client using the control mechanism */
+ int64_t max_size;
+
+ /** Pointer to the temp i/o instance used to write temporary data */
+ struct VC_CONTAINER_IO_T *tmp_io;
+
+ /** Current status of the container (only used for writers to prevent trying to write
+ * more data if one of the writes failed) */
+ VC_CONTAINER_STATUS_T status;
+
+ /** Logging verbosity */
+ uint32_t verbosity;
+
+ /** Uniform Resource Identifier */
+ struct VC_URI_PARTS_T *uri;
+
+ /** Flag specifying whether one of the tracks is being packetized */
+ bool packetizing;
+
+ /** Temporary packet structure used to feed data to the packetizer */
+ VC_CONTAINER_PACKET_T packetizer_packet;
+
+ /** Temporary buffer used by the packetizer */
+ uint8_t *packetizer_buffer;
+
+} VC_CONTAINER_PRIVATE_T;
+
+/* Internal functions */
+VC_CONTAINER_TRACK_T *vc_container_allocate_track( VC_CONTAINER_T *context, unsigned int extra_size );
+void vc_container_free_track( VC_CONTAINER_T *context, VC_CONTAINER_TRACK_T *track );
+VC_CONTAINER_STATUS_T vc_container_track_allocate_extradata( VC_CONTAINER_T *context,
+ VC_CONTAINER_TRACK_T *p_track, unsigned int extra_size );
+VC_CONTAINER_STATUS_T vc_container_track_allocate_drmdata( VC_CONTAINER_T *context,
+ VC_CONTAINER_TRACK_T *p_track, unsigned int size );
+
+/* @} */
+
+#endif /* VC_CONTAINERS_PRIVATE_H */
diff --git a/gfx/include/userland/containers/core/containers_time.h b/gfx/include/userland/containers/core/containers_time.h
new file mode 100644
index 0000000000..e625c34fa6
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_time.h
@@ -0,0 +1,103 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_TIME_H
+#define VC_CONTAINERS_TIME_H
+
+/** \file
+ * Utility functions to help with timestamping of elementary stream frames
+ */
+
+typedef struct VC_CONTAINER_TIME_T
+{
+ uint32_t samplerate_num;
+ uint32_t samplerate_den;
+ uint32_t time_base;
+
+ uint32_t remainder;
+
+ int64_t time;
+
+} VC_CONTAINER_TIME_T;
+
+/*****************************************************************************/
+STATIC_INLINE void vc_container_time_init( VC_CONTAINER_TIME_T *time, uint32_t time_base )
+{
+ time->samplerate_num = 0;
+ time->samplerate_den = 0;
+ time->remainder = 0;
+ time->time_base = time_base;
+ time->time = VC_CONTAINER_TIME_UNKNOWN;
+}
+
+/*****************************************************************************/
+STATIC_INLINE int64_t vc_container_time_get( VC_CONTAINER_TIME_T *time )
+{
+ if (time->time == VC_CONTAINER_TIME_UNKNOWN || !time->samplerate_num || !time->samplerate_den)
+ return VC_CONTAINER_TIME_UNKNOWN;
+ return time->time + time->remainder * (int64_t)time->time_base * time->samplerate_den / time->samplerate_num;
+}
+
+/*****************************************************************************/
+STATIC_INLINE void vc_container_time_set_samplerate( VC_CONTAINER_TIME_T *time, uint32_t samplerate_num, uint32_t samplerate_den )
+{
+ if(time->samplerate_num == samplerate_num &&
+ time->samplerate_den == samplerate_den)
+ return;
+
+ /* We're changing samplerate, we need to reset our remainder */
+ if(time->remainder)
+ time->time = vc_container_time_get( time );
+ time->remainder = 0;
+ time->samplerate_num = samplerate_num;
+ time->samplerate_den = samplerate_den;
+}
+
+/*****************************************************************************/
+STATIC_INLINE void vc_container_time_set( VC_CONTAINER_TIME_T *time, int64_t new_time )
+{
+ if (new_time == VC_CONTAINER_TIME_UNKNOWN)
+ return;
+ time->remainder = 0;
+ time->time = new_time;
+}
+
+/*****************************************************************************/
+STATIC_INLINE int64_t vc_container_time_add( VC_CONTAINER_TIME_T *time, uint32_t samples )
+{
+ uint32_t increment;
+
+ if (time->time == VC_CONTAINER_TIME_UNKNOWN || !time->samplerate_num || !time->samplerate_den)
+ return VC_CONTAINER_TIME_UNKNOWN;
+
+ samples += time->remainder;
+ increment = samples * time->samplerate_den / time->samplerate_num;
+ time->time += increment * time->time_base;
+ time->remainder = samples - increment * time->samplerate_num / time->samplerate_den;
+ return vc_container_time_get(time);
+}
+
+#endif /* VC_CONTAINERS_TIME_H */
diff --git a/gfx/include/userland/containers/core/containers_uri.c b/gfx/include/userland/containers/core/containers_uri.c
new file mode 100644
index 0000000000..b0d3c59af9
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_uri.c
@@ -0,0 +1,1120 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+#include
+
+#include "containers/core/containers_uri.h"
+
+/*****************************************************************************/
+/* Internal types and definitions */
+/*****************************************************************************/
+
+typedef struct VC_URI_QUERY_T
+{
+ char *name;
+ char *value;
+} VC_URI_QUERY_T;
+
+struct VC_URI_PARTS_T
+{
+ char *scheme; /**< Unescaped scheme */
+ char *userinfo; /**< Unescaped userinfo */
+ char *host; /**< Unescaped host name/IP address */
+ char *port; /**< Unescaped port */
+ char *path; /**< Unescaped path */
+ char *path_extension; /**< Unescaped path extension */
+ char *fragment; /**< Unescaped fragment */
+ VC_URI_QUERY_T *queries; /**< Array of queries */
+ uint32_t num_queries; /**< Number of queries in array */
+};
+
+typedef const uint32_t *RESERVED_CHARS_TABLE_T;
+
+/** Reserved character table for scheme component
+ * Controls, space, !"#$%&'()*,/:;<=>?@[\]^`{|} and 0x7F and above reserved. */
+static uint32_t scheme_reserved_chars[8] = {
+ 0xFFFFFFFF, 0xFC0097FF, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/** Reserved character table for userinfo component
+ * Controls, space, "#%/<>?@[\]^`{|} and 0x7F and above reserved. */
+static uint32_t userinfo_reserved_chars[8] = {
+ 0xFFFFFFFF, 0xD000802D, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/** Reserved character table for host component
+ * Controls, space, "#%/<>?@\^`{|} and 0x7F and above reserved. */
+static uint32_t host_reserved_chars[8] = {
+ 0xFFFFFFFF, 0xD000802D, 0x50000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/** Reserved character table for port component
+ * Controls, space, !"#$%&'()*+,/:;<=>?@[\]^`{|} and 0x7F and above reserved. */
+static uint32_t port_reserved_chars[8] = {
+ 0xFFFFFFFF, 0xFC009FFF, 0x78000001, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/** Reserved character table for path component
+ * Controls, space, "#%<>?[\]^`{|} and 0x7F and above reserved. */
+static uint32_t path_reserved_chars[8] = {
+ 0xFFFFFFFF, 0xD000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/** Reserved character table for query component
+ * Controls, space, "#%<>[\]^`{|} and 0x7F and above reserved. */
+static uint32_t query_reserved_chars[8] = {
+ 0xFFFFFFFF, 0x5000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+/** Reserved character table for fragment component
+ * Controls, space, "#%<>[\]^`{|} and 0x7F and above reserved. */
+static uint32_t fragment_reserved_chars[8] = {
+ 0xFFFFFFFF, 0x5000002D, 0x78000000, 0xB8000001, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+#define URI_RESERVED(C, TABLE) (!!((TABLE)[(unsigned char)(C) >> 5] & (1 << ((C) & 0x1F))))
+
+#define SCHEME_DELIMITERS ":/?#"
+#define NETWORK_DELIMITERS "@/?#"
+#define HOST_PORT_DELIMITERS "/?#"
+#define PATH_DELIMITERS "?#"
+#define QUERY_DELIMITERS "#"
+
+/*****************************************************************************/
+/* Internal functions */
+/*****************************************************************************/
+
+static char to_hex(int v)
+{
+ if (v > 9)
+ return 'A' + v - 10;
+ return '0' + v;
+}
+
+/*****************************************************************************/
+static uint32_t from_hex(const char *str, uint32_t str_len)
+{
+ uint32_t val = 0;
+
+ while (str_len--)
+ {
+ char c = *str++;
+ if (c >= '0' && c <= '9')
+ c -= '0';
+ else if (c >= 'A' && c <= 'F')
+ c -= 'A' - 10;
+ else if (c >= 'a' && c <= 'f')
+ c -= 'a' - 10;
+ else
+ c = 0; /* Illegal character (not hex) */
+ val = (val << 4) + c;
+ }
+
+ return val;
+}
+
+/*****************************************************************************/
+static uint32_t escaped_length( const char *str, RESERVED_CHARS_TABLE_T reserved )
+{
+ uint32_t ii;
+ uint32_t esclen = 0;
+ char c;
+
+ for (ii = strlen(str); ii > 0; ii--)
+ {
+ c = *str++;
+ if (URI_RESERVED(c, reserved))
+ {
+ /* Reserved character needs escaping as %xx */
+ esclen += 3;
+ } else {
+ esclen++;
+ }
+ }
+
+ return esclen;
+}
+
+/*****************************************************************************/
+static uint32_t escape_string( const char *str, char *escaped,
+ RESERVED_CHARS_TABLE_T reserved )
+{
+ uint32_t ii;
+ uint32_t esclen = 0;
+
+ if (!str)
+ return 0;
+
+ for (ii = strlen(str); ii > 0; ii--)
+ {
+ char c = *str++;
+
+ if (URI_RESERVED(c, reserved))
+ {
+ escaped[esclen++] = '%';
+ escaped[esclen++] = to_hex((c >> 4) & 0xF);
+ escaped[esclen++] = to_hex(c & 0xF);
+ } else {
+ escaped[esclen++] = c;
+ }
+ }
+
+ return esclen;
+}
+
+/*****************************************************************************/
+static uint32_t unescaped_length( const char *str, uint32_t str_len )
+{
+ uint32_t ii;
+ uint32_t unesclen = 0;
+
+ for (ii = 0; ii < str_len; ii++)
+ {
+ if (*str++ == '%' && (ii + 2) < str_len)
+ {
+ str += 2; /* Should be two hex values next */
+ ii += 2;
+ }
+ unesclen++;
+ }
+
+ return unesclen;
+}
+
+/*****************************************************************************/
+static void unescape_string( const char *str, uint32_t str_len, char *unescaped )
+{
+ uint32_t ii;
+
+ for (ii = 0; ii < str_len; ii++)
+ {
+ char c = *str++;
+
+ if (c == '%' && (ii + 2) < str_len )
+ {
+ c = (char)(from_hex(str, 2) & 0xFF);
+ str += 2;
+ ii += 2;
+ }
+ *unescaped++ = c;
+ }
+
+ *unescaped = '\0';
+}
+
+/*****************************************************************************/
+static char *create_unescaped_string( const char *escstr, uint32_t esclen )
+{
+ char *unescstr;
+
+ unescstr = (char *)malloc(unescaped_length(escstr, esclen) + 1); /* Allow for NUL */
+ if (unescstr)
+ unescape_string(escstr, esclen, unescstr);
+
+ return unescstr;
+}
+
+/*****************************************************************************/
+static bool duplicate_string( const char *src, char **p_dst )
+{
+ if (*p_dst)
+ free(*p_dst);
+
+ if (src)
+ {
+ size_t str_size = strlen(src) + 1;
+
+ *p_dst = (char *)malloc(str_size);
+ if (!*p_dst)
+ return false;
+
+ memcpy(*p_dst, src, str_size);
+ } else
+ *p_dst = NULL;
+
+ return true;
+}
+
+/*****************************************************************************/
+static void release_string( char **str )
+{
+ if (*str)
+ {
+ free(*str);
+ *str = NULL;
+ }
+}
+
+/*****************************************************************************/
+static void to_lower_string( char *str )
+{
+ char c;
+
+ while ((c = *str) != '\0')
+ {
+ if (c >= 'A' && c <= 'Z')
+ *str = c - 'A' + 'a';
+ str++;
+ }
+}
+
+/*****************************************************************************/
+static const char *vc_uri_find_delimiter(const char *str, const char *delimiters)
+{
+ const char *ptr = str;
+ char c;
+
+ while ((c = *ptr) != 0)
+ {
+ if (strchr(delimiters, c) != 0)
+ break;
+ ptr++;
+ }
+
+ return ptr;
+}
+
+/*****************************************************************************/
+static void vc_uri_set_path_extension(VC_URI_PARTS_T *p_uri)
+{
+ char *end;
+
+ if (!p_uri)
+ return;
+
+ p_uri->path_extension = NULL;
+
+ if (!p_uri->path)
+ return;
+
+ /* Look for the magic dot */
+ for (end = p_uri->path + strlen(p_uri->path); *end != '.'; end--)
+ if (end == p_uri->path || *end == '/' || *end == '\\')
+ return;
+
+ p_uri->path_extension = end + 1;
+}
+
+/*****************************************************************************/
+static bool parse_authority( VC_URI_PARTS_T *p_uri, const char *str,
+ uint32_t str_len, const char *userinfo_end )
+{
+ const char *marker = userinfo_end;
+ const char *str_end = str + str_len;
+ char c;
+
+ if (marker)
+ {
+ p_uri->userinfo = create_unescaped_string(str, marker - str);
+ if (!p_uri->userinfo)
+ return false;
+ str = marker + 1; /* Past '@' character */
+ }
+
+ if (*str == '[') /* IPvFuture / IPv6 address */
+ {
+ /* Find end of address marker */
+ for (marker = str; marker < str_end; marker++)
+ {
+ c = *marker;
+ if (c == ']')
+ break;
+ }
+
+ if (marker < str_end)
+ marker++; /* Found marker, move to next character */
+ } else {
+ /* Find port value marker*/
+ for (marker = str; marker < str_end; marker++)
+ {
+ c = *marker;
+ if (c == ':')
+ break;
+ }
+ }
+
+ /* Always store the host, even if empty, to trigger the "://" form of URI */
+ p_uri->host = create_unescaped_string(str, marker - str);
+ if (!p_uri->host)
+ return false;
+ to_lower_string(p_uri->host); /* Host names are case-insensitive */
+
+ if (*marker == ':')
+ {
+ str = marker + 1;
+ p_uri->port = create_unescaped_string(str, str_end - str);
+ if (!p_uri->port)
+ return false;
+ }
+
+ return true;
+}
+
+/*****************************************************************************/
+static bool store_query( VC_URI_PARTS_T *p_uri, const char *name_start,
+ const char *equals_ptr, const char *query_end)
+{
+ uint32_t name_len, value_len;
+
+ if (equals_ptr)
+ {
+ name_len = equals_ptr - name_start;
+ value_len = query_end - equals_ptr - 1; /* Don't include '=' itself */
+ } else {
+ name_len = query_end - name_start;
+ value_len = 0;
+ }
+
+ /* Only store something if there is a name */
+ if (name_len)
+ {
+ char *name, *value = NULL;
+ VC_URI_QUERY_T *p_query;
+
+ if (equals_ptr)
+ {
+ value = create_unescaped_string(equals_ptr + 1, value_len);
+ if (!value)
+ return false;
+ equals_ptr = query_end;
+ }
+
+ name = create_unescaped_string(name_start, name_len);
+ if (!name)
+ {
+ if (value)
+ free(value);
+ return false;
+ }
+
+ /* Store query data in URI structure */
+ p_query = &p_uri->queries[ p_uri->num_queries++ ];
+ p_query->name = name;
+ p_query->value = value;
+ }
+
+ return true;
+}
+
+/*****************************************************************************/
+static bool parse_query( VC_URI_PARTS_T *p_uri, const char *str, uint32_t str_len )
+{
+ uint32_t ii;
+ uint32_t query_count;
+ VC_URI_QUERY_T *queries;
+ const char *name_start = str;
+ const char *equals_ptr = NULL;
+ char c;
+
+ if (!str_len)
+ return true;
+
+ /* Scan for the number of query items, so array can be allocated the right size */
+ query_count = 1; /* At least */
+ for (ii = 0; ii < str_len; ii++)
+ {
+ c = str[ii];
+
+ if (c == '&' || c ==';')
+ query_count++;
+ }
+
+ queries = (VC_URI_QUERY_T *)malloc(query_count * sizeof(VC_URI_QUERY_T));
+ if (!queries)
+ return false;
+
+ p_uri->queries = queries;
+
+ /* Go back and parse the string for each query item and store in array */
+ for (ii = 0; ii < str_len; ii++)
+ {
+ c = *str;
+
+ /* Take first '=' as break between name and value */
+ if (c == '=' && !equals_ptr)
+ equals_ptr = str;
+
+ /* If at the end of the name or name/value pair */
+ if (c == '&' || c ==';')
+ {
+ if (!store_query(p_uri, name_start, equals_ptr, str))
+ return false;
+
+ equals_ptr = NULL;
+ name_start = str + 1;
+ }
+
+ str++;
+ }
+
+ return store_query(p_uri, name_start, equals_ptr, str);
+}
+
+/*****************************************************************************/
+static uint32_t calculate_uri_length(const VC_URI_PARTS_T *p_uri)
+{
+ uint32_t length = 0;
+ uint32_t count;
+
+ /* With no scheme, assume this is a plain path (without escaping) */
+ if (!p_uri->scheme)
+ return p_uri->path ? strlen(p_uri->path) : 0;
+
+ length += escaped_length(p_uri->scheme, scheme_reserved_chars);
+ length++; /* for the colon */
+
+ if (p_uri->host)
+ {
+ length += escaped_length(p_uri->host, host_reserved_chars) + 2; /* for the double slash */
+ if (p_uri->userinfo)
+ length += escaped_length(p_uri->userinfo, userinfo_reserved_chars) + 1; /* for the '@' */
+ if (p_uri->port)
+ length += escaped_length(p_uri->port, port_reserved_chars) + 1; /* for the ':' */
+ }
+
+ if (p_uri->path)
+ length += escaped_length(p_uri->path, path_reserved_chars);
+
+ count = p_uri->num_queries;
+ if (count)
+ {
+ VC_URI_QUERY_T * queries = p_uri->queries;
+
+ while (count--)
+ {
+ /* The name is preceded by either the '?' or the '&' */
+ length += escaped_length(queries->name, query_reserved_chars) + 1;
+
+ /* The value is optional, but if present will require an '=' */
+ if (queries->value)
+ length += escaped_length(queries->value, query_reserved_chars) + 1;
+ queries++;
+ }
+ }
+
+ if (p_uri->fragment)
+ length += escaped_length(p_uri->fragment, fragment_reserved_chars) + 1; /* for the '#' */
+
+ return length;
+}
+
+/*****************************************************************************/
+static void build_uri(const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size)
+{
+ uint32_t count;
+
+ /* With no scheme, assume this is a plain path (without escaping) */
+ if (!p_uri->scheme)
+ {
+ if (p_uri->path)
+ strncpy(buffer, p_uri->path, buffer_size);
+ else
+ buffer[0] = '\0';
+ return;
+ }
+
+ buffer += escape_string(p_uri->scheme, buffer, scheme_reserved_chars);
+ *buffer++ = ':';
+
+ if (p_uri->host)
+ {
+ *buffer++ = '/';
+ *buffer++ = '/';
+ if (p_uri->userinfo)
+ {
+ buffer += escape_string(p_uri->userinfo, buffer, userinfo_reserved_chars);
+ *buffer++ = '@';
+ }
+ buffer += escape_string(p_uri->host, buffer, host_reserved_chars);
+ if (p_uri->port)
+ {
+ *buffer++ = ':';
+ buffer += escape_string(p_uri->port, buffer, port_reserved_chars);
+ }
+ }
+
+ if (p_uri->path)
+ buffer += escape_string(p_uri->path, buffer, path_reserved_chars);
+
+ count = p_uri->num_queries;
+ if (count)
+ {
+ VC_URI_QUERY_T * queries = p_uri->queries;
+
+ *buffer++ = '?';
+ while (count--)
+ {
+ buffer += escape_string(queries->name, buffer, query_reserved_chars);
+
+ if (queries->value)
+ {
+ *buffer++ = '=';
+ buffer += escape_string(queries->value, buffer, query_reserved_chars);
+ }
+
+ /* Add separator if there is another item to add */
+ if (count)
+ *buffer++ = '&';
+
+ queries++;
+ }
+ }
+
+ if (p_uri->fragment)
+ {
+ *buffer++ = '#';
+ buffer += escape_string(p_uri->fragment, buffer, fragment_reserved_chars);
+ }
+
+ *buffer = '\0';
+}
+
+/*****************************************************************************/
+static bool vc_uri_copy_base_path( const VC_URI_PARTS_T *base_uri,
+ VC_URI_PARTS_T *relative_uri )
+{
+ const char *base_path = vc_uri_path(base_uri);
+
+ /* No path set (or empty), copy from base */
+ if (!vc_uri_set_path(relative_uri, base_path))
+ return false;
+
+ /* If relative path has no queries, copy base queries across */
+ if (!vc_uri_num_queries(relative_uri))
+ {
+ uint32_t base_queries = vc_uri_num_queries(base_uri);
+ const char *name, *value;
+ uint32_t ii;
+
+ for (ii = 0; ii < base_queries; ii++)
+ {
+ vc_uri_query(base_uri, ii, &name, &value);
+ if (!vc_uri_add_query(relative_uri, name, value))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*****************************************************************************/
+static void vc_uri_remove_single_dot_segments( char *path_str )
+{
+ char *slash = path_str - 1;
+
+ while (slash++)
+ {
+ if (*slash == '.')
+ {
+ switch (slash[1])
+ {
+ case '/': /* Single dot segment, remove it */
+ memmove(slash, slash + 2, strlen(slash + 2) + 1);
+ break;
+ case '\0': /* Trailing single dot, remove it */
+ *slash = '\0';
+ break;
+ default: /* Something else (e.g. ".." or ".foo") */
+ ; /* Do nothing */
+ }
+ }
+ slash = strchr(slash, '/');
+ }
+}
+
+/*****************************************************************************/
+static void vc_uri_remove_double_dot_segments( char *path_str )
+{
+ char *previous_segment = path_str;
+ char *slash;
+
+ if (previous_segment[0] == '/')
+ previous_segment++;
+
+ /* Remove strings of the form "/../" (or "/.." at the end of the path)
+ * as long as is not itself ".." */
+ slash = strchr(previous_segment, '/');
+ while (slash)
+ {
+ if (previous_segment[0] != '.' || previous_segment[1] != '.' || previous_segment[2] != '/')
+ {
+ if (slash[1] == '.' && slash[2] == '.')
+ {
+ bool previous_segment_removed = true;
+
+ switch (slash[3])
+ {
+ case '/': /* "/../" inside path, snip it and last segment out */
+ memmove(previous_segment, slash + 4, strlen(slash + 4) + 1);
+ break;
+ case '\0': /* Trailing "/.." on path, just terminate path at last segment */
+ *previous_segment = '\0';
+ break;
+ default: /* Not a simple ".." segment, so skip over it */
+ previous_segment_removed = false;
+ }
+
+ if (previous_segment_removed)
+ {
+ /* The segment just removed was the first one in the path (optionally
+ * prefixed by a slash), so no more can be removed: stop. */
+ if (previous_segment < path_str + 2)
+ break;
+
+ /* Move back to slash before previous segment, or the start of the path */
+ slash = previous_segment - 1;
+ while (--slash >= path_str && *slash != '/')
+ ; /* Everything done in the while */
+ }
+ }
+ }
+ previous_segment = slash + 1;
+ slash = strchr(previous_segment, '/');
+ }
+}
+
+/*****************************************************************************/
+/* API functions */
+/*****************************************************************************/
+
+VC_URI_PARTS_T *vc_uri_create( void )
+{
+ VC_URI_PARTS_T *p_uri;
+
+ p_uri = (VC_URI_PARTS_T *)malloc(sizeof(VC_URI_PARTS_T));
+ if (p_uri)
+ {
+ memset(p_uri, 0, sizeof(VC_URI_PARTS_T));
+ }
+
+ return p_uri;
+}
+
+/*****************************************************************************/
+void vc_uri_clear( VC_URI_PARTS_T *p_uri )
+{
+ if (!p_uri)
+ return;
+
+ release_string(&p_uri->scheme);
+ release_string(&p_uri->userinfo);
+ release_string(&p_uri->host);
+ release_string(&p_uri->port);
+ release_string(&p_uri->path);
+ release_string(&p_uri->fragment);
+
+ if (p_uri->queries)
+ {
+ VC_URI_QUERY_T *queries = p_uri->queries;
+ uint32_t count = p_uri->num_queries;
+
+ while (count--)
+ {
+ release_string(&queries[count].name);
+ release_string(&queries[count].value);
+ }
+
+ free(queries);
+ p_uri->queries = NULL;
+ p_uri->num_queries = 0;
+ }
+}
+
+/*****************************************************************************/
+void vc_uri_release( VC_URI_PARTS_T *p_uri )
+{
+ if (!p_uri)
+ return;
+
+ vc_uri_clear(p_uri);
+
+ free(p_uri);
+}
+
+/*****************************************************************************/
+bool vc_uri_parse( VC_URI_PARTS_T *p_uri, const char *uri )
+{
+ const char *marker;
+ uint32_t len;
+
+ if (!p_uri || !uri)
+ return false;
+
+ vc_uri_clear(p_uri);
+
+ /* URI = scheme ":" hier_part [ "?" query ] [ "#" fragment ] */
+
+ /* Find end of scheme, or another separator */
+ marker = vc_uri_find_delimiter(uri, SCHEME_DELIMITERS);
+
+ if (*marker == ':')
+ {
+ len = (marker - uri);
+ if (isalpha((int)*uri) && len == 1 && marker[1] == '\\')
+ {
+ /* Looks like a bare, absolute DOS/Windows filename with a drive letter */
+ /* coverity[double_free] Pointer freed and set to NULL */
+ bool ret = duplicate_string(uri, &p_uri->path);
+ vc_uri_set_path_extension(p_uri);
+ return ret;
+ }
+
+ p_uri->scheme = create_unescaped_string(uri, len);
+ if (!p_uri->scheme)
+ goto error;
+
+ to_lower_string(p_uri->scheme); /* Schemes should be handled case-insensitively */
+ uri = marker + 1;
+ }
+
+ if (uri[0] == '/' && uri[1] == '/') /* hier-part includes authority */
+ {
+ const char *userinfo_end = NULL;
+
+ /* authority = [ userinfo "@" ] host [ ":" port ] */
+ uri += 2;
+
+ marker = vc_uri_find_delimiter(uri, NETWORK_DELIMITERS);
+ if (*marker == '@')
+ {
+ userinfo_end = marker;
+ marker = vc_uri_find_delimiter(marker + 1, HOST_PORT_DELIMITERS);
+ }
+
+ if (!parse_authority(p_uri, uri, marker - uri, userinfo_end))
+ goto error;
+ uri = marker;
+ }
+
+ /* path */
+ marker = vc_uri_find_delimiter(uri, PATH_DELIMITERS);
+ len = marker - uri;
+ if (len)
+ {
+ p_uri->path = create_unescaped_string(uri, len);
+ vc_uri_set_path_extension(p_uri);
+ if (!p_uri->path)
+ goto error;
+ }
+
+ /* query */
+ if (*marker == '?')
+ {
+ uri = marker + 1;
+ marker = vc_uri_find_delimiter(uri, QUERY_DELIMITERS);
+ if (!parse_query(p_uri, uri, marker - uri))
+ goto error;
+ }
+
+ /* fragment */
+ if (*marker == '#')
+ {
+ uri = marker + 1;
+ p_uri->fragment = create_unescaped_string(uri, strlen(uri));
+ if (!p_uri->fragment)
+ goto error;
+ }
+
+ return true;
+
+error:
+ vc_uri_clear(p_uri);
+ return false;
+}
+
+/*****************************************************************************/
+uint32_t vc_uri_build( const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size )
+{
+ uint32_t required_length;
+
+ if (!p_uri)
+ return 0;
+
+ required_length = calculate_uri_length(p_uri);
+ if (buffer && required_length < buffer_size) /* Allow for NUL */
+ build_uri(p_uri, buffer, buffer_size);
+
+ return required_length;
+}
+
+/*****************************************************************************/
+const char *vc_uri_scheme( const VC_URI_PARTS_T *p_uri )
+{
+ return p_uri ? p_uri->scheme : NULL;
+}
+
+/*****************************************************************************/
+const char *vc_uri_userinfo( const VC_URI_PARTS_T *p_uri )
+{
+ return p_uri ? p_uri->userinfo : NULL;
+}
+
+/*****************************************************************************/
+const char *vc_uri_host( const VC_URI_PARTS_T *p_uri )
+{
+ return p_uri ? p_uri->host : NULL;
+}
+
+/*****************************************************************************/
+const char *vc_uri_port( const VC_URI_PARTS_T *p_uri )
+{
+ return p_uri ? p_uri->port : NULL;
+}
+
+/*****************************************************************************/
+const char *vc_uri_path( const VC_URI_PARTS_T *p_uri )
+{
+ return p_uri ? p_uri->path : NULL;
+}
+
+/*****************************************************************************/
+const char *vc_uri_path_extension( const VC_URI_PARTS_T *p_uri )
+{
+ return p_uri ? p_uri->path_extension : NULL;
+}
+
+/*****************************************************************************/
+const char *vc_uri_fragment( const VC_URI_PARTS_T *p_uri )
+{
+ return p_uri ? p_uri->fragment : NULL;
+}
+
+/*****************************************************************************/
+uint32_t vc_uri_num_queries( const VC_URI_PARTS_T *p_uri )
+{
+ return p_uri ? p_uri->num_queries : 0;
+}
+
+/*****************************************************************************/
+void vc_uri_query( const VC_URI_PARTS_T *p_uri, uint32_t index, const char **p_name, const char **p_value )
+{
+ const char *name = NULL;
+ const char *value = NULL;
+
+ if (p_uri)
+ {
+ if (index < p_uri->num_queries)
+ {
+ name = p_uri->queries[index].name;
+ value = p_uri->queries[index].value;
+ }
+ }
+
+ if (p_name)
+ *p_name = name;
+ if (p_value)
+ *p_value = value;
+}
+
+/*****************************************************************************/
+bool vc_uri_find_query( VC_URI_PARTS_T *p_uri, uint32_t *p_index, const char *name, const char **p_value )
+{
+ unsigned int i = p_index ? *p_index : 0;
+
+ if (!p_uri)
+ return false;
+
+ for (; name && i < p_uri->num_queries; i++)
+ {
+ if (!strcmp(name, p_uri->queries[i].name))
+ {
+ if (p_value)
+ *p_value = p_uri->queries[i].value;
+ if (p_index)
+ *p_index = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*****************************************************************************/
+bool vc_uri_set_scheme( VC_URI_PARTS_T *p_uri, const char *scheme )
+{
+ return p_uri ? duplicate_string(scheme, &p_uri->scheme) : false;
+}
+
+/*****************************************************************************/
+bool vc_uri_set_userinfo( VC_URI_PARTS_T *p_uri, const char *userinfo )
+{
+ return p_uri ? duplicate_string(userinfo, &p_uri->userinfo) : false;
+}
+
+/*****************************************************************************/
+bool vc_uri_set_host( VC_URI_PARTS_T *p_uri, const char *host )
+{
+ return p_uri ? duplicate_string(host, &p_uri->host) : false;
+}
+
+/*****************************************************************************/
+bool vc_uri_set_port( VC_URI_PARTS_T *p_uri, const char *port )
+{
+ return p_uri ? duplicate_string(port, &p_uri->port) : false;
+}
+
+/*****************************************************************************/
+bool vc_uri_set_path( VC_URI_PARTS_T *p_uri, const char *path )
+{
+ bool ret = p_uri ? duplicate_string(path, &p_uri->path) : false;
+ vc_uri_set_path_extension(p_uri);
+ return ret;
+}
+
+/*****************************************************************************/
+bool vc_uri_set_fragment( VC_URI_PARTS_T *p_uri, const char *fragment )
+{
+ return p_uri ? duplicate_string(fragment, &p_uri->fragment) : false;
+}
+
+/*****************************************************************************/
+bool vc_uri_add_query( VC_URI_PARTS_T *p_uri, const char *name, const char *value )
+{
+ VC_URI_QUERY_T *queries;
+ uint32_t count;
+
+ if (!p_uri || !name)
+ return false;
+
+ count = p_uri->num_queries;
+ if (p_uri->queries)
+ queries = (VC_URI_QUERY_T *)realloc(p_uri->queries, (count + 1) * sizeof(VC_URI_QUERY_T));
+ else
+ queries = (VC_URI_QUERY_T *)malloc(sizeof(VC_URI_QUERY_T));
+
+ if (!queries)
+ return false;
+
+ /* Always store the pointer, in case it has changed, and even if we fail to copy name/value */
+ p_uri->queries = queries;
+ queries[count].name = NULL;
+ queries[count].value = NULL;
+
+ if (duplicate_string(name, &queries[count].name))
+ {
+ if (duplicate_string(value, &queries[count].value))
+ {
+ /* Successful exit path */
+ p_uri->num_queries++;
+ return true;
+ }
+
+ release_string(&queries[count].name);
+ }
+
+ return false;
+}
+
+/*****************************************************************************/
+bool vc_uri_merge( const VC_URI_PARTS_T *base_uri, VC_URI_PARTS_T *relative_uri )
+{
+ bool success = true;
+ const char *relative_path;
+
+ /* If scheme is already set, the URI is already absolute */
+ if (relative_uri->scheme)
+ return true;
+
+ /* Otherwise, copy the base scheme */
+ if (!duplicate_string(base_uri->scheme, &relative_uri->scheme))
+ return false;
+
+ /* If any of the network info is set, use the rest of the relative URI as-is */
+ if (relative_uri->host || relative_uri->port || relative_uri->userinfo)
+ return true;
+
+ /* Otherwise, copy the base network info */
+ if (!duplicate_string(base_uri->host, &relative_uri->host) ||
+ !duplicate_string(base_uri->port, &relative_uri->port) ||
+ !duplicate_string(base_uri->userinfo, &relative_uri->userinfo))
+ return false;
+
+ relative_path = relative_uri->path;
+
+ if (!relative_path || !*relative_path)
+ {
+ /* No relative path (could be queries and/or fragment), so take base path */
+ success = vc_uri_copy_base_path(base_uri, relative_uri);
+ }
+ else if (*relative_path != '/')
+ {
+ const char *base_path = base_uri->path;
+ char *merged_path;
+ char *slash;
+ size_t len;
+
+ /* Path is relative, merge in with base path */
+ if (!base_path || !*base_path)
+ {
+ if (relative_uri->host || relative_uri->port || relative_uri->userinfo)
+ base_path = "/"; /* Need a separator to split network info from path */
+ else
+ base_path = "";
+ }
+
+ len = strlen(base_path) + strlen(relative_path) + 1;
+
+ /* Allocate space for largest possible combined path */
+ merged_path = (char *)malloc(len);
+ if (!merged_path)
+ return false;
+
+ strncpy(merged_path, base_path, len);
+
+ slash = strrchr(merged_path, '/'); /* Note: reverse search */
+ if (*relative_path == ';')
+ {
+ char *semi;
+
+ /* Relative path is just parameters, so remove any base parameters in final segment */
+ if (!slash)
+ slash = merged_path;
+ semi = strchr(slash, ';');
+ if (semi)
+ semi[0] = '\0';
+ } else {
+ /* Remove final segment */
+ if (slash)
+ slash[1] = '\0';
+ else
+ merged_path[0] = '\0';
+ }
+ strncat(merged_path, relative_path, len - strlen(merged_path) - 1);
+
+ vc_uri_remove_single_dot_segments(merged_path);
+ vc_uri_remove_double_dot_segments(merged_path);
+
+ success = duplicate_string(merged_path, &relative_uri->path);
+
+ free(merged_path);
+ }
+ /* Otherwise path is absolute, which can be left as-is */
+
+ return success;
+}
diff --git a/gfx/include/userland/containers/core/containers_uri.h b/gfx/include/userland/containers/core/containers_uri.h
new file mode 100644
index 0000000000..77a06e7f4e
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_uri.h
@@ -0,0 +1,241 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef VC_CONTAINERS_URI_H
+#define VC_CONTAINERS_URI_H
+
+/** \file containers_uri.h
+ * API for parsing and building URI strings as described in RFC3986.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "containers/containers.h"
+
+typedef struct VC_URI_PARTS_T VC_URI_PARTS_T;
+
+/** Create an empty URI structure.
+ *
+ * \return The new URI structure. */
+VC_URI_PARTS_T *vc_uri_create( void );
+
+/** Destroy a URI structure.
+ *
+ * \param p_uri Pointer to a URI parts structure. */
+void vc_uri_release( VC_URI_PARTS_T *p_uri );
+
+/** Clear a URI structure.
+ * Any URI component strings held are released, but the structure itself is not.
+ *
+ * \param p_uri Pointer to a URI parts structure. */
+void vc_uri_clear( VC_URI_PARTS_T *p_uri );
+
+/** Parses and unescapes a URI into the component parts.
+ *
+ * \param p_uri Pointer to a URI parts structure.
+ * \param uri Pointer to a URI string to be parsed.
+ * \return True if successful, false if not. */
+bool vc_uri_parse( VC_URI_PARTS_T *p_uri, const char *uri );
+
+/** Builds the URI component parts into a URI string.
+ * If buffer is NULL, or buffer_size is too small, nothing is written to the
+ * buffer but the required string length is still returned. buffer_size must be
+ * at least one more than the value returned.
+ *
+ * \param p_uri Pointer to a URI parts structure.
+ * \param buffer Pointer to where the URI string is to be built, or NULL.
+ * \param buffer_size Number of bytes available in the buffer.
+ * \return The length of the URI string. */
+uint32_t vc_uri_build( const VC_URI_PARTS_T *p_uri, char *buffer, size_t buffer_size );
+
+/** Retrieves the scheme of the URI.
+ * The string is valid until either the scheme is changed or the URI is released.
+ *
+ * \param p_uri The parsed URI.
+ * \return Pointer to the scheme string. */
+const char *vc_uri_scheme( const VC_URI_PARTS_T *p_uri );
+
+/** Retrieves the userinfo of the URI.
+ * The string is valid until either the userinfo is changed or the URI is released.
+ *
+ * \param p_uri The parsed URI.
+ * \return Pointer to the userinfo string. */
+const char *vc_uri_userinfo( const VC_URI_PARTS_T *p_uri );
+
+/** Retrieves the host of the URI.
+ * The string is valid until either the host is changed or the URI is released.
+ *
+ * \param p_uri The parsed URI.
+ * \return Pointer to the host string. */
+const char *vc_uri_host( const VC_URI_PARTS_T *p_uri );
+
+/** Retrieves the port of the URI.
+ * The string is valid until either the port is changed or the URI is released.
+ *
+ * \param p_uri The parsed URI.
+ * \return Pointer to the port string. */
+const char *vc_uri_port( const VC_URI_PARTS_T *p_uri );
+
+/** Retrieves the path of the URI.
+ * The string is valid until either the path is changed or the URI is released.
+ *
+ * \param p_uri The parsed URI.
+ * \return Pointer to the path string. */
+const char *vc_uri_path( const VC_URI_PARTS_T *p_uri );
+
+/** Retrieves the extension part of the path of the URI.
+ * The string is valid until either the path is changed or the URI is released.
+ *
+ * \param p_uri The parsed URI.
+ * \return Pointer to the extension string. */
+const char *vc_uri_path_extension( const VC_URI_PARTS_T *p_uri );
+
+/** Retrieves the fragment of the URI.
+ * The string is valid until either the fragment is changed or the URI is released.
+ *
+ * \param p_uri The parsed URI.
+ * \return Pointer to the fragment string. */
+const char *vc_uri_fragment( const VC_URI_PARTS_T *p_uri );
+
+/** Returns the number of query name/value pairs stored.
+ *
+ * \param p_uri The parsed URI.
+ * \return Number of queries stored. */
+uint32_t vc_uri_num_queries( const VC_URI_PARTS_T *p_uri );
+
+/** Retrieves a given query's name and value
+ * If either p_name or p_value are NULL, that part of the query is not returned,
+ * otherwise it is set to the address of the string (which may itself be NULL).
+ *
+ * \param p_uri The parsed URI.
+ * \param index Selects the query to get.
+ * \param p_name Address of a string pointer to receive query name, or NULL.
+ * \param p_value Address of a string pointer to receive query value, or NULL. */
+void vc_uri_query( const VC_URI_PARTS_T *p_uri, uint32_t index, const char **p_name, const char **p_value );
+
+/** Finds a specific query in the array.
+ * If p_index is NULL, then it is assumed the search should start at index 0,
+ * otherwise the search will start at the specified index and the index will
+ * be updated on return to point to the query which has been found.
+ * If p_value is NULL, that part of the query is not returned,
+ * otherwise it is set to the address of the value string (which may itself be NULL).
+ *
+ * \param p_uri Pointer to a URI parts structure.
+ * \param p_index Index from which to start the search. May be NULL.
+ * \param name Unescaped query name.
+ * \param value Unescaped query value. May be NULL.
+ * \return True if successful, false if not. */
+bool vc_uri_find_query( VC_URI_PARTS_T *p_uri, uint32_t *p_index, const char *name, const char **p_value );
+
+/** Sets the scheme of the URI.
+ * The string will be copied and stored in the URI, releasing and replacing
+ * any existing string. If NULL is passed, any existing string shall simply be
+ * released.
+ *
+ * \param p_uri The parsed URI.
+ * \param scheme Pointer to the new scheme string, or NULL.
+ * \return True if successful, false on memory allocation failure. */
+bool vc_uri_set_scheme( VC_URI_PARTS_T *p_uri, const char *scheme );
+
+/** Sets the userinfo of the URI.
+ * The string will be copied and stored in the URI, releasing and replacing
+ * any existing string. If NULL is passed, any existing string shall simply be
+ * released.
+ *
+ * \param p_uri The parsed URI.
+ * \param userinfo Pointer to the new userinfo string, or NULL.
+ * \return True if successful, false on memory allocation failure. */
+bool vc_uri_set_userinfo( VC_URI_PARTS_T *p_uri, const char *userinfo );
+
+/** Sets the host of the URI.
+ * The string will be copied and stored in the URI, releasing and replacing
+ * any existing string. If NULL is passed, any existing string shall simply be
+ * released.
+ *
+ * \param p_uri The parsed URI.
+ * \param host Pointer to the new host string, or NULL.
+ * \return True if successful, false on memory allocation failure. */
+bool vc_uri_set_host( VC_URI_PARTS_T *p_uri, const char *host );
+
+/** Sets the port of the URI.
+ * The string will be copied and stored in the URI, releasing and replacing
+ * any existing string. If NULL is passed, any existing string shall simply be
+ * released.
+ *
+ * \param p_uri The parsed URI.
+ * \param port Pointer to the new port string, or NULL.
+ * \return True if successful, false on memory allocation failure. */
+bool vc_uri_set_port( VC_URI_PARTS_T *p_uri, const char *port );
+
+/** Sets the path of the URI.
+ * The string will be copied and stored in the URI, releasing and replacing
+ * any existing string. If NULL is passed, any existing string shall simply be
+ * released.
+ *
+ * \param p_uri The parsed URI.
+ * \param path Pointer to the new path string, or NULL.
+ * \return True if successful, false on memory allocation failure. */
+bool vc_uri_set_path( VC_URI_PARTS_T *p_uri, const char *path );
+
+/** Sets the fragment of the URI.
+ * The string will be copied and stored in the URI, releasing and replacing
+ * any existing string. If NULL is passed, any existing string shall simply be
+ * released.
+ *
+ * \param p_uri The parsed URI.
+ * \param fragment Pointer to the new fragment string, or NULL.
+ * \return True if successful, false on memory allocation failure. */
+bool vc_uri_set_fragment( VC_URI_PARTS_T *p_uri, const char *fragment );
+
+/** Adds an query to the array.
+ * Note that the queries pointer may change after this function is called.
+ * May fail due to memory allocation failure or invalid parameters.
+ *
+ * \param p_uri Pointer to a URI parts structure.
+ * \param name Unescaped query name.
+ * \param value Unescaped query value. May be NULL.
+ * \return True if successful, false if not. */
+bool vc_uri_add_query( VC_URI_PARTS_T *p_uri, const char *name, const char *value );
+
+/** Merge a base URI and a relative URI.
+ * In general, where the relative URI does not have a given element, the
+ * corresponding element from the base URI is used. See RFC1808.
+ * The combined URI is left in relative_uri. If the function is unsuccessful,
+ * the relative_uri may have been partially modified.
+ *
+ * \param base_uri Pointer to the base URI parts structure.
+ * \param relative_uri Pointer to the relative URI parts structure.
+ * \return True if successful, false if not. */
+bool vc_uri_merge( const VC_URI_PARTS_T *base_uri, VC_URI_PARTS_T *relative_uri );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VC_CONTAINERS_URI_H */
diff --git a/gfx/include/userland/containers/core/containers_utils.c b/gfx/include/userland/containers/core/containers_utils.c
new file mode 100644
index 0000000000..477093ff37
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_utils.c
@@ -0,0 +1,366 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+
+#include "containers/containers.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_utils.h"
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+#define BITMAPINFOHEADER_SIZE_MAX 40
+#define MAX_EXTENSION_SIZE 4
+
+#define VC_CONTAINER_ES_FORMAT_MAGIC ((uint32_t)VC_FOURCC('m','a','g','f'))
+#define EXTRADATA_SIZE_DEFAULT 32
+#define EXTRADATA_SIZE_MAX (10*1024)
+
+/*****************************************************************************/
+typedef struct VC_CONTAINER_ES_FORMAT_PRIVATE_T
+{
+ VC_CONTAINER_ES_FORMAT_T format;
+ VC_CONTAINER_ES_SPECIFIC_FORMAT_T type;
+
+ uint32_t magic;
+
+ unsigned int extradata_size;
+ uint8_t *extradata;
+
+ uint8_t buffer[EXTRADATA_SIZE_DEFAULT];
+
+} VC_CONTAINER_ES_FORMAT_PRIVATE_T;
+
+/*****************************************************************************/
+VC_CONTAINER_ES_FORMAT_T *vc_container_format_create(unsigned int extradata_size)
+{
+ VC_CONTAINER_ES_FORMAT_PRIVATE_T *private;
+ VC_CONTAINER_STATUS_T status;
+
+ private = malloc(sizeof(*private));
+ if(!private) return 0;
+ memset(private, 0, sizeof(*private));
+
+ private->magic = VC_CONTAINER_ES_FORMAT_MAGIC;
+ private->format.type = (void *)&private->type;
+ private->extradata_size = EXTRADATA_SIZE_DEFAULT;
+
+ status = vc_container_format_extradata_alloc(&private->format, extradata_size);
+ if(status != VC_CONTAINER_SUCCESS)
+ {
+ free(private);
+ return NULL;
+ }
+
+ return &private->format;
+}
+
+/*****************************************************************************/
+void vc_container_format_delete(VC_CONTAINER_ES_FORMAT_T *format)
+{
+ VC_CONTAINER_ES_FORMAT_PRIVATE_T *private = (VC_CONTAINER_ES_FORMAT_PRIVATE_T *)format;
+ vc_container_assert(private->magic == VC_CONTAINER_ES_FORMAT_MAGIC);
+ if(private->extradata) free(private->extradata);
+ free(private);
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_format_extradata_alloc(
+ VC_CONTAINER_ES_FORMAT_T *format, unsigned int size)
+{
+ VC_CONTAINER_ES_FORMAT_PRIVATE_T *private = (VC_CONTAINER_ES_FORMAT_PRIVATE_T *)format;
+ vc_container_assert(private->magic == VC_CONTAINER_ES_FORMAT_MAGIC);
+
+ /* Sanity check the size requested */
+ if(size > EXTRADATA_SIZE_MAX)
+ return VC_CONTAINER_ERROR_CORRUPTED;
+
+ /* Allocate memory if needed */
+ if(private->extradata_size < size)
+ {
+ if(private->extradata) free(private->extradata);
+ private->extradata = malloc(size);
+ if(!private->extradata)
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ private->extradata_size = size;
+ }
+
+ /* Set the fields in the actual format structure */
+ if(private->extradata) private->format.extradata = private->extradata;
+ else private->format.extradata = private->buffer;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_format_copy( VC_CONTAINER_ES_FORMAT_T *p_out,
+ VC_CONTAINER_ES_FORMAT_T *p_in,
+ unsigned int extra_buffer_size)
+{
+ void *type = p_out->type;
+ uint8_t *extradata = p_out->extradata;
+
+ /* Check we have a sufficient buffer to copy the extra data */
+ if(p_in->extradata_size > extra_buffer_size ||
+ (p_in->extradata_size && !p_out->extradata))
+ return VC_CONTAINER_ERROR_BUFFER_TOO_SMALL;
+
+ *p_out->type = *p_in->type;
+ *p_out = *p_in;
+ p_out->type = type;
+ p_out->extradata = extradata;
+ if(p_in->extradata_size)
+ memcpy(p_out->extradata, p_in->extradata, p_in->extradata_size);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+int utf8_from_charset(const char *charset, char *out, unsigned int out_size,
+ const void *in, unsigned int in_size)
+{
+ unsigned int i;
+ const uint16_t *in16 = (const uint16_t *)in;
+ const uint8_t *in8 = (const uint8_t *)in;
+
+ if(out_size < 1) return 1;
+ if(!strcmp(charset, "UTF16-LE")) goto utf16le;
+ if(!strcmp(charset, "UTF8")) goto utf8;
+ else return 1;
+
+ utf16le:
+ for(i = 0; i < in_size / 2 && in16[i] && i < out_size - 1; i++)
+ {
+ out[i] = in16[i];
+ }
+ out[i] = 0;
+ return 0;
+
+ utf8:
+ for(i = 0; i < in_size && in8[i] && i < out_size - 1; i++)
+ {
+ out[i] = in8[i];
+ }
+ out[i] = 0;
+ return 0;
+}
+
+/*****************************************************************************/
+unsigned int vc_container_es_format_to_waveformatex(VC_CONTAINER_ES_FORMAT_T *format,
+ uint8_t *buffer, unsigned int buffer_size)
+{
+ uint16_t waveformat = codec_to_waveformat(format->codec);
+
+ if(format->es_type != VC_CONTAINER_ES_TYPE_AUDIO ||
+ waveformat == WAVE_FORMAT_UNKNOWN) return 0;
+
+ if(!buffer) return format->extradata_size + 18;
+
+ if(buffer_size < format->extradata_size + 18) return 0;
+
+ /* Build a waveformatex header */
+ buffer[0] = waveformat;
+ buffer[1] = waveformat >> 8;
+ buffer[2] = format->type->audio.channels;
+ buffer[3] = 0;
+ buffer[4] = (format->type->audio.sample_rate >> 0) & 0xFF;
+ buffer[5] = (format->type->audio.sample_rate >> 8) & 0xFF;
+ buffer[6] = (format->type->audio.sample_rate >> 16) & 0xFF;
+ buffer[7] = (format->type->audio.sample_rate >> 24) & 0xFF;
+ buffer[8] = (format->bitrate >> 3) & 0xFF;
+ buffer[9] = (format->bitrate >> 11) & 0xFF;
+ buffer[10] = (format->bitrate >> 19) & 0xFF;
+ buffer[11] = (format->bitrate >> 27) & 0xFF;
+ buffer[12] = (format->type->audio.block_align >> 0) & 0xFF;
+ buffer[13] = (format->type->audio.block_align >> 8) & 0xFF;
+ buffer[14] = (format->type->audio.bits_per_sample >> 0) & 0xFF;
+ buffer[15] = (format->type->audio.bits_per_sample >> 8) & 0xFF;
+ buffer[16] = (format->extradata_size >> 0) & 0xFF;
+ buffer[17] = (format->extradata_size >> 8) & 0xFF;
+ memcpy(buffer+18, format->extradata, format->extradata_size);
+ return format->extradata_size + 18;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_waveformatex_to_es_format(uint8_t *p,
+ unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size,
+ VC_CONTAINER_ES_FORMAT_T *format)
+{
+ VC_CONTAINER_FOURCC_T fourcc;
+ uint32_t waveformat_id;
+
+ if(!p || buffer_size < 16) return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ waveformat_id = (p[1] << 8) | p[0];
+ fourcc = waveformat_to_codec(waveformat_id);
+
+ /* Read the waveformatex header */
+ if(extra_offset) *extra_offset = 16;
+ if(extra_size) *extra_size = 0;
+ format->type->audio.channels = p[2];
+ format->type->audio.sample_rate = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4];
+ format->bitrate = ((p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8]) * 8;
+ format->type->audio.block_align = (p[13] << 8) | p[12];
+ format->type->audio.bits_per_sample = (p[15] << 8) | p[14];
+
+ if(waveformat_id == WAVE_FORMAT_PCM && format->type->audio.bits_per_sample == 8)
+ fourcc = VC_CONTAINER_CODEC_PCM_UNSIGNED_LE;
+
+ if(buffer_size >= 18)
+ {
+ if(extra_size)
+ {
+ *extra_size = (p[17] << 8) | p[16];
+ if(*extra_size + 18 > buffer_size) *extra_size = buffer_size - 18;
+ }
+ if(extra_offset) *extra_offset = 18;
+ }
+
+ /* Skip the MPEGLAYER3WAVEFORMAT structure */
+ if(waveformat_id == WAVE_FORMAT_MPEGLAYER3 && extra_size)
+ {
+ if(extra_offset) *extra_offset += *extra_size;
+ *extra_size = 0;
+ }
+
+ format->es_type = VC_CONTAINER_ES_TYPE_AUDIO;
+ format->codec = fourcc;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+unsigned int vc_container_es_format_to_bitmapinfoheader(VC_CONTAINER_ES_FORMAT_T *format,
+ uint8_t *buffer, unsigned int buffer_size)
+{
+ uint32_t fourcc = codec_to_vfw_fourcc(format->codec);
+ uint32_t size = BITMAPINFOHEADER_SIZE_MAX + format->extradata_size;
+
+ if(format->es_type != VC_CONTAINER_ES_TYPE_VIDEO ||
+ fourcc == VC_CONTAINER_CODEC_UNKNOWN) return 0;
+
+ if(!buffer) return size;
+ if(buffer_size < size) return 0;
+
+ /* Build a bitmapinfoheader header */
+ memset(buffer, 0, BITMAPINFOHEADER_SIZE_MAX);
+ buffer[0] = (size >> 0) & 0xFF;
+ buffer[1] = (size >> 8) & 0xFF;
+ buffer[2] = (size >> 16) & 0xFF;
+ buffer[3] = (size >> 24) & 0xFF;
+ buffer[4] = (format->type->video.width >> 0) & 0xFF;
+ buffer[5] = (format->type->video.width >> 8) & 0xFF;
+ buffer[6] = (format->type->video.width >> 16) & 0xFF;
+ buffer[7] = (format->type->video.width >> 24) & 0xFF;
+ buffer[8] = (format->type->video.height >> 0) & 0xFF;
+ buffer[9] = (format->type->video.height >> 8) & 0xFF;
+ buffer[10] = (format->type->video.height >> 16) & 0xFF;
+ buffer[11] = (format->type->video.height >> 24) & 0xFF;
+ memcpy(buffer + 16, &fourcc, 4);
+ memcpy(buffer + BITMAPINFOHEADER_SIZE_MAX, format->extradata, format->extradata_size);
+ return size;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_bitmapinfoheader_to_es_format(uint8_t *p,
+ unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size,
+ VC_CONTAINER_ES_FORMAT_T *format)
+{
+ VC_CONTAINER_FOURCC_T fourcc;
+
+ if(!p || buffer_size < BITMAPINFOHEADER_SIZE_MAX) return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ /* size = (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; */
+ format->type->video.width = (p[7] << 24) | (p[6] << 16) | (p[5] << 8) | p[4];
+ format->type->video.height = (p[11] << 24) | (p[10] << 16) | (p[9] << 8) | p[8];
+ memcpy(&fourcc, p + 16, 4);
+
+ format->es_type = VC_CONTAINER_ES_TYPE_VIDEO;
+ format->codec = vfw_fourcc_to_codec(fourcc);
+
+ /* If no mapping is found from vfw, try a more generic one */
+ if (format->codec == fourcc && (fourcc = fourcc_to_codec(fourcc)) != VC_CONTAINER_CODEC_UNKNOWN)
+ format->codec = fourcc;
+
+ if(extra_offset) *extra_offset = BITMAPINFOHEADER_SIZE_MAX;
+ if(extra_size)
+ {
+ if (buffer_size > BITMAPINFOHEADER_SIZE_MAX)
+ *extra_size = buffer_size - BITMAPINFOHEADER_SIZE_MAX;
+ else
+ *extra_size = 0;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static struct {
+ VC_CONTAINER_METADATA_KEY_T key;
+ const char *name;
+} meta_key_conv[] =
+{ {VC_CONTAINER_METADATA_KEY_TITLE, "title"},
+ {VC_CONTAINER_METADATA_KEY_ARTIST, "artist"},
+ {VC_CONTAINER_METADATA_KEY_ALBUM, "album"},
+ {VC_CONTAINER_METADATA_KEY_DESCRIPTION, "description"},
+ {VC_CONTAINER_METADATA_KEY_YEAR, "year"},
+ {VC_CONTAINER_METADATA_KEY_GENRE, "genre"},
+ {VC_CONTAINER_METADATA_KEY_TRACK, "track"},
+ {VC_CONTAINER_METADATA_KEY_LYRICS, "lyrics"},
+ {VC_CONTAINER_METADATA_KEY_UNKNOWN, 0} };
+
+/*****************************************************************************/
+const char *vc_container_metadata_id_to_string(VC_CONTAINER_METADATA_KEY_T key)
+{
+ int i;
+ for(i = 0; meta_key_conv[i].key != VC_CONTAINER_METADATA_KEY_UNKNOWN; i++ )
+ if(meta_key_conv[i].key == key) break;
+ return meta_key_conv[i].name;
+}
+
+/*****************************************************************************/
+int64_t vc_container_maths_gcd(int64_t a, int64_t b)
+{
+ while(b != 0)
+ {
+ int64_t t = b;
+ b = a % b;
+ a = t;
+ }
+ return a;
+}
+
+/*****************************************************************************/
+void vc_container_maths_rational_simplify(uint32_t *num, uint32_t *den)
+{
+ int64_t div = vc_container_maths_gcd((int64_t)*num, (int64_t)*den);
+ if(div)
+ {
+ *num /= div;
+ *den /= div;
+ }
+}
diff --git a/gfx/include/userland/containers/core/containers_utils.h b/gfx/include/userland/containers/core/containers_utils.h
new file mode 100644
index 0000000000..22349ef7a4
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_utils.h
@@ -0,0 +1,86 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_UTILS_H
+#define VC_CONTAINERS_UTILS_H
+
+#include "containers/containers.h"
+#include "containers/containers_codecs.h"
+#include "containers/core/containers_waveformat.h"
+
+/*****************************************************************************
+ * Type definitions
+ *****************************************************************************/
+
+/** Definition of the Global Unique Identifier type as used by some containers */
+typedef struct GUID_T
+{
+ uint32_t word0;
+ uint16_t short0;
+ uint16_t short1;
+ uint8_t bytes[8];
+
+} GUID_T;
+
+VC_CONTAINER_ES_FORMAT_T *vc_container_format_create(unsigned int extradata_size);
+void vc_container_format_delete(VC_CONTAINER_ES_FORMAT_T *);
+VC_CONTAINER_STATUS_T vc_container_format_extradata_alloc(
+ VC_CONTAINER_ES_FORMAT_T *format, unsigned int size);
+VC_CONTAINER_STATUS_T vc_container_format_copy( VC_CONTAINER_ES_FORMAT_T *p_out,
+ VC_CONTAINER_ES_FORMAT_T *p_in,
+ unsigned int extra_buffer_size );
+int utf8_from_charset(const char *charset, char *out, unsigned int out_size,
+ const void *in, unsigned int in_size);
+const char *vc_container_metadata_id_to_string(VC_CONTAINER_METADATA_KEY_T key);
+
+unsigned int vc_container_es_format_to_waveformatex(VC_CONTAINER_ES_FORMAT_T *format,
+ uint8_t *buffer, unsigned int buffer_size);
+unsigned int vc_container_es_format_to_bitmapinfoheader(VC_CONTAINER_ES_FORMAT_T *format,
+ uint8_t *buffer, unsigned int buffer_size);
+VC_CONTAINER_STATUS_T vc_container_waveformatex_to_es_format(uint8_t *p,
+ unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size,
+ VC_CONTAINER_ES_FORMAT_T *format);
+VC_CONTAINER_STATUS_T vc_container_bitmapinfoheader_to_es_format(uint8_t *p,
+ unsigned int buffer_size, unsigned int *extra_offset, unsigned int *extra_size,
+ VC_CONTAINER_ES_FORMAT_T *format);
+
+/** Find the greatest common denominator of 2 numbers.
+ *
+ * @param a first number
+ * @param b second number
+ *
+ * @return greatest common denominator of a and b
+ */
+int64_t vc_container_maths_gcd(int64_t a, int64_t b);
+
+/** Reduce a rational number to it's simplest form.
+ *
+ * @param num Pointer to the numerator of the rational number to simplify
+ * @param den Pointer to the denominator of the rational number to simplify
+ */
+void vc_container_maths_rational_simplify(uint32_t *num, uint32_t *den);
+
+#endif /* VC_CONTAINERS_UTILS_H */
diff --git a/gfx/include/userland/containers/core/containers_waveformat.h b/gfx/include/userland/containers/core/containers_waveformat.h
new file mode 100644
index 0000000000..ade6b9e650
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_waveformat.h
@@ -0,0 +1,180 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_WAVEFORMAT_H
+#define VC_CONTAINERS_WAVEFORMAT_H
+
+/* WAVE form wFormatTag IDs */
+#define WAVE_FORMAT_UNKNOWN 0x0000 /* Microsoft Corporation */
+#define WAVE_FORMAT_ADPCM 0x0002 /* Microsoft Corporation */
+#define WAVE_FORMAT_IEEE_FLOAT 0x0003 /* Microsoft Corporation */
+#define WAVE_FORMAT_VSELP 0x0004 /* Compaq Computer Corp. */
+#define WAVE_FORMAT_IBM_CVSD 0x0005 /* IBM Corporation */
+#define WAVE_FORMAT_ALAW 0x0006 /* Microsoft Corporation */
+#define WAVE_FORMAT_MULAW 0x0007 /* Microsoft Corporation */
+#define WAVE_FORMAT_DTS 0x0008 /* Microsoft Corporation */
+#define WAVE_FORMAT_DRM 0x0009 /* Microsoft Corporation */
+#define WAVE_FORMAT_WMAUDIO_VOICE 0x000A /* Microsoft Corporation */
+#define WAVE_FORMAT_OKI_ADPCM 0x0010 /* OKI */
+#define WAVE_FORMAT_DVI_ADPCM 0x0011 /* Intel Corporation */
+#define WAVE_FORMAT_IMA_ADPCM (WAVE_FORMAT_DVI_ADPCM) /* Intel Corporation */
+#define WAVE_FORMAT_MEDIASPACE_ADPCM 0x0012 /* Videologic */
+#define WAVE_FORMAT_SIERRA_ADPCM 0x0013 /* Sierra Semiconductor Corp */
+#define WAVE_FORMAT_G723_ADPCM 0x0014 /* Antex Electronics Corporation */
+#define WAVE_FORMAT_DIGISTD 0x0015 /* DSP Solutions, Inc. */
+#define WAVE_FORMAT_DIGIFIX 0x0016 /* DSP Solutions, Inc. */
+#define WAVE_FORMAT_DIALOGIC_OKI_ADPCM 0x0017 /* Dialogic Corporation */
+#define WAVE_FORMAT_MEDIAVISION_ADPCM 0x0018 /* Media Vision, Inc. */
+#define WAVE_FORMAT_CU_CODEC 0x0019 /* Hewlett-Packard Company */
+#define WAVE_FORMAT_YAMAHA_ADPCM 0x0020 /* Yamaha Corporation of America */
+#define WAVE_FORMAT_SONARC 0x0021 /* Speech Compression */
+#define WAVE_FORMAT_DSPGROUP_TRUESPEECH 0x0022 /* DSP Group, Inc */
+#define WAVE_FORMAT_ECHOSC1 0x0023 /* Echo Speech Corporation */
+#define WAVE_FORMAT_AUDIOFILE_AF36 0x0024 /* Virtual Music, Inc. */
+#define WAVE_FORMAT_APTX 0x0025 /* Audio Processing Technology */
+#define WAVE_FORMAT_AUDIOFILE_AF10 0x0026 /* Virtual Music, Inc. */
+#define WAVE_FORMAT_PROSODY_1612 0x0027 /* Aculab plc */
+#define WAVE_FORMAT_LRC 0x0028 /* Merging Technologies S.A. */
+#define WAVE_FORMAT_DOLBY_AC2 0x0030 /* Dolby Laboratories */
+#define WAVE_FORMAT_GSM610 0x0031 /* Microsoft Corporation */
+#define WAVE_FORMAT_MSNAUDIO 0x0032 /* Microsoft Corporation */
+#define WAVE_FORMAT_ANTEX_ADPCME 0x0033 /* Antex Electronics Corporation */
+#define WAVE_FORMAT_CONTROL_RES_VQLPC 0x0034 /* Control Resources Limited */
+#define WAVE_FORMAT_DIGIREAL 0x0035 /* DSP Solutions, Inc. */
+#define WAVE_FORMAT_DIGIADPCM 0x0036 /* DSP Solutions, Inc. */
+#define WAVE_FORMAT_CONTROL_RES_CR10 0x0037 /* Control Resources Limited */
+#define WAVE_FORMAT_NMS_VBXADPCM 0x0038 /* Natural MicroSystems */
+#define WAVE_FORMAT_CS_IMAADPCM 0x0039 /* Crystal Semiconductor IMA ADPCM */
+#define WAVE_FORMAT_ECHOSC3 0x003A /* Echo Speech Corporation */
+#define WAVE_FORMAT_ROCKWELL_ADPCM 0x003B /* Rockwell International */
+#define WAVE_FORMAT_ROCKWELL_DIGITALK 0x003C /* Rockwell International */
+#define WAVE_FORMAT_XEBEC 0x003D /* Xebec Multimedia Solutions Limited */
+#define WAVE_FORMAT_G721_ADPCM 0x0040 /* Antex Electronics Corporation */
+#define WAVE_FORMAT_G728_CELP 0x0041 /* Antex Electronics Corporation */
+#define WAVE_FORMAT_MSG723 0x0042 /* Microsoft Corporation */
+#define WAVE_FORMAT_PANASONIC_G726 0x0045 /* Not official Panasonic G.726 codec */
+#define WAVE_FORMAT_MPEG 0x0050 /* Microsoft Corporation */
+#define WAVE_FORMAT_RT24 0x0052 /* InSoft, Inc. */
+#define WAVE_FORMAT_PAC 0x0053 /* InSoft, Inc. */
+#define WAVE_FORMAT_MPEGLAYER3 0x0055 /* ISO/MPEG Layer3 Format Tag */
+#define WAVE_FORMAT_LUCENT_G723 0x0059 /* Lucent Technologies */
+#define WAVE_FORMAT_CIRRUS 0x0060 /* Cirrus Logic */
+#define WAVE_FORMAT_ESPCM 0x0061 /* ESS Technology */
+#define WAVE_FORMAT_VOXWARE 0x0062 /* Voxware Inc */
+#define WAVE_FORMAT_CANOPUS_ATRAC 0x0063 /* Canopus, co., Ltd. */
+#define WAVE_FORMAT_G726_ADPCM 0x0064 /* APICOM */
+#define WAVE_FORMAT_G722_ADPCM 0x0065 /* APICOM */
+#define WAVE_FORMAT_DSAT_DISPLAY 0x0067 /* Microsoft Corporation */
+#define WAVE_FORMAT_VOXWARE_BYTE_ALIGNED 0x0069 /* Voxware Inc */
+#define WAVE_FORMAT_VOXWARE_AC8 0x0070 /* Voxware Inc */
+#define WAVE_FORMAT_VOXWARE_AC10 0x0071 /* Voxware Inc */
+#define WAVE_FORMAT_VOXWARE_AC16 0x0072 /* Voxware Inc */
+#define WAVE_FORMAT_VOXWARE_AC20 0x0073 /* Voxware Inc */
+#define WAVE_FORMAT_VOXWARE_RT24 0x0074 /* Voxware Inc */
+#define WAVE_FORMAT_VOXWARE_RT29 0x0075 /* Voxware Inc */
+#define WAVE_FORMAT_VOXWARE_RT29HW 0x0076 /* Voxware Inc */
+#define WAVE_FORMAT_VOXWARE_VR12 0x0077 /* Voxware Inc */
+#define WAVE_FORMAT_VOXWARE_VR18 0x0078 /* Voxware Inc */
+#define WAVE_FORMAT_VOXWARE_TQ40 0x0079 /* Voxware Inc */
+#define WAVE_FORMAT_SOFTSOUND 0x0080 /* Softsound, Ltd. */
+#define WAVE_FORMAT_VOXWARE_TQ60 0x0081 /* Voxware Inc */
+#define WAVE_FORMAT_MSRT24 0x0082 /* Microsoft Corporation */
+#define WAVE_FORMAT_G729A 0x0083 /* AT&T Labs, Inc. */
+#define WAVE_FORMAT_MVI_MVI2 0x0084 /* Motion Pixels */
+#define WAVE_FORMAT_DF_G726 0x0085 /* DataFusion Systems (Pty) (Ltd) */
+#define WAVE_FORMAT_DF_GSM610 0x0086 /* DataFusion Systems (Pty) (Ltd) */
+#define WAVE_FORMAT_ISIAUDIO 0x0088 /* Iterated Systems, Inc. */
+#define WAVE_FORMAT_ONLIVE 0x0089 /* OnLive! Technologies, Inc. */
+#define WAVE_FORMAT_SBC24 0x0091 /* Siemens Business Communications Sys */
+#define WAVE_FORMAT_DOLBY_AC3_SPDIF 0x0092 /* Sonic Foundry */
+#define WAVE_FORMAT_MEDIASONIC_G723 0x0093 /* MediaSonic */
+#define WAVE_FORMAT_PROSODY_8KBPS 0x0094 /* Aculab plc */
+#define WAVE_FORMAT_ZYXEL_ADPCM 0x0097 /* ZyXEL Communications, Inc. */
+#define WAVE_FORMAT_PHILIPS_LPCBB 0x0098 /* Philips Speech Processing */
+#define WAVE_FORMAT_PACKED 0x0099 /* Studer Professional Audio AG */
+#define WAVE_FORMAT_MALDEN_PHONYTALK 0x00A0 /* Malden Electronics Ltd. */
+#define WAVE_FORMAT_MP4A 0x00FF /* AAC */
+#define WAVE_FORMAT_RHETOREX_ADPCM 0x0100 /* Rhetorex Inc. */
+#define WAVE_FORMAT_IRAT 0x0101 /* BeCubed Software Inc. */
+#define WAVE_FORMAT_VIVO_G723 0x0111 /* Vivo Software */
+#define WAVE_FORMAT_VIVO_SIREN 0x0112 /* Vivo Software */
+#define WAVE_FORMAT_DIGITAL_G723 0x0123 /* Digital Equipment Corporation */
+#define WAVE_FORMAT_SANYO_LD_ADPCM 0x0125 /* Sanyo Electric Co., Ltd. */
+#define WAVE_FORMAT_SIPROLAB_ACEPLNET 0x0130 /* Sipro Lab Telecom Inc. */
+#define WAVE_FORMAT_SIPROLAB_ACELP4800 0x0131 /* Sipro Lab Telecom Inc. */
+#define WAVE_FORMAT_SIPROLAB_ACELP8V3 0x0132 /* Sipro Lab Telecom Inc. */
+#define WAVE_FORMAT_SIPROLAB_G729 0x0133 /* Sipro Lab Telecom Inc. */
+#define WAVE_FORMAT_SIPROLAB_G729A 0x0134 /* Sipro Lab Telecom Inc. */
+#define WAVE_FORMAT_SIPROLAB_KELVIN 0x0135 /* Sipro Lab Telecom Inc. */
+#define WAVE_FORMAT_G726ADPCM 0x0140 /* Dictaphone Corporation */
+#define WAVE_FORMAT_QUALCOMM_PUREVOICE 0x0150 /* Qualcomm, Inc. */
+#define WAVE_FORMAT_QUALCOMM_HALFRATE 0x0151 /* Qualcomm, Inc. */
+#define WAVE_FORMAT_TUBGSM 0x0155 /* Ring Zero Systems, Inc. */
+#define WAVE_FORMAT_WMAUDIO1 0x0160 /* Microsoft Corporation */
+#define WAVE_FORMAT_WMAUDIO2 0x0161 /* Microsoft Corporation */
+#define WAVE_FORMAT_WMAUDIOPRO 0x0162 /* Microsoft Corporation */
+#define WAVE_FORMAT_WMAUDIO_LOSSLESS 0x0163 /* Microsoft Corporation */
+#define WAVE_FORMAT_UNISYS_NAP_ADPCM 0x0170 /* Unisys Corp. */
+#define WAVE_FORMAT_UNISYS_NAP_ULAW 0x0171 /* Unisys Corp. */
+#define WAVE_FORMAT_UNISYS_NAP_ALAW 0x0172 /* Unisys Corp. */
+#define WAVE_FORMAT_UNISYS_NAP_16K 0x0173 /* Unisys Corp. */
+#define WAVE_FORMAT_CREATIVE_ADPCM 0x0200 /* Creative Labs, Inc */
+#define WAVE_FORMAT_CREATIVE_FASTSPEECH8 0x0202 /* Creative Labs, Inc */
+#define WAVE_FORMAT_CREATIVE_FASTSPEECH10 0x0203 /* Creative Labs, Inc */
+#define WAVE_FORMAT_UHER_ADPCM 0x0210 /* UHER informatic GmbH */
+#define WAVE_FORMAT_QUARTERDECK 0x0220 /* Quarterdeck Corporation */
+#define WAVE_FORMAT_ILINK_VC 0x0230 /* I-link Worldwide */
+#define WAVE_FORMAT_RAW_SPORT 0x0240 /* Aureal Semiconductor */
+#define WAVE_FORMAT_ESST_AC3 0x0241 /* ESS Technology, Inc. */
+#define WAVE_FORMAT_IPI_HSX 0x0250 /* Interactive Products, Inc. */
+#define WAVE_FORMAT_IPI_RPELP 0x0251 /* Interactive Products, Inc. */
+#define WAVE_FORMAT_CS2 0x0260 /* Consistent Software */
+#define WAVE_FORMAT_SONY_SCX 0x0270 /* Sony Corp. */
+#define WAVE_FORMAT_FM_TOWNS_SND 0x0300 /* Fujitsu Corp. */
+#define WAVE_FORMAT_BTV_DIGITAL 0x0400 /* Brooktree Corporation */
+#define WAVE_FORMAT_QDESIGN_MUSIC 0x0450 /* QDesign Corporation */
+#define WAVE_FORMAT_VME_VMPCM 0x0680 /* AT&T Labs, Inc. */
+#define WAVE_FORMAT_TPC 0x0681 /* AT&T Labs, Inc. */
+#define WAVE_FORMAT_OLIGSM 0x1000 /* Ing C. Olivetti & C., S.p.A. */
+#define WAVE_FORMAT_OLIADPCM 0x1001 /* Ing C. Olivetti & C., S.p.A. */
+#define WAVE_FORMAT_OLICELP 0x1002 /* Ing C. Olivetti & C., S.p.A. */
+#define WAVE_FORMAT_OLISBC 0x1003 /* Ing C. Olivetti & C., S.p.A. */
+#define WAVE_FORMAT_OLIOPR 0x1004 /* Ing C. Olivetti & C., S.p.A. */
+#define WAVE_FORMAT_LH_CODEC 0x1100 /* Lernout & Hauspie */
+#define WAVE_FORMAT_NORRIS 0x1400 /* Norris Communications, Inc. */
+#define WAVE_FORMAT_SOUNDSPACE_MUSICOMPRESS 0x1500 /* AT&T Labs, Inc. */
+#define WAVE_FORMAT_DVM 0x2000 /* FAST Multimedia AG */
+#define WAVE_FORMAT_AAC 0x706D /* AAC */
+
+#if !defined(WAVE_FORMAT_EXTENSIBLE)
+#define WAVE_FORMAT_EXTENSIBLE 0xFFFE /* Microsoft */
+#endif // !defined(WAVE_FORMAT_EXTENSIBLE)
+
+#if !defined(WAVE_FORMAT_PCM)
+#define WAVE_FORMAT_PCM 0x0001
+#endif // !defined(WAVE_FORMAT_PCM)
+
+#endif /* VC_CONTAINERS_WAVEFORMAT_H */
diff --git a/gfx/include/userland/containers/core/containers_writer_utils.c b/gfx/include/userland/containers/core/containers_writer_utils.c
new file mode 100644
index 0000000000..583ad93cd5
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_writer_utils.c
@@ -0,0 +1,127 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+
+#include "containers/containers.h"
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_writer_utils.h"
+#include "vcos.h"
+
+#include
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T vc_container_writer_extraio_create(VC_CONTAINER_T *context, const char *uri,
+ VC_CONTAINER_WRITER_EXTRAIO_T *extraio)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_PARAM_UNUSED(context);
+
+ extraio->io = vc_container_io_open( uri, VC_CONTAINER_IO_MODE_WRITE, &status );
+ extraio->refcount = 0;
+ extraio->temp = 0;
+ return status;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_null(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio)
+{
+ return vc_container_writer_extraio_create(context, "null://", extraio);
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_temp(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ unsigned int length = strlen(context->priv->io->uri) + 5;
+ char *uri = malloc(length);
+ if(!uri) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ snprintf(uri, length, "%s.tmp", context->priv->io->uri);
+ status = vc_container_writer_extraio_create(context, uri, extraio);
+ free(uri);
+ extraio->temp = true;
+
+ if(status == VC_CONTAINER_SUCCESS && !context->priv->tmp_io)
+ context->priv->tmp_io = extraio->io;
+
+ return status;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_writer_extraio_delete(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio)
+{
+ VC_CONTAINER_STATUS_T status;
+ char *uri = extraio->temp ? vcos_strdup(extraio->io->uri) : 0;
+
+ while(extraio->refcount) vc_container_writer_extraio_disable(context, extraio);
+ status = vc_container_io_close( extraio->io );
+
+ /* coverity[check_return] On failure the worst case is a file or directory is not removed */
+ if(uri) remove(uri);
+ if(uri) free(uri);
+
+ if(context->priv->tmp_io == extraio->io)
+ context->priv->tmp_io = 0;
+
+ return status;
+}
+
+/*****************************************************************************/
+int64_t vc_container_writer_extraio_enable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio)
+{
+ VC_CONTAINER_IO_T *tmp;
+
+ if(!extraio->refcount)
+ {
+ vc_container_io_seek(extraio->io, INT64_C(0));
+ tmp = context->priv->io;
+ context->priv->io = extraio->io;
+ extraio->io = tmp;
+ }
+ return extraio->refcount++;
+}
+
+/*****************************************************************************/
+int64_t vc_container_writer_extraio_disable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *extraio)
+{
+ VC_CONTAINER_IO_T *tmp;
+
+ if(extraio->refcount)
+ {
+ extraio->refcount--;
+ if(!extraio->refcount)
+ {
+ tmp = context->priv->io;
+ context->priv->io = extraio->io;
+ extraio->io = tmp;
+ }
+ }
+ return extraio->refcount;
+}
diff --git a/gfx/include/userland/containers/core/containers_writer_utils.h b/gfx/include/userland/containers/core/containers_writer_utils.h
new file mode 100644
index 0000000000..e2ccf9e938
--- /dev/null
+++ b/gfx/include/userland/containers/core/containers_writer_utils.h
@@ -0,0 +1,96 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_CONTAINERS_WRITER_UTILS_H
+#define VC_CONTAINERS_WRITER_UTILS_H
+
+/** \file containers_writer_utils.h
+ * Helper functions and macros for container writers
+ */
+
+#include "containers/containers.h"
+#include "containers/containers_codecs.h"
+#include "containers/core/containers_io_helpers.h"
+
+/*****************************************************************************
+ * Helper inline functions to write format specific structus to an i/o stream
+ *****************************************************************************/
+
+STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_write_waveformatex( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_ES_FORMAT_T *format)
+{
+ /* Write waveformatex structure */
+ WRITE_U16(p_ctx, codec_to_waveformat(format->codec), "Codec ID");
+ WRITE_U16(p_ctx, format->type->audio.channels, "Number of Channels");
+ WRITE_U32(p_ctx, format->type->audio.sample_rate, "Samples per Second");
+ WRITE_U32(p_ctx, format->bitrate >> 3, "Average Number of Bytes Per Second");
+ WRITE_U16(p_ctx, format->type->audio.block_align, "Block Alignment");
+ WRITE_U16(p_ctx, format->type->audio.bits_per_sample, "Bits Per Sample");
+ WRITE_U16(p_ctx, format->extradata_size, "Codec Specific Data Size");
+ WRITE_BYTES(p_ctx, format->extradata, format->extradata_size);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+STATIC_INLINE VC_CONTAINER_STATUS_T vc_container_write_bitmapinfoheader( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_ES_FORMAT_T *format)
+{
+ VC_CONTAINER_FOURCC_T fourcc;
+
+ /* Write bitmapinfoheader structure */
+ WRITE_U32(p_ctx, 40, "Format Data Size");
+ WRITE_U32(p_ctx, format->type->video.width, "Image Width");
+ WRITE_U32(p_ctx, format->type->video.height, "Image Height");
+ WRITE_U16(p_ctx, 0, "Reserved");
+ WRITE_U16(p_ctx, 0, "Bits Per Pixel Count");
+ fourcc = codec_to_vfw_fourcc(format->codec);
+ WRITE_BYTES(p_ctx, (char *)&fourcc, 4); /* Compression ID */
+ LOG_FORMAT(p_ctx, "Compression ID: %4.4s", (char *)&fourcc);
+ WRITE_U32(p_ctx, 0, "Image Size");
+ WRITE_U32(p_ctx, 0, "Horizontal Pixels Per Meter");
+ WRITE_U32(p_ctx, 0, "Vertical Pixels Per Meter");
+ WRITE_U32(p_ctx, 0, "Colors Used Count");
+ WRITE_U32(p_ctx, 0, "Important Colors Count");
+
+ WRITE_BYTES(p_ctx, format->extradata, format->extradata_size);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/* Helper functions to create and use extra i/o */
+typedef struct VC_CONTAINER_WRITER_EXTRAIO_T {
+ VC_CONTAINER_IO_T *io;
+ unsigned int refcount;
+ bool temp;
+} VC_CONTAINER_WRITER_EXTRAIO_T;
+
+VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_null(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null);
+VC_CONTAINER_STATUS_T vc_container_writer_extraio_create_temp(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null);
+VC_CONTAINER_STATUS_T vc_container_writer_extraio_delete(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null);
+int64_t vc_container_writer_extraio_enable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null);
+int64_t vc_container_writer_extraio_disable(VC_CONTAINER_T *context, VC_CONTAINER_WRITER_EXTRAIO_T *null);
+
+#endif /* VC_CONTAINERS_WRITER_UTILS_H */
diff --git a/gfx/include/userland/containers/core/packetizers.c b/gfx/include/userland/containers/core/packetizers.c
new file mode 100644
index 0000000000..abfb2d7c80
--- /dev/null
+++ b/gfx/include/userland/containers/core/packetizers.c
@@ -0,0 +1,233 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+
+#include "containers/packetizers.h"
+#include "containers/core/packetizers_private.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_logging.h"
+#include "containers/core/containers_utils.h"
+
+/** List of registered packetizers. */
+static VC_PACKETIZER_REGISTRY_ENTRY_T *registry;
+
+/*****************************************************************************/
+void vc_packetizer_register(VC_PACKETIZER_REGISTRY_ENTRY_T *entry)
+{
+ LOG_DEBUG(0, "registering packetizer %s", entry->name);
+ entry->next = registry;
+ registry = entry;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T vc_packetizer_load(VC_PACKETIZER_T *p_ctx)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ VC_PACKETIZER_REGISTRY_ENTRY_T *entry;
+
+ /* Try all the packetizers until we find the right one */
+ for (entry = registry; entry; entry = entry->next)
+ {
+ status = entry->open(p_ctx);
+ if(status != VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED)
+ break;
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+static void vc_packetizer_unload(VC_PACKETIZER_T *p_ctx)
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+}
+
+/*****************************************************************************/
+VC_PACKETIZER_T *vc_packetizer_open( VC_CONTAINER_ES_FORMAT_T *in,
+ VC_CONTAINER_FOURCC_T out_variant, VC_CONTAINER_STATUS_T *p_status )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_PACKETIZER_T *p_ctx = 0;
+
+ /* Allocate our context before trying out the different packetizers */
+ p_ctx = malloc( sizeof(*p_ctx) + sizeof(*p_ctx->priv));
+ if(!p_ctx) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(p_ctx, 0, sizeof(*p_ctx) + sizeof(*p_ctx->priv));
+ p_ctx->priv = (VC_PACKETIZER_PRIVATE_T *)(p_ctx + 1);
+ bytestream_init( &p_ctx->priv->stream );
+
+ p_ctx->in = vc_container_format_create(in->extradata_size);
+ if(!p_ctx->in) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ p_ctx->out = vc_container_format_create(in->extradata_size);
+ if(!p_ctx->out) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+
+ vc_container_format_copy( p_ctx->in, in, in->extradata_size );
+ p_ctx->in->extradata_size = 0;
+ vc_container_format_copy( p_ctx->out, p_ctx->in, in->extradata_size );
+ p_ctx->in->extradata_size = in->extradata_size;
+ p_ctx->out->extradata = p_ctx->in->extradata;
+ p_ctx->out->extradata_size = p_ctx->in->extradata_size;
+ p_ctx->out->codec_variant = out_variant;
+
+ vc_container_time_init(&p_ctx->priv->time, 1000000);
+
+ status = vc_packetizer_load(p_ctx);
+ if(status != VC_CONTAINER_SUCCESS)
+ goto error;
+
+ end:
+ if(p_status) *p_status = status;
+ return p_ctx;
+
+ error:
+ if(p_ctx) vc_packetizer_close(p_ctx);
+ p_ctx = NULL;
+ goto end;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_packetizer_close( VC_PACKETIZER_T *p_ctx )
+{
+ VC_CONTAINER_BYTESTREAM_T *stream;
+ VC_CONTAINER_PACKET_T *packet, *next;
+
+ if(!p_ctx) return VC_CONTAINER_SUCCESS;
+
+ stream = &p_ctx->priv->stream;
+
+ if(p_ctx->in) vc_container_format_delete(p_ctx->in);
+ if(p_ctx->out) vc_container_format_delete(p_ctx->out);
+ if(p_ctx->priv->pf_close) p_ctx->priv->pf_close(p_ctx);
+ if(p_ctx->priv->module_handle) vc_packetizer_unload(p_ctx);
+
+ /* Free the bytestream */
+ for(packet = stream->first; packet; packet = next)
+ {
+ next = packet->next;
+ if(packet->framework_data) free(packet);
+ }
+
+ free(p_ctx);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_packetizer_push( VC_PACKETIZER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *in)
+{
+ /* Do some sanity checking on packet ? */
+
+ in->framework_data = 0;
+ bytestream_push(&p_ctx->priv->stream, in);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_packetizer_pop( VC_PACKETIZER_T *p_ctx,
+ VC_CONTAINER_PACKET_T **in, VC_PACKETIZER_FLAGS_T flags)
+{
+ VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream;
+ VC_CONTAINER_PACKET_T *packet, *new, **prev;
+
+ /* Release the packets which have been read */
+ while((*in = bytestream_pop(stream)) != NULL)
+ {
+ if(*in && (*in)->framework_data)
+ {
+ free(*in);
+ continue;
+ }
+
+ if(*in)
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ if(!(flags & VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT))
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
+
+ /* Look for the 1st non-framework packet */
+ for (packet = stream->first, prev = &stream->first;
+ packet && packet->framework_data; prev = &packet->next, packet = packet->next);
+
+ if (!packet || (packet && packet->framework_data))
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
+
+ /* We'll currently alloc an internal packet for each packet the client forcefully releases.
+ * We could probably do something a bit more clever than that though. */
+ /* Replace the packet with a newly allocated one */
+ new = malloc(sizeof(*packet) + packet->size);
+ if(!new)
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ *new = *packet;
+ new->framework_data = new;
+ if(!new->next)
+ stream->last = &new->next;
+ if(stream->current == packet)
+ stream->current = new;
+ *prev = new;
+ new->data = (uint8_t *)&new[1];
+ memcpy(new->data, packet->data, packet->size);
+ *in = packet;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_packetizer_read( VC_PACKETIZER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *packet, VC_PACKETIZER_FLAGS_T flags)
+{
+ if(!packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP))
+ return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ if(!packet && (flags & VC_CONTAINER_READ_FLAG_INFO))
+ return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ if(packet && !packet->data &&
+ (!(flags & VC_CONTAINER_READ_FLAG_INFO) &&
+ !(flags & VC_CONTAINER_READ_FLAG_SKIP)))
+ return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+
+ /* Always having a packet structure to work with simplifies things */
+ if(!packet)
+ packet = &p_ctx->priv->packet;
+
+ return p_ctx->priv->pf_packetize(p_ctx, packet, flags);
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_packetizer_reset( VC_PACKETIZER_T *p_ctx )
+{
+ VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream;
+
+ bytestream_skip( stream, stream->bytes - stream->current_offset - stream->offset );
+
+ if (p_ctx->priv->pf_reset)
+ return p_ctx->priv->pf_reset(p_ctx);
+ else
+ return VC_CONTAINER_SUCCESS;
+}
diff --git a/gfx/include/userland/containers/core/packetizers_private.h b/gfx/include/userland/containers/core/packetizers_private.h
new file mode 100644
index 0000000000..ad5f8f59a4
--- /dev/null
+++ b/gfx/include/userland/containers/core/packetizers_private.h
@@ -0,0 +1,111 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_PACKETIZERS_PRIVATE_H
+#define VC_PACKETIZERS_PRIVATE_H
+
+/** \file
+ * Private interface for packetizers
+ */
+
+#include
+#include "containers/packetizers.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_bytestream.h"
+#include "containers/core/containers_time.h"
+
+/** \defgroup VcPacketizerModuleApi Packetizer Module API
+ * Private interface for modules implementing packetizers */
+/* @{ */
+
+/** Context private to the packetizer instance. This private context is used to
+ * store data which shouldn't be exported by the public API. */
+typedef struct VC_PACKETIZER_PRIVATE_T
+{
+ /** Pointer to the private data of the packetizer module in use */
+ struct VC_PACKETIZER_MODULE_T *module;
+
+ /** Bytestream abstraction layer */
+ struct VC_CONTAINER_BYTESTREAM_T stream;
+
+ /** Current stream time */
+ VC_CONTAINER_TIME_T time;
+
+ /** Packetize the bytestream.
+ *
+ * \param context Pointer to the context of the instance of the packetizer
+ * \param out Pointer to the output packet structure which needs to be filled
+ * \param flags Miscellaneous flags controlling the packetizing
+ * \return the status of the operation
+ */
+ VC_CONTAINER_STATUS_T (*pf_packetize)( VC_PACKETIZER_T *context,
+ VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags );
+
+ /** Reset packetizer state.
+ *
+ * \param context Pointer to the context of the instance of the packetizer
+ * \return the status of the operation
+ */
+ VC_CONTAINER_STATUS_T (*pf_reset)( VC_PACKETIZER_T *context );
+
+ /** Closes a packetizer module.
+ *
+ * \param context Pointer to the context of the instance to close
+ * \return the status of the operation
+ */
+ VC_CONTAINER_STATUS_T (*pf_close)( struct VC_PACKETIZER_T *context );
+
+ /** Pointer to the packetizer module code and symbols*/
+ void *module_handle;
+
+ /** Temporary packet structure used when the caller does not provide one */
+ VC_CONTAINER_PACKET_T packet;
+
+} VC_PACKETIZER_PRIVATE_T;
+
+/** Structure used by packetizers to register themselves with the core. */
+typedef struct VC_PACKETIZER_REGISTRY_ENTRY_T
+{
+ struct VC_PACKETIZER_REGISTRY_ENTRY_T *next; /**< To link entries together */
+ const char *name; /**< Name of the packetizer */
+ VC_CONTAINER_STATUS_T (*open)( VC_PACKETIZER_T * ); /**< Called to open packetizer */
+} VC_PACKETIZER_REGISTRY_ENTRY_T;
+
+/** Register a packetizer with the core.
+ *
+ * \param entry Entry to register with the core
+ */
+void vc_packetizer_register(VC_PACKETIZER_REGISTRY_ENTRY_T *entry);
+
+/** Utility macro used to register a packetizer with the core */
+#define VC_PACKETIZER_REGISTER(func, name) \
+VC_CONTAINER_CONSTRUCTOR(func##_register); \
+static VC_PACKETIZER_REGISTRY_ENTRY_T registry_entry = {0, name, func}; \
+void func##_register(void) { vc_packetizer_register(®istry_entry); }
+
+/* @} */
+
+#endif /* VC_PACKETIZERS_PRIVATE_H */
diff --git a/gfx/include/userland/containers/dummy/CMakeLists.txt b/gfx/include/userland/containers/dummy/CMakeLists.txt
new file mode 100644
index 0000000000..46434382f3
--- /dev/null
+++ b/gfx/include/userland/containers/dummy/CMakeLists.txt
@@ -0,0 +1,12 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(writer_dummy ${LIBRARY_TYPE} dummy_writer.c)
+
+target_link_libraries(writer_dummy containers)
+
+install(TARGETS writer_dummy DESTINATION ${VMCS_PLUGIN_DIR})
diff --git a/gfx/include/userland/containers/dummy/dummy_writer.c b/gfx/include/userland/containers/dummy/dummy_writer.c
new file mode 100644
index 0000000000..0430b98f20
--- /dev/null
+++ b/gfx/include/userland/containers/dummy/dummy_writer.c
@@ -0,0 +1,137 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *track[2];
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T dummy_writer_open( VC_CONTAINER_T * );
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+ *****************************************************************************/
+static VC_CONTAINER_STATUS_T dummy_writer_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--)
+ vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]);
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T dummy_writer_write( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *packet )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(packet);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T dummy_writer_control( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_CONTROL_T operation, va_list args )
+{
+ VC_CONTAINER_TRACK_T *track;
+ VC_CONTAINER_PARAM_UNUSED(args);
+
+ switch(operation)
+ {
+ case VC_CONTAINER_CONTROL_TRACK_ADD:
+ if(p_ctx->tracks_num >= 2) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+
+ /* Allocate and initialise track data */
+ p_ctx->tracks[p_ctx->tracks_num] = track = vc_container_allocate_track(p_ctx, 0);
+ if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ p_ctx->tracks_num++;
+ return VC_CONTAINER_SUCCESS;
+
+ case VC_CONTAINER_CONTROL_TRACK_ADD_DONE:
+ return VC_CONTAINER_SUCCESS;
+
+ default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ }
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T dummy_writer_open( VC_CONTAINER_T *p_ctx )
+{
+ const char *extension = vc_uri_path_extension(p_ctx->priv->uri);
+ VC_CONTAINER_MODULE_T *module = 0;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ /* Check if the user has specified a container */
+ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
+
+ /* Check we're the right writer for this */
+ if(!extension)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if(strcasecmp(extension, "dummy"))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks = module->track;
+
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_DYNAMIC_TRACK_ADD;
+
+ p_ctx->priv->pf_close = dummy_writer_close;
+ p_ctx->priv->pf_write = dummy_writer_write;
+ p_ctx->priv->pf_control = dummy_writer_control;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ LOG_DEBUG(p_ctx, "dummy: error opening stream (%i)", status);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak writer_open dummy_writer_open
+#endif
diff --git a/gfx/include/userland/containers/flash/CMakeLists.txt b/gfx/include/userland/containers/flash/CMakeLists.txt
new file mode 100644
index 0000000000..bf5393597d
--- /dev/null
+++ b/gfx/include/userland/containers/flash/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(reader_flv ${LIBRARY_TYPE} flv_reader.c)
+
+target_link_libraries(reader_flv containers)
+
+install(TARGETS reader_flv DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/flash/flv_reader.c b/gfx/include/userland/containers/flash/flv_reader.c
new file mode 100644
index 0000000000..6e15e6b2f0
--- /dev/null
+++ b/gfx/include/userland/containers/flash/flv_reader.c
@@ -0,0 +1,1174 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+/* Work-around for MSVC debugger issue */
+#define VC_CONTAINER_MODULE_T VC_CONTAINER_MODULE_FLV_READER_T
+#define VC_CONTAINER_TRACK_MODULE_T VC_CONTAINER_TRACK_MODULE_FLV_READER_T
+
+//#define ENABLE_FLV_EXTRA_LOGGING
+#define CONTAINER_IS_BIG_ENDIAN
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_index.h"
+#include "containers/core/containers_logging.h"
+#undef CONTAINER_HELPER_LOG_INDENT
+#define CONTAINER_HELPER_LOG_INDENT(a) 0
+
+VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T *p_ctx );
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+#define FLV_TRACKS_MAX 2
+
+#define FLV_TAG_TYPE_AUDIO 8
+#define FLV_TAG_TYPE_VIDEO 9
+#define FLV_TAG_TYPE_METADATA 18
+#define FLV_TAG_HEADER_SIZE 15
+
+#define FLV_SCRIPT_DATA_TYPE_NUMBER 0
+#define FLV_SCRIPT_DATA_TYPE_BOOL 1
+#define FLV_SCRIPT_DATA_TYPE_STRING 2
+#define FLV_SCRIPT_DATA_TYPE_ECMA 8
+#define FLV_SCRIPT_DATA_TYPE_LONGSTRING 12
+
+#define FLV_FLAG_DISCARD 1
+#define FLV_FLAG_KEYFRAME 2
+#define FLV_FLAG_INTERFRAME 4
+
+/******************************************************************************
+Type definitions.
+******************************************************************************/
+typedef struct
+{
+ VC_CONTAINER_STATUS_T status;
+
+ int64_t tag_position; /* position of the current tag we're reading */
+ int64_t data_position; /* position to the start of the data within the tag */
+ int data_offset; /* current position inside the tag's data */
+ int data_size; /* size of the data from the current tag */
+ int tag_prev_size; /* size of the previous tag in the stream */
+ int flags; /* flags for the current tag */
+ uint32_t timestamp; /* timestamp for the current tag */
+ uint32_t track; /* track the current tag belongs to */
+ VC_CONTAINER_INDEX_T *index; /* index of key frames */
+
+} FLV_READER_STATE_T;
+
+typedef struct VC_CONTAINER_TRACK_MODULE_T
+{
+ FLV_READER_STATE_T *state;
+ FLV_READER_STATE_T track_state;
+
+} VC_CONTAINER_TRACK_MODULE_T;
+
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *tracks[FLV_TRACKS_MAX];
+
+ int64_t data_offset; /*< offset to the first FLV tag in the stream */
+
+ FLV_READER_STATE_T state; /*< global state of the reader */
+
+ int audio_track;
+ int video_track;
+
+ uint32_t meta_videodatarate;
+ uint32_t meta_audiodatarate;
+ float meta_framerate;
+ uint32_t meta_width;
+ uint32_t meta_height;
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Static functions within this file.
+******************************************************************************/
+/** Reads an FLV tag header
+ *
+ * @param p_ctx pointer to our context
+ * @param[out] p_prev_size size of the previous tag
+ * @param[out] p_type type of the tag
+ * @param[out] p_type size of the tag
+ * @param[out] p_type timestamp for the tag
+ * @return VC_CONTAINER_SUCCESS on success
+ */
+static VC_CONTAINER_STATUS_T flv_read_tag_header(VC_CONTAINER_T *p_ctx, int *p_prev_size,
+ int *p_type, int *p_size, uint32_t *p_timestamp)
+{
+ int prev_size, type, size;
+ uint32_t timestamp;
+
+ prev_size = READ_U32(p_ctx, "PreviousTagSize");
+ type = READ_U8(p_ctx, "TagType");
+ size = READ_U24(p_ctx, "DataSize");
+ timestamp = READ_U24(p_ctx, "Timestamp");
+ timestamp |= (READ_U8(p_ctx, "TimestampExtended") << 24);
+ SKIP_U24(p_ctx, "StreamID");
+
+ if(p_prev_size) *p_prev_size = prev_size + 4;
+ if(p_type) *p_type = type;
+ if(p_size) *p_size = size;
+ if(p_timestamp) *p_timestamp = timestamp;
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an FLV video data header.
+ * This contains the codec id and the current frame type.
+ *
+ * @param p_ctx pointer to our context
+ * @param[out] codec video codec
+ * @param[out] frame_type type of the current frame
+ * @return VC_CONTAINER_SUCCESS on success
+ */
+static VC_CONTAINER_STATUS_T flv_read_videodata_header(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_FOURCC_T *codec, int *frame_type)
+{
+ uint8_t header = READ_U8(p_ctx, "FrameType/CodecID");
+
+ if(frame_type)
+ *frame_type = (header >> 4) == 1 ? FLV_FLAG_KEYFRAME :
+ (header >> 4) == 3 ? FLV_FLAG_INTERFRAME : 0;
+
+ switch(header &0xF)
+ {
+ case 2: *codec = VC_CONTAINER_CODEC_SPARK; break;
+ case 3: *codec = VC_FOURCC('s','c','r','1'); break; /* screen video */
+ case 4: *codec = VC_CONTAINER_CODEC_VP6; break;
+ case 5: *codec = VC_FOURCC('v','p','6','a'); break; /* vp6 alpha */
+ case 6: *codec = VC_FOURCC('s','c','r','2'); break; /* screen video 2 */
+ case 7: *codec = VC_CONTAINER_CODEC_H264; break;
+ default: *codec = 0; break;
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Get the properties of a video frame
+ * This is only really useful at setup time when trying to detect
+ * the type of content we are dealing with.
+ * This will try to get some of the properties of the video stream
+ * as well as codec configuration data if there is any.
+ *
+ * @param p_ctx pointer to our context
+ * @param track track number this data/tag belongs to
+ * @param size size of the data we are parsing
+ * @return VC_CONTAINER_SUCCESS on success
+ */
+static VC_CONTAINER_STATUS_T flv_read_videodata_properties(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track, int size)
+{
+ VC_CONTAINER_STATUS_T status;
+ int width = 0, height = 0;
+
+ if(track->format->codec == VC_CONTAINER_CODEC_VP6 ||
+ track->format->codec == VC_FOURCC('v','p','6','a'))
+ {
+ /* Just extract the width / height */
+ uint8_t data = _READ_U8(p_ctx);
+ _SKIP_U16(p_ctx);
+ height = _READ_U8(p_ctx) * 16;
+ width = _READ_U8(p_ctx) * 16;
+ width -= data >> 4;
+ height -= data & 0xf;
+ }
+ else if(track->format->codec == VC_CONTAINER_CODEC_H264)
+ {
+ uint8_t type = _READ_U8(p_ctx); size--;
+ if(type || size <= 8) return VC_CONTAINER_ERROR_CORRUPTED;
+ _SKIP_U24(p_ctx); size-=3;
+
+ track->format->codec_variant = VC_FOURCC('a','v','c','C');
+ status = vc_container_track_allocate_extradata(p_ctx, track, size);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size);
+ }
+
+ track->format->type->video.width = width;
+ track->format->type->video.height = height;
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an FLV audio data header.
+ * This contains the codec id, samplerate, number of channels and bitrate.
+ *
+ * @param p_ctx pointer to our context
+ * @param[out] codec audio codec
+ * @param[out] p_samplerate audio sampling rate
+ * @param[out] p_channels number of audio channels
+ * @param[out] p_bps bits per sample
+ * @return VC_CONTAINER_SUCCESS on success
+ */
+static VC_CONTAINER_STATUS_T flv_read_audiodata_header(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_FOURCC_T *codec, int *p_samplerate, int *p_channels, int *p_bps)
+{
+ int samplerate, channels, bps;
+ uint8_t header = _READ_U8(p_ctx);
+
+ switch((header >> 2) & 0x3)
+ {
+ case 0: samplerate = 5512; break;
+ case 1: samplerate = 11025; break;
+ case 2: samplerate = 22050; break;
+ default:
+ case 3: samplerate = 44100; break;
+ }
+
+ channels = 1 << (header & 1);
+ bps = 8 << ((header >> 1) & 1);
+
+ switch(header >> 4)
+ {
+ case 0: *codec = bps == 8 ? VC_CONTAINER_CODEC_PCM_UNSIGNED : VC_CONTAINER_CODEC_PCM_SIGNED; break;
+ case 1: *codec = VC_CONTAINER_CODEC_ADPCM_SWF; break;
+ case 2: *codec = VC_CONTAINER_CODEC_MPGA; break;
+ case 3: *codec = bps == 8 ? VC_CONTAINER_CODEC_PCM_UNSIGNED : VC_CONTAINER_CODEC_PCM_SIGNED_LE; break;
+ case 4: *codec = VC_CONTAINER_CODEC_NELLYMOSER; samplerate = 8000; channels = 1; break;
+ case 5: *codec = VC_CONTAINER_CODEC_NELLYMOSER; samplerate = 16000; channels = 1; break;
+ case 6: *codec = VC_CONTAINER_CODEC_NELLYMOSER; channels = 1; break;
+ case 7: *codec = VC_CONTAINER_CODEC_ALAW; break;
+ case 8: *codec = VC_CONTAINER_CODEC_MULAW; break;
+ case 10: *codec = VC_CONTAINER_CODEC_MP4A; samplerate = 44100; channels = 2; break;
+ case 11: *codec = VC_CONTAINER_CODEC_SPEEX; break;
+ case 14: *codec = VC_CONTAINER_CODEC_MPGA; samplerate = 8000; break;
+ default: *codec = 0; break;
+ }
+
+ if(p_samplerate) *p_samplerate = samplerate;
+ if(p_channels) *p_channels = channels;
+ if(p_bps) *p_bps = bps;
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Get the properties of an audio frame
+ * This is only really useful at setup time when trying to detect
+ * the type of content we are dealing with.
+ * This will try to get some of the properties of the audio stream
+ * as well as codec configuration data if there is any.
+ *
+ * @param p_ctx pointer to our context
+ * @param track track number this data/tag belongs to
+ * @param size size of the data we are parsing
+ * @param samplerate sampling rate of the audio data
+ * @param channels number of channels of the audio data
+ * @param bps bits per sample
+ * @return VC_CONTAINER_SUCCESS on success
+ */
+static VC_CONTAINER_STATUS_T flv_read_audiodata_properties(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track, int size, int samplerate, int channels, int bps)
+{
+ static const int aac_freq[16] = {96000, 88200, 64000, 48000, 44100, 32000, 24000,
+ 22050, 16000, 12000, 11025, 8000, 7350};
+ VC_CONTAINER_STATUS_T status;
+
+ if(track->format->codec == VC_CONTAINER_CODEC_MP4A)
+ {
+ uint8_t *p_data, data = _READ_U8(p_ctx);
+ size--;
+ if(data || size <= 0) return VC_CONTAINER_ERROR_FAILED;
+
+ /* Read the AudioSpecificConfig */
+ status = vc_container_track_allocate_extradata(p_ctx, track, size);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size);
+
+ if(track->format->extradata_size >= 2)
+ {
+ p_data = track->format->extradata;
+ data = ((p_data[0] & 0x7) << 1) | (p_data[1] >> 7);
+ if(data >= countof(aac_freq))
+ return VC_CONTAINER_ERROR_FAILED;
+
+ samplerate = aac_freq[data];
+ channels = (p_data[1] >> 3) & 0xf;
+ if(track->format->extradata_size >= 5 && data == 0xf)
+ {
+ samplerate = ((p_data[1] & 0x7f) << 17) | (p_data[2] << 9) |
+ (p_data[3] << 1) | (p_data[4] >> 7);
+ channels = (p_data[4] >> 3) & 0xf;
+ }
+ }
+ }
+
+ track->format->type->audio.sample_rate = samplerate;
+ track->format->type->audio.channels = channels;
+ track->format->type->audio.bits_per_sample = bps;
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an FLV metadata tag.
+ * This contains metadata information about the stream.
+ * All the data we extract from this will be placed directly in the context.
+ *
+ * @param p_ctx pointer to our context
+ * @param size size of the tag
+ * @return FLV_FILE_NO_ERROR on success
+ */
+static int flv_read_metadata(VC_CONTAINER_T *p_ctx, int size)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+#define MAX_METADATA_STRING_SIZE 25
+ char psz_string[MAX_METADATA_STRING_SIZE+1];
+ uint16_t length, num_values;
+ double f_value;
+ uint64_t u_value;
+ uint8_t type;
+
+ /* We're looking for an onMetaData script */
+ type = READ_U8(p_ctx, "Type"); size--;
+ if(type != FLV_SCRIPT_DATA_TYPE_STRING) return VC_CONTAINER_SUCCESS;
+ length = READ_U16(p_ctx, "StringLength"); size -= 2;
+ if(!length || length > size || length > MAX_METADATA_STRING_SIZE) return VC_CONTAINER_SUCCESS;
+ if(READ_BYTES(p_ctx, psz_string, length) != length) return VC_CONTAINER_SUCCESS;
+ psz_string[length] = 0; size -= length;
+ if(strcmp(psz_string, "onMetaData")) return VC_CONTAINER_SUCCESS;
+ if(size < 5) return VC_CONTAINER_SUCCESS;
+ type = READ_U8(p_ctx, "Type"); size--;
+ if(type != FLV_SCRIPT_DATA_TYPE_ECMA) return VC_CONTAINER_SUCCESS;
+ num_values = READ_U32(p_ctx, "ECMAArrayLength"); size -= 4;
+
+ /* We found our script, now extract the metadata values */
+ while(num_values-- && size >= 2)
+ {
+ uint16_t length = _READ_U16(p_ctx); size -= 2;
+ if(!length || length >= size || length > MAX_METADATA_STRING_SIZE) break;
+ if(READ_BYTES(p_ctx, psz_string, length) != length) break;
+ psz_string[length] = 0; size -= length;
+ type = _READ_U8(p_ctx); size--;
+
+ switch(type)
+ {
+ case FLV_SCRIPT_DATA_TYPE_NUMBER:
+ /* We only cope with DOUBLE types*/
+ if(size < 8) return VC_CONTAINER_SUCCESS;
+
+ u_value = _READ_U64(p_ctx); size -= 8;
+ /* Convert value into a double */
+ {
+ int64_t value = ((u_value & ((UINT64_C(1)<<52)-1)) + (UINT64_C(1)<<52)) * ((((int64_t)u_value)>>63)|1);
+ int exp = ((u_value>>52)&0x7FF)-1075 + 16;
+ if(exp >= 0) value <<= exp;
+ else value >>= -exp;
+ f_value = ((double)value) / (1 << 16);
+ }
+
+ LOG_DEBUG(p_ctx, "metadata (%s=%i.%i)", psz_string,
+ ((int)(f_value*100))/100, ((int)(f_value*100))%100);
+
+ if(!strcmp(psz_string, "duration"))
+ p_ctx->duration = (int64_t)(f_value*INT64_C(1000000));
+ if(!strcmp(psz_string, "videodatarate"))
+ module->meta_videodatarate = (uint32_t)f_value;
+ if(!strcmp(psz_string, "width"))
+ module->meta_width = (uint32_t)f_value;
+ if(!strcmp(psz_string, "height"))
+ module->meta_height = (uint32_t)f_value;
+ if(!strcmp(psz_string, "framerate"))
+ module->meta_framerate = f_value;
+ if(!strcmp(psz_string, "audiodatarate"))
+ module->meta_audiodatarate = (uint32_t)f_value;
+ continue;
+
+ /* We skip these */
+ case FLV_SCRIPT_DATA_TYPE_BOOL:
+ if(size < 1) return VC_CONTAINER_SUCCESS;
+ u_value = _READ_U8(p_ctx); size -= 1;
+ LOG_DEBUG(p_ctx, "metadata (%s=%i)", psz_string, (int)u_value);
+ continue;
+
+ case FLV_SCRIPT_DATA_TYPE_STRING:
+ if(size < 2) return VC_CONTAINER_SUCCESS;
+ length = _READ_U16(p_ctx); size -= 2;
+ if(length > size) return VC_CONTAINER_SUCCESS;
+ SKIP_BYTES(p_ctx, length); size -= length;
+ LOG_DEBUG(p_ctx, "metadata skipping (%s)", psz_string);
+ continue;
+
+ /* We can't cope with anything else */
+ default:
+ LOG_DEBUG(p_ctx, "unknown amf type (%s,%i)", psz_string, type);
+ return VC_CONTAINER_SUCCESS;
+ }
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an FLV frame header.
+ * This reads the current tag header and matches the contained frame
+ * with one of the tracks we have. If no match can be found, the frame is marked
+ * for discarding. The current read position will be updated to the start
+ * of the data (i.e. the frame) contained within the FLV tag.
+ *
+ * @param p_ctx pointer to our context
+ * @param[out] p_track track this frame belongs to
+ * @param[out] p_size size of the frame
+ * @param[out] p_timestamp timestamp of the frame
+ * @param[out] p_flags flags associated with the frame
+ * @param b_extra_check whether to perform extra sanity checking on the tag
+ * @return VC_CONTAINER_SUCCESS on success
+ */
+static int flv_read_frame_header(VC_CONTAINER_T *p_ctx, int *p_prev_size,
+ int *p_track, int *p_size, uint32_t *p_timestamp, int *p_flags,
+ int b_extra_check)
+{
+ int64_t position = STREAM_POSITION(p_ctx);
+ int type, size, flags = 0, frametype = 0;
+ VC_CONTAINER_STATUS_T status;
+ VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN;
+ unsigned int track = p_ctx->tracks_num;
+ uint32_t codec = 0;
+
+ status = flv_read_tag_header(p_ctx, p_prev_size, &type, &size, p_timestamp);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) return status;
+ if(position == STREAM_POSITION(p_ctx)) return VC_CONTAINER_ERROR_EOS;
+ if(type == 0) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ /* Sanity checking */
+ if(b_extra_check && type != FLV_TAG_TYPE_AUDIO &&
+ type != FLV_TAG_TYPE_VIDEO && type != FLV_TAG_TYPE_METADATA)
+ return VC_CONTAINER_ERROR_CORRUPTED;
+
+ /* We're only interested in audio / video */
+ if((type != FLV_TAG_TYPE_AUDIO && type != FLV_TAG_TYPE_VIDEO) || !size)
+ {
+ flags |= FLV_FLAG_DISCARD;
+ goto end;
+ }
+
+ if(type == FLV_TAG_TYPE_AUDIO)
+ {
+ flv_read_audiodata_header(p_ctx, &codec, 0, 0, 0);
+ es_type = VC_CONTAINER_ES_TYPE_AUDIO;
+ }
+ else if(type == FLV_TAG_TYPE_VIDEO)
+ {
+ flv_read_videodata_header(p_ctx, &codec, &frametype);
+ es_type = VC_CONTAINER_ES_TYPE_VIDEO;
+ }
+ size--;
+
+ /* Find which track this belongs to */
+ for(track = 0; track < p_ctx->tracks_num; track++)
+ if(es_type == p_ctx->tracks[track]->format->es_type) break;
+ if(track == p_ctx->tracks_num)
+ flags |= FLV_FLAG_DISCARD;
+
+ /* Sanity checking */
+ if(b_extra_check && codec != p_ctx->tracks[track]->format->codec)
+ return VC_CONTAINER_ERROR_CORRUPTED;
+
+ end:
+ // add to the index if we have one, and we're not discarding this frame.
+ // also require that we either have no video track or this is a video frame marked as a key frame.
+ if(p_ctx->priv->module->state.index && !(flags & FLV_FLAG_DISCARD) &&
+ (p_ctx->priv->module->video_track < 0 || (es_type == VC_CONTAINER_ES_TYPE_VIDEO && (frametype & FLV_FLAG_KEYFRAME))))
+ vc_container_index_add(p_ctx->priv->module->state.index, (int64_t) (*p_timestamp) * 1000LL, position);
+
+ *p_flags = flags | frametype;
+ *p_size = size;
+ *p_track = track;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/** Validate the data contained within the frame and update the read
+ * position to the start of the frame data that we want to feed to the codec.
+ *
+ * Each codec is packed slightly differently so this function is necessary
+ * to prepare for reading the actual codec data.
+ *
+ * @param p_ctx pointer to our context
+ * @param track track this frame belongs to
+ * @param[in] p_size size of the frame
+ * @param[out] p_size updated size of the frame
+ * @param[in] p_timestamp timestamp for the frame
+ * @param[out] p_timestamp updated timestamp for the frame
+ * @return VC_CONTAINER_SUCCESS on success
+ */
+static VC_CONTAINER_STATUS_T flv_validate_frame_data(VC_CONTAINER_T *p_ctx,
+ int track, int *p_size, uint32_t *p_timestamp)
+{
+ int32_t time_offset;
+
+ if(track >= (int)p_ctx->tracks_num)
+ return *p_size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_CONTINUE;
+
+ switch(p_ctx->tracks[track]->format->codec)
+ {
+ case VC_CONTAINER_CODEC_VP6:
+ if(*p_size < 1) return VC_CONTAINER_ERROR_CORRUPTED;
+ _READ_U8(p_ctx); *p_size -= 1;
+ break;
+ case VC_CONTAINER_CODEC_MP4A:
+ if(*p_size < 1) return VC_CONTAINER_ERROR_CORRUPTED;
+ *p_size -= 1;
+ if(_READ_U8(p_ctx)!=1) return VC_CONTAINER_ERROR_CONTINUE; /* empty frame*/
+ break;
+ case VC_CONTAINER_CODEC_H264:
+ if(*p_size < 4) return VC_CONTAINER_ERROR_CORRUPTED;
+ *p_size -= 1;
+ if(_READ_U8(p_ctx)!=1) return VC_CONTAINER_ERROR_CONTINUE; /* empty frame*/
+ time_offset = _READ_U24(p_ctx);
+ time_offset <<= 8; time_offset >>= 8; /* change to signed */
+ *p_timestamp += time_offset;
+ *p_size -= 3;
+ break;
+ default:
+ break;
+ }
+
+ return *p_size ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_CONTINUE;
+}
+
+/** Small utility function to update the reading position of a track
+ */
+static void flv_update_track_position(VC_CONTAINER_T *p_ctx, int track,
+ int64_t tag_position, int tag_prev_size, int64_t data_position,
+ int data_size, uint32_t timestamp, int flags)
+{
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module;
+ track_module->state->tag_position = tag_position;
+ track_module->state->tag_prev_size = tag_prev_size;
+ track_module->state->data_position = data_position;
+ track_module->state->data_size = data_size;
+ track_module->state->data_offset = 0;
+ track_module->state->timestamp = timestamp;
+ track_module->state->flags = flags;
+ track_module->state->track = track;
+}
+
+/** Utility function to find the next frame of a given track in the stream.
+ *
+ * This will basically walk through all the tags in the file until it
+ * finds a tag/frame which belongs to the given track.
+ *
+ * @param p_ctx pointer to our context
+ * @param track track wanted
+ * @param[out] p_size size of the frame
+ * @param[out] p_timestamp timestamp of the frame
+ * @param[out] p_flags flags associated with the frame
+ * @param b_keyframe whether we specifically want a keyframe or not
+ * @param b_extra_check whether to perform extra sanity checking on the tag
+ * @return VC_CONTAINER_SUCCESS on success
+ */
+static VC_CONTAINER_STATUS_T flv_find_next_frame(VC_CONTAINER_T *p_ctx, int track, int *p_size,
+ uint32_t *p_timestamp, int *p_flags, int b_keyframe, int b_extra_check)
+{
+ int frame_track, prev_size, size, flags;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ FLV_READER_STATE_T *state = p_ctx->tracks[track]->priv->module->state;
+ uint32_t timestamp;
+ int64_t position;
+ VC_CONTAINER_PARAM_UNUSED(b_extra_check);
+
+ /* Seek to the next tag in the stream or the current position
+ * if none of its data has been consumed */
+ position = state->tag_position;
+ if(state->data_offset)
+ position = state->data_position + state->data_size;
+ status = SEEK(p_ctx, position);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ /* Look for the next frame we want */
+ while (status == VC_CONTAINER_SUCCESS)
+ {
+ position = STREAM_POSITION(p_ctx);
+ status = flv_read_frame_header(p_ctx, &prev_size, &frame_track,
+ &size, ×tamp, &flags, 0);
+ if(status != VC_CONTAINER_SUCCESS) break;
+
+ if(flags & FLV_FLAG_DISCARD) goto skip;
+ if(frame_track != track) goto skip;
+
+ if(b_keyframe && p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO &&
+ !(flags & FLV_FLAG_KEYFRAME)) goto skip;
+
+ if(flv_validate_frame_data(p_ctx, track, &size, ×tamp) != VC_CONTAINER_SUCCESS)
+ goto skip;
+
+ /* We have what we need */
+ flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx),
+ size, timestamp, flags);
+ break;
+
+ skip:
+ flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx),
+ size, timestamp, 0);
+ state->data_offset = size; /* consume data */
+
+ if(SKIP_BYTES(p_ctx, size) != (size_t)size) status = STREAM_STATUS(p_ctx);
+ }
+
+ if(!status)
+ {
+ if(p_size) *p_size = size;
+ if(p_timestamp) *p_timestamp = timestamp;
+ if(p_flags) *p_flags = flags;
+ }
+
+ return status;
+}
+
+/** Utility function to find the previous frame of a given track in the stream.
+ *
+ * This will basically walk back through all the tags in the file until it
+ * finds a tag/frame which belongs to the given track.
+ *
+ * @param p_ctx pointer to our context
+ * @param track track wanted
+ * @param[out] p_size size of the frame
+ * @param[out] p_timestamp timestamp of the frame
+ * @param[out] p_flags flags associated with the frame
+ * @param b_keyframe whether we specifically want a keyframe or not
+ * @param b_extra_check whether to perform extra sanity checking on the tag
+ * @return VC_CONTAINER_SUCCESS on success
+ */
+static VC_CONTAINER_STATUS_T flv_find_previous_frame(VC_CONTAINER_T *p_ctx, int track, int *p_size,
+ uint32_t *p_timestamp, int *p_flags, int b_keyframe, int b_extra_check)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ FLV_READER_STATE_T *state = p_ctx->tracks[track]->priv->module->state;
+ int frame_track, prev_size, size, flags;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint32_t timestamp;
+ int64_t position;
+
+ /* Look for the previous frame we want */
+ while (status == VC_CONTAINER_SUCCESS)
+ {
+ /* Seek to the previous tag in the stream */
+ position = state->tag_position - state->tag_prev_size;
+ if(position < module->data_offset) position = module->data_offset;
+ status = SEEK(p_ctx, position);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ status = flv_read_frame_header(p_ctx, &prev_size, &frame_track,
+ &size, ×tamp, &flags, 0);
+ if(status) break;
+
+ if(flags & FLV_FLAG_DISCARD) goto skip;
+ if(frame_track != track) goto skip;
+
+ if(b_keyframe && p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO &&
+ !(flags & FLV_FLAG_KEYFRAME)) goto skip;
+
+ if(flv_validate_frame_data(p_ctx, track, &size, ×tamp) != VC_CONTAINER_SUCCESS)
+ goto skip;
+
+ /* We have what we need */
+ flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx),
+ size, timestamp, flags);
+ break;
+
+ skip:
+ if(position <= module->data_offset)
+ {
+ /* We're back at the beginning but we still want to return something */
+ flv_update_track_position(p_ctx, track, (int64_t)module->data_offset, 0,
+ (int64_t)module->data_offset, 0, 0, 0);
+ return flv_find_next_frame(p_ctx, track, p_size, p_timestamp, p_flags, b_keyframe, b_extra_check);
+ }
+ flv_update_track_position(p_ctx, track, position, prev_size, STREAM_POSITION(p_ctx),
+ size, timestamp, 0);
+ state->data_offset = size; /* consume data */
+ }
+
+ if(!status)
+ {
+ if(p_size) *p_size = size;
+ if(p_timestamp) *p_timestamp = timestamp;
+ if(p_flags) *p_flags = flags;
+ }
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T flv_reader_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int i;
+
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ vc_container_free_track(p_ctx, p_ctx->tracks[i]);
+
+ if(module->state.index)
+ vc_container_index_free(module->state.index);
+
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T flv_read_sample_header( VC_CONTAINER_T *p_ctx,
+ FLV_READER_STATE_T *state)
+{
+ int track, prev_size, size, flags;
+ uint32_t timestamp;
+ int64_t position;
+
+ /* Check if we still have some data left to read from the current frame */
+ if(state->data_offset < state->data_size)
+ return state->status;
+
+ /* Read the next tag header */
+ position = STREAM_POSITION(p_ctx);
+ state->status = flv_read_frame_header(p_ctx, &prev_size, &track,
+ &size, ×tamp, &flags, 0);
+ if(state->status != VC_CONTAINER_SUCCESS)
+ return state->status;
+
+ state->status = flv_validate_frame_data(p_ctx, track, &size, ×tamp);
+ if(state->status == VC_CONTAINER_ERROR_CONTINUE)
+ {
+ /* Skip it */
+ state->status = VC_CONTAINER_SUCCESS;
+ track = p_ctx->tracks_num;
+ }
+ if(state->status != VC_CONTAINER_SUCCESS)
+ return state->status;
+
+ state->tag_position = position;
+ state->data_position = STREAM_POSITION(p_ctx);
+ state->data_size = size;
+ state->data_offset = 0;
+ state->flags = flags;
+ state->tag_prev_size = prev_size;
+ state->timestamp = timestamp;
+ state->track = track;
+ return state->status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T flv_read_sample_data( VC_CONTAINER_T *p_ctx,
+ FLV_READER_STATE_T *state, uint8_t *data, unsigned int *data_size )
+{
+ unsigned int size = state->data_size - state->data_offset;
+
+ if(state->status != VC_CONTAINER_SUCCESS) return state->status;
+
+ if(data_size && *data_size < size) size = *data_size;
+
+ if(!data) size = SKIP_BYTES(p_ctx, size);
+ else size = READ_BYTES(p_ctx, data, size);
+ state->data_offset += size;
+
+ if(data_size) *data_size = size;
+ state->status = STREAM_STATUS(p_ctx);
+
+ return state->status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T flv_reader_read( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *packet, uint32_t flags )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ FLV_READER_STATE_T *state = &module->state;
+ unsigned int data_size;
+
+ /* TODO: select right state */
+
+ status = flv_read_sample_header(p_ctx, state);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+#ifdef ENABLE_FLV_EXTRA_LOGGING
+ LOG_DEBUG(p_ctx, "read_sample_header (%i,%i,%i/%i/%i/%i)", state->timestamp, state->flags,
+ (int)state->tag_position, (int)(state->data_position-state->tag_position), state->data_offset, state->data_size);
+#endif
+
+ if(state->track >= p_ctx->tracks_num || !p_ctx->tracks[state->track]->is_enabled)
+ {
+ /* Skip packet */
+ status = flv_read_sample_data(p_ctx, state, 0, 0);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ return VC_CONTAINER_ERROR_CONTINUE;
+ }
+
+ if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */
+ return flv_read_sample_data(p_ctx, state, 0, 0);
+
+ packet->dts = packet->pts = state->timestamp * (int64_t)1000;
+ packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END;
+ if(state->flags & FLV_FLAG_KEYFRAME) packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME;
+ if(!state->data_offset) packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
+ packet->track = state->track;
+
+ // The frame size is all the data
+ packet->frame_size = state->data_size;
+
+ // the size is what's left
+ packet->size = state->data_size - state->data_offset;
+
+ if(flags & VC_CONTAINER_READ_FLAG_SKIP)
+ return flv_read_sample_data(p_ctx, state, 0, 0);
+ else if(flags & VC_CONTAINER_READ_FLAG_INFO)
+ return VC_CONTAINER_SUCCESS;
+
+ data_size = packet->buffer_size;
+ status = flv_read_sample_data(p_ctx, state, packet->data, &data_size);
+ if(status != VC_CONTAINER_SUCCESS)
+ {
+ /* FIXME */
+ return status;
+ }
+
+ packet->size = data_size;
+ if(state->data_offset != state->data_size)
+ packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T flv_reader_seek(VC_CONTAINER_T *p_ctx,
+ int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ FLV_READER_STATE_T last_state = {0};
+ FLV_READER_STATE_T *state;
+ uint32_t time = (*offset / 1000), timestamp, previous_time;
+ unsigned int i, track;
+ int size, past = 0;
+ int64_t position;
+ VC_CONTAINER_PARAM_UNUSED(mode);
+
+ /* If we have a video track, then we want to find the keyframe closest to
+ * the requested time, otherwise we just look for the tag with the
+ * closest timestamp */
+
+ /* Select the track on which we'll do our seeking */
+ for(i = 0, track = 0; i < p_ctx->tracks_num; i++)
+ {
+ if(p_ctx->tracks[i]->format->es_type != VC_CONTAINER_ES_TYPE_VIDEO) continue;
+ track = i;
+ break;
+ }
+ if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_CORRUPTED;
+ state = p_ctx->tracks[track]->priv->module->state;
+ previous_time = state->timestamp;
+
+ LOG_DEBUG(p_ctx, "seek (%i, prev %i)", time, previous_time);
+
+ if(state->index && vc_container_index_get(state->index, flags & VC_CONTAINER_SEEK_FLAG_FORWARD,
+ offset, &position, &past) == VC_CONTAINER_SUCCESS)
+ {
+ flv_update_track_position(p_ctx, track, position, 0, position, 0, (uint32_t) (*offset / 1000LL), 0);
+ }
+ else
+ {
+ if(time < state->timestamp / 2)
+ flv_update_track_position(p_ctx, track, (int64_t)module->data_offset, 0,
+ (int64_t)module->data_offset, 0, 0, 0);
+ past = 1;
+ }
+
+ /* If past it clear then we're done, otherwise we need to find our point from here */
+ if(past == 0)
+ {
+ status = flv_find_next_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0);
+ }
+ else
+ {
+ if(time > previous_time)
+ {
+ while(!status)
+ {
+ status = flv_find_next_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0);
+ if(status) break;
+
+ /* Check if we have our frame */
+ if(time <= timestamp) break;
+
+ last_state = *state;
+ state->data_offset = size; /* consume data */
+ }
+ }
+ else
+ {
+ while(!status)
+ {
+ status = flv_find_previous_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0);
+ if(status) break;
+
+ /* Check if we have our frame */
+ if(time >= timestamp) break;
+
+ /* Detect when we've reached the 1st keyframe to avoid an infinite loop */
+ if(state->timestamp == last_state.timestamp) break;
+
+ last_state = *state;
+ state->data_offset = size; /* consume data */
+ }
+ }
+ }
+
+ if(status != VC_CONTAINER_SUCCESS && (flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
+ {
+ LOG_DEBUG(p_ctx, "seek failed (%i)", status);
+ return status;
+ }
+ else if(status != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "seek failed (%i), look for previous frame", status);
+ if(last_state.tag_position) *state = last_state;
+ else status = flv_find_previous_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0);
+ }
+
+ LOG_DEBUG(p_ctx, "seek done (%i)", timestamp);
+ state->status = VC_CONTAINER_SUCCESS;
+ last_state.status = VC_CONTAINER_SUCCESS;
+
+ if(past == 1)
+ {
+ /* Make adjustment based on seek mode */
+ if((flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && timestamp < time && timestamp < previous_time)
+ {
+ if(last_state.tag_position) *state = last_state;
+ else status = flv_find_next_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0);
+ }
+ else if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD) && timestamp > time)
+ {
+ if(last_state.tag_position) *state = last_state;
+ else status = flv_find_previous_frame(p_ctx, track, &size, ×tamp, 0, 1 /*keyframe*/, 0);
+ }
+
+ LOG_DEBUG(p_ctx, "seek adjustment (%i)", timestamp);
+ }
+
+ if(state->data_position == last_state.data_position)
+ status = SEEK(p_ctx, state->data_position);
+
+ *offset = timestamp * INT64_C(1000);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/******************************************************************************
+Global function definitions.
+******************************************************************************/
+
+VC_CONTAINER_STATUS_T flv_reader_open( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = 0;
+ uint8_t buffer[4], type_flags;
+ unsigned int i, frames, audio_present, video_present;
+ uint32_t data_offset;
+
+ /* Check the FLV marker */
+ if( PEEK_BYTES(p_ctx, buffer, 4) < 4 ) goto error;
+ if( buffer[0] != 'F' || buffer[1] != 'L' || buffer[2] != 'V' )
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ /* Check FLV version */
+ if( buffer[3] > 4 )
+ {
+ LOG_DEBUG(p_ctx, "Version too high: %d", buffer[3]);
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+
+ SKIP_BYTES(p_ctx, 4); /* FLV marker and version */
+
+ /* Find out which tracks should be available.
+ * FLV can only have up to 1 audio track and 1 video track. */
+ type_flags = READ_U8(p_ctx, "TypeFlags");
+ audio_present = !!(type_flags & 0x04);
+ video_present = !!(type_flags & 0x01);
+
+ /* Sanity check DataOffset */
+ data_offset = READ_U32(p_ctx, "DataOffset");
+ if(data_offset < 9) goto error;
+
+ /*
+ * We are dealing with an FLV file
+ */
+
+ LOG_DEBUG(p_ctx, "using flv reader");
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks = module->tracks;
+ module->data_offset = data_offset;
+ module->audio_track = -1;
+ module->video_track = -1;
+
+ /* Skip to the start of the actual data */
+ SKIP_BYTES(p_ctx, data_offset - 9);
+
+ /* We'll start parsing a few of the FLV tags to find out the
+ * metadata / audio / video properties.
+ * The first tag we should see is the metadata one which will give us all the
+ * properties of the stream. However we do not rely on that being there and we
+ * actually look at the first audio / video tags as well. */
+ for(frames = 0; frames < 20; frames++)
+ {
+ VC_CONTAINER_TRACK_T *track;
+ int64_t offset, skip;
+ int prev_size, type, size, channels, samplerate, bps;
+ uint32_t codec, timestamp;
+
+ /* Stop there if we have everything we want */
+ if(audio_present == (module->audio_track >= 0) &&
+ video_present == (module->video_track >= 0)) break;
+ if(module->audio_track >= 0 && module->video_track >= 0) break;
+
+ /* Start reading the next tag */
+ if(flv_read_tag_header(p_ctx, &prev_size, &type, &size, ×tamp)) break;
+ if(!size) continue;
+
+ offset = STREAM_POSITION(p_ctx); /* to keep track of how much data we read */
+
+ switch(type)
+ {
+ case FLV_TAG_TYPE_AUDIO:
+ if(module->audio_track >= 0) break; /* We already have our audio track */
+ flv_read_audiodata_header(p_ctx, &codec, &samplerate, &channels, &bps);
+
+ p_ctx->tracks[p_ctx->tracks_num] = track =
+ vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
+ if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO;
+ track->format->codec = codec;
+ flv_read_audiodata_properties(p_ctx, track, size - 1, samplerate, channels, bps);
+
+ module->audio_track = p_ctx->tracks_num++;
+ track->is_enabled = 1;
+ track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+ break;
+
+ case FLV_TAG_TYPE_VIDEO:
+ if(module->video_track >= 0) break; /* We already have our video track */
+ flv_read_videodata_header(p_ctx, &codec, 0);
+
+ p_ctx->tracks[p_ctx->tracks_num] = track =
+ vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
+ if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ track->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO;
+ track->format->codec = codec;
+
+ status = flv_read_videodata_properties(p_ctx, track, size - 1);
+ if(status != VC_CONTAINER_SUCCESS) { vc_container_free_track(p_ctx, track); break; }
+
+ module->video_track = p_ctx->tracks_num++;
+ track->is_enabled = 1;
+ track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+ break;
+
+ case FLV_TAG_TYPE_METADATA:
+ flv_read_metadata(p_ctx, size);
+ break;
+
+ default: break;
+ }
+
+ /* Skip any data that's left unparsed from the current tag */
+ skip = size - (STREAM_POSITION(p_ctx) - offset);
+ if(skip < 0) break;
+ SKIP_BYTES(p_ctx, (size_t)skip);
+ }
+
+ /* Make sure we found something we can play */
+ if(!p_ctx->tracks_num) {LOG_DEBUG(p_ctx, "didn't find any track"); goto error;}
+
+ /* Try and create an index. All times are signed, so adding a base timestamp
+ * of zero means that we will always seek back to the start of the file, even if
+ * the actual frame timestamps start at some higher number. */
+ if(vc_container_index_create(&module->state.index, 512) == VC_CONTAINER_SUCCESS)
+ vc_container_index_add(module->state.index, 0LL, (int64_t) data_offset);
+
+ /* Use the metadata we read */
+ if(module->audio_track >= 0)
+ {
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->audio_track];
+ track->format->bitrate = module->meta_audiodatarate;
+ }
+ if(module->video_track >= 0)
+ {
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->video_track];
+ track->format->bitrate = module->meta_videodatarate;
+ if(module->meta_framerate)
+ {
+ track->format->type->video.frame_rate_num = (uint32_t)(100 * module->meta_framerate);
+ track->format->type->video.frame_rate_den = 100;
+ }
+
+ if(module->meta_width && module->meta_width > track->format->type->video.width)
+ track->format->type->video.width = module->meta_width;
+ if(module->meta_height && module->meta_height > track->format->type->video.height)
+ track->format->type->video.height = module->meta_height;
+ }
+
+ status = SEEK(p_ctx, data_offset);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ /* Some initialisation */
+ module->state.tag_position = data_offset;
+ module->state.data_position = data_offset;
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[i]->priv->module;
+ track_module->state = &module->state;
+ }
+
+ if(STREAM_SEEKABLE(p_ctx))
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
+
+ p_ctx->priv->pf_close = flv_reader_close;
+ p_ctx->priv->pf_read = flv_reader_read;
+ p_ctx->priv->pf_seek = flv_reader_seek;
+
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ LOG_DEBUG(p_ctx, "flv: error opening stream");
+ if(module) flv_reader_close(p_ctx);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open flv_reader_open
+#endif
diff --git a/gfx/include/userland/containers/h264/avc1_packetizer.c b/gfx/include/userland/containers/h264/avc1_packetizer.c
new file mode 100644
index 0000000000..8082f0b492
--- /dev/null
+++ b/gfx/include/userland/containers/h264/avc1_packetizer.c
@@ -0,0 +1,343 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** \file
+ * Implementation of an ISO 14496-15 to Annexe-B AVC video packetizer.
+ */
+
+#include
+#include
+
+#include "containers/packetizers.h"
+#include "containers/core/packetizers_private.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_logging.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_bytestream.h"
+
+#ifndef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+#endif
+
+/** Arbitrary number which should be sufficiently high so that no sane frame will
+ * be bigger than that. */
+#define MAX_FRAME_SIZE (1920*1088*2)
+
+VC_CONTAINER_STATUS_T avc1_packetizer_open( VC_PACKETIZER_T * );
+
+/*****************************************************************************/
+typedef struct VC_PACKETIZER_MODULE_T {
+ enum {
+ STATE_FRAME_WAIT = 0,
+ STATE_BUFFER_INIT,
+ STATE_NAL_START,
+ STATE_NAL_DATA,
+ } state;
+
+ unsigned int length_size;
+
+ unsigned int frame_size;
+ unsigned int bytes_read;
+ unsigned int start_code_bytes_left;
+ unsigned int nal_bytes_left;
+
+} VC_PACKETIZER_MODULE_T;
+
+static const uint8_t h264_start_code[] = {0, 0, 0, 1};
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avc1_packetizer_close( VC_PACKETIZER_T *p_ctx )
+{
+ free(p_ctx->priv->module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avc1_packetizer_reset( VC_PACKETIZER_T *p_ctx )
+{
+ VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
+ module->state = STATE_FRAME_WAIT;
+ module->frame_size = 0;
+ module->bytes_read = 0;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avc1_packetizer_packetize( VC_PACKETIZER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags)
+{
+ VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream;
+ VC_CONTAINER_PACKET_T *packet;
+ unsigned int offset, size, nal_num;
+ uint8_t data[4];
+ VC_CONTAINER_PARAM_UNUSED(nal_num);
+
+ while(1) switch (module->state)
+ {
+ case STATE_FRAME_WAIT:
+ for (packet = stream->current, size = 0;
+ packet && !(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END);
+ packet = packet->next)
+ size += packet->size;
+ if (!packet)
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We need more data */
+
+ size += packet->size;
+
+ /* We now have a complete frame available */
+
+ module->nal_bytes_left = 0;
+ module->start_code_bytes_left = 0;
+
+ /* Find out the number of NAL units and size of the frame */
+ for (offset = nal_num = 0; offset + module->length_size < size; nal_num++)
+ {
+ unsigned int nal_size;
+
+ bytestream_peek_at(stream, offset, data, module->length_size);
+ offset += module->length_size;
+
+ nal_size = data[0];
+ if (module->length_size > 1)
+ nal_size = (nal_size << 8)|data[1];
+ if (module->length_size > 2)
+ nal_size = (nal_size << 8)|data[2];
+ if (module->length_size > 3)
+ nal_size = (nal_size << 8)|data[3];
+ if (offset + nal_size > size)
+ nal_size = size - offset;
+
+ offset += nal_size;
+ module->frame_size += nal_size + sizeof(h264_start_code);
+#ifdef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+ LOG_DEBUG(0, "nal unit size %u", nal_size);
+#endif
+ }
+ LOG_DEBUG(0, "frame size: %u(%u/%u), pts: %"PRIi64, module->frame_size,
+ size, nal_num, stream->current->pts);
+
+ /* fall through to the next state */
+ module->state = STATE_BUFFER_INIT;
+
+ case STATE_BUFFER_INIT:
+ packet = stream->current;
+ out->size = module->frame_size - module->bytes_read;
+ out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN;
+ out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END;
+
+ if (!module->bytes_read)
+ {
+ out->pts = packet->pts;
+ out->dts = packet->dts;
+ out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
+ }
+
+ if (flags & VC_PACKETIZER_FLAG_INFO)
+ return VC_CONTAINER_SUCCESS;
+
+ if (flags & VC_PACKETIZER_FLAG_SKIP)
+ {
+ /* The easiest is to just drop all the packets belonging to the frame */
+ while (!(stream->current->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END))
+ bytestream_skip_packet(stream);
+ bytestream_skip_packet(stream);
+
+ module->frame_size = 0;
+ module->bytes_read = 0;
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ /* We now know that we'll have to read some data so reset the output size */
+ out->size = 0;
+
+ /* Go to the next relevant state */
+ module->state = STATE_NAL_START;
+ if (module->nal_bytes_left || module->bytes_read == module->frame_size)
+ module->state = STATE_NAL_DATA;
+ break;
+
+ case STATE_NAL_START:
+ /* Extract the size of the current NAL */
+ bytestream_get(stream, data, module->length_size);
+
+ module->nal_bytes_left = data[0];
+ if (module->length_size > 1)
+ module->nal_bytes_left = (module->nal_bytes_left << 8)|data[1];
+ if (module->length_size > 2)
+ module->nal_bytes_left = (module->nal_bytes_left << 8)|data[2];
+ if (module->length_size > 3)
+ module->nal_bytes_left = (module->nal_bytes_left << 8)|data[3];
+
+ if (module->bytes_read + module->nal_bytes_left + sizeof(h264_start_code) >
+ module->frame_size)
+ {
+ LOG_ERROR(0, "truncating nal (%u/%u)", module->nal_bytes_left,
+ module->frame_size - module->bytes_read - sizeof(h264_start_code));
+ module->nal_bytes_left = module->frame_size - sizeof(h264_start_code);
+ }
+
+#ifdef ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+ LOG_DEBUG(0, "nal unit size %u", module->nal_bytes_left);
+#endif
+
+ module->start_code_bytes_left = sizeof(h264_start_code);
+
+ /* fall through to the next state */
+ module->state = STATE_NAL_DATA;
+
+ case STATE_NAL_DATA:
+ /* Start by adding the start code */
+ if (module->start_code_bytes_left)
+ {
+ size = MIN(out->buffer_size - out->size, module->start_code_bytes_left);
+ memcpy(out->data + out->size, h264_start_code + sizeof(h264_start_code) -
+ module->start_code_bytes_left, size);
+ module->start_code_bytes_left -= size;
+ module->bytes_read += size;
+ out->size += size;
+ }
+
+ /* Then append the NAL unit itself */
+ if (module->nal_bytes_left)
+ {
+ size = MIN(out->buffer_size - out->size, module->nal_bytes_left);
+ bytestream_get( stream, out->data + out->size, size );
+ module->nal_bytes_left -= size;
+ module->bytes_read += size;
+ out->size += size;
+ }
+
+ /* Check whether we're done */
+ if (module->bytes_read == module->frame_size)
+ {
+ bytestream_skip_packet(stream);
+ module->state = STATE_FRAME_WAIT;
+ module->frame_size = 0;
+ module->bytes_read = 0;
+ return VC_CONTAINER_SUCCESS;
+ }
+ else if (out->buffer_size == out->size)
+ {
+ out->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END;
+ module->state = STATE_BUFFER_INIT;
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ /* We're not done, go to the next relevant state */
+ module->state = STATE_NAL_START;
+ break;
+
+ default:
+ break;
+ };
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T avc1_packetizer_codecconfig( VC_PACKETIZER_T *p_ctx )
+{
+ VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ uint8_t *out, *extra = p_ctx->in->extradata + 5;
+ uint8_t *extra_end = extra + p_ctx->in->extradata_size - 5;
+ unsigned int i, j, nal_size, out_size = 0;
+
+ if (p_ctx->in->extradata_size <= 5 ||
+ p_ctx->in->extradata[0] != 1 /* configurationVersion */)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ status = vc_container_format_extradata_alloc(p_ctx->out, p_ctx->in->extradata_size);
+ if (status != VC_CONTAINER_SUCCESS)
+ return status;
+
+ out = p_ctx->out->extradata;
+ module->length_size = (*(p_ctx->in->extradata + 4) & 0x3) + 1;
+
+ for (i = 0; i < 2 && extra < extra_end - 1; i++)
+ {
+ j = *(extra++) & (!i ? 0x1F : 0xFF);
+ for (; j > 0 && extra < extra_end - 2; j--)
+ {
+ nal_size = (extra[0] << 8) | extra[1]; extra += 2;
+ if (extra + nal_size > extra_end)
+ {
+ extra = extra_end;
+ break;
+ }
+
+ out[0] = out[1] = out[2] = 0; out[3] = 1;
+ memcpy(out + 4, extra, nal_size);
+ out += nal_size + 4; extra += nal_size;
+ out_size += nal_size + 4;
+ }
+ }
+
+ p_ctx->out->extradata_size = out_size;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T avc1_packetizer_open( VC_PACKETIZER_T *p_ctx )
+{
+ VC_PACKETIZER_MODULE_T *module;
+ VC_CONTAINER_STATUS_T status;
+
+ if(p_ctx->in->codec != VC_CONTAINER_CODEC_H264 &&
+ p_ctx->out->codec != VC_CONTAINER_CODEC_H264)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if(p_ctx->in->codec_variant != VC_CONTAINER_VARIANT_H264_AVC1 &&
+ p_ctx->out->codec_variant != VC_CONTAINER_VARIANT_H264_DEFAULT)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if(!(p_ctx->in->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ p_ctx->priv->module = module = malloc(sizeof(*module));
+ if(!module)
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ memset(module, 0, sizeof(*module));
+
+ vc_container_format_copy(p_ctx->out, p_ctx->in, 0);
+ status = avc1_packetizer_codecconfig(p_ctx);
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ free(module);
+ return status;
+ }
+
+ p_ctx->out->codec_variant = VC_CONTAINER_VARIANT_H264_DEFAULT;
+ p_ctx->max_frame_size = MAX_FRAME_SIZE;
+ p_ctx->priv->pf_close = avc1_packetizer_close;
+ p_ctx->priv->pf_packetize = avc1_packetizer_packetize;
+ p_ctx->priv->pf_reset = avc1_packetizer_reset;
+ LOG_DEBUG(0, "using avc1 video packetizer");
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_PACKETIZER_REGISTER(avc1_packetizer_open, "avc1");
diff --git a/gfx/include/userland/containers/io/io_file.c b/gfx/include/userland/containers/io/io_file.c
new file mode 100644
index 0000000000..ea4b945d9b
--- /dev/null
+++ b/gfx/include/userland/containers/io/io_file.c
@@ -0,0 +1,154 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+#include
+#include
+
+#include "containers/containers.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_io.h"
+#include "containers/core/containers_uri.h"
+
+typedef struct VC_CONTAINER_IO_MODULE_T
+{
+ FILE *stream;
+
+} VC_CONTAINER_IO_MODULE_T;
+
+VC_CONTAINER_STATUS_T vc_container_io_file_open( VC_CONTAINER_IO_T *, const char *,
+ VC_CONTAINER_IO_MODE_T );
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_file_close( VC_CONTAINER_IO_T *p_ctx )
+{
+ VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
+ fclose(module->stream);
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static size_t io_file_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size)
+{
+ size_t ret = fread(buffer, 1, size, p_ctx->module->stream);
+ if(ret != size)
+ {
+ /* Sanity check return value. Some platforms (e.g. Android) can return -1 */
+ if( ((int)ret) < 0 ) ret = 0;
+
+ if( feof(p_ctx->module->stream) ) p_ctx->status = VC_CONTAINER_ERROR_EOS;
+ else p_ctx->status = VC_CONTAINER_ERROR_FAILED;
+ }
+ return ret;
+}
+
+/*****************************************************************************/
+static size_t io_file_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size)
+{
+ return fwrite(buffer, 1, size, p_ctx->module->stream);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_file_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int ret;
+
+ //FIXME: large file support
+#ifdef _VIDEOCORE
+ extern int fseek64(FILE *fp, int64_t offset, int whence);
+ ret = fseek64(p_ctx->module->stream, offset, SEEK_SET);
+#else
+ if (offset > (int64_t)UINT_MAX)
+ {
+ p_ctx->status = VC_CONTAINER_ERROR_EOS;
+ return VC_CONTAINER_ERROR_EOS;
+ }
+ ret = fseek(p_ctx->module->stream, (long)offset, SEEK_SET);
+#endif
+ if(ret)
+ {
+ if( feof(p_ctx->module->stream) ) status = VC_CONTAINER_ERROR_EOS;
+ else status = VC_CONTAINER_ERROR_FAILED;
+ }
+
+ p_ctx->status = status;
+ return status;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_io_file_open( VC_CONTAINER_IO_T *p_ctx,
+ const char *unused, VC_CONTAINER_IO_MODE_T mode )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_IO_MODULE_T *module = 0;
+ const char *psz_mode = mode == VC_CONTAINER_IO_MODE_WRITE ? "wb+" : "rb";
+ const char *uri = p_ctx->uri;
+ FILE *stream = 0;
+ VC_CONTAINER_PARAM_UNUSED(unused);
+
+ if(vc_uri_path(p_ctx->uri_parts))
+ uri = vc_uri_path(p_ctx->uri_parts);
+
+ stream = fopen(uri, psz_mode);
+ if(!stream) { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; }
+
+ /* Turn off buffering. The container layer will provide its own cache */
+ setvbuf(stream, NULL, _IONBF, 0);
+
+ module = malloc( sizeof(*module) );
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+
+ p_ctx->module = module;
+ module->stream = stream;
+ p_ctx->pf_close = io_file_close;
+ p_ctx->pf_read = io_file_read;
+ p_ctx->pf_write = io_file_write;
+ p_ctx->pf_seek = io_file_seek;
+
+ if(mode == VC_CONTAINER_IO_MODE_WRITE)
+ {
+ p_ctx->max_size = (1UL<<31)-1; /* For now limit to 2GB */
+ }
+ else
+ {
+ //FIXME: large file support, platform-specific file size
+ fseek(p_ctx->module->stream, 0, SEEK_END);
+ p_ctx->size = ftell(p_ctx->module->stream);
+ fseek(p_ctx->module->stream, 0, SEEK_SET);
+ }
+
+ p_ctx->capabilities = VC_CONTAINER_IO_CAPS_NO_CACHING;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ if(stream) fclose(stream);
+ return status;
+}
diff --git a/gfx/include/userland/containers/io/io_http.c b/gfx/include/userland/containers/io/io_http.c
new file mode 100644
index 0000000000..48d86619c0
--- /dev/null
+++ b/gfx/include/userland/containers/io/io_http.c
@@ -0,0 +1,898 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+#include
+#include
+
+#include "containers/containers.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_io.h"
+#include "containers/core/containers_uri.h"
+#include "containers/core/containers_logging.h"
+#include "containers/core/containers_list.h"
+#include "containers/core/containers_utils.h"
+#include "containers/net/net_sockets.h"
+
+/* Set to 1 if you want to log all HTTP requests */
+#define ENABLE_HTTP_EXTRA_LOGGING 0
+
+/******************************************************************************
+Defines and constants.
+******************************************************************************/
+
+#define IO_HTTP_DEFAULT_PORT "80"
+
+/** Space for sending requests and receiving responses */
+#define COMMS_BUFFER_SIZE 4000
+
+/** Largest allowed HTTP URI. Must be substantially smaller than COMMS_BUFFER_SIZE
+ * to allow for the headers that may be sent. */
+#define HTTP_URI_LENGTH_MAX 1024
+
+/** Initial capacity of header list */
+#define HEADER_LIST_INITIAL_CAPACITY 16
+
+/** Format of the first line of an HTTP request */
+#define HTTP_REQUEST_LINE_FORMAT "%s %s HTTP/1.1\r\nHost: %s\r\n"
+
+/** Format of a range request */
+#define HTTP_RANGE_REQUEST "Range: bytes=%"PRId64"-%"PRId64"\r\n"
+
+/** Format string for common headers used with all request methods.
+ * Note: includes double new line to terminate headers */
+#define TRAILING_HEADERS_FORMAT "User-Agent: Broadcom/1.0\r\n\r\n"
+
+/** \name HTTP methods, used as the first item in the request line
+ * @{ */
+#define GET_METHOD "GET"
+#define HEAD_METHOD "HEAD"
+/* @} */
+
+/** \name Names of headers used by the code
+ * @{ */
+#define CONTENT_LENGTH_NAME "Content-Length"
+#define CONTENT_BASE_NAME "Content-Base"
+#define CONTENT_LOCATION_NAME "Content-Location"
+#define ACCEPT_RANGES_NAME "Accept-Ranges"
+#define CONNECTION_NAME "Connection"
+/* @} */
+
+/** Supported HTTP major version number */
+#define HTTP_MAJOR_VERSION 1
+/** Supported HTTP minor version number */
+#define HTTP_MINOR_VERSION 1
+
+/** Lowest successful status code value */
+#define HTTP_STATUS_OK 200
+#define HTTP_STATUS_PARTIAL_CONTENT 206
+
+typedef struct http_header_tag {
+ const char *name;
+ char *value;
+} HTTP_HEADER_T;
+
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+typedef struct VC_CONTAINER_IO_MODULE_T
+{
+ VC_CONTAINER_NET_T *sock;
+ VC_CONTAINERS_LIST_T *header_list; /**< Parsed response headers, pointing into comms buffer */
+
+ bool persistent;
+ int64_t cur_offset;
+ bool reconnecting;
+
+ /* Buffer used for sending and receiving HTTP messages */
+ char comms_buffer[COMMS_BUFFER_SIZE];
+} VC_CONTAINER_IO_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+
+static int io_http_header_comparator(const HTTP_HEADER_T *first, const HTTP_HEADER_T *second);
+static VC_CONTAINER_STATUS_T io_http_send(VC_CONTAINER_IO_T *p_ctx);
+
+VC_CONTAINER_STATUS_T vc_container_io_http_open(VC_CONTAINER_IO_T *, const char *,
+ VC_CONTAINER_IO_MODE_T);
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+/**************************************************************************//**
+ * Trim whitespace from the end and start of the string
+ *
+ * \param str String to be trimmed
+ * \return Trimmed string
+ */
+static char *io_http_trim(char *str)
+{
+ char *s = str + strlen(str);
+
+ /* Search backwards for first non-whitespace */
+ while (--s >= str &&(*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r'))
+ ; /* Everything done in the while */
+ s[1] = '\0';
+
+ /* Now move start of string forwards to first non-whitespace */
+ s = str;
+ while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
+ s++;
+
+ return s;
+}
+
+/**************************************************************************//**
+ * Header comparison function.
+ * Compare two header structures and return whether the first is less than,
+ * equal to or greater than the second.
+ *
+ * @param first The first structure to be compared.
+ * @param second The second structure to be compared.
+ * @return Negative if first is less than second, positive if first is greater
+ * and zero if they are equal.
+ */
+static int io_http_header_comparator(const HTTP_HEADER_T *first, const HTTP_HEADER_T *second)
+{
+ return strcasecmp(first->name, second->name);
+}
+
+/**************************************************************************//**
+ * Check a response status line to see if the response is usable or not.
+ * Reasons for invalidity include:
+ * - Incorrectly formatted
+ * - Unsupported version
+ * - Status code is not in the 2xx range
+ *
+ * @param status_line The response status line.
+ * @return The resulting status of the function.
+ */
+static bool io_http_successful_response_status(const char *status_line)
+{
+ unsigned int major_version, minor_version, status_code;
+
+ /* coverity[secure_coding] String is null-terminated */
+ if (sscanf(status_line, "HTTP/%u.%u %u", &major_version, &minor_version, &status_code) != 3)
+ {
+ LOG_ERROR(NULL, "HTTP: Invalid response status line:\n%s", status_line);
+ return false;
+ }
+
+ if (major_version != HTTP_MAJOR_VERSION || minor_version != HTTP_MINOR_VERSION)
+ {
+ LOG_ERROR(NULL, "HTTP: Unexpected response HTTP version: %u.%u", major_version, minor_version);
+ return false;
+ }
+
+ if (status_code != HTTP_STATUS_OK && status_code != HTTP_STATUS_PARTIAL_CONTENT)
+ {
+ LOG_ERROR(NULL, "HTTP: Response status unsuccessful:\n%s", status_line);
+ return false;
+ }
+
+ return true;
+}
+
+/**************************************************************************//**
+ * Get the content length header from the response headers as an unsigned
+ * 64-bit integer.
+ * If the content length header is not found or badly formatted, zero is
+ * returned.
+ *
+ * @param header_list The response headers.
+ * @return The content length.
+ */
+static uint64_t io_http_get_content_length(VC_CONTAINERS_LIST_T *header_list)
+{
+ uint64_t content_length = 0;
+ HTTP_HEADER_T header;
+
+ header.name = CONTENT_LENGTH_NAME;
+ if (header_list && vc_containers_list_find_entry(header_list, &header))
+ /* coverity[secure_coding] String is null-terminated */
+ sscanf(header.value, "%"PRIu64, &content_length);
+
+ return content_length;
+}
+
+/**************************************************************************//**
+ * Get the accept ranges header from the response headers and verify that
+ * the server accepts byte ranges..
+ * If the accept ranges header is not found false is returned.
+ *
+ * @param header_list The response headers.
+ * @return The resulting status of the function.
+ */
+static bool io_http_check_accept_range(VC_CONTAINERS_LIST_T *header_list)
+{
+ HTTP_HEADER_T header;
+
+ header.name = ACCEPT_RANGES_NAME;
+ if (header_list && vc_containers_list_find_entry(header_list, &header))
+ {
+ /* coverity[secure_coding] String is null-terminated */
+ if (!strcasecmp(header.value, "bytes"))
+ return true;
+ }
+
+ return false;
+}
+
+/**************************************************************************//**
+ * Check whether the server supports persistent connections.
+ *
+ * @param header_list The response headers.
+ * @return The resulting status of the function.
+ */
+static bool io_http_check_persistent_connection(VC_CONTAINERS_LIST_T *header_list)
+{
+ HTTP_HEADER_T header;
+
+ header.name = CONNECTION_NAME;
+ if (header_list && vc_containers_list_find_entry(header_list, &header))
+ {
+ /* coverity[secure_coding] String is null-terminated */
+ if (!strcasecmp(header.value, "close"))
+ return false;
+ }
+
+ return true;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T translate_net_status_to_container_status(vc_container_net_status_t net_status)
+{
+ switch (net_status)
+ {
+ case VC_CONTAINER_NET_SUCCESS: return VC_CONTAINER_SUCCESS;
+ case VC_CONTAINER_NET_ERROR_INVALID_SOCKET: return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ case VC_CONTAINER_NET_ERROR_NOT_ALLOWED: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ case VC_CONTAINER_NET_ERROR_INVALID_PARAMETER: return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ case VC_CONTAINER_NET_ERROR_NO_MEMORY: return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ case VC_CONTAINER_NET_ERROR_IN_USE: return VC_CONTAINER_ERROR_URI_OPEN_FAILED;
+ case VC_CONTAINER_NET_ERROR_NETWORK: return VC_CONTAINER_ERROR_EOS;
+ case VC_CONTAINER_NET_ERROR_CONNECTION_LOST: return VC_CONTAINER_ERROR_EOS;
+ case VC_CONTAINER_NET_ERROR_NOT_CONNECTED: return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ case VC_CONTAINER_NET_ERROR_TIMED_OUT: return VC_CONTAINER_ERROR_ABORTED;
+ case VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED: return VC_CONTAINER_ERROR_NOT_FOUND;
+ case VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND: return VC_CONTAINER_ERROR_NOT_FOUND;
+ case VC_CONTAINER_NET_ERROR_TRY_AGAIN: return VC_CONTAINER_ERROR_CONTINUE;
+ default: return VC_CONTAINER_ERROR_FAILED;
+ }
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_http_open_socket(VC_CONTAINER_IO_T *ctx)
+{
+ VC_CONTAINER_IO_MODULE_T *module = ctx->module;
+ VC_CONTAINER_STATUS_T status;
+ const char *host, *port;
+
+ /* Treat empty host or port strings as not defined */
+ port = vc_uri_port(ctx->uri_parts);
+ if (port && !*port)
+ port = NULL;
+
+ /* Require the port to be defined */
+ if (!port)
+ {
+ status = VC_CONTAINER_ERROR_URI_OPEN_FAILED;
+ goto error;
+ }
+
+ host = vc_uri_host(ctx->uri_parts);
+ if (host && !*host)
+ host = NULL;
+
+ if (!host)
+ {
+ status = VC_CONTAINER_ERROR_URI_OPEN_FAILED;
+ goto error;
+ }
+
+ module->sock = vc_container_net_open(host, port, VC_CONTAINER_NET_OPEN_FLAG_STREAM, NULL);
+ if (!module->sock)
+ {
+ status = VC_CONTAINER_ERROR_URI_NOT_FOUND;
+ goto error;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+
+error:
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_http_close_socket(VC_CONTAINER_IO_MODULE_T *module)
+{
+ if (module->sock)
+ {
+ vc_container_net_close(module->sock);
+ module->sock = NULL;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static size_t io_http_read_from_net(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size)
+{
+ size_t ret;
+ vc_container_net_status_t net_status;
+
+ ret = vc_container_net_read(p_ctx->module->sock, buffer, size);
+ net_status = vc_container_net_status(p_ctx->module->sock);
+ p_ctx->status = translate_net_status_to_container_status(net_status);
+
+ return ret;
+}
+
+/**************************************************************************//**
+ * Reads an HTTP response and parses it into headers and content.
+ * The headers and content remain stored in the comms buffer, but referenced
+ * by the module's header list. Content uses a special header name that cannot
+ * occur in the real headers.
+ *
+ * @param p_ctx The HTTP reader context.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T io_http_read_response(VC_CONTAINER_IO_T *p_ctx)
+{
+ VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
+ char *next_read = module->comms_buffer;
+ size_t space_available = sizeof(module->comms_buffer) - 1; /* Allow for a NUL */
+ char *ptr = next_read;
+ bool end_response = false;
+ HTTP_HEADER_T header;
+ const char endstr[] = "\r\n\r\n";
+ int endcount = sizeof(endstr) - 1;
+ int endchk = 0;
+
+ vc_containers_list_reset(module->header_list);
+
+ /* Response status line doesn't need to be stored, just checked */
+ header.name = NULL;
+ header.value = next_read;
+
+ /*
+ * We need to read just a byte at a time to make sure that we just read the HTTP response and
+ * no more. For example, if a GET operation was requested the file being fetched will also
+ * be waiting to be read on the socket.
+ */
+
+ while (space_available)
+ {
+ if (io_http_read_from_net(p_ctx, next_read, 1) != 1)
+ break;
+
+ next_read++;
+ space_available--;
+
+ if (next_read[-1] == endstr[endchk])
+ {
+ if (++endchk == endcount)
+ break;
+ }
+ else
+ endchk = 0;
+ }
+ if (!space_available)
+ {
+ LOG_ERROR(NULL, "comms buffer too small for complete HTTP message (%d)",
+ sizeof(module->comms_buffer));
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ *next_read = '\0';
+
+ if (endchk == endcount)
+ {
+ if (ENABLE_HTTP_EXTRA_LOGGING)
+ LOG_DEBUG(NULL, "READ FROM SERVER: %d bytes\n%s\n-----------------------------------------",
+ sizeof(module->comms_buffer) - 1 - space_available, module->comms_buffer);
+
+ while (!end_response && ptr < next_read)
+ {
+ switch (*ptr)
+ {
+ case ':':
+ if (header.value)
+ {
+ /* Just another character in the value */
+ ptr++;
+ } else {
+ /* End of name, expect value next */
+ *ptr++ = '\0';
+ header.value = ptr;
+ }
+ break;
+
+ case '\n':
+ if (header.value)
+ {
+ /* End of line while parsing the value part of the header, add name/value pair to list */
+ *ptr++ = '\0';
+ header.value = io_http_trim(header.value);
+ if (header.name)
+ {
+ if (!vc_containers_list_insert(module->header_list, &header, false))
+ {
+ LOG_ERROR(NULL, "HTTP: Failed to add <%s> header to list", header.name);
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ }
+ } else {
+ /* Check response status line */
+ if (!io_http_successful_response_status(header.value))
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+ /* Ready for next header */
+ header.name = ptr;
+ header.value = NULL;
+ } else {
+ /* End of line while parsing the name of a header */
+ *ptr++ = '\0';
+ if (*header.name && *header.name != '\r')
+ {
+ /* A non-empty name is invalid, so fail */
+ LOG_ERROR(NULL, "HTTP: Invalid name in header - no colon:\n%s", header.name);
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ /* An empty name signifies the end of the HTTP response */
+ end_response = true;
+ }
+ break;
+
+ default:
+ /* Just another character in either the name or the value */
+ ptr++;
+ }
+ }
+ }
+
+ if (!space_available && !end_response)
+ {
+ /* Ran out of buffer space */
+ LOG_ERROR(NULL, "HTTP: Response header section too big");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ return p_ctx->status;
+}
+
+/**************************************************************************//**
+ * Send a GET request to the HTTP server.
+ *
+ * @param p_ctx The reader context.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T io_http_send_get_request(VC_CONTAINER_IO_T *p_ctx, size_t size)
+{
+ VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
+ char *ptr = module->comms_buffer, *end = ptr + sizeof(module->comms_buffer);
+ int64_t end_offset;
+
+ ptr += snprintf(ptr, end - ptr, HTTP_REQUEST_LINE_FORMAT, GET_METHOD,
+ vc_uri_path(p_ctx->uri_parts), vc_uri_host(p_ctx->uri_parts));
+
+ end_offset = module->cur_offset + size - 1;
+ if (end_offset >= p_ctx->size)
+ end_offset = p_ctx->size - 1;
+
+ if (ptr < end)
+ ptr += snprintf(ptr, end - ptr, HTTP_RANGE_REQUEST, module->cur_offset, end_offset);
+
+ if (ptr < end)
+ ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT);
+
+ if (ptr >= end)
+ {
+ LOG_ERROR(0, "comms buffer too small (%i/%u)", (int)(end - ptr),
+ sizeof(module->comms_buffer));
+ return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ }
+
+ if (ENABLE_HTTP_EXTRA_LOGGING)
+ LOG_DEBUG(NULL, "Sending server read request:\n%s\n---------------------\n", module->comms_buffer);
+ return io_http_send(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_http_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset)
+{
+ VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
+
+ /*
+ * No seeking past the end of the file.
+ */
+
+ if (offset < 0 || offset > p_ctx->size)
+ {
+ p_ctx->status = VC_CONTAINER_ERROR_EOS;
+ return VC_CONTAINER_ERROR_EOS;
+ }
+
+ module->cur_offset = offset;
+ p_ctx->status = VC_CONTAINER_SUCCESS;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_http_close(VC_CONTAINER_IO_T *p_ctx)
+{
+ VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
+
+ if (!module)
+ return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+
+ io_http_close_socket(module);
+ if (module->header_list)
+ vc_containers_list_destroy(module->header_list);
+
+ free(module);
+ p_ctx->module = NULL;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static size_t io_http_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
+ size_t content_length;
+ size_t bytes_read;
+ size_t ret = 0;
+ char *ptr = buffer;
+
+ /*
+ * Are we at the end of the file?
+ */
+
+ if (module->cur_offset >= p_ctx->size)
+ {
+ p_ctx->status = VC_CONTAINER_ERROR_EOS;
+ return 0;
+ }
+
+ if (!module->persistent)
+ {
+ status = io_http_open_socket(p_ctx);
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ LOG_ERROR(NULL, "Error opening socket for GET request");
+ return status;
+ }
+ }
+
+ /* Send GET request and get response */
+ status = io_http_send_get_request(p_ctx, size);
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ LOG_ERROR(NULL, "Error sending GET request");
+ goto error;
+ }
+
+ status = io_http_read_response(p_ctx);
+ if (status == VC_CONTAINER_ERROR_EOS && !module->reconnecting)
+ {
+ LOG_DEBUG(NULL, "reconnecting");
+ io_http_close_socket(module);
+ status = io_http_open_socket(p_ctx);
+ if (status == VC_CONTAINER_SUCCESS)
+ {
+ module->reconnecting = true;
+ status = io_http_read(p_ctx, buffer, size);
+ module->reconnecting = false;
+ return status;
+ }
+ }
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ LOG_ERROR(NULL, "Error reading GET response");
+ goto error;
+ }
+
+ /*
+ * How much data is the server offering us?
+ */
+
+ content_length = (size_t)io_http_get_content_length(module->header_list);
+ if (content_length > size)
+ {
+ LOG_ERROR(NULL, "received too much data (%i/%i)",
+ (int)content_length, (int)size);
+ status = VC_CONTAINER_ERROR_CORRUPTED;
+ goto error;
+ }
+
+ bytes_read = 0;
+ while (bytes_read < content_length && p_ctx->status == VC_CONTAINER_SUCCESS)
+ {
+ ret = io_http_read_from_net(p_ctx, ptr, content_length - bytes_read);
+ if (p_ctx->status == VC_CONTAINER_SUCCESS)
+ {
+ bytes_read += ret;
+ ptr += ret;
+ }
+ }
+
+ if (p_ctx->status == VC_CONTAINER_SUCCESS)
+ {
+ module->cur_offset += bytes_read;
+ ret = bytes_read;
+ }
+
+ if (!module->persistent)
+ io_http_close_socket(module);
+
+ return ret;
+
+error:
+ if (!module->persistent)
+ io_http_close_socket(module);
+
+ return status;
+}
+
+/*****************************************************************************/
+static size_t io_http_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size)
+{
+ size_t ret = vc_container_net_write(p_ctx->module->sock, buffer, size);
+ vc_container_net_status_t net_status;
+
+ net_status = vc_container_net_status(p_ctx->module->sock);
+ p_ctx->status = translate_net_status_to_container_status(net_status);
+
+ return ret;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_http_control(struct VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_CONTROL_T operation,
+ va_list args)
+{
+ vc_container_net_status_t net_status;
+ VC_CONTAINER_STATUS_T status;
+
+ switch (operation)
+ {
+ case VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE:
+ net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE, args);
+ break;
+ case VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS:
+ net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, args);
+ break;
+ default:
+ net_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
+ }
+
+ status = translate_net_status_to_container_status(net_status);
+ p_ctx->status = status;
+
+ return status;
+}
+
+/**************************************************************************//**
+ * Send out the data in the comms buffer.
+ *
+ * @param p_ctx The reader context.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T io_http_send(VC_CONTAINER_IO_T *p_ctx)
+{
+ VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
+ size_t to_write;
+ size_t written;
+ const char *buffer = module->comms_buffer;
+
+ to_write = strlen(buffer);
+
+ while (to_write)
+ {
+ written = io_http_write(p_ctx, buffer, to_write);
+ if (p_ctx->status != VC_CONTAINER_SUCCESS)
+ break;
+
+ to_write -= written;
+ buffer += written;
+ }
+
+ return p_ctx->status;
+}
+
+/**************************************************************************//**
+ * Send a HEAD request to the HTTP server.
+ *
+ * @param p_ctx The reader context.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T io_http_send_head_request(VC_CONTAINER_IO_T *p_ctx)
+{
+ VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
+ char *ptr = module->comms_buffer, *end = ptr + sizeof(module->comms_buffer);
+
+ ptr += snprintf(ptr, end - ptr, HTTP_REQUEST_LINE_FORMAT, HEAD_METHOD,
+ vc_uri_path(p_ctx->uri_parts), vc_uri_host(p_ctx->uri_parts));
+ if (ptr < end)
+ ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT);
+
+ if (ptr >= end)
+ {
+ LOG_ERROR(0, "comms buffer too small (%i/%u)", (int)(end - ptr),
+ sizeof(module->comms_buffer));
+ return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ }
+
+ return io_http_send(p_ctx);
+}
+
+static VC_CONTAINER_STATUS_T io_http_head(VC_CONTAINER_IO_T *p_ctx)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
+ uint64_t content_length;
+
+ /* Send HEAD request and get response */
+ status = io_http_send_head_request(p_ctx);
+ if (status != VC_CONTAINER_SUCCESS)
+ return status;
+ status = io_http_read_response(p_ctx);
+ if (status != VC_CONTAINER_SUCCESS)
+ return status;
+
+ /*
+ * Save the content length since that's our file size.
+ */
+
+ content_length = io_http_get_content_length(module->header_list);
+ if (content_length)
+ {
+ p_ctx->size = content_length;
+ LOG_DEBUG(NULL, "File size is %"PRId64, p_ctx->size);
+ }
+
+ /*
+ * Now make sure that the server supports byte range requests.
+ */
+
+ if (!io_http_check_accept_range(module->header_list))
+ {
+ LOG_ERROR(NULL, "Server doesn't support byte range requests");
+ return VC_CONTAINER_ERROR_FAILED;
+ }
+
+ /*
+ * Does it support persistent connections?
+ */
+
+ if (io_http_check_persistent_connection(module->header_list))
+ {
+ module->persistent = true;
+ }
+ else
+ {
+ LOG_DEBUG(NULL, "Server does not support persistent connections");
+ io_http_close_socket(module);
+ }
+
+ module->cur_offset = 0;
+
+ return status;
+}
+
+/*****************************************************************************
+Functions exported as part of the I/O Module API
+ *****************************************************************************/
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_io_http_open(VC_CONTAINER_IO_T *p_ctx,
+ const char *unused, VC_CONTAINER_IO_MODE_T mode)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_IO_MODULE_T *module = 0;
+ VC_CONTAINER_PARAM_UNUSED(unused);
+
+ /* Check the URI to see if we're dealing with an http stream */
+ if (!vc_uri_scheme(p_ctx->uri_parts) ||
+ strcasecmp(vc_uri_scheme(p_ctx->uri_parts), "http"))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /*
+ * Some basic error checking.
+ */
+
+ if (mode == VC_CONTAINER_IO_MODE_WRITE)
+ {
+ status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ goto error;
+ }
+
+ if (strlen(p_ctx->uri) > HTTP_URI_LENGTH_MAX)
+ {
+ status = VC_CONTAINER_ERROR_URI_OPEN_FAILED;
+ goto error;
+ }
+
+ module = calloc(1, sizeof(*module));
+ if (!module)
+ {
+ status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ goto error;
+ }
+ p_ctx->module = module;
+
+ /* header_list will contain pointers into the response_buffer, so take care in re-use */
+ module->header_list = vc_containers_list_create(HEADER_LIST_INITIAL_CAPACITY, sizeof(HTTP_HEADER_T),
+ (VC_CONTAINERS_LIST_COMPARATOR_T)io_http_header_comparator);
+ if (!module->header_list)
+ {
+ status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ /*
+ * Make sure that we have a port number.
+ */
+
+ if (vc_uri_port(p_ctx->uri_parts) == NULL)
+ vc_uri_set_port(p_ctx->uri_parts, IO_HTTP_DEFAULT_PORT);
+
+ status = io_http_open_socket(p_ctx);
+ if (status != VC_CONTAINER_SUCCESS)
+ goto error;
+
+ /*
+ * Whoo hoo! Our socket is open. Now let's send a HEAD request.
+ */
+
+ status = io_http_head(p_ctx);
+ if (status != VC_CONTAINER_SUCCESS)
+ goto error;
+
+ p_ctx->pf_close = io_http_close;
+ p_ctx->pf_read = io_http_read;
+ p_ctx->pf_write = NULL;
+ p_ctx->pf_control = io_http_control;
+ p_ctx->pf_seek = io_http_seek;
+
+ p_ctx->capabilities = VC_CONTAINER_IO_CAPS_NO_CACHING;
+ p_ctx->capabilities |= VC_CONTAINER_IO_CAPS_SEEK_SLOW;
+
+ return VC_CONTAINER_SUCCESS;
+
+error:
+ io_http_close(p_ctx);
+ return status;
+}
diff --git a/gfx/include/userland/containers/io/io_net.c b/gfx/include/userland/containers/io/io_net.c
new file mode 100644
index 0000000000..a1912e52e3
--- /dev/null
+++ b/gfx/include/userland/containers/io/io_net.c
@@ -0,0 +1,379 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+
+#include "containers/containers.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_io.h"
+#include "containers/core/containers_uri.h"
+#include "containers/net/net_sockets.h"
+
+/* Uncomment this macro definition to capture data read and written through this interface */
+/* #define IO_NET_CAPTURE_PACKETS */
+
+#ifdef IO_NET_CAPTURE_PACKETS
+#include
+
+#ifdef ENABLE_CONTAINERS_STANDALONE
+#ifdef _MSC_VER
+#define IO_NET_CAPTURE_PREFIX "C:\\"
+#else /* !_MSC_VER */
+#define IO_NET_CAPTURE_PREFIX "~/"
+#endif
+#else /* !ENABLE_CONTAINERS_STANDALONE */
+#define IO_NET_CAPTURE_PREFIX "/mfs/sd/"
+#endif
+
+#define IO_NET_CAPTURE_READ_FILE "capture_read_%s_%s%c.pkt"
+#define IO_NET_CAPTURE_WRITE_FILE "capture_write_%s_%s%c.pkt"
+#define IO_NET_CAPTURE_READ_FORMAT IO_NET_CAPTURE_PREFIX IO_NET_CAPTURE_READ_FILE
+#define IO_NET_CAPTURE_WRITE_FORMAT IO_NET_CAPTURE_PREFIX IO_NET_CAPTURE_WRITE_FILE
+
+#define CAPTURE_FILENAME_BUFFER_SIZE 300
+
+#define CAPTURE_BUFFER_SIZE 65536
+
+/** Native byte order word */
+#define NATIVE_BYTE_ORDER 0x50415753
+#endif
+
+/******************************************************************************
+Defines and constants.
+******************************************************************************/
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+typedef struct VC_CONTAINER_IO_MODULE_T
+{
+ VC_CONTAINER_NET_T *sock;
+#ifdef IO_NET_CAPTURE_PACKETS
+ FILE *read_capture_file;
+ FILE *write_capture_file;
+#endif
+} VC_CONTAINER_IO_MODULE_T;
+
+/** List of recognised network URI schemes (TCP or UDP).
+ * Note: always use lower case for the scheme name. */
+static struct
+{
+ const char *scheme;
+ bool is_udp;
+} recognised_schemes[] = {
+ { "rtp:", true },
+ { "rtsp:", false },
+};
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *, const char *,
+ VC_CONTAINER_IO_MODE_T );
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+#ifdef IO_NET_CAPTURE_PACKETS
+/*****************************************************************************/
+static FILE *io_net_open_capture_file(const char *host_str,
+ const char *port_str,
+ bool is_udp,
+ VC_CONTAINER_IO_MODE_T mode)
+{
+ char filename[CAPTURE_FILENAME_BUFFER_SIZE];
+ const char *format;
+ FILE *stream = NULL;
+ uint32_t byte_order = NATIVE_BYTE_ORDER;
+
+ switch (mode)
+ {
+ case VC_CONTAINER_IO_MODE_WRITE:
+ format = IO_NET_CAPTURE_WRITE_FORMAT;
+ break;
+ case VC_CONTAINER_IO_MODE_READ:
+ format = IO_NET_CAPTURE_READ_FORMAT;
+ break;
+ default:
+ /* Invalid mode */
+ return NULL;
+ }
+
+ if (!host_str)
+ host_str = "";
+ if (!port_str)
+ port_str = "";
+
+ /* Check filename will fit in buffer */
+ if (strlen(format) + strlen(host_str) + strlen(port_str) - 4 > CAPTURE_FILENAME_BUFFER_SIZE)
+ return NULL;
+
+ /* Create the file */
+ sprintf(filename, format, host_str, port_str, is_udp ? 'u' : 't');
+ stream = fopen(filename, "wb");
+ if (!stream)
+ return NULL;
+
+ /* Buffer plenty of data at a time, if possible */
+ setvbuf(stream, NULL, _IOFBF, CAPTURE_BUFFER_SIZE);
+
+ /* Start file with a byte order marker */
+ if (fwrite(&byte_order, 1, sizeof(byte_order), stream) != sizeof(byte_order))
+ {
+ /* Failed to write even just the byte order mark - abort */
+ fclose(stream);
+ stream = NULL;
+ remove(filename);
+ }
+
+ return stream;
+}
+
+/*****************************************************************************/
+static void io_net_capture_write_packet( FILE *stream,
+ const char *buffer,
+ uint32_t buffer_size )
+{
+ if (stream && buffer && buffer_size)
+ {
+ fwrite(&buffer_size, 1, sizeof(buffer_size), stream);
+ fwrite(buffer, 1, buffer_size, stream);
+ }
+}
+#endif
+
+/*****************************************************************************/
+static bool io_net_recognise_scheme(const char *uri, bool *is_udp)
+{
+ size_t ii;
+ const char *scheme;
+
+ if (!uri)
+ return false;
+
+ for (ii = 0; ii < countof(recognised_schemes); ii++)
+ {
+ scheme = recognised_schemes[ii].scheme;
+ if (strncmp(scheme, uri, strlen(scheme)) == 0)
+ {
+ *is_udp = recognised_schemes[ii].is_udp;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T translate_net_status_to_container_status(vc_container_net_status_t net_status)
+{
+ switch (net_status)
+ {
+ case VC_CONTAINER_NET_SUCCESS: return VC_CONTAINER_SUCCESS;
+ case VC_CONTAINER_NET_ERROR_INVALID_SOCKET: return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ case VC_CONTAINER_NET_ERROR_NOT_ALLOWED: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ case VC_CONTAINER_NET_ERROR_INVALID_PARAMETER: return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ case VC_CONTAINER_NET_ERROR_NO_MEMORY: return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ case VC_CONTAINER_NET_ERROR_IN_USE: return VC_CONTAINER_ERROR_URI_OPEN_FAILED;
+ case VC_CONTAINER_NET_ERROR_NETWORK: return VC_CONTAINER_ERROR_EOS;
+ case VC_CONTAINER_NET_ERROR_CONNECTION_LOST: return VC_CONTAINER_ERROR_EOS;
+ case VC_CONTAINER_NET_ERROR_NOT_CONNECTED: return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+ case VC_CONTAINER_NET_ERROR_TIMED_OUT: return VC_CONTAINER_ERROR_ABORTED;
+ case VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED: return VC_CONTAINER_ERROR_NOT_FOUND;
+ case VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND: return VC_CONTAINER_ERROR_NOT_FOUND;
+ case VC_CONTAINER_NET_ERROR_TRY_AGAIN: return VC_CONTAINER_ERROR_CONTINUE;
+ default: return VC_CONTAINER_ERROR_FAILED;
+ }
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_net_close( VC_CONTAINER_IO_T *p_ctx )
+{
+ VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
+
+ if (!module)
+ return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+
+ if (module->sock)
+ vc_container_net_close(module->sock);
+#ifdef IO_NET_CAPTURE_PACKETS
+ if (module->read_capture_file)
+ fclose(module->read_capture_file);
+ if (module->write_capture_file)
+ fclose(module->write_capture_file);
+#endif
+ free(module);
+ p_ctx->module = NULL;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static size_t io_net_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size)
+{
+ size_t ret = vc_container_net_read(p_ctx->module->sock, buffer, size);
+ vc_container_net_status_t net_status;
+
+ net_status = vc_container_net_status(p_ctx->module->sock);
+ p_ctx->status = translate_net_status_to_container_status(net_status);
+
+#ifdef IO_NET_CAPTURE_PACKETS
+ if (p_ctx->status == VC_CONTAINER_SUCCESS)
+ io_net_capture_write_packet(p_ctx->module->read_capture_file, (const char *)buffer, ret);
+#endif
+
+ return ret;
+}
+
+/*****************************************************************************/
+static size_t io_net_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size)
+{
+ size_t ret = vc_container_net_write(p_ctx->module->sock, buffer, size);
+ vc_container_net_status_t net_status;
+
+ net_status = vc_container_net_status(p_ctx->module->sock);
+ p_ctx->status = translate_net_status_to_container_status(net_status);
+
+#ifdef IO_NET_CAPTURE_PACKETS
+ if (p_ctx->status == VC_CONTAINER_SUCCESS)
+ io_net_capture_write_packet(p_ctx->module->write_capture_file, (const char *)buffer, ret);
+#endif
+
+ return ret;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_net_control(struct VC_CONTAINER_IO_T *p_ctx,
+ VC_CONTAINER_CONTROL_T operation,
+ va_list args)
+{
+ vc_container_net_status_t net_status;
+ VC_CONTAINER_STATUS_T status;
+
+ switch (operation)
+ {
+ case VC_CONTAINER_CONTROL_IO_SET_READ_BUFFER_SIZE:
+ net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE, args);
+ break;
+ case VC_CONTAINER_CONTROL_IO_SET_READ_TIMEOUT_MS:
+ net_status = vc_container_net_control(p_ctx->module->sock, VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS, args);
+ break;
+ default:
+ net_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
+ }
+
+ status = translate_net_status_to_container_status(net_status);
+ p_ctx->status = status;
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_net_open_socket(VC_CONTAINER_IO_T *ctx,
+ VC_CONTAINER_IO_MODE_T mode, bool is_udp)
+{
+ VC_CONTAINER_IO_MODULE_T *module = ctx->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ const char *host, *port;
+
+ /* Treat empty host or port strings as not defined */
+ port = vc_uri_port(ctx->uri_parts);
+ if (port && !*port)
+ port = NULL;
+
+ /* Require the port to be defined */
+ if (!port) { status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; }
+
+ host = vc_uri_host(ctx->uri_parts);
+ if (host && !*host)
+ host = NULL;
+
+ if (!host)
+ {
+ /* TCP servers cannot be handled by this interface and UDP senders need a target */
+ if (!is_udp || mode == VC_CONTAINER_IO_MODE_WRITE)
+ {
+ status = VC_CONTAINER_ERROR_URI_OPEN_FAILED;
+ goto error;
+ }
+ }
+
+ module->sock = vc_container_net_open(host, port, is_udp ? 0 : VC_CONTAINER_NET_OPEN_FLAG_STREAM, NULL);
+ if (!module->sock) { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; }
+
+#ifdef IO_NET_CAPTURE_PACKETS
+ if (!is_udp || mode == VC_CONTAINER_IO_MODE_READ)
+ module->read_capture_file = io_net_open_capture_file(host, port, is_udp, VC_CONTAINER_IO_MODE_READ);
+ if (!is_udp || mode == VC_CONTAINER_IO_MODE_WRITE)
+ module->write_capture_file = io_net_open_capture_file(host, port, is_udp, VC_CONTAINER_IO_MODE_WRITE);
+#endif
+
+error:
+ return status;
+}
+
+/*****************************************************************************
+Functions exported as part of the I/O Module API
+ *****************************************************************************/
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_io_net_open( VC_CONTAINER_IO_T *p_ctx,
+ const char *unused, VC_CONTAINER_IO_MODE_T mode )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_IO_MODULE_T *module = 0;
+ bool is_udp;
+ VC_CONTAINER_PARAM_UNUSED(unused);
+
+ if (!io_net_recognise_scheme(p_ctx->uri, &is_udp))
+ { status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; }
+
+ module = (VC_CONTAINER_IO_MODULE_T *)malloc( sizeof(*module) );
+ if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ p_ctx->module = module;
+
+ status = io_net_open_socket(p_ctx, mode, is_udp);
+ if (status != VC_CONTAINER_SUCCESS)
+ goto error;
+
+ p_ctx->pf_close = io_net_close;
+ p_ctx->pf_read = io_net_read;
+ p_ctx->pf_write = io_net_write;
+ p_ctx->pf_control = io_net_control;
+
+ /* Disable caching, as this will block waiting for enough data to fill the cache or an error */
+ p_ctx->capabilities = VC_CONTAINER_IO_CAPS_CANT_SEEK;
+
+ return VC_CONTAINER_SUCCESS;
+
+error:
+ io_net_close(p_ctx);
+ return status;
+}
diff --git a/gfx/include/userland/containers/io/io_null.c b/gfx/include/userland/containers/io/io_null.c
new file mode 100644
index 0000000000..cd83bb840a
--- /dev/null
+++ b/gfx/include/userland/containers/io/io_null.c
@@ -0,0 +1,91 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+#include
+
+#include "containers/containers.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_io.h"
+#include "containers/core/containers_uri.h"
+
+VC_CONTAINER_STATUS_T vc_container_io_null_open( VC_CONTAINER_IO_T *, const char *,
+ VC_CONTAINER_IO_MODE_T );
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_null_close( VC_CONTAINER_IO_T *p_ctx )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static size_t io_null_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size)
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(buffer);
+ VC_CONTAINER_PARAM_UNUSED(size);
+ return size;
+}
+
+/*****************************************************************************/
+static size_t io_null_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size)
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(buffer);
+ VC_CONTAINER_PARAM_UNUSED(size);
+ return size;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_null_seek(VC_CONTAINER_IO_T *p_ctx, int64_t offset)
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(offset);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_io_null_open( VC_CONTAINER_IO_T *p_ctx,
+ const char *unused, VC_CONTAINER_IO_MODE_T mode )
+{
+ VC_CONTAINER_PARAM_UNUSED(unused);
+ VC_CONTAINER_PARAM_UNUSED(mode);
+
+ /* Check the URI */
+ if (!vc_uri_scheme(p_ctx->uri_parts) ||
+ (strcasecmp(vc_uri_scheme(p_ctx->uri_parts), "null") &&
+ strcasecmp(vc_uri_scheme(p_ctx->uri_parts), "null")))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ p_ctx->pf_close = io_null_close;
+ p_ctx->pf_read = io_null_read;
+ p_ctx->pf_write = io_null_write;
+ p_ctx->pf_seek = io_null_seek;
+ return VC_CONTAINER_SUCCESS;
+}
diff --git a/gfx/include/userland/containers/io/io_pktfile.c b/gfx/include/userland/containers/io/io_pktfile.c
new file mode 100644
index 0000000000..7f4defbbcb
--- /dev/null
+++ b/gfx/include/userland/containers/io/io_pktfile.c
@@ -0,0 +1,261 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+#include
+#include
+
+#include "containers/containers.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_io.h"
+#include "containers/core/containers_uri.h"
+
+/** Native byte order word */
+#define NATIVE_BYTE_ORDER 0x50415753
+/** Reverse of native byte order - need to swap bytes around */
+#define SWAP_BYTE_ORDER 0x53574150
+
+typedef struct VC_CONTAINER_IO_MODULE_T
+{
+ FILE *stream;
+ bool is_native_order;
+} VC_CONTAINER_IO_MODULE_T;
+
+/** List of recognised schemes.
+ * Note: always use lower case for the scheme name. */
+static const char * recognised_schemes[] = {
+ "rtp", "rtppkt", "rtsp", "rtsppkt", "pktfile",
+};
+
+VC_CONTAINER_STATUS_T vc_container_io_pktfile_open( VC_CONTAINER_IO_T *, const char *,
+ VC_CONTAINER_IO_MODE_T );
+
+/*****************************************************************************/
+static bool recognise_scheme(const char *scheme)
+{
+ size_t ii;
+
+ if (!scheme)
+ return false;
+
+ for (ii = 0; ii < countof(recognised_schemes); ii++)
+ {
+ if (strcmp(recognised_schemes[ii], scheme) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+/*****************************************************************************/
+static uint32_t swap_byte_order( uint32_t value )
+{
+ /* Reverse the order of the bytes in the word */
+ return ((value << 24) | ((value & 0xFF00) << 8) | ((value >> 8) & 0xFF00) | (value >> 24));
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T io_pktfile_close( VC_CONTAINER_IO_T *p_ctx )
+{
+ VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
+ fclose(module->stream);
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static size_t io_pktfile_read(VC_CONTAINER_IO_T *p_ctx, void *buffer, size_t size)
+{
+ VC_CONTAINER_IO_MODULE_T *module = p_ctx->module;
+ uint32_t length = 0;
+ size_t ret;
+
+ ret = fread(&length, 1, sizeof(length), module->stream);
+ if (ret != sizeof(length))
+ {
+ if( feof(module->stream) ) p_ctx->status = VC_CONTAINER_ERROR_EOS;
+ else p_ctx->status = VC_CONTAINER_ERROR_FAILED;
+ return 0;
+ }
+
+ if (!module->is_native_order)
+ length = swap_byte_order(length);
+
+ if (length > 1<<20)
+ {
+ p_ctx->status = VC_CONTAINER_ERROR_FAILED;
+ return 0;
+ }
+
+ if (size > length)
+ size = length;
+ ret = fread(buffer, 1, size, module->stream);
+ if(ret != size)
+ {
+ if( feof(module->stream) ) p_ctx->status = VC_CONTAINER_ERROR_EOS;
+ else p_ctx->status = VC_CONTAINER_ERROR_FAILED;
+ }
+ else if (length > size)
+ {
+ /* Not enough space to read all the packet, so skip to the next one. */
+ length -= size;
+ vc_container_assert((long)length > 0);
+ fseek(module->stream, (long)length, SEEK_CUR);
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+static size_t io_pktfile_write(VC_CONTAINER_IO_T *p_ctx, const void *buffer, size_t size)
+{
+ uint32_t size_word;
+ size_t ret;
+
+ if (size >= 0xFFFFFFFFUL)
+ size_word = 0xFFFFFFFFUL;
+ else
+ size_word = (uint32_t)size;
+
+ ret = fwrite(&size_word, 1, sizeof(size_word), p_ctx->module->stream);
+ if (ret != sizeof(size_word))
+ {
+ p_ctx->status = VC_CONTAINER_ERROR_FAILED;
+ return 0;
+ }
+
+ ret = fwrite(buffer, 1, size_word, p_ctx->module->stream);
+ if (ret != size_word)
+ p_ctx->status = VC_CONTAINER_ERROR_FAILED;
+ if (fflush(p_ctx->module->stream) != 0)
+ p_ctx->status = VC_CONTAINER_ERROR_FAILED;
+
+ return ret;
+}
+
+/*****************************************************************************/
+static FILE *open_file(VC_CONTAINER_IO_T *ctx, VC_CONTAINER_IO_MODE_T mode,
+ VC_CONTAINER_STATUS_T *p_status)
+{
+ const char *psz_mode = mode == VC_CONTAINER_IO_MODE_WRITE ? "wb+" : "rb";
+ FILE *stream = 0;
+ const char *port, *path;
+
+ /* Treat empty port or path strings as not defined */
+ port = vc_uri_port(ctx->uri_parts);
+ if (port && !*port)
+ port = NULL;
+
+ path = vc_uri_path(ctx->uri_parts);
+ if (path && !*path)
+ path = NULL;
+
+ /* Require the port to be undefined and the path to be defined */
+ if (port || !path) { *p_status = VC_CONTAINER_ERROR_URI_OPEN_FAILED; goto error; }
+
+ if (!recognise_scheme(vc_uri_scheme(ctx->uri_parts)))
+ { *p_status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; }
+
+ stream = fopen(path, psz_mode);
+ if(!stream) { *p_status = VC_CONTAINER_ERROR_URI_NOT_FOUND; goto error; }
+
+ *p_status = VC_CONTAINER_SUCCESS;
+ return stream;
+
+error:
+ return NULL;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T write_byte_order(FILE *stream)
+{
+ /* Simple byte order header word */
+ uint32_t value = NATIVE_BYTE_ORDER;
+
+ if (fwrite(&value, 1, sizeof(value), stream) != sizeof(value))
+ return VC_CONTAINER_ERROR_OUT_OF_SPACE;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T read_byte_order(FILE *stream, bool *is_native)
+{
+ uint32_t value;
+
+ if (fread(&value, 1, sizeof(value), stream) != sizeof(value))
+ return VC_CONTAINER_ERROR_EOS;
+
+ switch (value)
+ {
+ case NATIVE_BYTE_ORDER: *is_native = true; break;
+ case SWAP_BYTE_ORDER: *is_native = false; break;
+ default: return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T vc_container_io_pktfile_open( VC_CONTAINER_IO_T *p_ctx,
+ const char *unused, VC_CONTAINER_IO_MODE_T mode )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_IO_MODULE_T *module = 0;
+ FILE *stream = 0;
+ bool is_native_order = true;
+ VC_CONTAINER_PARAM_UNUSED(unused);
+
+ stream = open_file(p_ctx, mode, &status);
+ if (status != VC_CONTAINER_SUCCESS) goto error;
+
+ if (mode == VC_CONTAINER_IO_MODE_WRITE)
+ status = write_byte_order(stream);
+ else
+ status = read_byte_order(stream, &is_native_order);
+ if (status != VC_CONTAINER_SUCCESS) goto error;
+
+ module = malloc( sizeof(*module) );
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+
+ p_ctx->module = module;
+ module->stream = stream;
+ module->is_native_order = is_native_order;
+ p_ctx->pf_close = io_pktfile_close;
+ p_ctx->pf_read = io_pktfile_read;
+ p_ctx->pf_write = io_pktfile_write;
+
+ /* Do not allow caching by I/O core, as this will merge packets in the cache. */
+ p_ctx->capabilities = VC_CONTAINER_IO_CAPS_CANT_SEEK;
+ return VC_CONTAINER_SUCCESS;
+
+error:
+ if(stream) fclose(stream);
+ return status;
+}
diff --git a/gfx/include/userland/containers/metadata/id3/CMakeLists.txt b/gfx/include/userland/containers/metadata/id3/CMakeLists.txt
new file mode 100644
index 0000000000..30178f8f64
--- /dev/null
+++ b/gfx/include/userland/containers/metadata/id3/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(reader_metadata_id3 ${LIBRARY_TYPE} id3_metadata_reader.c)
+
+target_link_libraries(reader_metadata_id3 containers)
+
+install(TARGETS reader_metadata_id3 DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/metadata/id3/id3_metadata_reader.c b/gfx/include/userland/containers/metadata/id3/id3_metadata_reader.c
new file mode 100644
index 0000000000..549273b8bb
--- /dev/null
+++ b/gfx/include/userland/containers/metadata/id3/id3_metadata_reader.c
@@ -0,0 +1,450 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+#include
+
+#define CONTAINER_IS_BIG_ENDIAN
+//#define ENABLE_CONTAINERS_LOG_FORMAT
+//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+#define CONTAINER_HELPER_LOG_INDENT(a) 0
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+
+#include "id3_metadata_strings.h"
+
+/******************************************************************************
+Defines
+******************************************************************************/
+#define ID3_SYNC_SAFE(x) ((((x >> 24) & 0x7f) << 21) | (((x >> 16) & 0x7f) << 14) | \
+ (((x >> 8) & 0x7f) << 7) | (((x >> 0) & 0x7f) << 0))
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+static VC_CONTAINER_METADATA_T *id3_metadata_append( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_METADATA_KEY_T key,
+ unsigned int size )
+{
+ VC_CONTAINER_METADATA_T *meta, **p_meta;
+ unsigned int i;
+
+ for (i = 0; i != p_ctx->meta_num; ++i)
+ {
+ if (key == p_ctx->meta[i]->key) break;
+ }
+
+ /* Avoid duplicate entries for now */
+ if (i < p_ctx->meta_num) return NULL;
+
+ /* Sanity check size, truncate if necessary */
+ size = MIN(size, 512);
+
+ /* Allocate a new metadata entry */
+ if((meta = malloc(sizeof(VC_CONTAINER_METADATA_T) + size)) == NULL)
+ return NULL;
+
+ /* We need to grow the array holding the metadata entries somehow, ideally,
+ we'd like to use a linked structure of some sort but realloc is probably
+ okay in this case */
+ if((p_meta = realloc(p_ctx->meta, sizeof(VC_CONTAINER_METADATA_T *) * (p_ctx->meta_num + 1))) == NULL)
+ {
+ free(meta);
+ return NULL;
+ }
+
+ p_ctx->meta = p_meta;
+ memset(meta, 0, sizeof(VC_CONTAINER_METADATA_T) + size);
+ p_ctx->meta[p_ctx->meta_num] = meta;
+ meta->key = key;
+ meta->value = (char *)&meta[1];
+ meta->size = size;
+ p_ctx->meta_num++;
+
+ return meta;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_METADATA_T *id3_read_metadata_entry( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_METADATA_KEY_T key, unsigned int len )
+{
+ VC_CONTAINER_METADATA_T *meta;
+
+ if ((meta = id3_metadata_append(p_ctx, key, len + 1)) != NULL)
+ {
+ unsigned int size = meta->size - 1;
+ READ_BYTES(p_ctx, meta->value, size);
+
+ if (len > size)
+ {
+ LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size);
+ SKIP_BYTES(p_ctx, len - size);
+ }
+ }
+ else
+ {
+ SKIP_BYTES(p_ctx, len);
+ }
+
+ return meta;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_METADATA_T *id3_read_metadata_entry_ex( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_METADATA_KEY_T key, unsigned int len, const char *encoding )
+{
+ VC_CONTAINER_METADATA_T *meta;
+
+ if ((meta = id3_metadata_append(p_ctx, key, encoding ? len + 2 : len + 1)) != NULL)
+ {
+ unsigned int size;
+
+ if (encoding)
+ {
+ size = meta->size - 2;
+ READ_STRING_UTF16(p_ctx, meta->value, size, "ID3v2 data");
+ }
+ else
+ {
+ size = meta->size - 1;
+ READ_STRING(p_ctx, meta->value, size, "ID3v2 data");
+ }
+
+ if (len > size)
+ {
+ LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size);
+ SKIP_BYTES(p_ctx, len - size);
+ }
+ }
+
+ return meta;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_METADATA_T *id3_add_metadata_entry( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_METADATA_KEY_T key, const char *value )
+{
+ VC_CONTAINER_METADATA_T *meta;
+ unsigned int len = strlen(value);
+
+ if ((meta = id3_metadata_append(p_ctx, key, len + 1)) != NULL)
+ {
+ unsigned int size = meta->size - 1;
+
+ if (len > size)
+ {
+ LOG_DEBUG(p_ctx, "metadata value truncated (%d characters lost)", len - size);
+ }
+
+ strncpy(meta->value, value, size);
+ }
+
+ return meta;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T id3_read_id3v2_frame( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_FOURCC_T frame_id, uint32_t frame_size )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_METADATA_KEY_T key;
+ VC_CONTAINER_METADATA_T *meta = NULL;
+ uint8_t encoding;
+ const char *charset = NULL;
+
+ if(frame_size < 1) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ switch (frame_id)
+ {
+ case VC_FOURCC('T','A','L','B'): key = VC_CONTAINER_METADATA_KEY_ALBUM; break;
+ case VC_FOURCC('T','I','T','2'): key = VC_CONTAINER_METADATA_KEY_TITLE; break;
+ case VC_FOURCC('T','R','C','K'): key = VC_CONTAINER_METADATA_KEY_TRACK; break;
+ case VC_FOURCC('T','P','E','1'): key = VC_CONTAINER_METADATA_KEY_ARTIST; break;
+ case VC_FOURCC('T','C','O','N'): key = VC_CONTAINER_METADATA_KEY_GENRE; break;
+ default: key = VC_CONTAINER_METADATA_KEY_UNKNOWN; break;
+ }
+
+ if (key == VC_CONTAINER_METADATA_KEY_UNKNOWN) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ encoding = READ_U8(p_ctx, "ID3v2 text encoding byte");
+ frame_size -= 1;
+
+ switch(encoding)
+ {
+ case 0: /* ISO-8859-1 */
+ case 3: /* UTF-8 */
+ break;
+ case 1: /* UTF-16 with BOM */
+ if(frame_size < 2) return VC_CONTAINER_ERROR_CORRUPTED;
+ SKIP_U16(p_ctx, "ID3v2 text encoding BOM"); /* FIXME: Check BOM, 0xFFFE vs 0xFEFFF */
+ frame_size -= 2;
+ charset = "UTF16-LE";
+ break;
+ case 2: /* UTF-16BE */
+ charset = "UTF16-BE";
+ break;
+ default:
+ LOG_DEBUG(p_ctx, "skipping frame, text encoding %x not supported", encoding);
+ SKIP_BYTES(p_ctx, frame_size);
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+
+ if ((meta = id3_read_metadata_entry_ex(p_ctx, key, frame_size, charset)) != NULL)
+ {
+ if (charset)
+ {
+ utf8_from_charset(charset, meta->value, meta->size, meta->value, meta->size);
+ }
+
+ meta->encoding = VC_CONTAINER_CHAR_ENCODING_UTF8; /* Okay for ISO-8859-1 as well? */
+
+ status = VC_CONTAINER_SUCCESS;
+ }
+ else
+ {
+ SKIP_BYTES(p_ctx, frame_size);
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T id3_read_id3v2_tag( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint8_t maj_version, flags;
+ uint32_t tag_size, size = 0;
+ uint8_t peek_buf[10];
+
+ SKIP_STRING(p_ctx, 3, "ID3v2 identifier");
+ maj_version = READ_U8(p_ctx, "ID3v2 version (major)");
+ SKIP_U8(p_ctx, "ID3v2 version (minor)");
+ flags = READ_U8(p_ctx, "ID3v2 flags");
+ tag_size = READ_U32(p_ctx, "ID3v2 syncsafe tag size");
+ tag_size = ID3_SYNC_SAFE(tag_size);
+ LOG_DEBUG(p_ctx, "ID3v2 tag size: %d", tag_size);
+
+ /* Check that we support this major version */
+ if (!(maj_version == 4 || maj_version == 3 || maj_version == 2))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* We can't currently handle unsynchronisation */
+ if ((flags >> 7) & 1)
+ {
+ LOG_DEBUG(p_ctx, "skipping unsynchronised tag, not supported");
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+
+ /* FIXME: check for version 2.2 and extract iTunes gapless playback information */
+ if (maj_version == 2) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ if ((flags >> 6) & 1)
+ {
+ /* Skip extended header, we don't support it */
+ uint32_t ext_hdr_size;
+ LOG_DEBUG(p_ctx, "skipping ID3v2 extended header, not supported");
+ ext_hdr_size = READ_U32(p_ctx, "ID3v2 syncsafe extended header size");
+ ext_hdr_size = ID3_SYNC_SAFE(ext_hdr_size);
+ LOG_DEBUG(p_ctx, "ID3v2 extended header size: %d", ext_hdr_size);
+ SKIP_BYTES(p_ctx, MIN(tag_size, ext_hdr_size));
+ size += ext_hdr_size;
+ }
+
+ while (PEEK_BYTES(p_ctx, peek_buf, 10) == 10 && size < tag_size)
+ {
+ VC_CONTAINER_FOURCC_T frame_id;
+ uint32_t frame_size;
+ uint8_t format_flags;
+
+ frame_id = READ_FOURCC(p_ctx, "Frame ID");
+ frame_size = READ_U32(p_ctx, "Frame Size");
+
+ if (maj_version >= 4)
+ {
+ frame_size = ID3_SYNC_SAFE(frame_size);
+ LOG_DEBUG(p_ctx, "ID3v2 actual frame size: %d", frame_size);
+ }
+
+ SKIP_U8(p_ctx, "ID3v2 status message flags");
+ format_flags = READ_U8(p_ctx, "ID3v2 format description flags");
+
+ size += 10;
+
+ if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS || !frame_id)
+ break;
+
+ /* Early exit if we detect an invalid tag size */
+ if (size + frame_size > tag_size)
+ {
+ status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ break;
+ }
+
+ /* We can't currently handle unsynchronised frames */
+ if ((format_flags >> 1) & 1)
+ {
+ LOG_DEBUG(p_ctx, "skipping unsynchronised frame, not supported");
+ SKIP_BYTES(p_ctx, frame_size);
+ continue;
+ }
+
+ if ((status = id3_read_id3v2_frame(p_ctx, frame_id, frame_size)) != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "skipping unsupported frame");
+ SKIP_BYTES(p_ctx, frame_size);
+ }
+
+ size += frame_size;
+ }
+
+ /* Try to skip to end of tag in case we bailed out early */
+ if (size < tag_size) SKIP_BYTES(p_ctx, tag_size - size);
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T id3_read_id3v1_tag( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint8_t track, genre;
+ char track_num[4] = {0};
+
+ SKIP_STRING(p_ctx, 3, "ID3v1 identifier");
+ /* ID3v1 title */
+ id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_TITLE, 30);
+ /* ID3v1 artist */
+ id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_ARTIST, 30);
+ /* ID3v1 album */
+ id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_ALBUM, 30);
+ /* ID3v1 year */
+ id3_read_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_YEAR, 4);
+ SKIP_STRING(p_ctx, 28, "ID3v1 comment");
+ if (READ_U8(p_ctx, "ID3v1 zero-byte") == 0)
+ {
+ track = READ_U8(p_ctx, "ID3v1 track");
+ snprintf(track_num, sizeof(track_num) - 1, "%02d", track);
+ id3_add_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_TRACK, track_num);
+ }
+ else
+ {
+ SKIP_BYTES(p_ctx, 1);
+ }
+ genre = READ_U8(p_ctx, "ID3v1 genre");
+ if (genre < countof(id3_genres))
+ {
+ id3_add_metadata_entry(p_ctx, VC_CONTAINER_METADATA_KEY_GENRE, id3_genres[genre]);
+ }
+
+ status = STREAM_STATUS(p_ctx);
+
+ return status;
+}
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+ *****************************************************************************/
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T id3_metadata_reader_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T id3_metadata_reader_open( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ uint8_t peek_buf[10];
+ int64_t data_offset;
+
+ if (PEEK_BYTES(p_ctx, peek_buf, 10) != 10)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Initial ID3v2 tag(s), variable size */
+ while ((peek_buf[0] == 'I') && (peek_buf[1] == 'D') && (peek_buf[2] == '3'))
+ {
+ if ((status = id3_read_id3v2_tag(p_ctx)) != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "error reading ID3v2 tag (%i)", status);
+ }
+
+ if (PEEK_BYTES(p_ctx, peek_buf, 10) != 10) break;
+ }
+
+ data_offset = STREAM_POSITION(p_ctx);
+
+ /* ID3v1 tag, 128 bytes at the end of a file */
+ if (p_ctx->priv->io->size >= INT64_C(128) && STREAM_SEEKABLE(p_ctx))
+ {
+ SEEK(p_ctx, p_ctx->priv->io->size - INT64_C(128));
+ if (PEEK_BYTES(p_ctx, peek_buf, 3) != 3) goto end;
+
+ if ((peek_buf[0] == 'T') && (peek_buf[1] == 'A') && (peek_buf[2] == 'G'))
+ {
+ if ((status = id3_read_id3v1_tag(p_ctx)) != VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "error reading ID3v1 tag (%i)", status);
+ }
+ }
+ }
+
+end:
+ /* Restore position to start of data */
+ if (STREAM_POSITION(p_ctx) != data_offset)
+ SEEK(p_ctx, data_offset);
+
+ p_ctx->priv->pf_close = id3_metadata_reader_close;
+
+ if((status = STREAM_STATUS(p_ctx)) != VC_CONTAINER_SUCCESS) goto error;
+
+ return VC_CONTAINER_SUCCESS;
+
+error:
+ LOG_DEBUG(p_ctx, "error opening stream (%i)", status);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open id3_metadata_reader_open
+#endif
diff --git a/gfx/include/userland/containers/metadata/id3/id3_metadata_strings.h b/gfx/include/userland/containers/metadata/id3/id3_metadata_strings.h
new file mode 100644
index 0000000000..1c953d97d9
--- /dev/null
+++ b/gfx/include/userland/containers/metadata/id3/id3_metadata_strings.h
@@ -0,0 +1,179 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* ID3 genre byte translation table */
+static const char* id3_genres[] =
+{
+ "Blues",
+ "Classic Rock",
+ "Country",
+ "Dance",
+ "Disco",
+ "Funk",
+ "Grunge",
+ "Hip-Hop",
+ "Jazz",
+ "Metal",
+ "New Age",
+ "Oldies",
+ "Other",
+ "Pop",
+ "R&B",
+ "Rap",
+ "Reggae",
+ "Rock",
+ "Techno",
+ "Industrial",
+ "Alternative",
+ "Ska",
+ "Death Metal",
+ "Pranks",
+ "Soundtrack",
+ "Euro-Techno",
+ "Ambient",
+ "Trip-Hop",
+ "Vocal",
+ "Jazz+Funk",
+ "Fusion",
+ "Trance",
+ "Classical",
+ "Instrumental",
+ "Acid",
+ "House",
+ "Game",
+ "Sound Clip",
+ "Gospel",
+ "Noise",
+ "Alternative Rock",
+ "Bass",
+ "Soul",
+ "Punk",
+ "Space",
+ "Meditative",
+ "Instrumental Pop",
+ "Instrumental Rock",
+ "Ethnic",
+ "Gothic",
+ "Darkwave",
+ "Techno-Industrial",
+ "Electronic",
+ "Pop-Folk",
+ "Eurodance",
+ "Dream",
+ "Southern Rock",
+ "Comedy",
+ "Cult",
+ "Gangsta",
+ "Top 40",
+ "Christian Rap",
+ "Pop/Funk",
+ "Jungle",
+ "Native American",
+ "Cabaret",
+ "New Wave",
+ "Psychadelic",
+ "Rave",
+ "Showtunes",
+ "Trailer",
+ "Lo-Fi",
+ "Tribal",
+ "Acid Punk",
+ "Acid Jazz",
+ "Polka",
+ "Retro",
+ "Musical",
+ "Rock & Roll",
+ "Hard Rock",
+ "Folk",
+ "Folk-Rock",
+ "National Folk",
+ "Swing",
+ "Fast Fusion",
+ "Bebob",
+ "Latin",
+ "Revival",
+ "Celtic",
+ "Bluegrass",
+ "Avantgarde",
+ "Gothic Rock",
+ "Progressive Rock",
+ "Psychedelic Rock",
+ "Symphonic Rock",
+ "Slow Rock",
+ "Big Band",
+ "Chorus",
+ "Easy Listening",
+ "Acoustic",
+ "Humour",
+ "Speech",
+ "Chanson",
+ "Opera",
+ "Chamber Music",
+ "Sonata",
+ "Symphony",
+ "Booty Bass",
+ "Primus",
+ "Porn Groove",
+ "Satire",
+ "Slow Jam",
+ "Club",
+ "Tango",
+ "Samba",
+ "Folklore",
+ "Ballad",
+ "Power Ballad",
+ "Rhythmic Soul",
+ "Freestyle",
+ "Duet",
+ "Punk Rock",
+ "Drum Solo",
+ "A capella",
+ "Euro-House",
+ "Dance Hall",
+ "Goa",
+ "Drum & Bass",
+ "Club-House",
+ "Hardcore",
+ "Terror",
+ "Indie",
+ "BritPop",
+ "Negerpunk",
+ "Polsk Punk",
+ "Beat",
+ "Christian Gangsta Rap",
+ "Heavy Metal",
+ "Black Metal",
+ "Crossover",
+ "Contemporary Christian",
+ "Christian Rock",
+ "Merengue",
+ "Salsa",
+ "Thrash Metal",
+ "Anime",
+ "JPop",
+ "SynthPop"
+};
diff --git a/gfx/include/userland/containers/mkv/CMakeLists.txt b/gfx/include/userland/containers/mkv/CMakeLists.txt
new file mode 100644
index 0000000000..0c31a30c04
--- /dev/null
+++ b/gfx/include/userland/containers/mkv/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(reader_mkv ${LIBRARY_TYPE} matroska_reader.c)
+
+target_link_libraries(reader_mkv containers)
+
+install(TARGETS reader_mkv DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/mkv/matroska_reader.c b/gfx/include/userland/containers/mkv/matroska_reader.c
new file mode 100644
index 0000000000..8625d0ce01
--- /dev/null
+++ b/gfx/include/userland/containers/mkv/matroska_reader.c
@@ -0,0 +1,2323 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+//#define ENABLE_MKV_EXTRA_LOGGING
+#define CONTAINER_IS_BIG_ENDIAN
+#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->element_level
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+#define MKV_TRACKS_MAX 16
+#define MKV_CODECID_MAX 32
+#define MKV_MAX_LACING_NUM 64
+
+#define MKV_MAX_ENCODINGS 1
+#define MKV_MAX_ENCODING_DATA 256
+
+#define MKV_MAX_ELEMENT_LEVEL 8
+#define MKV_MAX_CONSECUTIVE_UNKNOWN_ELEMENTS 5
+#define MKV_MAX_ELEMENT_SIZE (1<<29) /* Does not apply to the data element */
+#define MKV_MAX_STRING_SIZE 256
+#define MKV_ELEMENT_MIN_HEADER_SIZE 2
+
+#define MKV_MAX_READER_STATE_LEVEL 4
+
+#define MKV_SKIP_U8(ctx,n) (size -= 1, SKIP_U8(ctx,n))
+#define MKV_SKIP_U16(ctx,n) (size -= 2, SKIP_U16(ctx,n))
+#define MKV_SKIP_U24(ctx,n) (size -= 3, SKIP_U24(ctx,n))
+#define MKV_SKIP_U32(ctx,n) (size -= 4, SKIP_U32(ctx,n))
+#define MKV_SKIP_U64(ctx,n) (size -= 8, SKIP_U64(ctx,n))
+#define MKV_READ_U8(ctx,n) (size -= 1, READ_U8(ctx,n))
+#define MKV_READ_U16(ctx,n) (size -= 2, READ_U16(ctx,n))
+#define MKV_READ_U24(ctx,n) (size -= 3, READ_U24(ctx,n))
+#define MKV_READ_U32(ctx,n) (size -= 4, READ_U32(ctx,n))
+#define MKV_READ_U64(ctx,n) (size -= 8, READ_U64(ctx,n))
+#define MKV_READ_BYTES(ctx,buffer,sz) (size -= sz, READ_BYTES(ctx,buffer,sz))
+#define MKV_SKIP_BYTES(ctx,sz) (size -= sz, SKIP_BYTES(ctx,sz))
+
+#define CHECK_POINT(a) do { \
+ /*if(size < 0 && size != INT64_C(-1)) return VC_CONTAINER_ERROR_CORRUPTED;*/ \
+ if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx); } while(0)
+
+static uint32_t mkv_io_read_id(VC_CONTAINER_IO_T *io, int64_t *size)
+{
+ uint32_t value, mask;
+
+ value = vc_container_io_read_uint8(io); (*size)--;
+ for(mask = 0x80; mask; mask <<= 7)
+ {
+ if(value & mask) return value;
+ value = (value << 8) | vc_container_io_read_uint8(io); (*size)--;
+ }
+ return 0;
+}
+
+static int64_t mkv_io_read_uint(VC_CONTAINER_IO_T *io, int64_t *size)
+{
+ uint64_t value, mask;
+
+ value = vc_container_io_read_uint8(io); (*size)--;
+ if(value == 0xFF) return -1;
+
+ for(mask = 0x80; mask; mask <<= 7)
+ {
+ if(value & mask) return value & ~mask;
+ value = (value << 8) | vc_container_io_read_uint8(io); (*size)--;
+ }
+ return 0;
+}
+
+static int64_t mkv_io_read_sint(VC_CONTAINER_IO_T *io, int64_t *size)
+{
+ int64_t value, count = io->offset;
+ value = mkv_io_read_uint(io, size);
+ count = io->offset - count;
+
+ switch(count)
+ {
+ case 1: value -= 0x3F; break;
+ case 2: value -= 0x1FFF; break;
+ case 3: value -= 0xFFFFF; break;
+ case 4: value -= 0x7FFFFFF; break;
+ default: break;
+ }
+ return value;
+}
+
+#define MKV_READ_ID(ctx, n) mkv_io_read_id((ctx)->priv->io, &size)
+#define MKV_READ_UINT(ctx, n) mkv_io_read_uint((ctx)->priv->io, &size)
+#define MKV_READ_SINT(ctx, n) mkv_io_read_sint((ctx)->priv->io, &size)
+
+/******************************************************************************
+Type definitions.
+******************************************************************************/
+
+typedef enum
+{
+ MKV_ELEMENT_ID_UNKNOWN = 0,
+
+ /* EBML Basics */
+ MKV_ELEMENT_ID_EBML = 0x1A45DFA3,
+ MKV_ELEMENT_ID_EBML_VERSION = 0x4286,
+ MKV_ELEMENT_ID_EBML_READ_VERSION = 0x42F7,
+ MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH = 0x42F2,
+ MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH = 0x42F3,
+ MKV_ELEMENT_ID_DOCTYPE = 0x4282,
+ MKV_ELEMENT_ID_DOCTYPE_VERSION = 0x4287,
+ MKV_ELEMENT_ID_DOCTYPE_READ_VERSION = 0x4285,
+
+ /* Global Elements */
+ MKV_ELEMENT_ID_CRC32 = 0xBF,
+ MKV_ELEMENT_ID_VOID = 0xEC,
+
+ /* Segment */
+ MKV_ELEMENT_ID_SEGMENT = 0x18538067,
+
+ /* Meta Seek Information */
+ MKV_ELEMENT_ID_SEEK_HEAD = 0x114D9B74,
+ MKV_ELEMENT_ID_SEEK = 0x4DBB,
+ MKV_ELEMENT_ID_SEEK_ID = 0x53AB,
+ MKV_ELEMENT_ID_SEEK_POSITION = 0x53AC,
+
+ /* Segment Information */
+ MKV_ELEMENT_ID_INFO = 0x1549A966,
+ MKV_ELEMENT_ID_SEGMENT_UID = 0x73A4,
+ MKV_ELEMENT_ID_SEGMENT_FILENAME = 0x7384,
+ MKV_ELEMENT_ID_PREV_UID = 0x3CB923,
+ MKV_ELEMENT_ID_PREV_FILENAME = 0x3C83AB,
+ MKV_ELEMENT_ID_NEXT_UID = 0x3EB923,
+ MKV_ELEMENT_ID_NEXT_FILENAME = 0x3E83BB,
+ MKV_ELEMENT_ID_SEGMENT_FAMILY = 0x4444,
+ MKV_ELEMENT_ID_CHAPTER_TRANSLATE = 0x6924,
+ MKV_ELEMENT_ID_CHAPTER_TRANSLATE_EDITION_UID = 0x69FC,
+ MKV_ELEMENT_ID_CHAPTER_TRANSLATE_CODEC = 0x69BF,
+ MKV_ELEMENT_ID_CHAPTER_TRANSLATE_ID = 0x69A5,
+ MKV_ELEMENT_ID_TIMECODE_SCALE = 0x2AD7B1,
+ MKV_ELEMENT_ID_DURATION = 0x4489,
+ MKV_ELEMENT_ID_DATE_UTC = 0x4461,
+ MKV_ELEMENT_ID_TITLE = 0x7BA9,
+ MKV_ELEMENT_ID_MUXING_APP = 0x4D80,
+ MKV_ELEMENT_ID_WRITING_APP = 0x5741,
+
+ /* Cluster */
+ MKV_ELEMENT_ID_CLUSTER = 0x1F43B675,
+ MKV_ELEMENT_ID_TIMECODE = 0xE7,
+ MKV_ELEMENT_ID_SILENT_TRACKS = 0x5854,
+ MKV_ELEMENT_ID_SILENT_TRACK_NUMBER = 0x58D7,
+ MKV_ELEMENT_ID_POSITION = 0xA7,
+ MKV_ELEMENT_ID_PREV_SIZE = 0xAB,
+ MKV_ELEMENT_ID_BLOCKGROUP = 0xA0,
+ MKV_ELEMENT_ID_BLOCK = 0xA1,
+ MKV_ELEMENT_ID_BLOCK_ADDITIONS = 0x75A1,
+ MKV_ELEMENT_ID_BLOCK_MORE = 0xA6,
+ MKV_ELEMENT_ID_BLOCK_ADD_ID = 0xEE,
+ MKV_ELEMENT_ID_BLOCK_ADDITIONAL = 0xA5,
+ MKV_ELEMENT_ID_BLOCK_DURATION = 0x9B,
+ MKV_ELEMENT_ID_REFERENCE_PRIORITY = 0xFA,
+ MKV_ELEMENT_ID_REFERENCE_BLOCK = 0xFB,
+ MKV_ELEMENT_ID_CODEC_STATE = 0xA4,
+ MKV_ELEMENT_ID_SLICES = 0x8E,
+ MKV_ELEMENT_ID_TIME_SLICE = 0xE8,
+ MKV_ELEMENT_ID_LACE_NUMBER = 0xCC,
+ MKV_ELEMENT_ID_SIMPLE_BLOCK = 0xA3,
+
+ /* Track */
+ MKV_ELEMENT_ID_TRACKS = 0x1654AE6B,
+ MKV_ELEMENT_ID_TRACK_ENTRY = 0xAE,
+ MKV_ELEMENT_ID_TRACK_NUMBER = 0xD7,
+ MKV_ELEMENT_ID_TRACK_UID = 0x73C5,
+ MKV_ELEMENT_ID_TRACK_TYPE = 0x83,
+ MKV_ELEMENT_ID_FLAG_ENABLED = 0xB9,
+ MKV_ELEMENT_ID_FLAG_DEFAULT = 0x88,
+ MKV_ELEMENT_ID_FLAG_FORCED = 0x55AA,
+ MKV_ELEMENT_ID_FLAG_LACING = 0x9C,
+ MKV_ELEMENT_ID_MIN_CACHE = 0x6DE7,
+ MKV_ELEMENT_ID_MAX_CACHE = 0x6DF8,
+ MKV_ELEMENT_ID_DEFAULT_DURATION = 0x23E383,
+ MKV_ELEMENT_ID_TRACK_TIMECODE_SCALE = 0x23314F,
+ MKV_ELEMENT_ID_MAX_BLOCK_ADDITION_ID = 0x55EE,
+ MKV_ELEMENT_ID_NAME = 0x536E,
+ MKV_ELEMENT_ID_LANGUAGE = 0x22B59C,
+ MKV_ELEMENT_ID_TRACK_CODEC_ID = 0x86,
+ MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE = 0x63A2,
+ MKV_ELEMENT_ID_TRACK_CODEC_NAME = 0x258688,
+ MKV_ELEMENT_ID_ATTACHMENT_LINK = 0x7446,
+ MKV_ELEMENT_ID_CODEC_DECODE_ALL = 0xAA,
+ MKV_ELEMENT_ID_TRACK_OVERLAY = 0x6FAB,
+ MKV_ELEMENT_ID_TRACK_TRANSLATE = 0x6624,
+ MKV_ELEMENT_ID_TRACK_TRANSLATE_EDITION_UID = 0x66FC,
+ MKV_ELEMENT_ID_TRACK_TRANSLATE_CODEC = 0x66BF,
+ MKV_ELEMENT_ID_TRACK_TRANSLATE_TRACK_ID = 0x66A5,
+
+ /* Video */
+ MKV_ELEMENT_ID_VIDEO = 0xE0,
+ MKV_ELEMENT_ID_FLAG_INTERLACED = 0x9A,
+ MKV_ELEMENT_ID_STEREO_MODE = 0x53B8,
+ MKV_ELEMENT_ID_PIXEL_WIDTH = 0xB0,
+ MKV_ELEMENT_ID_PIXEL_HEIGHT = 0xBA,
+ MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM = 0x54AA,
+ MKV_ELEMENT_ID_PIXEL_CROP_TOP = 0x54BB,
+ MKV_ELEMENT_ID_PIXEL_CROP_LEFT = 0x54CC,
+ MKV_ELEMENT_ID_PIXEL_CROP_RIGHT = 0x54DD,
+ MKV_ELEMENT_ID_DISPLAY_WIDTH = 0x54B0,
+ MKV_ELEMENT_ID_DISPLAY_HEIGHT = 0x54BA,
+ MKV_ELEMENT_ID_DISPLAY_UNIT = 0x54B2,
+ MKV_ELEMENT_ID_ASPECT_RATIO_TYPE = 0x54B3,
+ MKV_ELEMENT_ID_COLOUR_SPACE = 0x2EB524,
+ MKV_ELEMENT_ID_FRAME_RATE = 0x2383E3,
+
+ /* Audio */
+ MKV_ELEMENT_ID_AUDIO = 0xE1,
+ MKV_ELEMENT_ID_SAMPLING_FREQUENCY = 0xB5,
+ MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY = 0x78B5,
+ MKV_ELEMENT_ID_CHANNELS = 0x9F,
+ MKV_ELEMENT_ID_BIT_DEPTH = 0x6264,
+
+ /* Content Encoding */
+ MKV_ELEMENT_ID_CONTENT_ENCODINGS = 0x6D80,
+ MKV_ELEMENT_ID_CONTENT_ENCODING = 0x6240,
+ MKV_ELEMENT_ID_CONTENT_ENCODING_ORDER = 0x5031,
+ MKV_ELEMENT_ID_CONTENT_ENCODING_SCOPE = 0x5032,
+ MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE = 0x5033,
+ MKV_ELEMENT_ID_CONTENT_COMPRESSION = 0x5034,
+ MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO = 0x4254,
+ MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS = 0x4255,
+ MKV_ELEMENT_ID_CONTENT_ENCRYPTION = 0x5035,
+ MKV_ELEMENT_ID_CONTENT_ENCRYPTION_ALGO = 0x47E1,
+ MKV_ELEMENT_ID_CONTENT_ENCRYPTION_KEY_ID = 0x47E2,
+ MKV_ELEMENT_ID_CONTENT_SIGNATURE = 0x47E3,
+ MKV_ELEMENT_ID_CONTENT_SIGNATURE_KEY_ID = 0x47E4,
+ MKV_ELEMENT_ID_CONTENT_SIGNATURE_ALGO = 0x47E5,
+ MKV_ELEMENT_ID_CONTENT_SIGNATURE_HASH_ALGO = 0x47E6,
+
+ /* Cueing Data */
+ MKV_ELEMENT_ID_CUES = 0x1C53BB6B,
+ MKV_ELEMENT_ID_CUE_POINT = 0xBB,
+ MKV_ELEMENT_ID_CUE_TIME = 0xB3,
+ MKV_ELEMENT_ID_CUE_TRACK_POSITIONS = 0xB7,
+ MKV_ELEMENT_ID_CUE_TRACK = 0xF7,
+ MKV_ELEMENT_ID_CUE_CLUSTER_POSITION = 0xF1,
+ MKV_ELEMENT_ID_CUE_BLOCK_NUMBER = 0x5378,
+
+ /* Attachments */
+ MKV_ELEMENT_ID_ATTACHMENTS = 0x1941A469,
+
+ /* Chapters */
+ MKV_ELEMENT_ID_CHAPTERS = 0x1043A770,
+
+ /* Tagging */
+ MKV_ELEMENT_ID_TAGS = 0x1254C367,
+ MKV_ELEMENT_ID_TAG = 0x7373,
+ MKV_ELEMENT_ID_TAG_TARGETS = 0x63C0,
+ MKV_ELEMENT_ID_TAG_TARGET_TYPE_VALUE = 0x68CA,
+ MKV_ELEMENT_ID_TAG_TARGET_TYPE = 0x63CA,
+ MKV_ELEMENT_ID_TAG_TRACK_UID = 0x63C5,
+ MKV_ELEMENT_ID_TAG_EDITION_UID = 0x63C9,
+ MKV_ELEMENT_ID_TAG_CHAPTER_UID = 0x63C4,
+ MKV_ELEMENT_ID_TAG_ATTACHMENT_UID = 0x63C6,
+ MKV_ELEMENT_ID_TAG_SIMPLE_TAG = 0x67C8,
+ MKV_ELEMENT_ID_TAG_NAME = 0x45A3,
+ MKV_ELEMENT_ID_TAG_LANGUAGE = 0x447A,
+ MKV_ELEMENT_ID_TAG_DEFAULT = 0x4484,
+ MKV_ELEMENT_ID_TAG_STRING = 0x4487,
+ MKV_ELEMENT_ID_TAG_BINARY = 0x4485,
+
+ MKV_ELEMENT_ID_INVALID = 0xFFFFFFFF
+} MKV_ELEMENT_ID_T;
+
+/** Context for our reader
+ */
+
+typedef struct
+{
+ unsigned int track;
+ unsigned int flags;
+ int64_t pts;
+ int64_t cluster_timecode;
+ int64_t prev_cluster_size; /* Size of the previous cluster if available */
+ int64_t frame_duration;
+
+ int level;
+ struct {
+ int64_t offset;
+ int64_t data_start;
+ int64_t data_offset;
+ int64_t size;
+ MKV_ELEMENT_ID_T id;
+ } levels[MKV_MAX_READER_STATE_LEVEL];
+
+ bool eos;
+ bool corrupted;
+ bool seen_ref_block;
+
+ uint32_t lacing_num_frames;
+ uint32_t lacing_size;
+ uint16_t lacing_sizes[MKV_MAX_LACING_NUM];
+ uint32_t lacing_current_size;
+
+ /* For header stripping compression */
+ uint32_t header_size;
+ uint8_t *header_data;
+ uint32_t header_size_backup;
+} MKV_READER_STATE_T;
+
+typedef struct
+{
+ const MKV_ELEMENT_ID_T id;
+ const MKV_ELEMENT_ID_T parent_id;
+ const char *psz_name;
+ VC_CONTAINER_STATUS_T (*pf_func)(VC_CONTAINER_T *, MKV_ELEMENT_ID_T, int64_t);
+
+} MKV_ELEMENT_T;
+
+typedef struct VC_CONTAINER_TRACK_MODULE_T
+{
+ MKV_READER_STATE_T *state;
+ MKV_READER_STATE_T track_state;
+
+ /* Information extracted from the track entry */
+ uint32_t number;
+ uint32_t type;
+ int64_t timecode_scale;
+ uint32_t duration;
+ int64_t frame_duration;
+ char codecid[MKV_CODECID_MAX];
+
+ union {
+ /* video specific */
+ struct {
+ unsigned int interlaced:1;
+ unsigned int stereo_mode:2;
+ uint32_t pixel_width;
+ uint32_t pixel_height;
+ uint32_t pixel_crop_bottom;
+ uint32_t pixel_crop_top;
+ uint32_t pixel_crop_left;
+ uint32_t pixel_crop_right;
+ uint32_t display_width;
+ uint32_t display_height;
+ uint32_t display_unit;
+ uint32_t aspect_ratio_type;
+ float frame_rate;
+ } video;
+
+ /* audio specific */
+ struct {
+ uint32_t sampling_frequency;
+ uint32_t output_sampling_frequency;
+ uint32_t channels;
+ uint32_t bit_depth;
+ } audio;
+ } es_type;
+
+ /* content encoding (i.e. lossless compression and encryption) */
+ unsigned int encodings_num;
+ struct {
+ enum {
+ MKV_CONTENT_ENCODING_COMPRESSION_ZLIB,
+ MKV_CONTENT_ENCODING_COMPRESSION_HEADER,
+ MKV_CONTENT_ENCODING_ENCRYPTION,
+ MKV_CONTENT_ENCODING_UNKNOWN
+ } type;
+ unsigned int data_size;
+ uint8_t *data;
+ } encodings[MKV_MAX_ENCODINGS];
+
+} VC_CONTAINER_TRACK_MODULE_T;
+
+typedef struct VC_CONTAINER_MODULE_T
+{
+ MKV_ELEMENT_T *elements_list;
+ int element_level;
+ MKV_ELEMENT_ID_T parent_id;
+
+ uint64_t element_offset; /**< Offset to the start of the current element */
+
+ uint64_t segment_offset; /**< Offset to the start of the data packets */
+ int64_t segment_size;
+
+ int tracks_num;
+ VC_CONTAINER_TRACK_T *tracks[MKV_TRACKS_MAX];
+
+ MKV_READER_STATE_T state;
+ int64_t timecode_scale;
+ float duration;
+
+ uint64_t cluster_offset; /**< Offset to the first cluster */
+ uint64_t cues_offset; /**< Offset to the start of the seeking cues */
+ uint64_t tags_offset; /**< Offset to the start of the tags */
+
+ /*
+ * Variables only used during parsing of the header
+ */
+
+ VC_CONTAINER_TRACK_T *parsing; /**< Current track being parsed */
+ bool is_doctype_valid;
+
+ MKV_ELEMENT_ID_T seekhead_elem_id;
+ int64_t seekhead_elem_offset;
+
+ /* Cues */
+ unsigned int cue_track;
+ int64_t cue_timecode;
+ uint64_t cue_cluster_offset;
+ unsigned int cue_block;
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T mkv_reader_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Prototypes for local functions
+******************************************************************************/
+static VC_CONTAINER_STATUS_T mkv_read_element( VC_CONTAINER_T *p_ctx, int64_t size, MKV_ELEMENT_ID_T parent_id );
+static VC_CONTAINER_STATUS_T mkv_read_elements( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_element_data_uint( VC_CONTAINER_T *p_ctx, int64_t size, uint64_t *value );
+static VC_CONTAINER_STATUS_T mkv_read_element_data_float( VC_CONTAINER_T *p_ctx, int64_t size, double *value );
+static VC_CONTAINER_STATUS_T mkv_read_element_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_subelements_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_element_segment( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_element_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_subelements_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_subelements_video( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_subelements_audio( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_subelements_info( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_element_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_subelements_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_subelements_compression( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_subelements_seek_head( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_element_cues( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+static VC_CONTAINER_STATUS_T mkv_read_subelements_cue_point( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+
+static VC_CONTAINER_STATUS_T mkv_read_subelements_cluster( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size );
+
+/******************************************************************************
+List of element IDs and their associated processing functions
+******************************************************************************/
+MKV_ELEMENT_T mkv_elements_list[] =
+{
+ /* EBML Basics */
+ {MKV_ELEMENT_ID_EBML, MKV_ELEMENT_ID_UNKNOWN, "EBML", mkv_read_element_ebml},
+ {MKV_ELEMENT_ID_EBML_VERSION, MKV_ELEMENT_ID_EBML, "EBMLVersion", mkv_read_subelements_ebml},
+ {MKV_ELEMENT_ID_EBML_READ_VERSION, MKV_ELEMENT_ID_EBML, "EBMLReadVersion", mkv_read_subelements_ebml},
+ {MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH, MKV_ELEMENT_ID_EBML, "EBMLMaxIDLength", mkv_read_subelements_ebml},
+ {MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH, MKV_ELEMENT_ID_EBML, "EBMLMaxSizeLength", mkv_read_subelements_ebml},
+ {MKV_ELEMENT_ID_DOCTYPE, MKV_ELEMENT_ID_EBML, "DocType", mkv_read_subelements_ebml},
+ {MKV_ELEMENT_ID_DOCTYPE_VERSION, MKV_ELEMENT_ID_EBML, "DocTypeVersion", mkv_read_subelements_ebml},
+ {MKV_ELEMENT_ID_DOCTYPE_READ_VERSION, MKV_ELEMENT_ID_EBML, "DocTypeReadVersion", mkv_read_subelements_ebml},
+
+ /* Global Elements */
+ {MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0},
+ {MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0},
+
+ /* Segment */
+ {MKV_ELEMENT_ID_SEGMENT, MKV_ELEMENT_ID_UNKNOWN, "Segment", mkv_read_element_segment},
+
+ /* Meta Seek Information */
+ {MKV_ELEMENT_ID_SEEK_HEAD, MKV_ELEMENT_ID_SEGMENT, "SeekHead", mkv_read_elements},
+ {MKV_ELEMENT_ID_SEEK, MKV_ELEMENT_ID_SEEK_HEAD, "Seek", mkv_read_subelements_seek_head},
+ {MKV_ELEMENT_ID_SEEK_ID, MKV_ELEMENT_ID_SEEK, "SeekID", mkv_read_subelements_seek_head},
+ {MKV_ELEMENT_ID_SEEK_POSITION, MKV_ELEMENT_ID_SEEK, "SeekPosition", mkv_read_subelements_seek_head},
+
+ /* Segment Information */
+ {MKV_ELEMENT_ID_INFO, MKV_ELEMENT_ID_SEGMENT, "Info", mkv_read_elements},
+ {MKV_ELEMENT_ID_SEGMENT_UID, MKV_ELEMENT_ID_INFO, "SegmentUID", 0},
+ {MKV_ELEMENT_ID_SEGMENT_FILENAME, MKV_ELEMENT_ID_INFO, "SegmentFilename", 0},
+ {MKV_ELEMENT_ID_PREV_UID, MKV_ELEMENT_ID_INFO, "PrevUID", 0},
+ {MKV_ELEMENT_ID_PREV_FILENAME, MKV_ELEMENT_ID_INFO, "PrevFilename", 0},
+ {MKV_ELEMENT_ID_NEXT_UID, MKV_ELEMENT_ID_INFO, "NextUID", 0},
+ {MKV_ELEMENT_ID_NEXT_FILENAME, MKV_ELEMENT_ID_INFO, "NextFilename", 0},
+ {MKV_ELEMENT_ID_SEGMENT_FAMILY, MKV_ELEMENT_ID_INFO, "SegmentFamily", 0},
+ {MKV_ELEMENT_ID_CHAPTER_TRANSLATE, MKV_ELEMENT_ID_INFO, "ChapterTranslate", 0},
+ {MKV_ELEMENT_ID_CHAPTER_TRANSLATE_EDITION_UID, MKV_ELEMENT_ID_INFO, "ChapterTranslateEditionUID", 0},
+ {MKV_ELEMENT_ID_CHAPTER_TRANSLATE_CODEC, MKV_ELEMENT_ID_INFO, "ChapterTranslateCodec", 0},
+ {MKV_ELEMENT_ID_CHAPTER_TRANSLATE_ID, MKV_ELEMENT_ID_INFO, "ChapterTranslateID", 0},
+ {MKV_ELEMENT_ID_TIMECODE_SCALE, MKV_ELEMENT_ID_INFO, "TimecodeScale", mkv_read_subelements_info},
+ {MKV_ELEMENT_ID_DURATION, MKV_ELEMENT_ID_INFO, "Duration", mkv_read_subelements_info},
+ {MKV_ELEMENT_ID_DATE_UTC, MKV_ELEMENT_ID_INFO, "DateUTC", 0},
+ {MKV_ELEMENT_ID_TITLE, MKV_ELEMENT_ID_INFO, "Title", mkv_read_subelements_info},
+ {MKV_ELEMENT_ID_MUXING_APP, MKV_ELEMENT_ID_INFO, "MuxingApp", mkv_read_subelements_info},
+ {MKV_ELEMENT_ID_WRITING_APP, MKV_ELEMENT_ID_INFO, "WritingApp", mkv_read_subelements_info},
+
+ /* Cluster */
+ {MKV_ELEMENT_ID_CLUSTER, MKV_ELEMENT_ID_SEGMENT, "Cluster", 0},
+ {MKV_ELEMENT_ID_TIMECODE, MKV_ELEMENT_ID_CLUSTER, "Timecode", 0},
+ {MKV_ELEMENT_ID_SILENT_TRACKS, MKV_ELEMENT_ID_CLUSTER, "SilentTracks", 0},
+ {MKV_ELEMENT_ID_SILENT_TRACK_NUMBER, MKV_ELEMENT_ID_CLUSTER, "SilentTrackNumber", 0},
+ {MKV_ELEMENT_ID_POSITION, MKV_ELEMENT_ID_CLUSTER, "Position", 0},
+ {MKV_ELEMENT_ID_PREV_SIZE, MKV_ELEMENT_ID_CLUSTER, "PrevSize", 0},
+ {MKV_ELEMENT_ID_BLOCKGROUP, MKV_ELEMENT_ID_CLUSTER, "BlockGroup", 0},
+ {MKV_ELEMENT_ID_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "Block", 0},
+ {MKV_ELEMENT_ID_BLOCK_ADDITIONS, MKV_ELEMENT_ID_BLOCKGROUP, "BlockAdditions", 0},
+ {MKV_ELEMENT_ID_BLOCK_MORE, MKV_ELEMENT_ID_BLOCK_ADDITIONS, "BlockMore", 0},
+ {MKV_ELEMENT_ID_BLOCK_ADD_ID, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAddId", 0},
+ {MKV_ELEMENT_ID_BLOCK_ADDITIONAL, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAdditional", 0},
+ {MKV_ELEMENT_ID_BLOCK_DURATION, MKV_ELEMENT_ID_BLOCKGROUP, "BlockDuration", 0},
+ {MKV_ELEMENT_ID_REFERENCE_PRIORITY, MKV_ELEMENT_ID_BLOCKGROUP, "ReferencePriority", 0},
+ {MKV_ELEMENT_ID_REFERENCE_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "ReferenceBlock", 0},
+ {MKV_ELEMENT_ID_CODEC_STATE, MKV_ELEMENT_ID_BLOCKGROUP, "CodecState", 0},
+ {MKV_ELEMENT_ID_SLICES, MKV_ELEMENT_ID_BLOCKGROUP, "Slices", 0},
+ {MKV_ELEMENT_ID_TIME_SLICE, MKV_ELEMENT_ID_SLICES, "TimeSlice", 0},
+ {MKV_ELEMENT_ID_LACE_NUMBER, MKV_ELEMENT_ID_TIME_SLICE, "LaceNumber", 0},
+ {MKV_ELEMENT_ID_SIMPLE_BLOCK, MKV_ELEMENT_ID_CLUSTER, "SimpleBlock", 0},
+
+ /* Track */
+ {MKV_ELEMENT_ID_TRACKS, MKV_ELEMENT_ID_SEGMENT, "Tracks", mkv_read_elements},
+ {MKV_ELEMENT_ID_TRACK_ENTRY, MKV_ELEMENT_ID_TRACKS, "TrackEntry", mkv_read_element_track_entry},
+ {MKV_ELEMENT_ID_TRACK_NUMBER, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackNumber", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_TRACK_UID, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackUID", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_TRACK_TYPE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackType", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_FLAG_ENABLED, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagEnabled", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_FLAG_DEFAULT, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagDefault", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_FLAG_FORCED, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagForced", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_FLAG_LACING, MKV_ELEMENT_ID_TRACK_ENTRY, "FlagLacing", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_MIN_CACHE, MKV_ELEMENT_ID_TRACK_ENTRY, "MinCache", 0},
+ {MKV_ELEMENT_ID_MAX_CACHE, MKV_ELEMENT_ID_TRACK_ENTRY, "MaxCache", 0},
+ {MKV_ELEMENT_ID_DEFAULT_DURATION, MKV_ELEMENT_ID_TRACK_ENTRY, "DefaultDuration", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_TRACK_TIMECODE_SCALE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackTimecodeScale", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_MAX_BLOCK_ADDITION_ID, MKV_ELEMENT_ID_TRACK_ENTRY, "MaxBlockAdditionID", 0},
+ {MKV_ELEMENT_ID_NAME, MKV_ELEMENT_ID_TRACK_ENTRY, "Name", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_LANGUAGE, MKV_ELEMENT_ID_TRACK_ENTRY, "Language", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_TRACK_CODEC_ID, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecID", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecPrivate", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_TRACK_CODEC_NAME, MKV_ELEMENT_ID_TRACK_ENTRY, "CodecName", mkv_read_subelements_track_entry},
+ {MKV_ELEMENT_ID_ATTACHMENT_LINK, MKV_ELEMENT_ID_TRACK_ENTRY, "AttachmentLink", 0},
+ {MKV_ELEMENT_ID_CODEC_DECODE_ALL, MKV_ELEMENT_ID_TRACK_ENTRY, "DecodeAll", 0},
+ {MKV_ELEMENT_ID_TRACK_OVERLAY, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackOverlay", 0},
+ {MKV_ELEMENT_ID_TRACK_TRANSLATE, MKV_ELEMENT_ID_TRACK_ENTRY, "TrackTranslate", 0},
+ {MKV_ELEMENT_ID_TRACK_TRANSLATE_EDITION_UID, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateEditionUID", 0},
+ {MKV_ELEMENT_ID_TRACK_TRANSLATE_CODEC, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateCodec", 0},
+ {MKV_ELEMENT_ID_TRACK_TRANSLATE_TRACK_ID, MKV_ELEMENT_ID_TRACK_TRANSLATE, "TrackTranslateTrackID", 0},
+
+ /* Video */
+ {MKV_ELEMENT_ID_VIDEO, MKV_ELEMENT_ID_TRACK_ENTRY, "Video", mkv_read_elements},
+ {MKV_ELEMENT_ID_FLAG_INTERLACED, MKV_ELEMENT_ID_VIDEO, "FlagInterlaced", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_STEREO_MODE, MKV_ELEMENT_ID_VIDEO, "StereoMode", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_PIXEL_WIDTH, MKV_ELEMENT_ID_VIDEO, "PixelWidth", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_PIXEL_HEIGHT, MKV_ELEMENT_ID_VIDEO, "PixelHeight", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM, MKV_ELEMENT_ID_VIDEO, "PixelCropBottom", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_PIXEL_CROP_TOP, MKV_ELEMENT_ID_VIDEO, "PixelCropTop", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_PIXEL_CROP_LEFT, MKV_ELEMENT_ID_VIDEO, "PixelCropLeft", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_PIXEL_CROP_RIGHT, MKV_ELEMENT_ID_VIDEO, "PixelCropRight", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_DISPLAY_WIDTH, MKV_ELEMENT_ID_VIDEO, "DisplayWidth", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_DISPLAY_HEIGHT, MKV_ELEMENT_ID_VIDEO, "DisplayHeight", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_DISPLAY_UNIT, MKV_ELEMENT_ID_VIDEO, "DisplayUnit", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_ASPECT_RATIO_TYPE, MKV_ELEMENT_ID_VIDEO, "AspectRatioType", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_COLOUR_SPACE, MKV_ELEMENT_ID_VIDEO, "ColourSpace", mkv_read_subelements_video},
+ {MKV_ELEMENT_ID_FRAME_RATE, MKV_ELEMENT_ID_VIDEO, "FrameRate", mkv_read_subelements_video},
+
+ /* Audio */
+ {MKV_ELEMENT_ID_AUDIO, MKV_ELEMENT_ID_TRACK_ENTRY, "Audio", mkv_read_elements},
+ {MKV_ELEMENT_ID_SAMPLING_FREQUENCY, MKV_ELEMENT_ID_AUDIO, "SamplingFrequency", mkv_read_subelements_audio},
+ {MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY, MKV_ELEMENT_ID_AUDIO, "OutputSamplingFrequency", mkv_read_subelements_audio},
+ {MKV_ELEMENT_ID_CHANNELS, MKV_ELEMENT_ID_AUDIO, "Channels", mkv_read_subelements_audio},
+ {MKV_ELEMENT_ID_BIT_DEPTH, MKV_ELEMENT_ID_AUDIO, "BitDepth", mkv_read_subelements_audio},
+
+ /* Content Encoding */
+ {MKV_ELEMENT_ID_CONTENT_ENCODINGS, MKV_ELEMENT_ID_TRACK_ENTRY, "ContentEncodings", mkv_read_elements},
+ {MKV_ELEMENT_ID_CONTENT_ENCODING, MKV_ELEMENT_ID_CONTENT_ENCODINGS, "ContentEncoding", mkv_read_element_encoding},
+ {MKV_ELEMENT_ID_CONTENT_ENCODING_ORDER, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingOrder", mkv_read_subelements_encoding},
+ {MKV_ELEMENT_ID_CONTENT_ENCODING_SCOPE, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingScope", mkv_read_subelements_encoding},
+ {MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncodingType", mkv_read_subelements_encoding},
+ {MKV_ELEMENT_ID_CONTENT_COMPRESSION, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentCompression", mkv_read_elements},
+ {MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO, MKV_ELEMENT_ID_CONTENT_COMPRESSION, "ContentCompAlgo", mkv_read_subelements_compression},
+ {MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS, MKV_ELEMENT_ID_CONTENT_COMPRESSION, "ContentCompSettings", mkv_read_subelements_compression},
+ {MKV_ELEMENT_ID_CONTENT_ENCRYPTION, MKV_ELEMENT_ID_CONTENT_ENCODING, "ContentEncryption", mkv_read_elements},
+ {MKV_ELEMENT_ID_CONTENT_ENCRYPTION_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentEncAlgo", 0},
+ {MKV_ELEMENT_ID_CONTENT_ENCRYPTION_KEY_ID, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentEncKeyID", 0},
+ {MKV_ELEMENT_ID_CONTENT_SIGNATURE, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSignature", 0},
+ {MKV_ELEMENT_ID_CONTENT_SIGNATURE_KEY_ID, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigKeyID", 0},
+ {MKV_ELEMENT_ID_CONTENT_SIGNATURE_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigAlgo", 0},
+ {MKV_ELEMENT_ID_CONTENT_SIGNATURE_HASH_ALGO, MKV_ELEMENT_ID_CONTENT_ENCRYPTION, "ContentSigHashAlgo", 0},
+
+ /* Cueing data */
+ {MKV_ELEMENT_ID_CUES, MKV_ELEMENT_ID_SEGMENT, "Cues", mkv_read_element_cues},
+ {MKV_ELEMENT_ID_CUE_POINT, MKV_ELEMENT_ID_CUES, "Cue Point", mkv_read_elements},
+ {MKV_ELEMENT_ID_CUE_TIME, MKV_ELEMENT_ID_CUE_POINT, "Cue Time", 0},
+ {MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, MKV_ELEMENT_ID_CUE_POINT, "Cue Track Positions", mkv_read_elements},
+ {MKV_ELEMENT_ID_CUE_TRACK, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Track", 0},
+ {MKV_ELEMENT_ID_CUE_CLUSTER_POSITION, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Cluster Position", 0},
+ {MKV_ELEMENT_ID_CUE_BLOCK_NUMBER, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Block Number", 0},
+
+ /* Attachments */
+ {MKV_ELEMENT_ID_ATTACHMENTS, MKV_ELEMENT_ID_SEGMENT, "Attachments", 0},
+
+ /* Chapters */
+ {MKV_ELEMENT_ID_CHAPTERS, MKV_ELEMENT_ID_SEGMENT, "Chapters", 0},
+
+ /* Tagging */
+ {MKV_ELEMENT_ID_TAGS, MKV_ELEMENT_ID_SEGMENT, "Tags", mkv_read_elements},
+ {MKV_ELEMENT_ID_TAG, MKV_ELEMENT_ID_TAGS, "Tag", mkv_read_elements},
+ {MKV_ELEMENT_ID_TAG_TARGETS, MKV_ELEMENT_ID_TAG, "Tag Targets", mkv_read_elements},
+ {MKV_ELEMENT_ID_TAG_TARGET_TYPE_VALUE, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Target Type Value", 0},
+ {MKV_ELEMENT_ID_TAG_TARGET_TYPE, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Target Type", 0},
+ {MKV_ELEMENT_ID_TAG_TRACK_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Track UID", 0},
+ {MKV_ELEMENT_ID_TAG_EDITION_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Edition UID", 0},
+ {MKV_ELEMENT_ID_TAG_CHAPTER_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Chapter UID", 0},
+ {MKV_ELEMENT_ID_TAG_ATTACHMENT_UID, MKV_ELEMENT_ID_TAG_TARGETS, "Tag Attachment UID", 0},
+ {MKV_ELEMENT_ID_TAG_SIMPLE_TAG, MKV_ELEMENT_ID_TAG, "Simple Tag", mkv_read_elements},
+ {MKV_ELEMENT_ID_TAG_NAME, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Name", 0},
+ {MKV_ELEMENT_ID_TAG_LANGUAGE, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Language", 0},
+ {MKV_ELEMENT_ID_TAG_DEFAULT, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Default", 0},
+ {MKV_ELEMENT_ID_TAG_STRING, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag String", 0},
+ {MKV_ELEMENT_ID_TAG_BINARY, MKV_ELEMENT_ID_TAG_SIMPLE_TAG, "Tag Binary", 0},
+
+ {MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0}
+};
+
+MKV_ELEMENT_T mkv_cluster_elements_list[] =
+{
+ /* Cluster */
+ {MKV_ELEMENT_ID_CLUSTER, MKV_ELEMENT_ID_SEGMENT, "Cluster", 0},
+ {MKV_ELEMENT_ID_TIMECODE, MKV_ELEMENT_ID_CLUSTER, "Timecode", mkv_read_subelements_cluster},
+ {MKV_ELEMENT_ID_SILENT_TRACKS, MKV_ELEMENT_ID_CLUSTER, "SilentTracks", 0},
+ {MKV_ELEMENT_ID_SILENT_TRACK_NUMBER, MKV_ELEMENT_ID_CLUSTER, "SilentTrackNumber", 0},
+ {MKV_ELEMENT_ID_POSITION, MKV_ELEMENT_ID_CLUSTER, "Position", 0},
+ {MKV_ELEMENT_ID_PREV_SIZE, MKV_ELEMENT_ID_CLUSTER, "PrevSize", 0},
+ {MKV_ELEMENT_ID_BLOCKGROUP, MKV_ELEMENT_ID_CLUSTER, "BlockGroup", 0},
+ {MKV_ELEMENT_ID_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "Block", 0},
+ {MKV_ELEMENT_ID_BLOCK_ADDITIONS, MKV_ELEMENT_ID_BLOCKGROUP, "BlockAdditions", 0},
+ {MKV_ELEMENT_ID_BLOCK_MORE, MKV_ELEMENT_ID_BLOCK_ADDITIONS, "BlockMore", 0},
+ {MKV_ELEMENT_ID_BLOCK_ADD_ID, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAddId", 0},
+ {MKV_ELEMENT_ID_BLOCK_ADDITIONAL, MKV_ELEMENT_ID_BLOCK_MORE, "BlockAdditional", 0},
+ {MKV_ELEMENT_ID_BLOCK_DURATION, MKV_ELEMENT_ID_BLOCKGROUP, "BlockDuration", mkv_read_subelements_cluster},
+ {MKV_ELEMENT_ID_REFERENCE_PRIORITY, MKV_ELEMENT_ID_BLOCKGROUP, "ReferencePriority", 0},
+ {MKV_ELEMENT_ID_REFERENCE_BLOCK, MKV_ELEMENT_ID_BLOCKGROUP, "ReferenceBlock", 0},
+ {MKV_ELEMENT_ID_CODEC_STATE, MKV_ELEMENT_ID_BLOCKGROUP, "CodecState", 0},
+ {MKV_ELEMENT_ID_SLICES, MKV_ELEMENT_ID_BLOCKGROUP, "Slices", 0},
+ {MKV_ELEMENT_ID_TIME_SLICE, MKV_ELEMENT_ID_SLICES, "TimeSlice", 0},
+ {MKV_ELEMENT_ID_LACE_NUMBER, MKV_ELEMENT_ID_TIME_SLICE, "LaceNumber", 0},
+ {MKV_ELEMENT_ID_SIMPLE_BLOCK, MKV_ELEMENT_ID_CLUSTER, "SimpleBlock", 0},
+
+ /* Global Elements */
+ {MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0},
+ {MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0},
+
+ {MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0}
+};
+
+MKV_ELEMENT_T mkv_cue_elements_list[] =
+{
+ /* Cueing data */
+ {MKV_ELEMENT_ID_CUES, MKV_ELEMENT_ID_SEGMENT, "Cues", 0},
+ {MKV_ELEMENT_ID_CUE_POINT, MKV_ELEMENT_ID_CUES, "Cue Point", mkv_read_elements},
+ {MKV_ELEMENT_ID_CUE_TIME, MKV_ELEMENT_ID_CUE_POINT, "Cue Time", mkv_read_subelements_cue_point},
+ {MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, MKV_ELEMENT_ID_CUE_POINT, "Cue Track Positions", mkv_read_elements},
+ {MKV_ELEMENT_ID_CUE_TRACK, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Track", mkv_read_subelements_cue_point},
+ {MKV_ELEMENT_ID_CUE_CLUSTER_POSITION, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Cluster Position", mkv_read_subelements_cue_point},
+ {MKV_ELEMENT_ID_CUE_BLOCK_NUMBER, MKV_ELEMENT_ID_CUE_TRACK_POSITIONS, "Cue Block Number", mkv_read_subelements_cue_point},
+
+ /* Global Elements */
+ {MKV_ELEMENT_ID_CRC32, MKV_ELEMENT_ID_INVALID, "CRC-32", 0},
+ {MKV_ELEMENT_ID_VOID, MKV_ELEMENT_ID_INVALID, "Void", 0},
+
+ {MKV_ELEMENT_ID_UNKNOWN, MKV_ELEMENT_ID_INVALID, "unknown", 0}
+};
+
+/******************************************************************************
+List of codec mapping
+******************************************************************************/
+static const struct {
+ VC_CONTAINER_FOURCC_T fourcc;
+ const char *codecid;
+ VC_CONTAINER_FOURCC_T variant;
+} codecid_to_fourcc_table[] =
+{
+ /* Video */
+ {VC_CONTAINER_CODEC_MP1V, "V_MPEG1", 0},
+ {VC_CONTAINER_CODEC_MP2V, "V_MPEG2", 0},
+ {VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/ASP", 0},
+ {VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/SP", 0},
+ {VC_CONTAINER_CODEC_MP4V, "V_MPEG4/ISO/AP", 0},
+ {VC_CONTAINER_CODEC_DIV3, "V_MPEG4/MS/V3", 0},
+ {VC_CONTAINER_CODEC_H264, "V_MPEG4/ISO/AVC", VC_CONTAINER_VARIANT_H264_AVC1},
+ {VC_CONTAINER_CODEC_MJPEG, "V_MJPEG", 0},
+ {VC_CONTAINER_CODEC_RV10, "V_REAL/RV10", 0},
+ {VC_CONTAINER_CODEC_RV20, "V_REAL/RV20", 0},
+ {VC_CONTAINER_CODEC_RV30, "V_REAL/RV30", 0},
+ {VC_CONTAINER_CODEC_RV40, "V_REAL/RV40", 0},
+ {VC_CONTAINER_CODEC_THEORA, "V_THEORA", 0},
+ {VC_CONTAINER_CODEC_DIRAC, "V_DIRAC", 0},
+ {VC_CONTAINER_CODEC_VP8, "V_VP8", 0},
+
+ /* Audio */
+ {VC_CONTAINER_CODEC_MPGA, "A_MPEG/L3", VC_CONTAINER_VARIANT_MPGA_L3},
+ {VC_CONTAINER_CODEC_MPGA, "A_MPEG/L2", VC_CONTAINER_VARIANT_MPGA_L2},
+ {VC_CONTAINER_CODEC_MPGA, "A_MPEG/L1", VC_CONTAINER_VARIANT_MPGA_L1},
+ {VC_CONTAINER_CODEC_MP4A, "A_AAC", 0},
+ {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/MAIN", 0},
+ {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/LC", 0},
+ {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/SSR", 0},
+ {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG2/LC/SBR", 0},
+ {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/MAIN", 0},
+ {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/LC", 0},
+ {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/SSR", 0},
+ {VC_CONTAINER_CODEC_MP4A, "A_AAC/MPEG4/LC/SBR", 0},
+ {VC_CONTAINER_CODEC_AC3, "A_AC3", 0},
+ {VC_CONTAINER_CODEC_EAC3, "A_EAC3", 0},
+ {VC_CONTAINER_CODEC_DTS, "A_DTS", 0},
+ {VC_CONTAINER_CODEC_MLP, "A_MLP", 0},
+ {0, "A_TRUEHD", 0},
+ {VC_CONTAINER_CODEC_VORBIS, "A_VORBIS", 0},
+ {VC_CONTAINER_CODEC_FLAC, "A_FLAC", 0},
+ {VC_CONTAINER_CODEC_PCM_SIGNED_LE, "A_PCM/INT/LIT", 0},
+ {VC_CONTAINER_CODEC_PCM_SIGNED_BE, "A_PCM/INT/BIG", 0},
+ {VC_CONTAINER_CODEC_PCM_FLOAT_LE, "A_PCM/FLOAT/IEEE", 0},
+ {0, "A_REAL/xyzt", 0},
+ {0, "A_REAL/14_4", 0},
+
+ /* Text */
+ {VC_CONTAINER_CODEC_TEXT, "S_TEXT/ASCII", 0},
+ {VC_CONTAINER_CODEC_TEXT, "S_TEXT/UTF8", 0},
+ {VC_CONTAINER_CODEC_SSA, "S_TEXT/ASS", 0},
+ {VC_CONTAINER_CODEC_SSA, "S_TEXT/SSA", 0},
+ {VC_CONTAINER_CODEC_SSA, "S_ASS", 0},
+ {VC_CONTAINER_CODEC_SSA, "S_SSA", 0},
+ {VC_CONTAINER_CODEC_USF, "S_TEXT/USF", 0},
+ {VC_CONTAINER_CODEC_VOBSUB, "S_VOBSUB", 0},
+
+ {0, 0}
+};
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+static VC_CONTAINER_FOURCC_T mkv_codecid_to_fourcc(const char *codecid,
+ VC_CONTAINER_FOURCC_T *variant)
+{
+ unsigned int i;
+ for(i = 0; codecid_to_fourcc_table[i].codecid; i++)
+ if(!strcmp(codecid_to_fourcc_table[i].codecid, codecid)) break;
+ if (variant) *variant = codecid_to_fourcc_table[i].variant;
+ return codecid_to_fourcc_table[i].fourcc;
+}
+
+#if 0
+/** Find the track associated with an MKV track number */
+static VC_CONTAINER_TRACK_T *mkv_reader_find_track( VC_CONTAINER_T *p_ctx, unsigned int mkv_track_num)
+{
+ VC_CONTAINER_TRACK_T *p_track = 0;
+ unsigned int i;
+
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ if(p_ctx->tracks[i]->priv->module->number == mkv_track_num) break;
+
+ if(i < p_ctx->tracks_num) /* We found it */
+ p_track = p_ctx->tracks[i];
+
+ return p_track;
+}
+#endif
+
+/** Base function used to read an MKV/EBML element header.
+ * This will read the element header do lots of sanity checking and return the element id
+ * and the size of the data contained in the element */
+static VC_CONTAINER_STATUS_T mkv_read_element_header(VC_CONTAINER_T *p_ctx, int64_t size,
+ MKV_ELEMENT_ID_T *id, int64_t *element_size, MKV_ELEMENT_ID_T parent_id,
+ MKV_ELEMENT_T **elem)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ MKV_ELEMENT_T *element;
+
+ module->element_offset = STREAM_POSITION(p_ctx);
+
+ *id = MKV_READ_ID(p_ctx, "Element ID");
+ CHECK_POINT(p_ctx);
+ if(!*id)
+ {
+ LOG_DEBUG(p_ctx, "invalid element id %i", *id);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ if(elem) element = *elem;
+ else element = mkv_elements_list;
+
+ /* Find out which Element we are dealing with */
+ while(element->id && *id != element->id) element++;
+
+ *element_size = MKV_READ_UINT(p_ctx, "Element Size");
+ CHECK_POINT(p_ctx);
+ LOG_FORMAT(p_ctx, "- Element %s (ID 0x%x), Size: %"PRIi64", Offset: %"PRIi64,
+ element->psz_name, *id, *element_size, module->element_offset);
+
+ /* Sanity check the element size */
+ if(*element_size + 1 < 0 /* Shouldn't ever get that big */ ||
+ /* Only the segment / cluster elements can really be massive */
+ (*id != MKV_ELEMENT_ID_SEGMENT && *id != MKV_ELEMENT_ID_CLUSTER &&
+ *element_size > MKV_MAX_ELEMENT_SIZE))
+ {
+ LOG_DEBUG(p_ctx, "element %s has an invalid size (%"PRIi64")",
+ element->psz_name, *element_size);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+ if(size >= 0 && *element_size > size)
+ {
+ LOG_DEBUG(p_ctx, "element %s is bigger than it should (%"PRIi64" > %"PRIi64")",
+ element->psz_name, *element_size, size);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ /* Sanity check that the element has the right parent */
+ if(element->id && element->parent_id != MKV_ELEMENT_ID_INVALID &&
+ parent_id != MKV_ELEMENT_ID_INVALID && parent_id != element->parent_id)
+ {
+ LOG_FORMAT(p_ctx, "Ignoring mis-placed element %s (ID 0x%x)", element->psz_name, *id);
+ while(element->id != MKV_ELEMENT_ID_UNKNOWN) element++;
+ }
+
+ /* Sanity check that the element isn't too deeply nested */
+ if(module->element_level >= MKV_MAX_ELEMENT_LEVEL)
+ {
+ LOG_DEBUG(p_ctx, "element %s is too deep. skipping", element->psz_name);
+ while(element->id != MKV_ELEMENT_ID_UNKNOWN) element++;
+ }
+
+ if(elem) *elem = element;
+ return STREAM_STATUS(p_ctx);
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_element_data(VC_CONTAINER_T *p_ctx,
+ MKV_ELEMENT_T *element, int64_t element_size, int64_t size)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t offset;
+
+ offset = STREAM_POSITION(p_ctx);
+ if (size < 0) size = element_size;
+ if (size < 0) size = INT64_C(1) << 62;
+
+ /* Call the element specific parsing function */
+ if(element->pf_func)
+ status = element->pf_func(p_ctx, element->id, element_size < 0 ? size : element_size);
+
+ if(status != VC_CONTAINER_SUCCESS)
+ LOG_DEBUG(p_ctx, "element %s appears to be corrupted (%i)", element->psz_name, status);
+
+ if(element_size < 0) return STREAM_STATUS(p_ctx); /* Unknown size */
+
+ /* Skip the rest of the element */
+ element_size -= (STREAM_POSITION(p_ctx) - offset);
+ if(element_size < 0) /* Check for overruns */
+ {
+ /* Things have gone really bad here and we ended up reading past the end of the
+ * element. We could maybe try to be clever and recover by seeking back to the end
+ * of the element. However if we get there, the file is clearly corrupted so there's
+ * no guarantee it would work anyway. */
+ LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of element %s",
+ -element_size, element->psz_name);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ if(element_size)
+ LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread in element %s", element_size, element->psz_name);
+
+ if(element_size < MKV_MAX_ELEMENT_SIZE) SKIP_BYTES(p_ctx, element_size);
+ else SEEK(p_ctx, STREAM_POSITION(p_ctx) + element_size);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Base function used to read an MKV/EBML element.
+ * This will read the element header do lots of sanity checking and pass on the rest
+ * of the reading to the element specific reading function */
+static VC_CONTAINER_STATUS_T mkv_read_element(VC_CONTAINER_T *p_ctx,
+ int64_t size, MKV_ELEMENT_ID_T parent_id)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ MKV_ELEMENT_T *element = p_ctx->priv->module->elements_list;
+ int64_t element_size;
+ MKV_ELEMENT_ID_T id;
+
+ status = mkv_read_element_header(p_ctx, size, &id, &element_size, parent_id, &element);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ return mkv_read_element_data(p_ctx, element, element_size, size);
+}
+
+/** Reads an unsigned integer element */
+static VC_CONTAINER_STATUS_T mkv_read_element_data_uint(VC_CONTAINER_T *p_ctx,
+ int64_t size, uint64_t *value)
+{
+ switch(size)
+ {
+ case 1: *value = READ_U8(p_ctx, "u8-integer"); break;
+ case 2: *value = READ_U16(p_ctx, "u16-integer"); break;
+ case 3: *value = READ_U24(p_ctx, "u24-integer"); break;
+ case 4: *value = READ_U32(p_ctx, "u32-integer"); break;
+ case 5: *value = READ_U40(p_ctx, "u40-integer"); break;
+ case 6: *value = READ_U48(p_ctx, "u48-integer"); break;
+ case 7: *value = READ_U56(p_ctx, "u56-integer"); break;
+ case 8: *value = READ_U64(p_ctx, "u64-integer"); break;
+ default: return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads a float element */
+static VC_CONTAINER_STATUS_T mkv_read_element_data_float(VC_CONTAINER_T *p_ctx,
+ int64_t size, double *value)
+{
+ union {
+ uint32_t u32;
+ uint64_t u64;
+ float f;
+ double d;
+ } u;
+
+ switch(size)
+ {
+ case 4: u.u32 = READ_U32(p_ctx, "f32-float"); *value = u.f; break;
+ case 8: u.u64 = READ_U64(p_ctx, "f64-float"); *value = u.d; break;
+ default: return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+ LOG_FORMAT(p_ctx, "float: %f", *value);
+ return STREAM_STATUS(p_ctx);
+}
+
+/** Reads an MKV EBML element */
+static VC_CONTAINER_STATUS_T mkv_read_element_ebml(VC_CONTAINER_T *p_ctx,
+ MKV_ELEMENT_ID_T id, int64_t size)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t offset = STREAM_POSITION(p_ctx);
+
+ /* Read contained elements */
+ module->element_level++;
+ while(status == VC_CONTAINER_SUCCESS && size >= MKV_ELEMENT_MIN_HEADER_SIZE)
+ {
+ offset = STREAM_POSITION(p_ctx);
+ status = mkv_read_element(p_ctx, size, id);
+ size -= (STREAM_POSITION(p_ctx) - offset);
+ }
+ module->element_level--;
+ return status;
+}
+
+/** Reads the MKV EBML sub-element */
+static VC_CONTAINER_STATUS_T mkv_read_subelements_ebml( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ uint64_t value;
+
+ /* Deal with DocType first since it's a special case */
+ if(id == MKV_ELEMENT_ID_DOCTYPE)
+ {
+ char doctype[] = "matroska doctype";
+
+ /* Check we've got the right doctype string for matroska */
+ if(size <= 0) goto unknown_doctype;
+ if(size > (int)sizeof(doctype)) goto unknown_doctype;
+ if((int)READ_STRING(p_ctx, doctype, size, "string") != size) return STREAM_STATUS(p_ctx);
+ if((size != sizeof("matroska")-1 || strncmp(doctype, "matroska", (int)size)) &&
+ (size != sizeof("webm")-1 || strncmp(doctype, "webm", (int)size)))
+ goto unknown_doctype;
+
+ module->is_doctype_valid = true;
+ return VC_CONTAINER_SUCCESS;
+
+ unknown_doctype:
+ LOG_DEBUG(p_ctx, "invalid doctype");
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+
+ /* The rest are just unsigned integers */
+ status = mkv_read_element_data_uint(p_ctx, size, &value);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ switch(id)
+ {
+ case MKV_ELEMENT_ID_EBML_VERSION:
+ case MKV_ELEMENT_ID_EBML_READ_VERSION:
+ if(value != 1) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ break;
+ case MKV_ELEMENT_ID_EBML_MAX_ID_LENGTH:
+ if(value > 4) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ break;
+ case MKV_ELEMENT_ID_EBML_MAX_SIZE_LENGTH:
+ if(value > 8) return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ break;
+ case MKV_ELEMENT_ID_DOCTYPE_VERSION:
+ case MKV_ELEMENT_ID_DOCTYPE_READ_VERSION:
+ default: break;
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_elements( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t offset = STREAM_POSITION(p_ctx);
+ bool unknown_size = size < 0;
+
+ /* Read contained elements */
+ module->element_level++;
+ while(status == VC_CONTAINER_SUCCESS &&
+ (unknown_size || size >= MKV_ELEMENT_MIN_HEADER_SIZE))
+ {
+ offset = STREAM_POSITION(p_ctx);
+ status = mkv_read_element(p_ctx, size, id);
+ if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset);
+ }
+ module->element_level--;
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_element_segment( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t offset = STREAM_POSITION(p_ctx);
+ bool unknown_size = size < 0;
+
+ /* Read contained elements */
+ /* Initialise state used by reader */
+ module->state.level = 0;
+ module->state.levels[0].offset = STREAM_POSITION(p_ctx);
+ module->state.levels[0].size = size;
+ module->state.levels[0].id = MKV_ELEMENT_ID_SEGMENT;
+ module->state.levels[0].data_start = 0;
+ module->state.levels[0].data_offset = 0;
+ module->timecode_scale = 1000000;
+ module->duration = 0.0;
+ module->segment_offset = STREAM_POSITION(p_ctx);
+ module->segment_size = size;
+
+ /* Read contained elements until we have all the information we need to start
+ * playing the stream */
+ module->element_level++;
+ while(status == VC_CONTAINER_SUCCESS &&
+ (unknown_size || size >= MKV_ELEMENT_MIN_HEADER_SIZE))
+ {
+ MKV_ELEMENT_T *child = mkv_elements_list;
+ MKV_ELEMENT_ID_T child_id;
+ int64_t child_size;
+
+ offset = STREAM_POSITION(p_ctx);
+
+ status = mkv_read_element_header(p_ctx, size, &child_id, &child_size, id, &child);
+ if(status != VC_CONTAINER_SUCCESS) break;
+
+ if(child_id == MKV_ELEMENT_ID_CLUSTER)
+ {
+ /* We found the start of the data */
+ module->cluster_offset = module->element_offset;
+ module->state.level = 1;
+ module->state.levels[1].offset = STREAM_POSITION(p_ctx);
+ module->state.levels[1].size = child_size;
+ module->state.levels[1].id = MKV_ELEMENT_ID_CLUSTER;
+ module->state.levels[1].data_start = 0;
+ module->state.levels[1].data_offset = 0;
+ break;
+ }
+
+ status = mkv_read_element_data(p_ctx, child, child_size, size);
+ if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset);
+ }
+
+ module->element_level--;
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_element_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN;
+ VC_CONTAINER_TRACK_MODULE_T *track_module;
+ VC_CONTAINER_TRACK_T *track;
+ VC_CONTAINER_FOURCC_T fourcc = 0, variant = 0;
+ unsigned int i, extra_size = 0, extra_offset = 0, is_wf = 0, is_bmih = 0;
+
+ /* Allocate and initialise track data */
+ if(p_ctx->tracks_num >= MKV_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ p_ctx->tracks[p_ctx->tracks_num] = track =
+ vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
+ if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ module->parsing = track;
+ track_module = track->priv->module;
+ track->is_enabled = true;
+ track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+ track_module->timecode_scale = 1.0;
+ track_module->es_type.video.frame_rate = 0;
+
+ status = mkv_read_elements( p_ctx, id, size );
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ /* Sanity check the data we got from the track entry */
+ if(!track_module->number || !track_module->type)
+ { status = VC_CONTAINER_ERROR_FORMAT_INVALID; goto error; }
+
+ /* Check the encodings for the track are supported */
+ if(track_module->encodings_num > MKV_MAX_ENCODINGS)
+ { status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; }
+ for(i = 0; i < track_module->encodings_num; i++)
+ {
+ if(track_module->encodings[i].type != MKV_CONTENT_ENCODING_COMPRESSION_HEADER ||
+ !track_module->encodings[i].data_size)
+ { status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; }
+ }
+
+ /* Find out the track type */
+ if(track_module->type == 0x1)
+ es_type = VC_CONTAINER_ES_TYPE_VIDEO;
+ else if(track_module->type == 0x2)
+ es_type = VC_CONTAINER_ES_TYPE_AUDIO;
+ else if(track_module->type == 0x11)
+ es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE;
+
+ if(es_type == VC_CONTAINER_ES_TYPE_UNKNOWN)
+ { status = VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED; goto error; }
+
+ if(!strcmp(track_module->codecid, "V_MS/VFW/FOURCC"))
+ {
+ if(vc_container_bitmapinfoheader_to_es_format(track->format->extradata,
+ track->format->extradata_size, &extra_offset, &extra_size,
+ track->format) == VC_CONTAINER_SUCCESS)
+ {
+ fourcc = track->format->codec;
+ is_bmih = 1;
+ }
+ track->format->extradata += extra_offset;
+ track->format->extradata_size = extra_size;
+ }
+ else if(!strcmp(track_module->codecid, "A_MS/ACM"))
+ {
+ if(vc_container_waveformatex_to_es_format(track->format->extradata,
+ track->format->extradata_size, &extra_offset, &extra_size,
+ track->format) == VC_CONTAINER_SUCCESS)
+ {
+ fourcc = track->format->codec;
+ is_wf = 1;
+ }
+ track->format->extradata += extra_offset;
+ track->format->extradata_size = extra_size;
+ }
+ else if((!strncmp(track_module->codecid, "A_AAC/MPEG2/", sizeof("A_AAC/MPEG2/")-1) ||
+ !strncmp(track_module->codecid, "A_AAC/MPEG4/", sizeof("A_AAC/MPEG4/")-1)) &&
+ !track->format->extradata_size)
+ {
+ static const unsigned int sample_rates[16] =
+ {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350};
+ unsigned int samplerate, samplerate_idx, profile, sbr = 0;
+ uint8_t *extra;
+
+ fourcc = mkv_codecid_to_fourcc(track_module->codecid, &variant);
+
+ /* Create extra data */
+ if( !strcmp( &track_module->codecid[12], "MAIN" ) ) profile = 0;
+ else if( !strcmp( &track_module->codecid[12], "LC" ) ) profile = 1;
+ else if( !strcmp( &track_module->codecid[12], "SSR" ) ) profile = 2;
+ else if( !strcmp( &track_module->codecid[12], "LC/SBR" ) ) { profile = 1; sbr = 1; }
+ else profile = 3;
+
+ samplerate = track_module->es_type.audio.sampling_frequency;
+ for( samplerate_idx = 0; samplerate_idx < 13; samplerate_idx++ )
+ if( sample_rates[samplerate_idx] == samplerate ) break;
+
+ status = vc_container_track_allocate_extradata(p_ctx, track, sbr ? 5 : 2);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+ track->format->extradata_size = sbr ? 5 : 2;
+ extra = track->format->extradata;
+
+ extra[0] = ((profile + 1) << 3) | ((samplerate_idx & 0xe) >> 1);
+ extra[1] = ((samplerate_idx & 0x1) << 7) | (track_module->es_type.audio.channels << 3);
+
+ if(sbr)
+ {
+ unsigned int sync_extension_type = 0x2B7;
+ samplerate = track_module->es_type.audio.output_sampling_frequency;
+ for(samplerate_idx = 0; samplerate_idx < 13; samplerate_idx++)
+ if(sample_rates[samplerate_idx] == samplerate) break;
+ extra[2] = (sync_extension_type >> 3) & 0xFF;
+ extra[3] = ((sync_extension_type & 0x7) << 5) | 5;
+ extra[4] = (1 << 7) | (samplerate_idx << 3);
+ }
+ }
+ else fourcc = mkv_codecid_to_fourcc(track_module->codecid, &variant);
+
+ if(!fourcc)
+ {
+ LOG_DEBUG(p_ctx, "codec id %s is not supported", track_module->codecid);
+ }
+
+ LOG_DEBUG(p_ctx, "found track %4.4s", (char *)&fourcc);
+ track->format->codec = fourcc;
+ track->format->codec_variant = variant;
+ track->format->es_type = es_type;
+
+ switch(es_type)
+ {
+ case VC_CONTAINER_ES_TYPE_VIDEO:
+ if(!is_bmih)
+ {
+ track->format->type->video.width = track_module->es_type.video.pixel_width;
+ track->format->type->video.height = track_module->es_type.video.pixel_height;
+ }
+ track->format->type->video.visible_width = track->format->type->video.width;
+ track->format->type->video.visible_height = track->format->type->video.height;
+ if(track_module->es_type.video.pixel_crop_left < track->format->type->video.visible_width &&
+ track_module->es_type.video.pixel_crop_top < track->format->type->video.visible_height)
+ {
+ track->format->type->video.x_offset = track_module->es_type.video.pixel_crop_left;
+ track->format->type->video.y_offset = track_module->es_type.video.pixel_crop_right;
+ track->format->type->video.visible_width -= track->format->type->video.x_offset;
+ track->format->type->video.visible_height -= track->format->type->video.y_offset;
+ }
+ if(track_module->es_type.video.pixel_crop_right < track->format->type->video.visible_width &&
+ track_module->es_type.video.pixel_crop_bottom < track->format->type->video.visible_height)
+ {
+ track->format->type->video.visible_width -= track_module->es_type.video.pixel_crop_right;
+ track->format->type->video.visible_height -= track_module->es_type.video.pixel_crop_bottom;
+ }
+ if(track_module->es_type.video.frame_rate)
+ {
+ track->format->type->video.frame_rate_den = 100;
+ track->format->type->video.frame_rate_num = 100 * track_module->es_type.video.frame_rate;
+ }
+ if(track_module->es_type.video.display_width && track_module->es_type.video.display_height)
+ {
+ track->format->type->video.par_num = track_module->es_type.video.display_width *
+ track->format->type->video.visible_height;
+ track->format->type->video.par_den = track_module->es_type.video.display_height *
+ track->format->type->video.visible_width;
+ vc_container_maths_rational_simplify(&track->format->type->video.par_num,
+ &track->format->type->video.par_den);
+ }
+ break;
+ case VC_CONTAINER_ES_TYPE_AUDIO:
+ if(is_wf) break;
+ track->format->type->audio.sample_rate = track_module->es_type.audio.sampling_frequency;
+ if(track_module->es_type.audio.output_sampling_frequency)
+ track->format->type->audio.sample_rate = track_module->es_type.audio.output_sampling_frequency;
+ track->format->type->audio.channels = track_module->es_type.audio.channels;
+ track->format->type->audio.bits_per_sample = track_module->es_type.audio.bit_depth;
+ break;
+ default:
+ case VC_CONTAINER_ES_TYPE_SUBPICTURE:
+ track->format->type->subpicture.encoding = VC_CONTAINER_CHAR_ENCODING_UTF8;
+ if(!strcmp(track_module->codecid, "S_TEXT/ASCII"))
+ track->format->type->subpicture.encoding = VC_CONTAINER_CHAR_ENCODING_UNKNOWN;
+ }
+
+ track->is_enabled = true;
+
+ p_ctx->tracks_num++;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ for(i = 0; i < MKV_MAX_ENCODINGS; i++) free(track_module->encodings[i].data);
+ vc_container_free_track(p_ctx, track);
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_subelements_track_entry( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = module->parsing;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module;
+ uint64_t value;
+
+ /* Deal with string elements */
+ if( id == MKV_ELEMENT_ID_NAME ||
+ id == MKV_ELEMENT_ID_LANGUAGE ||
+ id == MKV_ELEMENT_ID_TRACK_CODEC_ID ||
+ id == MKV_ELEMENT_ID_TRACK_CODEC_NAME )
+ {
+ char stringbuf[MKV_MAX_STRING_SIZE+1];
+
+ if(size > MKV_MAX_STRING_SIZE)
+ {
+ LOG_DEBUG(p_ctx, "string truncated (%i>%i)", (int)size, MKV_MAX_STRING_SIZE);
+ size = MKV_MAX_STRING_SIZE;
+ }
+ if(READ_BYTES(p_ctx, stringbuf, size) != (size_t)size)
+ {
+ //XXX do sane thing
+ return STREAM_STATUS(p_ctx);
+ }
+ stringbuf[size] = 0; /* null terminate */
+
+ LOG_FORMAT(p_ctx, "%s", stringbuf);
+
+ if(id == MKV_ELEMENT_ID_TRACK_CODEC_ID)
+ strncpy(track_module->codecid, stringbuf, MKV_CODECID_MAX-1);
+
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ /* Deal with codec private data */
+ if( id == MKV_ELEMENT_ID_TRACK_CODEC_PRIVATE )
+ {
+ status = vc_container_track_allocate_extradata(p_ctx, track, (unsigned int)size);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size);
+ return STREAM_STATUS(p_ctx);
+ }
+
+ /* The rest are just unsigned integers */
+ status = mkv_read_element_data_uint(p_ctx, size, &value);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ switch(id)
+ {
+ case MKV_ELEMENT_ID_TRACK_NUMBER:
+ track_module->number = value; break;
+ case MKV_ELEMENT_ID_TRACK_TYPE:
+ track_module->type = value; break;
+ case MKV_ELEMENT_ID_DEFAULT_DURATION:
+ track_module->frame_duration = value; break;
+ default: break;
+ }
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_subelements_video( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module;
+ uint64_t value;
+
+ /* Deal with floating point values first */
+ if(id == MKV_ELEMENT_ID_FRAME_RATE)
+ {
+ double fvalue;
+ status = mkv_read_element_data_float(p_ctx, size, &fvalue);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ track_module->es_type.video.frame_rate = fvalue;
+ return status;
+ }
+
+ /* The rest are just unsigned integers */
+ status = mkv_read_element_data_uint(p_ctx, size, &value);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ switch(id)
+ {
+ case MKV_ELEMENT_ID_PIXEL_WIDTH: track_module->es_type.video.pixel_width = value; break;
+ case MKV_ELEMENT_ID_PIXEL_HEIGHT: track_module->es_type.video.pixel_height = value; break;
+ case MKV_ELEMENT_ID_PIXEL_CROP_BOTTOM: track_module->es_type.video.pixel_crop_bottom = value; break;
+ case MKV_ELEMENT_ID_PIXEL_CROP_TOP: track_module->es_type.video.pixel_crop_top = value; break;
+ case MKV_ELEMENT_ID_PIXEL_CROP_LEFT: track_module->es_type.video.pixel_crop_left = value; break;
+ case MKV_ELEMENT_ID_PIXEL_CROP_RIGHT: track_module->es_type.video.pixel_crop_right = value; break;
+ case MKV_ELEMENT_ID_DISPLAY_WIDTH: track_module->es_type.video.display_width = value; break;
+ case MKV_ELEMENT_ID_DISPLAY_HEIGHT: track_module->es_type.video.display_height = value; break;
+ case MKV_ELEMENT_ID_DISPLAY_UNIT: track_module->es_type.video.display_unit = value; break;
+ case MKV_ELEMENT_ID_ASPECT_RATIO_TYPE: track_module->es_type.video.aspect_ratio_type = value; break;
+ default: break;
+ }
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_subelements_audio( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module;
+ uint64_t value;
+
+ /* Deal with floating point values first */
+ if(id == MKV_ELEMENT_ID_SAMPLING_FREQUENCY ||
+ id == MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY)
+ {
+ double fvalue;
+ status = mkv_read_element_data_float(p_ctx, size, &fvalue);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ if(id == MKV_ELEMENT_ID_SAMPLING_FREQUENCY)
+ track_module->es_type.audio.sampling_frequency = fvalue;
+
+ if(id == MKV_ELEMENT_ID_OUTPUT_SAMPLING_FREQUENCY)
+ track_module->es_type.audio.output_sampling_frequency = fvalue;
+
+ return status;
+ }
+
+ /* The rest are just unsigned integers */
+ status = mkv_read_element_data_uint(p_ctx, size, &value);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ switch(id)
+ {
+ case MKV_ELEMENT_ID_CHANNELS: track_module->es_type.audio.channels = value; break;
+ case MKV_ELEMENT_ID_BIT_DEPTH: track_module->es_type.audio.bit_depth = value; break;
+ default: break;
+ }
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_element_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ status = mkv_read_elements(p_ctx, id, size);
+ track_module->encodings_num++;
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_subelements_encoding( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ uint64_t value;
+
+ /* These are just unsigned integers */
+ status = mkv_read_element_data_uint(p_ctx, size, &value);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ if(track_module->encodings_num >= MKV_MAX_ENCODINGS) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+
+ switch(id)
+ {
+ case MKV_ELEMENT_ID_CONTENT_ENCODING_TYPE:
+ if(value == 0) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_ZLIB;
+ if(value == 1) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_ENCRYPTION;
+ else track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_UNKNOWN;
+ break;
+ default: break;
+ }
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_subelements_compression( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = module->parsing->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ uint64_t value;
+ uint8_t *data;
+
+ if(id == MKV_ELEMENT_ID_CONTENT_COMPRESSION_ALGO)
+ {
+ status = mkv_read_element_data_uint(p_ctx, size, &value);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ if(value == 0) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_ZLIB;
+ if(value == 3) track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_COMPRESSION_HEADER;
+ else track_module->encodings[track_module->encodings_num].type = MKV_CONTENT_ENCODING_UNKNOWN;
+ return status;
+ }
+
+ if(id == MKV_ELEMENT_ID_CONTENT_COMPRESSION_SETTINGS)
+ {
+ if(track_module->encodings[track_module->encodings_num].type == MKV_CONTENT_ENCODING_COMPRESSION_HEADER)
+ {
+ if(size > MKV_MAX_ENCODING_DATA) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+
+ data = malloc((int)size);
+ if(!data) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ track_module->encodings[track_module->encodings_num].data = data;
+ track_module->encodings[track_module->encodings_num].data_size = READ_BYTES(p_ctx, data, size);
+ if(track_module->encodings[track_module->encodings_num].data_size != size)
+ track_module->encodings[track_module->encodings_num].data_size = 0;
+ return STREAM_STATUS(p_ctx);
+ }
+ else
+ {
+ SKIP_BYTES(p_ctx, size);
+ }
+ return STREAM_STATUS(p_ctx);
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_subelements_info( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint64_t value;
+ double fvalue;
+
+ switch(id)
+ {
+ case MKV_ELEMENT_ID_TIMECODE_SCALE:
+ status = mkv_read_element_data_uint(p_ctx, size, &value);
+ if(status != VC_CONTAINER_SUCCESS) break;
+ module->timecode_scale = value;
+ break;
+ case MKV_ELEMENT_ID_DURATION:
+ status = mkv_read_element_data_float(p_ctx, size, &fvalue);
+ if(status != VC_CONTAINER_SUCCESS) break;
+ module->duration = fvalue;
+ break;
+ case MKV_ELEMENT_ID_TITLE:
+ case MKV_ELEMENT_ID_MUXING_APP:
+ case MKV_ELEMENT_ID_WRITING_APP:
+ SKIP_STRING(p_ctx, size, "string");
+ break;
+
+ default: break;
+ }
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_subelements_seek_head( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint64_t value;
+
+ if(id == MKV_ELEMENT_ID_SEEK)
+ {
+ module->seekhead_elem_id = MKV_ELEMENT_ID_UNKNOWN;
+ module->seekhead_elem_offset = -1;
+ status = mkv_read_elements(p_ctx, id, size);
+ if(status == VC_CONTAINER_SUCCESS && !module->cues_offset &&
+ module->seekhead_elem_id == MKV_ELEMENT_ID_CUES && module->seekhead_elem_offset)
+ module->cues_offset = module->seekhead_elem_offset;
+ if(status == VC_CONTAINER_SUCCESS && !module->tags_offset &&
+ module->seekhead_elem_id == MKV_ELEMENT_ID_TAGS && module->seekhead_elem_offset)
+ module->tags_offset = module->seekhead_elem_offset;
+ return status;
+ }
+
+ if(id == MKV_ELEMENT_ID_SEEK_ID)
+ {
+ MKV_ELEMENT_T *element = mkv_elements_list;
+ id = MKV_READ_ID(p_ctx, "Element ID");
+ module->seekhead_elem_id = id;
+
+ /* Find out which Element we are dealing with */
+ while(element->id && id != element->id) element++;
+ LOG_FORMAT(p_ctx, "element: %s (ID 0x%x)", element->psz_name, id);
+ }
+ else if(id == MKV_ELEMENT_ID_SEEK_POSITION)
+ {
+ status = mkv_read_element_data_uint(p_ctx, size, &value);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ LOG_FORMAT(p_ctx, "offset: %"PRIi64, value + module->segment_offset);
+ module->seekhead_elem_offset = value + module->segment_offset;
+ }
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_element_cues( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_PARAM_UNUSED(id);
+ VC_CONTAINER_PARAM_UNUSED(size);
+ module->cues_offset = module->element_offset;
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_subelements_cue_point( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ uint64_t value;
+
+ /* All the values are unsigned integers */
+ status = mkv_read_element_data_uint(p_ctx, size, &value);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ switch(id)
+ {
+ case MKV_ELEMENT_ID_CUE_TIME:
+ module->cue_timecode = value; break;
+ case MKV_ELEMENT_ID_CUE_TRACK:
+ module->cue_track = value; break;
+ case MKV_ELEMENT_ID_CUE_CLUSTER_POSITION:
+ module->cue_cluster_offset = value; break;
+ case MKV_ELEMENT_ID_CUE_BLOCK_NUMBER:
+ module->cue_block = value; break;
+ default: break;
+ }
+
+ return status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_subelements_cluster( VC_CONTAINER_T *p_ctx, MKV_ELEMENT_ID_T id, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ uint64_t value;
+
+ /* The rest are just unsigned integers */
+ status = mkv_read_element_data_uint(p_ctx, size, &value);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ switch(id)
+ {
+ //XXX
+ case MKV_ELEMENT_ID_TIMECODE: module->state.cluster_timecode = value; break;
+ case MKV_ELEMENT_ID_BLOCK_DURATION: module->state.frame_duration = value; break;
+ default: break;
+ }
+
+ return status;
+}
+
+/*******************************/
+
+static VC_CONTAINER_STATUS_T mkv_skip_element(VC_CONTAINER_T *p_ctx,
+ MKV_READER_STATE_T *state)
+{
+ /* Skip any trailing data from the current element */
+ int64_t skip = state->levels[state->level].offset +
+ state->levels[state->level].size - STREAM_POSITION(p_ctx);
+
+ if(skip < 0) /* Check for overruns */
+ {
+ /* Things have gone really bad here and we ended up reading past the end of the
+ * element. We could maybe try to be clever and recover by seeking back to the end
+ * of the element. However if we get there, the file is clearly corrupted so there's
+ * no guarantee it would work anyway. */
+ LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of the element", -skip);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ if(skip)
+ LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread at the end of element", skip);
+
+ state->level--;
+
+ if (skip >= MKV_MAX_ELEMENT_SIZE)
+ return SEEK(p_ctx, STREAM_POSITION(p_ctx) + skip);
+ SKIP_BYTES(p_ctx, skip);
+ return STREAM_STATUS(p_ctx);
+}
+
+static VC_CONTAINER_STATUS_T mkv_find_next_element(VC_CONTAINER_T *p_ctx,
+ MKV_READER_STATE_T *state, MKV_ELEMENT_ID_T element_id)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t element_size, element_offset;
+ MKV_ELEMENT_ID_T id;
+
+ /* Skip all elements until we find the next requested element */
+ do
+ {
+ MKV_ELEMENT_T *element = mkv_cluster_elements_list;
+
+ /* Check whether we've reach the end of the parent element */
+ if(STREAM_POSITION(p_ctx) >= state->levels[state->level].offset +
+ state->levels[state->level].size)
+ return VC_CONTAINER_ERROR_NOT_FOUND;
+
+ status = mkv_read_element_header(p_ctx, INT64_C(1) << 30, &id,
+ &element_size, state->levels[state->level].id, &element);
+ element_offset = STREAM_POSITION(p_ctx);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ if(id == element_id) break;
+ if(element_id == MKV_ELEMENT_ID_BLOCKGROUP && id == MKV_ELEMENT_ID_SIMPLE_BLOCK) break;
+
+ if(element_id == MKV_ELEMENT_ID_BLOCK && id == MKV_ELEMENT_ID_REFERENCE_BLOCK)
+ state->seen_ref_block = 1;
+
+ /* Check whether we've reached the end of the parent element */
+ if(STREAM_POSITION(p_ctx) + element_size >= state->levels[state->level].offset +
+ state->levels[state->level].size)
+ return VC_CONTAINER_ERROR_NOT_FOUND;
+
+ status = mkv_read_element_data(p_ctx, element, element_size, INT64_C(1) << 30);
+#if 0
+ if(element_size < MKV_MAX_ELEMENT_SIZE) SKIP_BYTES(p_ctx, element_size);
+ else SEEK(p_ctx, STREAM_POSITION(p_ctx) + element_size);
+#endif
+ } while (status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS);
+
+ if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS)
+ return STREAM_STATUS(p_ctx);
+
+ state->level++;
+ state->levels[state->level].offset = element_offset;
+ state->levels[state->level].size = element_size;
+ state->levels[state->level].id = id;
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T mkv_find_next_segment(VC_CONTAINER_T *p_ctx,
+ MKV_READER_STATE_T *state)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t element_size, element_offset;
+ MKV_ELEMENT_ID_T id;
+
+ /* Skip all elements until we find the next segment */
+ do
+ {
+ MKV_ELEMENT_T *element = mkv_cluster_elements_list;
+
+ status = mkv_read_element_header(p_ctx, INT64_C(-1), &id,
+ &element_size, MKV_ELEMENT_ID_INVALID, &element);
+
+ element_offset = STREAM_POSITION(p_ctx);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ if(id == MKV_ELEMENT_ID_SEGMENT) break;
+
+ status = mkv_read_element_data(p_ctx, element, element_size, INT64_C(-1));
+ } while (status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS);
+
+ if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS)
+ return STREAM_STATUS(p_ctx);
+
+ state->level++;
+ state->levels[state->level].offset = element_offset;
+ state->levels[state->level].size = element_size;
+ state->levels[state->level].id = id;
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T mkv_find_next_block(VC_CONTAINER_T *p_ctx, MKV_READER_STATE_T *state)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND;
+
+ do
+ {
+ if(state->level < 0)
+ {
+#ifdef ENABLE_MKV_EXTRA_LOGGING
+ LOG_DEBUG(p_ctx, "find segment");
+#endif
+ status = mkv_find_next_segment(p_ctx, state);
+ if(status == VC_CONTAINER_SUCCESS)
+ {
+ LOG_ERROR(p_ctx, "multi-segment files not supported");
+ status = VC_CONTAINER_ERROR_EOS;
+ break;
+ }
+ }
+ if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK ||
+ state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK)
+ {
+ status = mkv_skip_element(p_ctx, state);
+ }
+ if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCKGROUP)
+ {
+#ifdef ENABLE_MKV_EXTRA_LOGGING
+ LOG_DEBUG(p_ctx, "find block");
+#endif
+ state->seen_ref_block = 0;
+ state->frame_duration = 0;
+ status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_BLOCK);
+ if(status == VC_CONTAINER_SUCCESS) break;
+
+ if(status == VC_CONTAINER_ERROR_NOT_FOUND)
+ status = mkv_skip_element(p_ctx, state);
+ }
+ if(state->levels[state->level].id == MKV_ELEMENT_ID_CLUSTER)
+ {
+#ifdef ENABLE_MKV_EXTRA_LOGGING
+ LOG_DEBUG(p_ctx, "find blockgroup or simpleblock");
+#endif
+ state->frame_duration = 0;
+ status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_BLOCKGROUP);
+ if(status == VC_CONTAINER_SUCCESS &&
+ state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) break;
+ if(status == VC_CONTAINER_ERROR_NOT_FOUND)
+ status = mkv_skip_element(p_ctx, state);
+ }
+ if(state->levels[state->level].id == MKV_ELEMENT_ID_SEGMENT)
+ {
+#ifdef ENABLE_MKV_EXTRA_LOGGING
+ LOG_DEBUG(p_ctx, "find cluster");
+#endif
+ status = mkv_find_next_element(p_ctx, state, MKV_ELEMENT_ID_CLUSTER);
+ if(status == VC_CONTAINER_ERROR_NOT_FOUND)
+ status = mkv_skip_element(p_ctx, state);
+ }
+
+ } while(status == VC_CONTAINER_SUCCESS && STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS);
+
+ return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_next_frame_header(VC_CONTAINER_T *p_ctx,
+ MKV_READER_STATE_T *state, uint32_t *pi_track, uint32_t *pi_length)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = 0;
+ unsigned int i, track, flags, is_first_lace = 0;
+ int64_t size, pts;
+
+ if ((state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK ||
+ state->levels[state->level].id == MKV_ELEMENT_ID_SIMPLE_BLOCK) &&
+ state->levels[state->level].data_start + state->levels[state->level].data_offset <
+ state->levels[state->level].size)
+ goto end;
+
+ status = mkv_find_next_block(p_ctx, state);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ /* We have a block */
+ size = state->levels[state->level].size;
+ track = MKV_READ_UINT(p_ctx, "Track Number"); LOG_FORMAT(p_ctx, "Track Number: %u", track);
+ pts = (int16_t)MKV_READ_U16(p_ctx, "Timecode");
+ flags = MKV_READ_U8(p_ctx, "Flags");
+ if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK) flags &= 0x0F;
+
+ //TODO improve sanity checking
+ /* Sanity checking */
+ if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx);
+
+ for (i = 0; i < p_ctx->tracks_num; i++)
+ if (p_ctx->tracks[i]->priv->module->number == track)
+ { track_module = p_ctx->tracks[i]->priv->module; break; }
+
+ /* Finding out if we have a keyframe when dealing with an MKV_ELEMENT_ID_BLOCK is tricky */
+ if(state->levels[state->level].id == MKV_ELEMENT_ID_BLOCK &&
+ i < p_ctx->tracks_num && p_ctx->tracks[i]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ {
+ /* The absence of a ReferenceBlock element tells us if we are a keyframe or not.
+ * The problem we have is that this element can appear before as well as after the block element.
+ * To avoid seeking to look for this element, we do some guess work. */
+ if(!state->seen_ref_block && state->level &&
+ state->levels[state->level].offset + state->levels[state->level].size >=
+ state->levels[state->level-1].offset + state->levels[state->level-1].size)
+ flags |= 0x80;
+ }
+
+ /* Take care of the lacing */
+ state->lacing_num_frames = 0;
+ if(i < p_ctx->tracks_num && (flags & 0x06))
+ {
+ unsigned int i, value = 0;
+ int32_t fs = 0;
+
+ state->lacing_num_frames = MKV_READ_U8(p_ctx, "Lacing Head");
+ state->lacing_size = 0;
+ switch((flags & 0x06)>>1)
+ {
+ case 1: /* Xiph lacing */
+ for(i = 0; i < state->lacing_num_frames; i++, fs = 0)
+ {
+ do {
+ value = vc_container_io_read_uint8(p_ctx->priv->io);
+ fs += value; size--;
+ } while(value == 255);
+ LOG_FORMAT(p_ctx, "Frame Size: %i", (int)fs);
+ if(state->lacing_num_frames > MKV_MAX_LACING_NUM) continue;
+ state->lacing_sizes[state->lacing_num_frames-(i+1)] = fs;
+ }
+ break;
+ case 3: /* EBML lacing */
+ for(i = 0; i < state->lacing_num_frames; i++)
+ {
+ if(!i) fs = MKV_READ_UINT(p_ctx, "Frame Size");
+ else fs += MKV_READ_SINT(p_ctx, "Frame Size");
+ LOG_FORMAT(p_ctx, "Frame Size: %i", (int)fs);
+ if(state->lacing_num_frames > MKV_MAX_LACING_NUM) continue;
+ state->lacing_sizes[state->lacing_num_frames-(i+1)] = fs;
+ }
+ break;
+ default: /* Fixed-size lacing */
+ state->lacing_size = size / (state->lacing_num_frames+1);
+ break;
+ }
+
+ /* There is a max number of laced frames we can support but after we can still give back
+ * all the other frames in 1 packet */
+ if(state->lacing_num_frames > MKV_MAX_LACING_NUM)
+ state->lacing_num_frames = MKV_MAX_LACING_NUM;
+
+ /* Sanity check the size of the frames */
+ if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(STREAM_STATUS(p_ctx)) return STREAM_STATUS(p_ctx);
+ state->lacing_current_size = state->lacing_size;
+ if(!state->lacing_size)
+ {
+ int64_t frames_size = 0;
+ for(i = state->lacing_num_frames; i > 0; i--)
+ {
+ if(frames_size + state->lacing_sizes[i-1] > size) /* return error ? */
+ state->lacing_sizes[i-1] = size - frames_size;
+ frames_size += state->lacing_sizes[i-1];
+ }
+ }
+ state->lacing_current_size = 0;
+ state->lacing_num_frames++; /* Will be decremented further down */
+ is_first_lace = 1;
+ }
+
+ state->track = i;
+ state->pts = (state->cluster_timecode + pts) * module->timecode_scale;
+ if(track_module) state->pts *= track_module->timecode_scale;
+ state->pts /= 1000;
+ state->flags = flags;
+
+ state->frame_duration = state->frame_duration * module->timecode_scale / 1000;
+ if(state->lacing_num_frames) state->frame_duration /= state->lacing_num_frames;
+ if(!state->frame_duration && track_module)
+ state->frame_duration = track_module->frame_duration / 1000;
+
+ state->levels[state->level].data_start = STREAM_POSITION(p_ctx) -
+ state->levels[state->level].offset;
+ state->levels[state->level].data_offset = 0;
+
+ /* Deal with header stripping compression */
+ state->header_size = 0;
+ if(track_module && track_module->encodings_num)
+ {
+ state->header_size = track_module->encodings[0].data_size;
+ state->header_data = track_module->encodings[0].data;
+ }
+ state->header_size_backup = state->header_size;
+
+ end:
+ *pi_length = state->levels[state->level].size - state->levels[state->level].data_start -
+ state->levels[state->level].data_offset + state->header_size;
+ *pi_track = state->track;
+
+ /* Special case for lacing */
+ if(state->lacing_num_frames &&
+ state->levels[state->level].data_offset >= state->lacing_current_size)
+ {
+ /* We need to switch to the next lace */
+ if(--state->lacing_num_frames)
+ {
+ unsigned int lace_size = state->lacing_size;
+ if(!state->lacing_size) lace_size = state->lacing_sizes[state->lacing_num_frames-1];
+ state->lacing_current_size = lace_size;
+ }
+ state->levels[state->level].data_start += state->levels[state->level].data_offset;
+ state->levels[state->level].data_offset = 0;
+ if(!is_first_lace && state->frame_duration)
+ state->pts += state->frame_duration;
+ else if(!is_first_lace)
+ state->pts = VC_CONTAINER_TIME_UNKNOWN;
+
+ /* Deal with header stripping compression */
+ state->header_data -= (state->header_size_backup - state->header_size);
+ state->header_size = state->header_size_backup;
+ }
+ if(state->lacing_num_frames)
+ *pi_length = state->lacing_current_size - state->levels[state->level].data_offset + state->header_size;
+
+ return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status;
+}
+
+static VC_CONTAINER_STATUS_T mkv_read_frame_data(VC_CONTAINER_T *p_ctx,
+ MKV_READER_STATE_T *state, uint8_t *p_data, uint32_t *pi_length)
+{
+ uint64_t size;
+ uint32_t header_size;
+
+ size = state->levels[state->level].size - state->levels[state->level].data_start -
+ state->levels[state->level].data_offset;
+
+ /* Special case for lacing */
+ if(state->lacing_num_frames)
+ {
+ size = state->lacing_current_size - state->levels[state->level].data_offset;
+
+ if(!p_data)
+ {
+ size = SKIP_BYTES(p_ctx, size);
+ state->levels[state->level].data_offset += size;
+ return STREAM_STATUS(p_ctx);
+ }
+ }
+
+ size += state->header_size;
+
+ if(!p_data) return mkv_skip_element(p_ctx, state);
+ if(size > *pi_length) size = *pi_length;
+
+ header_size = state->header_size;
+ if(header_size)
+ {
+ if(header_size > size) header_size = size;
+ memcpy(p_data, state->header_data, header_size);
+ state->header_size -= header_size;
+ state->header_data += header_size;
+ size -= header_size;
+ }
+
+ size = READ_BYTES(p_ctx, p_data + header_size, size);
+ state->levels[state->level].data_offset += size;
+ *pi_length = size + header_size;
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************
+ Functions exported as part of the Container Module API
+ *****************************************************************************/
+
+static VC_CONTAINER_STATUS_T mkv_reader_read(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *p_packet, uint32_t flags)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *p_track = 0;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint32_t buffer_size = 0, track = 0, data_size;
+ MKV_READER_STATE_T *state = &module->state;
+
+ /* If a specific track has been selected, we need to use the track packet state */
+ if(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK)
+ {
+ p_track = p_ctx->tracks[p_packet->track];
+ state = p_track->priv->module->state;
+ }
+
+ /**/
+ if(state->eos) return VC_CONTAINER_ERROR_EOS;
+ if(state->corrupted) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ /* Look at the next frame header */
+ status = mkv_read_next_frame_header(p_ctx, state, &track, &data_size);
+ if(status == VC_CONTAINER_ERROR_EOS) state->eos = true;
+ if(status == VC_CONTAINER_ERROR_CORRUPTED) state->corrupted = true;
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ if(track >= p_ctx->tracks_num || !p_ctx->tracks[track]->is_enabled)
+ {
+ /* Skip frame */
+ status = mkv_read_frame_data(p_ctx, state, 0, &data_size);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+ return VC_CONTAINER_ERROR_CONTINUE;
+ }
+
+ if((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)) /* Skip packet */
+ return mkv_read_frame_data(p_ctx, state, 0, &data_size);
+
+ p_packet->dts = p_packet->pts = state->pts;
+ p_packet->flags = 0;
+ if(state->flags & 0x80) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME;
+ if(!state->levels[state->level].data_offset) p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
+ p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
+ p_packet->size = data_size;
+ p_packet->track = track;
+
+ if(flags & VC_CONTAINER_READ_FLAG_SKIP)
+ return mkv_read_frame_data(p_ctx, state, 0, &data_size );
+ else if(flags & VC_CONTAINER_READ_FLAG_INFO)
+ return VC_CONTAINER_SUCCESS;
+
+ /* Read the frame data */
+ buffer_size = p_packet->buffer_size;
+ status = mkv_read_frame_data(p_ctx, state, p_packet->data, &buffer_size);
+ if(status != VC_CONTAINER_SUCCESS)
+ {
+ /* FIXME */
+ return status;
+ }
+
+ p_packet->size = buffer_size;
+ if(buffer_size != data_size)
+ p_packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END;
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mkv_reader_seek(VC_CONTAINER_T *p_ctx,
+ int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ MKV_READER_STATE_T *state = &module->state;
+ uint64_t offset = 0, prev_offset = 0, position = STREAM_POSITION(p_ctx);
+ int64_t time_offset = 0, prev_time_offset = 0;
+ unsigned int i, video_track;
+ MKV_ELEMENT_T *element = mkv_cue_elements_list;
+ int64_t size, element_size;
+ MKV_ELEMENT_ID_T id;
+ VC_CONTAINER_PARAM_UNUSED(mode);
+ VC_CONTAINER_PARAM_UNUSED(flags);
+
+ /* Find out if we have a video track */
+ for(video_track = 0; video_track < p_ctx->tracks_num; video_track++)
+ if(p_ctx->tracks[video_track]->is_enabled &&
+ p_ctx->tracks[video_track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break;
+
+ if(!*p_offset) goto end; /* Nothing much to do */
+ if(!module->cues_offset) {status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION; goto error;}
+
+ /* We need to do a search in the cue list */
+ status = SEEK(p_ctx, module->cues_offset);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ /* First read the header of the cues element */
+ status = mkv_read_element_header(p_ctx, INT64_C(-1) /* TODO */, &id, &element_size,
+ MKV_ELEMENT_ID_SEGMENT, &element);
+ if(status != VC_CONTAINER_SUCCESS || id != MKV_ELEMENT_ID_CUES) goto error;
+ size = element_size;
+
+ module->elements_list = mkv_cue_elements_list;
+ do
+ {
+ MKV_ELEMENT_T *element = mkv_cue_elements_list;
+ int64_t element_offset = STREAM_POSITION(p_ctx);
+
+ /* Exit condition for when we've scanned the whole cues list */
+ if(size <= 0)
+ {
+ if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
+ break; /* Just use the last valid entry in that case */
+ status = VC_CONTAINER_ERROR_EOS;
+ goto error;
+ }
+
+ status = mkv_read_element_header(p_ctx, size, &id, &element_size,
+ MKV_ELEMENT_ID_CUES, &element);
+ size -= STREAM_POSITION(p_ctx) - element_offset;
+ if(status == VC_CONTAINER_SUCCESS && element->id != MKV_ELEMENT_ID_UNKNOWN)
+ status = mkv_read_element_data(p_ctx, element, element_size, size);
+
+ if(status != VC_CONTAINER_SUCCESS || element->id == MKV_ELEMENT_ID_UNKNOWN)
+ {
+ if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
+ break; /* Just use the last valid entry in that case */
+ goto error;
+ }
+
+ size -= element_size;
+ if(id != MKV_ELEMENT_ID_CUE_POINT) continue;
+
+ /* Ignore cue points which don't belong to the track we want */
+ if(video_track != p_ctx->tracks_num &&
+ p_ctx->tracks[video_track]->priv->module->number != module->cue_track) continue;
+
+ time_offset = module->cue_timecode * module->timecode_scale / 1000;
+ offset = module->cue_cluster_offset;
+ LOG_DEBUG(p_ctx, "INDEX: %"PRIi64, time_offset);
+ if( time_offset > *p_offset )
+ {
+ if(!(flags & VC_CONTAINER_SEEK_FLAG_FORWARD))
+ {
+ time_offset = prev_time_offset;
+ offset = prev_offset;
+ }
+ break;
+ }
+ prev_time_offset = time_offset;
+ prev_offset = offset;
+ } while( 1 );
+ module->elements_list = mkv_elements_list;
+ *p_offset = time_offset;
+
+ end:
+ /* Try seeking to the requested position */
+ status = SEEK(p_ctx, module->segment_offset + offset);
+ if(status != VC_CONTAINER_SUCCESS && status != VC_CONTAINER_ERROR_EOS) goto error;
+
+ /* Reinitialise the state */
+ memset(state, 0, sizeof(*state));
+ state->levels[0].offset = module->segment_offset;
+ state->levels[0].size = module->segment_size;
+ state->levels[0].id = MKV_ELEMENT_ID_SEGMENT;
+ if(status == VC_CONTAINER_ERROR_EOS) state->eos = true;
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ VC_CONTAINER_TRACK_T *p_track = p_ctx->tracks[i];
+ p_track->priv->module->state = state;
+ }
+
+ /* If we have a video track, we skip frames until the next keyframe */
+ for(i = 0; video_track != p_ctx->tracks_num && i < 200 /* limit search */; )
+ {
+ uint32_t track, data_size;
+ status = mkv_read_next_frame_header(p_ctx, state, &track, &data_size);
+ if(status != VC_CONTAINER_SUCCESS) break; //FIXME
+ if(track == video_track) i++;
+ if(track == video_track && (state->flags & 0x80) &&
+ state->pts >= time_offset) break;
+
+ /* Skip frame */
+ status = mkv_read_frame_data(p_ctx, state, 0, &data_size);
+ }
+
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ /* Reset everything as it was before the seek */
+ SEEK(p_ctx, position);
+ if(status == VC_CONTAINER_SUCCESS) status = VC_CONTAINER_ERROR_FAILED;
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mkv_reader_close(VC_CONTAINER_T *p_ctx)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int i, j;
+
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ for(j = 0; j < MKV_MAX_ENCODINGS; j++)
+ free(p_ctx->tracks[i]->priv->module->encodings[j].data);
+ vc_container_free_track(p_ctx, p_ctx->tracks[i]);
+ }
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T mkv_reader_open(VC_CONTAINER_T *p_ctx)
+{
+ VC_CONTAINER_MODULE_T *module = 0;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ uint8_t buffer[4];
+
+ // Can start with ASCII strings ????
+
+ /* Check for an EBML element */
+ if(PEEK_BYTES(p_ctx, buffer, 4) < 4 ||
+ buffer[0] != 0x1A || buffer[1] != 0x45 || buffer[2] != 0xDF || buffer[3] != 0xA3)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /*
+ * We are dealing with an MKV file
+ */
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if(!module) {status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error;}
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks = module->tracks;
+ module->elements_list = mkv_elements_list;
+
+ /* Read and sanity check the EBML header */
+ status = mkv_read_element(p_ctx, INT64_C(-1), MKV_ELEMENT_ID_UNKNOWN);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+ if(!module->is_doctype_valid) {status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; goto error;}
+
+ /* Read the other elements until we find the start of the data */
+ do
+ {
+ status = mkv_read_element(p_ctx, INT64_C(-1), MKV_ELEMENT_ID_UNKNOWN);
+ if(status != VC_CONTAINER_SUCCESS) break;
+
+ if(module->cluster_offset) break;
+ } while(1);
+
+ /* Bail out if we didn't find a track */
+ if(!p_ctx->tracks_num)
+ {
+ status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE;
+ goto error;
+ }
+
+ /*
+ * We now have all the information we really need to start playing the stream
+ */
+
+ p_ctx->priv->pf_close = mkv_reader_close;
+ p_ctx->priv->pf_read = mkv_reader_read;
+ p_ctx->priv->pf_seek = mkv_reader_seek;
+ p_ctx->duration = module->duration / 1000 * module->timecode_scale;
+
+ /* Check if we're done */
+ if(!STREAM_SEEKABLE(p_ctx))
+ return VC_CONTAINER_SUCCESS;
+
+ if(module->cues_offset && (int64_t)module->cues_offset < p_ctx->size)
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
+
+ if(module->tags_offset)
+ {
+ status = SEEK(p_ctx, module->tags_offset);
+ if(status == VC_CONTAINER_SUCCESS)
+ status = mkv_read_element(p_ctx, INT64_C(-1) /*FIXME*/, MKV_ELEMENT_ID_SEGMENT);
+ }
+
+ /* Seek back to the start of the data */
+ return SEEK(p_ctx, module->state.levels[1].offset);
+
+ error:
+ LOG_DEBUG(p_ctx, "mkv: error opening stream (%i)", status);
+ if(module) mkv_reader_close(p_ctx);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open mkv_reader_open
+#endif
diff --git a/gfx/include/userland/containers/mp4/CMakeLists.txt b/gfx/include/userland/containers/mp4/CMakeLists.txt
new file mode 100644
index 0000000000..87892c2b7a
--- /dev/null
+++ b/gfx/include/userland/containers/mp4/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(reader_mp4 ${LIBRARY_TYPE} mp4_reader.c)
+
+target_link_libraries(reader_mp4 containers)
+
+install(TARGETS reader_mp4 DESTINATION ${VMCS_PLUGIN_DIR})
+
+add_library(writer_mp4 ${LIBRARY_TYPE} mp4_writer.c)
+
+target_link_libraries(writer_mp4 containers)
+
+install(TARGETS writer_mp4 DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/mp4/mp4_common.h b/gfx/include/userland/containers/mp4/mp4_common.h
new file mode 100644
index 0000000000..8718535c26
--- /dev/null
+++ b/gfx/include/userland/containers/mp4/mp4_common.h
@@ -0,0 +1,128 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef MP4_COMMON_H
+#define MP4_COMMON_H
+
+/******************************************************************************
+Type definitions.
+******************************************************************************/
+typedef enum {
+ MP4_BOX_TYPE_UNKNOWN = 0,
+ MP4_BOX_TYPE_ROOT = VC_FOURCC('r','o','o','t'),
+ MP4_BOX_TYPE_FTYP = VC_FOURCC('f','t','y','p'),
+ MP4_BOX_TYPE_MDAT = VC_FOURCC('m','d','a','t'),
+ MP4_BOX_TYPE_MOOV = VC_FOURCC('m','o','o','v'),
+ MP4_BOX_TYPE_MVHD = VC_FOURCC('m','v','h','d'),
+ MP4_BOX_TYPE_TRAK = VC_FOURCC('t','r','a','k'),
+ MP4_BOX_TYPE_TKHD = VC_FOURCC('t','k','h','d'),
+ MP4_BOX_TYPE_MDIA = VC_FOURCC('m','d','i','a'),
+ MP4_BOX_TYPE_MDHD = VC_FOURCC('m','d','h','d'),
+ MP4_BOX_TYPE_HDLR = VC_FOURCC('h','d','l','r'),
+ MP4_BOX_TYPE_MINF = VC_FOURCC('m','i','n','f'),
+ MP4_BOX_TYPE_VMHD = VC_FOURCC('v','m','h','d'),
+ MP4_BOX_TYPE_SMHD = VC_FOURCC('s','m','h','d'),
+ MP4_BOX_TYPE_DINF = VC_FOURCC('d','i','n','f'),
+ MP4_BOX_TYPE_DREF = VC_FOURCC('d','r','e','f'),
+ MP4_BOX_TYPE_STBL = VC_FOURCC('s','t','b','l'),
+ MP4_BOX_TYPE_STSD = VC_FOURCC('s','t','s','d'),
+ MP4_BOX_TYPE_STTS = VC_FOURCC('s','t','t','s'),
+ MP4_BOX_TYPE_CTTS = VC_FOURCC('c','t','t','s'),
+ MP4_BOX_TYPE_STSC = VC_FOURCC('s','t','s','c'),
+ MP4_BOX_TYPE_STSZ = VC_FOURCC('s','t','s','z'),
+ MP4_BOX_TYPE_STCO = VC_FOURCC('s','t','c','o'),
+ MP4_BOX_TYPE_CO64 = VC_FOURCC('c','o','6','4'),
+ MP4_BOX_TYPE_STSS = VC_FOURCC('s','t','s','s'),
+ MP4_BOX_TYPE_VIDE = VC_FOURCC('v','i','d','e'),
+ MP4_BOX_TYPE_SOUN = VC_FOURCC('s','o','u','n'),
+ MP4_BOX_TYPE_TEXT = VC_FOURCC('t','e','x','t'),
+ MP4_BOX_TYPE_FREE = VC_FOURCC('f','r','e','e'),
+ MP4_BOX_TYPE_SKIP = VC_FOURCC('s','k','i','p'),
+ MP4_BOX_TYPE_WIDE = VC_FOURCC('w','i','d','e'),
+ MP4_BOX_TYPE_PNOT = VC_FOURCC('p','m','o','t'),
+ MP4_BOX_TYPE_PICT = VC_FOURCC('P','I','C','T'),
+ MP4_BOX_TYPE_UDTA = VC_FOURCC('u','d','t','a'),
+ MP4_BOX_TYPE_UUID = VC_FOURCC('u','u','i','d'),
+ MP4_BOX_TYPE_ESDS = VC_FOURCC('e','s','d','s'),
+ MP4_BOX_TYPE_AVCC = VC_FOURCC('a','v','c','C'),
+ MP4_BOX_TYPE_D263 = VC_FOURCC('d','2','6','3'),
+ MP4_BOX_TYPE_DAMR = VC_FOURCC('d','a','m','r'),
+ MP4_BOX_TYPE_DAWP = VC_FOURCC('d','a','w','p'),
+ MP4_BOX_TYPE_DEVC = VC_FOURCC('d','e','v','c'),
+ MP4_BOX_TYPE_WAVE = VC_FOURCC('w','a','v','e'),
+ MP4_BOX_TYPE_ZERO = 0
+} MP4_BOX_TYPE_T;
+
+typedef enum {
+ MP4_BRAND_ISOM = VC_FOURCC('i','s','o','m'),
+ MP4_BRAND_MP42 = VC_FOURCC('m','p','4','2'),
+ MP4_BRAND_3GP4 = VC_FOURCC('3','g','p','4'),
+ MP4_BRAND_3GP5 = VC_FOURCC('3','g','p','5'),
+ MP4_BRAND_3GP6 = VC_FOURCC('3','g','p','6'),
+ MP4_BRAND_SKM2 = VC_FOURCC('s','k','m','2'),
+ MP4_BRAND_SKM3 = VC_FOURCC('s','k','m','3'),
+ MP4_BRAND_QT = VC_FOURCC('q','t',' ',' '),
+ MP4_BRAND_NUM
+} MP4_BRAND_T;
+
+typedef enum
+{
+ MP4_SAMPLE_TABLE_STTS = 0, /* decoding time to sample */
+ MP4_SAMPLE_TABLE_STSZ = 1, /* sample size */
+ MP4_SAMPLE_TABLE_STSC = 2, /* sample to chunk */
+ MP4_SAMPLE_TABLE_STCO = 3, /* sample to chunk-offset */
+ MP4_SAMPLE_TABLE_STSS = 4, /* sync sample */
+ MP4_SAMPLE_TABLE_CO64 = 5, /* sample to chunk-offset */
+ MP4_SAMPLE_TABLE_CTTS = 6, /* composite time to sample */
+ MP4_SAMPLE_TABLE_NUM
+} MP4_SAMPLE_TABLE_T;
+
+/* Values for object_type_indication (mp4_decoder_config_descriptor)
+ * see ISO/IEC 14496-1:2001(E) section 8.6.6.2 table 8 p. 30
+ * see ISO/IEC 14496-15:2003 (draft) section 4.2.2 table 3 p. 11
+ * see SKT Spec 8.2.3 p. 107
+ * see 3GPP2 Spec v1.0 p. 22 */
+#define MP4_MPEG4_VISUAL_OBJECT_TYPE 0x20 /* visual ISO/IEC 14496-2 */
+#define MP4_MPEG4_H264_OBJECT_TYPE 0x21 /* visual ISO/IEC 14496-10 */
+#define MP4_MPEG4_H264_PS_OBJECT_TYPE 0x22 /* visual ISO/IEC 14496-10 (used for parameter ES) */
+#define MP4_MPEG4_AAC_LC_OBJECT_TYPE 0x40 /* audio ISO/IEC 14496-3 */
+#define MP4_MPEG2_SP_OBJECT_TYPE 0x60 /* visual ISO/IEC 13818-2 Simple Profile */
+#define MP4_MPEG2_MP_OBJECT_TYPE 0x61 /* visual ISO/IEC 13818-2 Main Profile */
+#define MP4_MPEG2_SNR_OBJECT_TYPE 0x62 /* visual ISO/IEC 13818-2 SNR Profile */
+#define MP4_MPEG2_AAC_LC_OBJECT_TYPE 0x67 /* audio ISO/IEC 13818-7 LowComplexity Profile */
+#define MP4_MP3_OBJECT_TYPE 0x69 /* audio ISO/IEC 13818-3 */
+#define MP4_MPEG1_VISUAL_OBJECT_TYPE 0x6A /* visual ISO/IEC 11172-2 */
+#define MP4_MPEG1_AUDIO_OBJECT_TYPE 0x6B /* audio ISO/IEC 11172-3 */
+#define MP4_JPEG_OBJECT_TYPE 0x6C /* visual ISO/IEC 10918-1 */
+#define MP4_SKT_EVRC_2V1_OBJECT_TYPE 0x82 /* SKT spec V2.1 for EVRC */
+#define MP4_KTF_EVRC_OBJECT_TYPE 0xC2 /* KTF spec V1.2 for EVRC */
+#define MP4_KTF_AMR_OBJECT_TYPE 0xC4 /* KTF spec V1.2 for AMR */
+#define MP4_KTF_MP3_OBJECT_TYPE 0xC5 /* KTF spec V1.2 for MP3 */
+#define MP4_SKT_TEXT_OBJECT_TYPE 0xD0 /* SKT spec V2.2 for Text */
+#define MP4_SKT_EVRC_OBJECT_TYPE 0xD1 /* SKT spec V2.2 for EVRC */
+#define MP4_3GPP2_QCELP_OBJECT_TYPE 0xE1 /* 3GPP2 spec V1.0 for QCELP13K */
+
+#endif /* MP4_COMMON_H */
diff --git a/gfx/include/userland/containers/mp4/mp4_reader.c b/gfx/include/userland/containers/mp4/mp4_reader.c
new file mode 100644
index 0000000000..08341f551e
--- /dev/null
+++ b/gfx/include/userland/containers/mp4/mp4_reader.c
@@ -0,0 +1,1879 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+/* Work-around for MSVC debugger issue */
+#define VC_CONTAINER_MODULE_T VC_CONTAINER_MODULE_MP4_READER_T
+#define VC_CONTAINER_TRACK_MODULE_T VC_CONTAINER_TRACK_MODULE_MP4_READER_T
+
+#define CONTAINER_IS_BIG_ENDIAN
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+#include "containers/mp4/mp4_common.h"
+#undef CONTAINER_HELPER_LOG_INDENT
+#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->box_level
+
+VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T *p_ctx );
+
+/******************************************************************************
+TODO:
+- aspect ratio
+- itunes gapless
+- edit list
+- subpicture track
+******************************************************************************/
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+#define MP4_TRACKS_MAX 16
+
+#define MP4_BOX_MIN_HEADER_SIZE 8
+#define MP4_MAX_BOX_SIZE (1<<29) /* Does not apply to the mdat box */
+#define MP4_MAX_BOX_LEVEL 10
+
+#define MP4_MAX_SAMPLES_BATCH_SIZE (16*1024)
+
+#define MP4_SKIP_U8(ctx,n) (size -= 1, SKIP_U8(ctx,n))
+#define MP4_SKIP_U16(ctx,n) (size -= 2, SKIP_U16(ctx,n))
+#define MP4_SKIP_U24(ctx,n) (size -= 3, SKIP_U24(ctx,n))
+#define MP4_SKIP_U32(ctx,n) (size -= 4, SKIP_U32(ctx,n))
+#define MP4_SKIP_U64(ctx,n) (size -= 8, SKIP_U64(ctx,n))
+#define MP4_READ_U8(ctx,n) (size -= 1, READ_U8(ctx,n))
+#define MP4_READ_U16(ctx,n) (size -= 2, READ_U16(ctx,n))
+#define MP4_READ_U24(ctx,n) (size -= 3, READ_U24(ctx,n))
+#define MP4_READ_U32(ctx,n) (size -= 4, READ_U32(ctx,n))
+#define MP4_READ_U64(ctx,n) (size -= 8, READ_U64(ctx,n))
+#define MP4_READ_FOURCC(ctx,n) (size -= 4, READ_FOURCC(ctx,n))
+#define MP4_SKIP_FOURCC(ctx,n) (size -= 4, SKIP_FOURCC(ctx,n))
+#define MP4_READ_BYTES(ctx,buffer,sz) (size -= sz, READ_BYTES(ctx,buffer,sz))
+#define MP4_SKIP_BYTES(ctx,sz) (size -= sz, SKIP_BYTES(ctx,sz))
+#define MP4_SKIP_STRING(ctx,sz,n) (size -= sz, SKIP_STRING(ctx,sz,n))
+
+/******************************************************************************
+Type definitions.
+******************************************************************************/
+typedef struct
+{
+ VC_CONTAINER_STATUS_T status;
+
+ int64_t duration;
+ int64_t pts;
+ int64_t dts;
+
+ uint32_t sample;
+ int64_t offset;
+ unsigned int sample_offset;
+ unsigned int sample_size;
+
+ uint32_t sample_duration;
+ uint32_t sample_duration_count;
+ int32_t sample_composition_offset;
+ uint32_t sample_composition_count;
+
+ uint32_t next_sync_sample;
+ bool keyframe;
+
+ uint32_t samples_per_chunk;
+ uint32_t chunks;
+ uint32_t samples_in_chunk;
+
+ struct {
+ uint32_t entry;
+ } sample_table[MP4_SAMPLE_TABLE_NUM];
+
+} MP4_READER_STATE_T;
+
+typedef struct VC_CONTAINER_TRACK_MODULE_T
+{
+ MP4_READER_STATE_T state;
+
+ int64_t timescale;
+ uint8_t object_type_indication;
+
+ uint32_t sample_size;
+ struct {
+ int64_t offset;
+ uint32_t entries;
+ uint32_t entry_size;
+ } sample_table[MP4_SAMPLE_TABLE_NUM];
+
+ uint32_t samples_batch_size;
+
+} VC_CONTAINER_TRACK_MODULE_T;
+
+typedef struct VC_CONTAINER_MODULE_T
+{
+ int64_t box_offset;
+ int box_level;
+
+ MP4_BRAND_T brand;
+
+ int64_t timescale;
+
+ VC_CONTAINER_TRACK_T *tracks[MP4_TRACKS_MAX];
+ unsigned int current_track;
+
+ bool found_moov;
+ int64_t data_offset;
+ int64_t data_size;
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Static functions within this file.
+******************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box( VC_CONTAINER_T *p_ctx, int64_t size, MP4_BOX_TYPE_T parent_type );
+static VC_CONTAINER_STATUS_T mp4_read_boxes( VC_CONTAINER_T *p_ctx, int64_t size, MP4_BOX_TYPE_T type );
+static VC_CONTAINER_STATUS_T mp4_read_box_ftyp( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_moov( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_mvhd( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_trak( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_tkhd( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_mdia( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_mdhd( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_hdlr( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_minf( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_vmhd( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_smhd( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_dinf( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_dref( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_stbl( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_stsd( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_stts( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_ctts( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_stsc( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_stsz( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_stco( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_co64( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_stss( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_vide( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_soun( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_text( VC_CONTAINER_T *p_ctx, int64_t size );
+
+static VC_CONTAINER_STATUS_T mp4_read_box_esds( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_vide_avcC( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_vide_d263( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_soun_damr( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_soun_dawp( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_soun_devc( VC_CONTAINER_T *p_ctx, int64_t size );
+static VC_CONTAINER_STATUS_T mp4_read_box_soun_wave( VC_CONTAINER_T *p_ctx, int64_t size );
+
+static struct {
+ const MP4_BOX_TYPE_T type;
+ VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T *, int64_t );
+ const MP4_BOX_TYPE_T parent_type;
+} mp4_box_list[] =
+{
+ {MP4_BOX_TYPE_FTYP, mp4_read_box_ftyp, MP4_BOX_TYPE_ROOT},
+ {MP4_BOX_TYPE_MDAT, 0, MP4_BOX_TYPE_ROOT},
+ {MP4_BOX_TYPE_MOOV, mp4_read_box_moov, MP4_BOX_TYPE_ROOT},
+ {MP4_BOX_TYPE_MVHD, mp4_read_box_mvhd, MP4_BOX_TYPE_MOOV},
+ {MP4_BOX_TYPE_TRAK, mp4_read_box_trak, MP4_BOX_TYPE_MOOV},
+ {MP4_BOX_TYPE_TKHD, mp4_read_box_tkhd, MP4_BOX_TYPE_TRAK},
+ {MP4_BOX_TYPE_MDIA, mp4_read_box_mdia, MP4_BOX_TYPE_TRAK},
+ {MP4_BOX_TYPE_MDHD, mp4_read_box_mdhd, MP4_BOX_TYPE_MDIA},
+ {MP4_BOX_TYPE_HDLR, mp4_read_box_hdlr, MP4_BOX_TYPE_MDIA},
+ {MP4_BOX_TYPE_MINF, mp4_read_box_minf, MP4_BOX_TYPE_MDIA},
+ {MP4_BOX_TYPE_VMHD, mp4_read_box_vmhd, MP4_BOX_TYPE_MINF},
+ {MP4_BOX_TYPE_SMHD, mp4_read_box_smhd, MP4_BOX_TYPE_MINF},
+ {MP4_BOX_TYPE_DINF, mp4_read_box_dinf, MP4_BOX_TYPE_MINF},
+ {MP4_BOX_TYPE_DREF, mp4_read_box_dref, MP4_BOX_TYPE_DINF},
+ {MP4_BOX_TYPE_STBL, mp4_read_box_stbl, MP4_BOX_TYPE_MINF},
+ {MP4_BOX_TYPE_STSD, mp4_read_box_stsd, MP4_BOX_TYPE_STBL},
+ {MP4_BOX_TYPE_STTS, mp4_read_box_stts, MP4_BOX_TYPE_STBL},
+ {MP4_BOX_TYPE_CTTS, mp4_read_box_ctts, MP4_BOX_TYPE_STBL},
+ {MP4_BOX_TYPE_STSC, mp4_read_box_stsc, MP4_BOX_TYPE_STBL},
+ {MP4_BOX_TYPE_STSZ, mp4_read_box_stsz, MP4_BOX_TYPE_STBL},
+ {MP4_BOX_TYPE_STCO, mp4_read_box_stco, MP4_BOX_TYPE_STBL},
+ {MP4_BOX_TYPE_CO64, mp4_read_box_co64, MP4_BOX_TYPE_STBL},
+ {MP4_BOX_TYPE_STSS, mp4_read_box_stss, MP4_BOX_TYPE_STBL},
+ {MP4_BOX_TYPE_VIDE, mp4_read_box_vide, MP4_BOX_TYPE_STSD},
+ {MP4_BOX_TYPE_SOUN, mp4_read_box_soun, MP4_BOX_TYPE_STSD},
+ {MP4_BOX_TYPE_TEXT, mp4_read_box_text, MP4_BOX_TYPE_STSD},
+
+ /* Codec specific boxes */
+ {MP4_BOX_TYPE_AVCC, mp4_read_box_vide_avcC, MP4_BOX_TYPE_VIDE},
+ {MP4_BOX_TYPE_D263, mp4_read_box_vide_d263, MP4_BOX_TYPE_VIDE},
+ {MP4_BOX_TYPE_ESDS, mp4_read_box_esds, MP4_BOX_TYPE_VIDE},
+ {MP4_BOX_TYPE_DAMR, mp4_read_box_soun_damr, MP4_BOX_TYPE_SOUN},
+ {MP4_BOX_TYPE_DAWP, mp4_read_box_soun_dawp, MP4_BOX_TYPE_SOUN},
+ {MP4_BOX_TYPE_DEVC, mp4_read_box_soun_devc, MP4_BOX_TYPE_SOUN},
+ {MP4_BOX_TYPE_WAVE, mp4_read_box_soun_wave, MP4_BOX_TYPE_SOUN},
+ {MP4_BOX_TYPE_ESDS, mp4_read_box_esds, MP4_BOX_TYPE_SOUN},
+
+ {MP4_BOX_TYPE_UNKNOWN, 0, MP4_BOX_TYPE_UNKNOWN}
+};
+
+static struct {
+ const VC_CONTAINER_FOURCC_T type;
+ const VC_CONTAINER_FOURCC_T codec;
+ bool batch;
+} mp4_codec_mapping[] =
+{
+ {VC_FOURCC('a','v','c','1'), VC_CONTAINER_CODEC_H264, 0},
+ {VC_FOURCC('m','p','4','v'), VC_CONTAINER_CODEC_MP4V, 0},
+ {VC_FOURCC('s','2','6','3'), VC_CONTAINER_CODEC_H263, 0},
+ {VC_FOURCC('m','p','e','g'), VC_CONTAINER_CODEC_MP2V, 0},
+ {VC_FOURCC('m','j','p','a'), VC_CONTAINER_CODEC_MJPEGA, 0},
+ {VC_FOURCC('m','j','p','b'), VC_CONTAINER_CODEC_MJPEGB, 0},
+
+ {VC_FOURCC('j','p','e','g'), VC_CONTAINER_CODEC_JPEG, 0},
+
+ {VC_FOURCC('m','p','4','a'), VC_CONTAINER_CODEC_MP4A, 0},
+ {VC_FOURCC('s','a','m','r'), VC_CONTAINER_CODEC_AMRNB, 0},
+ {VC_FOURCC('s','a','w','b'), VC_CONTAINER_CODEC_AMRWB, 0},
+ {VC_FOURCC('s','a','w','p'), VC_CONTAINER_CODEC_AMRWBP, 0},
+ {VC_FOURCC('a','c','-','3'), VC_CONTAINER_CODEC_AC3, 0},
+ {VC_FOURCC('e','c','-','3'), VC_CONTAINER_CODEC_EAC3, 0},
+ {VC_FOURCC('s','e','v','c'), VC_CONTAINER_CODEC_EVRC, 0},
+ {VC_FOURCC('e','v','r','c'), VC_CONTAINER_CODEC_EVRC, 0},
+ {VC_FOURCC('s','q','c','p'), VC_CONTAINER_CODEC_QCELP, 0},
+ {VC_FOURCC('a','l','a','w'), VC_CONTAINER_CODEC_ALAW, 1},
+ {VC_FOURCC('u','l','a','w'), VC_CONTAINER_CODEC_MULAW, 1},
+ {VC_FOURCC('t','w','o','s'), VC_CONTAINER_CODEC_PCM_SIGNED_BE, 1},
+ {VC_FOURCC('s','o','w','t'), VC_CONTAINER_CODEC_PCM_SIGNED_LE, 1},
+
+ {0, 0},
+};
+static VC_CONTAINER_FOURCC_T mp4_box_type_to_codec(VC_CONTAINER_FOURCC_T type)
+{
+ int i;
+ for(i = 0; mp4_codec_mapping[i].type; i++ )
+ if(mp4_codec_mapping[i].type == type) break;
+ return mp4_codec_mapping[i].codec;
+}
+
+static bool codec_needs_batch_mode(VC_CONTAINER_FOURCC_T codec)
+{
+ int i;
+ for(i = 0; mp4_codec_mapping[i].codec; i++ )
+ if(mp4_codec_mapping[i].codec == codec) break;
+ return mp4_codec_mapping[i].batch;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_header( VC_CONTAINER_T *p_ctx, int64_t size,
+ MP4_BOX_TYPE_T *box_type, int64_t *box_size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ int64_t offset = STREAM_POSITION(p_ctx);
+
+ module->box_offset = offset;
+
+ *box_size = _READ_U32(p_ctx);
+ *box_type = _READ_FOURCC(p_ctx);
+ if(!*box_type) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ if(*box_size == 1) *box_size = _READ_U64(p_ctx);
+ LOG_FORMAT(p_ctx, "- Box %4.4s, Size: %"PRIi64", Offset: %"PRIi64,
+ (const char *)box_type, *box_size, offset);
+
+ /* Sanity check the box size */
+ if(*box_size < 0 /* Shouldn't ever get that big */ ||
+ /* Only the mdat box can really be massive */
+ (*box_type != MP4_BOX_TYPE_MDAT && *box_size > MP4_MAX_BOX_SIZE))
+ {
+ LOG_DEBUG(p_ctx, "box %4.4s has an invalid size (%"PRIi64")",
+ (const char *)box_type, *box_size);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+#if 0
+ /* It is valid for a box to have a zero size (i.e unknown) if it is the last one */
+ if(*box_size == 0 && size >= 0) *box_size = size;
+ else if(*box_size == 0) *box_size = INT64_C(-1);
+#else
+ if(*box_size <= 0)
+ {
+ LOG_DEBUG(p_ctx, "box %4.4s has an invalid size (%"PRIi64")",
+ (const char *)box_type, *box_size);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+#endif
+
+ /* Sanity check box size against parent */
+ if(size >= 0 && *box_size > size)
+ {
+ LOG_DEBUG(p_ctx, "box %4.4s is bigger than it should (%"PRIi64" > %"PRIi64")",
+ (const char *)box_type, *box_size, size);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ *box_size -= (STREAM_POSITION(p_ctx) - offset);
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_data( VC_CONTAINER_T *p_ctx,
+ MP4_BOX_TYPE_T box_type, int64_t box_size, MP4_BOX_TYPE_T parent_type )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ int64_t offset = STREAM_POSITION(p_ctx);
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ unsigned int i;
+
+ /* Check if the box is a recognised one */
+ for(i = 0; mp4_box_list[i].type; i++)
+ if(mp4_box_list[i].type == box_type &&
+ mp4_box_list[i].parent_type == parent_type) break;
+ if(mp4_box_list[i].type == MP4_BOX_TYPE_UNKNOWN)
+ for(i = 0; mp4_box_list[i].type; i++)
+ if(mp4_box_list[i].type == box_type) break;
+
+ /* Sanity check that the box has the right parent */
+ if(mp4_box_list[i].type != MP4_BOX_TYPE_UNKNOWN &&
+ mp4_box_list[i].parent_type != MP4_BOX_TYPE_UNKNOWN &&
+ parent_type != MP4_BOX_TYPE_UNKNOWN && parent_type != mp4_box_list[i].parent_type)
+ {
+ LOG_FORMAT(p_ctx, "Ignoring mis-placed box %4.4s", (const char *)&box_type);
+ goto skip;
+ }
+
+ /* Sanity check that the element isn't too deeply nested */
+ if(module->box_level >= 2 * MP4_MAX_BOX_LEVEL)
+ {
+ LOG_DEBUG(p_ctx, "box %4.4s is too deep. skipping", (const char *)&box_type);
+ goto skip;
+ }
+
+ module->box_level++;
+
+ /* Call the box specific parsing function */
+ if(mp4_box_list[i].pf_func)
+ status = mp4_box_list[i].pf_func(p_ctx, box_size);
+
+ module->box_level--;
+
+ if(status != VC_CONTAINER_SUCCESS)
+ LOG_DEBUG(p_ctx, "box %4.4s appears to be corrupted (%i)", (const char *)&box_type, status);
+
+ skip:
+ /* Skip the rest of the box */
+ box_size -= (STREAM_POSITION(p_ctx) - offset);
+ if(box_size < 0) /* Check for overruns */
+ {
+ /* Things have gone really bad here and we ended up reading past the end of the
+ * box. We could maybe try to be clever and recover by seeking back to the end
+ * of the box. However if we get there, the file is clearly corrupted so there's
+ * no guarantee it would work anyway. */
+ LOG_DEBUG(p_ctx, "%"PRIi64" bytes overrun past the end of box %4.4s",
+ -box_size, (const char *)&box_type);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ if(box_size)
+ LOG_FORMAT(p_ctx, "%"PRIi64" bytes left unread in box %4.4s",
+ box_size, (const char *)&box_type );
+
+ if(box_size < MP4_MAX_BOX_SIZE) box_size = SKIP_BYTES(p_ctx, box_size);
+ else SEEK(p_ctx, STREAM_POSITION(p_ctx) + box_size);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box( VC_CONTAINER_T *p_ctx, int64_t size,
+ MP4_BOX_TYPE_T parent_type )
+{
+ VC_CONTAINER_STATUS_T status;
+ MP4_BOX_TYPE_T box_type;
+ int64_t box_size;
+
+ status = mp4_read_box_header( p_ctx, size, &box_type, &box_size );
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ return mp4_read_box_data( p_ctx, box_type, box_size, parent_type );
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_boxes( VC_CONTAINER_T *p_ctx, int64_t size,
+ MP4_BOX_TYPE_T type)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t offset = STREAM_POSITION(p_ctx);
+ bool unknown_size = size < 0;
+
+ /* Read contained boxes */
+ module->box_level++;
+ while(status == VC_CONTAINER_SUCCESS &&
+ (unknown_size || size >= MP4_BOX_MIN_HEADER_SIZE))
+ {
+ offset = STREAM_POSITION(p_ctx);
+ status = mp4_read_box(p_ctx, size, type);
+ if(!unknown_size) size -= (STREAM_POSITION(p_ctx) - offset);
+ }
+ module->box_level--;
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_ftyp( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ module->brand = MP4_READ_FOURCC(p_ctx, "major_brand");
+ MP4_SKIP_U32(p_ctx, "minor_version");
+ while(size >= 4) MP4_SKIP_FOURCC(p_ctx, "compatible_brands");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_moov( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_MOOV);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_mvhd( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ uint32_t version, i;
+ int64_t duration;
+
+ version = MP4_READ_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ if(version)
+ {
+ MP4_SKIP_U64(p_ctx, "creation_time");
+ MP4_SKIP_U64(p_ctx, "modification_time");
+ module->timescale = MP4_READ_U32(p_ctx, "timescale");
+ duration = MP4_READ_U64(p_ctx, "duration");
+ }
+ else
+ {
+ MP4_SKIP_U32(p_ctx, "creation_time");
+ MP4_SKIP_U32(p_ctx, "modification_time");
+ module->timescale = MP4_READ_U32(p_ctx, "timescale");
+ duration = MP4_READ_U32(p_ctx, "duration");
+ }
+
+ if(module->timescale)
+ p_ctx->duration = duration * 1000000 / module->timescale;
+
+ MP4_SKIP_U32(p_ctx, "rate");
+ MP4_SKIP_U16(p_ctx, "volume");
+ MP4_SKIP_U16(p_ctx, "reserved");
+ for(i = 0; i < 2; i++) MP4_SKIP_U32(p_ctx, "reserved");
+ for(i = 0; i < 9; i++) MP4_SKIP_U32(p_ctx, "matrix");
+ for(i = 0; i < 6; i++) MP4_SKIP_U32(p_ctx, "pre_defined");
+ MP4_SKIP_U32(p_ctx, "next_track_ID");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_trak( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track;
+
+ /* We have a new track. Allocate and initialise our track context */
+ if(p_ctx->tracks_num >= MP4_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ p_ctx->tracks[p_ctx->tracks_num] = track =
+ vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
+ if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size = 8;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size = 4;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size = 12;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size = 4;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size = 4;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_CO64].entry_size = 8;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_CTTS].entry_size = 8;
+
+ status = mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_TRAK);
+
+ /* TODO: Sanity check track */
+
+ track->is_enabled = true;
+ track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+ module->current_track++;
+ p_ctx->tracks_num++;
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_tkhd( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ uint32_t i, version;
+ int64_t duration;
+
+ version = MP4_READ_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ if(version)
+ {
+ MP4_SKIP_U64(p_ctx, "creation_time");
+ MP4_SKIP_U64(p_ctx, "modification_time");
+ MP4_SKIP_U32(p_ctx, "track_ID");
+ MP4_SKIP_U32(p_ctx, "reserved");
+ duration = MP4_READ_U64(p_ctx, "duration");
+ }
+ else
+ {
+ MP4_SKIP_U32(p_ctx, "creation_time");
+ MP4_SKIP_U32(p_ctx, "modification_time");
+ MP4_SKIP_U32(p_ctx, "track_ID");
+ MP4_SKIP_U32(p_ctx, "reserved");
+ duration = MP4_READ_U32(p_ctx, "duration");
+ }
+
+ if(module->timescale)
+ duration = duration * 1000000 / module->timescale;
+
+ for(i = 0; i < 2; i++) MP4_SKIP_U32(p_ctx, "reserved");
+ MP4_SKIP_U16(p_ctx, "layer");
+ MP4_SKIP_U16(p_ctx, "alternate_group");
+ MP4_SKIP_U16(p_ctx, "volume");
+ MP4_SKIP_U16(p_ctx, "reserved");
+ for(i = 0; i < 9; i++) MP4_SKIP_U32(p_ctx, "matrix");
+
+ MP4_SKIP_U32(p_ctx, "width");
+ MP4_SKIP_U32(p_ctx, "height");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_mdia( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_MDIA);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_mdhd( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
+ uint32_t version, timescale;
+ int64_t duration;
+
+ version = MP4_READ_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ if(version)
+ {
+ MP4_SKIP_U64(p_ctx, "creation_time");
+ MP4_SKIP_U64(p_ctx, "modification_time");
+ timescale = MP4_READ_U32(p_ctx, "timescale");
+ duration = MP4_READ_U64(p_ctx, "duration");
+ }
+ else
+ {
+ MP4_SKIP_U32(p_ctx, "creation_time");
+ MP4_SKIP_U32(p_ctx, "modification_time");
+ timescale = MP4_READ_U32(p_ctx, "timescale");
+ duration = MP4_READ_U32(p_ctx, "duration");
+ }
+
+ if(timescale) duration = duration * 1000000 / timescale;
+ track_module->timescale = timescale;
+
+ MP4_SKIP_U16(p_ctx, "language"); /* ISO-639-2/T language code */
+ MP4_SKIP_U16(p_ctx, "pre_defined");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_hdlr( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ uint32_t i, fourcc, string_size;
+ VC_CONTAINER_ES_TYPE_T es_type = VC_CONTAINER_ES_TYPE_UNKNOWN;
+
+ if(size <= 24) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ MP4_SKIP_U32(p_ctx, "pre-defined");
+
+ fourcc = MP4_READ_FOURCC(p_ctx, "handler_type");
+ if(fourcc == MP4_BOX_TYPE_VIDE) es_type = VC_CONTAINER_ES_TYPE_VIDEO;
+ if(fourcc == MP4_BOX_TYPE_SOUN) es_type = VC_CONTAINER_ES_TYPE_AUDIO;
+ if(fourcc == MP4_BOX_TYPE_TEXT) es_type = VC_CONTAINER_ES_TYPE_SUBPICTURE;
+ track->format->es_type = es_type;
+
+ for(i = 0; i < 3; i++) MP4_SKIP_U32(p_ctx, "reserved");
+
+ string_size = size;
+ if(module->brand == MP4_BRAND_QT)
+ string_size = MP4_READ_U8(p_ctx, "string_size");
+
+ if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(string_size > size) string_size = size;
+
+ MP4_SKIP_STRING(p_ctx, string_size, "name");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_minf( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_MINF);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_vmhd( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ MP4_SKIP_U16(p_ctx, "graphicsmode");
+ MP4_SKIP_U16(p_ctx, "opcolor");
+ MP4_SKIP_U16(p_ctx, "opcolor");
+ MP4_SKIP_U16(p_ctx, "opcolor");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_smhd( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ MP4_SKIP_U16(p_ctx, "balance");
+ MP4_SKIP_U16(p_ctx, "reserved");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_dinf( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_DINF);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_dref( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ MP4_SKIP_U32(p_ctx, "entry_count");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_stbl( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_STBL);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_stsd( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ VC_CONTAINER_STATUS_T status;
+ MP4_BOX_TYPE_T box_type;
+ int64_t box_size;
+ uint32_t count;
+
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ count = MP4_READ_U32(p_ctx, "entry_count");
+ if(!count) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ status = mp4_read_box_header( p_ctx, size, &box_type, &box_size );
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ track->format->codec = mp4_box_type_to_codec(box_type);
+ if(!track->format->codec) track->format->codec = box_type;
+
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) box_type = MP4_BOX_TYPE_VIDE;
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) box_type = MP4_BOX_TYPE_SOUN;
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) box_type = MP4_BOX_TYPE_TEXT;
+ status = mp4_read_box_data( p_ctx, box_type, box_size, MP4_BOX_TYPE_STSD );
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ /* Special treatment for MPEG4 */
+ if(track->format->codec == VC_CONTAINER_CODEC_MP4A)
+ {
+ switch (track->priv->module->object_type_indication)
+ {
+ case MP4_MPEG4_AAC_LC_OBJECT_TYPE:
+ case MP4_MPEG2_AAC_LC_OBJECT_TYPE:
+ track->format->codec = VC_CONTAINER_CODEC_MP4A; break;
+ case MP4_MP3_OBJECT_TYPE:
+ case MP4_MPEG1_AUDIO_OBJECT_TYPE:
+ case MP4_KTF_MP3_OBJECT_TYPE:
+ track->format->codec = VC_CONTAINER_CODEC_MPGA; break;
+ case MP4_SKT_EVRC_2V1_OBJECT_TYPE:
+ case MP4_SKT_EVRC_OBJECT_TYPE:
+ track->format->codec = VC_CONTAINER_CODEC_EVRC; break;
+ case MP4_3GPP2_QCELP_OBJECT_TYPE:
+ track->format->codec = VC_CONTAINER_CODEC_QCELP; break;
+ default:
+ track->format->codec = VC_CONTAINER_CODEC_UNKNOWN; break;
+ }
+ }
+ else if(track->format->codec == VC_CONTAINER_CODEC_MP4V)
+ {
+ switch (track->priv->module->object_type_indication)
+ {
+ case MP4_MPEG4_VISUAL_OBJECT_TYPE:
+ track->format->codec = VC_CONTAINER_CODEC_MP4V; break;
+ case MP4_JPEG_OBJECT_TYPE:
+ track->format->codec = VC_CONTAINER_CODEC_JPEG; break;
+ case MP4_MPEG2_SP_OBJECT_TYPE:
+ case MP4_MPEG2_SNR_OBJECT_TYPE:
+ case MP4_MPEG2_AAC_LC_OBJECT_TYPE:
+ case MP4_MPEG2_MP_OBJECT_TYPE:
+ track->format->codec = VC_CONTAINER_CODEC_MP2V; break;
+ case MP4_MPEG1_VISUAL_OBJECT_TYPE:
+ track->format->codec = VC_CONTAINER_CODEC_MP1V; break;
+ default:
+ track->format->codec = VC_CONTAINER_CODEC_UNKNOWN; break;
+ }
+ }
+
+ /* For some codecs we process the samples in batches to be more efficient */
+ if(codec_needs_batch_mode(track->format->codec))
+ track->priv->module->samples_batch_size = MP4_MAX_SAMPLES_BATCH_SIZE;
+
+ /* Fix-up some of the data */
+ switch(track->format->codec)
+ {
+ case VC_CONTAINER_CODEC_ALAW:
+ case VC_CONTAINER_CODEC_MULAW:
+ track->format->type->audio.bits_per_sample = 8;
+ track->priv->module->sample_size = track->format->type->audio.channels;
+ break;
+ case VC_CONTAINER_CODEC_PCM_SIGNED_LE:
+ case VC_CONTAINER_CODEC_PCM_SIGNED_BE:
+ track->priv->module->sample_size = (track->format->type->audio.bits_per_sample + 7) /
+ 8 * track->format->type->audio.channels;
+ break;
+ case VC_CONTAINER_CODEC_MP4A:
+ /* samplerate / channels is sometimes invalid so sanity check it using the codec config data */
+ if(track->format->extradata_size >= 2)
+ {
+ static unsigned int rate[] =
+ { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
+ 16000, 12000, 11025, 8000, 7350 };
+ unsigned int samplerate = 0, channels = 0;
+ uint8_t *p = track->format->extradata;
+ uint32_t index = (p[0] & 7) << 1 | (p[1] >> 7);
+ if(index == 15 && track->format->extradata_size >= 5)
+ {
+ samplerate = (p[1] & 0x7f) << 17 | (p[2] << 9) | (p[3] << 1) | (p[4] >> 7);
+ channels = (p[4] >> 3) & 15;
+ }
+ else if(index < 13)
+ {
+ samplerate = rate[index];
+ channels = (p[1] >> 3) & 15;;
+ }
+
+ if(samplerate && samplerate != track->format->type->audio.sample_rate &&
+ 2 * samplerate != track->format->type->audio.sample_rate)
+ track->format->type->audio.sample_rate = samplerate;
+ if(channels && channels != track->format->type->audio.channels &&
+ 2 * channels != track->format->type->audio.channels)
+ track->format->type->audio.channels = channels;
+ }
+ break;
+ default: break;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_cache_table( VC_CONTAINER_T *p_ctx, MP4_SAMPLE_TABLE_T table,
+ uint32_t entries, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
+ uint32_t available_entries, entries_size;
+
+ if(size < 0) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ track_module->sample_table[table].offset = STREAM_POSITION(p_ctx);
+ track_module->sample_table[table].entries = entries;
+
+ available_entries = size / track_module->sample_table[table].entry_size;
+ if(available_entries < entries)
+ {
+ LOG_DEBUG(p_ctx, "table has less entries than advertised (%i/%i)", available_entries, entries);
+ entries = available_entries;
+ }
+
+ entries_size = entries * track_module->sample_table[table].entry_size;
+ size = vc_container_io_cache(p_ctx->priv->io, entries_size );
+ if(size != entries_size)
+ {
+ available_entries = size / track_module->sample_table[table].entry_size;
+ LOG_DEBUG(p_ctx, "cached less table entries than advertised (%i/%i)", available_entries, entries);
+ track_module->sample_table[table].entries = available_entries;
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_stts( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint32_t entries;
+
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ entries = MP4_READ_U32(p_ctx, "entry_count");
+ return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STTS, entries, size );
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_ctts( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint32_t entries;
+
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ entries = MP4_READ_U32(p_ctx, "entry_count");
+ return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_CTTS, entries, size );
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_stsc( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint32_t entries;
+
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ entries = MP4_READ_U32(p_ctx, "entry_count");
+ return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STSC, entries, size );
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_stsz( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
+ uint32_t entries;
+
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ track_module->sample_size = READ_U32(p_ctx, "sample_size");
+ if(track_module->sample_size) return STREAM_STATUS(p_ctx);
+
+ entries = MP4_READ_U32(p_ctx, "sample_count");
+ return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STSZ, entries, size );
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_stco( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint32_t entries;
+
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ entries = MP4_READ_U32(p_ctx, "entry_count");
+ return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STCO, entries, size );
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_co64( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint32_t entries;
+
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ entries = MP4_READ_U32(p_ctx, "entry_count");
+ return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_CO64, entries, size );
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_stss( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ uint32_t entries;
+
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ entries = MP4_READ_U32(p_ctx, "entry_count");
+ return mp4_cache_table( p_ctx, MP4_SAMPLE_TABLE_STSS, entries, size );
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_esds_descriptor_header(VC_CONTAINER_T *p_ctx, int64_t *size,
+ uint32_t *descriptor_length, uint8_t *descriptor_type)
+{
+ uint32_t byte, length = 0;
+
+ if(*size <= 0) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ *descriptor_type = _READ_U8(p_ctx);
+ (*size)--;
+
+ /* Read descriptor size */
+ while(*size)
+ {
+ byte = _READ_U8(p_ctx);
+ (*size)--;
+ length = (length << 7) | (byte&0x7F);
+ if(!(byte & 0x80)) break;
+ }
+
+ if(*size <= 0 || length > *size)
+ {
+ LOG_FORMAT(p_ctx, "esds descriptor is corrupted");
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ *descriptor_length = length;
+ LOG_FORMAT(p_ctx, "esds descriptor %x, size %i", *descriptor_type, *descriptor_length);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_esds( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ VC_CONTAINER_STATUS_T status;
+ uint32_t descriptor_length;
+ uint8_t descriptor_type;
+
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U24(p_ctx, "flags");
+
+ status = mp4_read_esds_descriptor_header(p_ctx, &size, &descriptor_length, &descriptor_type);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ if(descriptor_type == 0x3) /* ES descriptor */
+ {
+ uint8_t flags;
+
+ MP4_SKIP_U16(p_ctx, "es_id");
+ flags = MP4_READ_U8(p_ctx, "flags");
+
+ if(flags & 0x80) /* Stream dependence */
+ MP4_SKIP_U16(p_ctx, "depend_on_es_id");
+
+ if(flags & 0x40) /* URL */
+ {
+ uint32_t url_size = MP4_READ_U8(p_ctx, "url_size");
+ MP4_SKIP_STRING(p_ctx, url_size, "url");
+ }
+
+ if(flags & 0x20) /* OCR_stream*/
+ MP4_SKIP_U16(p_ctx, "OCR_es_id");
+
+ status = mp4_read_esds_descriptor_header(p_ctx, &size, &descriptor_length, &descriptor_type);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ }
+
+ if(descriptor_type == 0x4) /* Decoder Config descriptor */
+ {
+ track->priv->module->object_type_indication = MP4_READ_U8(p_ctx, "object_type_indication");
+ MP4_SKIP_U8(p_ctx, "stream_type");
+ MP4_SKIP_U24(p_ctx, "buffer_size_db");
+ MP4_SKIP_U32(p_ctx, "max_bitrate");
+ track->format->bitrate = MP4_READ_U32(p_ctx, "avg_bitrate");
+
+ if(size <= 0 || descriptor_length <= 13) return STREAM_STATUS(p_ctx);
+
+ status = mp4_read_esds_descriptor_header(p_ctx, &size, &descriptor_length, &descriptor_type);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ if(descriptor_type == 0x05 && descriptor_length)
+ {
+ status = vc_container_track_allocate_extradata(p_ctx, track, descriptor_length);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ track->format->extradata_size = MP4_READ_BYTES(p_ctx, track->format->extradata, descriptor_length);
+ }
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_vide( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ unsigned int i;
+
+ for(i = 0; i < 6; i++) MP4_SKIP_U8(p_ctx, "reserved");
+ MP4_SKIP_U16(p_ctx, "data_reference_index");
+
+ MP4_SKIP_U16(p_ctx, "pre_defined");
+ MP4_SKIP_U16(p_ctx, "reserved");
+ for(i = 0; i < 3; i++) MP4_SKIP_U32(p_ctx, "pre_defined");
+ track->format->type->video.width = MP4_READ_U16(p_ctx, "width");
+ track->format->type->video.height = MP4_READ_U16(p_ctx, "height");
+ MP4_SKIP_U32(p_ctx, "horizresolution"); /* dpi */
+ MP4_SKIP_U32(p_ctx, "vertresolution"); /* dpi */
+ MP4_SKIP_U32(p_ctx, "reserved");
+ MP4_SKIP_U16(p_ctx, "frame_count");
+ MP4_SKIP_BYTES(p_ctx, 32);
+ MP4_SKIP_U16(p_ctx, "depth");
+ MP4_SKIP_U16(p_ctx, "pre_defined");
+
+ if(size > 0)
+ return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_VIDE );
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_vide_avcC( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ VC_CONTAINER_STATUS_T status;
+
+ if(track->format->codec != VC_CONTAINER_CODEC_H264 || size <= 0)
+ return VC_CONTAINER_ERROR_CORRUPTED;
+
+ track->format->codec_variant = VC_FOURCC('a','v','c','C');
+
+ status = vc_container_track_allocate_extradata(p_ctx, track, (unsigned int)size);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ track->format->extradata_size = READ_BYTES(p_ctx, track->format->extradata, size);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_vide_d263( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ MP4_SKIP_FOURCC(p_ctx, "vendor");
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U8(p_ctx, "level");
+ MP4_SKIP_U8(p_ctx, "profile");
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_soun( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ unsigned int i, version = 0;
+
+ for(i = 0; i < 6; i++) MP4_SKIP_U8(p_ctx, "reserved");
+ MP4_SKIP_U16(p_ctx, "data_reference_index");
+
+ version = MP4_READ_U16(p_ctx, "version");
+ MP4_SKIP_U16(p_ctx, "revision_level");
+ MP4_SKIP_U32(p_ctx, "vendor");
+
+ track->format->type->audio.channels = MP4_READ_U16(p_ctx, "channelcount");
+ track->format->type->audio.bits_per_sample = MP4_READ_U16(p_ctx, "samplesize");
+ MP4_SKIP_U16(p_ctx, "pre_defined");
+ MP4_SKIP_U16(p_ctx, "reserved");
+ track->format->type->audio.sample_rate = MP4_READ_U16(p_ctx, "samplerate");
+ MP4_SKIP_U16(p_ctx, "samplerate_fp_low");
+
+ if(version == 1)
+ {
+ MP4_SKIP_U32(p_ctx, "samples_per_packet");
+ MP4_SKIP_U32(p_ctx, "bytes_per_packet");
+ MP4_SKIP_U32(p_ctx, "bytes_per_frame");
+ MP4_SKIP_U32(p_ctx, "bytes_per_sample");
+ }
+
+ if(size > 0)
+ return mp4_read_box( p_ctx, size, MP4_BOX_TYPE_SOUN );
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_soun_damr( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+
+ MP4_SKIP_FOURCC(p_ctx, "vendor");
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U8(p_ctx, "mode_set");
+ MP4_SKIP_U8(p_ctx, "mode_change_period");
+ MP4_SKIP_U8(p_ctx, "frame_per_second");
+
+ track->format->type->audio.channels = 1;
+ if(track->format->codec == VC_CONTAINER_CODEC_AMRNB)
+ track->format->type->audio.sample_rate = 8000;
+ else if(track->format->codec == VC_CONTAINER_CODEC_AMRWB)
+ track->format->type->audio.sample_rate = 16000;
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_soun_dawp( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+
+ MP4_SKIP_FOURCC(p_ctx, "vendor");
+ MP4_SKIP_U8(p_ctx, "version");
+
+ track->format->type->audio.channels = 2;
+ track->format->type->audio.sample_rate = 16000;
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_soun_devc( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+
+ MP4_SKIP_FOURCC(p_ctx, "vendor");
+ MP4_SKIP_U8(p_ctx, "version");
+ MP4_SKIP_U8(p_ctx, "samples_per_frame");
+
+ track->format->type->audio.channels = 1;
+ track->format->type->audio.sample_rate = 8000;
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_soun_wave( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ return mp4_read_boxes( p_ctx, size, MP4_BOX_TYPE_SOUN);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_box_text( VC_CONTAINER_T *p_ctx, int64_t size )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_PARAM_UNUSED(module);
+
+ /* TODO */if(1) return VC_CONTAINER_ERROR_FAILED;
+
+ if(size > 0)
+ return mp4_read_box( p_ctx, size, MP4_BOX_TYPE_TEXT );
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_reader_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int i;
+
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ vc_container_free_track(p_ctx, p_ctx->tracks[i]);
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+#ifdef ENABLE_MP4_READER_LOG_STATE
+static void mp4_log_state( VC_CONTAINER_T *p_ctx, MP4_READER_STATE_T *state )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+
+ LOG_DEBUG(p_ctx, "state:");
+ LOG_DEBUG(p_ctx, "duration: %i, pts %i, dts %i", (int)state->duration,
+ (int)state->pts, (int)state->dts);
+ LOG_DEBUG(p_ctx, "sample: %i, offset %i, sample_offset %i, sample_size %i",
+ state->sample, (int)state->offset, state->sample_offset,
+ state->sample_size);
+ LOG_DEBUG(p_ctx, "sample_duration: %i, count %i",
+ state->sample_duration, state->sample_duration_count);
+ LOG_DEBUG(p_ctx, "sample_composition_offset: %i, count %i",
+ state->sample_composition_offset, state->sample_composition_count);
+ LOG_DEBUG(p_ctx, "next_sync_sample: %i, keyframe %i",
+ state->next_sync_sample, state->keyframe);
+ LOG_DEBUG(p_ctx, "samples_per_chunk: %i, chunks %i, samples_in_chunk %i",
+ state->samples_per_chunk, state->chunks, state->samples_in_chunk);
+ LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STTS %i", state->sample_table[MP4_SAMPLE_TABLE_STTS].entry);
+ LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STSZ %i", state->sample_table[MP4_SAMPLE_TABLE_STSZ].entry);
+ LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STSC %i", state->sample_table[MP4_SAMPLE_TABLE_STSC].entry);
+ LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_STCO %i", state->sample_table[MP4_SAMPLE_TABLE_STCO].entry);
+ LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_CO64 %i", state->sample_table[MP4_SAMPLE_TABLE_CO64].entry);
+ LOG_DEBUG(p_ctx, "MP4_SAMPLE_TABLE_CTTS %i", state->sample_table[MP4_SAMPLE_TABLE_CTTS].entry);
+}
+#endif /* ENABLE_MP4_READER_LOG_STATE */
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_seek_sample_table( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_MODULE_T *track_module, MP4_READER_STATE_T *state,
+ MP4_SAMPLE_TABLE_T table)
+{
+ int64_t seek_offset;
+
+ /* Seek to the next entry in the table */
+ if(state->sample_table[table].entry >= track_module->sample_table[table].entries)
+ return VC_CONTAINER_ERROR_EOS;
+
+ seek_offset = track_module->sample_table[table].offset +
+ track_module->sample_table[table].entry_size * state->sample_table[table].entry;
+
+ return SEEK(p_ctx, seek_offset);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_sample_table( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_MODULE_T *track_module, MP4_READER_STATE_T *state,
+ MP4_SAMPLE_TABLE_T table, unsigned int seek)
+{
+ uint32_t value;
+
+ if(table == MP4_SAMPLE_TABLE_STSZ && track_module->sample_size)
+ {
+ state->sample_size = track_module->sample_size;
+ return state->status;
+ }
+
+ /* CO64 support */
+ if(table == MP4_SAMPLE_TABLE_STCO &&
+ track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries)
+ table = MP4_SAMPLE_TABLE_CO64;
+
+ /* Seek to the next entry in the table */
+ if(seek)
+ {
+ state->status = mp4_seek_sample_table( p_ctx, track_module, state, table );
+ if(state->status != VC_CONTAINER_SUCCESS) return state->status;
+ }
+
+ switch(table)
+ {
+ case MP4_SAMPLE_TABLE_STSZ:
+ state->sample_size = _READ_U32(p_ctx);
+ state->status = STREAM_STATUS(p_ctx);
+ break;
+
+ case MP4_SAMPLE_TABLE_STTS:
+ state->sample_duration_count = _READ_U32(p_ctx);
+ state->sample_duration = _READ_U32(p_ctx);
+ state->status = STREAM_STATUS(p_ctx);
+ if(state->status != VC_CONTAINER_SUCCESS) break;
+ if(!state->sample_duration_count) state->status = VC_CONTAINER_ERROR_CORRUPTED;
+ break;
+
+ case MP4_SAMPLE_TABLE_CTTS:
+ state->sample_composition_count = _READ_U32(p_ctx);
+ state->sample_composition_offset = _READ_U32(p_ctx); /* Converted to signed */
+ state->status = STREAM_STATUS(p_ctx);
+ if(state->status != VC_CONTAINER_SUCCESS) break;
+ if(!state->sample_composition_count) state->status = VC_CONTAINER_ERROR_CORRUPTED;
+ break;
+
+ case MP4_SAMPLE_TABLE_STSC:
+ state->chunks = _READ_U32(p_ctx);
+ state->samples_per_chunk = _READ_U32(p_ctx);
+ _SKIP_U32(p_ctx);
+ state->status = STREAM_STATUS(p_ctx);
+ if(state->status != VC_CONTAINER_SUCCESS) break;
+
+ if(state->sample_table[table].entry + 1 <
+ track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries) value = _READ_U32(p_ctx);
+ else value = -1;
+
+ if(!state->chunks || !state->samples_per_chunk || state->chunks >= value )
+ {state->status = VC_CONTAINER_ERROR_CORRUPTED; break;}
+ state->chunks = value - state->chunks;
+ state->samples_in_chunk = state->samples_per_chunk;
+ break;
+
+ case MP4_SAMPLE_TABLE_STCO:
+ case MP4_SAMPLE_TABLE_CO64:
+ state->offset = table == MP4_SAMPLE_TABLE_STCO ? _READ_U32(p_ctx) : _READ_U64(p_ctx);
+ state->status = STREAM_STATUS(p_ctx);
+ if(state->status != VC_CONTAINER_SUCCESS) break;
+ if(!state->offset) state->status = VC_CONTAINER_ERROR_CORRUPTED;
+ state->samples_in_chunk = state->samples_per_chunk;
+ break;
+
+ case MP4_SAMPLE_TABLE_STSS:
+ state->next_sync_sample = _READ_U32(p_ctx);
+ state->status = STREAM_STATUS(p_ctx);
+ break;
+
+ default: break;
+ }
+
+ state->sample_table[table].entry++;
+ return state->status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_sample_header( VC_CONTAINER_T *p_ctx, uint32_t track,
+ MP4_READER_STATE_T *state )
+{
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module;
+
+ if(state->status != VC_CONTAINER_SUCCESS) return state->status;
+
+ if(state->sample_offset < state->sample_size)
+ return state->status; /* We still have data left from the current sample */
+
+ /* Switch to the next sample */
+ state->offset += state->sample_size;
+ state->sample_offset = 0;
+ state->sample_size = 0;
+ state->sample++;
+
+ if(!state->samples_in_chunk)
+ {
+ /* We're switching to the next chunk */
+ if(!state->chunks)
+ {
+ /* Seek to the next entry in the STSC */
+ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSC, 1 );
+ if(state->status != VC_CONTAINER_SUCCESS) goto error;
+ }
+
+ /* Get the offset of the new chunk */
+ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STCO, 1 );
+ if(state->status != VC_CONTAINER_SUCCESS) goto error;
+
+ state->chunks--;
+ }
+ state->samples_in_chunk--;
+
+ /* Get the new sample size */
+ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSZ, 1 );
+ if(state->status != VC_CONTAINER_SUCCESS) goto error;
+
+ /* Get the timestamp */
+ if(track_module->timescale)
+ state->pts = state->dts = state->duration * 1000000 / track_module->timescale;
+ if(!state->sample_duration_count)
+ {
+ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STTS, 1 );
+ if(state->status != VC_CONTAINER_SUCCESS) goto error;
+ }
+ state->sample_duration_count--;
+
+ /* Get the composition time */
+ if(track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries)
+ {
+ if(!state->sample_composition_count)
+ {
+ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_CTTS, 1 );
+ if(state->status != VC_CONTAINER_SUCCESS) goto error;
+ }
+ if(track_module->timescale)
+ state->pts = (state->duration + state->sample_composition_offset) * 1000000 / track_module->timescale;
+ state->sample_composition_count--;
+ }
+ state->duration += state->sample_duration;
+
+ /* Get the keyframe flag */
+ if(state->sample_table[MP4_SAMPLE_TABLE_STSS].entry <
+ track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries &&
+ !state->next_sync_sample)
+ {
+ mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSS, 1 );
+ state->status = VC_CONTAINER_SUCCESS; /* This isn't a critical error */
+ }
+
+ state->keyframe =
+ track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries &&
+ state->sample == state->next_sync_sample;
+ if(state->keyframe)
+ state->next_sync_sample = 0;
+
+ /* Try to batch several samples together if requested. We'll always stop at the chunk boundary */
+ if(track_module->samples_batch_size)
+ {
+ uint32_t size = state->sample_size;
+ while(state->samples_in_chunk && size < track_module->samples_batch_size)
+ {
+ if(mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSZ, 1 )) break;
+
+ if(!state->sample_duration_count)
+ if(mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STTS, 1 )) break;
+
+ state->sample_duration_count--;
+ state->duration += state->sample_duration;
+
+ size += state->sample_size;
+ state->samples_in_chunk--;
+ state->sample++;
+ }
+ state->sample_size = size;
+ }
+
+#ifdef ENABLE_MP4_READER_LOG_STATE
+ mp4_log_state(p_ctx, state);
+#endif
+
+ error:
+ return state->status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_read_sample_data( VC_CONTAINER_T *p_ctx, uint32_t track,
+ MP4_READER_STATE_T *state, uint8_t *data, unsigned int *data_size )
+{
+ VC_CONTAINER_STATUS_T status;
+ unsigned int size = state->sample_size - state->sample_offset;
+
+ if(state->status != VC_CONTAINER_SUCCESS) return state->status;
+
+ if(data_size && *data_size < size) size = *data_size;
+
+ if(data)
+ {
+ state->status = SEEK(p_ctx, state->offset + state->sample_offset);
+ if(state->status != VC_CONTAINER_SUCCESS) return state->status;
+
+ size = READ_BYTES(p_ctx, data, size);
+ }
+ state->sample_offset += size;
+
+ if(data_size) *data_size = size;
+ state->status = STREAM_STATUS(p_ctx);
+ if(state->status != VC_CONTAINER_SUCCESS) return state->status;
+
+ status = state->status;
+
+ /* Switch to the start of the next sample */
+ if(state->sample_offset >= state->sample_size)
+ mp4_read_sample_header(p_ctx, track, state);
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_reader_read( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *packet, uint32_t flags )
+{
+ VC_CONTAINER_TRACK_MODULE_T *track_module;
+ VC_CONTAINER_STATUS_T status;
+ MP4_READER_STATE_T *state;
+ uint32_t i, track;
+ unsigned int data_size;
+ uint8_t *data = 0;
+ int64_t offset;
+
+ /* Select the track to read from. If no specific track is requested by the caller, this
+ * will be the track to which the next bit of data in the mdat belongs to */
+ if(!(flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK))
+ {
+ for(i = 0, track = 0, offset = -1; i < p_ctx->tracks_num; i++)
+ {
+ track_module = p_ctx->tracks[i]->priv->module;
+
+ /* Ignore tracks which have no more readable data */
+ if(track_module->state.status != VC_CONTAINER_SUCCESS) continue;
+
+ if(offset >= 0 && track_module->state.offset >= offset) continue;
+ offset = track_module->state.offset;
+ track = i;
+ }
+ }
+ else track = packet->track;
+
+ if(track >= p_ctx->tracks_num) return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+
+ track_module = p_ctx->tracks[track]->priv->module;
+ state = &track_module->state;
+
+ status = mp4_read_sample_header(p_ctx, track, state);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ if(!packet) /* Skip packet */
+ return mp4_read_sample_data(p_ctx, track, state, 0, 0);
+
+ packet->dts = state->dts;
+ packet->pts = state->pts;
+ packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END;
+ if(state->keyframe) packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME;
+ if(!state->sample_offset) packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
+ packet->track = track;
+ packet->frame_size = state->sample_size;
+ packet->size = state->sample_size - state->sample_offset;
+
+ if(flags & VC_CONTAINER_READ_FLAG_SKIP)
+ return mp4_read_sample_data(p_ctx, track, state, 0, 0);
+ else if((flags & VC_CONTAINER_READ_FLAG_INFO) || !packet->data)
+ return VC_CONTAINER_SUCCESS;
+
+ data = packet->data;
+ data_size = packet->buffer_size;
+
+ status = mp4_read_sample_data(p_ctx, track, state, data, &data_size);
+ if(status != VC_CONTAINER_SUCCESS)
+ {
+ /* FIXME */
+ return status;
+ }
+
+ packet->size = data_size;
+ if(state->sample_offset) //?
+ packet->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END;
+
+ return status;
+}
+
+/*****************************************************************************/
+static uint32_t mp4_find_sample( VC_CONTAINER_T *p_ctx, uint32_t track,
+ MP4_READER_STATE_T *state, int64_t seek_time, VC_CONTAINER_STATUS_T *p_status )
+{
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint32_t sample = 0, sample_duration_count;
+ int64_t sample_duration, seek_time_up = seek_time + 1;
+ unsigned int i;
+ VC_CONTAINER_PARAM_UNUSED(state);
+
+ seek_time = seek_time * track_module->timescale / 1000000;
+ /* We also need to check against the time rounded up to account for
+ * rounding errors in the timestamp (because of the timescale conversion) */
+ seek_time_up = seek_time_up * track_module->timescale / 1000000;
+
+ status = SEEK(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].offset);
+ if(status != VC_CONTAINER_SUCCESS) goto end;
+
+ /* Find the sample which corresponds to the requested time */
+ for(i = 0; i < track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries; i++)
+ {
+ sample_duration_count = _READ_U32(p_ctx);
+ sample_duration = _READ_U32(p_ctx);
+ status = STREAM_STATUS(p_ctx);
+ if(status != VC_CONTAINER_SUCCESS) break;
+
+ if(sample_duration_count * sample_duration <= seek_time)
+ {
+ seek_time -= sample_duration_count * sample_duration;
+ seek_time_up -= sample_duration_count * sample_duration;
+ sample += sample_duration_count;
+ continue;
+ }
+ if(!sample_duration) break;
+
+ seek_time /= sample_duration;
+ seek_time_up /= sample_duration;
+ sample += MAX(seek_time, seek_time_up);
+ break;
+ }
+
+ end:
+ if(p_status) *p_status = status;
+ return sample;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_seek_track( VC_CONTAINER_T *p_ctx, uint32_t track,
+ MP4_READER_STATE_T *state, uint32_t sample )
+{
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[track]->priv->module;
+ uint32_t chunk = 0, samples;
+ unsigned int i;
+
+ memset(state, 0, sizeof(*state));
+
+ /* Find the right chunk */
+ for(i = 0, samples = sample; i < track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries; i++)
+ {
+ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSC, 1 );
+ if(state->status != VC_CONTAINER_SUCCESS) goto error;
+
+ if(state->chunks * state->samples_per_chunk <= samples)
+ {
+ samples -= state->chunks * state->samples_per_chunk;
+ chunk += state->chunks;
+ continue;
+ }
+
+ while(samples >= state->samples_per_chunk)
+ {
+ samples -= state->samples_per_chunk;
+ state->chunks--;
+ chunk++;
+ }
+
+ state->chunks--;
+ break;
+ }
+
+ /* Get the offset of the selected chunk */
+ state->sample_table[MP4_SAMPLE_TABLE_STCO].entry = chunk;
+ state->sample_table[MP4_SAMPLE_TABLE_CO64].entry = chunk;
+ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STCO, 1 );
+ if(state->status != VC_CONTAINER_SUCCESS) goto error;
+
+ /* Find the sample offset within the chunk */
+ state->sample_table[MP4_SAMPLE_TABLE_STSZ].entry = sample - samples;
+ for(i = 0; i < samples; i++)
+ {
+ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSZ, !i );
+ if(state->status != VC_CONTAINER_SUCCESS) goto error;
+ state->offset += state->sample_size;
+ state->samples_in_chunk--;
+ }
+
+ /* Get the timestamp */
+ for(i = 0, samples = sample; i < track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries; i++)
+ {
+ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STTS, !i );
+ if(state->status != VC_CONTAINER_SUCCESS) goto error;
+
+ if(state->sample_duration_count <= samples)
+ {
+ samples -= state->sample_duration_count;
+ state->duration += state->sample_duration * state->sample_duration_count;
+ continue;
+ }
+
+ state->sample_duration_count -= samples;
+ state->duration += samples * state->sample_duration;
+ break;
+ }
+
+ /* Find the right place in the sample composition table */
+ for(i = 0, samples = sample; i < track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries; i++)
+ {
+ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_CTTS, !i );
+ if(state->status != VC_CONTAINER_SUCCESS) goto error;
+
+ if(state->sample_composition_count <= samples)
+ {
+ samples -= state->sample_composition_count;
+ continue;
+ }
+
+ state->sample_composition_count -= samples;
+ break;
+ }
+
+ /* Find the right place in the synchronisation table */
+ for(i = 0; i < track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries; i++)
+ {
+ state->status = mp4_read_sample_table( p_ctx, track_module, state, MP4_SAMPLE_TABLE_STSS, !i );
+ if(state->status != VC_CONTAINER_SUCCESS) goto error;
+
+ if(state->next_sync_sample >= sample + 1) break;
+ }
+
+ state->sample = sample;
+ state->sample_size = 0;
+ mp4_read_sample_header(p_ctx, track, state);
+
+ error:
+ return state->status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_reader_seek(VC_CONTAINER_T *p_ctx,
+ int64_t *offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module;
+ VC_CONTAINER_STATUS_T status;
+ uint32_t i, track, sample, prev_sample, next_sample;
+ int64_t seek_time = *offset;
+ VC_CONTAINER_PARAM_UNUSED(module);
+ VC_CONTAINER_PARAM_UNUSED(mode);
+
+ /* Reset the states */
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ memset(&p_ctx->tracks[i]->priv->module->state, 0, sizeof(p_ctx->tracks[i]->priv->module->state));
+
+ /* Deal with the easy case first */
+ if(!*offset)
+ {
+ /* Initialise tracks */
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ /* FIXME: we should check we've got at least one success */
+ mp4_read_sample_header(p_ctx, i, &p_ctx->tracks[i]->priv->module->state);
+ }
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ /* Find the first enabled video track */
+ for(track = 0; track < p_ctx->tracks_num; track++)
+ if(p_ctx->tracks[track]->is_enabled &&
+ p_ctx->tracks[track]->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) break;
+ if(track == p_ctx->tracks_num) goto seek_time_found; /* No video track found */
+ track_module = p_ctx->tracks[track]->priv->module;
+
+ /* Find the sample number for the requested time */
+ sample = mp4_find_sample( p_ctx, track, &track_module->state, seek_time, &status );
+ if(status != VC_CONTAINER_SUCCESS) goto seek_time_found;
+
+ /* Find the closest sync sample */
+ status = mp4_seek_sample_table( p_ctx, track_module, &track_module->state, MP4_SAMPLE_TABLE_STSS );
+ if(status != VC_CONTAINER_SUCCESS) goto seek_time_found;
+ for(i = 0, prev_sample = 0, next_sample = 0;
+ i < track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries; i++)
+ {
+ next_sample = _READ_U32(p_ctx) - 1;
+ if(next_sample > sample)
+ {
+ sample = (flags & VC_CONTAINER_SEEK_FLAG_FORWARD) ? next_sample : prev_sample;
+ break;
+ }
+ prev_sample = next_sample;
+ }
+
+ /* Do the seek on this track and use its timestamp as the new seek point */
+ status = mp4_seek_track(p_ctx, track, &track_module->state, sample);
+ if(status != VC_CONTAINER_SUCCESS) goto seek_time_found;
+ seek_time = track_module->state.pts;
+
+ seek_time_found:
+
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ uint32_t sample;
+ track_module = p_ctx->tracks[i]->priv->module;
+ if(track_module->state.offset) continue;
+ sample = mp4_find_sample( p_ctx, i, &track_module->state, seek_time, &status );
+ if(status != VC_CONTAINER_SUCCESS) return status; //FIXME
+
+ status = mp4_seek_track(p_ctx, i, &track_module->state, sample);
+ }
+
+ *offset = seek_time;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/******************************************************************************
+Global function definitions.
+******************************************************************************/
+
+VC_CONTAINER_STATUS_T mp4_reader_open( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ VC_CONTAINER_MODULE_T *module = 0;
+ unsigned int i;
+ uint8_t h[8];
+
+ /* Check for a known box type to see if we're dealing with mp4 */
+ if( PEEK_BYTES(p_ctx, h, 8) != 8 )
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ switch(VC_FOURCC(h[4],h[5],h[6],h[7]))
+ {
+ case MP4_BOX_TYPE_FTYP:
+ case MP4_BOX_TYPE_MDAT:
+ case MP4_BOX_TYPE_MOOV:
+ case MP4_BOX_TYPE_FREE:
+ case MP4_BOX_TYPE_SKIP:
+ case MP4_BOX_TYPE_WIDE:
+ case MP4_BOX_TYPE_PNOT:
+ case MP4_BOX_TYPE_PICT:
+ case MP4_BOX_TYPE_UDTA:
+ case MP4_BOX_TYPE_UUID:
+ break;
+ default:
+ /* Couldn't recognize the box type. This doesn't look like an mp4. */
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+
+ /*
+ * We are dealing with an MP4 file
+ */
+
+ LOG_DEBUG(p_ctx, "using mp4 reader");
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks = module->tracks;
+
+ while(STREAM_STATUS(p_ctx) == VC_CONTAINER_SUCCESS)
+ {
+ MP4_BOX_TYPE_T box_type;
+ int64_t box_size;
+
+ status = mp4_read_box_header( p_ctx, INT64_C(-1), &box_type, &box_size );
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ if(box_type == MP4_BOX_TYPE_MDAT)
+ {
+ module->data_offset = STREAM_POSITION(p_ctx);
+ module->data_size = box_size;
+ if(module->found_moov) break; /* We've got everything we want */
+ }
+ else if(box_type == MP4_BOX_TYPE_MOOV)
+ module->found_moov = true;
+
+ status = mp4_read_box_data( p_ctx, box_type, box_size, MP4_BOX_TYPE_ROOT );
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ if(module->found_moov && module->data_offset) break; /* We've got everything we want */
+ }
+
+ /* Initialise tracks */
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ /* FIXME: we should check we've got at least one success */
+ status = mp4_read_sample_header(p_ctx, i, &p_ctx->tracks[i]->priv->module->state);
+ }
+
+ status = SEEK(p_ctx, module->data_offset);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ p_ctx->priv->pf_close = mp4_reader_close;
+ p_ctx->priv->pf_read = mp4_reader_read;
+ p_ctx->priv->pf_seek = mp4_reader_seek;
+
+ if(STREAM_SEEKABLE(p_ctx))
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
+
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ LOG_DEBUG(p_ctx, "mp4: error opening stream");
+ if(module) mp4_reader_close(p_ctx);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open mp4_reader_open
+#endif
diff --git a/gfx/include/userland/containers/mp4/mp4_writer.c b/gfx/include/userland/containers/mp4/mp4_writer.c
new file mode 100644
index 0000000000..5b33e0170f
--- /dev/null
+++ b/gfx/include/userland/containers/mp4/mp4_writer.c
@@ -0,0 +1,1441 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+#define CONTAINER_IS_BIG_ENDIAN
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_writer_utils.h"
+#include "containers/core/containers_logging.h"
+#include "containers/mp4/mp4_common.h"
+#undef CONTAINER_HELPER_LOG_INDENT
+#define CONTAINER_HELPER_LOG_INDENT(a) (a)->priv->module->box_level
+
+VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T *p_ctx );
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+#define MP4_TRACKS_MAX 16
+#define MP4_TIMESCALE 1000
+
+#define MP4_64BITS_TIME 0 /* 0 to disable / 1 to enable */
+
+/******************************************************************************
+Type definitions.
+******************************************************************************/
+typedef struct VC_CONTAINER_TRACK_MODULE_T
+{
+ uint32_t fourcc;
+ uint32_t samples;
+ uint32_t chunks;
+
+ int64_t offset;
+ int64_t timestamp;
+ int64_t delta_timestamp;
+ int64_t samples_in_chunk;
+ int64_t samples_in_prev_chunk;
+ struct {
+ uint32_t entries;
+ uint32_t entry_size;
+ } sample_table[MP4_SAMPLE_TABLE_NUM];
+
+ int64_t first_pts;
+ int64_t last_pts;
+
+} VC_CONTAINER_TRACK_MODULE_T;
+
+typedef struct VC_CONTAINER_MODULE_T
+{
+ int box_level;
+ MP4_BRAND_T brand;
+
+ VC_CONTAINER_TRACK_T *tracks[MP4_TRACKS_MAX];
+ bool tracks_add_done;
+
+ VC_CONTAINER_WRITER_EXTRAIO_T null;
+
+ unsigned int current_track;
+
+ unsigned moov_size;
+ int64_t mdat_offset;
+ int64_t data_offset;
+
+ uint32_t samples;
+ VC_CONTAINER_WRITER_EXTRAIO_T temp;
+ VC_CONTAINER_PACKET_T sample;
+ int64_t sample_offset;
+ int64_t prev_sample_dts;
+
+ int64_t duration;
+ /**/
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Static functions within this file.
+******************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_extended( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T box_type, uint32_t fourcc );
+static VC_CONTAINER_STATUS_T mp4_write_box( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T box_type );
+static VC_CONTAINER_STATUS_T mp4_write_box_ftyp( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_moov( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_mvhd( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_trak( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_tkhd( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_mdia( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_mdhd( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_hdlr( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_minf( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_vmhd( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_smhd( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_dinf( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_dref( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_stbl( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_stsd( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_stts( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_ctts( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_stsc( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_stsz( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_stco( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_co64( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_stss( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_vide( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_soun( VC_CONTAINER_T *p_ctx );
+static VC_CONTAINER_STATUS_T mp4_write_box_esds( VC_CONTAINER_T *p_ctx );
+
+static struct {
+ const MP4_BOX_TYPE_T type;
+ VC_CONTAINER_STATUS_T (*pf_func)( VC_CONTAINER_T * );
+} mp4_box_list[] =
+{
+ {MP4_BOX_TYPE_FTYP, mp4_write_box_ftyp},
+ {MP4_BOX_TYPE_MOOV, mp4_write_box_moov},
+ {MP4_BOX_TYPE_MVHD, mp4_write_box_mvhd},
+ {MP4_BOX_TYPE_TRAK, mp4_write_box_trak},
+ {MP4_BOX_TYPE_TKHD, mp4_write_box_tkhd},
+ {MP4_BOX_TYPE_MDIA, mp4_write_box_mdia},
+ {MP4_BOX_TYPE_MDHD, mp4_write_box_mdhd},
+ {MP4_BOX_TYPE_HDLR, mp4_write_box_hdlr},
+ {MP4_BOX_TYPE_MINF, mp4_write_box_minf},
+ {MP4_BOX_TYPE_VMHD, mp4_write_box_vmhd},
+ {MP4_BOX_TYPE_SMHD, mp4_write_box_smhd},
+ {MP4_BOX_TYPE_DINF, mp4_write_box_dinf},
+ {MP4_BOX_TYPE_DREF, mp4_write_box_dref},
+ {MP4_BOX_TYPE_STBL, mp4_write_box_stbl},
+ {MP4_BOX_TYPE_STSD, mp4_write_box_stsd},
+ {MP4_BOX_TYPE_STTS, mp4_write_box_stts},
+ {MP4_BOX_TYPE_CTTS, mp4_write_box_ctts},
+ {MP4_BOX_TYPE_STSC, mp4_write_box_stsc},
+ {MP4_BOX_TYPE_STSZ, mp4_write_box_stsz},
+ {MP4_BOX_TYPE_STCO, mp4_write_box_stco},
+ {MP4_BOX_TYPE_CO64, mp4_write_box_co64},
+ {MP4_BOX_TYPE_STSS, mp4_write_box_stss},
+ {MP4_BOX_TYPE_VIDE, mp4_write_box_vide},
+ {MP4_BOX_TYPE_SOUN, mp4_write_box_soun},
+ {MP4_BOX_TYPE_ESDS, mp4_write_box_esds},
+ {MP4_BOX_TYPE_UNKNOWN, 0}
+};
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_extended( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T type, uint32_t fourcc )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t box_size = 0;
+ unsigned int i;
+
+ /* Find out which object we want to write */
+ for( i = 0; mp4_box_list[i].type && mp4_box_list[i].type != type; i++ );
+
+ /* Check we found the requested type */
+ if(!mp4_box_list[i].type)
+ {
+ vc_container_assert(0);
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ /* We need to find out the size of the object we're going to write it. */
+ if(!vc_container_writer_extraio_enable(p_ctx, &module->null))
+ {
+ status = mp4_write_box_extended( p_ctx, type, fourcc );
+ box_size = STREAM_POSITION(p_ctx);
+ }
+ vc_container_writer_extraio_disable(p_ctx, &module->null);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ /* Write the object header */
+ LOG_FORMAT(p_ctx, "- Box %4.4s, size: %"PRIi64, (const char *)&fourcc, box_size);
+ _WRITE_U32(p_ctx, (uint32_t)box_size);
+ _WRITE_FOURCC(p_ctx, fourcc);
+
+ module->box_level++;
+
+ /* Call the object specific writing function */
+ status = mp4_box_list[i].pf_func(p_ctx);
+
+ module->box_level--;
+
+ if(status != VC_CONTAINER_SUCCESS)
+ LOG_DEBUG(p_ctx, "box %4.4s appears to be corrupted", (char *)mp4_box_list[i].type);
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box( VC_CONTAINER_T *p_ctx, MP4_BOX_TYPE_T type )
+{
+ return mp4_write_box_extended( p_ctx, type, type );
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_ftyp( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ WRITE_FOURCC(p_ctx, module->brand, "major_brand");
+ WRITE_U32(p_ctx, 512, "minor_version");
+ if(module->brand == MP4_BRAND_QT)
+ {
+ WRITE_FOURCC(p_ctx, MP4_BRAND_QT, "compatible_brands");
+ return STREAM_STATUS(p_ctx);
+ }
+
+ if(module->brand == MP4_BRAND_SKM2)
+ WRITE_FOURCC(p_ctx, MP4_BRAND_SKM2, "compatible_brands");
+ WRITE_FOURCC(p_ctx, MP4_BRAND_ISOM, "compatible_brands");
+ WRITE_FOURCC(p_ctx, MP4_BRAND_MP42, "compatible_brands");
+ WRITE_FOURCC(p_ctx, MP4_BRAND_3GP4, "compatible_brands");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_moov( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int i;
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MVHD);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ module->current_track = i;
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_TRAK);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_mvhd( VC_CONTAINER_T *p_ctx )
+{
+ static uint32_t matrix[] = { 0x10000,0,0,0,0x10000,0,0,0,0x40000000 };
+ unsigned int version = MP4_64BITS_TIME;
+ unsigned int i;
+
+ WRITE_U8(p_ctx, version, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+
+ /**/
+ p_ctx->duration = 0;
+ for(i = 0; i < p_ctx->tracks_num; i++)
+ {
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[i];
+ VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module;
+ int64_t track_duration = track_module->last_pts - track_module->first_pts;
+ if(track_duration > p_ctx->duration)
+ p_ctx->duration = track_duration;
+ }
+
+ if(version)
+ {
+ WRITE_U64(p_ctx, 0, "creation_time");
+ WRITE_U64(p_ctx, 0, "modification_time");
+ WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale");
+ WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration");
+ }
+ else
+ {
+ WRITE_U32(p_ctx, 0, "creation_time");
+ WRITE_U32(p_ctx, 0, "modification_time");
+ WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale");
+ WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration");
+ }
+
+ WRITE_U32(p_ctx, 0x10000, "rate"); /* 1.0 */
+ WRITE_U16(p_ctx, 0x100, "volume"); /* full volume */
+ WRITE_U16(p_ctx, 0, "reserved");
+ for(i = 0; i < 2; i++)
+ WRITE_U32(p_ctx, 0, "reserved");
+ for(i = 0; i < 9; i++) /* unity matrix */
+ WRITE_U32(p_ctx, matrix[i], "matrix");
+ for(i = 0; i < 6; i++)
+ WRITE_U32(p_ctx, 0, "pre_defined");
+ WRITE_U32(p_ctx, p_ctx->tracks_num + 1, "next_track_ID");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_trak( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status;
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_TKHD);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MDIA);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_tkhd( VC_CONTAINER_T *p_ctx )
+{
+ static uint32_t matrix[] = { 0x10000,0,0,0,0x10000,0,0,0,0x40000000 };
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ unsigned int version = MP4_64BITS_TIME;
+ uint32_t i, width = 0, height = 0;
+
+ WRITE_U8(p_ctx, version, "version");
+ WRITE_U24(p_ctx, 0x7, "flags"); /* track enabled */
+
+ if(version)
+ {
+ WRITE_U64(p_ctx, 0, "creation_time");
+ WRITE_U64(p_ctx, 0, "modification_time");
+ WRITE_U32(p_ctx, module->current_track + 1, "track_ID");
+ WRITE_U32(p_ctx, 0, "reserved");
+ WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration");
+ }
+ else
+ {
+ WRITE_U32(p_ctx, 0, "creation_time");
+ WRITE_U32(p_ctx, 0, "modification_time");
+ WRITE_U32(p_ctx, module->current_track + 1, "track_ID");
+ WRITE_U32(p_ctx, 0, "reserved");
+ WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration");
+ }
+
+ for(i = 0; i < 2; i++)
+ WRITE_U32(p_ctx, 0, "reserved");
+ WRITE_U16(p_ctx, 0, "layer");
+ WRITE_U16(p_ctx, 0, "alternate_group");
+ WRITE_U16(p_ctx, track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO ? 0x100 : 0, "volume");
+ WRITE_U16(p_ctx, 0, "reserved");
+ for(i = 0; i < 9; i++) /* unity matrix */
+ WRITE_U32(p_ctx, matrix[i], "matrix");
+
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ {
+ width = track->format->type->video.width << 16;
+ height = track->format->type->video.height << 16;
+ if(track->format->type->video.par_num && track->format->type->video.par_den)
+ width = width * (uint64_t)track->format->type->video.par_num /
+ track->format->type->video.par_den;
+ }
+ else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE)
+ {
+ /* FIXME */
+ }
+
+ WRITE_U32(p_ctx, width, "width");
+ WRITE_U32(p_ctx, height, "height");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_mdia( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MDHD);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_HDLR);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MINF);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_mdhd( VC_CONTAINER_T *p_ctx )
+{
+ unsigned int version = MP4_64BITS_TIME;
+
+ WRITE_U8(p_ctx, version, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+
+ // FIXME: take a better timescale ??
+ if(version)
+ {
+ WRITE_U64(p_ctx, 0, "creation_time");
+ WRITE_U64(p_ctx, 0, "modification_time");
+ WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale");
+ WRITE_U64(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration");
+ }
+ else
+ {
+ WRITE_U32(p_ctx, 0, "creation_time");
+ WRITE_U32(p_ctx, 0, "modification_time");
+ WRITE_U32(p_ctx, MP4_TIMESCALE, "timescale");
+ WRITE_U32(p_ctx, p_ctx->duration * MP4_TIMESCALE / 1000000, "duration");
+ }
+
+ WRITE_U16(p_ctx, 0x55c4, "language"); /* ISO-639-2/T language code */
+ WRITE_U16(p_ctx, 0, "pre_defined");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_hdlr( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ uint32_t i, handler_size, fourcc = 0;
+ const char *handler_name;
+
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO) fourcc = VC_FOURCC('v','i','d','e');
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO) fourcc = VC_FOURCC('s','o','u','n');
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE) fourcc = VC_FOURCC('t','e','x','t');
+
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+
+ if(module->brand == MP4_BRAND_QT)
+ WRITE_FOURCC(p_ctx, VC_FOURCC('m','h','l','r'), "component_type");
+ else
+ WRITE_U32(p_ctx, 0, "pre-defined");
+
+ WRITE_FOURCC(p_ctx, fourcc, "handler_type");
+ for(i = 0; i < 3; i++)
+ WRITE_U32(p_ctx, 0, "reserved");
+
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ { handler_name = "Video Media Handler"; handler_size = sizeof("Video Media Handler"); }
+ else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
+ { handler_name = "Audio Media Handler"; handler_size = sizeof("Audio Media Handler"); }
+ else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE)
+ { handler_name = "Text Media Handler"; handler_size = sizeof("Text Media Handler"); }
+ else { handler_name = ""; handler_size = sizeof(""); }
+
+ if(module->brand == MP4_BRAND_QT)
+ { handler_size--; WRITE_U8(p_ctx, handler_size, "string_size"); }
+
+ WRITE_STRING(p_ctx, handler_name, handler_size, "name");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_minf( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_VMHD);
+ else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_SMHD);
+#if 0
+ else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE)
+ /*FIXME */;
+#endif
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_DINF);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STBL);
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_vmhd( VC_CONTAINER_T *p_ctx )
+{
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 1, "flags");
+
+ WRITE_U16(p_ctx, 0, "graphicsmode");
+ WRITE_U16(p_ctx, 0, "opcolor");
+ WRITE_U16(p_ctx, 0, "opcolor");
+ WRITE_U16(p_ctx, 0, "opcolor");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_smhd( VC_CONTAINER_T *p_ctx )
+{
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+
+ WRITE_U16(p_ctx, 0, "balance");
+ WRITE_U16(p_ctx, 0, "reserved");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_dinf( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_DREF);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_dref( VC_CONTAINER_T *p_ctx )
+{
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+
+ WRITE_U32(p_ctx, 1, "entry_count");
+
+ /* Add a URL box */
+ WRITE_U32(p_ctx, 12, "box_size");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('u','r','l',' '), "box_type");
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0x1, "flags");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_stbl( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSD);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STTS);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ if( 0 && track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ {
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_CTTS);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ }
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSC);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSZ);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ if(1)
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STCO);
+ else
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_CO64);
+
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ {
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_STSS);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_stsd( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+
+ WRITE_U32(p_ctx, 1, "entry_count");
+
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO)
+ status = mp4_write_box_extended(p_ctx, MP4_BOX_TYPE_VIDE, track->priv->module->fourcc);
+ else if(track->format->es_type == VC_CONTAINER_ES_TYPE_AUDIO)
+ status = mp4_write_box_extended(p_ctx, MP4_BOX_TYPE_SOUN, track->priv->module->fourcc);
+#if 0
+ else if(track->format->es_type == VC_CONTAINER_ES_TYPE_SUBPICTURE)
+ /*FIXME*/;
+#endif
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_writer_write_sample_to_temp( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *packet)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ int32_t dts_diff = packet->dts - module->prev_sample_dts;
+ uint8_t keyframe = (packet->flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME) ? 0x80 : 0;
+
+ vc_container_io_write_be_uint32(module->temp.io, packet->size);
+ vc_container_io_write_be_uint32(module->temp.io, dts_diff);
+ vc_container_io_write_be_uint24(module->temp.io, (uint32_t)(packet->pts - packet->dts));
+ vc_container_io_write_uint8(module->temp.io, packet->track | keyframe);
+ module->prev_sample_dts = packet->dts;
+ return module->temp.io->status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_writer_read_sample_from_temp( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *packet)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ packet->size = vc_container_io_read_be_uint32(module->temp.io);
+ packet->dts += (int32_t)vc_container_io_read_be_uint32(module->temp.io);
+ packet->pts = packet->dts + vc_container_io_read_be_uint24(module->temp.io);
+ packet->track = vc_container_io_read_uint8(module->temp.io);
+ packet->flags = (packet->track & 0x80) ? VC_CONTAINER_PACKET_FLAG_KEYFRAME : 0;
+ packet->track &= 0x7F;
+ return module->temp.io->status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_stts( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_PACKET_T sample;
+ unsigned int entries = 0;
+ int64_t last_dts = 0, delta;
+
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+
+ WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries, "entry_count");
+
+ if(module->null.refcount)
+ {
+ /* We're not actually writing the data, we just want the size */
+ WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries * 8);
+ return STREAM_STATUS(p_ctx);
+ }
+
+ /* Go through all the samples written */
+ vc_container_io_seek(module->temp.io, INT64_C(0));
+ sample.dts = 0;
+
+ status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
+ while(status == VC_CONTAINER_SUCCESS)
+ {
+ if(sample.track != module->current_track) goto skip;
+
+ delta = sample.dts * MP4_TIMESCALE / 1000000 - last_dts;
+ if(delta < 0) delta = 0;
+ WRITE_U32(p_ctx, 1, "sample_count");
+ WRITE_U32(p_ctx, delta, "sample_delta");
+ entries++;
+ last_dts += delta;
+
+ skip:
+ status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
+ }
+ vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_ctts( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
+
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+ WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_CTTS].entries, "entry_count");
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_stsc( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_PACKET_T sample;
+ int64_t offset = 0, track_offset = -1;
+ unsigned int entries = 0, chunks = 0, first_chunk = 0, samples_in_chunk = 0;
+
+ memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T));
+
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+ WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries, "entry_count");
+
+ if(module->null.refcount)
+ {
+ /* We're not actually writing the data, we just want the size */
+ WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries * 12);
+ return STREAM_STATUS(p_ctx);
+ }
+
+ /* Go through all the samples written */
+ vc_container_io_seek(module->temp.io, INT64_C(0));
+
+ status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
+ while(status == VC_CONTAINER_SUCCESS)
+ {
+ if(sample.track != module->current_track) goto skip;
+
+ /* Is it a new chunk ? */
+ if(track_offset != offset)
+ {
+ chunks++;
+ if(samples_in_chunk)
+ {
+ WRITE_U32(p_ctx, first_chunk, "first_chunk");
+ WRITE_U32(p_ctx, samples_in_chunk, "samples_per_chunk");
+ WRITE_U32(p_ctx, 1, "sample_description_index");
+ entries++;
+ }
+ first_chunk = chunks;
+ samples_in_chunk = 0;
+ }
+ track_offset = offset + sample.size;
+ samples_in_chunk++;
+
+ skip:
+ offset += sample.size;
+ status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
+ }
+
+ if(samples_in_chunk)
+ {
+ WRITE_U32(p_ctx, first_chunk, "first_chunk");
+ WRITE_U32(p_ctx, samples_in_chunk, "samples_per_chunk");
+ WRITE_U32(p_ctx, 1, "sample_description_index");
+ entries++;
+ }
+
+ vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_stsz( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_PACKET_T sample;
+ unsigned int entries = 0;
+
+ memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T));
+
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+
+ WRITE_U32(p_ctx, 0, "sample_size");
+ WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries, "sample_count");
+
+ if(module->null.refcount)
+ {
+ /* We're not actually writing the data, we just want the size */
+ WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries * 4);
+ return STREAM_STATUS(p_ctx);
+ }
+
+ /* Go through all the samples written */
+ vc_container_io_seek(module->temp.io, INT64_C(0));
+
+ status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
+ while(status == VC_CONTAINER_SUCCESS)
+ {
+ if(sample.track != module->current_track) goto skip;
+
+ WRITE_U32(p_ctx, sample.size, "entry_size");
+ entries++;
+
+ skip:
+ status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
+ }
+ vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_stco( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_PACKET_T sample;
+ int64_t offset = module->data_offset, track_offset = -1;
+ unsigned int entries = 0;
+
+ memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T));
+
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+ WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries, "entry_count");
+
+ if(module->null.refcount)
+ {
+ /* We're not actually writing the data, we just want the size */
+ WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries * 4);
+ return STREAM_STATUS(p_ctx);
+ }
+
+ /* Go through all the samples written */
+ vc_container_io_seek(module->temp.io, INT64_C(0));
+
+ status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
+ while(status == VC_CONTAINER_SUCCESS)
+ {
+ if(sample.track != module->current_track) goto skip;
+
+ /* Is it a new chunk ? */
+ if(track_offset != offset)
+ {
+ WRITE_U32(p_ctx, offset, "chunk_offset");
+ entries++;
+ }
+ track_offset = offset + sample.size;
+
+ skip:
+ offset += sample.size;
+ status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
+ }
+ vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_co64( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
+
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+ WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries, "entry_count");
+
+ if(module->null.refcount)
+ {
+ /* We're not actually writing the data, we just want the size */
+ WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_CO64].entries * 8);
+ return STREAM_STATUS(p_ctx);
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_stss( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_MODULE_T *track_module = p_ctx->tracks[module->current_track]->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_PACKET_T sample;
+ unsigned int entries = 0, samples = 0;
+
+ memset(&sample, 0, sizeof(VC_CONTAINER_PACKET_T));
+
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+ WRITE_U32(p_ctx, track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries, "entry_count");
+
+ if(module->null.refcount)
+ {
+ /* We're not actually writing the data, we just want the size */
+ WRITE_BYTES(p_ctx, 0, track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries * 4);
+ return STREAM_STATUS(p_ctx);
+ }
+
+ /* Go through all the samples written */
+ vc_container_io_seek(module->temp.io, INT64_C(0));
+
+ status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
+ while(status == VC_CONTAINER_SUCCESS)
+ {
+ if(sample.track != module->current_track) goto skip;
+
+ samples++;
+ if(sample.flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME)
+ {
+ WRITE_U32(p_ctx, samples, "sample_number");
+ entries++;
+ }
+
+ skip:
+ status = mp4_writer_read_sample_from_temp(p_ctx, &sample);
+ }
+ vc_container_assert(entries == track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_vide_avcC( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+
+ WRITE_U32(p_ctx, track->format->extradata_size + 8, "size");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('a','v','c','C'), "type");
+ WRITE_BYTES(p_ctx, track->format->extradata, track->format->extradata_size);
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_vide_d263( VC_CONTAINER_T *p_ctx )
+{
+ WRITE_U32(p_ctx, 8 + 7, "size");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('d','2','6','3'), "type");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor");
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U8(p_ctx, 10, "level");
+ WRITE_U8(p_ctx, 0, "profile");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_vide( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ unsigned int i;
+
+ for(i = 0; i < 6; i++) WRITE_U8(p_ctx, 0, "reserved");
+ WRITE_U16(p_ctx, 1, "data_reference_index");
+
+ WRITE_U16(p_ctx, 0, "pre_defined");
+ WRITE_U16(p_ctx, 0, "reserved");
+ for(i = 0; i < 3; i++) WRITE_U32(p_ctx, 0, "pre_defined");
+ WRITE_U16(p_ctx, track->format->type->video.width, "width");
+ WRITE_U16(p_ctx, track->format->type->video.height, "height");
+ WRITE_U32(p_ctx, 0x480000, "horizresolution"); /* 72 dpi */
+ WRITE_U32(p_ctx, 0x480000, "vertresolution"); /* 72 dpi */
+ WRITE_U32(p_ctx, 0, "reserved");
+ WRITE_U16(p_ctx, 1, "frame_count");
+ for(i = 0; i < 32; i++) _WRITE_U8(p_ctx, 0);
+ WRITE_U16(p_ctx, 0x18, "depth");
+ WRITE_U16(p_ctx, -1, "pre_defined");
+
+ switch(track->format->codec)
+ {
+ case VC_CONTAINER_CODEC_H264: return mp4_write_box_vide_avcC(p_ctx);
+ case VC_CONTAINER_CODEC_H263: return mp4_write_box_vide_d263(p_ctx);
+ case VC_CONTAINER_CODEC_MP4V: return mp4_write_box(p_ctx, MP4_BOX_TYPE_ESDS);
+ default: break;
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_soun_damr( VC_CONTAINER_T *p_ctx )
+{
+ WRITE_U32(p_ctx, 8 + 8, "size");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('d','a','m','r'), "type");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor");
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U8(p_ctx, 0x80, "mode_set");
+ WRITE_U8(p_ctx, 0, "mode_change_period");
+ WRITE_U8(p_ctx, 1, "frame_per_second");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_soun_dawp( VC_CONTAINER_T *p_ctx )
+{
+ WRITE_U32(p_ctx, 8 + 5, "size");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('d','a','w','p'), "type");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor");
+ WRITE_U8(p_ctx, 0, "version");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_soun_devc( VC_CONTAINER_T *p_ctx )
+{
+ WRITE_U32(p_ctx, 8 + 6, "size");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('d','e','v','c'), "type");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('B','R','C','M'), "vendor");
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U8(p_ctx, 1, "samples_per_frame");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_soun( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ unsigned int i, version = 0;
+
+ for(i = 0; i < 6; i++) WRITE_U8(p_ctx, 0, "reserved");
+ WRITE_U16(p_ctx, 1, "data_reference_index");
+
+ if(module->brand == MP4_BRAND_QT)
+ {
+ if(track->format->codec == VC_CONTAINER_CODEC_MP4A) version = 1;
+ WRITE_U16(p_ctx, version, "version");
+ WRITE_U16(p_ctx, 0, "revision_level");
+ WRITE_U32(p_ctx, 0, "vendor");
+ }
+ else
+ {
+ for(i = 0; i < 2; i++) WRITE_U32(p_ctx, 0, "reserved");
+ }
+
+ WRITE_U16(p_ctx, track->format->type->audio.channels, "channelcount");
+ WRITE_U16(p_ctx, 0, "samplesize");
+ WRITE_U16(p_ctx, 0, "pre_defined");
+ WRITE_U16(p_ctx, 0, "reserved");
+ WRITE_U32(p_ctx, track->format->type->audio.sample_rate << 16, "samplerate");
+
+ if(module->brand == MP4_BRAND_QT && version == 1) /* FIXME */
+ {
+ WRITE_U32(p_ctx, 1024, "samples_per_packet");
+ WRITE_U32(p_ctx, 1536, "bytes_per_packet");
+ WRITE_U32(p_ctx, 2, "bytes_per_frame");
+ WRITE_U32(p_ctx, 2, "bytes_per_sample");
+ }
+
+ switch(track->format->codec)
+ {
+ case VC_CONTAINER_CODEC_AMRNB:
+ case VC_CONTAINER_CODEC_AMRWB:
+ return mp4_write_box_soun_damr(p_ctx);
+ case VC_CONTAINER_CODEC_AMRWBP:
+ return mp4_write_box_soun_dawp(p_ctx);
+ case VC_CONTAINER_CODEC_EVRC:
+ return mp4_write_box_soun_devc(p_ctx);
+ case VC_CONTAINER_CODEC_MP4A:
+ case VC_CONTAINER_CODEC_MPGA:
+ return mp4_write_box(p_ctx, MP4_BOX_TYPE_ESDS);
+ default: break;
+ }
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_write_box_esds( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[module->current_track];
+ unsigned int decoder_specific_size = 0, decoder_config_size, sl_size;
+ unsigned int stream_type, object_type;
+
+#define MP4_GET_DESCRIPTOR_SIZE(size) \
+ ((size) < 0x0080) ? 2 + (size) : ((size) < 0x4000) ? 3 + (size) : 4 + (size)
+#define MP4_WRITE_DESCRIPTOR_HEADER(type, size) \
+ LOG_FORMAT(p_ctx, "descriptor %x, size %i", type, size); _WRITE_U8(p_ctx, type); \
+ if((size) >= 0x4000) _WRITE_U8(p_ctx, (((size) >> 14) & 0x7F) | 0x80); \
+ if((size) >= 0x80 ) _WRITE_U8(p_ctx, (((size) >> 7 ) & 0x7F) | 0x80); \
+ _WRITE_U8(p_ctx, (size) & 0x7F)
+
+ /* We only support small size descriptors */
+ if(track->format->extradata_size > 0x200000 - 100)
+ return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED;
+
+ switch(track->format->es_type)
+ {
+ case VC_CONTAINER_ES_TYPE_VIDEO: stream_type = 0x4; break;
+ case VC_CONTAINER_ES_TYPE_AUDIO: stream_type = 0x5; break;
+ case VC_CONTAINER_ES_TYPE_SUBPICTURE: stream_type = 0x20; break;
+ default: return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED;
+ }
+ switch(track->format->codec)
+ {
+ case VC_CONTAINER_CODEC_MP4V: object_type = 0x20; break;
+ case VC_CONTAINER_CODEC_MP1V: object_type = 0x6B; break;
+ case VC_CONTAINER_CODEC_MP2V: object_type = 0x60; break;
+ case VC_CONTAINER_CODEC_JPEG: object_type = 0x6C; break;
+ case VC_CONTAINER_CODEC_MP4A: object_type = 0x40; break;
+ case VC_CONTAINER_CODEC_MPGA:
+ object_type = track->format->type->audio.sample_rate < 32000 ? 0x69 : 0x6B; break;
+ default: return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED;
+ }
+
+ decoder_specific_size = MP4_GET_DESCRIPTOR_SIZE(track->format->extradata_size);
+ decoder_config_size = MP4_GET_DESCRIPTOR_SIZE(13 + decoder_specific_size);
+ sl_size = MP4_GET_DESCRIPTOR_SIZE(1);
+
+ WRITE_U8(p_ctx, 0, "version");
+ WRITE_U24(p_ctx, 0, "flags");
+
+ /* Write the ES descriptor */
+ MP4_WRITE_DESCRIPTOR_HEADER(0x3, 3 + decoder_config_size + sl_size);
+ WRITE_U16(p_ctx, module->current_track + 1, "es_id");
+ WRITE_U8(p_ctx, 0x1f, "flags"); /* stream_priority = 0x1f */
+
+ /* Write the Decoder Config descriptor */
+ MP4_WRITE_DESCRIPTOR_HEADER(0x4, 13 + decoder_specific_size);
+ WRITE_U8(p_ctx, object_type, "object_type_indication");
+ WRITE_U8(p_ctx, (stream_type << 2) | 1, "stream_type");
+ WRITE_U24(p_ctx, 8000, "buffer_size_db");
+ WRITE_U32(p_ctx, track->format->bitrate, "max_bitrate");
+ WRITE_U32(p_ctx, track->format->bitrate, "avg_bitrate");
+ if(track->format->extradata_size)
+ {
+ MP4_WRITE_DESCRIPTOR_HEADER(0x5, track->format->extradata_size);
+ WRITE_BYTES(p_ctx, track->format->extradata, track->format->extradata_size);
+ }
+
+ /* Write the SL descriptor */
+ MP4_WRITE_DESCRIPTOR_HEADER(0x6, 1);
+ WRITE_U8(p_ctx, 0x2, "flags");
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_writer_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ int64_t mdat_size;
+
+ mdat_size = STREAM_POSITION(p_ctx) - module->mdat_offset;
+
+ /* Write the moov box */
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MOOV);
+
+ /* Finalise the mdat box */
+ SEEK(p_ctx, module->mdat_offset);
+ WRITE_U32(p_ctx, (uint32_t)mdat_size, "mdat size" );
+
+ for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--)
+ vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]);
+
+ vc_container_writer_extraio_delete(p_ctx, &module->temp);
+ vc_container_writer_extraio_delete(p_ctx, &module->null);
+ free(module);
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_writer_add_track( VC_CONTAINER_T *p_ctx, VC_CONTAINER_ES_FORMAT_T *format )
+{
+ VC_CONTAINER_STATUS_T status;
+ VC_CONTAINER_TRACK_T *track;
+ uint32_t type = 0;
+
+ if(!(format->flags & VC_CONTAINER_ES_FORMAT_FLAG_FRAMED))
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ /* Check we support this format */
+ switch(format->codec)
+ {
+ case VC_CONTAINER_CODEC_AMRNB: type = VC_FOURCC('s','a','m','r'); break;
+ case VC_CONTAINER_CODEC_AMRWB: type = VC_FOURCC('s','a','w','b'); break;
+ case VC_CONTAINER_CODEC_AMRWBP: type = VC_FOURCC('s','a','w','p'); break;
+ case VC_CONTAINER_CODEC_EVRC: type = VC_FOURCC('s','e','v','c'); break;
+ case VC_CONTAINER_CODEC_MP4A: type = VC_FOURCC('m','p','4','a'); break;
+ case VC_CONTAINER_CODEC_MPGA: type = VC_FOURCC('m','p','4','a'); break;
+
+ case VC_CONTAINER_CODEC_MP4V: type = VC_FOURCC('m','p','4','v'); break;
+ case VC_CONTAINER_CODEC_JPEG: type = VC_FOURCC('m','p','4','v'); break;
+ case VC_CONTAINER_CODEC_H263: type = VC_FOURCC('s','2','6','3'); break;
+ case VC_CONTAINER_CODEC_H264:
+ if(format->codec_variant == VC_FOURCC('a','v','c','C')) type = VC_FOURCC('a','v','c','1'); break;
+ case VC_CONTAINER_CODEC_MJPEG: type = VC_FOURCC('j','p','e','g'); break;
+ case VC_CONTAINER_CODEC_MJPEGA: type = VC_FOURCC('m','j','p','a'); break;
+ case VC_CONTAINER_CODEC_MJPEGB: type = VC_FOURCC('m','j','p','b'); break;
+ case VC_CONTAINER_CODEC_MP1V: type = VC_FOURCC('m','p','e','g'); break;
+ case VC_CONTAINER_CODEC_MP2V: type = VC_FOURCC('m','p','e','g'); break;
+
+ default: type = 0; break;
+ }
+
+ if(!type) return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED;
+
+ /* Allocate and initialise track data */
+ if(p_ctx->tracks_num >= MP4_TRACKS_MAX) return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ p_ctx->tracks[p_ctx->tracks_num] = track =
+ vc_container_allocate_track(p_ctx, sizeof(*p_ctx->tracks[0]->priv->module));
+ if(!track) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ if(format->extradata_size)
+ {
+ status = vc_container_track_allocate_extradata( p_ctx, track, format->extradata_size );
+ if(status) goto error;
+ }
+
+ vc_container_format_copy(track->format, format, format->extradata_size);
+ track->priv->module->fourcc = type;
+ track->priv->module->offset = -1;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size = 8;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size = 4;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size = 12;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size = 4;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size = 4;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_CO64].entry_size = 8;
+ track->priv->module->sample_table[MP4_SAMPLE_TABLE_CTTS].entry_size = 8;
+
+ p_ctx->tracks_num++;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ vc_container_free_track(p_ctx, track);
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_writer_add_track_done( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ if(module->tracks_add_done) return status;
+
+ /* We need to find out the size of the object we're going to write it. */
+ if(!vc_container_writer_extraio_enable(p_ctx, &module->null))
+ {
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_MOOV);
+ module->moov_size = STREAM_POSITION(p_ctx);
+ p_ctx->size = module->moov_size;
+ }
+ vc_container_writer_extraio_disable(p_ctx, &module->null);
+
+ if(status == VC_CONTAINER_SUCCESS) module->tracks_add_done = true;
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_writer_control( VC_CONTAINER_T *p_ctx, VC_CONTAINER_CONTROL_T operation, va_list args )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ switch(operation)
+ {
+ case VC_CONTAINER_CONTROL_TRACK_ADD:
+ {
+ VC_CONTAINER_ES_FORMAT_T *p_format =
+ (VC_CONTAINER_ES_FORMAT_T *)va_arg( args, VC_CONTAINER_ES_FORMAT_T * );
+ if(module->tracks_add_done) return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ return mp4_writer_add_track(p_ctx, p_format);
+ }
+
+ case VC_CONTAINER_CONTROL_TRACK_ADD_DONE:
+ return mp4_writer_add_track_done(p_ctx);
+
+ default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ }
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_writer_add_sample( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *packet )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[packet->track];
+ VC_CONTAINER_TRACK_MODULE_T *track_module = track->priv->module;
+
+ track_module->last_pts = packet->pts;
+ if(!track_module->samples) track_module->first_pts = packet->pts;
+
+ track_module->samples++;
+ track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entries++; /* sample size */
+ p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSZ].entry_size;
+ track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entries++; /* time to sample */
+ p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STTS].entry_size;
+
+ #if 0
+ delta_ts = packet->dts - track_module->timestamp;
+ track_module->timestamp = packet->dts;
+ if(!track_module->samples) track_module->delta_ts =
+ if()
+#endif
+
+ /* Is it a new chunk ? */
+ if(module->sample_offset != track_module->offset)
+ {
+ track_module->chunks++;
+ track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entries++; /* chunk offset */
+ p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STCO].entry_size;
+ track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entries++; /* sample to chunk */
+ p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSC].entry_size;
+ }
+ track_module->offset = module->sample_offset + packet->size;
+
+ if(track->format->es_type == VC_CONTAINER_ES_TYPE_VIDEO &&
+ (packet->flags & VC_CONTAINER_PACKET_FLAG_KEYFRAME))
+ {
+ track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entries++; /* sync sample */
+ p_ctx->size += track_module->sample_table[MP4_SAMPLE_TABLE_STSS].entry_size;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mp4_writer_write( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *packet )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_PACKET_T *sample = &module->sample;
+ VC_CONTAINER_STATUS_T status;
+
+ if(!module->tracks_add_done)
+ {
+ status = mp4_writer_add_track_done(p_ctx);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+ }
+
+ if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)
+ ++module->samples; /* Switching to a new sample */
+
+ if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START)
+ {
+ module->sample_offset = STREAM_POSITION(p_ctx);
+ sample->size = packet->size;
+ sample->pts = packet->pts;
+ sample->dts = packet->pts;
+ sample->track = packet->track;
+ sample->flags = packet->flags;
+ }
+ else
+ {
+ sample->size += packet->size;
+ sample->flags |= packet->flags;
+ }
+
+ if(WRITE_BYTES(p_ctx, packet->data, packet->size) != packet->size)
+ return STREAM_STATUS(p_ctx); // TODO do something
+ p_ctx->size += packet->size;
+
+ //
+ if(packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_END)
+ {
+ status = mp4_writer_write_sample_to_temp(p_ctx, sample);
+ status = mp4_writer_add_sample(p_ctx, sample);
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/******************************************************************************
+Global function definitions.
+******************************************************************************/
+
+VC_CONTAINER_STATUS_T mp4_writer_open( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ const char *extension = vc_uri_path_extension(p_ctx->priv->uri);
+ VC_CONTAINER_MODULE_T *module = 0;
+ MP4_BRAND_T brand;
+
+ /* Check if the user has specified a container */
+ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
+
+ /* Check we're the right writer for this */
+ if(!extension)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if(strcasecmp(extension, "3gp") && strcasecmp(extension, "skm") &&
+ strcasecmp(extension, "mov") && strcasecmp(extension, "mp4") &&
+ strcasecmp(extension, "m4v") && strcasecmp(extension, "m4a"))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks = module->tracks;
+
+ /* Find out which brand we're going write */
+ if(!strcasecmp(extension, "3gp")) brand = MP4_BRAND_3GP5;
+ else if(!strcasecmp(extension, "skm")) brand = MP4_BRAND_SKM2;
+ else if(!strcasecmp(extension, "mov")) brand = MP4_BRAND_QT;
+ else brand = MP4_BRAND_ISOM;
+ module->brand = brand;
+
+ /* Create a null i/o writer to help us out in writing our data */
+ status = vc_container_writer_extraio_create_null(p_ctx, &module->null);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ /* Create a temporary i/o writer to help us out in writing our data */
+ status = vc_container_writer_extraio_create_temp(p_ctx, &module->temp);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ status = mp4_write_box(p_ctx, MP4_BOX_TYPE_FTYP);
+ if(status != VC_CONTAINER_SUCCESS) goto error;
+
+ /* Start the mdat box */
+ module->mdat_offset = STREAM_POSITION(p_ctx);
+ WRITE_U32(p_ctx, 0, "size");
+ WRITE_FOURCC(p_ctx, VC_FOURCC('m','d','a','t'), "type");
+ module->data_offset = STREAM_POSITION(p_ctx);
+
+ p_ctx->priv->pf_close = mp4_writer_close;
+ p_ctx->priv->pf_write = mp4_writer_write;
+ p_ctx->priv->pf_control = mp4_writer_control;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ LOG_DEBUG(p_ctx, "mp4: error opening stream");
+ if(module)
+ {
+ if(module->null.io) vc_container_writer_extraio_delete(p_ctx, &module->null);
+ free(module);
+ }
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak writer_open mp4_writer_open
+#endif
diff --git a/gfx/include/userland/containers/mpeg/CMakeLists.txt b/gfx/include/userland/containers/mpeg/CMakeLists.txt
new file mode 100644
index 0000000000..5afe585f58
--- /dev/null
+++ b/gfx/include/userland/containers/mpeg/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(reader_ps ${LIBRARY_TYPE} ps_reader.c)
+
+target_link_libraries(reader_ps containers)
+
+install(TARGETS reader_ps DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/mpeg/ps_reader.c b/gfx/include/userland/containers/mpeg/ps_reader.c
new file mode 100644
index 0000000000..13943008c3
--- /dev/null
+++ b/gfx/include/userland/containers/mpeg/ps_reader.c
@@ -0,0 +1,1268 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+#define CONTAINER_IS_BIG_ENDIAN
+//#define ENABLE_CONTAINERS_LOG_FORMAT
+//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+#include "containers/core/containers_bits.h"
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+#undef CONTAINER_HELPER_LOG_INDENT
+#define CONTAINER_HELPER_LOG_INDENT(a) (2*(a)->priv->module->level)
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+#define PS_TRACKS_MAX 2
+#define PS_EXTRADATA_MAX 256
+
+#define PS_SYNC_FAIL_MAX 65536 /** Maximum number of byte-wise sync attempts,
+ should be enough to stride at least one
+ PES packet (length encoded using 16 bits). */
+
+/** Maximum number of pack/packet start codes scanned when searching for tracks
+ at open time or when resyncing. */
+#define PS_PACK_SCAN_MAX 128
+
+/******************************************************************************
+Type definitions.
+******************************************************************************/
+typedef struct VC_CONTAINER_TRACK_MODULE_T
+{
+ /** Coding and elementary stream id of the track */
+ uint32_t stream_id;
+
+ /** Sub-stream id (for private_stream_1 only) */
+ uint32_t substream_id;
+
+ /** PES packet payload offset (for private_stream_1) */
+ unsigned int payload_offset;
+
+ uint8_t extradata[PS_EXTRADATA_MAX];
+
+} VC_CONTAINER_TRACK_MODULE_T;
+
+typedef struct VC_CONTAINER_MODULE_T
+{
+ /** Logging indentation level */
+ uint32_t level;
+
+ /** Track data */
+ int tracks_num;
+ VC_CONTAINER_TRACK_T *tracks[PS_TRACKS_MAX];
+
+ /** State flag denoting whether or not we are searching
+ for tracks (at open time) */
+ bool searching_tracks;
+
+ /** Size of program stream data (if known) */
+ uint64_t data_size;
+
+ /** Offset to the first pack or PES packet start code we've seen */
+ uint64_t data_offset;
+
+ /** The first system_clock_reference value we've seen, in (27MHz ticks) */
+ int64_t scr_offset;
+
+ /** Most recent system_clock_reference value we've seen, in (27MHz ticks) */
+ int64_t scr;
+
+ /** Global offset we add to PES timestamps to make them zero based and
+ to work around discontinuity in the system_clock_reference */
+ int64_t scr_bias;
+
+ /** Most recent program stream mux rate (in units of 50 bytes/second). */
+ uint32_t mux_rate;
+
+ /** Offset to the most recent pack start code we've seen */
+ uint64_t pack_offset;
+
+ /** Program stream mux rate is often incorrect or fixed to 25200 (10.08
+ Mbit/s) which yields inaccurate duration estimate for most files. We
+ maintain a moving average data rate (in units of bytes/second) based
+ on the system_clock_reference to give better estimates. */
+ int64_t data_rate;
+
+ /** Offset to the most recent PES packet start code prefix we've seen */
+ unsigned int packet_data_size;
+ unsigned int packet_data_left;
+ int64_t packet_pts;
+ int64_t packet_dts;
+ int packet_track;
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+
+VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Prototypes for local functions
+******************************************************************************/
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+/** Find the track associated with a PS stream_id */
+static VC_CONTAINER_TRACK_T *ps_find_track( VC_CONTAINER_T *ctx, uint32_t stream_id,
+ uint32_t substream_id, bool b_create )
+{
+ VC_CONTAINER_TRACK_T *track = 0;
+ unsigned int i;
+
+ for(i = 0; i < ctx->tracks_num; i++)
+ if(ctx->tracks[i]->priv->module->stream_id == stream_id &&
+ ctx->tracks[i]->priv->module->substream_id == substream_id) break;
+
+ if(i < ctx->tracks_num) /* We found it */
+ track = ctx->tracks[i];
+
+ if(!track && b_create && i < PS_TRACKS_MAX)
+ {
+ /* Allocate and initialise a new track */
+ ctx->tracks[i] = track =
+ vc_container_allocate_track(ctx, sizeof(*ctx->tracks[0]->priv->module));
+ if(track)
+ {
+ track->priv->module->stream_id = stream_id;
+ track->priv->module->substream_id = substream_id;
+ ctx->tracks_num++;
+ }
+ }
+
+ if(!track && b_create)
+ LOG_DEBUG(ctx, "could not create track for stream id: %i", stream_id);
+
+ return track;
+}
+
+/*****************************************************************************/
+STATIC_INLINE VC_CONTAINER_STATUS_T ps_find_start_code( VC_CONTAINER_T *ctx, uint8_t *buffer )
+{
+ unsigned int i;
+
+ /* Scan for a pack or PES packet start code prefix */
+ for (i = 0; i < PS_SYNC_FAIL_MAX; ++i)
+ {
+ if(PEEK_BYTES(ctx, buffer, 4) < 4)
+ return VC_CONTAINER_ERROR_EOS;
+
+ if(buffer[0] == 0x0 && buffer[1] == 0x0 && buffer[2] == 0x1 && buffer[3] >= 0xB9)
+ break;
+
+ if (SKIP_BYTES(ctx, 1) != 1)
+ return VC_CONTAINER_ERROR_EOS;
+ }
+
+ if(i == PS_SYNC_FAIL_MAX) /* We didn't find a valid pack or PES packet */
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ if (buffer[3] == 0xB9) /* MPEG_program_end_code */
+ return VC_CONTAINER_ERROR_EOS;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T ps_read_system_header( VC_CONTAINER_T *ctx )
+{
+ uint8_t header[8];
+ uint32_t length;
+ VC_CONTAINER_BITS_T bits;
+
+ if(_READ_U32(ctx) != 0x1BB) return VC_CONTAINER_ERROR_CORRUPTED;
+ LOG_FORMAT(ctx, "system_header");
+ ctx->priv->module->level++;
+
+ length = READ_U16(ctx, "header_length");
+ if(length < 6) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS;
+
+ BITS_INIT(ctx, &bits, header, 6);
+
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ BITS_SKIP(ctx, &bits, 22, "rate_bound");
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ BITS_SKIP(ctx, &bits, 6, "audio_bound");
+ BITS_SKIP(ctx, &bits, 1, "fixed_flag");
+ BITS_SKIP(ctx, &bits, 1, "CSPS_flag");
+ BITS_SKIP(ctx, &bits, 1, "system_audio_lock_flag");
+ BITS_SKIP(ctx, &bits, 1, "system_video_lock_flag");
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ BITS_SKIP(ctx, &bits, 5, "video_bound");
+ BITS_SKIP(ctx, &bits, 1, "packet_rate_restriction_flag");
+ BITS_SKIP(ctx, &bits, 7, "reserved_bits");
+ length -= 6;
+
+ while(length >= 3 && (PEEK_U8(ctx) & 0x80))
+ {
+ SKIP_U8(ctx, "stream_id");
+ SKIP_BYTES(ctx, 2);
+ length -= 3;
+ }
+ SKIP_BYTES(ctx, length);
+
+ ctx->priv->module->level--;
+ return STREAM_STATUS(ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T ps_read_pack_header( VC_CONTAINER_T *ctx )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ uint8_t header[10];
+ int64_t scr, scr_base, scr_ext = INT64_C(0);
+ uint64_t pack_offset = STREAM_POSITION(ctx);
+ uint32_t mux_rate, stuffing;
+ VC_CONTAINER_BITS_T bits;
+ VC_CONTAINER_STATUS_T status;
+
+ if(_READ_U32(ctx) != 0x1BA) return VC_CONTAINER_ERROR_CORRUPTED;
+ LOG_FORMAT(ctx, "pack_header");
+
+ module->level++;
+
+ if (PEEK_U8(ctx) & 0x40) /* program stream */
+ {
+ if(READ_BYTES(ctx, header, 10) != 10) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 10);
+ if(BITS_READ_U32(ctx, &bits, 2, "'01' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ scr_base = BITS_READ_U32(ctx, &bits, 3, "system_clock_reference_base [32..30]") << 30;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [29..15]") << 15;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [14..0]");
+ LOG_FORMAT(ctx, "system_clock_reference_base %"PRId64, scr_base);
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ scr_ext = BITS_READ_U32(ctx, &bits, 9, "system_clock_reference_extension");
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ mux_rate = BITS_READ_U32(ctx, &bits, 22, "program_mux_rate");
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ BITS_SKIP(ctx, &bits, 5, "reserved");
+ stuffing = BITS_READ_U32(ctx, &bits, 3, "pack_stuffing_length");
+ SKIP_BYTES(ctx, stuffing);
+ }
+ else /* system stream */
+ {
+ if(READ_BYTES(ctx, header, 8) != 8) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 8);
+ if(BITS_READ_U32(ctx, &bits, 4, "'0010' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED;
+ scr_base = BITS_READ_U32(ctx, &bits, 3, "system_clock_reference_base [32..30]") << 30;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [29..15]") << 15;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ scr_base |= BITS_READ_U32(ctx, &bits, 15, "system_clock_reference_base [14..0]");
+ LOG_FORMAT(ctx, "system_clock_reference_base %"PRId64, scr_base);
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ mux_rate = BITS_READ_U32(ctx, &bits, 22, "program_mux_rate");
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ if ((status = STREAM_STATUS(ctx)) != VC_CONTAINER_SUCCESS) return status;
+
+ module->level--;
+
+ /* Set or update system_clock_reference, adjust bias if necessary */
+ scr = scr_base * INT64_C(300) + scr_ext;
+
+ if (module->scr_offset == VC_CONTAINER_TIME_UNKNOWN)
+ module->scr_offset = scr;
+
+ if (module->scr == VC_CONTAINER_TIME_UNKNOWN)
+ module->scr_bias = -scr;
+ else if (scr < module->scr)
+ module->scr_bias = module->scr - scr;
+
+ if (module->scr != VC_CONTAINER_TIME_UNKNOWN)
+ {
+ /* system_clock_reference is not necessarily continuous across the entire stream */
+ if (scr > module->scr)
+ {
+ int64_t data_rate;
+ data_rate = INT64_C(27000000) * (pack_offset - module->pack_offset) / (scr - module->scr);
+
+ if (module->data_rate)
+ {
+ /* Simple moving average over data rate seen so far */
+ module->data_rate = (module->data_rate * 31 + data_rate) >> 5;
+ }
+ else
+ {
+ module->data_rate = mux_rate * 50;
+ }
+ }
+
+ module->pack_offset = pack_offset;
+ }
+
+ module->scr = scr;
+ module->mux_rate = mux_rate;
+
+ /* Check for a system header */
+ if(PEEK_U32(ctx) == 0x1BB)
+ return ps_read_system_header(ctx);
+
+ return STREAM_STATUS(ctx);
+}
+
+/*****************************************************************************/
+static void ps_get_stream_coding( VC_CONTAINER_T *ctx, unsigned int stream_id,
+ VC_CONTAINER_ES_TYPE_T *p_type, VC_CONTAINER_FOURCC_T *p_codec,
+ VC_CONTAINER_FOURCC_T *p_variant)
+{
+ VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_UNKNOWN;
+ VC_CONTAINER_FOURCC_T codec = VC_CONTAINER_CODEC_UNKNOWN;
+ VC_CONTAINER_FOURCC_T variant = 0;
+
+ VC_CONTAINER_PARAM_UNUSED(ctx);
+
+ if (stream_id == 0xE2) /* FIXME: why is this stream number reserved for H264? */
+ {
+ type = VC_CONTAINER_ES_TYPE_VIDEO;
+ codec = VC_CONTAINER_CODEC_H264;
+ }
+ else if ((stream_id & 0xF0) == 0xE0)
+ {
+ type = VC_CONTAINER_ES_TYPE_VIDEO;
+ codec = VC_CONTAINER_CODEC_MP2V;
+ }
+ else if ((stream_id & 0xE0) == 0xC0)
+ {
+ type = VC_CONTAINER_ES_TYPE_AUDIO;
+ codec = VC_CONTAINER_CODEC_MPGA;
+ variant = VC_CONTAINER_VARIANT_MPGA_L2;
+ }
+
+ /* FIXME: PRIVATE_EVOB_PES_PACKET with stream_id 0xFD ? */
+
+ *p_type = type;
+ *p_codec = codec;
+ *p_variant = variant;
+}
+
+/*****************************************************************************/
+static int64_t ps_pes_time_to_us( VC_CONTAINER_T *ctx, int64_t time )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+
+ if (time == VC_CONTAINER_TIME_UNKNOWN)
+ return VC_CONTAINER_TIME_UNKNOWN;
+
+ /* Need to wait for system_clock_reference first */
+ if (module->scr_bias == VC_CONTAINER_TIME_UNKNOWN)
+ return VC_CONTAINER_TIME_UNKNOWN;
+
+ /* Can't have valid bias without known system_clock_reference */
+ vc_container_assert(module->scr != VC_CONTAINER_TIME_UNKNOWN);
+
+ /* 90kHz (PES) clock --> (zero based) 27MHz system clock --> microseconds */
+ return (INT64_C(300) * time + module->scr_bias) / INT64_C(27);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T ps_read_pes_time( VC_CONTAINER_T *ctx,
+ uint32_t *p_length, unsigned int pts_dts, int64_t *p_pts, int64_t *p_dts )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint8_t header[10];
+ uint32_t length = *p_length;
+ VC_CONTAINER_BITS_T bits;
+ int64_t pts, dts;
+
+ if (p_pts) *p_pts = VC_CONTAINER_TIME_UNKNOWN;
+ if (p_dts) *p_dts = VC_CONTAINER_TIME_UNKNOWN;
+
+ if (pts_dts == 0x2)
+ {
+ /* PTS only */
+ LOG_FORMAT(ctx, "PTS");
+ ctx->priv->module->level++;
+ if(length < 5) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, header, 5) != 5) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 5);
+
+ if(BITS_READ_U32(ctx, &bits, 4, "'0010' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED;
+ pts = BITS_READ_U32(ctx, &bits, 3, "PTS [32..30]") << 30;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [29..15]") << 15;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [14..0]");
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ LOG_FORMAT(ctx, "PTS %"PRId64, pts);
+ if (p_pts) *p_pts = pts;
+ length -= 5;
+ ctx->priv->module->level--;
+ }
+ else if (pts_dts == 0x3)
+ {
+ /* PTS & DTS */
+ LOG_FORMAT(ctx, "PTS DTS");
+ ctx->priv->module->level++;
+ if(length < 10) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, header, 10) != 10) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 10);
+
+ /* PTS */
+ if(BITS_READ_U32(ctx, &bits, 4, "'0011' marker bits") != 0x3) return VC_CONTAINER_ERROR_CORRUPTED;
+ pts = BITS_READ_U32(ctx, &bits, 3, "PTS [32..30]") << 30;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [29..15]") << 15;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ pts |= BITS_READ_U32(ctx, &bits, 15, "PTS [14..0]");
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ /* DTS */
+ if(BITS_READ_U32(ctx, &bits, 4, "'0001' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ dts = BITS_READ_U32(ctx, &bits, 3, "DTS [32..30]") << 30;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ dts |= BITS_READ_U32(ctx, &bits, 15, "DTS [29..15]") << 15;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ dts |= BITS_READ_U32(ctx, &bits, 15, "DTS [14..0]");
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ LOG_FORMAT(ctx, "PTS %"PRId64, pts);
+ LOG_FORMAT(ctx, "DTS %"PRId64, dts);
+ if (p_pts) *p_pts = pts;
+ if (p_dts) *p_dts = dts;
+ length -= 10;
+ ctx->priv->module->level--;
+ }
+ else
+ {
+ status = VC_CONTAINER_ERROR_NOT_FOUND;
+ }
+
+ *p_length = *p_length - length;
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T ps_read_pes_extension( VC_CONTAINER_T *ctx,
+ uint32_t *p_length )
+{
+ unsigned int pes_private_data, pack_header, packet_seq_counter, pstd_buffer, extension2;
+ uint8_t header[2];
+ uint32_t length = *p_length;
+ VC_CONTAINER_BITS_T bits;
+ unsigned int i;
+
+ LOG_FORMAT(ctx, "PES_extension");
+ ctx->priv->module->level++;
+ if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, header, 1) != 1) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 1);
+
+ pes_private_data = BITS_READ_U32(ctx, &bits, 1, "PES_private_data_flag");
+ pack_header = BITS_READ_U32(ctx, &bits, 1, "pack_header_field_flag");
+ packet_seq_counter = BITS_READ_U32(ctx, &bits, 1, "program_packet_sequence_counter_flag");
+ pstd_buffer = BITS_READ_U32(ctx, &bits, 1, "P-STD_buffer_flag");
+ BITS_SKIP(ctx, &bits, 3, "3 reserved_bits");
+ extension2 = BITS_READ_U32(ctx, &bits, 1, "PES_extension_flag_2");
+ length -= 1;
+
+ if (pes_private_data)
+ {
+ if(length < 16) return VC_CONTAINER_ERROR_CORRUPTED;
+ SKIP_BYTES(ctx, 16); /* PES_private_data */
+ length -= 16;
+ }
+
+ if (pack_header)
+ {
+ unsigned int pack_field_len;
+ if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED;
+ pack_field_len = READ_U8(ctx, "pack_field_length");
+ length -= 1;
+ if(length < pack_field_len) return VC_CONTAINER_ERROR_CORRUPTED;
+ SKIP_BYTES(ctx, pack_field_len); /* pack_header */
+ length -= pack_field_len;
+ }
+
+ if (packet_seq_counter)
+ {
+ if(length < 2) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, header, 2) != 2) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 2);
+
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ BITS_SKIP(ctx, &bits, 7, "program_packet_sequence_counter");
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ BITS_SKIP(ctx, &bits, 1, "MPEG1_MPEG2_identifier");
+ BITS_SKIP(ctx, &bits, 6, "original_stuff_length");
+ length -= 2;
+ }
+
+ if (pstd_buffer)
+ {
+ if(length < 2) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, header, 2) != 2) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 2);
+
+ if(BITS_READ_U32(ctx, &bits, 2, "'01' marker bits") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ BITS_SKIP(ctx, &bits, 1, "P-STD_buffer_scale");
+ BITS_SKIP(ctx, &bits, 13, "P-STD_buffer_size");
+ length -= 2;
+ }
+
+ if (extension2)
+ {
+ uint8_t ext_field_len;
+
+ if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, &ext_field_len, 1) != 1) return VC_CONTAINER_ERROR_EOS;
+ length -= 1;
+
+ if((ext_field_len & 0x80) != 0x80) return VC_CONTAINER_ERROR_CORRUPTED; /* marker_bit */
+ ext_field_len &= ~0x80;
+ LOG_FORMAT(ctx, "PES_extension_field_length %d", ext_field_len);
+
+ for (i = 0; i < ext_field_len; i++)
+ {
+ SKIP_U8(ctx, "reserved");
+ length--;
+ }
+ }
+
+ ctx->priv->module->level--;
+
+ *p_length = *p_length - length; /* Number of bytes read from stream */
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T ps_read_pes_packet_header( VC_CONTAINER_T *ctx,
+ uint32_t *p_length, int64_t *p_pts, int64_t *p_dts )
+{
+ VC_CONTAINER_STATUS_T status;
+ VC_CONTAINER_BITS_T bits;
+ uint32_t size, length = *p_length;
+ unsigned int pts_dts;
+ uint8_t header[10];
+
+ if(length < 3) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ if ((PEEK_U8(ctx) & 0xC0) == 0x80) /* program stream */
+ {
+ unsigned int escr, es_rate, dsm_trick_mode, additional_copy_info, pes_crc, pes_extension;
+ unsigned int header_length;
+
+ if(READ_BYTES(ctx, header, 3) != 3) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 3);
+
+ if (BITS_READ_U32(ctx, &bits, 2, "'10' marker bits") != 0x2) return VC_CONTAINER_ERROR_CORRUPTED;
+ BITS_SKIP(ctx, &bits, 2, "PES_scrambling_control");
+ BITS_SKIP(ctx, &bits, 1, "PES_priority");
+ BITS_SKIP(ctx, &bits, 1, "data_alignment_indicator");
+ BITS_SKIP(ctx, &bits, 1, "copyright");
+ BITS_SKIP(ctx, &bits, 1, "original_or_copy");
+ pts_dts = BITS_READ_U32(ctx, &bits, 2, "PTS_DTS_flags");
+ escr = BITS_READ_U32(ctx, &bits, 1, "ESCR_flag");
+ es_rate = BITS_READ_U32(ctx, &bits, 1, "ES_rate_flag");
+ dsm_trick_mode = BITS_READ_U32(ctx, &bits, 1, "DSM_trick_mode_flag");
+ additional_copy_info = BITS_READ_U32(ctx, &bits, 1, "additional_copy_info_flag");
+ pes_crc = BITS_READ_U32(ctx, &bits, 1, "PES_CRC_flag");
+ pes_extension = BITS_READ_U32(ctx, &bits, 1, "PES_extension_flag");
+ header_length = BITS_READ_U32(ctx, &bits, 8, "PES_header_data_length");
+ length -= 3;
+
+ size = length;
+ status = ps_read_pes_time(ctx, &size, pts_dts, p_pts, p_dts);
+ if (status && status != VC_CONTAINER_ERROR_NOT_FOUND) return status;
+ length -= size;
+ header_length -= size;
+
+ if (escr)
+ {
+ /* Elementary stream clock reference */
+ int64_t escr;
+
+ ctx->priv->module->level++;
+ if(length < 6) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 6);
+
+ BITS_SKIP(ctx, &bits, 2, "reserved_bits");
+ escr = BITS_READ_U32(ctx, &bits, 3, "ESCR_base [32..30]") << 30;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ escr |= BITS_READ_U32(ctx, &bits, 15, "ESCR_base [29..15]") << 15;
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ escr |= BITS_READ_U32(ctx, &bits, 15, "ESCR_base [14..0]");
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ BITS_READ_U32(ctx, &bits, 9, "ESCR_extension");
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ LOG_FORMAT(ctx, "ESCR_base %"PRId64, escr);
+ length -= 6;
+ header_length -= 6;
+ ctx->priv->module->level--;
+ }
+
+ if (es_rate)
+ {
+ /* Elementary stream rate */
+ if(length < 3) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, header, 3) != 3) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 3);
+
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ BITS_READ_U32(ctx, &bits, 22, "ES_rate");
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ length -= 3;
+ header_length -= 3;
+ }
+
+ if (dsm_trick_mode)
+ {
+ unsigned int trick_mode;
+
+ if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, header, 1) != 1) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 1);
+
+ trick_mode = BITS_READ_U32(ctx, &bits, 3, "trick_mode_control");
+ if (trick_mode == 0x0 /* fast_forward */)
+ {
+ BITS_SKIP(ctx, &bits, 2, "field_id");
+ BITS_SKIP(ctx, &bits, 1, "intra_slice_refresh");
+ BITS_SKIP(ctx, &bits, 2, "frequency_truncation");
+ }
+ else if (trick_mode == 0x1 /* slow_motion */)
+ {
+ BITS_SKIP(ctx, &bits, 5, "rep_cntrl");
+ }
+ else if (trick_mode == 0x2 /* freeze_frame */)
+ {
+ BITS_SKIP(ctx, &bits, 2, "field_id");
+ BITS_SKIP(ctx, &bits, 3, "reserved_bits");
+ }
+ else if (trick_mode == 0x3 /* fast_reverse */)
+ {
+ BITS_SKIP(ctx, &bits, 2, "field_id");
+ BITS_SKIP(ctx, &bits, 1, "intra_slice_refresh");
+ BITS_SKIP(ctx, &bits, 2, "frequency_truncation");
+ }
+ else if (trick_mode == 0x4 /* slow_reverse */)
+ BITS_SKIP(ctx, &bits, 5, "rep_cntrl");
+ else
+ BITS_SKIP(ctx, &bits, 5, "5 reserved_bits");
+
+ length -= 1;
+ header_length -= 1;
+ }
+
+ if (additional_copy_info)
+ {
+ if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, header, 1) != 1) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 1);
+
+ if(BITS_READ_U32(ctx, &bits, 1, "marker_bit") != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ BITS_SKIP(ctx, &bits, 7, "additional_copy_info");
+
+ length -= 1;
+ header_length -= 1;
+ }
+
+ if (pes_crc)
+ {
+ SKIP_U16(ctx, "previous_PES_packet_CRC");
+ length -= 2;
+ header_length -= 2;
+ }
+
+ if (pes_extension)
+ {
+ size = length;
+ if ((status = ps_read_pes_extension(ctx, &size)) != VC_CONTAINER_SUCCESS) return status;
+ length -= size;
+ header_length -= size;
+ }
+
+ if (header_length <= length)
+ {
+ SKIP_BYTES(ctx, header_length); /* header stuffing */
+ length -= header_length;
+ }
+ }
+ else /* MPEG 1 PES header */
+ {
+ if(length < 12) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ while (PEEK_U8(ctx) == 0xFF && length > 0)
+ {
+ SKIP_U8(ctx, "stuffing");
+ length--;
+ }
+
+ if (length == 0) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ if ((PEEK_U8(ctx) & 0xC0) == 0x40)
+ {
+ if(length < 2) return VC_CONTAINER_ERROR_CORRUPTED;
+ SKIP_U8(ctx, "???");
+ SKIP_U8(ctx, "???");
+ length -= 2;
+ }
+
+ pts_dts = (PEEK_U8(ctx) & 0x30) >> 4;
+ size = length;
+ status = ps_read_pes_time(ctx, &size, pts_dts, p_pts, p_dts);
+ if (status && status != VC_CONTAINER_ERROR_NOT_FOUND)
+ return status;
+ length -= size;
+
+ if (status == VC_CONTAINER_ERROR_NOT_FOUND)
+ {
+ if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED;
+ SKIP_U8(ctx, "???");
+ length -= 1;
+ }
+ }
+
+ *p_length = length;
+ return STREAM_STATUS(ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T ps_read_private_stream_1_coding( VC_CONTAINER_T *ctx,
+ VC_CONTAINER_ES_TYPE_T *p_type, VC_CONTAINER_FOURCC_T *p_codec,
+ uint32_t *substream_id, uint32_t *p_length )
+{
+ VC_CONTAINER_ES_TYPE_T type = VC_CONTAINER_ES_TYPE_UNKNOWN;
+ VC_CONTAINER_FOURCC_T codec = VC_CONTAINER_CODEC_UNKNOWN;
+ uint32_t length;
+ uint8_t id = 0;
+
+ length = *p_length;
+
+ if(length < 1) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, &id, 1) != 1) return VC_CONTAINER_ERROR_EOS;
+ length -= 1;
+
+ LOG_FORMAT(ctx, "private_stream_1 byte: 0x%x (%u)", id, id);
+
+ if (id >= 0x20 && id <= 0x3f)
+ {
+ type = VC_CONTAINER_ES_TYPE_SUBPICTURE;
+ codec = VC_CONTAINER_CODEC_UNKNOWN;
+ }
+ else if ((id >= 0x80 && id <= 0x87) || (id >= 0xC0 && id <= 0xCF))
+ {
+ type = VC_CONTAINER_ES_TYPE_AUDIO;
+ codec = VC_CONTAINER_CODEC_AC3;
+ }
+ else if ((id >= 0x88 && id <= 0x8F) || (id >= 0x98 && id <= 0x9F))
+ {
+ type = VC_CONTAINER_ES_TYPE_AUDIO;
+ codec = VC_CONTAINER_CODEC_DTS;
+ }
+ else if (id >= 0xA0 && id <= 0xBF)
+ {
+ type = VC_CONTAINER_ES_TYPE_AUDIO;
+ codec = VC_CONTAINER_CODEC_PCM_SIGNED;
+ }
+ else
+ {
+ LOG_FORMAT(ctx, "Unknown private_stream_1 byte: 0x%x (%u)", id, id);
+ }
+
+ *substream_id = id;
+ *p_type = type;
+ *p_codec = codec;
+ *p_length = length;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T ps_read_private_stream_1_format( VC_CONTAINER_T *ctx,
+ VC_CONTAINER_ES_FORMAT_T *format, uint32_t *length )
+{
+ uint8_t header[8];
+ VC_CONTAINER_BITS_T bits;
+
+ if (format->codec == VC_CONTAINER_CODEC_PCM_SIGNED)
+ {
+ static const unsigned fs_tab[4] = { 48000, 96000, 44100, 32000 };
+ static const unsigned bps_tab[] = {16, 20, 24, 0};
+
+ unsigned fs, bps, nchan;
+
+ if(*length < 6) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, header, 6) != 6) return VC_CONTAINER_ERROR_EOS;
+ BITS_INIT(ctx, &bits, header, 6);
+
+ BITS_SKIP(ctx, &bits, 8, "???");
+ BITS_SKIP(ctx, &bits, 8, "???");
+ BITS_SKIP(ctx, &bits, 8, "???");
+ BITS_SKIP(ctx, &bits, 1, "emphasis");
+ BITS_SKIP(ctx, &bits, 1, "mute");
+ BITS_SKIP(ctx, &bits, 1, "reserved");
+ BITS_SKIP(ctx, &bits, 5, "frame number");
+ bps = BITS_READ_U32(ctx, &bits, 2, "quant");
+ fs = BITS_READ_U32(ctx, &bits, 2, "freq");
+ BITS_SKIP(ctx, &bits, 1, "reserved");
+ nchan = BITS_READ_U32(ctx, &bits, 3, "channels");
+ *length -= 6;
+
+ format->type->audio.sample_rate = fs_tab[fs];
+ format->type->audio.bits_per_sample = bps_tab[bps];
+ format->type->audio.channels = nchan + 1;
+ format->type->audio.block_align =
+ (format->type->audio.channels * format->type->audio.bits_per_sample + 7 ) / 8;
+ }
+ else
+ {
+ if(*length < 3) return VC_CONTAINER_ERROR_CORRUPTED;
+ SKIP_U8(ctx, "num of frames");
+ SKIP_U8(ctx, "start pos hi");
+ SKIP_U8(ctx, "start pos lo");
+ *length -= 3;
+ }
+
+ return STREAM_STATUS(ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T ps_read_pes_packet( VC_CONTAINER_T *ctx )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ uint8_t header[10];
+ VC_CONTAINER_BITS_T bits;
+ uint32_t length, stream_id, substream_id = 0;
+ VC_CONTAINER_ES_TYPE_T type;
+ VC_CONTAINER_FOURCC_T codec, variant = 0;
+ VC_CONTAINER_TRACK_T *track;
+ int64_t pts, dts;
+
+ if(_READ_U24(ctx) != 0x1) return VC_CONTAINER_ERROR_CORRUPTED;
+ if(READ_BYTES(ctx, header, 3) != 3) return VC_CONTAINER_ERROR_EOS;
+ LOG_FORMAT(ctx, "pes_packet_header");
+ module->level++;
+
+ BITS_INIT(ctx, &bits, header, 3);
+ stream_id = BITS_READ_U32(ctx, &bits, 8, "stream_id");
+ length = BITS_READ_U32(ctx, &bits, 16, "PES_packet_length");
+
+ if (stream_id < 0xBC) return VC_CONTAINER_ERROR_CORRUPTED;
+
+ if (stream_id == 0xBC /* program_stream_map */ || stream_id == 0xBE /* padding_stream */ ||
+ stream_id == 0xBF /* private_stream_2 */ || stream_id == 0xF0 /* ECM */ ||
+ stream_id == 0xF1 /* EMM */ || stream_id == 0xFF /* program_stream_directory */ ||
+ stream_id == 0xF2 /* DSMCC_stream */ || stream_id == 0xF8 /* ITU-T Rec. H.222.1 type E */)
+ goto skip;
+
+ /* Parse PES packet header */
+ if ((status = ps_read_pes_packet_header(ctx, &length, &pts, &dts)) != VC_CONTAINER_SUCCESS)
+ return status;
+
+ /* For private_stream_1, encoding format is stored in the payload */
+ if (stream_id == 0xBD)
+ {
+ status = ps_read_private_stream_1_coding(ctx, &type, &codec, &substream_id, &length);
+ if (status) return status;
+ }
+ else
+ ps_get_stream_coding(ctx, stream_id, &type, &codec, &variant);
+
+ /* Check that we know what to do with this track */
+ if(type == VC_CONTAINER_ES_TYPE_UNKNOWN || codec == VC_CONTAINER_CODEC_UNKNOWN)
+ goto skip;
+
+ track = ps_find_track(ctx, stream_id, substream_id, module->searching_tracks);
+ if(!track) goto skip;
+
+ if (module->searching_tracks)
+ {
+ track->is_enabled = true;
+ track->format->es_type = type;
+ track->format->codec = codec;
+ track->format->codec_variant = variant;
+
+ /* For private_stream_1, we need to parse payload further to get elementary stream
+ format */
+ if (stream_id == 0xBD)
+ {
+ uint32_t current_length = length;
+ status = ps_read_private_stream_1_format(ctx, track->format, &length);
+ if (status) return status;
+ track->priv->module->payload_offset = current_length - length;
+ }
+
+ goto skip;
+ }
+ else
+ {
+ unsigned i;
+ SKIP_BYTES(ctx, track->priv->module->payload_offset);
+ length -= track->priv->module->payload_offset;
+
+ /* Find track index */
+ for(i = 0; i < ctx->tracks_num; i++)
+ if(ctx->tracks[i] == track) break;
+ vc_container_assert(i < ctx->tracks_num);
+
+ module->packet_track = i;
+ module->packet_data_size = length;
+ module->packet_pts = pts;
+ module->packet_dts = dts;
+ }
+
+end:
+ module->level--;
+ return STREAM_STATUS(ctx);
+
+skip:
+ SKIP_BYTES(ctx, length); /* remaining PES_packet_data */
+ goto end;
+}
+
+/*****************************************************************************/
+STATIC_INLINE VC_CONTAINER_STATUS_T ps_find_pes_packet( VC_CONTAINER_T *ctx )
+{
+ VC_CONTAINER_STATUS_T status;
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ uint8_t buffer[4];
+ unsigned int i;
+
+ module->packet_data_size = 0;
+
+ for (i = 0; i != PS_PACK_SCAN_MAX; ++i)
+ {
+ if((status = ps_find_start_code(ctx, buffer)) != VC_CONTAINER_SUCCESS)
+ break;
+
+ if (buffer[3] == 0xBA && ((status = ps_read_pack_header(ctx)) != VC_CONTAINER_SUCCESS))
+ continue; /* pack start code but parsing failed, goto resync */
+
+ if ((status = ps_read_pes_packet(ctx)) == VC_CONTAINER_SUCCESS)
+ break;
+ }
+
+ return status;
+}
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+*****************************************************************************/
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T ps_reader_read( VC_CONTAINER_T *ctx,
+ VC_CONTAINER_PACKET_T *p_packet, uint32_t flags )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+
+ vc_container_assert(!module->searching_tracks);
+
+ while(!module->packet_data_left)
+ {
+ if(ps_find_pes_packet(ctx) != VC_CONTAINER_SUCCESS)
+ {
+ status = VC_CONTAINER_ERROR_EOS;
+ goto error;
+ }
+
+ module->packet_data_left = module->packet_data_size;
+ }
+
+ p_packet->track = module->packet_track;
+ p_packet->size = module->packet_data_left;
+ p_packet->flags = 0;
+ p_packet->pts = ps_pes_time_to_us(ctx, module->packet_pts);
+ p_packet->dts = ps_pes_time_to_us(ctx, module->packet_dts);
+
+ if (flags & VC_CONTAINER_READ_FLAG_SKIP)
+ {
+ SKIP_BYTES(ctx, module->packet_data_left);
+ module->packet_data_left = 0;
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ if (flags & VC_CONTAINER_READ_FLAG_INFO)
+ return VC_CONTAINER_SUCCESS;
+
+ p_packet->size = MIN(p_packet->buffer_size, module->packet_data_left);
+ p_packet->size = READ_BYTES(ctx, p_packet->data, p_packet->size);
+ module->packet_data_left -= p_packet->size;
+
+ /* Temporary work-around for lpcm audio */
+ {
+ VC_CONTAINER_TRACK_T *track = ctx->tracks[module->packet_track];
+ if (track->format->codec == VC_CONTAINER_CODEC_PCM_SIGNED)
+ {
+ unsigned i;
+ uint16_t *ptr = (uint16_t *)p_packet->data;
+
+ for (i = 0; i < p_packet->size / 2; i ++)
+ {
+ uint32_t v = *ptr;
+ *ptr++ = v >> 8 | ( (v & 0xFF) << 8 );
+ }
+ }
+ }
+
+ if (module->packet_data_left)
+ module->packet_pts = module->packet_dts = VC_CONTAINER_TIME_UNKNOWN;
+
+ return STREAM_STATUS(ctx);
+
+error:
+ if (status == VC_CONTAINER_ERROR_EOS)
+ {
+ /* Reset time reference and calculation state */
+ ctx->priv->module->scr = VC_CONTAINER_TIME_UNKNOWN;
+ ctx->priv->module->scr_bias = -module->scr_offset;
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T ps_reader_seek( VC_CONTAINER_T *ctx,
+ int64_t *p_offset, VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint64_t seekpos, position;
+ int64_t scr;
+
+ VC_CONTAINER_PARAM_UNUSED(flags);
+
+ if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(ctx))
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ position = STREAM_POSITION(ctx);
+ scr = module->scr;
+
+ if (*p_offset == INT64_C(0))
+ seekpos = module->data_offset;
+ else
+ {
+ if (!ctx->duration)
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ /* The following is an estimate that might be quite inaccurate */
+ seekpos = module->data_offset + (*p_offset * module->data_size) / ctx->duration;
+ }
+
+ SEEK(ctx, seekpos);
+ module->scr = module->scr_offset;
+ status = ps_find_pes_packet(ctx);
+ if (status && status != VC_CONTAINER_ERROR_EOS)
+ goto error;
+
+ module->packet_data_left = module->packet_data_size;
+
+ if (module->packet_pts != VC_CONTAINER_TIME_UNKNOWN)
+ *p_offset = ps_pes_time_to_us(ctx, module->packet_pts);
+ else if (module->data_size)
+ *p_offset = (STREAM_POSITION(ctx) - module->data_offset) * ctx->duration / module->data_size;
+
+ return STREAM_STATUS(ctx);
+
+error:
+ module->scr = scr;
+ SEEK(ctx, position);
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T ps_reader_close( VC_CONTAINER_T *ctx )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ unsigned int i;
+
+ for(i = 0; i < ctx->tracks_num; i++)
+ vc_container_free_track(ctx, ctx->tracks[i]);
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T ps_reader_open( VC_CONTAINER_T *ctx )
+{
+ const char *extension = vc_uri_path_extension(ctx->priv->uri);
+ VC_CONTAINER_MODULE_T *module = 0;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ uint8_t buffer[4];
+ unsigned int i;
+
+ /* Check if the user has specified a container */
+ vc_uri_find_query(ctx->priv->uri, 0, "container", &extension);
+
+ /* Since MPEG is difficult to auto-detect, we use the extension as
+ part of the autodetection */
+ if(!extension)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if(strcasecmp(extension, "ps") && strcasecmp(extension, "vob") &&
+ strcasecmp(extension, "mpg") && strcasecmp(extension, "mp2") &&
+ strcasecmp(extension, "mp3") && strcasecmp(extension, "mpeg"))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ if((status = ps_find_start_code(ctx, buffer)) != VC_CONTAINER_SUCCESS)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED; /* We didn't find a valid pack or PES packet */
+
+ LOG_DEBUG(ctx, "using ps reader");
+
+ /* We are probably dealing with a PS file */
+ LOG_FORMAT(ctx, "MPEG PS reader, found start code: 0x%02x%02x%02x%02x",
+ buffer[0], buffer[1], buffer[2], buffer[3]);
+
+ /* Need to allocate context before searching for streams */
+ module = malloc(sizeof(*module));
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ ctx->priv->module = module;
+ ctx->tracks = module->tracks;
+
+ /* Store offset so we can get back to what we consider the first pack or
+ packet */
+ module->data_offset = STREAM_POSITION(ctx);
+
+ /* Search for tracks, reset time reference and calculation state first */
+ ctx->priv->module->scr_offset = ctx->priv->module->scr = VC_CONTAINER_TIME_UNKNOWN;
+ ctx->priv->module->searching_tracks = true;
+
+ for (i = 0; i != PS_PACK_SCAN_MAX; ++i)
+ {
+ if (buffer[3] == 0xBA && (ps_read_pack_header(ctx) != VC_CONTAINER_SUCCESS))
+ goto resync;
+
+ if (ps_read_pes_packet(ctx) == VC_CONTAINER_SUCCESS)
+ continue;
+
+resync:
+ LOG_DEBUG(ctx, "Lost sync, scanning for start code");
+ if((status = ps_find_start_code(ctx, buffer)) != VC_CONTAINER_SUCCESS)
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ LOG_DEBUG(ctx, "MPEG PS reader, found start code: 0x%"PRIx64" (%"PRId64"): 0x%02x%02x%02x%02x",
+ STREAM_POSITION(ctx), STREAM_POSITION(ctx), buffer[0], buffer[1], buffer[2], buffer[3]);
+ }
+
+ /* Seek back to the start of data */
+ SEEK(ctx, module->data_offset);
+
+ /* Bail out if we didn't find any tracks */
+ if(!ctx->tracks_num)
+ {
+ status = VC_CONTAINER_ERROR_NO_TRACK_AVAILABLE;
+ goto error;
+ }
+
+ /* Set data size (necessary for seeking) */
+ module->data_size = MAX(ctx->priv->io->size - module->data_offset, INT64_C(0));
+
+ /* Estimate data rate (necessary for seeking) */
+ if(STREAM_SEEKABLE(ctx))
+ {
+ /* Estimate data rate by jumping in the stream */
+ #define PS_PACK_SEARCH_MAX 64
+ uint64_t position = module->data_offset;
+ for (i = 0; i != PS_PACK_SEARCH_MAX; ++i)
+ {
+ position += (module->data_size / (PS_PACK_SEARCH_MAX + 1));
+ SEEK(ctx, position);
+
+ for(;;)
+ {
+ if(ps_find_start_code(ctx, buffer) != VC_CONTAINER_SUCCESS)
+ break;
+
+ if (buffer[3] == 0xBA)
+ {
+ if (ps_read_pack_header(ctx) == VC_CONTAINER_SUCCESS)
+ break;
+ }
+ else
+ {
+ /* Skip PES packet */
+ unsigned length;
+ SKIP_U32(ctx, "PES packet startcode");
+ length = READ_U16(ctx, "PES packet length");
+ SKIP_BYTES(ctx, length);
+ }
+ }
+ }
+
+ ctx->duration = (INT64_C(1000000) * module->data_size) / (module->data_rate);
+
+ if (module->scr > module->scr_offset)
+ {
+ int64_t delta = (module->scr - module->scr_offset) / INT64_C(27);
+
+ if (delta > ctx->duration)
+ ctx->duration = delta;
+ }
+
+ /* Seek back to the start of data */
+ SEEK(ctx, module->data_offset);
+ }
+ else
+ {
+ /* For most files, program_mux_rate is not reliable at all */
+ ctx->duration = (INT64_C(100000) * module->data_size) / (INT64_C(5) * module->mux_rate);
+ }
+
+ /* Reset time reference and calculation state, we're now ready to read data */
+ module->scr = VC_CONTAINER_TIME_UNKNOWN;
+ module->scr_bias = VC_CONTAINER_TIME_UNKNOWN;
+
+ ctx->priv->module->searching_tracks = false;
+
+ if(STREAM_SEEKABLE(ctx)) ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
+
+ ctx->priv->pf_close = ps_reader_close;
+ ctx->priv->pf_read = ps_reader_read;
+ ctx->priv->pf_seek = ps_reader_seek;
+
+ return STREAM_STATUS(ctx);
+
+ error:
+ LOG_DEBUG(ctx, "ps: error opening stream (%i)", status);
+ if(module) ps_reader_close(ctx);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open ps_reader_open
+#endif
diff --git a/gfx/include/userland/containers/mpga/CMakeLists.txt b/gfx/include/userland/containers/mpga/CMakeLists.txt
new file mode 100644
index 0000000000..79cf39a344
--- /dev/null
+++ b/gfx/include/userland/containers/mpga/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(reader_mpga ${LIBRARY_TYPE} mpga_reader.c)
+
+target_link_libraries(reader_mpga containers)
+
+install(TARGETS reader_mpga DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/mpga/mpga_common.h b/gfx/include/userland/containers/mpga/mpga_common.h
new file mode 100644
index 0000000000..b8ba8e5210
--- /dev/null
+++ b/gfx/include/userland/containers/mpga/mpga_common.h
@@ -0,0 +1,147 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#define MPGA_HEADER_SIZE 6
+
+#define MPGA_MODE_STEREO 0
+#define MPGA_MODE_JSTEREO 1
+#define MPGA_MODE_DUAL 2
+#define MPGA_MODE_MONO 3
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpga_read_header( uint8_t frame_header[MPGA_HEADER_SIZE],
+ uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version,
+ unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels,
+ unsigned int *p_frame_size_samples, unsigned int *p_offset )
+{
+ static const uint16_t mpga_bitrate[2][3][15] =
+ {{{0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448}, /* MPEG1, Layer 1 */
+ {0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384}, /* MPEG1, Layer 2 */
+ {0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320}},/* MPEG1, Layer 3 */
+ {{0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256}, /* MPEG2 and MPEG2.5, Layer 1 */
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}, /* MPEG2 and MPEG2.5, Layer 2 */
+ {0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160}} /* MPEG2 and MPEG2.5, Layer 3 */};
+ static const uint16_t mpga_sample_rate[] = {44100, 48000, 32000};
+ static const uint16_t mpga_frame_size[] = {384, 1152, 576};
+
+ unsigned int version, layer, br_id, sr_id, emphasis;
+ unsigned int bitrate, sample_rate, padding, mode;
+
+ /* Check frame sync, 11 bits as we want to allow for MPEG2.5 */
+ if (frame_header[0] != 0xff || (frame_header[1] & 0xe0) != 0xe0)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ version = 4 - ((frame_header[1] >> 3) & 3);
+ layer = 4 - ((frame_header[1] >> 1) & 3 );
+ br_id = (frame_header[2] >> 4) & 0xf;
+ sr_id = (frame_header[2] >> 2) & 3;
+ padding = (frame_header[2] >> 1) & 1;
+ mode = (frame_header[3] >> 6) & 3;
+ emphasis = (frame_header[3]) & 3;
+
+ /* Check for invalid values */
+ if (version == 3 || layer == 4 || br_id == 15 || sr_id == 3 || emphasis == 2)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ if (version == 4) version = 3;
+
+ bitrate = mpga_bitrate[version == 1 ? 0 : 1][layer-1][br_id];
+ bitrate *= 1000;
+
+ sample_rate = mpga_sample_rate[sr_id];
+ sample_rate >>= (version - 1);
+
+ if (p_version) *p_version = version;
+ if (p_layer) *p_layer = layer;
+ if (p_sample_rate) *p_sample_rate = sample_rate;
+ if (p_channels) *p_channels = mode == MPGA_MODE_MONO ? 1 : 2;
+ if (p_frame_bitrate) *p_frame_bitrate = bitrate;
+ if (p_offset) *p_offset = 0;
+
+ if (p_frame_size_samples)
+ {
+ *p_frame_size_samples = mpga_frame_size[layer - 1];
+ if (version == 1 && layer == 3) *p_frame_size_samples <<= 1;
+ }
+
+ if (!p_frame_size)
+ return VC_CONTAINER_SUCCESS;
+
+ if (!bitrate)
+ *p_frame_size = 0;
+ else if (layer == 1)
+ *p_frame_size = (padding + bitrate * 12 / sample_rate) * 4;
+ else if (layer == 2)
+ *p_frame_size = padding + bitrate * 144 / sample_rate;
+ else
+ *p_frame_size = padding + bitrate * (version == 1 ? 144 : 72) / sample_rate;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T adts_read_header( uint8_t frame_header[MPGA_HEADER_SIZE],
+ uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version,
+ unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels,
+ unsigned int *p_frame_size_samples, unsigned int *p_offset )
+{
+ static const unsigned int adts_sample_rate[16] =
+ {96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350};
+ unsigned int profile, sr_id, bitrate, sample_rate, frame_size, channels, crc;
+ unsigned int frame_size_samples = 1024;
+
+ /* Check frame sync (12 bits) */
+ if (frame_header[0] != 0xff || (frame_header[1] & 0xf0) != 0xf0)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ /* Layer must be 0 */
+ if ((frame_header[1] >> 1) & 3)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ crc = !(frame_header[1] & 0x1);
+ profile = (frame_header[2] >> 6) + 1; /* MPEG-4 Audio Object Type */
+
+ sr_id = (frame_header[2] >> 2) & 0xf;
+ sample_rate = adts_sample_rate[sr_id];
+ channels = ((frame_header[2] & 0x1) << 2) | ((frame_header[3] >> 6) & 0x3);
+ frame_size = ((frame_header[3] & 0x03) << 11) | (frame_header[4] << 3) | (frame_header[5] >> 5);
+
+ if (!sample_rate || !channels || !frame_size)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ bitrate = frame_size * 8 * sample_rate / frame_size_samples;
+
+ if (p_version) *p_version = profile;
+ if (p_layer) *p_layer = 0;
+ if (p_sample_rate) *p_sample_rate = sample_rate;
+ if (p_channels) *p_channels = channels;
+ if (p_frame_bitrate) *p_frame_bitrate = bitrate;
+ if (p_frame_size) *p_frame_size = frame_size;
+ if (p_frame_size_samples) *p_frame_size_samples = frame_size_samples;
+ if (p_offset) *p_offset = crc ? 9 : 7;
+
+ return VC_CONTAINER_SUCCESS;
+}
diff --git a/gfx/include/userland/containers/mpga/mpga_packetizer.c b/gfx/include/userland/containers/mpga/mpga_packetizer.c
new file mode 100644
index 0000000000..1d7b7bffc9
--- /dev/null
+++ b/gfx/include/userland/containers/mpga/mpga_packetizer.c
@@ -0,0 +1,288 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** \file
+ * Implementation of an MPEG1/2/2.5 audio Layer I/II/III and AAC ADTS packetizer.
+ */
+
+#include
+#include
+
+#include "containers/packetizers.h"
+#include "containers/core/packetizers_private.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_logging.h"
+#include "containers/core/containers_time.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_bytestream.h"
+#include "mpga_common.h"
+
+#define MAX_FRAME_SIZE 2881 /* MPEG 2.5 Layer II, 8000 Hz, 160 kbps */
+
+VC_CONTAINER_STATUS_T mpga_packetizer_open( VC_PACKETIZER_T * );
+
+/*****************************************************************************/
+typedef struct VC_PACKETIZER_MODULE_T {
+ enum {
+ STATE_SYNC = 0,
+ STATE_SYNC_LOST,
+ STATE_SYNC_NEXT,
+ STATE_SYNC_DONE,
+ STATE_HEADER,
+ STATE_DATA,
+ } state;
+
+ VC_CONTAINER_STATUS_T (*pf_read_header)( uint8_t frame_header[MPGA_HEADER_SIZE],
+ uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version,
+ unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels,
+ unsigned int *p_frame_size_samples, unsigned int *p_offset);
+
+ uint32_t frame_size;
+ unsigned int frame_bitrate;
+ unsigned int version;
+ unsigned int layer;
+ unsigned int sample_rate;
+ unsigned int channels;
+ unsigned int frame_size_samples;
+ unsigned int offset;
+
+ unsigned int lost_sync;
+
+ unsigned int stream_version;
+ unsigned int stream_layer;
+
+ uint32_t bytes_read;
+
+} VC_PACKETIZER_MODULE_T;
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpga_packetizer_close( VC_PACKETIZER_T *p_ctx )
+{
+ free(p_ctx->priv->module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpga_packetizer_reset( VC_PACKETIZER_T *p_ctx )
+{
+ VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
+ module->lost_sync = 0;
+ module->state = STATE_SYNC;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpga_packetizer_packetize( VC_PACKETIZER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags )
+{
+ VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream;
+ VC_CONTAINER_TIME_T *time = &p_ctx->priv->time;
+ uint8_t header[MPGA_HEADER_SIZE];
+ VC_CONTAINER_STATUS_T status;
+ unsigned int version, layer;
+ int64_t pts, dts;
+
+ while(1) switch (module->state)
+ {
+ case STATE_SYNC_LOST:
+ bytestream_skip_byte( stream );
+ if( !module->lost_sync++ )
+ LOG_DEBUG(0, "lost sync");
+ module->state = STATE_SYNC;
+
+ case STATE_SYNC:
+ while( bytestream_peek( stream, header, 2 ) == VC_CONTAINER_SUCCESS )
+ {
+ /* 11 bits sync work (0xffe) */
+ if( header[0] == 0xff && (header[1] & 0xe0) == 0xe0 )
+ {
+ module->state = STATE_HEADER;
+ break;
+ }
+ bytestream_skip_byte( stream );
+ module->lost_sync++;
+ }
+ if( module->state != STATE_HEADER )
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We need more data */
+
+ case STATE_HEADER:
+ if( bytestream_peek( stream, header, MPGA_HEADER_SIZE ) != VC_CONTAINER_SUCCESS )
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
+
+ status = mpga_read_header( header,
+ &module->frame_size, &module->frame_bitrate, &module->version,
+ &module->layer, &module->sample_rate, &module->channels,
+ &module->frame_size_samples, &module->offset );
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ LOG_ERROR(0, "invalid header");
+ module->state = STATE_SYNC_LOST;
+ break;
+ }
+
+ /* Version and layer are not allowed to change mid-stream */
+ if ((module->stream_version && module->stream_version != module->version) ||
+ (module->stream_layer && module->stream_layer != module->layer))
+ {
+ LOG_ERROR(0, "invalid header");
+ module->state = STATE_SYNC_LOST;
+ break;
+ }
+ /* We currently do not support free format streams */
+ if (!module->frame_size)
+ {
+ LOG_ERROR(0, "free format not supported");
+ module->state = STATE_SYNC_LOST;
+ break;
+ }
+ module->state = STATE_SYNC_NEXT;
+ /* fall through to the next state */
+
+ case STATE_SYNC_NEXT:
+ /* To avoid being caught by emulated start codes, we also look at where the next frame is supposed to be */
+ if( bytestream_peek_at( stream, module->frame_size, header, MPGA_HEADER_SIZE ) != VC_CONTAINER_SUCCESS )
+ {
+ /* If we know there won't be anymore data then we can just assume
+ * we've got the frame we're looking for */
+ if (flags & VC_PACKETIZER_FLAG_FLUSH)
+ {
+ module->state = STATE_SYNC_DONE;
+ break;
+ }
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
+ }
+
+ status = mpga_read_header( header, 0, 0, &version, &layer, 0, 0, 0, 0 );
+ if (status != VC_CONTAINER_SUCCESS)
+ {
+ LOG_ERROR(0, "invalid next header");
+ module->state = STATE_SYNC_LOST;
+ break;
+ }
+
+ /* Version and layer are not allowed to change mid-stream */
+ if (module->version != version || module->layer != layer)
+ {
+ LOG_ERROR(0, "invalid header");
+ module->state = STATE_SYNC_LOST;
+ break;
+ }
+
+ module->state = STATE_SYNC_DONE;
+ /* fall through to the next state */
+
+ case STATE_SYNC_DONE:
+ if( module->lost_sync )
+ LOG_DEBUG(0, "recovered sync after %i bytes", module->lost_sync);
+ module->lost_sync = 0;
+
+ bytestream_skip( stream, module->offset );
+ module->stream_version = module->version;
+ module->stream_layer = module->layer;
+
+ vc_container_time_set_samplerate(time, module->sample_rate, 1);
+ bytestream_get_timestamps(stream, &pts, &dts, true);
+
+ vc_container_time_set(time, pts);
+
+ module->bytes_read = 0;
+ module->state = STATE_DATA;
+ /* fall through to the next state */
+
+ case STATE_DATA:
+ if( bytestream_size( stream ) < module->frame_size)
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
+
+ out->size = module->frame_size - module->bytes_read;
+ out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN;
+ out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END;
+
+ if(!module->bytes_read)
+ {
+ out->pts = out->dts = vc_container_time_get(time);
+ out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
+ }
+
+ if(flags & VC_PACKETIZER_FLAG_INFO)
+ return VC_CONTAINER_SUCCESS;
+
+ if(flags & VC_PACKETIZER_FLAG_SKIP)
+ {
+ bytestream_skip( stream, out->size );
+ }
+ else
+ {
+ out->size = MIN(out->size, out->buffer_size);
+ bytestream_get( stream, out->data, out->size );
+ }
+ module->bytes_read += out->size;
+
+ if(module->bytes_read == module->frame_size)
+ {
+ vc_container_time_add(time, module->frame_size_samples);
+ module->state = STATE_HEADER;
+ }
+ return VC_CONTAINER_SUCCESS;
+
+ default:
+ break;
+ };
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T mpga_packetizer_open( VC_PACKETIZER_T *p_ctx )
+{
+ VC_PACKETIZER_MODULE_T *module;
+
+ if(p_ctx->in->codec != VC_CONTAINER_CODEC_MPGA &&
+ p_ctx->in->codec != VC_CONTAINER_CODEC_MP4A)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ p_ctx->priv->module = module = malloc(sizeof(*module));
+ if(!module)
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ memset(module, 0, sizeof(*module));
+
+ if(p_ctx->in->codec == VC_CONTAINER_CODEC_MPGA)
+ module->pf_read_header = mpga_read_header;
+ else
+ module->pf_read_header = adts_read_header;
+
+ vc_container_format_copy( p_ctx->out, p_ctx->in, 0);
+ p_ctx->max_frame_size = MAX_FRAME_SIZE;
+ p_ctx->priv->pf_close = mpga_packetizer_close;
+ p_ctx->priv->pf_packetize = mpga_packetizer_packetize;
+ p_ctx->priv->pf_reset = mpga_packetizer_reset;
+ LOG_DEBUG(0, "using mpeg audio packetizer");
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_PACKETIZER_REGISTER(mpga_packetizer_open, "mpga");
diff --git a/gfx/include/userland/containers/mpga/mpga_reader.c b/gfx/include/userland/containers/mpga/mpga_reader.c
new file mode 100644
index 0000000000..da5df83c69
--- /dev/null
+++ b/gfx/include/userland/containers/mpga/mpga_reader.c
@@ -0,0 +1,618 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+#define CONTAINER_IS_BIG_ENDIAN
+//#define ENABLE_CONTAINERS_LOG_FORMAT
+//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+#define CONTAINER_HELPER_LOG_INDENT(a) 0
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+#include "mpga_common.h"
+
+/******************************************************************************
+Defines and constants.
+******************************************************************************/
+#define MPGA_XING_HAS_FRAMES 0x00000001
+#define MPGA_XING_HAS_BYTES 0x00000002
+#define MPGA_XING_HAS_TOC 0x00000004
+#define MPGA_XING_HAS_QUALITY 0x00000008
+
+#define MPGA_MAX_BAD_FRAMES 4096 /*< Maximum number of failed byte-wise syncs,
+ should be at least 2881+4 to cover the largest
+ frame size (MPEG2.5 Layer 2, 160kbit/s 8kHz)
+ + next frame header */
+
+static const unsigned int mpga_sample_rate_adts[16] =
+{96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350};
+
+static const GUID_T asf_guid_header =
+{0x75B22630, 0x668E, 0x11CF, {0xA6, 0xD9, 0x00, 0xAA, 0x00, 0x62, 0xCE, 0x6C}};
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *track;
+ uint64_t data_offset;
+ uint64_t data_size;
+ uint64_t num_frames; /**< Total number of frames (if known) */
+ unsigned int frame_size_samples; /**< Frame size in samples */
+ unsigned int bitrate; /**< Bitrate (might change on a per-frame basis if VBR) */
+ unsigned int sample_rate;
+ unsigned int channels;
+
+ /* MPEG audio header information */
+ unsigned int version; /**< 1 for MPEG1, 2 for MPEG2, etc. */
+ unsigned int layer;
+
+ /* VBR header information */
+ uint8_t xing_toc[100];
+ int xing_toc_valid;
+
+ /* Per-frame state (updated upon a read or a seek) */
+ unsigned int frame_size;
+ unsigned int frame_data_left;
+ uint64_t frame_index;
+ int64_t frame_offset;
+ int64_t frame_time_pos; /**< pts of current frame */
+ unsigned int frame_bitrate; /**< bitrate of current frame */
+
+ VC_CONTAINER_STATUS_T (*pf_parse_header)( uint8_t frame_header[MPGA_HEADER_SIZE],
+ uint32_t *p_frame_size, unsigned int *p_frame_bitrate, unsigned int *p_version,
+ unsigned int *p_layer, unsigned int *p_sample_rate, unsigned int *p_channels,
+ unsigned int *p_frame_size_samples, unsigned int *p_offset);
+
+ uint8_t extradata[2]; /**< codec extra data for aac */
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+static uint32_t PEEK_BYTES_AT( VC_CONTAINER_T *p_ctx, int64_t offset, uint8_t *buffer, int size )
+{
+ int ret;
+ int64_t current_position = STREAM_POSITION(p_ctx);
+ SEEK(p_ctx, current_position + offset);
+ ret = PEEK_BYTES(p_ctx, buffer, size);
+ SEEK(p_ctx, current_position);
+ return ret;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpga_check_frame_header( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_MODULE_T *module, uint8_t frame_header[MPGA_HEADER_SIZE] )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ return module->pf_parse_header(frame_header, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpga_sync( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ uint8_t frame_header[MPGA_HEADER_SIZE];
+ uint32_t frame_size;
+ unsigned int frame_bitrate, version, layer, sample_rate, channels;
+ unsigned int frame_size_samples, offset;
+ int sync_count = 0;
+
+ /* If we can't see a full frame header, we treat this as EOS although it
+ could be a bad stream as well, the caller should distinct between
+ these two cases */
+ if (PEEK_BYTES(p_ctx, (uint8_t*)frame_header, MPGA_HEADER_SIZE) != MPGA_HEADER_SIZE)
+ return VC_CONTAINER_ERROR_EOS;
+
+ while (sync_count++ < MPGA_MAX_BAD_FRAMES)
+ {
+ status = module->pf_parse_header(frame_header, &frame_size, &frame_bitrate,
+ &version, &layer, &sample_rate, &channels,
+ &frame_size_samples, &offset);
+ if (status == VC_CONTAINER_SUCCESS &&
+ frame_size /* We do not support free format streams */)
+ {
+ LOG_DEBUG(p_ctx, "MPEGv%d, layer %d, %d bps, %d Hz",
+ version, layer, frame_bitrate, sample_rate);
+ if (PEEK_BYTES_AT(p_ctx, (int64_t)frame_size, frame_header, MPGA_HEADER_SIZE) != MPGA_HEADER_SIZE ||
+ mpga_check_frame_header(p_ctx, module, frame_header) == VC_CONTAINER_SUCCESS)
+ break;
+
+ /* If we've reached an ID3 tag then the frame is valid as well */
+ if((frame_header[0] == 'I' && frame_header[1] == 'D' && frame_header[2] == '3') ||
+ (frame_header[0] == 'T' && frame_header[1] == 'A' && frame_header[2] == 'G'))
+ break;
+ }
+ else if (status == VC_CONTAINER_SUCCESS)
+ {
+ LOG_DEBUG(p_ctx, "free format not supported");
+ }
+
+ if (SKIP_BYTES(p_ctx, 1) != 1 || PEEK_BYTES(p_ctx, (uint8_t*)frame_header, MPGA_HEADER_SIZE) != MPGA_HEADER_SIZE)
+ return VC_CONTAINER_ERROR_EOS;
+ }
+
+ if(sync_count > MPGA_MAX_BAD_FRAMES) /* We didn't find a valid frame */
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ if (module->version)
+ {
+ /* FIXME: we don't currently care whether or not the number of channels changes mid-stream */
+ if (version != module->version || layer != module->layer)
+ {
+ LOG_DEBUG(p_ctx, "version or layer not allowed to change mid-stream");
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+ }
+ else
+ {
+ module->version = version;
+ module->layer = layer;
+ module->sample_rate = sample_rate;
+ module->channels = channels;
+ module->frame_size_samples = frame_size_samples;
+ }
+
+ if(offset) SKIP_BYTES(p_ctx, offset);
+ module->frame_data_left = module->frame_size = frame_size - offset;
+ module->frame_bitrate = frame_bitrate;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static int64_t mpga_calculate_frame_time( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ int64_t time;
+ time = INT64_C(1000000) * module->frame_index *
+ module->frame_size_samples / module->sample_rate;
+ return time;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpga_read_vbr_headers( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[0];
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_NOT_FOUND;
+ uint32_t peek_buf[1];
+ int64_t offset, start = STREAM_POSITION(p_ctx);
+
+ /* Look for XING header (immediately after layer 3 side information) */
+ offset = (module->version == 1) ? ((module->channels == 1) ? INT64_C(21) : INT64_C(36)) :
+ ((module->channels == 1) ? INT64_C(13) : INT64_C(21));
+
+ if (PEEK_BYTES_AT(p_ctx, offset, (uint8_t*)peek_buf, 4) != 4)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID; /* File would be way too small */
+
+ if (peek_buf[0] == VC_FOURCC('X','i','n','g') || peek_buf[0] == VC_FOURCC('I','n','f','o'))
+ {
+ uint32_t flags = 0, num_frames = 0, data_size = 0;
+
+ /* If the first frame has a XING header then we know it's a valid (but empty) audio
+ frame so we safely parse the header whilst skipping to the next frame */
+ SKIP_BYTES(p_ctx, offset); /* FIXME: we don't care about layer 3 side information? */
+
+ SKIP_FOURCC(p_ctx, "XING");
+ flags = READ_U32(p_ctx, "XING flags");
+
+ if (flags & MPGA_XING_HAS_FRAMES)
+ num_frames = READ_U32(p_ctx, "XING frames");
+
+ if (flags & MPGA_XING_HAS_BYTES)
+ data_size = READ_U32(p_ctx, "XING bytes");
+
+ if (flags & MPGA_XING_HAS_TOC)
+ {
+ READ_BYTES(p_ctx, module->xing_toc, sizeof(module->xing_toc));
+ /* TOC is useful only if we know the number of frames */
+ if (num_frames) module->xing_toc_valid = 1;
+ /* Ensure time zero points to first frame even if TOC is broken */
+ module->xing_toc[0] = 0;
+ }
+
+ if (flags & MPGA_XING_HAS_QUALITY)
+ SKIP_U32(p_ctx, "XING quality");
+
+ module->data_size = data_size;
+ module->num_frames = num_frames;
+
+ if (module->num_frames && module->data_size)
+ {
+ /* We can calculate average bitrate */
+ module->bitrate =
+ module->data_size * module->sample_rate * 8 / (module->num_frames * module->frame_size_samples);
+ }
+
+ p_ctx->duration = (module->num_frames * module->frame_size_samples * 1000000LL) / module->sample_rate;
+
+ /* Look for additional LAME header (follows XING) */
+ if (PEEK_BYTES(p_ctx, (uint8_t*)peek_buf, 4) != 4)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID; /* File would still be way too small */
+
+ if (peek_buf[0] == VC_FOURCC('L','A','M','E'))
+ {
+ uint32_t encoder_delay;
+
+ SKIP_FOURCC(p_ctx, "LAME");
+ SKIP_STRING(p_ctx, 5, "LAME encoder version");
+ SKIP_U8(p_ctx, "LAME tag revision/VBR method");
+ SKIP_U8(p_ctx, "LAME LP filter value");
+ SKIP_U32(p_ctx, "LAME peak signal amplitude");
+ SKIP_U16(p_ctx, "LAME radio replay gain");
+ SKIP_U16(p_ctx, "LAME audiophile replay gain");
+ SKIP_U8(p_ctx, "LAME encoder flags");
+ SKIP_U8(p_ctx, "LAME ABR/minimal bitrate");
+ encoder_delay = READ_U24(p_ctx, "LAME encoder delay/padding");
+ SKIP_U8(p_ctx, "LAME misc");
+ SKIP_U8(p_ctx, "LAME MP3 gain");
+ SKIP_U16(p_ctx, "LAME presets and surround info");
+ SKIP_U32(p_ctx, "LAME music length");
+ SKIP_U16(p_ctx, "LAME music CRC");
+ SKIP_U16(p_ctx, "LAME tag CRC");
+ track->format->type->audio.gap_delay = (encoder_delay >> 12) + module->frame_size_samples;
+ track->format->type->audio.gap_padding = encoder_delay & 0xfff;
+ }
+
+ SEEK(p_ctx, start);
+ status = VC_CONTAINER_SUCCESS;
+ }
+
+ /* FIXME: if not success, try to read 'VBRI' header */
+
+ return status;
+}
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+ *****************************************************************************/
+static VC_CONTAINER_STATUS_T mpga_reader_read( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *p_packet, uint32_t flags )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_TRACK_T *track = p_ctx->tracks[0];
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ if (module->frame_data_left == 0)
+ {
+ status = mpga_sync(p_ctx);
+ if (status != VC_CONTAINER_SUCCESS) goto error;
+ }
+
+ if (module->bitrate)
+ {
+ /* Simple moving average over bitrate values seen so far */
+ module->bitrate = (module->bitrate * 31 + module->frame_bitrate) >> 5;
+ }
+ else
+ {
+ module->bitrate = module->frame_bitrate;
+ }
+
+ /* Check if we can skip the frame straight-away */
+ if (!track->is_enabled ||
+ ((flags & VC_CONTAINER_READ_FLAG_SKIP) && !(flags & VC_CONTAINER_READ_FLAG_INFO)))
+ {
+ /* Just skip the frame */
+ SKIP_BYTES(p_ctx, module->frame_size);
+ module->frame_data_left = 0;
+ if(!track->is_enabled)
+ status = VC_CONTAINER_ERROR_CONTINUE;
+ goto end;
+ }
+
+ /* Fill in packet information */
+ p_packet->flags = p_packet->track = 0;
+ if (module->frame_data_left == module->frame_size)
+ p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME;
+ else
+ p_packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
+
+ p_packet->size = module->frame_data_left;
+
+ p_packet->pts = module->frame_time_pos;
+ p_packet->dts = VC_CONTAINER_TIME_UNKNOWN;
+
+ if ((flags & VC_CONTAINER_READ_FLAG_SKIP))
+ {
+ SKIP_BYTES(p_ctx, module->frame_size);
+ module->frame_data_left = 0;
+ goto end;
+ }
+
+ if (flags & VC_CONTAINER_READ_FLAG_INFO)
+ return VC_CONTAINER_SUCCESS;
+
+ p_packet->size = MIN(p_packet->buffer_size, module->frame_data_left);
+ p_packet->size = READ_BYTES(p_ctx, p_packet->data, p_packet->size);
+ module->frame_data_left -= p_packet->size;
+
+ end:
+ if (module->frame_data_left == 0)
+ {
+ module->frame_index++;
+ module->frame_offset += module->frame_size;
+ module->frame_time_pos = mpga_calculate_frame_time(p_ctx);
+
+#if 0 /* FIXME: is this useful e.g. progressive download? */
+ module->num_frames = MAX(module->num_frames, module->frame_index);
+ module->data_size = MAX(module->data_size, module->frame_offset);
+ p_ctx->duration = MAX(p_ctx->duration, mpga_calculate_frame_time(p_ctx));
+#endif
+ }
+
+ return status == VC_CONTAINER_SUCCESS ? STREAM_STATUS(p_ctx) : status;
+
+error:
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpga_reader_seek( VC_CONTAINER_T *p_ctx,
+ int64_t *p_offset,
+ VC_CONTAINER_SEEK_MODE_T mode,
+ VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint64_t seekpos, position = STREAM_POSITION(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(flags);
+
+ if (mode != VC_CONTAINER_SEEK_MODE_TIME || !STREAM_SEEKABLE(p_ctx))
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ if (*p_offset != INT64_C(0))
+ {
+ if (!p_ctx->duration)
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ if (module->xing_toc_valid)
+ {
+ int64_t ppm;
+ int percent, lower, upper, delta;
+
+ ppm = (*p_offset * module->sample_rate) / (module->num_frames * module->frame_size_samples);
+ ppm = MIN(ppm, INT64_C(999999));
+
+ percent = ppm / 10000;
+ delta = ppm % 10000;
+
+ lower = module->xing_toc[percent];
+ upper = percent < 99 ? module->xing_toc[percent + 1] : 256;
+
+ seekpos = module->data_offset +
+ (((module->data_size * lower) + (module->data_size * (upper - lower) * delta) / 10000) >> 8);
+ }
+ else
+ {
+ /* The following will be accurate for CBR only */
+ seekpos = module->data_offset + (*p_offset * module->data_size) / p_ctx->duration;
+ }
+ }
+ else
+ {
+ seekpos = module->data_offset;
+ }
+
+ SEEK(p_ctx, seekpos);
+ status = mpga_sync(p_ctx);
+ if (status && status != VC_CONTAINER_ERROR_EOS)
+ goto error;
+
+ module->frame_index = (*p_offset * module->num_frames + (p_ctx->duration >> 1)) / p_ctx->duration;
+ module->frame_offset = STREAM_POSITION(p_ctx) - module->data_offset;
+
+ *p_offset = module->frame_time_pos = mpga_calculate_frame_time(p_ctx);
+
+ return STREAM_STATUS(p_ctx);
+
+error:
+ SEEK(p_ctx, position);
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpga_reader_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ if (p_ctx->tracks_num != 0)
+ vc_container_free_track(p_ctx, p_ctx->tracks[0]);
+ p_ctx->tracks = NULL;
+ p_ctx->tracks_num = 0;
+ free(module);
+ p_ctx->priv->module = 0;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T mpga_reader_open( VC_CONTAINER_T *p_ctx )
+{
+ const char *extension = vc_uri_path_extension(p_ctx->priv->uri);
+ VC_CONTAINER_MODULE_T *module = 0;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINER_TRACK_T *track = NULL;
+ unsigned int i;
+ GUID_T guid;
+
+ /* Check if the user has specified a container */
+ vc_uri_find_query(p_ctx->priv->uri, 0, "container", &extension);
+
+ /* Since mpeg audio is difficult to auto-detect, we use the extension as
+ part of the autodetection */
+ if(!extension)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if(strcasecmp(extension, "mp3") && strcasecmp(extension, "mp2") &&
+ strcasecmp(extension, "aac") && strcasecmp(extension, "adts"))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Check we're not in fact dealing with an ASF file */
+ if(PEEK_BYTES(p_ctx, (uint8_t *)&guid, sizeof(guid)) == sizeof(guid) &&
+ !memcmp(&guid, &asf_guid_header, sizeof(guid)))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ LOG_DEBUG(p_ctx, "using mpga reader");
+
+ /* Allocate our context */
+ if ((module = malloc(sizeof(*module))) == NULL)
+ {
+ status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ goto error;
+ }
+
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks = &module->track;
+
+ p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0);
+ if(!p_ctx->tracks[0])
+ {
+ status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ goto error;
+ }
+ p_ctx->tracks_num = 1;
+
+ module->pf_parse_header = mpga_read_header;
+ if(!strcasecmp(extension, "aac") || !strcasecmp(extension, "adts"))
+ module->pf_parse_header = adts_read_header;
+
+ if ((status = mpga_sync(p_ctx)) != VC_CONTAINER_SUCCESS)
+ {
+ /* An error here probably means it's not an mpga file at all */
+ if(status == VC_CONTAINER_ERROR_FORMAT_INVALID)
+ status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ goto error;
+ }
+
+ /* If we got this far, we're probably dealing with an mpeg audio file */
+ track = p_ctx->tracks[0];
+ track->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO;
+ track->format->codec = VC_CONTAINER_CODEC_MPGA;
+ if(module->pf_parse_header == adts_read_header)
+ {
+ uint8_t *extra = track->format->extradata = module->extradata;
+ unsigned int sr_id;
+ for( sr_id = 0; sr_id < 13; sr_id++ )
+ if( mpga_sample_rate_adts[sr_id] == module->sample_rate ) break;
+ extra[0] = (module->version << 3) | ((sr_id & 0xe) >> 1);
+ extra[1] = ((sr_id & 0x1) << 7) | (module->channels << 3);
+ track->format->extradata_size = 2;
+ track->format->codec = VC_CONTAINER_CODEC_MP4A;
+ }
+ track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+ track->is_enabled = true;
+ track->format->type->audio.channels = module->channels;
+ track->format->type->audio.sample_rate = module->sample_rate;
+ track->format->type->audio.bits_per_sample = 0;
+ track->format->type->audio.block_align = 1;
+
+ module->data_offset = STREAM_POSITION(p_ctx);
+
+ /* Look for VBR headers within the first frame */
+ status = mpga_read_vbr_headers(p_ctx);
+ if (status && status != VC_CONTAINER_ERROR_NOT_FOUND) goto error;
+
+ /* If we couldn't get this information from VBR headers, try to determine
+ file size, bitrate, number of frames and duration */
+ if (!module->data_size)
+ module->data_size = MAX(p_ctx->priv->io->size - module->data_offset, INT64_C(0));
+
+ if (!module->bitrate)
+ {
+ if (STREAM_SEEKABLE(p_ctx))
+ {
+ /* Scan past a few hundred frames (audio will often have
+ silence in the beginning so we need to see more than
+ just a few frames) and estimate bitrate */
+ for (i = 0; i < 256; ++i)
+ if (mpga_reader_read(p_ctx, NULL, VC_CONTAINER_READ_FLAG_SKIP)) break;
+ /* Seek back to start of data */
+ SEEK(p_ctx, module->data_offset);
+ module->frame_index = 0;
+ module->frame_offset = INT64_C(0);
+ module->frame_time_pos = mpga_calculate_frame_time(p_ctx);
+ }
+ else
+ {
+ /* Bitrate will be correct for CBR only */
+ module->bitrate = module->frame_bitrate;
+ }
+ }
+
+ track->format->bitrate = module->bitrate;
+
+ if (!module->num_frames)
+ {
+ module->num_frames = (module->data_size * module->sample_rate * 8LL) /
+ (module->bitrate * module->frame_size_samples);
+ }
+
+ if (!p_ctx->duration && module->bitrate)
+ {
+ p_ctx->duration = (INT64_C(8000000) * module->data_size) / module->bitrate;
+ }
+
+ p_ctx->priv->pf_close = mpga_reader_close;
+ p_ctx->priv->pf_read = mpga_reader_read;
+ p_ctx->priv->pf_seek = mpga_reader_seek;
+
+ if(STREAM_SEEKABLE(p_ctx)) p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
+
+ if(STREAM_STATUS(p_ctx) != VC_CONTAINER_SUCCESS) goto error;
+ return VC_CONTAINER_SUCCESS;
+
+error:
+ if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS)
+ status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ LOG_DEBUG(p_ctx, "error opening stream (%i)", status);
+ if (p_ctx->tracks_num != 0)
+ vc_container_free_track(p_ctx, p_ctx->tracks[0]);
+ p_ctx->tracks = NULL;
+ p_ctx->tracks_num = 0;
+ if (module) free(module);
+ p_ctx->priv->module = NULL;
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open mpga_reader_open
+#endif
diff --git a/gfx/include/userland/containers/mpgv/mpgv_packetizer.c b/gfx/include/userland/containers/mpgv/mpgv_packetizer.c
new file mode 100644
index 0000000000..79ccf78fb9
--- /dev/null
+++ b/gfx/include/userland/containers/mpgv/mpgv_packetizer.c
@@ -0,0 +1,431 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** \file
+ * Implementation of an MPEG1/2 video packetizer.
+ */
+
+#include
+#include
+
+#include "containers/packetizers.h"
+#include "containers/core/packetizers_private.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_logging.h"
+#include "containers/core/containers_time.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_bytestream.h"
+
+/** Arbitrary number which should be sufficiently high so that no sane frame will
+ * be bigger than that. */
+#define MAX_FRAME_SIZE (1920*1088*2)
+
+static uint8_t mpgv_startcode[3] = {0x0, 0x0, 0x1};
+
+#define PICTURE_CODING_TYPE_I 0x1
+#define PICTURE_CODING_TYPE_P 0x2
+#define PICTURE_CODING_TYPE_B 0x3
+
+VC_CONTAINER_STATUS_T mpgv_packetizer_open( VC_PACKETIZER_T * );
+
+/*****************************************************************************/
+typedef struct VC_PACKETIZER_MODULE_T {
+ enum {
+ STATE_SYNC = 0,
+ STATE_SYNC_NEXT,
+ STATE_FRAME_DONE,
+ STATE_UNIT_HEADER,
+ STATE_UNIT_SEQUENCE,
+ STATE_UNIT_GROUP,
+ STATE_UNIT_PICTURE,
+ STATE_UNIT_SLICE,
+ STATE_UNIT_OTHER,
+ STATE_DATA,
+ } state;
+
+ size_t frame_size;
+ size_t unit_offset;
+
+ unsigned int seen_sequence_header;
+ unsigned int seen_picture_header;
+ unsigned int seen_slice;
+ unsigned int lost_sync;
+
+ unsigned int picture_type;
+ unsigned int picture_temporal_ref;
+ int64_t pts;
+ int64_t dts;
+
+ uint32_t bytes_read;
+
+ unsigned int width, height;
+ unsigned int frame_rate_num, frame_rate_den;
+ unsigned int aspect_ratio_num, aspect_ratio_den;
+ bool low_delay;
+
+} VC_PACKETIZER_MODULE_T;
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpgv_packetizer_close( VC_PACKETIZER_T *p_ctx )
+{
+ free(p_ctx->priv->module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpgv_packetizer_reset( VC_PACKETIZER_T *p_ctx )
+{
+ VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
+ module->lost_sync = 0;
+ module->state = STATE_SYNC;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpgv_read_sequence_header(VC_CONTAINER_BYTESTREAM_T *stream,
+ size_t offset, unsigned int *width, unsigned int *height,
+ unsigned int *frame_rate_num, unsigned int *frame_rate_den,
+ unsigned int *aspect_ratio_num, unsigned int *aspect_ratio_den)
+{
+ static const int frame_rate[16][2] =
+ { {0, 0}, {24000, 1001}, {24, 1}, {25, 1}, {30000, 1001}, {30, 1}, {50, 1},
+ {60000, 1001}, {60, 1},
+ /* Unofficial values */
+ {15, 1001}, /* From Xing */
+ {5, 1001}, {10, 1001}, {12, 1001}, {15, 1001} /* From libmpeg3 */ };
+ static const int aspect_ratio[16][2] =
+ { {0, 0}, {1, 1}, {4, 3}, {16, 9}, {221, 100} };
+
+ VC_CONTAINER_STATUS_T status;
+ unsigned int w, h, fr, ar;
+ int64_t ar_num, ar_den, div;
+ uint8_t header[8];
+
+ status = bytestream_peek_at( stream, offset, header, sizeof(header));
+ if(status != VC_CONTAINER_SUCCESS)
+ return status;
+
+ w = (header[4] << 4) | (header[5] >> 4);
+ h = ((header[5]&0x0f) << 8) | header[6];
+ ar = header[7] >> 4;
+ fr = header[7]&0x0f;
+ if (!w || !h || !ar || !fr)
+ return VC_CONTAINER_ERROR_CORRUPTED;
+
+ *width = w;
+ *height = h;
+ *frame_rate_num = frame_rate[fr][0];
+ *frame_rate_den = frame_rate[fr][1];
+ ar_num = (int64_t)aspect_ratio[ar][0] * h;
+ ar_den = (int64_t)aspect_ratio[ar][1] * w;
+ div = vc_container_maths_gcd(ar_num, ar_den);
+ if (div)
+ {
+ ar_num /= div;
+ ar_den /= div;
+ }
+ *aspect_ratio_num = ar_num;
+ *aspect_ratio_den = ar_den;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpgv_read_picture_header(VC_CONTAINER_BYTESTREAM_T *stream,
+ size_t offset, unsigned int *type, unsigned int *temporal_ref)
+{
+ VC_CONTAINER_STATUS_T status;
+ uint8_t h[2];
+
+ status = bytestream_peek_at(stream, offset + sizeof(mpgv_startcode) + 1, h, sizeof(h));
+ if(status != VC_CONTAINER_SUCCESS)
+ return status;
+
+ *temporal_ref = (h[0] << 2) | (h[1] >> 6);
+ *type = (h[1] >> 3) & 0x7;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpgv_update_format( VC_PACKETIZER_T *p_ctx )
+{
+ VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
+
+ LOG_DEBUG(0, "mpgv format: width %i, height %i, rate %i/%i, ar %i/%i",
+ module->width, module->height, module->frame_rate_num, module->frame_rate_den,
+ module->aspect_ratio_num, module->aspect_ratio_den);
+
+ p_ctx->out->type->video.width = p_ctx->out->type->video.visible_width = module->width;
+ p_ctx->out->type->video.height = p_ctx->out->type->video.visible_height = module->height;
+ p_ctx->out->type->video.par_num = module->aspect_ratio_num;
+ p_ctx->out->type->video.par_den = module->aspect_ratio_den;
+ p_ctx->out->type->video.frame_rate_num = module->frame_rate_num;
+ p_ctx->out->type->video.frame_rate_den = module->frame_rate_den;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T mpgv_packetizer_packetize( VC_PACKETIZER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags)
+{
+ VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream;
+ VC_CONTAINER_TIME_T *time = &p_ctx->priv->time;
+ VC_CONTAINER_STATUS_T status;
+ uint8_t header[4];
+ size_t offset;
+
+ while(1) switch (module->state)
+ {
+ case STATE_SYNC:
+ offset = 0;
+ status = bytestream_find_startcode( stream, &offset,
+ mpgv_startcode, sizeof(mpgv_startcode) );
+
+ if(offset && !module->lost_sync)
+ LOG_DEBUG(0, "lost sync");
+
+ bytestream_skip(stream, offset);
+ module->lost_sync += offset;
+
+ if(status != VC_CONTAINER_SUCCESS)
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA; /* We need more data */
+
+ if(module->lost_sync)
+ LOG_DEBUG(0, "recovered sync after %i bytes", module->lost_sync);
+ module->lost_sync = 0;
+ module->state = STATE_UNIT_HEADER;
+ module->frame_size = 0;
+ module->unit_offset = 0;
+ /* fall through to the next state */
+
+ case STATE_UNIT_HEADER:
+ status = bytestream_peek_at( stream, module->unit_offset, header, sizeof(header));
+ if(status != VC_CONTAINER_SUCCESS)
+ {
+ if (!(flags & VC_PACKETIZER_FLAG_FLUSH) ||
+ !module->seen_picture_header || !module->seen_slice)
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
+ module->state = STATE_FRAME_DONE;
+ break;
+ }
+
+#if defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE)
+ LOG_DEBUG(0, "found unit (%x)", header[3]);
+#endif
+
+ /* Detect start of new frame */
+ if(module->seen_picture_header && module->seen_slice &&
+ (header[3] == 0x00 /* A picture header */ ||
+ (header[3] > 0xAF && header[3] != 0xB7) /* Not a slice or sequence end */))
+ {
+ module->state = STATE_FRAME_DONE;
+ break;
+ }
+
+ module->frame_size += sizeof(mpgv_startcode);
+ module->state = STATE_SYNC_NEXT;
+ /* fall through to the next state */
+
+ case STATE_SYNC_NEXT:
+ status = bytestream_find_startcode( stream, &module->frame_size,
+ mpgv_startcode, sizeof(mpgv_startcode) );
+
+ /* Sanity check the size of frames. This makes sure we don't endlessly accumulate data
+ * to make up a new frame. */
+ if(module->frame_size > p_ctx->max_frame_size)
+ {
+ LOG_ERROR(0, "frame too big (%i/%i), dropping", module->frame_size, p_ctx->max_frame_size);
+ bytestream_skip(stream, module->frame_size);
+ module->state = STATE_SYNC;
+ break;
+ }
+ if(status != VC_CONTAINER_SUCCESS)
+ {
+ if (!(flags & VC_PACKETIZER_FLAG_FLUSH) ||
+ !module->seen_picture_header || !module->seen_slice)
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
+ module->state = STATE_FRAME_DONE;
+ break;
+ }
+
+ bytestream_peek_at( stream, module->unit_offset, header, sizeof(header));
+
+ /* Drop everything until we've seen a sequence header */
+ if(header[3] != 0xB3 && !module->seen_sequence_header)
+ {
+ LOG_DEBUG(0, "waiting for sequence header, dropping %i bytes", module->frame_size);
+ module->state = STATE_UNIT_HEADER;
+ bytestream_skip(stream, module->frame_size);
+ module->unit_offset = module->frame_size = 0;
+ break;
+ }
+
+ if(header[3] == 0x00)
+ module->state = STATE_UNIT_PICTURE;
+ else if(header[3] >= 0x01 && header[3] <= 0xAF)
+ module->state = STATE_UNIT_SLICE;
+ else if(header[3] == 0xB3)
+ module->state = STATE_UNIT_SEQUENCE;
+ else if(header[3] == 0xB8)
+ module->state = STATE_UNIT_GROUP;
+ else
+ module->state = STATE_UNIT_OTHER;
+ break;
+
+ case STATE_UNIT_SEQUENCE:
+ status = mpgv_read_sequence_header(stream, module->unit_offset, &module->width, &module->height,
+ &module->frame_rate_num, &module->frame_rate_den, &module->aspect_ratio_num, &module->aspect_ratio_den);
+ if(status != VC_CONTAINER_SUCCESS && !module->seen_sequence_header)
+ {
+ /* We need a sequence header so drop everything until we see one */
+ LOG_DEBUG(0, "invalid first sequence header, dropping %i bytes", module->frame_size);
+ bytestream_skip(stream, module->frame_size);
+ module->state = STATE_SYNC;
+ break;
+ }
+ mpgv_update_format(p_ctx);
+ module->seen_sequence_header = true;
+ vc_container_time_set_samplerate(time, module->frame_rate_num, module->frame_rate_den);
+ module->state = STATE_UNIT_HEADER;
+ module->unit_offset = module->frame_size;
+ break;
+
+ case STATE_UNIT_PICTURE:
+ status = mpgv_read_picture_header(stream, module->unit_offset, &module->picture_type, &module->picture_temporal_ref);
+ if(status != VC_CONTAINER_SUCCESS)
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
+ module->seen_picture_header = true;
+ module->state = STATE_UNIT_HEADER;
+ module->unit_offset = module->frame_size;
+ break;
+
+ case STATE_UNIT_SLICE:
+ module->seen_slice = true;
+ module->state = STATE_UNIT_HEADER;
+ module->unit_offset = module->frame_size;
+ break;
+
+ case STATE_UNIT_GROUP:
+ case STATE_UNIT_OTHER:
+ module->state = STATE_UNIT_HEADER;
+ module->unit_offset = module->frame_size;
+ break;
+
+ case STATE_FRAME_DONE:
+ bytestream_get_timestamps(stream, &module->pts, &module->dts, false);
+
+ if(module->picture_type == PICTURE_CODING_TYPE_B || module->low_delay)
+ {
+ if(module->pts == VC_CONTAINER_TIME_UNKNOWN)
+ module->pts = module->dts;
+ if(module->dts == VC_CONTAINER_TIME_UNKNOWN)
+ module->dts = module->pts;
+ }
+ vc_container_time_set(time, module->pts);
+
+ module->bytes_read = 0;
+ module->state = STATE_DATA;
+ module->seen_slice = false;
+ module->seen_picture_header = false;
+
+#if defined(ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE)
+ LOG_DEBUG(0, "new frame, type %x, size %i, temp_ref %i)", module->picture_type,
+ module->frame_size, module->picture_temporal_ref);
+#endif
+ /* fall through to the next state */
+
+ case STATE_DATA:
+ out->size = module->frame_size - module->bytes_read;
+ out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN;
+ out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END;
+
+ if(!module->bytes_read)
+ {
+ out->pts = module->pts;
+ out->dts = module->dts;
+ out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
+ }
+
+ if(flags & VC_PACKETIZER_FLAG_INFO)
+ return VC_CONTAINER_SUCCESS;
+
+ if(flags & VC_PACKETIZER_FLAG_SKIP)
+ {
+ bytestream_skip( stream, out->size );
+ }
+ else
+ {
+ out->size = MIN(out->size, out->buffer_size);
+ bytestream_get( stream, out->data, out->size );
+ }
+ module->bytes_read += out->size;
+
+ if(module->bytes_read == module->frame_size)
+ {
+ vc_container_time_add(time, 1);
+ module->state = STATE_UNIT_HEADER;
+ module->frame_size = 0;
+ module->unit_offset = 0;
+ }
+ else
+ out->flags &= ~VC_CONTAINER_PACKET_FLAG_FRAME_END;
+
+ return VC_CONTAINER_SUCCESS;
+
+ default:
+ break;
+ };
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T mpgv_packetizer_open( VC_PACKETIZER_T *p_ctx )
+{
+ VC_PACKETIZER_MODULE_T *module;
+
+ if(p_ctx->in->codec != VC_CONTAINER_CODEC_MP1V &&
+ p_ctx->in->codec != VC_CONTAINER_CODEC_MP2V)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ p_ctx->priv->module = module = malloc(sizeof(*module));
+ if(!module)
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ memset(module, 0, sizeof(*module));
+
+ vc_container_format_copy( p_ctx->out, p_ctx->in, 0);
+ p_ctx->max_frame_size = MAX_FRAME_SIZE;
+ p_ctx->priv->pf_close = mpgv_packetizer_close;
+ p_ctx->priv->pf_packetize = mpgv_packetizer_packetize;
+ p_ctx->priv->pf_reset = mpgv_packetizer_reset;
+ LOG_DEBUG(0, "using mpeg video packetizer");
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_PACKETIZER_REGISTER(mpgv_packetizer_open, "mpgv");
diff --git a/gfx/include/userland/containers/net/net_sockets.h b/gfx/include/userland/containers/net/net_sockets.h
new file mode 100644
index 0000000000..e5a97a2cdd
--- /dev/null
+++ b/gfx/include/userland/containers/net/net_sockets.h
@@ -0,0 +1,263 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef VC_NET_SOCKETS_H
+#define VC_NET_SOCKETS_H
+
+/** \file net_sockets.h
+ * Abstraction layer for socket-style network communication, to enable porting
+ * between platforms.
+ *
+ * Does not support IPv6 multicast.
+ */
+
+#include "containers/containers_types.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Status codes that can occur in a socket instance. */
+typedef enum {
+ VC_CONTAINER_NET_SUCCESS = 0, /**< No error */
+ VC_CONTAINER_NET_ERROR_GENERAL, /**< An unrecognised error has occurred */
+ VC_CONTAINER_NET_ERROR_INVALID_SOCKET, /**< Invalid socket passed to function */
+ VC_CONTAINER_NET_ERROR_NOT_ALLOWED, /**< The operation requested is not allowed */
+ VC_CONTAINER_NET_ERROR_INVALID_PARAMETER, /**< An invalid parameter was passed in */
+ VC_CONTAINER_NET_ERROR_NO_MEMORY, /**< Failure due to lack of memory */
+ VC_CONTAINER_NET_ERROR_ACCESS_DENIED, /**< Permission denied */
+ VC_CONTAINER_NET_ERROR_TOO_BIG, /**< Too many handles already open */
+ VC_CONTAINER_NET_ERROR_WOULD_BLOCK, /**< Asynchronous operation would block */
+ VC_CONTAINER_NET_ERROR_IN_PROGRESS, /**< An operation is already in progress on this socket */
+ VC_CONTAINER_NET_ERROR_IN_USE, /**< The address/port is already in use */
+ VC_CONTAINER_NET_ERROR_NETWORK, /**< Network is unavailable */
+ VC_CONTAINER_NET_ERROR_CONNECTION_LOST, /**< The connection has been lost, closed by network, etc. */
+ VC_CONTAINER_NET_ERROR_NOT_CONNECTED, /**< The socket is not connected */
+ VC_CONTAINER_NET_ERROR_TIMED_OUT, /**< Operation timed out */
+ VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED, /**< Connection was refused by target */
+ VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND, /**< Target address could not be resolved */
+ VC_CONTAINER_NET_ERROR_TRY_AGAIN, /**< A temporary failure occurred that may clear */
+} vc_container_net_status_t;
+
+/** Operations that can be applied to sockets */
+typedef enum {
+ /** Set the buffer size used on the socket
+ * arg1: uint32_t - New buffer size in bytes */
+ VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE = 1,
+ /** Set the timeout to be used on read operations
+ * arg1: uint32_t - New timeout in milliseconds, or INFINITE_TIMEOUT_MS */
+ VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS,
+} vc_container_net_control_t;
+
+/** Container Input / Output Context.
+ * This is an opaque structure that defines the context for a socket instance.
+ * The details of the structure are contained within the platform implementation. */
+typedef struct vc_container_net_tag VC_CONTAINER_NET_T;
+
+/** \name Socket open flags
+ * The following flags can be used when opening a network socket. */
+/* @{ */
+typedef uint32_t vc_container_net_open_flags_t;
+/** Connected stream socket, rather than connectionless datagram socket */
+#define VC_CONTAINER_NET_OPEN_FLAG_STREAM 1
+/** Force use of IPv4 addressing */
+#define VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4 2
+/** Force use of IPv6 addressing */
+#define VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6 6
+/** Use IPv4 broadcast address for datagram delivery */
+#define VC_CONTAINER_NET_OPEN_FLAG_IP4_BROADCAST 8
+/* @} */
+
+/** Mask of bits used in forcing address type */
+#define VC_CONTAINER_NET_OPEN_FLAG_FORCE_MASK 6
+
+/** Blocks until data is available, or an error occurs.
+ * Used with the VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS control operation. */
+#define INFINITE_TIMEOUT_MS 0xFFFFFFFFUL
+
+
+/** Opens a network socket instance.
+ * The network address can be a host name, dotted IP4, hex IP6 address or NULL. Passing NULL
+ * signifies the socket is either to be used as a datagram receiver or a stream server,
+ * depending on the flags.
+ * \ref VC_CONTAINER_NET_OPEN_FLAG_STREAM will open the socket for connected streaming. The default
+ * is to use connectionless datagrams.
+ * \ref VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4 will force the use of IPv4 addressing or fail to open
+ * the socket. The default is to pick the first available.
+ * \ref VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6 will force the use of IPv6 addressing or fail to open
+ * the socket. The default is to pick the first available.
+ * \ref VC_CONTAINER_NET_OPEN_FLAG_IP4_BROADCAST will use IPv4 broadcast addressing for a datagram
+ * sender. Use with an IPv6 address, stream socket or datagram receiver will raise an error.
+ * If the p_status parameter is not NULL, the status code will be written to it to indicate the
+ * reason for failure, or VC_CONTAINER_NET_SUCCESS on success.
+ * Sockets shall be bound and connected as necessary. Stream server sockets shall further need
+ * to have vc_container_net_listen and vc_container_net_accept called on them before data can be transferred.
+ *
+ * \param address Network address or NULL.
+ * \param port Network port or well-known name. This is the local port for receivers/servers.
+ * \param flags Flags controlling socket type.
+ * \param p_status Optional pointer to variable to receive status of operation.
+ * \return The socket instance or NULL on error. */
+VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port,
+ vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status );
+
+/** Closes a network socket instance.
+ * The p_ctx pointer must not be used after it has been closed.
+ *
+ * \param p_ctx The socket instance to close.
+ * \return The status code for closing the socket. */
+vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx );
+
+/** Query the latest status of the socket.
+ *
+ * \param p_ctx The socket instance.
+ * \return The status of the socket. */
+vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx );
+
+/** Read data from the socket.
+ * The function will read up to the requested number of bytes into the buffer.
+ * If there is no data immediately available to read, the function will block
+ * until data arrives, an error occurs or the timeout is reached (if set).
+ * When the function returns zero, the socket may have been closed, an error
+ * may have occurred, a zero length datagram received, or the timeout reached.
+ * Check vc_container_net_status() to differentiate.
+ * Attempting to read on a datagram sender socket will trigger an error.
+ *
+ * \param p_ctx The socket instance.
+ * \param buffer The buffer into which bytes will be read.
+ * \param size The maximum number of bytes to read.
+ * \return The number of bytes actually read. */
+size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size );
+
+/** Write data to the socket.
+ * If the socket cannot send the requested number of bytes in one go, the function
+ * will return a value smaller than size.
+ * Attempting to write on a datagram receiver socket will trigger an error.
+ *
+ * \param p_ctx The socket instance.
+ * \param buffer The buffer from which bytes will be written.
+ * \param size The maximum number of bytes to write.
+ * \return The number of bytes actually written. */
+size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size );
+
+/** Start a stream server socket listening for connections from clients.
+ * Attempting to use this on anything other than a stream server socket shall
+ * trigger an error.
+ *
+ * \param p_ctx The socket instance.
+ * \param maximum_connections The maximum number of queued connections to allow.
+ * \return The status of the socket. */
+vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections );
+
+/** Accept a client connection on a listening stream server socket.
+ * Attempting to use this on anything other than a listening stream server socket
+ * shall trigger an error.
+ * When a client connection is made, the new instance representing it is returned
+ * via pp_client_ctx.
+ *
+ * \param p_server ctx The server socket instance.
+ * \param pp_client_ctx The address where the pointer to the new client's socket
+ * instance is written.
+ * \return The status of the socket. */
+vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx );
+
+/** Non-blocking check for data being available to read.
+ * If an error occurs, the function will return false and the error can be
+ * obtained using socket_status().
+ *
+ * \param p_ctx The socket instance.
+ * \return True if there is data available to read immediately. */
+bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx );
+
+/** Returns the maximum size of a datagram in bytes, for sending or receiving.
+ * The limit for reading from or writing to stream sockets will generally be
+ * greater than this value, although the call can also be made on such sockets.
+ *
+ * \param p_ctx The socket instance.
+ * \return The maximum size of a datagram in bytes. */
+size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx );
+
+/** Get the DNS name or IP address of a stream server client, if connected.
+ * The length of the name will be limited by name_len, taking into account a
+ * terminating NUL character.
+ * Calling this function on a non-stream server instance, or one that is not
+ * connected to a client, will result in an error status.
+ *
+ * \param p_ctx The socket instance.
+ * \param name Pointer where the name should be written.
+ * \param name_len Maximum number of characters to write to name.
+ * \return The status of the socket. */
+vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len );
+
+/** Get the port of a stream server client, if connected.
+ * The port is written to the address in host order.
+ * Calling this function on a non-stream server instance, or one that is not
+ * connected to a client, will result in an error status.
+ *
+ * \param p_ctx The socket instance.
+ * \param port Pointer where the port should be written.
+ * \return The status of the socket. */
+vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port );
+
+/** Perform a control operation on the socket.
+ * See vc_container_net_control_t for more details.
+ *
+ * \param p_ctx The socket instance.
+ * \param operation The control operation to perform.
+ * \param args Variable list of additional arguments to the operation.
+ * \return The status of the socket. */
+vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx, vc_container_net_control_t operation, va_list args);
+
+/** Convert a 32-bit unsigned value from network order (big endian) to host order.
+ *
+ * \param value The value to be converted.
+ * \return The converted value. */
+uint32_t vc_container_net_to_host( uint32_t value );
+
+/** Convert a 32-bit unsigned value from host order to network order (big endian).
+ *
+ * \param value The value to be converted.
+ * \return The converted value. */
+uint32_t vc_container_net_from_host( uint32_t value );
+
+/** Convert a 16-bit unsigned value from network order (big endian) to host order.
+ *
+ * \param value The value to be converted.
+ * \return The converted value. */
+uint16_t vc_container_net_to_host_16( uint16_t value );
+
+/** Convert a 16-bit unsigned value from host order to network order (big endian).
+ *
+ * \param value The value to be converted.
+ * \return The converted value. */
+uint16_t vc_container_net_from_host_16( uint16_t value );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VC_NET_SOCKETS_H */
diff --git a/gfx/include/userland/containers/net/net_sockets_bsd.c b/gfx/include/userland/containers/net/net_sockets_bsd.c
new file mode 100644
index 0000000000..4503a9a3dd
--- /dev/null
+++ b/gfx/include/userland/containers/net/net_sockets_bsd.c
@@ -0,0 +1,117 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+
+#include "net_sockets.h"
+#include "net_sockets_priv.h"
+#include "containers/core/containers_common.h"
+
+/*****************************************************************************/
+
+/** Default maximum datagram size.
+ * This is based on the default Ethernet MTU size, less the IP and UDP headers.
+ */
+#define DEFAULT_MAXIMUM_DATAGRAM_SIZE (1500 - 20 - 8)
+
+/** Maximum socket buffer size to use. */
+#define MAXIMUM_BUFFER_SIZE 65536
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_private_last_error()
+{
+ switch (errno)
+ {
+ case EACCES: return VC_CONTAINER_NET_ERROR_ACCESS_DENIED;
+ case EFAULT: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ case EINVAL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ case EMFILE: return VC_CONTAINER_NET_ERROR_TOO_BIG;
+ case EWOULDBLOCK: return VC_CONTAINER_NET_ERROR_WOULD_BLOCK;
+ case EINPROGRESS: return VC_CONTAINER_NET_ERROR_IN_PROGRESS;
+ case EALREADY: return VC_CONTAINER_NET_ERROR_IN_PROGRESS;
+ case EADDRINUSE: return VC_CONTAINER_NET_ERROR_IN_USE;
+ case EADDRNOTAVAIL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ case ENETDOWN: return VC_CONTAINER_NET_ERROR_NETWORK;
+ case ENETUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK;
+ case ENETRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
+ case ECONNABORTED: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
+ case ECONNRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
+ case ENOBUFS: return VC_CONTAINER_NET_ERROR_NO_MEMORY;
+ case ENOTCONN: return VC_CONTAINER_NET_ERROR_NOT_CONNECTED;
+ case ESHUTDOWN: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
+ case ETIMEDOUT: return VC_CONTAINER_NET_ERROR_TIMED_OUT;
+ case ECONNREFUSED: return VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED;
+ case ELOOP: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ case ENAMETOOLONG: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ case EHOSTDOWN: return VC_CONTAINER_NET_ERROR_NETWORK;
+ case EHOSTUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK;
+ case EUSERS: return VC_CONTAINER_NET_ERROR_NO_MEMORY;
+ case EDQUOT: return VC_CONTAINER_NET_ERROR_NO_MEMORY;
+ case ESTALE: return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+
+ /* All other errors are unexpected, so just map to a general purpose error code. */
+ default:
+ return VC_CONTAINER_NET_ERROR_GENERAL;
+ }
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_private_init()
+{
+ /* No additional initialization required */
+ return VC_CONTAINER_NET_SUCCESS;
+}
+
+/*****************************************************************************/
+void vc_container_net_private_deinit()
+{
+ /* No additional deinitialization required */
+}
+
+/*****************************************************************************/
+void vc_container_net_private_close( SOCKET_T sock )
+{
+ close(sock);
+}
+
+/*****************************************************************************/
+void vc_container_net_private_set_reusable( SOCKET_T sock, bool enable )
+{
+ int opt = enable ? 1 : 0;
+
+ (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt));
+}
+
+/*****************************************************************************/
+size_t vc_container_net_private_maximum_datagram_size( SOCKET_T sock )
+{
+ (void)sock;
+
+ /* No easy way to determine this, just use the default. */
+ return DEFAULT_MAXIMUM_DATAGRAM_SIZE;
+}
diff --git a/gfx/include/userland/containers/net/net_sockets_bsd.h b/gfx/include/userland/containers/net/net_sockets_bsd.h
new file mode 100644
index 0000000000..fd554c94d0
--- /dev/null
+++ b/gfx/include/userland/containers/net/net_sockets_bsd.h
@@ -0,0 +1,43 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _NET_SOCKETS_BSD_H_
+#define _NET_SOCKETS_BSD_H_
+
+#include
+#include
+#include
+#include
+#include
+
+typedef int SOCKET_T;
+typedef socklen_t SOCKADDR_LEN_T;
+typedef void *SOCKOPT_CAST_T;
+#define INVALID_SOCKET -1
+#define SOCKET_ERROR -1
+
+#endif /* _NET_SOCKETS_BSD_H_ */
diff --git a/gfx/include/userland/containers/net/net_sockets_common.c b/gfx/include/userland/containers/net/net_sockets_common.c
new file mode 100644
index 0000000000..b3815f350a
--- /dev/null
+++ b/gfx/include/userland/containers/net/net_sockets_common.c
@@ -0,0 +1,619 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include
+#include
+
+#include "containers/containers.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_logging.h"
+#include "net_sockets.h"
+#include "net_sockets_priv.h"
+
+/*****************************************************************************/
+
+struct vc_container_net_tag
+{
+ /** The underlying socket */
+ SOCKET_T socket;
+ /** Last error raised on the socket instance. */
+ vc_container_net_status_t status;
+ /** Simple socket type */
+ vc_container_net_type_t type;
+ /** Socket address, used for sending datagrams. */
+ union {
+ struct sockaddr_storage storage;
+ struct sockaddr sa;
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+ } to_addr;
+ /** Number of bytes in to_addr that have been filled. */
+ SOCKADDR_LEN_T to_addr_len;
+ /** Maximum size of datagrams. */
+ size_t max_datagram_size;
+ /** Timeout to use when reading from a socket. INFINITE_TIMEOUT_MS waits forever. */
+ uint32_t read_timeout_ms;
+};
+
+/*****************************************************************************/
+static void socket_clear_address(struct sockaddr *p_addr)
+{
+ switch (p_addr->sa_family)
+ {
+ case AF_INET:
+ {
+ struct sockaddr_in *p_addr_v4 = (struct sockaddr_in *)p_addr;
+
+ memset(&p_addr_v4->sin_addr, 0, sizeof(p_addr_v4->sin_addr));
+ }
+ break;
+ case AF_INET6:
+ {
+ struct sockaddr_in6 *p_addr_v6 = (struct sockaddr_in6 *)p_addr;
+
+ memset(&p_addr_v6->sin6_addr, 0, sizeof(p_addr_v6->sin6_addr));
+ }
+ break;
+ default:
+ /* Invalid or unsupported address family */
+ vc_container_assert(0);
+ }
+}
+
+/*****************************************************************************/
+static vc_container_net_status_t socket_set_read_buffer_size(VC_CONTAINER_NET_T *p_ctx,
+ uint32_t buffer_size)
+{
+ int result;
+ const SOCKOPT_CAST_T optptr = (const SOCKOPT_CAST_T)&buffer_size;
+
+ result = setsockopt(p_ctx->socket, SOL_SOCKET, SO_RCVBUF, optptr, sizeof(buffer_size));
+
+ if (result == SOCKET_ERROR)
+ return vc_container_net_private_last_error();
+
+ return VC_CONTAINER_NET_SUCCESS;
+}
+
+/*****************************************************************************/
+static vc_container_net_status_t socket_set_read_timeout_ms(VC_CONTAINER_NET_T *p_ctx,
+ uint32_t timeout_ms)
+{
+ p_ctx->read_timeout_ms = timeout_ms;
+ return VC_CONTAINER_NET_SUCCESS;
+}
+
+/*****************************************************************************/
+static bool socket_wait_for_data( VC_CONTAINER_NET_T *p_ctx, uint32_t timeout_ms )
+{
+ int result;
+ fd_set set;
+ struct timeval tv;
+
+ if (timeout_ms == INFINITE_TIMEOUT_MS)
+ return true;
+
+ FD_ZERO(&set);
+ FD_SET(p_ctx->socket, &set);
+ tv.tv_sec = timeout_ms / 1000;
+ tv.tv_usec = (timeout_ms - tv.tv_sec * 1000) * 1000;
+ result = select(p_ctx->socket + 1, &set, NULL, NULL, &tv);
+
+ if (result == SOCKET_ERROR)
+ p_ctx->status = vc_container_net_private_last_error();
+ else
+ p_ctx->status = VC_CONTAINER_NET_SUCCESS;
+
+ return (result == 1);
+}
+
+/*****************************************************************************/
+VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port,
+ vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status )
+{
+ VC_CONTAINER_NET_T *p_ctx;
+ struct addrinfo hints, *info, *p;
+ int result;
+ vc_container_net_status_t status;
+ SOCKET_T sock = INVALID_SOCKET;
+
+ status = vc_container_net_private_init();
+ if (status != VC_CONTAINER_NET_SUCCESS)
+ {
+ LOG_ERROR(NULL, "vc_container_net_open: platform initialization failure: %d", status);
+ if (p_status)
+ *p_status = status;
+ return NULL;
+ }
+
+ p_ctx = (VC_CONTAINER_NET_T *)malloc(sizeof(VC_CONTAINER_NET_T));
+ if (!p_ctx)
+ {
+ if (p_status)
+ *p_status = VC_CONTAINER_NET_ERROR_NO_MEMORY;
+
+ LOG_ERROR(NULL, "vc_container_net_open: malloc fail for VC_CONTAINER_NET_T");
+ vc_container_net_private_deinit();
+ return NULL;
+ }
+
+ /* Initialize the net socket instance structure */
+ memset(p_ctx, 0, sizeof(*p_ctx));
+ p_ctx->socket = INVALID_SOCKET;
+ if (flags & VC_CONTAINER_NET_OPEN_FLAG_STREAM)
+ p_ctx->type = address ? STREAM_CLIENT : STREAM_SERVER;
+ else
+ p_ctx->type = address ? DATAGRAM_SENDER : DATAGRAM_RECEIVER;
+
+ /* Create the address info linked list from the data provided */
+ memset(&hints, 0, sizeof(hints));
+ switch (flags & VC_CONTAINER_NET_OPEN_FLAG_FORCE_MASK)
+ {
+ case 0:
+ hints.ai_family = AF_UNSPEC;
+ break;
+ case VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP4:
+ hints.ai_family = AF_INET;
+ break;
+ case VC_CONTAINER_NET_OPEN_FLAG_FORCE_IP6:
+ hints.ai_family = AF_INET6;
+ break;
+ default:
+ status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ LOG_ERROR(NULL, "vc_container_net_open: invalid address forcing flag");
+ goto error;
+ }
+ hints.ai_socktype = (flags & VC_CONTAINER_NET_OPEN_FLAG_STREAM) ? SOCK_STREAM : SOCK_DGRAM;
+
+ result = getaddrinfo(address, port, &hints, &info);
+ if (result)
+ {
+ status = vc_container_net_private_last_error();
+ LOG_ERROR(NULL, "vc_container_net_open: unable to get address info: %d", status);
+ goto error;
+ }
+
+ /* Not all address infos may be useable. Search for one that is by skipping any
+ * that provoke errors. */
+ for(p = info; (p != NULL) && (sock == INVALID_SOCKET) ; p = p->ai_next)
+ {
+ sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
+ if (sock == INVALID_SOCKET)
+ {
+ status = vc_container_net_private_last_error();
+ continue;
+ }
+
+ switch (p_ctx->type)
+ {
+ case STREAM_CLIENT:
+ /* Simply connect to the given address/port */
+ if (connect(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR)
+ status = vc_container_net_private_last_error();
+ break;
+
+ case DATAGRAM_SENDER:
+ /* Nothing further to do */
+ break;
+
+ case STREAM_SERVER:
+ /* Try to avoid socket reuse timing issues on TCP server sockets */
+ vc_container_net_private_set_reusable(sock, true);
+
+ /* Allow any source address */
+ socket_clear_address(p->ai_addr);
+
+ if (bind(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR)
+ status = vc_container_net_private_last_error();
+ break;
+
+ case DATAGRAM_RECEIVER:
+ /* Allow any source address */
+ socket_clear_address(p->ai_addr);
+
+ if (bind(sock, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR)
+ status = vc_container_net_private_last_error();
+ break;
+ }
+
+ if (status == VC_CONTAINER_NET_SUCCESS)
+ {
+ /* Save addressing information for later use */
+ p_ctx->to_addr_len = p->ai_addrlen;
+ memcpy(&p_ctx->to_addr, p->ai_addr, p->ai_addrlen);
+ } else {
+ vc_container_net_private_close(sock); /* Try next entry in list */
+ sock = INVALID_SOCKET;
+ }
+ }
+
+ freeaddrinfo(info);
+
+ if (sock == INVALID_SOCKET)
+ {
+ LOG_ERROR(NULL, "vc_container_net_open: failed to open socket: %d", status);
+ goto error;
+ }
+
+ p_ctx->socket = sock;
+ p_ctx->max_datagram_size = vc_container_net_private_maximum_datagram_size(sock);
+ p_ctx->read_timeout_ms = INFINITE_TIMEOUT_MS;
+
+ if (p_status)
+ *p_status = VC_CONTAINER_NET_SUCCESS;
+
+ return p_ctx;
+
+error:
+ if (p_status)
+ *p_status = status;
+ (void)vc_container_net_close(p_ctx);
+ return NULL;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx )
+{
+ if (!p_ctx)
+ return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+
+ if (p_ctx->socket != INVALID_SOCKET)
+ {
+ vc_container_net_private_close(p_ctx->socket);
+ p_ctx->socket = INVALID_SOCKET;
+ }
+ free(p_ctx);
+
+ vc_container_net_private_deinit();
+
+ return VC_CONTAINER_NET_SUCCESS;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx )
+{
+ if (!p_ctx)
+ return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+ return p_ctx->status;
+}
+
+/*****************************************************************************/
+size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size )
+{
+ int result = 0;
+
+ if (!p_ctx)
+ return 0;
+
+ if (!buffer)
+ {
+ p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ return 0;
+ }
+
+ p_ctx->status = VC_CONTAINER_NET_SUCCESS;
+
+ switch (p_ctx->type)
+ {
+ case STREAM_CLIENT:
+ case STREAM_SERVER:
+ /* Receive data from the stream */
+ if (socket_wait_for_data(p_ctx, p_ctx->read_timeout_ms))
+ {
+ result = recv(p_ctx->socket, buffer, (int)size, 0);
+ if (!result)
+ p_ctx->status = VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
+ } else
+ p_ctx->status = VC_CONTAINER_NET_ERROR_TIMED_OUT;
+ break;
+
+ case DATAGRAM_RECEIVER:
+ {
+ /* Receive the packet */
+ /* FIXME Potential for data loss, as rest of packet will be lost if buffer was not large enough */
+ if (socket_wait_for_data(p_ctx, p_ctx->read_timeout_ms))
+ {
+ result = recvfrom(p_ctx->socket, buffer, size, 0, &p_ctx->to_addr.sa, &p_ctx->to_addr_len);
+ if (!result)
+ p_ctx->status = VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
+ } else
+ p_ctx->status = VC_CONTAINER_NET_ERROR_TIMED_OUT;
+ }
+ break;
+
+ default: /* DATAGRAM_SENDER */
+ p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
+ result = 0;
+ break;
+ }
+
+ if (result == SOCKET_ERROR)
+ {
+ p_ctx->status = vc_container_net_private_last_error();
+ result = 0;
+ }
+
+ return (size_t)result;
+}
+
+/*****************************************************************************/
+size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size )
+{
+ int result;
+
+ if (!p_ctx)
+ return 0;
+
+ if (!buffer)
+ {
+ p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ return 0;
+ }
+
+ p_ctx->status = VC_CONTAINER_NET_SUCCESS;
+
+ switch (p_ctx->type)
+ {
+ case STREAM_CLIENT:
+ case STREAM_SERVER:
+ /* Send data to the stream */
+ result = send(p_ctx->socket, buffer, (int)size, 0);
+ break;
+
+ case DATAGRAM_SENDER:
+ /* Send the datagram */
+
+ if (size > p_ctx->max_datagram_size)
+ size = p_ctx->max_datagram_size;
+
+ result = sendto(p_ctx->socket, buffer, size, 0, &p_ctx->to_addr.sa, p_ctx->to_addr_len);
+ break;
+
+ default: /* DATAGRAM_RECEIVER */
+ p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
+ result = 0;
+ break;
+ }
+
+ if (result == SOCKET_ERROR)
+ {
+ p_ctx->status = vc_container_net_private_last_error();
+ result = 0;
+ }
+
+ return (size_t)result;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections )
+{
+ if (!p_ctx)
+ return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+
+ p_ctx->status = VC_CONTAINER_NET_SUCCESS;
+
+ if (p_ctx->type == STREAM_SERVER)
+ {
+ if (listen(p_ctx->socket, maximum_connections) == SOCKET_ERROR)
+ p_ctx->status = vc_container_net_private_last_error();
+ } else {
+ p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
+ }
+
+ return p_ctx->status;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx )
+{
+ VC_CONTAINER_NET_T *p_client_ctx = NULL;
+
+ if (!p_server_ctx)
+ return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+
+ if (!pp_client_ctx)
+ return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+
+ *pp_client_ctx = NULL;
+
+ if (p_server_ctx->type != STREAM_SERVER)
+ {
+ p_server_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
+ goto error;
+ }
+
+ p_client_ctx = (VC_CONTAINER_NET_T *)malloc(sizeof(VC_CONTAINER_NET_T));
+ if (!p_client_ctx)
+ {
+ p_server_ctx->status = VC_CONTAINER_NET_ERROR_NO_MEMORY;
+ goto error;
+ }
+
+ /* Initialise the new context with the address information from the server context */
+ memset(p_client_ctx, 0, sizeof(*p_client_ctx));
+ memcpy(&p_client_ctx->to_addr, &p_server_ctx->to_addr, p_server_ctx->to_addr_len);
+ p_client_ctx->to_addr_len = p_server_ctx->to_addr_len;
+
+ p_client_ctx->socket = accept(p_server_ctx->socket, &p_client_ctx->to_addr.sa, &p_client_ctx->to_addr_len);
+
+ if (p_client_ctx->socket == INVALID_SOCKET)
+ {
+ p_server_ctx->status = vc_container_net_private_last_error();
+ goto error;
+ }
+
+ /* Need to bump up the initialisation count, as a new context has been created */
+ p_server_ctx->status = vc_container_net_private_init();
+ if (p_server_ctx->status != VC_CONTAINER_NET_SUCCESS)
+ goto error;
+
+ p_client_ctx->type = STREAM_CLIENT;
+ p_client_ctx->max_datagram_size = vc_container_net_private_maximum_datagram_size(p_client_ctx->socket);
+ p_client_ctx->read_timeout_ms = INFINITE_TIMEOUT_MS;
+ p_client_ctx->status = VC_CONTAINER_NET_SUCCESS;
+
+ *pp_client_ctx = p_client_ctx;
+ return VC_CONTAINER_NET_SUCCESS;
+
+error:
+ if (p_client_ctx)
+ free(p_client_ctx);
+ return p_server_ctx->status;
+}
+
+/*****************************************************************************/
+bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx )
+{
+ if (!p_ctx)
+ return false;
+
+ if (p_ctx->type == DATAGRAM_SENDER)
+ {
+ p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
+ return false;
+ }
+
+ return socket_wait_for_data(p_ctx, 0);
+}
+
+/*****************************************************************************/
+size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx )
+{
+ return p_ctx ? p_ctx->max_datagram_size : 0;
+}
+
+/*****************************************************************************/
+static vc_container_net_status_t translate_getnameinfo_error( int error )
+{
+ switch (error)
+ {
+ case EAI_AGAIN: return VC_CONTAINER_NET_ERROR_TRY_AGAIN;
+ case EAI_FAIL: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND;
+ case EAI_MEMORY: return VC_CONTAINER_NET_ERROR_NO_MEMORY;
+ case EAI_NONAME: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND;
+
+ /* All other errors are unexpected, so just map to a general purpose error code. */
+ default:
+ return VC_CONTAINER_NET_ERROR_GENERAL;
+ }
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len )
+{
+ int result;
+
+ if (!p_ctx)
+ return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+
+ if (p_ctx->socket == INVALID_SOCKET)
+ p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_CONNECTED;
+ else if (!name || !name_len)
+ p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ else if ((result = getnameinfo(&p_ctx->to_addr.sa, p_ctx->to_addr_len, name, name_len, NULL, 0, 0)) != 0)
+ p_ctx->status = translate_getnameinfo_error(result);
+ else
+ p_ctx->status = VC_CONTAINER_NET_SUCCESS;
+
+ return p_ctx->status;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port )
+{
+ if (!p_ctx)
+ return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+
+ if (p_ctx->socket == INVALID_SOCKET)
+ p_ctx->status = VC_CONTAINER_NET_ERROR_NOT_CONNECTED;
+ else if (!port)
+ p_ctx->status = VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ else
+ {
+ p_ctx->status = VC_CONTAINER_NET_SUCCESS;
+ switch (p_ctx->to_addr.sa.sa_family)
+ {
+ case AF_INET:
+ *port = ntohs(p_ctx->to_addr.in.sin_port);
+ break;
+ case AF_INET6:
+ *port = ntohs(p_ctx->to_addr.in6.sin6_port);
+ break;
+ default:
+ /* Highly unexepcted address family! */
+ p_ctx->status = VC_CONTAINER_NET_ERROR_GENERAL;
+ }
+ }
+
+ return p_ctx->status;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx,
+ vc_container_net_control_t operation,
+ va_list args)
+{
+ vc_container_net_status_t status;
+
+ switch (operation)
+ {
+ case VC_CONTAINER_NET_CONTROL_SET_READ_BUFFER_SIZE:
+ status = socket_set_read_buffer_size(p_ctx, va_arg(args, uint32_t));
+ break;
+ case VC_CONTAINER_NET_CONTROL_SET_READ_TIMEOUT_MS:
+ status = socket_set_read_timeout_ms(p_ctx, va_arg(args, uint32_t));
+ break;
+ default:
+ status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+uint32_t vc_container_net_to_host( uint32_t value )
+{
+ return ntohl(value);
+}
+
+/*****************************************************************************/
+uint32_t vc_container_net_from_host( uint32_t value )
+{
+ return htonl(value);
+}
+
+/*****************************************************************************/
+uint16_t vc_container_net_to_host_16( uint16_t value )
+{
+ return ntohs(value);
+}
+
+/*****************************************************************************/
+uint16_t vc_container_net_from_host_16( uint16_t value )
+{
+ return htons(value);
+}
diff --git a/gfx/include/userland/containers/net/net_sockets_null.c b/gfx/include/userland/containers/net/net_sockets_null.c
new file mode 100644
index 0000000000..a8fe8fbd16
--- /dev/null
+++ b/gfx/include/userland/containers/net/net_sockets_null.c
@@ -0,0 +1,175 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "net_sockets.h"
+#include "containers/core/containers_common.h"
+
+/*****************************************************************************/
+
+struct vc_container_net_tag
+{
+ uint32_t dummy; /* C requires structs not to be empty. */
+};
+
+/*****************************************************************************/
+VC_CONTAINER_NET_T *vc_container_net_open( const char *address, const char *port,
+ vc_container_net_open_flags_t flags, vc_container_net_status_t *p_status )
+{
+ VC_CONTAINER_PARAM_UNUSED(address);
+ VC_CONTAINER_PARAM_UNUSED(port);
+ VC_CONTAINER_PARAM_UNUSED(flags);
+
+ if (p_status)
+ *p_status = VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
+
+ return NULL;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_close( VC_CONTAINER_NET_T *p_ctx )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+
+ return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_status( VC_CONTAINER_NET_T *p_ctx )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+
+ return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+}
+
+/*****************************************************************************/
+size_t vc_container_net_read( VC_CONTAINER_NET_T *p_ctx, void *buffer, size_t size )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(buffer);
+ VC_CONTAINER_PARAM_UNUSED(size);
+
+ return 0;
+}
+
+/*****************************************************************************/
+size_t vc_container_net_write( VC_CONTAINER_NET_T *p_ctx, const void *buffer, size_t size )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(buffer);
+ VC_CONTAINER_PARAM_UNUSED(size);
+
+ return 0;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_listen( VC_CONTAINER_NET_T *p_ctx, uint32_t maximum_connections )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(maximum_connections);
+
+ return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_accept( VC_CONTAINER_NET_T *p_server_ctx, VC_CONTAINER_NET_T **pp_client_ctx )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_server_ctx);
+ VC_CONTAINER_PARAM_UNUSED(pp_client_ctx);
+
+ return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+}
+
+/*****************************************************************************/
+bool vc_container_net_is_data_available( VC_CONTAINER_NET_T *p_ctx )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+
+ return false;
+}
+
+/*****************************************************************************/
+size_t vc_container_net_maximum_datagram_size( VC_CONTAINER_NET_T *p_ctx )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+
+ return 0;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_get_client_name( VC_CONTAINER_NET_T *p_ctx, char *name, size_t name_len )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(name);
+ VC_CONTAINER_PARAM_UNUSED(name_len);
+
+ return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_get_client_port( VC_CONTAINER_NET_T *p_ctx , unsigned short *port )
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(port);
+
+ return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_control( VC_CONTAINER_NET_T *p_ctx,
+ vc_container_net_control_t operation,
+ va_list args)
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(operation);
+ VC_CONTAINER_PARAM_UNUSED(args);
+
+ return VC_CONTAINER_NET_ERROR_NOT_ALLOWED;
+}
+
+/*****************************************************************************/
+uint32_t vc_container_net_to_host( uint32_t value )
+{
+ return value;
+}
+
+/*****************************************************************************/
+uint32_t vc_container_net_from_host( uint32_t value )
+{
+ return value;
+}
+
+/*****************************************************************************/
+uint16_t vc_container_net_to_host_16( uint16_t value )
+{
+ return value;
+}
+
+/*****************************************************************************/
+uint16_t vc_container_net_from_host_16( uint16_t value )
+{
+ return value;
+}
diff --git a/gfx/include/userland/containers/net/net_sockets_priv.h b/gfx/include/userland/containers/net/net_sockets_priv.h
new file mode 100644
index 0000000000..41860836b6
--- /dev/null
+++ b/gfx/include/userland/containers/net/net_sockets_priv.h
@@ -0,0 +1,85 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _NET_SOCKETS_PRIV_H_
+#define _NET_SOCKETS_PRIV_H_
+
+#include "net_sockets.h"
+
+#ifdef WIN32
+#include "net_sockets_win32.h"
+#else
+#include "net_sockets_bsd.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum
+{
+ STREAM_CLIENT = 0, /**< TCP client */
+ STREAM_SERVER, /**< TCP server */
+ DATAGRAM_SENDER, /**< UDP sender */
+ DATAGRAM_RECEIVER /**< UDP receiver */
+} vc_container_net_type_t;
+
+
+/** Perform implementation-specific per-socket initialization.
+ *
+ * \return VC_CONTAINER_NET_SUCCESS or one of the error codes on failure. */
+vc_container_net_status_t vc_container_net_private_init( void );
+
+/** Perform implementation-specific per-socket deinitialization.
+ * This function is always called once for each successful call to socket_private_init(). */
+void vc_container_net_private_deinit( void );
+
+/** Return the last error from the socket implementation. */
+vc_container_net_status_t vc_container_net_private_last_error( void );
+
+/** Implementation-specific internal socket close.
+ *
+ * \param sock Internal socket to be closed. */
+void vc_container_net_private_close( SOCKET_T sock );
+
+/** Enable or disable socket address reusability.
+ *
+ * \param sock Internal socket to be closed.
+ * \param enable True to enable reusability, false to clear it. */
+void vc_container_net_private_set_reusable( SOCKET_T sock, bool enable );
+
+/** Query the maximum datagram size for the socket.
+ *
+ * \param sock The socket to query.
+ * \return The maximum supported datagram size on the socket. */
+size_t vc_container_net_private_maximum_datagram_size( SOCKET_T sock );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _NET_SOCKETS_PRIV_H_ */
diff --git a/gfx/include/userland/containers/net/net_sockets_win32.c b/gfx/include/userland/containers/net/net_sockets_win32.c
new file mode 100644
index 0000000000..e0dd374298
--- /dev/null
+++ b/gfx/include/userland/containers/net/net_sockets_win32.c
@@ -0,0 +1,140 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "net_sockets.h"
+#include "net_sockets_priv.h"
+#include "containers/core/containers_common.h"
+
+#pragma comment(lib, "Ws2_32.lib")
+
+/*****************************************************************************/
+
+/** Default maximum datagram size.
+ * This is based on the default Ethernet MTU size, less the IP and UDP headers.
+ */
+#define DEFAULT_MAXIMUM_DATAGRAM_SIZE (1500 - 20 - 8)
+
+/** Maximum socket buffer size to use. */
+#define MAXIMUM_BUFFER_SIZE 65536
+
+/*****************************************************************************/
+static vc_container_net_status_t translate_error_status( int error )
+{
+ switch (error)
+ {
+ case WSA_INVALID_HANDLE: return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+ case WSA_NOT_ENOUGH_MEMORY: return VC_CONTAINER_NET_ERROR_NO_MEMORY;
+ case WSA_INVALID_PARAMETER: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ case WSAEACCES: return VC_CONTAINER_NET_ERROR_ACCESS_DENIED;
+ case WSAEFAULT: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ case WSAEINVAL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ case WSAEMFILE: return VC_CONTAINER_NET_ERROR_TOO_BIG;
+ case WSAEWOULDBLOCK: return VC_CONTAINER_NET_ERROR_WOULD_BLOCK;
+ case WSAEINPROGRESS: return VC_CONTAINER_NET_ERROR_IN_PROGRESS;
+ case WSAEALREADY: return VC_CONTAINER_NET_ERROR_IN_PROGRESS;
+ case WSAEADDRINUSE: return VC_CONTAINER_NET_ERROR_IN_USE;
+ case WSAEADDRNOTAVAIL: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ case WSAENETDOWN: return VC_CONTAINER_NET_ERROR_NETWORK;
+ case WSAENETUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK;
+ case WSAENETRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
+ case WSAECONNABORTED: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
+ case WSAECONNRESET: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
+ case WSAENOBUFS: return VC_CONTAINER_NET_ERROR_NO_MEMORY;
+ case WSAENOTCONN: return VC_CONTAINER_NET_ERROR_NOT_CONNECTED;
+ case WSAESHUTDOWN: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
+ case WSAETIMEDOUT: return VC_CONTAINER_NET_ERROR_TIMED_OUT;
+ case WSAECONNREFUSED: return VC_CONTAINER_NET_ERROR_CONNECTION_REFUSED;
+ case WSAELOOP: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ case WSAENAMETOOLONG: return VC_CONTAINER_NET_ERROR_INVALID_PARAMETER;
+ case WSAEHOSTDOWN: return VC_CONTAINER_NET_ERROR_NETWORK;
+ case WSAEHOSTUNREACH: return VC_CONTAINER_NET_ERROR_NETWORK;
+ case WSAEPROCLIM: return VC_CONTAINER_NET_ERROR_NO_MEMORY;
+ case WSAEUSERS: return VC_CONTAINER_NET_ERROR_NO_MEMORY;
+ case WSAEDQUOT: return VC_CONTAINER_NET_ERROR_NO_MEMORY;
+ case WSAESTALE: return VC_CONTAINER_NET_ERROR_INVALID_SOCKET;
+ case WSAEDISCON: return VC_CONTAINER_NET_ERROR_CONNECTION_LOST;
+ case WSAHOST_NOT_FOUND: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND;
+ case WSATRY_AGAIN: return VC_CONTAINER_NET_ERROR_TRY_AGAIN;
+ case WSANO_RECOVERY: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND;
+ case WSANO_DATA: return VC_CONTAINER_NET_ERROR_HOST_NOT_FOUND;
+
+ /* All other errors are unexpected, so just map to a general purpose error code. */
+ default:
+ return VC_CONTAINER_NET_ERROR_GENERAL;
+ }
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_private_last_error()
+{
+ return translate_error_status( WSAGetLastError() );
+}
+
+/*****************************************************************************/
+vc_container_net_status_t vc_container_net_private_init()
+{
+ WSADATA wsa_data;
+ int result;
+
+ result = WSAStartup(MAKEWORD(2,2), &wsa_data);
+ if (result)
+ return translate_error_status( result );
+
+ return VC_CONTAINER_NET_SUCCESS;
+}
+
+/*****************************************************************************/
+void vc_container_net_private_deinit()
+{
+ WSACleanup();
+}
+
+/*****************************************************************************/
+void vc_container_net_private_close( SOCKET_T sock )
+{
+ closesocket(sock);
+}
+
+/*****************************************************************************/
+void vc_container_net_private_set_reusable( SOCKET_T sock, bool enable )
+{
+ BOOL opt = enable ? TRUE : FALSE;
+
+ (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof(opt));
+}
+
+/*****************************************************************************/
+size_t vc_container_net_private_maximum_datagram_size( SOCKET_T sock )
+{
+ size_t max_datagram_size = DEFAULT_MAXIMUM_DATAGRAM_SIZE;
+ int opt_size = sizeof(max_datagram_size);
+
+ /* Ignore errors and use the default if necessary */
+ (void)getsockopt(sock, SOL_SOCKET, SO_MAX_MSG_SIZE, (char *)&max_datagram_size, &opt_size);
+
+ return max_datagram_size;
+}
diff --git a/gfx/include/userland/containers/net/net_sockets_win32.h b/gfx/include/userland/containers/net/net_sockets_win32.h
new file mode 100644
index 0000000000..86edc3ad9b
--- /dev/null
+++ b/gfx/include/userland/containers/net/net_sockets_win32.h
@@ -0,0 +1,38 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _NET_SOCKETS_WIN32_H_
+#define _NET_SOCKETS_WIN32_H_
+
+#include
+#include
+
+typedef SOCKET SOCKET_T;
+typedef int SOCKADDR_LEN_T;
+typedef char *SOCKOPT_CAST_T;
+
+#endif /* _NET_SOCKETS_WIN32_H_ */
diff --git a/gfx/include/userland/containers/packetizers.h b/gfx/include/userland/containers/packetizers.h
new file mode 100644
index 0000000000..735816df18
--- /dev/null
+++ b/gfx/include/userland/containers/packetizers.h
@@ -0,0 +1,159 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef VC_PACKETIZERS_H
+#define VC_PACKETIZERS_H
+
+/** \file packetizers.h
+ * Public API for packetizing data (i.e. framing and timestamping)
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "containers/containers.h"
+
+/** \defgroup VcPacketizerApi Packetizer API
+ * API for packetizers */
+/* @{ */
+
+/** \name Packetizer flags
+ * \anchor packetizerflags
+ * The following flags describe properties of a packetizer */
+/* @{ */
+#define VC_PACKETIZER_FLAG_ES_CHANGED 0x1 /**< ES definition has changed */
+/* @} */
+
+/** Definition of the packetizer type */
+typedef struct VC_PACKETIZER_T
+{
+ struct VC_PACKETIZER_PRIVATE_T *priv; /**< Private member used by the implementation */
+ uint32_t flags; /**< Flags describing the properties of a packetizer.
+ * See \ref packetizerflags "Packetizer flags". */
+
+ VC_CONTAINER_ES_FORMAT_T *in; /**< Format of the input elementary stream */
+ VC_CONTAINER_ES_FORMAT_T *out; /**< Format of the output elementary stream */
+
+ uint32_t max_frame_size; /**< Maximum size of a packetized frame */
+
+} VC_PACKETIZER_T;
+
+/** Open a packetizer to convert the input format into the requested output format.
+ * This will create an an instance of a packetizer and its associated context.
+ *
+ * If no packetizer is found for the requested format, this will return a null pointer as well as
+ * an error code indicating why this failed.
+ *
+ * \param in Input elementary stream format
+ * \param out_variant Requested output variant for the output elementary stream format
+ * \param status Returns the status of the operation
+ * \return A pointer to the context of the new instance of the packetizer.
+ * Returns NULL on failure.
+ */
+VC_PACKETIZER_T *vc_packetizer_open(VC_CONTAINER_ES_FORMAT_T *in, VC_CONTAINER_FOURCC_T out_variant,
+ VC_CONTAINER_STATUS_T *status);
+
+/** Closes an instance of a packetizer.
+ * This will free all the resources associated with the context.
+ *
+ * \param context Pointer to the context of the instance to close
+ * \return the status of the operation
+ */
+VC_CONTAINER_STATUS_T vc_packetizer_close( VC_PACKETIZER_T *context );
+
+/** \name Packetizer flags
+ * The following flags can be passed during a packetize call */
+/* @{ */
+/** Type definition for the packetizer flags */
+typedef uint32_t VC_PACKETIZER_FLAGS_T;
+/** Ask the packetizer to only return information on the next packet without reading it */
+#define VC_PACKETIZER_FLAG_INFO 0x1
+/** Ask the packetizer to skip the next packet */
+#define VC_PACKETIZER_FLAG_SKIP 0x2
+/** Ask the packetizer to flush any data being processed */
+#define VC_PACKETIZER_FLAG_FLUSH 0x4
+/** Force the packetizer to release an input packet */
+#define VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT 0x8
+/* @} */
+
+/** Push a new packet of data to the packetizer.
+ * This is the mechanism used to feed data into the packetizer. Once a packet has been
+ * pushed into the packetizer it is owned by the packetizer until released by a call to
+ * \ref vc_packetizer_pop
+ *
+ * \param context Pointer to the context of the packetizer to use
+ * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet
+ * to push into the packetizer.
+ * \return the status of the operation
+ */
+VC_CONTAINER_STATUS_T vc_packetizer_push( VC_PACKETIZER_T *context,
+ VC_CONTAINER_PACKET_T *packet);
+
+/** Pop a packet of data from the packetizer.
+ * This allows the client to retrieve consumed data from the packetizer. Packets returned by
+ * the packetizer in this manner can then be released / recycled by the client.
+ * It is possible for the client to retrieve non-consumed data by passing the
+ * VC_PACKETIZER_FLAG_FORCE_RELEASE_INPUT flag. This will however trigger some internal buffering
+ * inside the packetizer and thus is less efficient.
+ *
+ * \param context Pointer to the context of the packetizer to use
+ * \param packet Pointer used to return a consumed packet
+ * \param flags Miscellaneous flags controlling the operation
+ *
+ * \return VC_CONTAINER_SUCCESS if a consumed packet was retrieved,
+ * VC_CONTAINER_ERROR_INCOMPLETE_DATA if none is available.
+ */
+VC_CONTAINER_STATUS_T vc_packetizer_pop( VC_PACKETIZER_T *context,
+ VC_CONTAINER_PACKET_T **packet, VC_PACKETIZER_FLAGS_T flags);
+
+/** Read packetized data out of the packetizer.
+ *
+ * \param context Pointer to the context of the packetizer to use
+ * \param packet Pointer to the VC_CONTAINER_PACKET_T structure describing the data packet
+ * This might need to be partially filled before the call (buffer, buffer_size)
+ * depending on the flags used.
+ * \param flags Miscellaneous flags controlling the operation
+ * \return the status of the operation
+ */
+VC_CONTAINER_STATUS_T vc_packetizer_read( VC_PACKETIZER_T *context,
+ VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags);
+
+/** Reset packetizer state.
+ * This will reset the state of the packetizer as well as mark all data pushed to it as consumed.
+ *
+ * \param context Pointer to the context of the packetizer to reset
+ * \return the status of the operation
+ */
+VC_CONTAINER_STATUS_T vc_packetizer_reset( VC_PACKETIZER_T *context );
+
+/* @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* VC_PACKETIZERS_H */
diff --git a/gfx/include/userland/containers/pcm/pcm_packetizer.c b/gfx/include/userland/containers/pcm/pcm_packetizer.c
new file mode 100644
index 0000000000..8dea39864c
--- /dev/null
+++ b/gfx/include/userland/containers/pcm/pcm_packetizer.c
@@ -0,0 +1,269 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/** \file
+ * Implementation of a PCM packetizer.
+ */
+
+#include
+#include
+
+#include "containers/packetizers.h"
+#include "containers/core/packetizers_private.h"
+#include "containers/core/containers_common.h"
+#include "containers/core/containers_logging.h"
+#include "containers/core/containers_time.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_bytestream.h"
+
+#define FRAME_SIZE (16*1024) /**< Arbitrary value which is neither too small nor too big */
+#define FACTOR_SHIFT 4 /**< Shift applied to the conversion factor */
+
+VC_CONTAINER_STATUS_T pcm_packetizer_open( VC_PACKETIZER_T * );
+
+/*****************************************************************************/
+enum conversion {
+ CONVERSION_NONE = 0,
+ CONVERSION_U8_TO_S16L,
+ CONVERSION_UNKNOWN
+};
+
+typedef struct VC_PACKETIZER_MODULE_T {
+ enum {
+ STATE_NEW_PACKET = 0,
+ STATE_DATA
+ } state;
+
+ unsigned int samples_per_frame;
+ unsigned int bytes_per_sample;
+ unsigned int max_frame_size;
+
+ uint32_t bytes_read;
+ unsigned int frame_size;
+
+ enum conversion conversion;
+ unsigned int conversion_factor;
+} VC_PACKETIZER_MODULE_T;
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T pcm_packetizer_close( VC_PACKETIZER_T *p_ctx )
+{
+ free(p_ctx->priv->module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T pcm_packetizer_reset( VC_PACKETIZER_T *p_ctx )
+{
+ VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
+ module->state = STATE_NEW_PACKET;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static void convert_pcm_u8_to_s16l( uint8_t **p_out, uint8_t *in, size_t size)
+{
+ int16_t *out = (int16_t *)*p_out;
+ uint8_t tmp;
+
+ while(size--)
+ {
+ tmp = *in++;
+ *out++ = ((tmp - 128) << 8) | tmp;
+ }
+ *p_out = (uint8_t *)out;
+}
+
+/*****************************************************************************/
+static void convert_pcm( VC_PACKETIZER_T *p_ctx,
+ VC_CONTAINER_BYTESTREAM_T *stream, size_t size, uint8_t *out )
+{
+ VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
+ uint8_t tmp[256];
+ size_t tmp_size;
+
+ while(size)
+ {
+ tmp_size = MIN(sizeof(tmp), size);
+ bytestream_get(stream, tmp, tmp_size);
+ if (module->conversion == CONVERSION_U8_TO_S16L)
+ convert_pcm_u8_to_s16l(&out, tmp, tmp_size);
+ else
+ bytestream_skip(stream, tmp_size);
+ size -= tmp_size;
+ }
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T pcm_packetizer_packetize( VC_PACKETIZER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *out, VC_PACKETIZER_FLAGS_T flags )
+{
+ VC_PACKETIZER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_BYTESTREAM_T *stream = &p_ctx->priv->stream;
+ VC_CONTAINER_TIME_T *time = &p_ctx->priv->time;
+ int64_t pts, dts;
+ size_t offset, size;
+
+ while(1) switch (module->state)
+ {
+ case STATE_NEW_PACKET:
+ /* Make sure we've got enough data */
+ if(bytestream_size(stream) < module->max_frame_size &&
+ !(flags & VC_PACKETIZER_FLAG_FLUSH))
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
+ if(!bytestream_size(stream))
+ return VC_CONTAINER_ERROR_INCOMPLETE_DATA;
+
+ module->frame_size = bytestream_size(stream);
+ if(module->frame_size > module->max_frame_size)
+ module->frame_size = module->max_frame_size;
+ bytestream_get_timestamps_and_offset(stream, &pts, &dts, &offset, true);
+ vc_container_time_set(time, pts);
+ if(pts != VC_CONTAINER_TIME_UNKNOWN)
+ vc_container_time_add(time, offset / module->bytes_per_sample);
+
+ module->bytes_read = 0;
+ module->state = STATE_DATA;
+ /* fall through to the next state */
+
+ case STATE_DATA:
+ size = module->frame_size - module->bytes_read;
+ out->pts = out->dts = VC_CONTAINER_TIME_UNKNOWN;
+ out->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END;
+ out->size = (size * module->conversion_factor) >> FACTOR_SHIFT;
+
+ if(!module->bytes_read)
+ {
+ out->pts = out->dts = vc_container_time_get(time);
+ out->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
+ }
+
+ if(flags & VC_PACKETIZER_FLAG_INFO)
+ return VC_CONTAINER_SUCCESS;
+
+ if(flags & VC_PACKETIZER_FLAG_SKIP)
+ {
+ bytestream_skip( stream, size );
+ }
+ else
+ {
+ out->size = MIN(out->size, out->buffer_size);
+ size = (out->size << FACTOR_SHIFT) / module->conversion_factor;
+ out->size = (size * module->conversion_factor) >> FACTOR_SHIFT;
+
+ if(module->conversion != CONVERSION_NONE)
+ convert_pcm(p_ctx, stream, size, out->data);
+ else
+ bytestream_get(stream, out->data, out->size);
+ }
+ module->bytes_read += size;
+
+ if(module->bytes_read == module->frame_size)
+ {
+ vc_container_time_add(time, module->samples_per_frame);
+ module->state = STATE_NEW_PACKET;
+ }
+ return VC_CONTAINER_SUCCESS;
+
+ default:
+ break;
+ };
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T pcm_packetizer_open( VC_PACKETIZER_T *p_ctx )
+{
+ VC_PACKETIZER_MODULE_T *module;
+ unsigned int bytes_per_sample = 0;
+ enum conversion conversion = CONVERSION_NONE;
+
+ if(p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_UNSIGNED_BE &&
+ p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_UNSIGNED_LE &&
+ p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_SIGNED_BE &&
+ p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_SIGNED_LE &&
+ p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_FLOAT_BE &&
+ p_ctx->in->codec != VC_CONTAINER_CODEC_PCM_FLOAT_LE)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ if(p_ctx->in->type->audio.block_align)
+ bytes_per_sample = p_ctx->in->type->audio.block_align;
+ else if(p_ctx->in->type->audio.bits_per_sample && p_ctx->in->type->audio.channels)
+ bytes_per_sample = p_ctx->in->type->audio.bits_per_sample *
+ p_ctx->in->type->audio.channels / 8;
+
+ if(!bytes_per_sample)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Check if we support any potential conversion we've been asked to do */
+ if(p_ctx->out->codec_variant)
+ conversion = CONVERSION_UNKNOWN;
+ if(p_ctx->out->codec_variant == VC_FOURCC('s','1','6','l') &&
+ p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_SIGNED_LE &&
+ p_ctx->in->type->audio.bits_per_sample == 16)
+ conversion = CONVERSION_NONE;
+ if(p_ctx->out->codec_variant == VC_FOURCC('s','1','6','l') &&
+ (p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_UNSIGNED_LE ||
+ p_ctx->in->codec == VC_CONTAINER_CODEC_PCM_UNSIGNED_BE) &&
+ p_ctx->in->type->audio.bits_per_sample == 8)
+ conversion = CONVERSION_U8_TO_S16L;
+ if(conversion == CONVERSION_UNKNOWN)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ p_ctx->priv->module = module = malloc(sizeof(*module));
+ if(!module)
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ memset(module, 0, sizeof(*module));
+ module->conversion = conversion;
+ module->conversion_factor = 1 << FACTOR_SHIFT;
+
+ p_ctx->out->codec_variant = 0;
+ if(conversion == CONVERSION_U8_TO_S16L)
+ {
+ module->conversion_factor = 2 << FACTOR_SHIFT;
+ p_ctx->out->type->audio.bits_per_sample *= 2;
+ p_ctx->out->type->audio.block_align *= 2;
+ p_ctx->out->codec = VC_CONTAINER_CODEC_PCM_SIGNED_LE;
+ }
+
+ vc_container_time_set_samplerate(&p_ctx->priv->time, p_ctx->in->type->audio.sample_rate, 1);
+
+ p_ctx->max_frame_size = FRAME_SIZE;
+ module->max_frame_size = (FRAME_SIZE << FACTOR_SHIFT) / module->conversion_factor;
+ module->bytes_per_sample = bytes_per_sample;
+ module->samples_per_frame = module->max_frame_size / bytes_per_sample;
+ p_ctx->priv->pf_close = pcm_packetizer_close;
+ p_ctx->priv->pf_packetize = pcm_packetizer_packetize;
+ p_ctx->priv->pf_reset = pcm_packetizer_reset;
+
+ LOG_DEBUG(0, "using pcm audio packetizer");
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_PACKETIZER_REGISTER(pcm_packetizer_open, "pcm");
diff --git a/gfx/include/userland/containers/qsynth/CMakeLists.txt b/gfx/include/userland/containers/qsynth/CMakeLists.txt
new file mode 100644
index 0000000000..b188e50697
--- /dev/null
+++ b/gfx/include/userland/containers/qsynth/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(reader_qsynth ${LIBRARY_TYPE} qsynth_reader.c)
+
+target_link_libraries(reader_qsynth containers)
+
+install(TARGETS reader_qsynth DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/qsynth/qsynth_reader.c b/gfx/include/userland/containers/qsynth/qsynth_reader.c
new file mode 100644
index 0000000000..06a9d54ec1
--- /dev/null
+++ b/gfx/include/userland/containers/qsynth/qsynth_reader.c
@@ -0,0 +1,482 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+
+#define BI32(b) (((b)[0]<<24)|((b)[1]<<16)|((b)[2]<<8)|((b)[3]))
+#define BI16(b) (((b)[0]<<8)|((b)[1]))
+
+#define HEADER_LENGTH 14
+#define MAX_TRACKS 128
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+struct _QSYNTH_SEGMENT_T {
+ struct _QSYNTH_SEGMENT_T *next;
+ uint32_t len;
+ uint8_t *data;
+};
+typedef struct _QSYNTH_SEGMENT_T QSYNTH_SEGMENT_T;
+
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *track;
+ uint32_t filesize;
+ QSYNTH_SEGMENT_T *seg;
+ QSYNTH_SEGMENT_T *pass;
+ uint32_t sent;
+ int64_t timestamp;
+ uint32_t seek;
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+static VC_CONTAINER_STATUS_T qsynth_read_header(uint8_t *data, uint32_t *tracks,
+ uint32_t *division, uint8_t *fps, uint8_t *dpf)
+{
+ if(data[0] != 'M' || data[1] != 'T' || data[2] != 'h' || data[3] != 'd' ||
+ data[4] != 0 || data[5] != 0 || data[6] != 0 || data[7] != 6)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ if(data[12] < 0x80)
+ {
+ if(division) *division = BI16(data+12);
+ }
+ else
+ {
+ if(fps) *fps = 256-data[12];
+ if(dpf) *dpf = data[13];
+ }
+
+ if(tracks) *tracks = BI16(data+10);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+static int qsynth_read_variable(uint8_t *data, uint32_t *val)
+{
+ int i = 0;
+ *val = 0;
+ do {
+ *val = (*val << 7) + (data[i] & 0x7f);
+ } while(data[i++] & 0x80);
+
+ return i;
+}
+
+static VC_CONTAINER_STATUS_T qsynth_read_event(uint8_t *data, uint32_t *used, uint8_t *last,
+ uint32_t *time, uint32_t *tempo, uint32_t *end)
+{
+ int read;
+
+ // need at least 4 bytes here
+ read = qsynth_read_variable(data, time);
+
+ if(data[read] == 0xff) // meta event
+ {
+ uint32_t len;
+ uint8_t type = data[read+1];
+
+ read += 2;
+ read += qsynth_read_variable(data+read, &len);
+
+ if(type == 0x2f) // end of track
+ {
+ if(len != 0)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ *end = 1;
+ }
+ else if(type == 0x51) // tempo event
+ {
+ if(len != 3)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ *tempo = (data[read]<<16) | (data[read+1]<<8) | data[read+2];
+ }
+
+ read += len;
+ }
+ else if(data[read] == 0xf0 || data[read] == 0xf7) // sysex events
+ {
+ uint32_t len;
+ read += 1;
+ read += qsynth_read_variable(data+read, &len) + len;
+ }
+ else // midi event
+ {
+ uint8_t type;
+
+ if(data[read] < 128)
+ type = *last;
+ else
+ {
+ type = data[read] >> 4;
+ *last = type;
+ read++;
+ }
+
+ switch(type) {
+ case 8: case 9: case 0xa: case 0xb: case 0xe:
+ read += 2;
+ break;
+ case 0xc: case 0xd:
+ read += 1;
+ break;
+ default:
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+ }
+
+ *used = read;
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T qsynth_read_track(QSYNTH_SEGMENT_T *seg,
+ uint32_t *ticks, int64_t *time,
+ uint32_t *us_perclock, uint32_t *tempo_ticks)
+{
+ uint32_t total_ticks = 0;
+ uint32_t used = 8;
+ uint8_t last = 0;
+
+ *time = 0LL;
+ *tempo_ticks = 0;
+
+ while(used < seg->len)
+ {
+ VC_CONTAINER_STATUS_T status;
+ uint32_t event_ticks, new_tempo = 0, end = 0, event_used;
+ if((status = qsynth_read_event(seg->data+used, &event_used, &last, &event_ticks, &new_tempo, &end)) != VC_CONTAINER_SUCCESS)
+ return status;
+
+ used += event_used;
+ total_ticks += event_ticks;
+
+ if(new_tempo != 0)
+ {
+ *time += ((int64_t) (total_ticks - *tempo_ticks)) * (*us_perclock);
+ *us_perclock = new_tempo;
+ *tempo_ticks = total_ticks;
+ }
+
+ if(end)
+ break;
+ }
+
+ *ticks = total_ticks;
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T qsynth_get_duration(VC_CONTAINER_T *p_ctx)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+ QSYNTH_SEGMENT_T **seg = &(module->seg);
+ uint32_t i, tracks, division = 0, max_ticks = 0, us_perclock = 500000;
+ uint32_t end_uspc = 0, end_ticks = 0;
+ int64_t end_time = 0;
+ uint8_t fps = 1, dpf = 1;
+
+ if((*seg = malloc(sizeof(QSYNTH_SEGMENT_T) + HEADER_LENGTH)) == NULL)
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ (*seg)->next = NULL;
+ (*seg)->len = HEADER_LENGTH;
+ (*seg)->data = (uint8_t *) ((*seg) + 1);
+
+ if(PEEK_BYTES(p_ctx, (*seg)->data, HEADER_LENGTH) != HEADER_LENGTH)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ if((status = qsynth_read_header((*seg)->data, &tracks, &division, &fps, &dpf)) != VC_CONTAINER_SUCCESS)
+ return status;
+
+ // if we have a suspiciously large number of tracks, this is probably a bad file
+ if(tracks > MAX_TRACKS)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ SKIP_BYTES(p_ctx, HEADER_LENGTH);
+
+ seg = &((*seg)->next);
+ module->filesize = HEADER_LENGTH;
+
+ if(division == 0)
+ {
+ us_perclock = 1000000 / (fps * dpf);
+ division = 1;
+ }
+
+ for(i=0; i (1<<20) || (*seg = malloc(sizeof(QSYNTH_SEGMENT_T) + 8 + len)) == NULL)
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ module->filesize += len+8;
+ (*seg)->next = NULL;
+ (*seg)->len = len + 8;
+ (*seg)->data = (uint8_t *) ((*seg) + 1);
+
+ memcpy((*seg)->data, dummy, 8);
+ if(READ_BYTES(p_ctx, (*seg)->data+8, len) != len)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ if((status = qsynth_read_track(*seg, &ticks, &time, &us_perclock, &tempo_ticks)) != VC_CONTAINER_SUCCESS)
+ return status;
+
+ if(end_uspc == 0)
+ {
+ end_uspc = us_perclock;
+ end_ticks = tempo_ticks;
+ end_time = time;
+ }
+
+ if(ticks > max_ticks)
+ max_ticks = ticks;
+
+ seg = &((*seg)->next);
+ }
+
+ if(end_uspc == 0)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ module->pass = module->seg;
+ module->sent = 0;
+ p_ctx->duration = (end_time + (((int64_t) (max_ticks - end_ticks)) * end_uspc)) / division;
+ module->track->format->extradata = (uint8_t *) &module->filesize;
+ module->track->format->extradata_size = 4;
+ return VC_CONTAINER_SUCCESS;
+}
+
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+*****************************************************************************/
+static VC_CONTAINER_STATUS_T qsynth_reader_read( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *packet,
+ uint32_t flags )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ if(module->pass)
+ {
+ packet->size = module->pass->len - module->sent;
+ packet->dts = packet->pts = 0;
+ packet->track = 0;
+ packet->flags = module->sent ? 0 : VC_CONTAINER_PACKET_FLAG_FRAME_START;
+ }
+ else
+ {
+ if(module->timestamp > p_ctx->duration)
+ return VC_CONTAINER_ERROR_EOS;
+
+ packet->size = 5;
+ packet->dts = packet->pts = module->timestamp;
+ packet->track = 0;
+ packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME;
+ }
+
+ if(flags & VC_CONTAINER_READ_FLAG_SKIP)
+ {
+ if(module->pass)
+ {
+ module->pass = module->pass->next;
+ module->sent = 0;
+ }
+ else
+ {
+ // if we're playing then we can't really skip, but have to simulate a seek instead
+ module->seek = 1;
+ module->timestamp += 40;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ if(flags & VC_CONTAINER_READ_FLAG_INFO)
+ return VC_CONTAINER_SUCCESS;
+
+ // read frame into packet->data
+ if(module->pass)
+ {
+ uint32_t copy = MIN(packet->size, packet->buffer_size);
+ memcpy(packet->data, module->pass->data + module->sent, copy);
+ packet->size = copy;
+
+ if((module->sent += copy) == module->pass->len)
+ {
+ module->pass = module->pass->next;
+ module->sent = 0;
+ packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
+ }
+ }
+ else
+ {
+ if(packet->buffer_size < packet->size)
+ return VC_CONTAINER_ERROR_BUFFER_TOO_SMALL;
+
+ if(module->seek)
+ {
+ uint32_t current_time = module->timestamp / 1000;
+
+ packet->data[0] = 'S';
+ packet->data[1] = (uint8_t)((current_time >> 24) & 0xFF);
+ packet->data[2] = (uint8_t)((current_time >> 16) & 0xFF);
+ packet->data[3] = (uint8_t)((current_time >> 8) & 0xFF);
+ packet->data[4] = (uint8_t)((current_time ) & 0xFF);
+ module->seek = 0;
+ }
+ else
+ {
+ packet->data[0] = 'P';
+ packet->data[1] = 0;
+ packet->data[2] = 0;
+ packet->data[3] = 0;
+ packet->data[4] = 40;
+ module->timestamp += 40 * 1000;
+ }
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T qsynth_reader_seek( VC_CONTAINER_T *p_ctx,
+ int64_t *offset,
+ VC_CONTAINER_SEEK_MODE_T mode,
+ VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_PARAM_UNUSED(flags);
+
+ if (mode != VC_CONTAINER_SEEK_MODE_TIME)
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+
+ if(*offset < 0)
+ *offset = 0;
+ else if(*offset > p_ctx->duration)
+ *offset = p_ctx->duration;
+
+ module->timestamp = *offset;
+ module->seek = 1;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T qsynth_reader_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ QSYNTH_SEGMENT_T *seg = module->seg;
+ for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--)
+ vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]);
+ while(seg != NULL)
+ {
+ QSYNTH_SEGMENT_T *next = seg->next;
+ free(seg);
+ seg = next;
+ }
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T qsynth_reader_open( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = 0;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ uint8_t header[HEADER_LENGTH];
+
+ /* Check the file header */
+ if((PEEK_BYTES(p_ctx, header, HEADER_LENGTH) != HEADER_LENGTH) ||
+ qsynth_read_header(header, 0, 0, 0, 0) != VC_CONTAINER_SUCCESS)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks_num = 1;
+ p_ctx->tracks = &module->track;
+ p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0);
+ if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_AUDIO;
+ p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_MIDI;
+ p_ctx->tracks[0]->is_enabled = true;
+
+ if((status = qsynth_get_duration(p_ctx)) != VC_CONTAINER_SUCCESS) goto error;
+
+ LOG_DEBUG(p_ctx, "using qsynth reader");
+
+ p_ctx->capabilities = VC_CONTAINER_CAPS_CAN_SEEK;
+
+ p_ctx->priv->pf_close = qsynth_reader_close;
+ p_ctx->priv->pf_read = qsynth_reader_read;
+ p_ctx->priv->pf_seek = qsynth_reader_seek;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ LOG_DEBUG(p_ctx, "qsynth: error opening stream (%i)", status);
+ if(module) qsynth_reader_close(p_ctx);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open qsynth_reader_open
+#endif
diff --git a/gfx/include/userland/containers/raw/CMakeLists.txt b/gfx/include/userland/containers/raw/CMakeLists.txt
new file mode 100644
index 0000000000..0ada17ea49
--- /dev/null
+++ b/gfx/include/userland/containers/raw/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(reader_raw_video ${LIBRARY_TYPE} raw_video_reader.c)
+
+target_link_libraries(reader_raw_video containers)
+
+install(TARGETS reader_raw_video DESTINATION ${VMCS_PLUGIN_DIR})
+
+add_library(writer_raw_video ${LIBRARY_TYPE} raw_video_writer.c)
+
+target_link_libraries(writer_raw_video containers)
+
+install(TARGETS writer_raw_video DESTINATION ${VMCS_PLUGIN_DIR})
diff --git a/gfx/include/userland/containers/raw/raw_video_common.h b/gfx/include/userland/containers/raw/raw_video_common.h
new file mode 100644
index 0000000000..52d73ec1c7
--- /dev/null
+++ b/gfx/include/userland/containers/raw/raw_video_common.h
@@ -0,0 +1,66 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef RAW_VIDEO_COMMON_H
+#define RAW_VIDEO_COMMON_H
+
+static struct {
+ const char *id;
+ VC_CONTAINER_FOURCC_T codec;
+ unsigned int size_num;
+ unsigned int size_den;
+} table[] = {
+ {"420", VC_CONTAINER_CODEC_I420, 3, 2},
+ {0, 0, 0, 0}
+};
+
+STATIC_INLINE bool from_yuv4mpeg2(const char *id, VC_CONTAINER_FOURCC_T *codec,
+ unsigned int *size_num, unsigned int *size_den)
+{
+ unsigned int i;
+ for (i = 0; table[i].id; i++)
+ if (!strcmp(id, table[i].id))
+ break;
+ if (codec) *codec = table[i].codec;
+ if (size_num) *size_num = table[i].size_num;
+ if (size_den) *size_den = table[i].size_den;
+ return !!table[i].id;
+}
+
+STATIC_INLINE bool to_yuv4mpeg2(VC_CONTAINER_FOURCC_T codec, const char **id,
+ unsigned int *size_num, unsigned int *size_den)
+{
+ unsigned int i;
+ for (i = 0; table[i].id; i++)
+ if (codec == table[i].codec)
+ break;
+ if (id) *id = table[i].id;
+ if (size_num) *size_num = table[i].size_num;
+ if (size_den) *size_den = table[i].size_den;
+ return !!table[i].id;
+}
+
+#endif /* RAW_VIDEO_COMMON_H */
diff --git a/gfx/include/userland/containers/raw/raw_video_reader.c b/gfx/include/userland/containers/raw/raw_video_reader.c
new file mode 100644
index 0000000000..00ac0640b9
--- /dev/null
+++ b/gfx/include/userland/containers/raw/raw_video_reader.c
@@ -0,0 +1,465 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+#include
+
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+
+#include "raw_video_common.h"
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+#define FILE_HEADER_SIZE_MAX 1024
+#define FRAME_HEADER_SIZE_MAX 256
+#define OPTION_SIZE_MAX 32
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *track;
+ VC_CONTAINER_STATUS_T status;
+
+ bool yuv4mpeg2;
+ bool non_standard;
+ char option[OPTION_SIZE_MAX];
+
+ bool frame_header;
+ unsigned int frame_header_size;
+
+ int64_t data_offset;
+ unsigned int block_size;
+ unsigned int block_offset;
+ unsigned int frames;
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+static VC_CONTAINER_STATUS_T read_yuv4mpeg2_option( VC_CONTAINER_T *ctx,
+ unsigned int *bytes_left )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ unsigned int size, i;
+
+ /* Start by skipping spaces */
+ while (*bytes_left && PEEK_U8(ctx) == ' ')
+ (*bytes_left)--, _SKIP_U8(ctx);
+
+ size = PEEK_BYTES(ctx, module->option,
+ MIN(sizeof(module->option), *bytes_left));
+
+ /* The config option ends at next space or newline */
+ for (i = 0; i < size; i++)
+ {
+ if (module->option[i] == ' ' || module->option[i] == 0x0a)
+ {
+ module->option[i] = 0;
+ break;
+ }
+ }
+ if (i == 0)
+ return VC_CONTAINER_ERROR_NOT_FOUND;
+
+ *bytes_left -= i;
+ SKIP_BYTES(ctx, i);
+
+ /* If option is too long, we just discard it */
+ if (i == size)
+ {
+ while (*bytes_left && PEEK_U8(ctx) != ' ' && PEEK_U8(ctx) != 0x0a)
+ (*bytes_left)--, _SKIP_U8(ctx);
+ return VC_CONTAINER_ERROR_NOT_FOUND;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T read_yuv4mpeg2_file_header( VC_CONTAINER_T *ctx )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ unsigned int bytes_left = FILE_HEADER_SIZE_MAX - 10;
+ unsigned int value1, value2;
+ char codec[OPTION_SIZE_MAX] = "420";
+ uint8_t h[10];
+
+ /* Check for the YUV4MPEG2 signature */
+ if (READ_BYTES(ctx, h, sizeof(h)) != sizeof(h))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ if (memcmp(h, "YUV4MPEG2 ", sizeof(h)))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Parse parameters */
+ while (read_yuv4mpeg2_option(ctx, &bytes_left) == VC_CONTAINER_SUCCESS)
+ {
+ if (sscanf(module->option, "W%i", &value1) == 1)
+ ctx->tracks[0]->format->type->video.width = value1;
+ else if (sscanf(module->option, "H%i", &value1) == 1)
+ ctx->tracks[0]->format->type->video.height = value1;
+ else if (sscanf(module->option, "S%i", &value1) == 1)
+ module->block_size = value1;
+ else if (sscanf(module->option, "F%i:%i", &value1, &value2) == 2)
+ {
+ ctx->tracks[0]->format->type->video.frame_rate_num = value1;
+ ctx->tracks[0]->format->type->video.frame_rate_den = value2;
+ }
+ else if (sscanf(module->option, "A%i:%i", &value1, &value2) == 2)
+ {
+ ctx->tracks[0]->format->type->video.par_num = value1;
+ ctx->tracks[0]->format->type->video.par_den = value2;
+ }
+ else if (module->option[0] == 'C')
+ {
+ strcpy(codec, module->option+1);
+ }
+ }
+
+ /* Check the end marker */
+ if (_READ_U8(ctx) != 0x0a)
+ {
+ LOG_ERROR(ctx, "missing end of header marker");
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ /* Find out which codec we are dealing with */
+ if (from_yuv4mpeg2(codec, &ctx->tracks[0]->format->codec, &value1, &value2))
+ {
+ module->block_size = ctx->tracks[0]->format->type->video.width *
+ ctx->tracks[0]->format->type->video.height * value1 / value2;
+ }
+ else
+ {
+ memcpy(&ctx->tracks[0]->format->codec, codec, 4);
+ module->non_standard = true;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T read_yuv4mpeg2_frame_header( VC_CONTAINER_T *ctx )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ unsigned int bytes_left = FRAME_HEADER_SIZE_MAX - 5;
+ unsigned int value1;
+ char header[5];
+
+ if (READ_BYTES(ctx, header, sizeof(header)) != sizeof(header) ||
+ memcmp(header, "FRAME", sizeof(header)))
+ {
+ LOG_ERROR(ctx, "missing frame marker");
+ return STREAM_STATUS(ctx) != VC_CONTAINER_SUCCESS ?
+ STREAM_STATUS(ctx) : VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ /* Parse parameters */
+ while (read_yuv4mpeg2_option(ctx, &bytes_left) == VC_CONTAINER_SUCCESS)
+ {
+ if (module->non_standard && sscanf(module->option, "S%i", &value1) == 1)
+ module->block_size = value1;
+ }
+
+ /* Check the end marker */
+ if (_READ_U8(ctx) != 0x0a)
+ {
+ LOG_ERROR(ctx, "missing end of frame header marker");
+ return VC_CONTAINER_ERROR_CORRUPTED;
+ }
+
+ module->frame_header_size = FRAME_HEADER_SIZE_MAX - bytes_left - 1;
+ return VC_CONTAINER_SUCCESS;
+}
+
+static VC_CONTAINER_STATUS_T rawvideo_parse_uri( VC_CONTAINER_T *ctx,
+ VC_CONTAINER_FOURCC_T *c, unsigned int *w, unsigned int *h,
+ unsigned int *fr_num, unsigned int *fr_den, unsigned *block_size )
+{
+ VC_CONTAINER_FOURCC_T codec = 0;
+ unsigned int i, matches, width = 0, height = 0, fn = 0, fd = 0, size = 0;
+ const char *uri = ctx->priv->io->uri;
+
+ /* Try and find a match for the string describing the format */
+ for (i = 0; uri[i]; i++)
+ {
+ if (uri[i] != '_' && uri[i+1] != 'C')
+ continue;
+
+ matches = sscanf(uri+i, "_C%4cW%iH%iF%i#%iS%i", (char *)&codec,
+ &width, &height, &fn, &fd, &size);
+ if (matches >= 3)
+ break;
+ }
+ if (!uri[i])
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ if (!size)
+ {
+ switch (codec)
+ {
+ case VC_CONTAINER_CODEC_I420:
+ case VC_CONTAINER_CODEC_YV12:
+ size = width * height * 3 / 2;
+ break;
+ default: break;
+ }
+ }
+
+ if (!width || !height || !size)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ if (block_size) *block_size = size;
+ if (c) *c = codec;
+ if (w) *w = width;
+ if (h) *h = height;
+ if (fr_num) *fr_num = fn;
+ if (fr_den) *fr_den = fd;
+ if (block_size) *block_size = size;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+ *****************************************************************************/
+static VC_CONTAINER_STATUS_T rawvideo_reader_read( VC_CONTAINER_T *ctx,
+ VC_CONTAINER_PACKET_T *packet, uint32_t flags )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ unsigned int size;
+
+ if (module->status != VC_CONTAINER_SUCCESS)
+ return module->status;
+
+ if (module->yuv4mpeg2 && !module->block_offset &&
+ !module->frame_header)
+ {
+ module->status = read_yuv4mpeg2_frame_header(ctx);
+ if (module->status != VC_CONTAINER_SUCCESS)
+ return module->status;
+
+ module->frame_header = true;
+ }
+
+ if (!module->block_offset)
+ packet->pts = packet->dts = module->frames * INT64_C(1000000) *
+ ctx->tracks[0]->format->type->video.frame_rate_den /
+ ctx->tracks[0]->format->type->video.frame_rate_num;
+ else
+ packet->pts = packet->dts = VC_CONTAINER_TIME_UNKNOWN;
+ packet->flags = VC_CONTAINER_PACKET_FLAG_FRAME_END |
+ VC_CONTAINER_PACKET_FLAG_KEYFRAME;
+ if (!module->block_offset)
+ packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
+ packet->frame_size = module->block_size;
+ packet->size = module->block_size - module->block_offset;
+ packet->track = 0;
+
+ if (flags & VC_CONTAINER_READ_FLAG_SKIP)
+ {
+ size = SKIP_BYTES(ctx, packet->size);
+ module->block_offset = 0;
+ module->frames++;
+ module->frame_header = 0;
+ module->status = STREAM_STATUS(ctx);
+ return module->status;
+ }
+
+ if (flags & VC_CONTAINER_READ_FLAG_INFO)
+ return VC_CONTAINER_SUCCESS;
+
+ size = MIN(module->block_size - module->block_offset, packet->buffer_size);
+ size = READ_BYTES(ctx, packet->data, size);
+ module->block_offset += size;
+ packet->size = size;
+
+ if (module->block_offset == module->block_size)
+ {
+ module->block_offset = 0;
+ module->frame_header = 0;
+ module->frames++;
+ }
+
+ module->status = size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(ctx);
+ return module->status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T rawvideo_reader_seek( VC_CONTAINER_T *ctx, int64_t *offset,
+ VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ VC_CONTAINER_PARAM_UNUSED(mode);
+
+ module->frames = *offset *
+ ctx->tracks[0]->format->type->video.frame_rate_num /
+ ctx->tracks[0]->format->type->video.frame_rate_den / INT64_C(1000000);
+ module->block_offset = 0;
+
+ if ((flags & VC_CONTAINER_SEEK_FLAG_FORWARD) &&
+ module->frames * INT64_C(1000000) *
+ ctx->tracks[0]->format->type->video.frame_rate_den /
+ ctx->tracks[0]->format->type->video.frame_rate_num < *offset)
+ module->frames++;
+
+ module->frame_header = 0;
+
+ module->status =
+ SEEK(ctx, module->data_offset + module->frames *
+ (module->block_size + module->frame_header_size));
+
+ return module->status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T rawvideo_reader_close( VC_CONTAINER_T *ctx )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ for (; ctx->tracks_num > 0; ctx->tracks_num--)
+ vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]);
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T rawvideo_reader_open( VC_CONTAINER_T *ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ const char *extension = vc_uri_path_extension(ctx->priv->uri);
+ VC_CONTAINER_MODULE_T *module = 0;
+ bool yuv4mpeg2 = false;
+ uint8_t h[10];
+
+ /* Check if the user has specified a container */
+ vc_uri_find_query(ctx->priv->uri, 0, "container", &extension);
+
+ /* Check for the YUV4MPEG2 signature */
+ if (PEEK_BYTES(ctx, h, sizeof(h)) != sizeof(h))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if (!memcmp(h, "YUV4MPEG2 ", sizeof(h)))
+ yuv4mpeg2 = true;
+
+ /* Or check if the extension is supported */
+ if (!yuv4mpeg2 &&
+ !(extension && !strcasecmp(extension, "yuv")))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ LOG_DEBUG(ctx, "using raw video reader");
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if (!module) return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ memset(module, 0, sizeof(*module));
+ ctx->priv->module = module;
+ ctx->tracks_num = 1;
+ ctx->tracks = &module->track;
+ ctx->tracks[0] = vc_container_allocate_track(ctx, 0);
+ if (!ctx->tracks[0])
+ {
+ status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ goto error;
+ }
+ ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO;
+ ctx->tracks[0]->is_enabled = true;
+ ctx->tracks[0]->format->type->video.frame_rate_num = 25;
+ ctx->tracks[0]->format->type->video.frame_rate_den = 1;
+ ctx->tracks[0]->format->type->video.par_num = 1;
+ ctx->tracks[0]->format->type->video.par_den = 1;
+
+ if (yuv4mpeg2)
+ {
+ status = read_yuv4mpeg2_file_header(ctx);
+ if (status != VC_CONTAINER_SUCCESS)
+ goto error;
+
+ module->data_offset = STREAM_POSITION(ctx);
+
+ status = read_yuv4mpeg2_frame_header(ctx);
+ if (status != VC_CONTAINER_SUCCESS)
+ goto error;
+ module->frame_header = true;
+ }
+ else
+ {
+ VC_CONTAINER_FOURCC_T codec;
+ unsigned int width, height, fr_num, fr_den, block_size;
+
+ status = rawvideo_parse_uri(ctx, &codec, &width, &height,
+ &fr_num, &fr_den, &block_size);
+ if (status != VC_CONTAINER_SUCCESS)
+ goto error;
+ ctx->tracks[0]->format->codec = codec;
+ ctx->tracks[0]->format->type->video.width = width;
+ ctx->tracks[0]->format->type->video.height = height;
+ if (fr_num && fr_den)
+ {
+ ctx->tracks[0]->format->type->video.frame_rate_num = fr_num;
+ ctx->tracks[0]->format->type->video.frame_rate_den = fr_den;
+ }
+ module->block_size = block_size;
+ }
+
+ /*
+ * We now have all the information we really need to start playing the stream
+ */
+
+ LOG_INFO(ctx, "rawvideo %4.4s/%ix%i/fps:%i:%i/size:%i",
+ (char *)&ctx->tracks[0]->format->codec,
+ ctx->tracks[0]->format->type->video.width,
+ ctx->tracks[0]->format->type->video.height,
+ ctx->tracks[0]->format->type->video.frame_rate_num,
+ ctx->tracks[0]->format->type->video.frame_rate_den, module->block_size);
+ ctx->priv->pf_close = rawvideo_reader_close;
+ ctx->priv->pf_read = rawvideo_reader_read;
+ ctx->priv->pf_seek = rawvideo_reader_seek;
+ module->yuv4mpeg2 = yuv4mpeg2;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ LOG_DEBUG(ctx, "rawvideo: error opening stream (%i)", status);
+ rawvideo_reader_close(ctx);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open rawvideo_reader_open
+#endif
diff --git a/gfx/include/userland/containers/raw/raw_video_writer.c b/gfx/include/userland/containers/raw/raw_video_writer.c
new file mode 100644
index 0000000000..49b7c7c5de
--- /dev/null
+++ b/gfx/include/userland/containers/raw/raw_video_writer.c
@@ -0,0 +1,264 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+#include
+#include
+
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+
+#include "raw_video_common.h"
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *track;
+ bool yuv4mpeg2;
+ bool header_done;
+ bool non_standard;
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+static VC_CONTAINER_STATUS_T rawvideo_write_header( VC_CONTAINER_T *ctx )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ unsigned int size;
+ char line[128];
+ const char *id;
+
+ size = snprintf(line, sizeof(line), "YUV4MPEG2 W%i H%i",
+ ctx->tracks[0]->format->type->video.width,
+ ctx->tracks[0]->format->type->video.height);
+ if (size >= sizeof(line))
+ return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ WRITE_BYTES(ctx, line, size);
+
+ if (ctx->tracks[0]->format->type->video.frame_rate_num &&
+ ctx->tracks[0]->format->type->video.frame_rate_den)
+ {
+ size = snprintf(line, sizeof(line), " F%i:%i",
+ ctx->tracks[0]->format->type->video.frame_rate_num,
+ ctx->tracks[0]->format->type->video.frame_rate_den);
+ if (size >= sizeof(line))
+ return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ WRITE_BYTES(ctx, line, size);
+ }
+
+ if (ctx->tracks[0]->format->type->video.par_num &&
+ ctx->tracks[0]->format->type->video.par_den)
+ {
+ size = snprintf(line, sizeof(line), " A%i:%i",
+ ctx->tracks[0]->format->type->video.par_num,
+ ctx->tracks[0]->format->type->video.par_den);
+ if (size >= sizeof(line))
+ return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ WRITE_BYTES(ctx, line, size);
+ }
+
+ if (to_yuv4mpeg2(ctx->tracks[0]->format->codec, &id, 0, 0))
+ {
+ size = snprintf(line, sizeof(line), " C%s", id);
+ }
+ else
+ {
+ module->non_standard = true;
+ size = snprintf(line, sizeof(line), " C%4.4s",
+ (char *)&ctx->tracks[0]->format->codec);
+ }
+ if (size >= sizeof(line))
+ return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ WRITE_BYTES(ctx, line, size);
+
+ _WRITE_U8(ctx, 0x0a);
+ module->header_done = true;
+ return STREAM_STATUS(ctx);
+}
+
+static VC_CONTAINER_STATUS_T simple_write_add_track( VC_CONTAINER_T *ctx,
+ VC_CONTAINER_ES_FORMAT_T *format )
+{
+ VC_CONTAINER_STATUS_T status;
+
+ /* Sanity check that we support the type of track being created */
+ if (ctx->tracks_num)
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ if (format->es_type != VC_CONTAINER_ES_TYPE_VIDEO)
+ return VC_CONTAINER_ERROR_TRACK_FORMAT_NOT_SUPPORTED;
+
+ /* Allocate and initialise track data */
+ ctx->tracks[0] = vc_container_allocate_track(ctx, 0);
+ if (!ctx->tracks[0])
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+
+ status = vc_container_track_allocate_extradata(ctx,
+ ctx->tracks[0], format->extradata_size);
+ if(status != VC_CONTAINER_SUCCESS)
+ return status;
+
+ vc_container_format_copy(ctx->tracks[0]->format, format,
+ format->extradata_size);
+ ctx->tracks_num++;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+ *****************************************************************************/
+static VC_CONTAINER_STATUS_T rawvideo_writer_close( VC_CONTAINER_T *ctx )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ for (; ctx->tracks_num > 0; ctx->tracks_num--)
+ vc_container_free_track(ctx, ctx->tracks[ctx->tracks_num-1]);
+ free(module);
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T rawvideo_writer_write( VC_CONTAINER_T *ctx,
+ VC_CONTAINER_PACKET_T *packet )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ VC_CONTAINER_STATUS_T status;
+
+ if (module->yuv4mpeg2 && !module->header_done)
+ {
+ status = rawvideo_write_header(ctx);
+ if (status != VC_CONTAINER_SUCCESS)
+ return status;
+ }
+
+ if (module->yuv4mpeg2 &&
+ (packet->flags & VC_CONTAINER_PACKET_FLAG_FRAME_START))
+ {
+ /* Write the metadata */
+ WRITE_BYTES(ctx, "FRAME", sizeof("FRAME")-1);
+
+ /* For formats not supported by the YUV4MPEG2 spec, we prepend
+ * each frame with its size */
+ if (module->non_standard)
+ {
+ unsigned int size;
+ char line[32];
+ size = snprintf(line, sizeof(line), " S%i",
+ packet->frame_size ? packet->frame_size : packet->size);
+ if (size < sizeof(line))
+ WRITE_BYTES(ctx, line, size);
+ }
+
+ _WRITE_U8(ctx, 0x0a);
+ }
+
+ /* Write the elementary stream */
+ WRITE_BYTES(ctx, packet->data, packet->size);
+
+ return STREAM_STATUS(ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T rawvideo_writer_control( VC_CONTAINER_T *ctx,
+ VC_CONTAINER_CONTROL_T operation, va_list args )
+{
+ VC_CONTAINER_MODULE_T *module = ctx->priv->module;
+ VC_CONTAINER_ES_FORMAT_T *format;
+
+ switch (operation)
+ {
+ case VC_CONTAINER_CONTROL_TRACK_ADD:
+ format = (VC_CONTAINER_ES_FORMAT_T *)va_arg(args, VC_CONTAINER_ES_FORMAT_T *);
+ return simple_write_add_track(ctx, format);
+
+ case VC_CONTAINER_CONTROL_TRACK_ADD_DONE:
+ return module->yuv4mpeg2 ?
+ rawvideo_write_header( ctx ) : VC_CONTAINER_SUCCESS;
+
+ default: return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ }
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T rawvideo_writer_open( VC_CONTAINER_T *ctx )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ const char *extension = vc_uri_path_extension(ctx->priv->uri);
+ VC_CONTAINER_MODULE_T *module;
+ bool yuv4mpeg2 = false;
+
+ /* Check if the user has specified a container */
+ vc_uri_find_query(ctx->priv->uri, 0, "container", &extension);
+
+ /* Check we're the right writer for this */
+ if(!extension)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ if(!strcasecmp(extension, "y4m") || !strcasecmp(extension, "yuv4mpeg2"))
+ yuv4mpeg2 = true;
+ if(!yuv4mpeg2 && strcasecmp(extension, "yuv"))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ LOG_DEBUG(ctx, "using rawvideo writer");
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ ctx->priv->module = module;
+ ctx->tracks = &module->track;
+ module->yuv4mpeg2 = yuv4mpeg2;
+
+ ctx->priv->pf_close = rawvideo_writer_close;
+ ctx->priv->pf_write = rawvideo_writer_write;
+ ctx->priv->pf_control = rawvideo_writer_control;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ LOG_DEBUG(ctx, "rawvideo: error opening stream (%i)", status);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak writer_open rawvideo_writer_open
+#endif
diff --git a/gfx/include/userland/containers/rcv/CMakeLists.txt b/gfx/include/userland/containers/rcv/CMakeLists.txt
new file mode 100644
index 0000000000..9407f582bc
--- /dev/null
+++ b/gfx/include/userland/containers/rcv/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+add_library(reader_rcv ${LIBRARY_TYPE} rcv_reader.c)
+
+target_link_libraries(reader_rcv containers)
+
+install(TARGETS reader_rcv DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/rcv/rcv_reader.c b/gfx/include/userland/containers/rcv/rcv_reader.c
new file mode 100644
index 0000000000..4d4db27c7f
--- /dev/null
+++ b/gfx/include/userland/containers/rcv/rcv_reader.c
@@ -0,0 +1,358 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_index.h"
+#include "containers/core/containers_logging.h"
+
+/******************************************************************************
+Defines.
+******************************************************************************/
+
+#define LI32(b) (((b)[3]<<24)|((b)[2]<<16)|((b)[1]<<8)|((b)[0]))
+#define LI24(b) (((b)[2]<<16)|((b)[1]<<8)|((b)[0]))
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+typedef struct {
+ unsigned int num_frames : 24;
+ unsigned int constant_c5 : 8;
+ int constant_4;
+ uint32_t struct_c;
+ uint32_t vert_size;
+ uint32_t horiz_size;
+ int constant_c;
+ uint32_t struct_b[2];
+ uint32_t framerate;
+} RCV_FILE_HEADER_T;
+
+typedef struct {
+ unsigned int framesize : 24;
+ unsigned int res : 7;
+ unsigned int keyframe : 1;
+ uint32_t timestamp;
+} RCV_FRAME_HEADER_T;
+
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *track;
+ uint8_t extradata[4];
+ uint8_t mid_frame;
+ uint32_t frame_read;
+ RCV_FRAME_HEADER_T frame;
+ VC_CONTAINER_INDEX_T *index; /* index of key frames */
+
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+static VC_CONTAINER_STATUS_T rcv_read_header(VC_CONTAINER_T *p_ctx)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ RCV_FILE_HEADER_T header;
+ uint8_t dummy[36];
+
+ if(PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) return VC_CONTAINER_ERROR_EOS;
+
+ header.num_frames = LI24(dummy);
+ header.constant_c5 = dummy[3];
+ header.constant_4 = LI32(dummy+4);
+
+ // extradata is just struct_c from the header
+ memcpy(module->extradata, dummy+8, 4);
+ module->track->format->extradata = module->extradata;
+ module->track->format->extradata_size = 4;
+
+ module->track->format->type->video.height = LI32(dummy+12);
+ module->track->format->type->video.width = LI32(dummy+16);
+
+ header.constant_c = LI32(dummy+20);
+ memcpy(header.struct_b, dummy+24, 8);
+ header.framerate = LI32(dummy+32);
+
+ if(header.constant_c5 != 0xc5 || header.constant_4 != 0x4 || header.constant_c != 0xc)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ if(header.framerate != 0 && header.framerate != 0xffffffffUL)
+ {
+ module->track->format->type->video.frame_rate_num = header.framerate;
+ module->track->format->type->video.frame_rate_den = 1;
+ }
+
+ // fill in general information
+ if(header.num_frames != (1<<24)-1 && header.framerate != 0 && header.framerate != 0xffffffffUL)
+ p_ctx->duration = ((int64_t) header.num_frames * 1000000LL) / (int64_t) header.framerate;
+
+ // we're happy that this is an rcv file
+ SKIP_BYTES(p_ctx, sizeof(dummy));
+
+ return STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************
+ * Utility function to seek to the keyframe nearest the given timestamp.
+ *
+ * @param p_ctx Pointer to the container context.
+ * @param timestamp The requested time. On success, this is updated with the time of the selected keyframe.
+ * @param later If true, the selected frame is the earliest keyframe with a time greater or equal to timestamp.
+ * If false, the selected frame is the latest keyframe with a time earlier or equal to timestamp.
+ * @return Status code.
+ */
+static VC_CONTAINER_STATUS_T rcv_seek_nearest_keyframe(VC_CONTAINER_T *p_ctx, int64_t *timestamp, int later)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ int64_t prev_keyframe_offset = sizeof(RCV_FILE_HEADER_T); /* set to very first frame */
+ int64_t prev_keyframe_timestamp = 0;
+ int use_prev_keyframe = !later;
+
+ if(use_prev_keyframe || (module->frame.timestamp * 1000LL > *timestamp))
+ {
+ /* A seek has been requested to an earlier keyframe, so rewind to the beginning
+ * of the stream since there's no information available on previous frames */
+ SEEK(p_ctx, sizeof(RCV_FILE_HEADER_T));
+ memset(&module->frame, 0, sizeof(RCV_FRAME_HEADER_T));
+ module->mid_frame = 0;
+ module->frame_read = 0;
+ }
+
+ if(module->mid_frame)
+ {
+ /* Seek back to the start of the current frame */
+ SEEK(p_ctx, STREAM_POSITION(p_ctx) - module->frame_read - sizeof(RCV_FILE_HEADER_T));
+ module->mid_frame = 0;
+ module->frame_read = 0;
+ }
+
+ while(1)
+ {
+ if(PEEK_BYTES(p_ctx, &module->frame, sizeof(RCV_FRAME_HEADER_T)) != sizeof(RCV_FRAME_HEADER_T))
+ {
+ status = VC_CONTAINER_ERROR_EOS;
+ break;
+ }
+
+ if(module->frame.keyframe)
+ {
+ if(module->index)
+ vc_container_index_add(module->index, module->frame.timestamp * 1000LL, STREAM_POSITION(p_ctx));
+
+ if((module->frame.timestamp * 1000LL) >= *timestamp)
+ {
+ if((module->frame.timestamp * 1000LL) == *timestamp)
+ use_prev_keyframe = 0;
+
+ *timestamp = module->frame.timestamp * 1000LL;
+
+ break;
+ }
+
+ prev_keyframe_offset = STREAM_POSITION(p_ctx);
+ prev_keyframe_timestamp = module->frame.timestamp * 1000LL;
+ }
+
+ SKIP_BYTES(p_ctx, module->frame.framesize + sizeof(RCV_FRAME_HEADER_T));
+ }
+
+ if(use_prev_keyframe)
+ {
+ *timestamp = prev_keyframe_timestamp;
+ status = SEEK(p_ctx, prev_keyframe_offset);
+ }
+
+ return status;
+}
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+*****************************************************************************/
+static VC_CONTAINER_STATUS_T rcv_reader_read( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *packet, uint32_t flags )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ unsigned int size;
+
+ if(!module->mid_frame)
+ {
+ /* Save the current position for updating the indexer */
+ int64_t position = STREAM_POSITION(p_ctx);
+
+ if(READ_BYTES(p_ctx, &module->frame, sizeof(RCV_FRAME_HEADER_T)) != sizeof(RCV_FRAME_HEADER_T))
+ return VC_CONTAINER_ERROR_EOS;
+ module->mid_frame = 1;
+ module->frame_read = 0;
+
+ if(module->index && module->frame.keyframe)
+ vc_container_index_add(module->index, (int64_t)module->frame.timestamp * 1000LL, position);
+ }
+
+ packet->size = module->frame.framesize;
+ packet->dts = packet->pts = module->frame.timestamp * 1000LL;
+ packet->track = 0;
+ packet->flags = 0;
+ if(module->frame_read == 0)
+ packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
+ if(module->frame.keyframe)
+ packet->flags |= VC_CONTAINER_PACKET_FLAG_KEYFRAME;
+
+ if(flags & VC_CONTAINER_READ_FLAG_SKIP)
+ {
+ size = SKIP_BYTES(p_ctx, module->frame.framesize - module->frame_read);
+ if((module->frame_read += size) == module->frame.framesize)
+ {
+ module->frame_read = 0;
+ module->mid_frame = 0;
+ }
+ return STREAM_STATUS(p_ctx);
+ }
+
+ if(flags & VC_CONTAINER_READ_FLAG_INFO)
+ return VC_CONTAINER_SUCCESS;
+
+ size = MIN(module->frame.framesize - module->frame_read, packet->buffer_size);
+ size = READ_BYTES(p_ctx, packet->data, size);
+ if((module->frame_read += size) == module->frame.framesize)
+ {
+ module->frame_read = 0;
+ module->mid_frame = 0;
+ packet->flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
+ }
+ packet->size = size;
+
+ return size ? VC_CONTAINER_SUCCESS : STREAM_STATUS(p_ctx);
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T rcv_reader_seek( VC_CONTAINER_T *p_ctx, int64_t *offset,
+ VC_CONTAINER_SEEK_MODE_T mode, VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ int past = 1;
+ int64_t position;
+ int64_t timestamp = *offset;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FAILED;
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_PARAM_UNUSED(mode);
+
+ if(module->index)
+ status = vc_container_index_get(module->index, flags & VC_CONTAINER_SEEK_FLAG_FORWARD, ×tamp, &position, &past);
+
+ if(status == VC_CONTAINER_SUCCESS && !past)
+ {
+ /* Indexed keyframe found */
+ module->frame_read = 0;
+ module->mid_frame = 0;
+ *offset = timestamp;
+ status = SEEK(p_ctx, position);
+ }
+ else
+ {
+ /* No indexed keyframe found, so seek through all frames */
+ status = rcv_seek_nearest_keyframe(p_ctx, offset, flags & VC_CONTAINER_SEEK_FLAG_FORWARD);
+ }
+
+ return status;
+}
+
+/*****************************************************************************/
+static VC_CONTAINER_STATUS_T rcv_reader_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ for(; p_ctx->tracks_num > 0; p_ctx->tracks_num--)
+ vc_container_free_track(p_ctx, p_ctx->tracks[p_ctx->tracks_num-1]);
+
+ if(module->index)
+ vc_container_index_free(module->index);
+
+ free(module);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/*****************************************************************************/
+VC_CONTAINER_STATUS_T rcv_reader_open( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = 0;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ uint8_t dummy[8];
+
+ /* Quick check for a valid file header */
+ if((PEEK_BYTES(p_ctx, dummy, sizeof(dummy)) != sizeof(dummy)) ||
+ dummy[3] != 0xc5 || LI32(dummy+4) != 0x4)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Allocate our context */
+ module = malloc(sizeof(*module));
+ if(!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks_num = 1;
+ p_ctx->tracks = &module->track;
+ p_ctx->tracks[0] = vc_container_allocate_track(p_ctx, 0);
+ if(!p_ctx->tracks[0]) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+ p_ctx->tracks[0]->format->es_type = VC_CONTAINER_ES_TYPE_VIDEO;
+ p_ctx->tracks[0]->format->codec = VC_CONTAINER_CODEC_WMV3;
+ p_ctx->tracks[0]->is_enabled = true;
+
+ if((status = rcv_read_header(p_ctx)) != VC_CONTAINER_SUCCESS) goto error;
+
+ LOG_DEBUG(p_ctx, "using rcv reader");
+
+ if(vc_container_index_create(&module->index, 512) == VC_CONTAINER_SUCCESS)
+ vc_container_index_add(module->index, 0LL, STREAM_POSITION(p_ctx));
+
+ if(STREAM_SEEKABLE(p_ctx))
+ p_ctx->capabilities |= VC_CONTAINER_CAPS_CAN_SEEK;
+
+ p_ctx->priv->pf_close = rcv_reader_close;
+ p_ctx->priv->pf_read = rcv_reader_read;
+ p_ctx->priv->pf_seek = rcv_reader_seek;
+ return VC_CONTAINER_SUCCESS;
+
+ error:
+ if(module) rcv_reader_close(p_ctx);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open rcv_reader_open
+#endif
diff --git a/gfx/include/userland/containers/rtp/CMakeLists.txt b/gfx/include/userland/containers/rtp/CMakeLists.txt
new file mode 100644
index 0000000000..e6ba3a95cb
--- /dev/null
+++ b/gfx/include/userland/containers/rtp/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+set(rtp_SRCS ${rtp_SRCS} rtp_reader.c)
+set(rtp_SRCS ${rtp_SRCS} rtp_h264.c)
+set(rtp_SRCS ${rtp_SRCS} rtp_mpeg4.c)
+set(rtp_SRCS ${rtp_SRCS} rtp_base64.c)
+add_library(reader_rtp ${LIBRARY_TYPE} ${rtp_SRCS})
+
+target_link_libraries(reader_rtp containers)
+
+install(TARGETS reader_rtp DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/rtp/rtp_base64.c b/gfx/include/userland/containers/rtp/rtp_base64.c
new file mode 100644
index 0000000000..f0e873fadb
--- /dev/null
+++ b/gfx/include/userland/containers/rtp/rtp_base64.c
@@ -0,0 +1,165 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "rtp_base64.h"
+
+/******************************************************************************
+Defines and constants.
+******************************************************************************/
+
+#define LOWEST_BASE64_CHAR '+'
+#define HIGHEST_BASE64_CHAR 'z'
+#define IN_BASE64_RANGE(C) ((C) >= LOWEST_BASE64_CHAR && (C) <= HIGHEST_BASE64_CHAR)
+
+/** Used as a marker in the lookup table to indicate an invalid Base64 character */
+#define INVALID 0xFF
+
+/* Reduced lookup table for translating a character to a 6-bit value. The
+ * table starts at the lowest Base64 character, '+' */
+uint8_t base64_decode_lookup[] = {
+ 62, INVALID, 62, INVALID, 63, /* '+' to '/' */
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, /* '0' to '9' */
+ INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, INVALID, /* ':' to '@' */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'A' to 'T' */
+ 20, 21, 22, 23, 24, 25, /* 'U' to 'Z' */
+ INVALID, INVALID, INVALID, INVALID, 63, INVALID, /* '[' to '`' */
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, /* 'a' to 'r' */
+ 44, 45, 46, 47, 48, 49, 50, 51 /* 's' to 'z' */
+};
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+/*****************************************************************************
+Functions exported as part of the Base64 API
+ *****************************************************************************/
+
+/*****************************************************************************/
+uint32_t rtp_base64_byte_length(const char *str, uint32_t str_len)
+{
+ uint32_t character_count = 0;
+ uint32_t ii;
+ char cc;
+
+ /* Scan through string until either a pad ('=') character or the end is
+ * reached. Ignore characters that are not part of the Base64 alphabet.
+ * Number of bytes should then be 3/4 of the character count. */
+
+ for (ii = 0; ii < str_len; ii++)
+ {
+ cc = *str++;
+ if (cc == '=')
+ break; /* Found a pad character: stop */
+
+ if (!IN_BASE64_RANGE(cc))
+ continue; /* Ignore invalid character */
+
+ if (base64_decode_lookup[cc - LOWEST_BASE64_CHAR] != INVALID)
+ character_count++;
+ }
+
+ return (character_count * 3) >> 2;
+}
+
+/*****************************************************************************/
+uint8_t *rtp_base64_decode(const char *str, uint32_t str_len, uint8_t *buffer, uint32_t buffer_len)
+{
+ uint32_t character_count = 0;
+ uint32_t value = 0;
+ uint32_t ii;
+ char cc;
+ uint8_t lookup;
+
+ /* Build up sets of four characters (ignoring invalid ones) to generate
+ * triplets of bytes, until either the end of the string or the pad ('=')
+ * characters are reached. */
+
+ for (ii = 0; ii < str_len; ii++)
+ {
+ cc = *str++;
+ if (cc == '=')
+ break; /* Found a pad character: stop */
+
+ if (!IN_BASE64_RANGE(cc))
+ continue; /* Ignore invalid character */
+
+ lookup = base64_decode_lookup[cc - LOWEST_BASE64_CHAR];
+ if (lookup == INVALID)
+ continue; /* Ignore invalid character */
+
+ value = (value << 6) | lookup;
+ character_count++;
+
+ if (character_count == 4)
+ {
+ if (buffer_len < 3)
+ return NULL; /* Not enough room in the output buffer */
+
+ *buffer++ = (uint8_t)(value >> 16);
+ *buffer++ = (uint8_t)(value >> 8);
+ *buffer++ = (uint8_t)(value );
+ buffer_len -= 3;
+
+ character_count = 0;
+ value = 0;
+ }
+ }
+
+ /* If there were extra characters on the end, these need to be handled to get
+ * the last one or two bytes. */
+
+ switch (character_count)
+ {
+ case 0: /* Nothing more to do, the final bytes were converted in the loop */
+ break;
+ case 2: /* One additional byte, padded with four zero bits */
+ if (!buffer_len)
+ return NULL;
+ *buffer++ = (uint8_t)(value >> 4);
+ break;
+ case 3: /* Two additional bytes, padded with two zero bits */
+ if (buffer_len < 2)
+ return NULL;
+ *buffer++ = (uint8_t)(value >> 10);
+ *buffer++ = (uint8_t)(value >> 2);
+ break;
+ default: /* This is an invalid Base64 encoding */
+ return NULL;
+ }
+
+ /* Return number of bytes written to the buffer */
+ return buffer;
+}
diff --git a/gfx/include/userland/containers/rtp/rtp_base64.h b/gfx/include/userland/containers/rtp/rtp_base64.h
new file mode 100644
index 0000000000..2cb6a76bbb
--- /dev/null
+++ b/gfx/include/userland/containers/rtp/rtp_base64.h
@@ -0,0 +1,49 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _RTP_BASE64_H_
+#define _RTP_BASE64_H_
+
+#include "containers/containers.h"
+
+/** Returns the number of bytes encoded by the given Base64 encoded string.
+ *
+ * \param str The Base64 encoded string.
+ * \param str_len The number of characters in the string.
+ * \return The number of bytes that can be decoded. */
+uint32_t rtp_base64_byte_length(const char *str, uint32_t str_len);
+
+/** Decodes a Base64 encoded string into a byte buffer.
+ *
+ * \param str The Base64 encoded string.
+ * \param str_len The number of characters in the string.
+ * \param buffer The buffer to receive the decoded output.
+ * \param buffer_len The maximum number of bytes to put in the buffer.
+ * \return Pointer to byte after the last one converted, or NULL on error. */
+uint8_t *rtp_base64_decode(const char *str, uint32_t str_len, uint8_t *buffer, uint32_t buffer_len);
+
+#endif /* _RTP_BASE64_H_ */
diff --git a/gfx/include/userland/containers/rtp/rtp_h264.c b/gfx/include/userland/containers/rtp/rtp_h264.c
new file mode 100644
index 0000000000..e7f0f8320e
--- /dev/null
+++ b/gfx/include/userland/containers/rtp/rtp_h264.c
@@ -0,0 +1,803 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+#include
+
+#include "containers/containers.h"
+
+#include "containers/core/containers_logging.h"
+#include "containers/core/containers_list.h"
+#include "containers/core/containers_bits.h"
+#include "rtp_priv.h"
+#include "rtp_base64.h"
+#include "rtp_h264.h"
+
+/******************************************************************************
+Defines and constants.
+******************************************************************************/
+
+/** H.264 payload flag bits */
+typedef enum
+{
+ H264F_NEXT_PACKET_IS_START = 0,
+ H264F_INSIDE_FRAGMENT,
+ H264F_OUTPUT_NAL_HEADER,
+} h264_flag_bit_t;
+
+/** Bit mask to extract F zero bit from NAL unit header */
+#define NAL_UNIT_FZERO_MASK 0x80
+/** Bit mask to extract NAL unit type from NAL unit header */
+#define NAL_UNIT_TYPE_MASK 0x1F
+
+/** NAL unit type codes */
+enum
+{
+ /* 0 unspecified */
+ NAL_UNIT_NON_IDR = 1,
+ NAL_UNIT_PARTITION_A = 2,
+ NAL_UNIT_PARTITION_B = 3,
+ NAL_UNIT_PARTITION_C = 4,
+ NAL_UNIT_IDR = 5,
+ NAL_UNIT_SEI = 6,
+ NAL_UNIT_SEQUENCE_PARAMETER_SET = 7,
+ NAL_UNIT_PICTURE_PARAMETER_SET = 8,
+ NAL_UNIT_ACCESS_UNIT_DELIMITER = 9,
+ NAL_UNIT_END_OF_SEQUENCE = 10,
+ NAL_UNIT_END_OF_STREAM = 11,
+ NAL_UNIT_FILLER = 12,
+ NAL_UNIT_EXT_SEQUENCE_PARAMETER_SET = 13,
+ NAL_UNIT_PREFIX = 14,
+ NAL_UNIT_SUBSET_SEQUENCE_PARAMETER_SET = 15,
+ /* 16 to 18 reserved */
+ NAL_UNIT_AUXILIARY = 19,
+ NAL_UNIT_EXTENSION = 20,
+ /* 21 to 23 reserved */
+ NAL_UNIT_STAP_A = 24,
+ NAL_UNIT_STAP_B = 25,
+ NAL_UNIT_MTAP16 = 26,
+ NAL_UNIT_MTAP24 = 27,
+ NAL_UNIT_FU_A = 28,
+ NAL_UNIT_FU_B = 29,
+ /* 30 to 31 unspecified */
+};
+
+/** Fragment unit header indicator bits */
+typedef enum
+{
+ FRAGMENT_UNIT_HEADER_RESERVED = 5,
+ FRAGMENT_UNIT_HEADER_END = 6,
+ FRAGMENT_UNIT_HEADER_START = 7,
+} fragment_unit_header_bit_t;
+
+#define MACROBLOCK_WIDTH 16
+#define MACROBLOCK_HEIGHT 16
+
+/** H.264 RTP timestamp clock rate */
+#define H264_TIMESTAMP_CLOCK 90000
+
+typedef enum
+{
+ CHROMA_FORMAT_MONO = 0,
+ CHROMA_FORMAT_YUV_420 = 1,
+ CHROMA_FORMAT_YUV_422 = 2,
+ CHROMA_FORMAT_YUV_444 = 3,
+ CHROMA_FORMAT_YUV_444_PLANAR = 4,
+ CHROMA_FORMAT_RGB = 5,
+} CHROMA_FORMAT_T;
+
+uint32_t chroma_sub_width[] = {
+ 1, 2, 2, 1, 1, 1
+};
+
+uint32_t chroma_sub_height[] = {
+ 1, 2, 1, 1, 1, 1
+};
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+
+typedef struct h264_payload_tag
+{
+ uint32_t nal_unit_size; /**< Number of NAL unit bytes left to write */
+ uint8_t flags; /**< H.264 payload flags */
+ uint8_t header_bytes_to_write; /**< Number of start code bytes left to write */
+ uint8_t nal_header; /**< Header for next NAL unit */
+} H264_PAYLOAD_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T h264_parameter_handler(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params);
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+/**************************************************************************//**
+ * Remove emulation prevention bytes from a buffer.
+ * These are 0x03 bytes inserted to prevent misinterprentation of a byte
+ * sequence in a buffer as a start code.
+ *
+ * @param sprop The buffer from which bytes are to be removed.
+ * @param sprop_size The number of bytes in the buffer.
+ * @return The new number of bytes in the buffer.
+ */
+static uint32_t h264_remove_emulation_prevention_bytes(uint8_t *sprop,
+ uint32_t sprop_size)
+{
+ uint32_t offset = 0;
+ uint8_t nal_unit_type = sprop[offset++];
+ uint32_t new_sprop_size = sprop_size;
+ uint8_t first_byte, second_byte;
+
+ nal_unit_type &= 0x1F; /* Just keep NAL unit type bits */
+
+ /* Certain NAL unit types need a byte triplet passed first */
+ if (nal_unit_type == NAL_UNIT_PREFIX || nal_unit_type == NAL_UNIT_EXTENSION)
+ offset += 3;
+
+ /* Make sure there is enough data for there to be a 0x00 0x00 0x03 sequence */
+ if (offset + 2 >= new_sprop_size)
+ return new_sprop_size;
+
+ /* Keep a rolling set of the last couple of bytes */
+ first_byte = sprop[offset++];
+ second_byte = sprop[offset++];
+
+ while (offset < new_sprop_size)
+ {
+ uint8_t next_byte = sprop[offset];
+
+ if (!first_byte && !second_byte && next_byte == 0x03)
+ {
+ /* Remove the emulation prevention byte (0x03) */
+ new_sprop_size--;
+ if (offset == new_sprop_size) /* No more data to check */
+ break;
+ memmove(&sprop[offset], &sprop[offset + 1], new_sprop_size - offset);
+ next_byte = sprop[offset];
+ } else
+ offset++;
+
+ first_byte = second_byte;
+ second_byte = next_byte;
+ }
+
+ return new_sprop_size;
+}
+
+/**************************************************************************//**
+ * Skip a scaling list in a bit stream.
+ *
+ * @param p_ctx The container context.
+ * @param sprop The bit stream containing the scaling list.
+ * @param size_of_scaling_list The size of the scaling list.
+ */
+static void h264_skip_scaling_list(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_BITS_T *sprop,
+ uint32_t size_of_scaling_list)
+{
+ uint32_t last_scale = 8;
+ uint32_t next_scale = 8;
+ int32_t delta_scale;
+ uint32_t jj;
+
+ /* Algorithm taken from H.264 section 7.3.2.1.1.1 */
+ for (jj = 0; jj < size_of_scaling_list; jj++)
+ {
+ if (next_scale)
+ {
+ delta_scale = BITS_READ_S32_EXP(p_ctx, sprop, "delta_scale");
+ next_scale = (last_scale + delta_scale + 256) & 0xFF;
+
+ if (next_scale)
+ last_scale = next_scale;
+ }
+ }
+}
+
+/**************************************************************************//**
+ * Get the chroma format from the bit stream.
+ *
+ * @param p_ctx The container context.
+ * @param sprop The bit stream containing the scaling list.
+ * @return The chroma format index.
+ */
+static uint32_t h264_get_chroma_format(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_BITS_T *sprop)
+{
+ uint32_t chroma_format_idc;
+
+ chroma_format_idc = BITS_READ_U32_EXP(p_ctx, sprop, "chroma_format_idc");
+ if (chroma_format_idc == 3 && BITS_READ_U32(p_ctx, sprop, 1, "separate_colour_plane_flag"))
+ chroma_format_idc = CHROMA_FORMAT_YUV_444_PLANAR;
+
+ BITS_SKIP_EXP(p_ctx, sprop, "bit_depth_luma_minus8");
+ BITS_SKIP_EXP(p_ctx, sprop, "bit_depth_chroma_minus8");
+ BITS_SKIP(p_ctx, sprop, 1, "qpprime_y_zero_transform_bypass_flag");
+
+ if (BITS_READ_U32(p_ctx, sprop, 1, "seq_scaling_matrix_present_flag"))
+ {
+ uint32_t scaling_lists = (chroma_format_idc == 3) ? 12 : 8;
+ uint32_t ii;
+
+ for (ii = 0; ii < scaling_lists; ii++)
+ {
+ if (BITS_READ_U32(p_ctx, sprop, 1, "seq_scaling_list_present_flag"))
+ h264_skip_scaling_list(p_ctx, sprop, (ii < 6) ? 16 : 64);
+ }
+ }
+
+ return chroma_format_idc;
+}
+
+/**************************************************************************//**
+ * Decode an H.264 sequence parameter set and update track information.
+ *
+ * @param p_ctx The RTP container context.
+ * @param track The track to be updated.
+ * @param sprop The bit stream containing the sequence parameter set.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T h264_decode_sequence_parameter_set(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ VC_CONTAINER_BITS_T *sprop)
+{
+ VC_CONTAINER_VIDEO_FORMAT_T *video = &track->format->type->video;
+ uint32_t pic_order_cnt_type, chroma_format_idc;
+ uint32_t pic_width_in_mbs_minus1, pic_height_in_map_units_minus1, frame_mbs_only_flag;
+ uint32_t frame_crop_left_offset, frame_crop_right_offset, frame_crop_top_offset, frame_crop_bottom_offset;
+ uint8_t profile_idc;
+
+ /* This structure is defined by H.264 section 7.3.2.1.1 */
+ profile_idc = BITS_READ_U8(p_ctx, sprop, 8, "profile_idc");
+ BITS_SKIP(p_ctx, sprop, 16, "Rest of profile_level_id");
+
+ BITS_READ_U32_EXP(p_ctx, sprop, "seq_parameter_set_id");
+
+ chroma_format_idc = CHROMA_FORMAT_RGB;
+ if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 ||
+ profile_idc == 244 || profile_idc == 44 || profile_idc == 83 ||
+ profile_idc == 86 || profile_idc == 118 || profile_idc == 128)
+ {
+ chroma_format_idc = h264_get_chroma_format(p_ctx, sprop);
+ if (chroma_format_idc > CHROMA_FORMAT_YUV_444_PLANAR)
+ goto error;
+ }
+
+ BITS_SKIP_EXP(p_ctx, sprop, "log2_max_frame_num_minus4");
+ pic_order_cnt_type = BITS_READ_U32_EXP(p_ctx, sprop, "pic_order_cnt_type");
+ if (pic_order_cnt_type == 0)
+ {
+ BITS_SKIP_EXP(p_ctx, sprop, "log2_max_pic_order_cnt_lsb_minus4");
+ }
+ else if (pic_order_cnt_type == 1)
+ {
+ uint32_t num_ref_frames_in_pic_order_cnt_cycle;
+ uint32_t ii;
+
+ BITS_SKIP(p_ctx, sprop, 1, "delta_pic_order_always_zero_flag");
+ BITS_SKIP_EXP(p_ctx, sprop, "offset_for_non_ref_pic");
+ BITS_SKIP_EXP(p_ctx, sprop, "offset_for_top_to_bottom_field");
+ num_ref_frames_in_pic_order_cnt_cycle = BITS_READ_U32_EXP(p_ctx, sprop, "num_ref_frames_in_pic_order_cnt_cycle");
+
+ for (ii = 0; ii < num_ref_frames_in_pic_order_cnt_cycle; ii++)
+ BITS_SKIP_EXP(p_ctx, sprop, "offset_for_ref_frame");
+ }
+
+ BITS_SKIP_EXP(p_ctx, sprop, "max_num_ref_frames");
+ BITS_SKIP(p_ctx, sprop, 1, "gaps_in_frame_num_value_allowed_flag");
+
+ pic_width_in_mbs_minus1 = BITS_READ_U32_EXP(p_ctx, sprop, "pic_width_in_mbs_minus1");
+ pic_height_in_map_units_minus1 = BITS_READ_U32_EXP(p_ctx, sprop, "pic_height_in_map_units_minus1");
+ frame_mbs_only_flag = BITS_READ_U32(p_ctx, sprop, 1, "frame_mbs_only_flag");
+
+ /* Can now set the overall width and height in pixels */
+ video->width = (pic_width_in_mbs_minus1 + 1) * MACROBLOCK_WIDTH;
+ video->height = (2 - frame_mbs_only_flag) * (pic_height_in_map_units_minus1 + 1) * MACROBLOCK_HEIGHT;
+
+ if (!frame_mbs_only_flag)
+ BITS_SKIP(p_ctx, sprop, 1, "mb_adaptive_frame_field_flag");
+ BITS_SKIP(p_ctx, sprop, 1, "direct_8x8_inference_flag");
+
+ if (BITS_READ_U32(p_ctx, sprop, 1, "frame_cropping_flag"))
+ {
+ /* Visible area is restricted */
+ frame_crop_left_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_left_offset");
+ frame_crop_right_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_right_offset");
+ frame_crop_top_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_top_offset");
+ frame_crop_bottom_offset = BITS_READ_U32_EXP(p_ctx, sprop, "frame_crop_bottom_offset");
+
+ /* Need to adjust offsets for 4:2:0 and 4:2:2 chroma formats and field/frame flag */
+ frame_crop_left_offset *= chroma_sub_width[chroma_format_idc];
+ frame_crop_right_offset *= chroma_sub_width[chroma_format_idc];
+ frame_crop_top_offset *= chroma_sub_height[chroma_format_idc] * (2 - frame_mbs_only_flag);
+ frame_crop_bottom_offset *= chroma_sub_height[chroma_format_idc] * (2 - frame_mbs_only_flag);
+
+ if ((frame_crop_left_offset + frame_crop_right_offset) >= video->width ||
+ (frame_crop_top_offset + frame_crop_bottom_offset) >= video->height)
+ {
+ LOG_ERROR(p_ctx, "H.264: frame crop offsets (%u, %u, %u, %u) larger than frame (%u, %u)",
+ frame_crop_left_offset, frame_crop_right_offset, frame_crop_top_offset,
+ frame_crop_bottom_offset, video->width, video->height);
+ goto error;
+ }
+
+ video->x_offset = frame_crop_left_offset;
+ video->y_offset = frame_crop_top_offset;
+ video->visible_width = video->width - frame_crop_left_offset - frame_crop_right_offset;
+ video->visible_height = video->height - frame_crop_top_offset - frame_crop_bottom_offset;
+ } else {
+ video->visible_width = video->width;
+ video->visible_height = video->height;
+ }
+
+ /* vui_parameters may follow, but these will not be decoded */
+
+ if (!BITS_VALID(p_ctx, sprop))
+ goto error;
+
+ return VC_CONTAINER_SUCCESS;
+
+error:
+ LOG_ERROR(p_ctx, "H.264: sequence_parameter_set failed to decode");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+}
+
+/**************************************************************************//**
+ * Decode an H.264 sprop and update track information.
+ *
+ * @param p_ctx The RTP container context.
+ * @param track The track to be updated.
+ * @param sprop The bit stream containing the sprop.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T h264_decode_sprop(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ VC_CONTAINER_BITS_T *sprop)
+{
+ switch (BITS_READ_U32(p_ctx, sprop, 8, "nal_unit_header") & NAL_UNIT_TYPE_MASK)
+ {
+ case NAL_UNIT_SEQUENCE_PARAMETER_SET:
+ return h264_decode_sequence_parameter_set(p_ctx, track, sprop);
+ case NAL_UNIT_PICTURE_PARAMETER_SET:
+ /* Not handled, but valid */
+ return VC_CONTAINER_SUCCESS;
+ default:
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+}
+
+/**************************************************************************//**
+ * Decode the sprop parameter sets URI parameter and update track information.
+ *
+ * @param p_ctx The RTP container context.
+ * @param track The track to be updated.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T h264_get_sprop_parameter_sets(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *params)
+{
+ VC_CONTAINER_STATUS_T status;
+ PARAMETER_T param;
+ size_t str_len;
+ uint32_t extradata_size = 0;
+ uint8_t *sprop;
+ const char *set;
+ const char *comma;
+
+ /* Get the value of sprop-parameter-sets, base64 decode the (comma separated)
+ * sets, store all of them in track->priv->extradata and also decode to
+ * validate and fill in video format info. */
+
+ param.name = "sprop-parameter-sets";
+ if (!vc_containers_list_find_entry(params, ¶m) || !param.value)
+ {
+ LOG_ERROR(p_ctx, "H.264: sprop-parameter-sets is required, but not found");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ /* First pass, calculate total size of buffer needed */
+ set = param.value;
+ do {
+ comma = strchr(set, ',');
+ str_len = comma ? (size_t)(comma - set) : strlen(set);
+ /* Allow space for the NAL unit and a start code */
+ extradata_size += rtp_base64_byte_length(set, str_len) + 4;
+ set = comma + 1;
+ } while (comma);
+
+ if (!extradata_size)
+ {
+ LOG_ERROR(p_ctx, "H.264: sprop-parameter-sets doesn't contain useful data");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ status = vc_container_track_allocate_extradata(p_ctx, track, extradata_size);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ track->format->extradata_size = extradata_size;
+ sprop = track->priv->extradata;
+
+ /* Now decode the data into the buffer, and validate / use it to fill in format */
+ set = param.value;
+ do {
+ uint8_t *next_sprop;
+ uint32_t sprop_size;
+ VC_CONTAINER_BITS_T sprop_stream;
+
+ comma = strchr(set, ',');
+ str_len = comma ? (size_t)(comma - set) : strlen(set);
+
+ /* Insert a start code (0x00000001 in network order) */
+ *sprop++ = 0x00; *sprop++ = 0x00; *sprop++ = 0x00; *sprop++ = 0x01;
+ extradata_size -= 4;
+
+ next_sprop = rtp_base64_decode(set, str_len, sprop, extradata_size);
+ if (!next_sprop)
+ {
+ LOG_ERROR(p_ctx, "H.264: sprop-parameter-sets failed to decode");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ sprop_size = next_sprop - sprop;
+ if (sprop_size)
+ {
+ uint32_t new_sprop_size;
+
+ /* Need to remove emulation prevention bytes before decoding */
+ new_sprop_size = h264_remove_emulation_prevention_bytes(sprop, sprop_size);
+
+ BITS_INIT(p_ctx, &sprop_stream, sprop, new_sprop_size);
+ status = h264_decode_sprop(p_ctx, track, &sprop_stream);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ /* If necessary, decode sprop again, to put back the emulation prevention bytes */
+ if (new_sprop_size != sprop_size)
+ rtp_base64_decode(set, str_len, sprop, sprop_size);
+
+ extradata_size -= sprop_size;
+ sprop = next_sprop;
+ }
+
+ set = comma + 1;
+ } while (comma);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Check URI parameter list for unsupported features.
+ *
+ * @param p_ctx The RTP container context.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T h264_check_unsupported_features(VC_CONTAINER_T *p_ctx,
+ const VC_CONTAINERS_LIST_T *params)
+{
+ uint32_t u32_unused;
+
+ /* Limitation: interleaving not yet supported */
+ if (rtp_get_parameter_u32(params, "sprop-interleaving-depth", &u32_unused) ||
+ rtp_get_parameter_u32(params, "sprop-deint-buf-req", &u32_unused) ||
+ rtp_get_parameter_u32(params, "sprop-init-buf-time", &u32_unused) ||
+ rtp_get_parameter_u32(params, "sprop-max-don-diff", &u32_unused))
+ {
+ LOG_ERROR(p_ctx, "H.264: Interleaved packetization is not supported");
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Get and check the packetization mode URI parameter.
+ *
+ * @param p_ctx The RTP container context.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T h264_get_packetization_mode(VC_CONTAINER_T *p_ctx,
+ const VC_CONTAINERS_LIST_T *params)
+{
+ uint32_t packetization_mode;
+
+ if (rtp_get_parameter_u32(params, "packetization-mode", &packetization_mode))
+ {
+ /* Only modes 0 and 1 are supported, no interleaving */
+ if (packetization_mode > 1)
+ {
+ LOG_ERROR(p_ctx, "H.264: Unsupported packetization mode: %u", packetization_mode);
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Initialise payload bit stream for a new RTP packet.
+ *
+ * @param p_ctx The RTP container context.
+ * @param t_module The track module with the new RTP packet.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T h264_new_rtp_packet(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_MODULE_T *t_module)
+{
+ VC_CONTAINER_BITS_T *payload = &t_module->payload;
+ H264_PAYLOAD_T *extra = (H264_PAYLOAD_T *)t_module->extra;
+ uint8_t unit_header;
+ uint8_t fragment_header;
+
+ /* Read the NAL unit type and process as necessary */
+ unit_header = BITS_READ_U8(p_ctx, payload, 8, "nal_unit_header");
+
+ /* When the top bit is set, the NAL unit is invalid */
+ if (unit_header & NAL_UNIT_FZERO_MASK)
+ {
+ LOG_DEBUG(p_ctx, "H.264: Invalid NAL unit (top bit of header set)");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ /* In most cases, a new packet means a new NAL unit, which will need a start code and the header */
+ extra->header_bytes_to_write = 5;
+ extra->nal_header = unit_header;
+ extra->nal_unit_size = BITS_BYTES_AVAILABLE(p_ctx, payload);
+
+ switch (unit_header & NAL_UNIT_TYPE_MASK)
+ {
+ case NAL_UNIT_STAP_A:
+ /* Single Time Aggregation Packet A */
+ CLEAR_BIT(extra->flags, H264F_INSIDE_FRAGMENT);
+ /* Trigger reading NAL unit length and header */
+ extra->nal_unit_size = 0;
+ break;
+
+ case NAL_UNIT_FU_A:
+ /* Fragementation Unit A */
+ fragment_header = BITS_READ_U8(p_ctx, payload, 8, "fragment_header");
+ extra->nal_unit_size--;
+
+ if (BIT_IS_CLEAR(fragment_header, FRAGMENT_UNIT_HEADER_START) ||
+ BIT_IS_SET(extra->flags, H264F_INSIDE_FRAGMENT))
+ {
+ /* This is a continuation packet, prevent start code and header from being output */
+ extra->header_bytes_to_write = 0;
+
+ /* If this is the end of a fragment, the next FU will be a new one */
+ if (BIT_IS_SET(fragment_header, FRAGMENT_UNIT_HEADER_END))
+ CLEAR_BIT(extra->flags, H264F_INSIDE_FRAGMENT);
+ } else {
+ /* Start of a new fragment. */
+ SET_BIT(extra->flags, H264F_INSIDE_FRAGMENT);
+
+ /* Merge type from fragment header and the rest from NAL unit header to form real NAL unit header */
+ fragment_header &= NAL_UNIT_TYPE_MASK;
+ fragment_header |= (unit_header & ~NAL_UNIT_TYPE_MASK);
+ extra->nal_header = fragment_header;
+ }
+ break;
+
+ case NAL_UNIT_STAP_B:
+ case NAL_UNIT_MTAP16:
+ case NAL_UNIT_MTAP24:
+ case NAL_UNIT_FU_B:
+ LOG_ERROR(p_ctx, "H.264: Unsupported RTP NAL unit type: %u", unit_header & NAL_UNIT_TYPE_MASK);
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ default:
+ /* Single NAL unit case */
+ CLEAR_BIT(extra->flags, H264F_INSIDE_FRAGMENT);
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * H.264 payload handler.
+ * Extracts/skips data from the payload according to the NAL unit headers.
+ *
+ * @param p_ctx The RTP container context.
+ * @param track The track being read.
+ * @param p_packet The container packet information, or NULL.
+ * @param flags The container read flags.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T h264_payload_handler(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ VC_CONTAINER_PACKET_T *p_packet,
+ uint32_t flags)
+{
+ VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module;
+ VC_CONTAINER_BITS_T *payload = &t_module->payload;
+ H264_PAYLOAD_T *extra = (H264_PAYLOAD_T *)t_module->extra;
+ uint32_t packet_flags = 0;
+ uint8_t header_bytes_to_write;
+ uint32_t size, offset;
+ uint8_t *data_ptr;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ bool last_nal_unit_in_packet = false;
+
+ if (BIT_IS_SET(t_module->flags, TRACK_NEW_PACKET))
+ {
+ status = h264_new_rtp_packet(p_ctx, t_module);
+ if (status != VC_CONTAINER_SUCCESS)
+ return status;
+ }
+
+ if (BIT_IS_SET(extra->flags, H264F_NEXT_PACKET_IS_START))
+ {
+ packet_flags |= VC_CONTAINER_PACKET_FLAG_FRAME_START;
+
+ if (!(flags & VC_CONTAINER_READ_FLAG_INFO))
+ CLEAR_BIT(extra->flags, H264F_NEXT_PACKET_IS_START);
+ }
+
+ if (!extra->nal_unit_size && BITS_BYTES_AVAILABLE(p_ctx, payload))
+ {
+ uint32_t stap_unit_header;
+
+ /* STAP-A packet: read NAL unit size and header from payload */
+ stap_unit_header = BITS_READ_U32(p_ctx, payload, 24, "STAP unit header");
+ extra->nal_unit_size = stap_unit_header >> 8;
+ if (extra->nal_unit_size > BITS_BYTES_AVAILABLE(p_ctx, payload))
+ {
+ LOG_ERROR(p_ctx, "H.264: STAP-A NAL unit size bigger than payload");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+ extra->header_bytes_to_write = 5;
+ extra->nal_header = (uint8_t)stap_unit_header;
+ }
+
+ header_bytes_to_write = extra->header_bytes_to_write;
+ size = extra->nal_unit_size + header_bytes_to_write;
+
+ if (p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP))
+ {
+ if (flags & VC_CONTAINER_READ_FLAG_INFO)
+ {
+ /* In order to set the frame end flag correctly, need to work out if this
+ * is the only NAL unit or last in an aggregated packet */
+ last_nal_unit_in_packet = (extra->nal_unit_size == BITS_BYTES_AVAILABLE(p_ctx, payload));
+ } else {
+ offset = 0;
+ data_ptr = p_packet->data;
+
+ if (size > p_packet->buffer_size)
+ {
+ /* Buffer not big enough */
+ size = p_packet->buffer_size;
+ }
+
+ /* Insert start code and header into the data stream */
+ while (offset < size && header_bytes_to_write)
+ {
+ uint8_t header_byte;
+
+ switch (header_bytes_to_write)
+ {
+ case 2: header_byte = 0x01; break;
+ case 1: header_byte = extra->nal_header; break;
+ default: header_byte = 0x00;
+ }
+ data_ptr[offset++] = header_byte;
+ header_bytes_to_write--;
+ }
+ extra->header_bytes_to_write = header_bytes_to_write;
+
+ if (offset < size)
+ {
+ BITS_COPY_BYTES(p_ctx, payload, size - offset, data_ptr + offset, "Packet data");
+ extra->nal_unit_size -= (size - offset);
+ }
+
+ /* If we've read the final bytes of the packet, this must be the last (or only)
+ * NAL unit in it */
+ last_nal_unit_in_packet = !BITS_BYTES_AVAILABLE(p_ctx, payload);
+ }
+ p_packet->size = size;
+ } else {
+ extra->header_bytes_to_write = 0;
+ BITS_SKIP_BYTES(p_ctx, payload, extra->nal_unit_size, "Packet data");
+ last_nal_unit_in_packet = !BITS_BYTES_AVAILABLE(p_ctx, payload);
+ extra->nal_unit_size = 0;
+ }
+
+ /* The marker bit on an RTP packet indicates the frame ends at the end of packet */
+ if (last_nal_unit_in_packet && BIT_IS_SET(t_module->flags, TRACK_HAS_MARKER))
+ {
+ packet_flags |= VC_CONTAINER_PACKET_FLAG_FRAME_END;
+
+ /* If this was the last packet of a frame, the next one must be the start */
+ if (!(flags & VC_CONTAINER_READ_FLAG_INFO))
+ SET_BIT(extra->flags, H264F_NEXT_PACKET_IS_START);
+ }
+
+ if (p_packet)
+ p_packet->flags = packet_flags;
+
+ return status;
+}
+
+/*****************************************************************************
+Functions exported as part of the RTP parameter handler API
+ *****************************************************************************/
+
+/**************************************************************************//**
+ * H.264 parameter handler.
+ * Parses the URI parameters to set up the track for an H.264 stream.
+ *
+ * @param p_ctx The reader context.
+ * @param track The track to be updated.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+VC_CONTAINER_STATUS_T h264_parameter_handler(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *params)
+{
+ H264_PAYLOAD_T *extra;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(params);
+
+ /* See RFC3984, section 8.1, for parameter names and details. */
+ extra = (H264_PAYLOAD_T *)malloc(sizeof(H264_PAYLOAD_T));
+ if (!extra)
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ track->priv->module->extra = extra;
+ memset(extra, 0, sizeof(H264_PAYLOAD_T));
+
+ /* Mandatory parameters */
+ status = h264_get_sprop_parameter_sets(p_ctx, track, params);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ /* Unsupported parameters */
+ status = h264_check_unsupported_features(p_ctx, params);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ /* Optional parameters */
+ status = h264_get_packetization_mode(p_ctx, params);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ track->priv->module->payload_handler = h264_payload_handler;
+ SET_BIT(extra->flags, H264F_NEXT_PACKET_IS_START);
+
+ track->format->flags |= VC_CONTAINER_ES_FORMAT_FLAG_FRAMED;
+ track->priv->module->timestamp_clock = H264_TIMESTAMP_CLOCK;
+
+ return status;
+}
+
diff --git a/gfx/include/userland/containers/rtp/rtp_h264.h b/gfx/include/userland/containers/rtp/rtp_h264.h
new file mode 100644
index 0000000000..ffd51da4d4
--- /dev/null
+++ b/gfx/include/userland/containers/rtp/rtp_h264.h
@@ -0,0 +1,42 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _RTP_H264_H_
+#define _RTP_H264_H_
+
+#include "containers/containers.h"
+#include "containers/core/containers_list.h"
+
+/** H.264 parameter handler
+ *
+ * \param p_ctx Container context.
+ * \param track Track data.
+ * \param params Parameter list.
+ * \return Status of decoding the H.264 parameters. */
+VC_CONTAINER_STATUS_T h264_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params);
+
+#endif /* _RTP_H264_H_ */
diff --git a/gfx/include/userland/containers/rtp/rtp_mpeg4.c b/gfx/include/userland/containers/rtp/rtp_mpeg4.c
new file mode 100644
index 0000000000..6a968e4546
--- /dev/null
+++ b/gfx/include/userland/containers/rtp/rtp_mpeg4.c
@@ -0,0 +1,788 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+#include
+
+#include "containers/containers.h"
+
+#include "containers/core/containers_logging.h"
+#include "containers/core/containers_bits.h"
+#include "containers/core/containers_list.h"
+#include "rtp_priv.h"
+#include "rtp_mpeg4.h"
+
+#ifdef _DEBUG
+#define RTP_DEBUG 1
+#endif
+
+/******************************************************************************
+Defines and constants.
+******************************************************************************/
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+
+/** MPEG-4 stream types, ISO/IEC 14496-1:2010 Table 6 */
+typedef enum
+{
+ MPEG4_OBJECT_DESCRIPTOR_STREAM = 1,
+ MPEG4_CLOCK_REFERENCE_STREAM = 2,
+ MPEG4_SCENE_DESCRIPTION_STREAM = 3,
+ MPEG4_VISUAL_STREAM = 4,
+ MPEG4_AUDIO_STREAM = 5,
+ MPEG4_MPEG7_STREAM = 6,
+ MPEG4_IPMP_STREAM = 7,
+ MPEG4_OBJECT_CONTENT_INFO_STREAM = 8,
+ MPEG4_MPEGJ_STREAM = 9,
+ MPEG4_INTERACTION_STREAM = 10,
+ MPEG4_IPMP_TOOL_STREAM = 11,
+} mp4_stream_type_t;
+
+/** MPEG-4 audio object types, ISO/IEC 14496-3:2009 Table 1.17 */
+typedef enum
+{
+ MPEG4A_AAC_MAIN = 1,
+ MPEG4A_AAC_LC = 2,
+ MPEG4A_AAC_SSR = 3,
+ MPEG4A_AAC_LTP = 4,
+ MPEG4A_SBR = 5,
+ MPEG4A_AAC_SCALABLE = 6,
+ MPEG4A_TWIN_VQ = 7,
+ MPEG4A_CELP = 8,
+ MPEG4A_HVXC = 9,
+ MPEG4A_TTSI = 12,
+ MPEG4A_MAIN_SYNTHETIC = 13,
+ MPEG4A_WAVETABLE_SYNTHESIS = 14,
+ MPEG4A_GENERAL_MIDI = 15,
+ MPEG4A_ALGORITHMIC_SYNTHESIS = 16,
+ MPEG4A_ER_AAC_LC = 17,
+ MPEG4A_ER_AAC_LTP = 19,
+ MPEG4A_ER_AAC_SCALABLE = 20,
+ MPEG4A_ER_TWIN_VQ = 21,
+ MPEG4A_ER_BSAC = 22,
+ MPEG4A_ER_AAC_LD = 23,
+ MPEG4A_ER_CELP = 24,
+ MPEG4A_ER_HVXC = 25,
+ MPEG4A_ER_HILN = 26,
+ MPEG4A_ER_PARAMETERIC = 27,
+ MPEG4A_SSC = 28,
+ MPEG4A_PS = 29,
+ MPEG4A_MPEG_SURROUND = 30,
+ MPEG4A_LAYER_1 = 32,
+ MPEG4A_LAYER_2 = 33,
+ MPEG4A_LAYER_3 = 34,
+ MPEG4A_DST = 35,
+ MPEG4A_ALS = 36,
+ MPEG4A_SLS = 37,
+ MPEG4A_SLS_NON_CORE = 38,
+ MPEG4A_ER_AAC_ELD = 39,
+ MPEG4A_SMR_SIMPLE = 40,
+ MPEG4A_SMR_MAIN = 41,
+} mp4_audio_object_type_t;
+
+/** RTP MPEG-4 modes */
+typedef enum
+{
+ MP4_GENERIC_MODE = 0,
+ MP4_CELP_CBR_MODE,
+ MP4_CELP_VBR_MODE,
+ MP4_AAC_LBR_MODE,
+ MP4_AAC_HBR_MODE
+} mp4_mode_t;
+
+typedef struct mp4_mode_detail_tag
+{
+ const char *name;
+ mp4_mode_t mode;
+} MP4_MODE_ENTRY_T;
+
+/* RTP MPEG-4 mode look-up table.
+ * Note: case-insensitive sort by name */
+static MP4_MODE_ENTRY_T mp4_mode_array[] = {
+ { "aac-hbr", MP4_AAC_HBR_MODE },
+ { "aac-lbr", MP4_AAC_LBR_MODE },
+ { "celp-cbr", MP4_CELP_CBR_MODE },
+ { "celp-vbr", MP4_CELP_VBR_MODE },
+ { "generic", MP4_GENERIC_MODE },
+};
+
+static int mp4_mode_comparator(const MP4_MODE_ENTRY_T *a, const MP4_MODE_ENTRY_T *b);
+
+VC_CONTAINERS_STATIC_LIST(mp4_mode_lookup, mp4_mode_array, mp4_mode_comparator);
+
+typedef struct au_info_tag
+{
+ uint32_t available;
+ uint32_t index;
+ int32_t cts_delta;
+ int32_t dts_delta;
+} AU_INFO_T;
+
+typedef struct mp4_payload_tag
+{
+ mp4_stream_type_t stream_type;
+ uint32_t profile_level_id;
+ mp4_mode_t mode;
+ uint32_t size_length;
+ uint32_t index_length;
+ uint32_t index_delta_length;
+ uint32_t cts_delta_length;
+ uint32_t dts_delta_length;
+ uint32_t object_type;
+ uint32_t constant_size;
+ uint32_t constant_duration;
+ uint32_t auxiliary_length;
+ VC_CONTAINER_BITS_T au_headers;
+ AU_INFO_T au_info;
+} MP4_PAYLOAD_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T mp4_parameter_handler(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params);
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+/**************************************************************************//**
+ * Convert a hexadecimal character to a value between 0 and 15.
+ * Upper and lower case characters are supported. An invalid chacter return zero.
+ *
+ * @param hex The character to convert.
+ * @return The value of the character.
+ */
+static uint8_t hex_to_nybble(char hex)
+{
+ if (hex >= '0' && hex <= '9')
+ return hex - '0';
+ if (hex >= 'A' && hex <= 'F')
+ return hex - 'A' + 10;
+ if (hex >= 'a' && hex <= 'f')
+ return hex - 'a' + 10;
+ return 0; /* Illegal character (not hex) */
+}
+
+/**************************************************************************//**
+ * Convert a sequence of hexadecimal characters to consecutive entries in a
+ * byte array.
+ * The string must contain at least twice as many characters as the number of
+ * bytes to convert.
+ *
+ * @param hex The hexadecimal string.
+ * @param buffer The buffer into which bytes are to be stored.
+ * @param bytes_to_convert The number of bytes in the array to be filled.
+ */
+static void hex_to_byte_buffer(const char *hex,
+ uint8_t *buffer,
+ uint32_t bytes_to_convert)
+{
+ uint8_t value;
+
+ while (bytes_to_convert--)
+ {
+ value = hex_to_nybble(*hex++) << 4;
+ value |= hex_to_nybble(*hex++);
+ *buffer++ = value;
+ }
+}
+
+/**************************************************************************//**
+ * Retrieves and checks the stream type in the URI parameters.
+ *
+ * @param p_ctx The RTP container context.
+ * @param track The track being constructed.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T mp4_get_stream_type(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *params)
+{
+ MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra;
+ uint32_t stream_type;
+ VC_CONTAINER_ES_TYPE_T expected_es_type;
+
+ if (!rtp_get_parameter_u32(params, "streamType", &stream_type))
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ switch (stream_type)
+ {
+ case MPEG4_AUDIO_STREAM:
+ extra->stream_type = MPEG4_AUDIO_STREAM;
+ expected_es_type = VC_CONTAINER_ES_TYPE_AUDIO;
+ break;
+ default:
+ LOG_ERROR(p_ctx, "Unsupported MPEG-4 stream type: %u", stream_type);
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ if (track->format->es_type != expected_es_type)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Decode and store audio configuration information from an MP4 audio
+ * configuration bit stream.
+ *
+ * @param p_ctx The RTP container context.
+ * @param track The track being constructed.
+ * @param bit_stream The bit stream containing the audio configuration.
+ * @return True if the configuration was decoded successfully, false otherwise.
+ */
+static bool mp4_decode_audio_config(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ VC_CONTAINER_BITS_T *bit_stream)
+{
+ static uint32_t mp4_audio_sample_rate[] =
+ { 96000, 88200, 64000, 48000, 44100, 32000, 24000,
+ 22050, 16000, 12000, 11025, 8000, 7350, 0, 0 };
+
+ VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio;
+ uint32_t audio_object_type;
+ uint32_t sampling_frequency_index;
+ uint32_t channel_configuration;
+
+ audio_object_type = BITS_READ_U32(p_ctx, bit_stream, 5, "audioObjectType");
+ if (audio_object_type == 31)
+ audio_object_type = BITS_READ_U32(p_ctx, bit_stream, 6, "audioObjectTypeExt") + 32;
+
+ sampling_frequency_index = BITS_READ_U32(p_ctx, bit_stream, 4, "samplingFrequencyIndex");
+ if (sampling_frequency_index == 0xF)
+ audio->sample_rate = BITS_READ_U32(p_ctx, bit_stream, 24, "samplingFrequency");
+ else
+ audio->sample_rate = mp4_audio_sample_rate[sampling_frequency_index];
+ if (!audio->sample_rate) return false;
+
+ track->priv->module->timestamp_clock = audio->sample_rate;
+
+ channel_configuration = BITS_READ_U32(p_ctx, bit_stream, 4, "channelConfiguration");
+ switch (channel_configuration)
+ {
+ case 1: /* 1 channel, centre front */
+ case 2: /* 2 channel, stereo front */
+ case 3: /* 3 channel, centre and stereo front */
+ case 4: /* 4 channel, centre and stereo front, mono surround */
+ case 5: /* 5 channel, centre and stereo front, stereo surround */
+ case 6: /* 5.1 channel, centre and stereo front, stereo surround, low freq */
+ audio->channels = channel_configuration;
+ break;
+ case 7: /* 7.1 channel, centre, stereo and stereo outside front, stereo surround, low freq */
+ audio->channels = channel_configuration + 1;
+ break;
+ default:
+ LOG_DEBUG(p_ctx, "MPEG-4: Unsupported channel configuration (%u)", channel_configuration);
+ return false;
+ }
+
+ switch (audio_object_type)
+ {
+ case MPEG4A_AAC_LC:
+ {
+ uint32_t ga_specific_config = BITS_READ_U32(p_ctx, bit_stream, 3, "GASpecificConfig");
+
+ /* Make sure there are no unexpected (and unsupported) additional configuration elements */
+ if (ga_specific_config != 0)
+ {
+ LOG_DEBUG(p_ctx, "MPEG-4: Unexpected additional configuration data (%u)", ga_specific_config);
+ return false;
+ }
+ }
+ break;
+ /* Add any further supported codecs here */
+ default:
+ LOG_DEBUG(p_ctx, "MPEG-4: Unsupported Audio Object Type (%u)", audio_object_type);
+ return false;
+ }
+
+ return true;
+}
+
+/**************************************************************************//**
+ * Get, store and decode the configuration information from the URI parameters.
+ *
+ * @param p_ctx The RTP container context.
+ * @param track The track being constructed.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T mp4_get_config(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *params)
+{
+ MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra;
+ PARAMETER_T param;
+ uint32_t config_len;
+ VC_CONTAINER_STATUS_T status;
+ uint8_t *config;
+ VC_CONTAINER_BITS_T bit_stream;
+
+ param.name = "config";
+ if (!vc_containers_list_find_entry(params, ¶m) || !param.value)
+ {
+ LOG_ERROR(p_ctx, "MPEG-4: config parameter missing");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ config_len = strlen(param.value);
+ if (config_len & 1)
+ {
+ LOG_ERROR(p_ctx, "MPEG-4: config parameter invalid");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+ config_len /= 2;
+
+ /* Copy AudioSpecificConfig into track extradata, to be decoded by client */
+ status = vc_container_track_allocate_extradata(p_ctx, track, config_len);
+ if(status != VC_CONTAINER_SUCCESS) return status;
+
+ config = track->priv->extradata;
+ track->format->extradata_size = config_len;
+ hex_to_byte_buffer(param.value, config, config_len);
+
+ /* Decode config locally, to determine sample rate, etc. */
+ BITS_INIT(p_ctx, &bit_stream, config, config_len);
+
+ switch (extra->stream_type)
+ {
+ case MPEG4_AUDIO_STREAM:
+ if (!mp4_decode_audio_config(p_ctx, track, &bit_stream))
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ break;
+ default:
+ /* Other stream types not yet supported */
+ LOG_ERROR(p_ctx, "MPEG-4: stream type %d not supported", extra->stream_type);
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * MP4 mode comparison function.
+ * Compare two MP4 mode structures and return whether the first is less than,
+ * equal to or greater than the second.
+ *
+ * @param first The first structure to be compared.
+ * @param second The second structure to be compared.
+ * @return Negative if first is less than second, positive if first is greater
+ * and zero if they are equal.
+ */
+static int mp4_mode_comparator(const MP4_MODE_ENTRY_T *a, const MP4_MODE_ENTRY_T *b)
+{
+ return strcasecmp(a->name, b->name);
+}
+
+/**************************************************************************//**
+ * Get and store the MP4 mode, if recognised, from the URI parameters.
+ *
+ * @param p_ctx The RTP container context.
+ * @param track The track being constructed.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T mp4_get_mode(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *params)
+{
+ MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra;
+ PARAMETER_T param;
+ MP4_MODE_ENTRY_T mode_entry;
+
+ param.name = "mode";
+ if (!vc_containers_list_find_entry(params, ¶m) || !param.value)
+ {
+ LOG_ERROR(p_ctx, "MPEG-4: mode parameter missing");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+#ifdef RTP_DEBUG
+ vc_containers_list_validate(&mp4_mode_lookup);
+#endif
+
+ mode_entry.name = param.value;
+ if (!vc_containers_list_find_entry(&mp4_mode_lookup, &mode_entry))
+ {
+ LOG_ERROR(p_ctx, "MPEG-4: Unrecognised mode parameter \"%s\"", mode_entry.name);
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ extra->mode = mode_entry.mode;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Check URI parameters for unsupported features.
+ *
+ * @param p_ctx The RTP container context.
+ * @param track The track being constructed.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T mp4_check_unsupported_features(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *params)
+{
+ uint32_t u32_unused;
+
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(track);
+
+ /* Limitation: RAP flag not yet supported */
+ if (rtp_get_parameter_u32(params, "randomAccessIndication", &u32_unused))
+ {
+ LOG_ERROR(p_ctx, "MPEG-4: random access not supported");
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+
+ /* Limitation: interleaving not yet supported */
+ if (rtp_get_parameter_u32(params, "maxDisplacement", &u32_unused) ||
+ rtp_get_parameter_u32(params, "de-interleaveBufferSize", &u32_unused))
+ {
+ LOG_ERROR(p_ctx, "MPEG-4: interleaved packetization not supported");
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+
+ /* Limitation: system streams not supported */
+ if (rtp_get_parameter_u32(params, "streamStateIndication", &u32_unused))
+ {
+ LOG_ERROR(p_ctx, "MPEG-4: system streams not supported");
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Validate parameters that have been read form the URI parameter list.
+ *
+ * @param p_ctx The RTP container context.
+ * @param track The track being constructed.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T mp4_check_parameters(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track)
+{
+ MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)track->priv->module->extra;
+
+ switch (extra->mode)
+ {
+ case MP4_CELP_CBR_MODE:
+ if (!extra->constant_size)
+ {
+ LOG_ERROR(p_ctx, "MPEG-4: CELP-cbr requires constantSize parameter.");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+ break;
+ case MP4_CELP_VBR_MODE:
+ case MP4_AAC_LBR_MODE:
+ if (extra->size_length != 6 || extra->index_length != 2 || extra->index_delta_length != 2)
+ {
+ LOG_ERROR(p_ctx, "MPEG-4: CELP-vbr/AAC-lbr invalid lengths (%u/%u/%u)",
+ extra->size_length, extra->index_length, extra->index_delta_length);
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+ break;
+ case MP4_AAC_HBR_MODE:
+ if (extra->size_length != 13 || extra->index_length != 3 || extra->index_delta_length != 3)
+ {
+ LOG_ERROR(p_ctx, "MPEG-4: AAC-hbr invalid lengths (%u/%u/%u)",
+ extra->size_length, extra->index_length, extra->index_delta_length);
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+ break;
+ default: /* MP4_GENERIC_MODE */
+ if (extra->size_length > 32 || extra->index_length > 32 || extra->index_delta_length > 32)
+ {
+ LOG_ERROR(p_ctx, "MPEG-4: generic invalid lengths (%u/%u/%u)",
+ extra->size_length, extra->index_length, extra->index_delta_length);
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+ }
+
+ if (extra->cts_delta_length > 32 || extra->dts_delta_length > 32)
+ {
+ LOG_ERROR(p_ctx, "MPEG-4: CTS/DTS invalid lengths (%u/%u)",
+ extra->cts_delta_length, extra->dts_delta_length);
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Initialise payload bit stream for a new RTP packet.
+ *
+ * @param p_ctx The RTP container context.
+ * @param t_module The track module with the new RTP packet.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T mp4_new_rtp_packet(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_MODULE_T *t_module)
+{
+ VC_CONTAINER_BITS_T *payload = &t_module->payload;
+ MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)t_module->extra;
+ VC_CONTAINER_BITS_T *au_headers = &extra->au_headers;
+
+ /* There will be an AU header section if any of its fields are non-zero. */
+ if (extra->size_length || extra->index_length || extra->cts_delta_length || extra->dts_delta_length)
+ {
+ uint32_t au_headers_length;
+
+ /* Calculate how far to advance the payload, to get past the AU headers */
+ au_headers_length = BITS_READ_U32(p_ctx, payload, 16, "AU headers length");
+ au_headers_length = BITS_TO_BYTES(au_headers_length); /* Round up to bytes */
+
+ /* Record where the AU headers are in the payload */
+ BITS_INIT(p_ctx, au_headers, BITS_CURRENT_POINTER(p_ctx, payload), au_headers_length);
+ BITS_SKIP_BYTES(p_ctx, &t_module->payload, au_headers_length, "Move payload past AU headers");
+ }
+
+ /* Skip the auxiliary section, if present */
+ if (extra->auxiliary_length)
+ {
+ uint32_t auxiliary_data_size;
+
+ auxiliary_data_size = BITS_READ_U32(p_ctx, payload, extra->auxiliary_length, "Auxiliary length");
+ auxiliary_data_size = BITS_TO_BYTES(auxiliary_data_size); /* Round up to bytes */
+ BITS_SKIP_BYTES(p_ctx, payload, auxiliary_data_size, "Auxiliary data");
+ }
+
+ return BITS_VALID(p_ctx, payload) ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FORMAT_INVALID;
+}
+
+/**************************************************************************//**
+ * Read a flagged delta from an AU header bit stream.
+ * A flagged delta is an optional value in the stream that is preceded by a
+ * flag bit that indicates whether the value is present in the stream. If the
+ * length of the value is zero bits, the flag is never present.
+ *
+ * @pre The delta_length must be 32 or less.
+ *
+ * @param p_ctx The container context.
+ * @param au_headers The AU header bit stream.
+ * @param delta_length The number of bits in the delta value.
+ * @return The delta value, or zero if not present.
+ */
+static int32_t mp4_flagged_delta(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_BITS_T *au_headers,
+ uint32_t delta_length)
+{
+ uint32_t value = 0;
+
+ /* Flag is only present if the delta length is non-zero */
+ if (delta_length && BITS_READ_U32(p_ctx, au_headers, 1, "CTS/DTS delta present"))
+ {
+ value = BITS_READ_U32(p_ctx, au_headers, delta_length, "CTS/DTS delta");
+
+ /* Sign extend value based on bit length */
+ if (value & (1 << (delta_length - 1)))
+ value |= ~((1 << delta_length) - 1);
+ }
+
+ return (int32_t)value;
+}
+
+/**************************************************************************//**
+ * Read next AU header from the bit stream.
+ *
+ * @param p_ctx The RTP container context.
+ * @param extra The MP4-specific track module information.
+ * @param is_first_au True if the first AU header in the packet is being read.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T mp4_next_au_header(VC_CONTAINER_T *p_ctx,
+ MP4_PAYLOAD_T *extra,
+ bool is_first_au)
+{
+ VC_CONTAINER_BITS_T *au_headers = &extra->au_headers;
+ AU_INFO_T *au_info = &extra->au_info;
+
+ /* See RFC3550 section 3.2.1.1 */
+
+ if (extra->constant_size)
+ au_info->available = extra->constant_size;
+ else
+ au_info->available = BITS_READ_U32(p_ctx, au_headers, extra->size_length, "AU size");
+
+ if (is_first_au)
+ au_info->index = BITS_READ_U32(p_ctx, au_headers, extra->index_length, "AU index");
+ else
+ au_info->index += BITS_READ_U32(p_ctx, au_headers, extra->index_delta_length, "AU index delta") + 1;
+
+ au_info->cts_delta = mp4_flagged_delta(p_ctx, au_headers, extra->cts_delta_length);
+ au_info->dts_delta = mp4_flagged_delta(p_ctx, au_headers, extra->dts_delta_length);
+
+ /* RAP and stream state not supported yet */
+
+ return BITS_VALID(p_ctx, au_headers) ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FORMAT_INVALID;
+}
+
+/**************************************************************************//**
+ * MP4 payload handler.
+ * Extracts/skips data from the payload according to the AU headers.
+ *
+ * @param p_ctx The RTP container context.
+ * @param track The track being read.
+ * @param p_packet The container packet information, or NULL.
+ * @param flags The container read flags.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T mp4_payload_handler(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ VC_CONTAINER_PACKET_T *p_packet,
+ uint32_t flags)
+{
+ VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module;
+ VC_CONTAINER_BITS_T *payload = &t_module->payload;
+ MP4_PAYLOAD_T *extra = (MP4_PAYLOAD_T *)t_module->extra;
+ AU_INFO_T *au_info = &extra->au_info;
+ bool is_new_packet = BIT_IS_SET(t_module->flags, TRACK_NEW_PACKET);
+ uint32_t bytes_left_in_payload;
+ uint32_t size;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ if (is_new_packet)
+ {
+ status = mp4_new_rtp_packet(p_ctx, t_module);
+ if (status != VC_CONTAINER_SUCCESS)
+ return status;
+ }
+
+ if (!au_info->available)
+ {
+ status = mp4_next_au_header(p_ctx, extra, is_new_packet);
+ if (status != VC_CONTAINER_SUCCESS)
+ return status;
+ }
+
+ if (p_packet)
+ {
+ /* Adjust the packet time stamps using deltas */
+ p_packet->pts += au_info->cts_delta;
+ p_packet->dts += au_info->dts_delta;
+ }
+
+ size = au_info->available;
+ bytes_left_in_payload = BITS_BYTES_AVAILABLE(p_ctx, payload);
+ if (size > bytes_left_in_payload)
+ {
+ /* AU is fragmented across RTP packets */
+ size = bytes_left_in_payload;
+ }
+
+ if (p_packet && !(flags & VC_CONTAINER_READ_FLAG_SKIP))
+ {
+ if (!(flags & VC_CONTAINER_READ_FLAG_INFO))
+ {
+ if (size > p_packet->buffer_size)
+ size = p_packet->buffer_size;
+
+ BITS_COPY_BYTES(p_ctx, payload, size, p_packet->data, "Packet data");
+ }
+ p_packet->size = size;
+ } else {
+ BITS_SKIP_BYTES(p_ctx, payload, size, "Packet data");
+ }
+
+ if (!(flags & VC_CONTAINER_READ_FLAG_INFO))
+ au_info->available -= size;
+
+ return BITS_VALID(p_ctx, payload) ? VC_CONTAINER_SUCCESS : VC_CONTAINER_ERROR_FORMAT_INVALID;
+}
+
+/*****************************************************************************
+Functions exported as part of the RTP parameter handler API
+ *****************************************************************************/
+
+/**************************************************************************//**
+ * MP4 parameter handler.
+ * Parses the URI parameters to set up the track for an MP4 stream.
+ *
+ * @param p_ctx The reader context.
+ * @param track The track to be updated.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+VC_CONTAINER_STATUS_T mp4_parameter_handler(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *params)
+{
+ MP4_PAYLOAD_T *extra;
+ VC_CONTAINER_STATUS_T status;
+
+ /* See RFC3640, section 4.1, for parameter names and details. */
+ extra = (MP4_PAYLOAD_T *)malloc(sizeof(MP4_PAYLOAD_T));
+ if (!extra)
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ track->priv->module->extra = extra;
+ memset(extra, 0, sizeof(MP4_PAYLOAD_T));
+
+ /* Mandatory parameters */
+ status = mp4_get_stream_type(p_ctx, track, params);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ status = mp4_get_config(p_ctx, track, params);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ status = mp4_get_mode(p_ctx, track, params);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ /* Unsupported parameters */
+ status = mp4_check_unsupported_features(p_ctx, track, params);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ /* Optional parameters */
+ rtp_get_parameter_u32(params, "sizeLength", &extra->size_length);
+ rtp_get_parameter_u32(params, "indexLength", &extra->index_length);
+ rtp_get_parameter_u32(params, "indexDeltaLength", &extra->index_delta_length);
+ rtp_get_parameter_u32(params, "CTSDeltaLength", &extra->cts_delta_length);
+ rtp_get_parameter_u32(params, "DTSDeltaLength", &extra->dts_delta_length);
+ rtp_get_parameter_u32(params, "objectType", &extra->object_type);
+ rtp_get_parameter_u32(params, "constantSize", &extra->constant_size);
+ rtp_get_parameter_u32(params, "constantDuration", &extra->constant_duration);
+ rtp_get_parameter_u32(params, "auxiliaryDataSizeLength", &extra->auxiliary_length);
+
+ if (extra->constant_size && extra->size_length)
+ {
+ LOG_ERROR(p_ctx, "MPEG4: constantSize and sizeLength cannot both be set.");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ status = mp4_check_parameters(p_ctx, track);
+ if (status != VC_CONTAINER_SUCCESS) return status;
+
+ track->priv->module->payload_handler = mp4_payload_handler;
+
+ return VC_CONTAINER_SUCCESS;
+}
diff --git a/gfx/include/userland/containers/rtp/rtp_mpeg4.h b/gfx/include/userland/containers/rtp/rtp_mpeg4.h
new file mode 100644
index 0000000000..99181bbc2e
--- /dev/null
+++ b/gfx/include/userland/containers/rtp/rtp_mpeg4.h
@@ -0,0 +1,42 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _RTP_MPEG4_H_
+#define _RTP_MPEG4_H_
+
+#include "containers/containers.h"
+#include "containers/core/containers_list.h"
+
+/** MPEG-4 parameter handler
+ *
+ * \param p_ctx Container context.
+ * \param track Track data.
+ * \param params Parameter list.
+ * \return Status of decoding the MPEG-4 parameters. */
+VC_CONTAINER_STATUS_T mp4_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params);
+
+#endif /* _RTP_MPEG4_H_ */
diff --git a/gfx/include/userland/containers/rtp/rtp_priv.h b/gfx/include/userland/containers/rtp/rtp_priv.h
new file mode 100644
index 0000000000..c54bfe8f7e
--- /dev/null
+++ b/gfx/include/userland/containers/rtp/rtp_priv.h
@@ -0,0 +1,111 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef _RTP_PRIV_H_
+#define _RTP_PRIV_H_
+
+#include "containers/containers.h"
+
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_bits.h"
+#include "containers/core/containers_list.h"
+
+typedef VC_CONTAINER_STATUS_T (*PAYLOAD_HANDLER_T)(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track, VC_CONTAINER_PACKET_T *p_packet, uint32_t flags);
+
+/** Parameter list entry type. */
+typedef struct parameter_tag
+{
+ const char *name;
+ const char *value;
+} PARAMETER_T;
+
+/** Prototype for MIME parameter handling.
+ * Each MIME type has a certain set of parameter names it uses, so a handler is
+ * needed for each type. This is that handler's prototype.
+ */
+typedef VC_CONTAINER_STATUS_T (*PARAMETER_HANDLER_T)(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params);
+
+/** Track module flag bit numbers (up to seven) */
+typedef enum
+{
+ TRACK_SSRC_SET = 0,
+ TRACK_HAS_MARKER,
+ TRACK_NEW_PACKET,
+} track_module_flag_bit_t;
+
+/** RTP track data */
+typedef struct VC_CONTAINER_TRACK_MODULE_T
+{
+ PAYLOAD_HANDLER_T payload_handler; /**< Extracts the data from the payload */
+ uint8_t *buffer; /**< Buffer into which the RTP packet is read */
+ VC_CONTAINER_BITS_T payload; /**< Payload bit bit_stream */
+ uint8_t flags; /**< Combination of track module flags */
+ uint8_t payload_type; /**< The expected payload type */
+ uint16_t max_seq_num; /**< Highest seq. number seen */
+ uint32_t timestamp; /**< RTP timestamp of packet */
+ uint32_t timestamp_base; /**< RTP timestamp value that equates to time zero */
+ uint32_t last_timestamp_top; /**< Top two bits of RTP timestamp of previous packet */
+ uint32_t timestamp_wraps; /**< Count of the times that the timestamp has wrapped */
+ uint32_t timestamp_clock; /**< Clock frequency of RTP timestamp values */
+ uint32_t expected_ssrc; /**< The expected SSRC, if set */
+ uint32_t base_seq; /**< Base seq number */
+ uint32_t bad_seq; /**< Last 'bad' seq number + 1 */
+ uint32_t probation; /**< Sequential packets till source is valid */
+ uint32_t received; /**< RTP packets received */
+ void *extra; /**< Payload specific data */
+} VC_CONTAINER_TRACK_MODULE_T;
+
+/** Determine minimum number of bytes needed to hold a number of bits */
+#define BITS_TO_BYTES(X) (((X) + 7) >> 3)
+
+/** Collection of bit manipulation routines */
+/* @{ */
+#define SET_BIT(V, B) V |= (1 << (B))
+#define CLEAR_BIT(V, B) V &= ~(1 << (B))
+#define BIT_IS_SET(V, B) (!(!((V) & (1 << (B)))))
+#define BIT_IS_CLEAR(V, B) (!((V) & (1 << (B))))
+/* }@ */
+
+
+/** Get a parameter's value as a decimal number.
+ *
+ * \param param_list The list of parameter name/value pairs.
+ * \param name The paramter's name.
+ * \param value Where to put the converted value.
+ * \return True if successful, false if the parameter was not found or didn't convert. */
+bool rtp_get_parameter_u32(const VC_CONTAINERS_LIST_T *param_list, const char *name, uint32_t *value);
+
+/** Get a parameter's value as a hexadecimal number.
+ *
+ * \param param_list The list of parameter name/value pairs.
+ * \param name The paramter's name.
+ * \param value Where to put the converted value.
+ * \return True if successful, false if the parameter was not found or didn't convert. */
+bool rtp_get_parameter_x32(const VC_CONTAINERS_LIST_T *param_list, const char *name, uint32_t *value);
+
+#endif /* _RTP_PRIV_H_ */
diff --git a/gfx/include/userland/containers/rtp/rtp_reader.c b/gfx/include/userland/containers/rtp/rtp_reader.c
new file mode 100644
index 0000000000..9ce4a595c5
--- /dev/null
+++ b/gfx/include/userland/containers/rtp/rtp_reader.c
@@ -0,0 +1,1158 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+#include
+
+#define CONTAINER_IS_BIG_ENDIAN
+//#define ENABLE_CONTAINERS_LOG_FORMAT
+//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+#define CONTAINER_HELPER_LOG_INDENT(a) 0
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_uri.h"
+#include "containers/core/containers_logging.h"
+#include "containers/core/containers_bits.h"
+#include "containers/core/containers_list.h"
+
+#include "rtp_priv.h"
+#include "rtp_mpeg4.h"
+#include "rtp_h264.h"
+
+#ifdef _DEBUG
+/* Validates static sorted lists are correctly constructed */
+#define RTP_DEBUG 1
+#endif
+
+/******************************************************************************
+Configurable defines and constants.
+******************************************************************************/
+
+/** Maximum size of an RTP packet */
+#define MAXIMUM_PACKET_SIZE 2048
+
+/** Maximum number of RTP packets that can be missed without restarting. */
+#define MAX_DROPOUT 3000
+/** Maximum number of out of sequence RTP packets that are accepted. */
+#define MAX_MISORDER 0
+/** Minimum number of sequential packets required for an acceptable connection
+ * when restarting. */
+#define MIN_SEQUENTIAL 2
+
+/******************************************************************************
+Defines and constants.
+******************************************************************************/
+
+#define RTP_SCHEME "rtp:"
+
+/** The RTP PKT scheme is used with test pkt files */
+#define RTP_PKT_SCHEME "rtppkt:"
+
+/** \name RTP URI parameter names
+ * @{ */
+#define PAYLOAD_TYPE_NAME "rtppt"
+#define MIME_TYPE_NAME "mime-type"
+#define CHANNELS_NAME "channels"
+#define RATE_NAME "rate"
+#define SSRC_NAME "ssrc"
+#define SEQ_NAME "seq"
+/* @} */
+
+/** A sentinel codec that is not supported */
+#define UNSUPPORTED_CODEC VC_FOURCC(0,0,0,0)
+
+/** Evaluates to true if the given payload type is in the supported static audio range. */
+#define IS_STATIC_AUDIO_TYPE(PT) ((PT) < countof(static_audio_payload_types))
+
+/** Payload type number for the first static video type */
+#define FIRST_STATIC_VIDEO_TYPE 24
+/** Evaluates to true if the given payload type is in the supported static video range. */
+#define IS_STATIC_VIDEO_TYPE(PT) ((PT) >= FIRST_STATIC_VIDEO_TYPE && \
+ (PT) < (FIRST_STATIC_VIDEO_TYPE + countof(static_video_payload_types)))
+
+/** Evaluates to true if the given payload type is in the dynamic range. */
+#define IS_DYNAMIC_TYPE(PT) ((PT) >= 96 && (PT) < 128)
+
+/** All sequence numbers are modulo this value. */
+#define RTP_SEQ_MOD (1 << 16)
+
+/** All the static video payload types use a 90kHz timestamp clock */
+#define STATIC_VIDEO_TIMESTAMP_CLOCK 90000
+
+/** Number of microseconds in a second, used to convert RTP timestamps to microseconds */
+#define MICROSECONDS_PER_SECOND 1000000
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+
+/** \name MIME type parameter handlers
+ * Function prototypes for payload parameter handlers */
+/* @{ */
+static VC_CONTAINER_STATUS_T audio_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params);
+static VC_CONTAINER_STATUS_T l8_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params);
+static VC_CONTAINER_STATUS_T l16_parameter_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track, const VC_CONTAINERS_LIST_T *params);
+/* @} */
+
+/** \name MIME type payload handlers */
+/* @{ */
+static VC_CONTAINER_STATUS_T l16_payload_handler(VC_CONTAINER_T *p_ctx, VC_CONTAINER_TRACK_T *track,
+ VC_CONTAINER_PACKET_T *p_packet, uint32_t flags);
+/* @} */
+
+/** Static audio payload type data */
+typedef struct audio_payload_type_data_tag
+{
+ VC_CONTAINER_FOURCC_T codec; /**< FourCC codec for this payload type */
+ uint32_t channels; /**< Number of audio channels */
+ uint32_t sample_rate; /**< Sample rate */
+ uint32_t bits_per_sample; /**< Bits per sample, or 1 if not applicable */
+ PARAMETER_HANDLER_T param_handler; /**< Optional parameter handler */
+ PAYLOAD_HANDLER_T payload_handler; /**< Optional payload handler */
+} AUDIO_PAYLOAD_TYPE_DATA_T;
+
+/** The details for the statically defined audio payload types from RFC3551 */
+static AUDIO_PAYLOAD_TYPE_DATA_T static_audio_payload_types[] =
+{
+ { VC_CONTAINER_CODEC_MULAW, 1, 8000, 8, audio_parameter_handler, NULL }, /* 0 - PCMU */
+ { UNSUPPORTED_CODEC }, /* 1 - reserved */
+ { UNSUPPORTED_CODEC }, /* 2 - reserved */
+ { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 3 - GSM */
+ { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 4 - G723 */
+ { UNSUPPORTED_CODEC, 1, 8000, 4, NULL, NULL }, /* 5 - DVI4 */
+ { UNSUPPORTED_CODEC, 1, 16000, 4, NULL, NULL }, /* 6 - DVI4 */
+ { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 7 - LPC */
+ { VC_CONTAINER_CODEC_ALAW, 1, 8000, 8, audio_parameter_handler, NULL }, /* 8 - PCMA */
+ { UNSUPPORTED_CODEC, 1, 8000, 8, NULL, NULL }, /* 9 - G722 */
+ { VC_CONTAINER_CODEC_PCM_SIGNED, 2, 44100, 16, audio_parameter_handler, l16_payload_handler }, /* 10 - L16 */
+ { VC_CONTAINER_CODEC_PCM_SIGNED, 1, 44100, 16, audio_parameter_handler, l16_payload_handler }, /* 11 - L16 */
+ { VC_CONTAINER_CODEC_QCELP, 1, 8000, 16, NULL, NULL }, /* 12 - QCELP */
+ { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 13 - CN */
+ { VC_CONTAINER_CODEC_MPGA, 1, 90000, 1, NULL, NULL }, /* 14 - MPA */
+ { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 15 - G728 */
+ { UNSUPPORTED_CODEC, 1, 11025, 4, NULL, NULL }, /* 16 - DVI4 */
+ { UNSUPPORTED_CODEC, 1, 22050, 4, NULL, NULL }, /* 17 - DVI4 */
+ { UNSUPPORTED_CODEC, 1, 8000, 1, NULL, NULL }, /* 18 - G729 */
+};
+
+/** Static video payload type data */
+typedef struct video_payload_type_data_tag
+{
+ VC_CONTAINER_FOURCC_T codec; /**< FourCC codec for this payload type */
+ PARAMETER_HANDLER_T param_handler; /**< Optional parameter handler */
+ PAYLOAD_HANDLER_T payload_handler; /**< Optional payload handler */
+} VIDEO_PAYLOAD_TYPE_DATA_T;
+
+/** The details for the statically defined video payload types from RFC3551 */
+static VIDEO_PAYLOAD_TYPE_DATA_T static_video_payload_types[] =
+{
+ { UNSUPPORTED_CODEC }, /* 24 - unassigned */
+ { UNSUPPORTED_CODEC }, /* 25 - CelB */
+ { UNSUPPORTED_CODEC }, /* 26 - JPEG */
+ { UNSUPPORTED_CODEC }, /* 27 - unassigned */
+ { UNSUPPORTED_CODEC }, /* 28 - nv */
+ { UNSUPPORTED_CODEC }, /* 29 - unassigned */
+ { UNSUPPORTED_CODEC }, /* 30 - unassigned */
+ { UNSUPPORTED_CODEC }, /* 31 - H261 */
+ { VC_CONTAINER_CODEC_MP2V, NULL, NULL }, /* 32 - MPV */
+ { UNSUPPORTED_CODEC }, /* 33 - MP2T */
+ { VC_CONTAINER_CODEC_H263, NULL, NULL } /* 34 - H263 */
+};
+
+/** MIME type details */
+typedef struct mime_type_data_tag
+{
+ const char *name; /**< Name of MIME type */
+ VC_CONTAINER_ES_TYPE_T es_type; /**< Elementary stream type */
+ VC_CONTAINER_FOURCC_T codec; /**< Codec to be used */
+ PARAMETER_HANDLER_T param_handler; /**< Parameter handler for this MIME type */
+} MIME_TYPE_DATA_T;
+
+/** Comparator for MIME type details. */
+static int mime_type_data_comparator(const MIME_TYPE_DATA_T *a, const MIME_TYPE_DATA_T *b);
+
+/** Dynamic audio payload details
+ * Note: case-insensitive sort by name */
+static MIME_TYPE_DATA_T dynamic_mime_details[] = {
+ { "audio/l16", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_PCM_SIGNED, l16_parameter_handler },
+ { "audio/l8", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_PCM_SIGNED, l8_parameter_handler },
+ { "audio/mpeg4-generic", VC_CONTAINER_ES_TYPE_AUDIO, VC_CONTAINER_CODEC_MP4A, mp4_parameter_handler },
+ { "video/h264", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_H264, h264_parameter_handler },
+ { "video/mpeg4-generic", VC_CONTAINER_ES_TYPE_VIDEO, VC_CONTAINER_CODEC_MP4V, mp4_parameter_handler },
+};
+
+/** Sorted list of dynamic MIME type details */
+VC_CONTAINERS_STATIC_LIST(dynamic_mime, dynamic_mime_details, mime_type_data_comparator);
+
+/** RTP reader data. */
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *track;
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+/**************************************************************************//**
+ * Parameter comparison function.
+ * Compare two parameter structures and return whether the first is less than,
+ * equal to or greater than the second.
+ *
+ * @param first The first structure to be compared.
+ * @param second The second structure to be compared.
+ * @return Negative if first is less than second, positive if first is greater
+ * and zero if they are equal.
+ */
+static int parameter_comparator(const PARAMETER_T *first, const PARAMETER_T *second)
+{
+ return strcasecmp(first->name, second->name);
+}
+
+/**************************************************************************//**
+ * Creates and populates a parameter list from a URI structure.
+ * The list does not copy the parameter strings themselves, so the URI structure
+ * must be retained (and its parameters unmodified) while the list is in use.
+ *
+ * @param uri The URI containing the parameters.
+ * @return List created from the parameters of the URI, or NULL on error.
+ */
+static VC_CONTAINERS_LIST_T *fill_parameter_list(VC_URI_PARTS_T *uri)
+{
+ uint32_t num_parameters = vc_uri_num_queries(uri);
+ VC_CONTAINERS_LIST_T *parameters;
+ uint32_t ii;
+
+ parameters = vc_containers_list_create(num_parameters, sizeof(PARAMETER_T), (VC_CONTAINERS_LIST_COMPARATOR_T)parameter_comparator);
+ if (!parameters)
+ return NULL;
+
+ for (ii = 0; ii < num_parameters; ii++)
+ {
+ PARAMETER_T param;
+
+ vc_uri_query(uri, ii, ¶m.name, ¶m.value);
+ if (!vc_containers_list_insert(parameters, ¶m, false))
+ {
+ vc_containers_list_destroy(parameters);
+ return NULL;
+ }
+ }
+
+#ifdef RTP_DEBUG
+ vc_containers_list_validate(parameters);
+#endif
+
+ return parameters;
+}
+
+/**************************************************************************//**
+ * Decodes a static audio payload type into track information.
+ * The static parameters may be overridden by URI parameters.
+ *
+ * @param p_ctx The reader context.
+ * @param track The track to be populated.
+ * @param param_list The URI parameter list.
+ * @param payload_type The static payload type.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T decode_static_audio_type(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *param_list,
+ uint32_t payload_type)
+{
+ VC_CONTAINER_ES_FORMAT_T *format = track->format;
+ AUDIO_PAYLOAD_TYPE_DATA_T *data = &static_audio_payload_types[payload_type];
+
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(param_list);
+
+ vc_container_assert(payload_type < countof(static_audio_payload_types));
+
+ if (data->codec == UNSUPPORTED_CODEC)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ format->es_type = VC_CONTAINER_ES_TYPE_AUDIO;
+ format->codec = data->codec;
+ format->type->audio.channels = data->channels;
+ format->type->audio.sample_rate = data->sample_rate;
+ format->type->audio.bits_per_sample = data->bits_per_sample;
+ format->type->audio.block_align = data->channels * BITS_TO_BYTES(data->bits_per_sample);
+ track->priv->module->timestamp_clock = format->type->audio.sample_rate;
+ track->priv->module->payload_handler = data->payload_handler;
+
+ if (data->param_handler)
+ data->param_handler(p_ctx, track, param_list);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Decodes a static video payload type into track information.
+ * The static parameters may be overridden by URI parameters.
+ *
+ * @param p_ctx The reader context.
+ * @param track The track to be populated.
+ * @param param_list The URI parameter list.
+ * @param payload_type The static payload type.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T decode_static_video_type(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *param_list,
+ uint32_t payload_type)
+{
+ VC_CONTAINER_ES_FORMAT_T *format = track->format;
+ VIDEO_PAYLOAD_TYPE_DATA_T *data = &static_video_payload_types[payload_type - FIRST_STATIC_VIDEO_TYPE];
+
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(param_list);
+
+ vc_container_assert(payload_type >= FIRST_STATIC_VIDEO_TYPE);
+ vc_container_assert(payload_type < FIRST_STATIC_VIDEO_TYPE + countof(static_video_payload_types));
+
+ if (data->codec == UNSUPPORTED_CODEC)
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ format->es_type = VC_CONTAINER_ES_TYPE_VIDEO;
+ format->codec = data->codec;
+ track->priv->module->timestamp_clock = STATIC_VIDEO_TIMESTAMP_CLOCK;
+ track->priv->module->payload_handler = data->payload_handler;
+
+ if (data->param_handler)
+ data->param_handler(p_ctx, track, param_list);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Compare two MIME type structures and return whether the first is less than,
+ * equal to or greater than the second.
+ *
+ * @param a The first parameter structure to be compared.
+ * @param b The second parameter structure to be compared.
+ * @return Negative if a is less than b, positive if a is greater and zero if
+ * they are equal.
+ */
+static int mime_type_data_comparator(const MIME_TYPE_DATA_T *a, const MIME_TYPE_DATA_T *b)
+{
+ return strcasecmp(a->name, b->name);
+}
+
+/**************************************************************************//**
+ * Generic audio parameter handler.
+ * Updates the track information with generic audio parameters, such as "rate"
+ * and "channels".
+ *
+ * @param p_ctx The reader context.
+ * @param track The track to be updated.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T audio_parameter_handler(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *params)
+{
+ VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio;
+
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+
+ /* See RFC3555. Generic audio parameters that can override static payload
+ * type defaults. */
+ if (rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate))
+ track->priv->module->timestamp_clock = audio->sample_rate;
+ if (rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels))
+ audio->block_align = audio->channels * BITS_TO_BYTES(audio->bits_per_sample);
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * L8 specific audio parameter handler.
+ * Updates the track information with audio parameters needed by the audio/L8
+ * MIME type. For example, the "rate" parameter is mandatory.
+ *
+ * @param p_ctx The reader context.
+ * @param track The track to be updated.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T l8_parameter_handler(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *params)
+{
+ VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio;
+
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+
+ /* See RFC3555, section 4.1.14, for parameter names and details. */
+ if (!rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate))
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ if (!rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels))
+ audio->channels = 1;
+ audio->bits_per_sample = 8;
+ audio->block_align = audio->channels;
+ track->priv->module->timestamp_clock = audio->sample_rate;
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * L16 specific audio parameter handler.
+ * Updates the track information with audio parameters needed by the audio/L16
+ * MIME type. For example, the "rate" parameter is mandatory.
+ *
+ * @param p_ctx The reader context.
+ * @param track The track to be updated.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T l16_parameter_handler(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *params)
+{
+ VC_CONTAINER_AUDIO_FORMAT_T *audio = &track->format->type->audio;
+
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+
+ /* See RFC3555, section 4.1.15, for parameter names and details. */
+ if (!rtp_get_parameter_u32(params, RATE_NAME, &audio->sample_rate))
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ if (!rtp_get_parameter_u32(params, CHANNELS_NAME, &audio->channels))
+ audio->channels = 1;
+ audio->bits_per_sample = 16;
+ audio->block_align = audio->channels * 2;
+ track->priv->module->timestamp_clock = audio->sample_rate;
+ track->priv->module->payload_handler = l16_payload_handler;
+
+ /* TODO: add support for "channel-order" to set channel mapping */
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Decode a dynamic payload type from parameters.
+ * Populates the track information with data for supported dynamic media types.
+ *
+ * @param p_ctx The reader context.
+ * @param track The track to be updated.
+ * @param param_list The URI parameter list.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T decode_dynamic_type(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *param_list)
+{
+ VC_CONTAINER_ES_FORMAT_T *format = track->format;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ PARAMETER_T mime_type;
+ MIME_TYPE_DATA_T mime_details;
+
+ /* Get MIME type parameter */
+ mime_type.name = MIME_TYPE_NAME;
+ if (!vc_containers_list_find_entry(param_list, &mime_type))
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+#ifdef RTP_DEBUG
+ vc_containers_list_validate(&dynamic_mime);
+#endif
+
+ /* Look up MIME type to see if it can be handled */
+ mime_details.name = mime_type.value;
+ if (!vc_containers_list_find_entry(&dynamic_mime, &mime_details))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ format->codec = mime_details.codec;
+ format->es_type = mime_details.es_type;
+
+ /* Default number of channels for audio is one */
+ if (mime_details.es_type == VC_CONTAINER_ES_TYPE_AUDIO)
+ format->type->audio.channels = 1;
+
+ /* Lete MIME type specific parameter handler deal with any other parameters */
+ status = mime_details.param_handler(p_ctx, track, param_list);
+
+ /* Ensure that the sample rate has been set for audio formats */
+ if (mime_details.es_type == VC_CONTAINER_ES_TYPE_AUDIO && !format->type->audio.sample_rate)
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+
+ return status;
+}
+
+/**************************************************************************//**
+ * Decode the RTP payload type.
+ * Populates track information with data from static tables and the URI
+ * parameter list, according to the payload and MIME types.
+ *
+ * @param p_ctx The reader context.
+ * @param track The track to be updated.
+ * @param params The URI parameter list.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T decode_payload_type(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ const VC_CONTAINERS_LIST_T *param_list,
+ uint32_t payload_type)
+{
+ VC_CONTAINER_TRACK_MODULE_T *module = track->priv->module;
+ VC_CONTAINER_STATUS_T status;
+
+ if (IS_STATIC_AUDIO_TYPE(payload_type))
+ status = decode_static_audio_type(p_ctx, track, param_list, payload_type);
+ else if (IS_STATIC_VIDEO_TYPE(payload_type))
+ status = decode_static_video_type(p_ctx, track, param_list, payload_type);
+ else if (IS_DYNAMIC_TYPE(payload_type))
+ status = decode_dynamic_type(p_ctx, track, param_list);
+ else
+ status = VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ module->payload_type = (uint8_t)payload_type;
+
+ return status;
+}
+
+/**************************************************************************//**
+ * Initialises the RTP sequence number algorithm with a new sequence number.
+ *
+ * @param t_module The track module.
+ * @param seq The new sequence number.
+ */
+static void init_sequence_number(VC_CONTAINER_TRACK_MODULE_T *t_module,
+ uint16_t seq)
+{
+ t_module->base_seq = seq;
+ t_module->max_seq_num = seq;
+ t_module->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */
+ t_module->received = 0;
+}
+
+/**************************************************************************//**
+ * Checks whether the sequence number for a packet is acceptable or not.
+ * The packet will be unacceptable if it is out of sequence by some degree, or
+ * if the packet sequence is still being established.
+ *
+ * @param t_module The track module.
+ * @param seq The new sequence number.
+ * @return True if the sequence number indicates the packet is acceptable
+ */
+static bool update_sequence_number(VC_CONTAINER_TRACK_MODULE_T *t_module,
+ uint16_t seq)
+{
+ uint16_t udelta = seq - t_module->max_seq_num;
+
+ /* NOTE: This source is derived from the example code in RFC3550, section A.1 */
+
+ /* Source is not valid until MIN_SEQUENTIAL packets with
+ * sequential sequence numbers have been received. */
+ if (t_module->probation)
+ {
+ /* packet is in sequence */
+ if (seq == t_module->max_seq_num + 1)
+ {
+ t_module->probation--;
+ t_module->max_seq_num = seq;
+ LOG_INFO(0, "RTP: Probation, %u more packet(s) to go at 0x%4.4hx", t_module->probation, seq);
+
+ if (!t_module->probation)
+ {
+ init_sequence_number(t_module, seq);
+ t_module->received++;
+ return 1;
+ }
+ } else {
+ t_module->probation = MIN_SEQUENTIAL - 1;
+ t_module->max_seq_num = seq;
+ LOG_INFO(0, "RTP: Probation reset, wait for %u packet(s) at 0x%4.4hx", t_module->probation, seq);
+ }
+ return 0;
+ } else if (udelta < MAX_DROPOUT)
+ {
+ if (!udelta)
+ {
+ /* Duplicate packet, drop it */
+ LOG_INFO(0, "RTP: Drop duplicate packet at 0x%4.4hx", seq);
+ return 0;
+ }
+ if (udelta > 1)
+ {
+ LOG_INFO(0, "RTP: Jumped by %hu packets to 0x%4.4hx", udelta, seq);
+ }
+ /* in order, with permissible gap */
+ t_module->max_seq_num = seq;
+ } else
+#if (MAX_MISORDER != 0)
+ /* When MAX_MISORDER is zero, always treat as out of order */
+ if (udelta <= RTP_SEQ_MOD - MAX_MISORDER)
+#endif
+ {
+ /* the sequence number made a very large jump */
+ if (seq == t_module->bad_seq)
+ {
+ LOG_INFO(0, "RTP: Misorder restart at 0x%4.4hx", seq);
+ /* Two sequential packets -- assume that the other side
+ * restarted without telling us so just re-sync
+ * (i.e., pretend this was the first packet). */
+ init_sequence_number(t_module, seq);
+ } else {
+ LOG_INFO(0, "RTP: Misorder at 0x%4.4hx, expected 0x%4.4hx", seq, t_module->max_seq_num);
+ t_module->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
+ return 0;
+ }
+ }
+#if (MAX_MISORDER != 0)
+ else {
+ /* duplicate or reordered packet */
+
+ /* TODO: handle out of order packets */
+ }
+#endif
+ t_module->received++;
+ return 1;
+}
+
+/**************************************************************************//**
+ * Extract the fields of an RTP packet and validate it.
+ *
+ * @param p_ctx The reader context.
+ * @param t_module The track module.
+ * @return True if successful, false if there were not enough bits in the
+ * packet or the packet was invalid.
+ */
+static void decode_rtp_packet_header(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_MODULE_T *t_module)
+{
+ VC_CONTAINER_BITS_T *payload = &t_module->payload;
+ uint32_t version, has_padding, has_extension, csrc_count, has_marker;
+ uint32_t payload_type, ssrc;
+ uint16_t seq_num;
+
+ /* Break down fixed header area into component parts */
+ version = BITS_READ_U32(p_ctx, payload, 2, "Version");
+ has_padding = BITS_READ_U32(p_ctx, payload, 1, "Has padding");
+ has_extension = BITS_READ_U32(p_ctx, payload, 1, "Has extension");
+ csrc_count = BITS_READ_U32(p_ctx, payload, 4, "CSRC count");
+ has_marker = BITS_READ_U32(p_ctx, payload, 1, "Has marker");
+ payload_type = BITS_READ_U32(p_ctx, payload, 7, "Payload type");
+ seq_num = BITS_READ_U16(p_ctx, payload, 16, "Sequence number");
+ t_module->timestamp = BITS_READ_U32(p_ctx, payload, 32, "Timestamp");
+ ssrc = BITS_READ_U32(p_ctx, payload, 32, "SSRC");
+
+ /* If there was only a partial header, abort immediately */
+ if (!BITS_VALID(p_ctx, payload))
+ return;
+
+ /* Validate version, payload type, sequence number and SSRC, if set */
+ if (version != 2 || payload_type != t_module->payload_type)
+ {
+ BITS_INVALIDATE(p_ctx, payload);
+ return;
+ }
+ if (BIT_IS_SET(t_module->flags, TRACK_SSRC_SET) && (ssrc != t_module->expected_ssrc))
+ {
+ LOG_DEBUG(p_ctx, "RTP: Unexpected SSRC (0x%8.8X)", ssrc);
+ BITS_INVALIDATE(p_ctx, payload);
+ return;
+ }
+
+ /* Check sequence number indicates packet is usable */
+ if (!update_sequence_number(t_module, seq_num))
+ {
+ BITS_INVALIDATE(p_ctx, payload);
+ return;
+ }
+
+ /* Adjust to account for padding, CSRCs and extension */
+ if (has_padding)
+ {
+ VC_CONTAINER_BITS_T bit_stream;
+ uint32_t available = BITS_BYTES_AVAILABLE(p_ctx, payload);
+ uint8_t padding;
+
+ BITS_COPY_STREAM(p_ctx, &bit_stream, payload);
+ /* The last byte of the payload is the number of padding bytes, including itself */
+ BITS_SKIP_BYTES(p_ctx, &bit_stream, available - 1, "Skip to padding length");
+ padding = BITS_READ_U8(p_ctx, &bit_stream, 8, "Padding length");
+
+ BITS_REDUCE_BYTES(p_ctx, payload, padding, "Remove padding");
+ }
+
+ /* Each CSRC is 32 bits, so shift count up to skip the right number of bits */
+ BITS_SKIP(p_ctx, payload, csrc_count << 5, "CSRC section");
+
+ if (has_extension)
+ {
+ uint32_t extension_bits;
+
+ /* Extension header is 16-bit ID (which isn't needed), then 16-bit length in 32-bit words */
+ BITS_SKIP(p_ctx, payload, 16, "Extension ID");
+ extension_bits = BITS_READ_U32(p_ctx, payload, 16, "Extension length") << 5;
+ BITS_SKIP(p_ctx, payload, extension_bits, "Extension data");
+ }
+
+ /* Record whether or not this RTP packet had the marker bit set */
+ if (has_marker)
+ SET_BIT(t_module->flags, TRACK_HAS_MARKER);
+ else
+ CLEAR_BIT(t_module->flags, TRACK_HAS_MARKER);
+
+ /* If it hasn't been set independently, use the first timestamp as a baseline */
+ if (!t_module->timestamp_base)
+ t_module->timestamp_base = t_module->timestamp;
+ t_module->timestamp -= t_module->timestamp_base;
+}
+
+/**************************************************************************//**
+ * Generic payload handler.
+ * Copies/skips data verbatim from the packet payload.
+ *
+ * @param p_ctx The reader context.
+ * @param track The track being read.
+ * @param p_packet The container packet information, or NULL.
+ * @param flags The container read flags.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T generic_payload_handler(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ VC_CONTAINER_PACKET_T *p_packet,
+ uint32_t flags)
+{
+ VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module;
+ VC_CONTAINER_BITS_T *payload = &t_module->payload;
+ uint32_t size;
+
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+
+ if (!p_packet)
+ {
+ /* Skip the rest of this RTP packet */
+ BITS_INVALIDATE(p_ctx, payload);
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ /* Copy as much as possible into the client packet buffer */
+ size = BITS_BYTES_AVAILABLE(p_ctx, payload);
+
+ if (flags & VC_CONTAINER_READ_FLAG_SKIP)
+ BITS_SKIP_BYTES(p_ctx, payload, size, "Packet data");
+ else {
+ if (!(flags & VC_CONTAINER_READ_FLAG_INFO))
+ {
+ if (size > p_packet->buffer_size)
+ size = p_packet->buffer_size;
+
+ BITS_COPY_BYTES(p_ctx, payload, size, p_packet->data, "Packet data");
+ }
+ p_packet->size = size;
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * L16 payload handler.
+ * Copies/skips data from the packet payload. On copy, swaps consecutive bytes
+ * in the data in order to get expected byte order.
+ *
+ * @param p_ctx The reader context.
+ * @param track The track being read.
+ * @param p_packet The container packet information, or NULL.
+ * @param flags The container read flags.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T l16_payload_handler(VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ VC_CONTAINER_PACKET_T *p_packet,
+ uint32_t flags)
+{
+ VC_CONTAINER_STATUS_T status;
+
+ /* Most aspects are handled adequately by the generic handler */
+ status = generic_payload_handler(p_ctx, track, p_packet, flags);
+ if (status != VC_CONTAINER_SUCCESS)
+ return status;
+
+ if (p_packet && !(flags & (VC_CONTAINER_READ_FLAG_SKIP | VC_CONTAINER_READ_FLAG_INFO)))
+ {
+ uint8_t *ptr, *end_ptr;
+
+ /* Ensure packet size is even */
+ p_packet->size &= ~1;
+
+ /* Swap bytes of each sample, to get host order instead of network order */
+ for (ptr = p_packet->data, end_ptr = ptr + p_packet->size; ptr < end_ptr; ptr += 2)
+ {
+ uint8_t high_byte = ptr[0];
+ ptr[0] = ptr[1];
+ ptr[1] = high_byte;
+ }
+ }
+
+ return status;
+}
+
+
+/*****************************************************************************
+Utility functions for use by RTP payload handlers
+ *****************************************************************************/
+
+/**************************************************************************//**
+ * Gets the value of a parameter as an unsigned 32-bit decimal integer.
+ *
+ * @param param_list The URI parameter list.
+ * @param name The name of the parameter.
+ * @param value Pointer to the variable to receive the value.
+ * @return True if the parameter value was read and stored correctly, false
+ * otherwise.
+ */
+bool rtp_get_parameter_u32(const VC_CONTAINERS_LIST_T *param_list,
+ const char *name,
+ uint32_t *value)
+{
+ PARAMETER_T param;
+
+ param.name = name;
+ if (vc_containers_list_find_entry(param_list, ¶m) && param.value)
+ {
+ char *end;
+
+ *value = strtoul(param.value, &end, 10);
+ return (end != param.value) && (*end == '\0');
+ }
+
+ return false;
+}
+
+/**************************************************************************//**
+ * Gets the value of a parameter as an unsigned 32-bit hexadecimal integer.
+ *
+ * @param param_list The URI parameter list.
+ * @param name The name of the parameter.
+ * @param value Pointer to the variable to receive the value.
+ * @return True if the parameter value was read and stored correctly, false
+ * otherwise.
+ */
+bool rtp_get_parameter_x32(const VC_CONTAINERS_LIST_T *param_list,
+ const char *name,
+ uint32_t *value)
+{
+ PARAMETER_T param;
+
+ param.name = name;
+ if (vc_containers_list_find_entry(param_list, ¶m) && param.value)
+ {
+ char *end;
+
+ *value = strtoul(param.value, &end, 16);
+ return (end != param.value) && (*end == '\0');
+ }
+
+ return false;
+}
+
+/*****************************************************************************
+Functions exported as part of the Container Module API
+ *****************************************************************************/
+
+/**************************************************************************//**
+ * Read/skip data from the container.
+ * Can also be used to query information about the next block of data.
+ *
+ * @param p_ctx The reader context.
+ * @param p_packet The container packet information, or NULL.
+ * @param flags The container read flags.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtp_reader_read( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_PACKET_T *p_packet,
+ uint32_t flags )
+{
+ VC_CONTAINER_TRACK_T *track;
+ VC_CONTAINER_TRACK_MODULE_T *t_module;
+ VC_CONTAINER_STATUS_T status;
+
+ if((flags & VC_CONTAINER_READ_FLAG_FORCE_TRACK) && p_packet->track)
+ return VC_CONTAINER_ERROR_INVALID_ARGUMENT;
+
+ track = p_ctx->tracks[0];
+ t_module = track->priv->module;
+
+ CLEAR_BIT(t_module->flags, TRACK_NEW_PACKET);
+
+ while (!BITS_AVAILABLE(p_ctx, &t_module->payload))
+ {
+ uint32_t bytes_read;
+
+ /* No data left from last RTP packet, get another one */
+ bytes_read = READ_BYTES(p_ctx, t_module->buffer, MAXIMUM_PACKET_SIZE);
+ if (!bytes_read)
+ return STREAM_STATUS(p_ctx);
+
+ BITS_INIT(p_ctx, &t_module->payload, t_module->buffer, bytes_read);
+
+ decode_rtp_packet_header(p_ctx, t_module);
+ SET_BIT(t_module->flags, TRACK_NEW_PACKET);
+ }
+
+ if (p_packet)
+ {
+ uint32_t timestamp_top = t_module->timestamp >> 30;
+
+ /* Determine whether timestamp has wrapped forwards or backwards around zero */
+ if ((timestamp_top == 0) && (t_module->last_timestamp_top == 3))
+ t_module->timestamp_wraps++;
+ else if ((timestamp_top == 3) && (t_module->last_timestamp_top == 0))
+ t_module->timestamp_wraps--;
+ t_module->last_timestamp_top = timestamp_top;
+
+ p_packet->dts = p_packet->pts = ((int64_t)t_module->timestamp_wraps << 32) | t_module->timestamp;
+ p_packet->track = 0;
+ p_packet->flags = 0;
+ }
+
+ status = t_module->payload_handler(p_ctx, track, p_packet, flags);
+ if (p_packet && status == VC_CONTAINER_SUCCESS)
+ {
+ /* Adjust timestamps from RTP clock rate to microseconds */
+ p_packet->pts = p_packet->pts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock;
+ p_packet->dts = p_packet->dts * MICROSECONDS_PER_SECOND / t_module->timestamp_clock;
+ }
+
+ STREAM_STATUS(p_ctx) = status;
+ return status;
+}
+
+/**************************************************************************//**
+ * Seek over data in the container.
+ *
+ * @param p_ctx The reader context.
+ * @param p_offset The seek offset.
+ * @param mode The seek mode.
+ * @param flags The seek flags.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtp_reader_seek( VC_CONTAINER_T *p_ctx,
+ int64_t *p_offset,
+ VC_CONTAINER_SEEK_MODE_T mode,
+ VC_CONTAINER_SEEK_FLAGS_T flags)
+{
+ VC_CONTAINER_PARAM_UNUSED(p_ctx);
+ VC_CONTAINER_PARAM_UNUSED(p_offset);
+ VC_CONTAINER_PARAM_UNUSED(mode);
+ VC_CONTAINER_PARAM_UNUSED(flags);
+
+ /* RTP is a non-seekable container */
+ return VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+}
+
+/**************************************************************************//**
+ * Apply a control operation to the container.
+ *
+ * @param p_ctx The reader context.
+ * @param operation The control operation.
+ * @param args Optional additional arguments for the operation.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtp_reader_control( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_CONTROL_T operation,
+ va_list args)
+{
+ VC_CONTAINER_STATUS_T status;
+ VC_CONTAINER_TRACK_MODULE_T *t_module = p_ctx->tracks[0]->priv->module;
+
+ switch (operation)
+ {
+ case VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE:
+ {
+ t_module->timestamp_base = va_arg(args, uint32_t);
+ if (!t_module->timestamp_base)
+ t_module->timestamp_base = 1; /* Zero is used to mean "not set" */
+ status = VC_CONTAINER_SUCCESS;
+ }
+ break;
+ case VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER:
+ {
+ init_sequence_number(t_module, (uint16_t)va_arg(args, uint32_t));
+ t_module->probation = 0;
+ status = VC_CONTAINER_SUCCESS;
+ }
+ break;
+ case VC_CONTAINER_CONTROL_SET_SOURCE_ID:
+ {
+ t_module->expected_ssrc = va_arg(args, uint32_t);
+ SET_BIT(t_module->flags, TRACK_SSRC_SET);
+ status = VC_CONTAINER_SUCCESS;
+ }
+ break;
+ default:
+ status = VC_CONTAINER_ERROR_UNSUPPORTED_OPERATION;
+ }
+
+ return status;
+}
+
+/**************************************************************************//**
+ * Close the container.
+ *
+ * @param p_ctx The reader context.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtp_reader_close( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ vc_container_assert(p_ctx->tracks_num < 2);
+
+ if (p_ctx->tracks_num)
+ {
+ void *payload_extra;
+
+ vc_container_assert(module);
+ vc_container_assert(module->track);
+ vc_container_assert(module->track->priv);
+ vc_container_assert(module->track->priv->module);
+
+ payload_extra = module->track->priv->module->extra;
+ if (payload_extra)
+ free(payload_extra);
+ vc_container_free_track(p_ctx, module->track);
+ }
+ p_ctx->tracks = NULL;
+ p_ctx->tracks_num = 0;
+ if (module) free(module);
+ p_ctx->priv->module = 0;
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Open the container.
+ * Uses the I/O URI and/or data to configure the container.
+ *
+ * @param p_ctx The reader context.
+ * @return The resulting status of the function.
+ */
+VC_CONTAINER_STATUS_T rtp_reader_open( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = 0;
+ VC_CONTAINER_TRACK_T *track = 0;
+ VC_CONTAINER_TRACK_MODULE_T *t_module = 0;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ VC_CONTAINERS_LIST_T *parameters = NULL;
+ uint32_t payload_type;
+ uint32_t initial_seq_num;
+
+ /* Check the URI scheme looks valid */
+ if (!vc_uri_scheme(p_ctx->priv->uri) ||
+ (strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTP_SCHEME) &&
+ strcasecmp(vc_uri_scheme(p_ctx->priv->uri), RTP_PKT_SCHEME)))
+ return VC_CONTAINER_ERROR_FORMAT_NOT_SUPPORTED;
+
+ /* Make the query/parameter list more easily searchable */
+ parameters = fill_parameter_list(p_ctx->priv->uri);
+ if (!parameters) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+
+ /* Payload type parameter is mandatory and must fit in 7 bits */
+ if (!rtp_get_parameter_u32(parameters, PAYLOAD_TYPE_NAME, &payload_type) || payload_type > 127)
+ {
+ status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ goto error;
+ }
+
+ /* Allocate our context */
+ module = (VC_CONTAINER_MODULE_T *)malloc(sizeof(VC_CONTAINER_MODULE_T));
+ if (!module) { status = VC_CONTAINER_ERROR_OUT_OF_MEMORY; goto error; }
+
+ memset(module, 0, sizeof(*module));
+ p_ctx->priv->module = module;
+ p_ctx->tracks = &module->track;
+
+ /* Allocate the track, including space for reading an RTP packet on the end */
+ track = vc_container_allocate_track(p_ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T) + MAXIMUM_PACKET_SIZE);
+ if (!track)
+ {
+ status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ goto error;
+ }
+ module->track = track;
+ p_ctx->tracks_num = 1;
+ t_module = track->priv->module;
+
+ /* Initialise the track data */
+ t_module->buffer = (uint8_t *)(t_module + 1);
+ status = decode_payload_type(p_ctx, track, parameters, payload_type);
+ if (status != VC_CONTAINER_SUCCESS)
+ goto error;
+
+ vc_container_assert(t_module->timestamp_clock != 0);
+
+ /* Default to a generic, unstructured payload handler */
+ if (!t_module->payload_handler)
+ t_module->payload_handler = generic_payload_handler;
+
+ if (rtp_get_parameter_x32(parameters, SSRC_NAME, &t_module->expected_ssrc))
+ SET_BIT(t_module->flags, TRACK_SSRC_SET);
+
+ t_module->probation = MIN_SEQUENTIAL;
+ if (rtp_get_parameter_u32(parameters, SEQ_NAME, &initial_seq_num))
+ {
+ /* If an initial sequence number is provided, avoid probation period */
+ t_module->max_seq_num = (uint16_t)initial_seq_num;
+ t_module->probation = 0;
+ }
+
+ track->is_enabled = true;
+
+ vc_containers_list_destroy(parameters);
+
+ p_ctx->priv->pf_close = rtp_reader_close;
+ p_ctx->priv->pf_read = rtp_reader_read;
+ p_ctx->priv->pf_seek = rtp_reader_seek;
+ p_ctx->priv->pf_control = rtp_reader_control;
+
+ return VC_CONTAINER_SUCCESS;
+
+error:
+ if (parameters) vc_containers_list_destroy(parameters);
+ if(status == VC_CONTAINER_SUCCESS || status == VC_CONTAINER_ERROR_EOS)
+ status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ LOG_DEBUG(p_ctx, "error opening RTP (%i)", status);
+ rtp_reader_close(p_ctx);
+ return status;
+}
+
+/********************************************************************************
+ Entrypoint function
+ ********************************************************************************/
+
+#if !defined(ENABLE_CONTAINERS_STANDALONE) && defined(__HIGHC__)
+# pragma weak reader_open rtp_reader_open
+#endif
diff --git a/gfx/include/userland/containers/rtsp/CMakeLists.txt b/gfx/include/userland/containers/rtsp/CMakeLists.txt
new file mode 100644
index 0000000000..5c386f663b
--- /dev/null
+++ b/gfx/include/userland/containers/rtsp/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Container module needs to go in as a plugins so different prefix
+# and install path
+set(CMAKE_SHARED_LIBRARY_PREFIX "")
+
+# Make sure the compiler can find the necessary include files
+include_directories (../..)
+
+set(rtsp_SRCS ${rtsp_SRCS} rtsp_reader.c)
+add_library(reader_rtsp ${LIBRARY_TYPE} ${rtsp_SRCS})
+
+target_link_libraries(reader_rtsp containers)
+
+install(TARGETS reader_rtsp DESTINATION ${VMCS_PLUGIN_DIR})
+
diff --git a/gfx/include/userland/containers/rtsp/rtsp_reader.c b/gfx/include/userland/containers/rtsp/rtsp_reader.c
new file mode 100644
index 0000000000..2d96234b93
--- /dev/null
+++ b/gfx/include/userland/containers/rtsp/rtsp_reader.c
@@ -0,0 +1,1983 @@
+/*
+Copyright (c) 2012, Broadcom Europe Ltd
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of the copyright holder nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#include
+#include
+#include
+#include
+
+#define CONTAINER_IS_BIG_ENDIAN
+//#define ENABLE_CONTAINERS_LOG_FORMAT
+//#define ENABLE_CONTAINERS_LOG_FORMAT_VERBOSE
+#define CONTAINER_HELPER_LOG_INDENT(a) 0
+#include "containers/core/containers_private.h"
+#include "containers/core/containers_io_helpers.h"
+#include "containers/core/containers_utils.h"
+#include "containers/core/containers_logging.h"
+#include "containers/core/containers_list.h"
+#include "containers/core/containers_uri.h"
+
+/******************************************************************************
+Configurable defines and constants.
+******************************************************************************/
+
+/** Maximum number of tracks allowed in an RTSP reader */
+#define RTSP_TRACKS_MAX 4
+
+/** Space for sending requests and receiving responses */
+#define COMMS_BUFFER_SIZE 2048
+
+/** Largest allowed RTSP URI. Must be substantially smaller than COMMS_BUFFER_SIZE
+ * to allow for the headers that may be sent. */
+#define RTSP_URI_LENGTH_MAX 1024
+
+/** Maximum allowed length for the Session: header recevied in a SETUP response,
+ * This is to ensure comms buffer is not overflowed. */
+#define SESSION_HEADER_LENGTH_MAX 100
+
+/** Number of milliseconds to block trying to read from the RTSP stream when no
+ * data is available from any of the tracks */
+#define DATA_UNAVAILABLE_READ_TIMEOUT_MS 1
+
+/** Size of buffer for each track to use when receiving packets */
+#define UDP_READ_BUFFER_SIZE 520000
+
+/* Arbitrary number of different dynamic ports to try */
+#define DYNAMIC_PORT_ATTEMPTS_MAX 16
+
+/******************************************************************************
+Defines and constants.
+******************************************************************************/
+
+#define RTSP_SCHEME "rtsp:"
+#define RTP_SCHEME "rtp"
+
+/** The RTSP PKT scheme is used with test pkt files */
+#define RTSP_PKT_SCHEME "rtsppkt:"
+
+#define RTSP_NETWORK_URI_START "rtsp://"
+#define RTSP_NETWORK_URI_START_LENGTH (sizeof(RTSP_NETWORK_URI_START)-1)
+
+/** Initial capacity of header list */
+#define HEADER_LIST_INITIAL_CAPACITY 16
+
+/** Format of the first line of an RTSP request */
+#define RTSP_REQUEST_LINE_FORMAT "%s %s RTSP/1.0\r\n"
+
+/** Format string for common headers used with all request methods.
+ * Note: includes double new line to terminate headers */
+#define TRAILING_HEADERS_FORMAT "CSeq: %u\r\nConnection: Keep-Alive\r\nUser-Agent: Broadcom/1.0\r\n\r\n"
+
+/** Format for the Transport: header */
+#define TRANSPORT_HEADER_FORMAT "Transport: RTP/AVP;unicast;client_port=%hu-%hu;mode=play\r\n"
+
+/** Format for including Session: header. */
+#define SESSION_HEADER_FORMAT "Session: %s\r\n"
+
+/** \name RTSP methods, used as the first item in the request line
+ * @{ */
+#define DESCRIBE_METHOD "DESCRIBE"
+#define SETUP_METHOD "SETUP"
+#define PLAY_METHOD "PLAY"
+#define TEARDOWN_METHOD "TEARDOWN"
+/* @} */
+
+/** \name Names of headers used by the code
+ * @{ */
+#define CONTENT_PSEUDOHEADER_NAME ":"
+#define CONTENT_LENGTH_NAME "Content-Length"
+#define CONTENT_BASE_NAME "Content-Base"
+#define CONTENT_LOCATION_NAME "Content-Location"
+#define RTP_INFO_NAME "RTP-Info"
+#define SESSION_NAME "Session"
+/* @} */
+
+/** Supported RTSP major version number */
+#define RTSP_MAJOR_VERSION 1
+/** Supported RTSP minor version number */
+#define RTSP_MINOR_VERSION 0
+
+/** Lowest successful status code value */
+#define RTSP_STATUS_OK 200
+/** Next failure status code after the set of successful ones */
+#define RTSP_STATUS_MULTIPLE_CHOICES 300
+
+/** Maximum size of a decimal string representation of a uint16_t, plus NUL */
+#define PORT_BUFFER_SIZE 6
+/** Start of private / dynamic port region */
+#define FIRST_DYNAMIC_PORT 0xC000
+/** End of private / dynamic port region */
+#define LAST_DYNAMIC_PORT 0xFFF0
+
+/** Format of RTP track file extension */
+#define RTP_PATH_EXTENSION_FORMAT ".t%u.pkt"
+/** Extra space need for creating an RTP track file name from an RTSP URI path */
+#define RTP_PATH_EXTRA 17
+
+/** \name RTP URI parameter names
+ * @{ */
+#define PAYLOAD_TYPE_NAME "rtppt"
+#define MIME_TYPE_NAME "mime-type"
+#define SAMPLE_RATE_NAME "rate"
+#define CHANNELS_NAME "channels"
+/* @} */
+
+/** Largest signed 64-bit integer */
+#define MAXIMUM_INT64 (int64_t)((1ULL << 63) - 1)
+
+/******************************************************************************
+Type definitions
+******************************************************************************/
+
+typedef int (*PARSE_IS_DELIMITER_FN_T)(int char_to_test);
+
+typedef struct rtsp_header_tag
+{
+ const char *name;
+ char *value;
+} RTSP_HEADER_T;
+
+typedef struct VC_CONTAINER_TRACK_MODULE_T
+{
+ VC_CONTAINER_T *reader; /**< RTP reader for track */
+ VC_URI_PARTS_T *reader_uri; /**< URI built up from SDP and used to open reader */
+ char *control_uri; /**< URI used to control track playback */
+ char *session_header; /**< Session header to be used when sending control requests */
+ char *payload_type; /**< RTP payload type for track */
+ char *media_type; /**< MIME type for track */
+ VC_CONTAINER_PACKET_T info; /**< Latest track packet info block */
+ unsigned short rtp_port; /**< UDP listener port being used in RTP reader */
+} VC_CONTAINER_TRACK_MODULE_T;
+
+typedef struct VC_CONTAINER_MODULE_T
+{
+ VC_CONTAINER_TRACK_T *tracks[RTSP_TRACKS_MAX];
+ char *comms_buffer; /**< Buffer used for sending and receiving RTSP messages */
+ VC_CONTAINERS_LIST_T *header_list; /**< Parsed response headers, pointing into comms buffer */
+ uint32_t cseq_value; /**< CSeq header value for next request */
+ uint16_t next_rtp_port; /**< Next RTP port to use when opening track reader */
+ uint16_t media_item; /**< Current media item number during initialization */
+ bool uri_has_network_info; /**< True if the RTSP URI contains network info */
+ int64_t ts_base; /**< Base value for dts and pts */
+ VC_CONTAINER_TRACK_MODULE_T *current_track; /**< Next track to be read, to keep info/data on same track */
+} VC_CONTAINER_MODULE_T;
+
+/******************************************************************************
+Function prototypes
+******************************************************************************/
+static int rtsp_header_comparator(const RTSP_HEADER_T *first, const RTSP_HEADER_T *second);
+
+VC_CONTAINER_STATUS_T rtsp_reader_open( VC_CONTAINER_T * );
+
+/******************************************************************************
+Local Functions
+******************************************************************************/
+
+/**************************************************************************//**
+ * Trim whitespace from the end and start of the string
+ *
+ * \param str String to be trimmed
+ * \return Trimmed string
+ */
+static char *rtsp_trim( char *str )
+{
+ char *trim = str + strlen(str);
+
+ /* Search backwards for first non-whitespace */
+ while (--trim >= str && isspace((int)*trim))
+ ; /* Everything done in the while */
+ trim[1] = '\0';
+
+ /* Now move start of string forwards to first non-whitespace */
+ trim = str;
+ while (isspace((int)*trim))
+ trim++;
+
+ return trim;
+}
+
+/**************************************************************************//**
+ * Send out the data in the comms buffer.
+ *
+ * @param p_ctx The reader context.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_send( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ uint32_t to_write;
+ uint32_t written;
+ const char *buffer = module->comms_buffer;
+
+ /* When reading from a captured file, do not attempt to send data */
+ if (!module->uri_has_network_info)
+ return VC_CONTAINER_SUCCESS;
+
+ to_write = strlen(buffer);
+
+ while (to_write)
+ {
+ written = vc_container_io_write(p_ctx->priv->io, buffer, to_write);
+ if (!written)
+ break;
+ to_write -= written;
+ buffer += written;
+ }
+
+ return p_ctx->priv->io->status;
+}
+
+/**************************************************************************//**
+ * Send a DESCRIBE request to the RTSP server.
+ *
+ * @param p_ctx The reader context.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_send_describe_request( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE;
+ char *uri = p_ctx->priv->io->uri;
+
+ if (strlen(uri) > RTSP_URI_LENGTH_MAX)
+ {
+ LOG_ERROR(p_ctx, "RTSP: URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX);
+ return VC_CONTAINER_ERROR_URI_OPEN_FAILED;
+ }
+
+ ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, DESCRIBE_METHOD, uri);
+ if (ptr < end)
+ ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++);
+ vc_container_assert(ptr < end);
+
+ return rtsp_send(p_ctx);
+}
+
+/**************************************************************************//**
+ * Send a SETUP request to the RTSP server.
+ *
+ * @param p_ctx The reader context.
+ * @param t_module The track module relating to the SETUP.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_send_setup_request( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_MODULE_T *t_module)
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE;
+ char *uri = t_module->control_uri;
+
+ if (strlen(uri) > RTSP_URI_LENGTH_MAX)
+ {
+ LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX);
+ return VC_CONTAINER_ERROR_URI_OPEN_FAILED;
+ }
+
+ ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, SETUP_METHOD, uri);
+ if (ptr < end)
+ ptr += snprintf(ptr, end - ptr, TRANSPORT_HEADER_FORMAT, t_module->rtp_port, t_module->rtp_port + 1);
+ if (ptr < end)
+ ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++);
+ vc_container_assert(ptr < end);
+
+ return rtsp_send(p_ctx);
+}
+
+/**************************************************************************//**
+ * Send a PLAY request to the RTSP server.
+ *
+ * @param p_ctx The reader context.
+ * @param t_module The track module relating to the PLAY.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_send_play_request( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_MODULE_T *t_module )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE;
+ char *uri = t_module->control_uri;
+
+ if (strlen(uri) > RTSP_URI_LENGTH_MAX)
+ {
+ LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX);
+ return VC_CONTAINER_ERROR_URI_OPEN_FAILED;
+ }
+
+ ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, PLAY_METHOD, uri);
+ if (ptr < end)
+ ptr += snprintf(ptr, end - ptr, SESSION_HEADER_FORMAT, t_module->session_header);
+ if (ptr < end)
+ ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++);
+ vc_container_assert(ptr < end);
+
+ return rtsp_send(p_ctx);
+}
+
+/**************************************************************************//**
+ * Send a TEARDOWN request to the RTSP server.
+ *
+ * @param p_ctx The reader context.
+ * @param t_module The track module relating to the SETUP.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_send_teardown_request( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_MODULE_T *t_module )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ char *ptr = module->comms_buffer, *end = ptr + COMMS_BUFFER_SIZE;
+ char *uri = t_module->control_uri;
+
+ if (strlen(uri) > RTSP_URI_LENGTH_MAX)
+ {
+ LOG_ERROR(p_ctx, "RTSP: Control URI is too long (%d>%d)", strlen(uri), RTSP_URI_LENGTH_MAX);
+ return VC_CONTAINER_ERROR_URI_OPEN_FAILED;
+ }
+
+ ptr += snprintf(ptr, end - ptr, RTSP_REQUEST_LINE_FORMAT, TEARDOWN_METHOD, uri);
+ if (ptr < end)
+ ptr += snprintf(ptr, end - ptr, SESSION_HEADER_FORMAT, t_module->session_header);
+ if (ptr < end)
+ ptr += snprintf(ptr, end - ptr, TRAILING_HEADERS_FORMAT, module->cseq_value++);
+ vc_container_assert(ptr < end);
+
+ return rtsp_send(p_ctx);
+}
+
+/**************************************************************************//**
+ * Check a response status line to see if the response is usable or not.
+ * Reasons for invalidity include:
+ * - Incorrectly formatted
+ * - Unsupported version
+ * - Status code is not in the 2xx range
+ *
+ * @param p_ctx The reader context.
+ * @param status_line The response status line.
+ * @return The resulting status of the function.
+ */
+static bool rtsp_successful_response_status( VC_CONTAINER_T *p_ctx,
+ const char *status_line)
+{
+ unsigned int major_version, minor_version, status_code;
+
+ /* coverity[secure_coding] String is null-terminated */
+ if (sscanf(status_line, "RTSP/%u.%u %u", &major_version, &minor_version, &status_code) != 3)
+ {
+ LOG_ERROR(p_ctx, "RTSP: Invalid response status line:\n%s", status_line);
+ return false;
+ }
+
+ if (major_version != RTSP_MAJOR_VERSION || minor_version != RTSP_MINOR_VERSION)
+ {
+ LOG_ERROR(p_ctx, "RTSP: Unexpected response RTSP version: %u.%u", major_version, minor_version);
+ return false;
+ }
+
+ if (status_code < RTSP_STATUS_OK || status_code >= RTSP_STATUS_MULTIPLE_CHOICES)
+ {
+ LOG_ERROR(p_ctx, "RTSP: Response status unsuccessful:\n%s", status_line);
+ return false;
+ }
+
+ return true;
+}
+
+/**************************************************************************//**
+ * Get the content length header from the response headers as an unsigned
+ * 32-bit integer.
+ * If the content length header is not found or badly formatted, zero is
+ * returned.
+ *
+ * @param header_list The response headers.
+ * @return The content length.
+ */
+static uint32_t rtsp_get_content_length( VC_CONTAINERS_LIST_T *header_list )
+{
+ unsigned int content_length = 0;
+ RTSP_HEADER_T header;
+
+ header.name = CONTENT_LENGTH_NAME;
+ if (header_list && vc_containers_list_find_entry(header_list, &header))
+ /* coverity[secure_coding] String is null-terminated */
+ sscanf(header.value, "%u", &content_length);
+
+ return content_length;
+}
+
+/**************************************************************************//**
+ * Get the session header from the response headers.
+ * If the session header is not found, the empty string is returned.
+ *
+ * @param header_list The response headers.
+ * @return The session header.
+ */
+static const char *rtsp_get_session_header(VC_CONTAINERS_LIST_T *header_list)
+{
+ RTSP_HEADER_T header;
+
+ header.name = SESSION_NAME;
+ if (header_list && vc_containers_list_find_entry(header_list, &header))
+ return header.value;
+
+ return "";
+}
+
+/**************************************************************************//**
+ * Returns pointer to the string with any leading whitespace trimmed and
+ * terminated by a delimiter, as determined by is_delimiter_fn. The delimiter
+ * character replaced by NUL (to terminate the string) is returned in the
+ * variable pointed at by p_delimiter_replaced, if it is not NULL.
+ *
+ * The parse_str pointer is moved on to the character after the delimiter, or
+ * the end of the string if it is reached first.
+ *
+ * @param parse_str Pointer to the string pointer to parse.
+ * @param is_delimiter_fn Function to test if a character is a delimiter or not.
+ * @param p_delimiter_replaced Pointer to variable to receive delimiter character, or NULL.
+ * @return Pointer to extracted string.
+ */
+static char *rtsp_parse_extract(char **parse_str,
+ PARSE_IS_DELIMITER_FN_T is_delimiter_fn,
+ char *p_delimiter_replaced)
+{
+ char *ptr;
+ char *result;
+
+ vc_container_assert(parse_str);
+ vc_container_assert(*parse_str);
+ vc_container_assert(is_delimiter_fn);
+
+ ptr = *parse_str;
+
+ while (isspace((int)*ptr))
+ ptr++;
+
+ result = ptr;
+
+ while (*ptr && !(*is_delimiter_fn)(*ptr))
+ ptr++;
+ if (p_delimiter_replaced)
+ *p_delimiter_replaced = *ptr;
+ if (*ptr)
+ *ptr++ = '\0';
+
+ *parse_str = ptr;
+ return result;
+}
+
+/**************************************************************************//**
+ * Specialised form of rtsp_parse_extract() where the delimiter is whitespace.
+ * Returns pointer to the string with any leading whitespace trimmed and
+ * terminated by further whitespace.
+ *
+ * The parse_str pointer is moved on to the character after the delimiter, or
+ * the end of the string if it is reached first.
+ *
+ * @param parse_str Pointer to the string pointer to parse.
+ * @return Pointer to extracted string.
+ */
+static char *rtsp_parse_extract_ws(char **parse_str)
+{
+ char *ptr;
+ char *result;
+
+ vc_container_assert(parse_str);
+ vc_container_assert(*parse_str);
+
+ ptr = *parse_str;
+
+ while (isspace((int)*ptr))
+ ptr++;
+
+ result = ptr;
+
+ while (*ptr && !isspace((int)*ptr))
+ ptr++;
+ if (*ptr)
+ *ptr++ = '\0';
+
+ *parse_str = ptr;
+ return result;
+}
+
+/**************************************************************************//**
+ * Returns whether the given character is a parameter name delimiter or not.
+ *
+ * @param char_to_test The character under test.
+ * @return True if the character is a name delimiter, false if not.
+ */
+static int name_delimiter_fn(int char_to_test)
+{
+ switch (char_to_test)
+ {
+ case ' ':
+ case '\t':
+ case '=':
+ case ';':
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**************************************************************************//**
+ * Returns whether the given character is a parameter value delimiter or not.
+ *
+ * @param char_to_test The character under test.
+ * @return True if the character is a value delimiter, false if not.
+ */
+static int value_delimiter_fn(int char_to_test)
+{
+ switch (char_to_test)
+ {
+ case ' ':
+ case '\t':
+ case ';':
+ return true;
+ default:
+ return false;
+ }
+}
+
+/**************************************************************************//**
+ * Extract a name/value pair from a given string.
+ * Each pair consists of a name, optionally followed by '=' and a value, with
+ * optional whitespace around either or both name and value. The parameter is
+ * terminated by a semi-colon, ';'.
+ *
+ * The parse_str pointer is moved on to the next parameter, or the end of the
+ * string if that is reached first.
+ *
+ * Name can be empty if there are two consecutive semi-colons, or a trailing
+ * semi-colon.
+ *
+ * @param parse_str Pointer to the string pointer to be parsed.
+ * @param p_name Pointer to where name string pointer shall be written.
+ * @param p_value Pointer to where value string pointer shall be written.
+ * @return True if the name is not empty.
+ */
+static bool rtsp_parse_extract_parameter(char **parse_str, char **p_name, char **p_value)
+{
+ char delimiter;
+
+ vc_container_assert(parse_str);
+ vc_container_assert(*parse_str);
+ vc_container_assert(p_name);
+ vc_container_assert(p_value);
+
+ /* General form of each parameter:
+ * [=]
+ * but allow for spaces before and after name and value */
+ *p_name = rtsp_parse_extract(parse_str, name_delimiter_fn, &delimiter);
+ if (isspace((int)delimiter))
+ {
+ /* Skip further spaces after parameter name */
+ do {
+ delimiter = **parse_str;
+ if (delimiter)
+ (*parse_str)++;
+ } while (isspace((int)delimiter));
+ }
+
+ if (delimiter == '=')
+ {
+ /* Parameter value present (although may be empty) */
+ *p_value = rtsp_parse_extract(parse_str, value_delimiter_fn, &delimiter);
+ if (isspace((int)delimiter))
+ {
+ /* Skip spaces after parameter value */
+ do {
+ delimiter = **parse_str;
+ if (delimiter)
+ (*parse_str)++;
+ } while (isspace((int)delimiter));
+ }
+ } else {
+ *p_value = NULL;
+ }
+
+ return (**p_name != '\0');
+}
+
+/**************************************************************************//**
+ * Parses RTP-Info header and stores relevant parts.
+ *
+ * @param header_list The response header list.
+ * @param t_module The track module relating to the response headers.
+ */
+static void rtsp_store_rtp_info(VC_CONTAINERS_LIST_T *header_list,
+ VC_CONTAINER_TRACK_MODULE_T *t_module )
+{
+ RTSP_HEADER_T header;
+ char *ptr;
+
+ header.name = RTP_INFO_NAME;
+ if (!vc_containers_list_find_entry(header_list, &header))
+ return;
+
+ ptr = header.value;
+ while (ptr && *ptr)
+ {
+ char *name;
+ char *value;
+
+ if (!rtsp_parse_extract_parameter(&ptr, &name, &value))
+ continue;
+
+ if (strcasecmp(name, "rtptime") == 0)
+ {
+ unsigned int timestamp_base = 0;
+
+ /* coverity[secure_coding] String is null-terminated */
+ if (sscanf(value, "%u", ×tamp_base) == 1)
+ (void)vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_SET_TIMESTAMP_BASE, timestamp_base);
+ }
+ else if (strcasecmp(name, "seq") == 0)
+ {
+ unsigned short int sequence_number = 0;
+
+ /* coverity[secure_coding] String is null-terminated */
+ if (sscanf(value, "%hu", &sequence_number) == 1)
+ (void)vc_container_control(t_module->reader, VC_CONTAINER_CONTROL_SET_NEXT_SEQUENCE_NUMBER, (uint32_t)sequence_number);
+ }
+ }
+}
+
+/**************************************************************************//**
+ * Reads an RTSP response and parses it into headers and content.
+ * The headers and content remain stored in the comms buffer, but referenced
+ * by the module's header list. Content uses a special header name that cannot
+ * occur in the real headers.
+ *
+ * @param p_ctx The RTSP reader context.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_read_response( VC_CONTAINER_T *p_ctx )
+{
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+ VC_CONTAINER_IO_T *p_ctx_io = p_ctx->priv->io;
+ char *next_read = module->comms_buffer;
+ uint32_t space_available = COMMS_BUFFER_SIZE - 1; /* Allow for a NUL */
+ uint32_t received;
+ char *ptr = next_read;
+ bool found_content = false;
+ RTSP_HEADER_T header;
+
+ vc_containers_list_reset(module->header_list);
+
+ /* Response status line doesn't need to be stored, just checked */
+ header.name = NULL;
+ header.value = next_read;
+
+ while (space_available)
+ {
+ received = vc_container_io_read(p_ctx_io, next_read, space_available);
+ if (p_ctx_io->status != VC_CONTAINER_SUCCESS)
+ break;
+
+ next_read += received;
+ space_available -= received;
+
+ while (!found_content && ptr < next_read)
+ {
+ switch (*ptr)
+ {
+ case ':':
+ if (header.value)
+ {
+ /* Just another character in the value */
+ ptr++;
+ } else {
+ /* End of name, expect value next */
+ *ptr++ = '\0';
+ header.value = ptr;
+ }
+ break;
+
+ case '\n':
+ if (header.value)
+ {
+ /* End of line while parsing the value part of the header, add name/value pair to list */
+ *ptr++ = '\0';
+ header.value = rtsp_trim(header.value);
+ if (header.name)
+ {
+ if (!vc_containers_list_insert(module->header_list, &header, false))
+ {
+ LOG_ERROR(p_ctx, "RTSP: Failed to add <%s> header to list", header.name);
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ }
+ } else {
+ /* Check response status line */
+ if (!rtsp_successful_response_status(p_ctx, header.value))
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+ /* Ready for next header */
+ header.name = ptr;
+ header.value = NULL;
+ } else {
+ uint32_t content_length;
+
+ /* End of line while parsing the name of a header */
+ *ptr++ = '\0';
+ if (*header.name && *header.name != '\r')
+ {
+ /* A non-empty name is invalid, so fail */
+ LOG_ERROR(p_ctx, "RTSP: Invalid name in header - no colon:\n%s", header.name);
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ /* An empty name signifies the start of the content has been found */
+ found_content = true;
+
+ /* Make a pseudo-header for the content and add it to the list */
+ header.name = CONTENT_PSEUDOHEADER_NAME;
+ header.value = ptr;
+ if (!vc_containers_list_insert(module->header_list, &header, false))
+ {
+ LOG_ERROR(p_ctx, "RTSP: Failed to add content pseudoheader to list");
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* Calculate how much content there is left to read, based on Content-Length header */
+ content_length = rtsp_get_content_length(module->header_list);
+ if (ptr + content_length < next_read)
+ {
+ /* Final content byte already present, with extra data after it */
+ space_available = 0;
+ } else {
+ uint32_t content_to_read = content_length - (next_read - ptr);
+
+ if (content_to_read >= space_available)
+ {
+ LOG_ERROR(p_ctx, "RTSP: Not enough room to read content");
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* Restrict further reading to the number of content bytes left */
+ space_available = content_to_read;
+ }
+ }
+ break;
+
+ default:
+ /* Just another character in either the name or the value */
+ ptr++;
+ }
+ }
+ }
+
+ if (!space_available)
+ {
+ if (found_content)
+ {
+ /* Terminate content region */
+ *next_read = '\0';
+ } else {
+ /* Ran out of buffer space and never found the content */
+ LOG_ERROR(p_ctx, "RTSP: Response header section too big / content missing");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+ }
+
+ return p_ctx_io->status;
+}
+
+/**************************************************************************//**
+ * Creates a new track from an SDP media field.
+ * Limitation: only the first payload type of the field is used.
+ *
+ * @param p_ctx The RTSP reader context.
+ * @param media The media field.
+ * @param p_track Pointer to the variable to receive the new track pointer.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_create_track_for_media_field(VC_CONTAINER_T *p_ctx,
+ char *media,
+ VC_CONTAINER_TRACK_T **p_track )
+{
+ VC_CONTAINER_TRACK_T *track = NULL;
+ VC_CONTAINER_TRACK_MODULE_T *t_module = NULL;
+ char *ptr = media;
+ char *media_type;
+ char *rtp_port;
+ char *transport_type;
+ char *payload_type;
+
+ *p_track = NULL;
+ if (p_ctx->tracks_num == RTSP_TRACKS_MAX)
+ {
+ LOG_DEBUG(p_ctx, "RTSP: Too many media items in SDP data, only %d are supported.", RTSP_TRACKS_MAX);
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ /* Format of media item:
+ * m=
+ * Only RTP/AVP transport and the first payload type are supported */
+ media_type = rtsp_parse_extract_ws(&ptr);
+ rtp_port = rtsp_parse_extract_ws(&ptr);
+ transport_type = rtsp_parse_extract_ws(&ptr);
+ payload_type = rtsp_parse_extract_ws(&ptr);
+ if (!*media_type || !*rtp_port || strcmp(transport_type, "RTP/AVP") || !*payload_type)
+ {
+ LOG_ERROR(p_ctx, "RTSP: Failure to parse media field");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ track = vc_container_allocate_track(p_ctx, sizeof(VC_CONTAINER_TRACK_MODULE_T));
+ if (!track) goto out_of_memory_error;
+ t_module = track->priv->module;
+
+ /* If the port specifier is invalid, treat it as if it were zero */
+ /* coverity[secure_coding] String is null-terminated */
+ sscanf(rtp_port, "%hu", &t_module->rtp_port);
+ t_module->payload_type = payload_type;
+ t_module->media_type = media_type;
+
+ t_module->reader_uri = vc_uri_create();
+ if (!t_module->reader_uri) goto out_of_memory_error;
+ if (!vc_uri_set_scheme(t_module->reader_uri, RTP_SCHEME)) goto out_of_memory_error;
+ if (!vc_uri_add_query(t_module->reader_uri, PAYLOAD_TYPE_NAME, payload_type)) goto out_of_memory_error;
+
+ p_ctx->tracks[p_ctx->tracks_num++] = track;
+ *p_track = track;
+ return VC_CONTAINER_SUCCESS;
+
+out_of_memory_error:
+ if (track)
+ {
+ if (t_module->reader_uri)
+ vc_uri_release(t_module->reader_uri);
+ vc_container_free_track(p_ctx, track);
+ }
+ LOG_ERROR(p_ctx, "RTSP: Memory allocation failure creating track");
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+}
+
+/**************************************************************************//**
+ * Returns whether the given character is a slash or not.
+ *
+ * @param char_to_test The character under test.
+ * @return True if the character is a slash, false if not.
+ */
+static int slash_delimiter_fn(int char_to_test)
+{
+ return char_to_test == '/';
+}
+
+/**************************************************************************//**
+ * Parse an rtpmap attribute and store values in the related track.
+ *
+ * @param p_ctx The RTSP reader context.
+ * @param track The track relating to the rtpmap.
+ * @param attribute The rtpmap attribute value.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_parse_rtpmap_attribute( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ char *attribute )
+{
+ VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module;
+ char *ptr = attribute;
+ char *payload_type;
+ char *mime_sub_type;
+ char *sample_rate;
+ char *full_mime_type;
+ char *channels;
+
+ /* rtpmap attribute format:
+ * /[/]
+ * Payload type must match the one used in the media field */
+ payload_type = rtsp_parse_extract_ws(&ptr);
+ if (strcmp(payload_type, t_module->payload_type))
+ {
+ /* Ignore any unsupported secondary payload type attributes */
+ LOG_DEBUG(p_ctx, "RTSP: Secondary payload type attribute - not supported");
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ mime_sub_type = rtsp_parse_extract(&ptr, slash_delimiter_fn, NULL);
+ if (!*mime_sub_type)
+ {
+ LOG_ERROR(p_ctx, "RTSP: rtpmap: MIME type missing");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ sample_rate = rtsp_parse_extract(&ptr, slash_delimiter_fn, NULL);
+ if (!*sample_rate)
+ {
+ LOG_ERROR(p_ctx, "RTSP: rtpmap: sample rate missing");
+ return VC_CONTAINER_ERROR_FORMAT_INVALID;
+ }
+
+ full_mime_type = (char *)malloc(strlen(t_module->media_type) + strlen(mime_sub_type) + 2);
+ if (!full_mime_type)
+ {
+ LOG_ERROR(p_ctx, "RTSP: Failed to allocate space for full MIME type");
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ }
+ /* coverity[secure_coding] String has been allocated of the right size */
+ sprintf(full_mime_type, "%s/%s", t_module->media_type, mime_sub_type);
+ if (!vc_uri_add_query(t_module->reader_uri, MIME_TYPE_NAME, full_mime_type))
+ {
+ free(full_mime_type);
+ LOG_ERROR(p_ctx, "RTSP: Failed to add MIME type to URI");
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ }
+ free(full_mime_type);
+
+ if (!vc_uri_add_query(t_module->reader_uri, SAMPLE_RATE_NAME, sample_rate))
+ {
+ LOG_ERROR(p_ctx, "RTSP: Failed to add sample rate to URI");
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ }
+
+ /* Optional channels specifier */
+ channels = rtsp_parse_extract_ws(&ptr);
+ if (*channels)
+ {
+ if (!vc_uri_add_query(t_module->reader_uri, CHANNELS_NAME, channels))
+ {
+ LOG_ERROR(p_ctx, "RTSP: Failed to add channels to URI");
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Parse an fmtp attribute and store values in the related track.
+ *
+ * @param p_ctx The RTSP reader context.
+ * @param track The track relating to the fmtp.
+ * @param attribute The fmtp attribute value.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_parse_fmtp_attribute( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_T *track,
+ char *attribute )
+{
+ VC_CONTAINER_TRACK_MODULE_T *t_module = track->priv->module;
+ char *ptr = attribute;
+ char *payload_type;
+
+ /* fmtp attribute format:
+ *
+ * The payload type must match the first one in the media field, parameters
+ * are semi-colon separated and may have additional whitespace around them. */
+
+ payload_type = rtsp_parse_extract_ws(&ptr);
+ if (strcmp(payload_type, t_module->payload_type))
+ {
+ /* Ignore any unsupported secondary payload type attributes */
+ LOG_DEBUG(p_ctx, "RTSP: Secondary payload type attribute - not supported");
+ return VC_CONTAINER_SUCCESS;
+ }
+
+ while (*ptr)
+ {
+ char *name;
+ char *value;
+
+ /* Only add the parameter if the name was not empty. This avoids problems with
+ * strings like ";;", ";" or ";=value;" */
+ if (rtsp_parse_extract_parameter(&ptr, &name, &value))
+ {
+ if (!vc_uri_add_query(t_module->reader_uri, name, value))
+ {
+ if (value)
+ LOG_ERROR(p_ctx, "RTSP: Failed to add <%s>=<%s> query to URI", name, value);
+ else
+ LOG_ERROR(p_ctx, "RTSP: Failed to add <%s> query to URI", name);
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ }
+
+ return VC_CONTAINER_SUCCESS;
+}
+
+/**************************************************************************//**
+ * Merge base URI and relative URI strings into a merged URI string.
+ * Always creates a new string, even if the relative URI is actually absolute.
+ *
+ * @param p_ctx The RTSP reader context.
+ * @param base_uri_str The base URI string.
+ * @param relative_uri_str The relative URI string.
+ * @param p_merged_uri_str Pointer to where to put the merged string pointer.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_merge_uris( VC_CONTAINER_T *p_ctx,
+ const char *base_uri_str,
+ const char *relative_uri_str,
+ char **p_merged_uri_str)
+{
+ VC_URI_PARTS_T *base_uri = NULL;
+ VC_URI_PARTS_T *relative_uri = NULL;
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ uint32_t merged_size;
+
+ *p_merged_uri_str = NULL;
+ relative_uri = vc_uri_create();
+ if (!relative_uri) goto tidy_up;
+ if (!vc_uri_parse(relative_uri, relative_uri_str))
+ {
+ status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ goto tidy_up;
+ }
+
+ if (vc_uri_scheme(relative_uri) != NULL)
+ {
+ /* URI is absolute, not relative, so return it as the merged URI */
+ size_t len = strlen(relative_uri_str);
+
+ *p_merged_uri_str = (char *)malloc(len + 1);
+ if (!*p_merged_uri_str) goto tidy_up;
+
+ strncpy(*p_merged_uri_str, relative_uri_str, len);
+ status = VC_CONTAINER_SUCCESS;
+ goto tidy_up;
+ }
+
+ base_uri = vc_uri_create();
+ if (!base_uri) goto tidy_up;
+ if (!vc_uri_parse(base_uri, base_uri_str))
+ {
+ status = VC_CONTAINER_ERROR_FORMAT_INVALID;
+ goto tidy_up;
+ }
+
+ /* Build up merged URI in relative_uri, using base_uri as necessary */
+ if (!vc_uri_merge(base_uri, relative_uri)) goto tidy_up;
+
+ merged_size = vc_uri_build(relative_uri, NULL, 0) + 1;
+ *p_merged_uri_str = (char *)malloc(merged_size);
+ if (!*p_merged_uri_str) goto tidy_up;
+
+ vc_uri_build(relative_uri, *p_merged_uri_str, merged_size);
+
+ status = VC_CONTAINER_SUCCESS;
+
+tidy_up:
+ if (base_uri) vc_uri_release(base_uri);
+ if (relative_uri) vc_uri_release(relative_uri);
+ if (status != VC_CONTAINER_SUCCESS)
+ LOG_ERROR(p_ctx, "RTSP: Error merging URIs: %d", (int)status);
+ return status;
+}
+
+/**************************************************************************//**
+ * Parse a control attribute and store it as an absolute URI.
+ *
+ * @param p_ctx The RTSP reader context.
+ * @param attribute The control attribute value.
+ * @param base_uri_str The base URI string.
+ * @param p_control_uri_str Pointer to where to put the control string pointer.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_parse_control_attribute( VC_CONTAINER_T *p_ctx,
+ const char *attribute,
+ const char *base_uri_str,
+ char **p_control_uri_str)
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+
+ /* control attribute format:
+ *
+ * The control URI is either absolute or relative to the base URI. If the
+ * control URI is just an asterisk, the control URI matches the base URI. */
+
+ if (!*attribute || strcmp(attribute, "*") == 0)
+ {
+ size_t len = strlen(base_uri_str);
+
+ *p_control_uri_str = (char *)malloc(len + 1);
+ if (!*p_control_uri_str)
+ {
+ LOG_ERROR(p_ctx, "RTSP: Failed to allocate control URI");
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ }
+ strncpy(*p_control_uri_str, base_uri_str, len);
+ } else {
+ status = rtsp_merge_uris(p_ctx, base_uri_str, attribute, p_control_uri_str);
+ }
+
+ return status;
+}
+
+/**************************************************************************//**
+ * Open a reader for the track using the URI that has been generated.
+ *
+ * @param p_ctx The RTSP reader context.
+ * @param t_module The track module for which a reader is needed.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_open_track_reader( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_MODULE_T *t_module )
+{
+ VC_CONTAINER_STATUS_T status = VC_CONTAINER_SUCCESS;
+ uint32_t uri_buffer_size;
+ char *uri_buffer;
+
+ uri_buffer_size = vc_uri_build(t_module->reader_uri, NULL, 0) + 1;
+ uri_buffer = (char *)malloc(uri_buffer_size);
+ if (!uri_buffer)
+ {
+ LOG_ERROR(p_ctx, "RTSP: Failed to build RTP URI");
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ }
+ vc_uri_build(t_module->reader_uri, uri_buffer, uri_buffer_size);
+
+ t_module->reader = vc_container_open_reader(uri_buffer, &status, NULL, NULL);
+ free(uri_buffer);
+
+ return status;
+}
+
+/**************************************************************************//**
+ * Open a reader for the track using the network URI that has been generated.
+ *
+ * @param p_ctx The RTSP reader context.
+ * @param t_module The track module for which a reader is needed.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_open_network_reader( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_MODULE_T *t_module )
+{
+ char port[PORT_BUFFER_SIZE] = {0};
+
+ if (!t_module->rtp_port)
+ {
+ VC_CONTAINER_MODULE_T *module = p_ctx->priv->module;
+
+ t_module->rtp_port = module->next_rtp_port;
+ if (t_module->rtp_port > LAST_DYNAMIC_PORT)
+ {
+ LOG_ERROR(p_ctx, "RTSP: Out of dynamic ports");
+ return VC_CONTAINER_ERROR_OUT_OF_RESOURCES;
+ }
+
+ module->next_rtp_port += 2;
+ }
+
+ snprintf(port, sizeof(port)-1, "%hu", t_module->rtp_port);
+ if (!vc_uri_set_port(t_module->reader_uri, port))
+ {
+ LOG_ERROR(p_ctx, "RTSP: Failed to set track reader URI port");
+ return VC_CONTAINER_ERROR_OUT_OF_MEMORY;
+ }
+
+ return rtsp_open_track_reader(p_ctx, t_module);
+}
+
+/**************************************************************************//**
+ * Open a reader for the track using the file URI that has been generated.
+ *
+ * @param p_ctx The RTSP reader context.
+ * @param t_module The track module for which a reader is needed.
+ * @return The resulting status of the function.
+ */
+static VC_CONTAINER_STATUS_T rtsp_open_file_reader( VC_CONTAINER_T *p_ctx,
+ VC_CONTAINER_TRACK_MODULE_T *t_module )
+{
+ VC_CONTAINER_STATUS_T status;
+ VC_URI_PARTS_T *rtsp_uri = NULL;
+ const char *rtsp_path;
+ int len;
+ char *new_path = NULL;
+ char *extension;
+
+ /* Use the RTSP URI's path, with the extension changed to ".t