1249 lines
41 KiB
Diff
Executable File
1249 lines
41 KiB
Diff
Executable File
diff --git a/configure.ac b/configure.ac
|
|
--- a/configure.ac
|
|
+++ b/configure.ac
|
|
@@ -322,6 +322,15 @@
|
|
LDFLAGS="${SAVED_LDFLAGS}" LIBS="${SAVED_LIBS}"])
|
|
|
|
dnl *** set variables based on configure arguments ***
|
|
+AC_ARG_ENABLE(isoplayready,
|
|
+ [AC_HELP_STRING([--enable-isoplayready],[enable isoplayready build])],
|
|
+ [ case "${enableval}" in
|
|
+ yes) isoplayready=true ;;
|
|
+ no) isoplayready=false ;;
|
|
+ *) AC_MSG_ERROR(bad value ${enableval} for --enable-isoplayready) ;;
|
|
+ esac],
|
|
+ [isoplayready=false])
|
|
+ AM_CONDITIONAL([CONFIG_PLAYREADY], [test x$isoplayready = xtrue])
|
|
|
|
dnl set license and copyright notice
|
|
GST_LICENSE="LGPL"
|
|
diff --git a/gst/isomp4/Makefile.am b/gst/isomp4/Makefile.am
|
|
index d76cb42..cfc5754 100644
|
|
--- a/gst/isomp4/Makefile.am
|
|
+++ b/gst/isomp4/Makefile.am
|
|
@@ -11,11 +11,29 @@
|
|
-lgsttag-@GST_API_VERSION@ \
|
|
-lgstpbutils-@GST_API_VERSION@ \
|
|
$(GST_BASE_LIBS) $(GST_LIBS) $(ZLIB_LIBS)
|
|
+
|
|
+if CONFIG_PLAYREADY
|
|
+libgstisomp4_la_CFLAGS += \
|
|
+ -I$(STAGING_DIR)/usr/include/ \
|
|
+ -I$(STAGING_DIR)/usr/include/inc \
|
|
+ -I$(STAGING_DIR)/usr/include/oem/common/inc \
|
|
+ -I$(STAGING_DIR)/usr/include/oem/linux/inc \
|
|
+ -I$(STAGING_DIR)/usr/include/results \
|
|
+ -I$(STAGING_DIR)/usr/include/tools/shared/common \
|
|
+ -I$(STAGING_DIR)/usr/include/tools/shared/netio \
|
|
+ -DADJUST_ADDRESS_FOR_SECURE_OS_OPTEE \
|
|
+ -DDRM_BUILD_PROFILE=900 \
|
|
+ -DARM=1 \
|
|
+ -DCONFIG_PLAYREADY
|
|
+
|
|
+libgstisomp4_la_LIBADD += -lplayreadypk
|
|
+endif
|
|
+
|
|
libgstisomp4_la_LDFLAGS = ${GST_PLUGIN_LDFLAGS}
|
|
libgstisomp4_la_SOURCES = isomp4-plugin.c gstrtpxqtdepay.c \
|
|
qtdemux.c qtdemux_types.c qtdemux_dump.c qtdemux_lang.c \
|
|
gstqtmux.c gstqtmoovrecover.c atoms.c atomsrecovery.c descriptors.c \
|
|
- properties.c gstqtmuxmap.c gstisoff.c
|
|
+ properties.c gstqtmuxmap.c gstisoff.c awPlayReadyLicense.c
|
|
|
|
presetdir = $(datadir)/gstreamer-$(GST_API_VERSION)/presets
|
|
preset_DATA = GstQTMux.prs
|
|
@@ -37,7 +55,8 @@
|
|
properties.h \
|
|
fourcc.h \
|
|
gstisoff.h \
|
|
- gstqtmuxmap.h
|
|
+ gstqtmuxmap.h \
|
|
+ awPlayReadyLicense.h
|
|
|
|
EXTRA_DIST = \
|
|
gstqtmux-doc.c \
|
|
diff --git a/gst/isomp4/awPlayReadyLicense.c b/gst/isomp4/awPlayReadyLicense.c
|
|
new file mode 100755
|
|
index 0000000..fd8fe6d
|
|
--- /dev/null
|
|
+++ b/gst/isomp4/awPlayReadyLicense.c
|
|
@@ -0,0 +1,891 @@
|
|
+/*
|
|
+ * Copyright (c) 2008-2018 Allwinner Technology Co. Ltd.
|
|
+ * All rights reserved.
|
|
+ *
|
|
+ * File : awPlayReadyLicense.c
|
|
+ * Description : aw PlayReady License
|
|
+ * History :
|
|
+ *
|
|
+ */
|
|
+#ifdef HAVE_CONFIG_H
|
|
+#include "config.h"
|
|
+#endif
|
|
+
|
|
+#include "gst/gst-i18n-plugin.h"
|
|
+
|
|
+#include <glib/gprintf.h>
|
|
+#include <gst/tag/tag.h>
|
|
+#include <gst/audio/audio.h>
|
|
+#include <gst/video/video.h>
|
|
+#include <gst/pbutils/pbutils.h>
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#include <pthread.h>
|
|
+
|
|
+#include <math.h>
|
|
+#include <gst/math-compat.h>
|
|
+
|
|
+#ifdef HAVE_ZLIB
|
|
+# include <zlib.h>
|
|
+#endif
|
|
+
|
|
+#include "awPlayReadyLicense.h"
|
|
+
|
|
+#ifdef CONFIG_PLAYREADY
|
|
+#include <drmwindowsenv.h>
|
|
+#include <drmfeatures.h>
|
|
+#include <drmtypes.h>
|
|
+#include <drmutilitieslite.h>
|
|
+#include <drmcrt.h>
|
|
+#include <drmmanager.h>
|
|
+#include <drmdomainimp.h>
|
|
+#include <drmrevocation.h>
|
|
+#include <drmcmdlnpars.h>
|
|
+#include <drmtoolsutils.h>
|
|
+#include <drmtoolsmediafile.h>
|
|
+#include <drmheaderparser.h>
|
|
+#include <drmutf.h>
|
|
+#include <drmsoapxmlutility.h>
|
|
+#include <drmmeterapi.h>
|
|
+#include <drmmathsafe.h>
|
|
+#include <drmtoolsnetio.h>
|
|
+#include <drmtrace.h>
|
|
+#include <drmtoolsinit.h>
|
|
+#include <drmconstants.h>
|
|
+#include <stdarg.h>
|
|
+#include <stdio.h>
|
|
+#include <stdlib.h>
|
|
+#include <string.h>
|
|
+#include <drmpath.h>
|
|
+
|
|
+#define DUMP_NETWORK_DATA 1
|
|
+
|
|
+#define ManifestFile PLAYREADY_TMPFILE_DIR "manifest"
|
|
+#define WRMHeaderPath PLAYREADY_TMPFILE_DIR "sstr.xml"
|
|
+
|
|
+#define DECRYPT_ERROR_NUM 3
|
|
+
|
|
+static guint8 currPlayreadyRef = 0;
|
|
+
|
|
+pthread_mutex_t playready_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
+static gboolean playready_doLicensed = FALSE;
|
|
+
|
|
+static const char *codes =
|
|
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
+
|
|
+static const unsigned char map[256] = {
|
|
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 253, 255,
|
|
+255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
+255, 255, 255, 255, 255, 255, 255, 255, 253, 255, 255, 255,
|
|
+255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
|
|
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
|
|
+255, 254, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6,
|
|
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
|
|
+ 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
|
|
+255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
|
|
+ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
|
|
+ 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
+255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
|
+255, 255, 255, 255 };
|
|
+
|
|
+struct DrmInfoS {
|
|
+ DRM_BOOL IsInit;
|
|
+ DRM_APP_CONTEXT *AppContext;
|
|
+ DRM_BYTE *OpaqueBuffer;
|
|
+ DRM_BYTE *RevocationBuffer;
|
|
+ DRM_DECRYPT_CONTEXT DecryptContext;
|
|
+ DRM_BOOL IsDecryptInit;
|
|
+} drminfo = { 0 };
|
|
+
|
|
+static int base64_encode(const unsigned char *in, unsigned long len,
|
|
+ unsigned char *out)
|
|
+{
|
|
+ unsigned long i, leven;
|
|
+ unsigned char *p;
|
|
+
|
|
+ p = out;
|
|
+ leven = 3*(len / 3);
|
|
+ for (i = 0; i < leven; i += 3) {
|
|
+ *p++ = codes[in[0] >> 2];
|
|
+ *p++ = codes[((in[0] & 3) << 4) + (in[1] >> 4)];
|
|
+ *p++ = codes[((in[1] & 0xf) << 2) + (in[2] >> 6)];
|
|
+ *p++ = codes[in[2] & 0x3f];
|
|
+ in += 3;
|
|
+ }
|
|
+ if (i < len) {
|
|
+ unsigned a = in[0];
|
|
+ unsigned b = (i+1 < len) ? in[1] : 0;
|
|
+ unsigned c = 0;
|
|
+
|
|
+ *p++ = codes[a >> 2];
|
|
+ *p++ = codes[((a & 3) << 4) + (b >> 4)];
|
|
+ *p++ = (i+1 < len) ? codes[((b & 0xf) << 2) + (c >> 6)] : '=';
|
|
+ *p++ = '=';
|
|
+ }
|
|
+
|
|
+ *p = '\0';
|
|
+
|
|
+ return p - out;
|
|
+}
|
|
+
|
|
+static int base64_decode(const unsigned char *in, unsigned char *out)
|
|
+{
|
|
+ unsigned long t, x, y, z;
|
|
+ unsigned char c;
|
|
+ int g = 3;
|
|
+
|
|
+ for (x = y = z = t = 0; in[x]!=0;) {
|
|
+ c = map[in[x++]];
|
|
+ if (c == 255) return -1;
|
|
+ if (c == 253) continue;
|
|
+ if (c == 254) { c = 0; g--; }
|
|
+ t = (t<<6)|c;
|
|
+ if (++y == 4) {
|
|
+ out[z++] = (unsigned char)((t>>16)&255);
|
|
+ if (g > 1) out[z++] = (unsigned char)((t>>8)&255);
|
|
+ if (g > 2) out[z++] = (unsigned char)(t&255);
|
|
+ y = t = 0;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ return z;
|
|
+}
|
|
+
|
|
+static DRM_RESULT DRM_CALL PolicyCallback(
|
|
+ __in const DRM_VOID *f_pvPolicyCallbackData,
|
|
+ __in DRM_POLICY_CALLBACK_TYPE f_dwCallbackType,
|
|
+ __in_opt const DRM_KID *f_pKID,
|
|
+ __in_opt const DRM_LID *f_pLID,
|
|
+ __in const DRM_VOID *f_pv )
|
|
+{
|
|
+ (void)f_pKID;
|
|
+ (void)f_pLID;
|
|
+ (void)f_pv;
|
|
+ return DRMTOOLS_PrintPolicyCallback( f_pvPolicyCallbackData, f_dwCallbackType );
|
|
+}
|
|
+
|
|
+static DRM_RESULT DRM_CALL BindCallback(
|
|
+ __in const DRM_VOID *f_pvPolicyCallbackData,
|
|
+ __in DRM_POLICY_CALLBACK_TYPE f_dwCallbackType,
|
|
+ __in_opt const DRM_KID *f_pKID,
|
|
+ __in_opt const DRM_LID *f_pLID,
|
|
+ __in const DRM_VOID *f_pv )
|
|
+{
|
|
+ (void)(f_pKID);
|
|
+ (void)(f_pLID);
|
|
+ (void)(f_pv);
|
|
+ return DRMTOOLS_PrintPolicyCallback( f_pvPolicyCallbackData, f_dwCallbackType );
|
|
+}
|
|
+
|
|
+static void DebugAnalyze(unsigned long dr, const char* file, unsigned long line, const char* expr)
|
|
+{
|
|
+ if (dr != 0)
|
|
+ GST_DEBUG("%s:%ld 0x%x(%s)", file, line, (int)dr, expr);
|
|
+}
|
|
+
|
|
+static DRM_RESULT DRM_CALL DumpData(
|
|
+ __in const DRM_CONST_STRING *f_path,
|
|
+ __in_ecount(f_cbPacket) DRM_BYTE *f_pbPacket,
|
|
+ __in DRM_DWORD f_cbPacket )
|
|
+{
|
|
+ DRM_RESULT dr = DRM_SUCCESS;
|
|
+
|
|
+#if DUMP_NETWORK_DATA
|
|
+
|
|
+ OEM_FILEHDL fp = OEM_INVALID_HANDLE_VALUE;
|
|
+ DRM_DWORD cbWritten = 0;
|
|
+
|
|
+ ChkArg( f_pbPacket != NULL && f_cbPacket > 0 );
|
|
+
|
|
+ fp = Oem_File_Open( NULL,
|
|
+ f_path->pwszString,
|
|
+ OEM_GENERIC_WRITE,
|
|
+ OEM_FILE_SHARE_NONE,
|
|
+ OEM_CREATE_ALWAYS,
|
|
+ OEM_ATTRIBUTE_NORMAL );
|
|
+
|
|
+ if ( fp == OEM_INVALID_HANDLE_VALUE )
|
|
+ {
|
|
+ ChkDR( DRM_E_FAIL );
|
|
+ }
|
|
+
|
|
+ if ( !Oem_File_Write( fp, f_pbPacket, f_cbPacket, &cbWritten ) ||
|
|
+ cbWritten != f_cbPacket )
|
|
+ {
|
|
+ ChkDR( DRM_E_FAIL );
|
|
+ }
|
|
+
|
|
+ErrorExit:
|
|
+
|
|
+ if ( fp != OEM_INVALID_HANDLE_VALUE )
|
|
+ {
|
|
+ Oem_File_Close( fp );
|
|
+ }
|
|
+
|
|
+#endif
|
|
+
|
|
+ return dr;
|
|
+}
|
|
+
|
|
+static DRM_BOOL DRM_CALL LoadFile(const DRM_CONST_STRING * pPath, DRM_BYTE **ppBuffer, DRM_DWORD *pSize)
|
|
+{
|
|
+ DRM_RESULT dr = DRM_SUCCESS;
|
|
+ OEM_FILEHDL fp = OEM_INVALID_HANDLE_VALUE;
|
|
+ DRM_DWORD cbRead = 0;
|
|
+
|
|
+ ChkBOOL( ( fp = Oem_File_Open( NULL,
|
|
+ pPath->pwszString,
|
|
+ OEM_GENERIC_READ,
|
|
+ OEM_FILE_SHARE_READ,
|
|
+ OEM_OPEN_EXISTING,
|
|
+ OEM_ATTRIBUTE_NORMAL ) ) != OEM_INVALID_HANDLE_VALUE,
|
|
+ DRM_E_FILEOPEN );
|
|
+
|
|
+ ChkBOOL( Oem_File_SetFilePointer( fp,
|
|
+ 0,
|
|
+ OEM_FILE_END,
|
|
+ pSize ), DRM_E_FILE_SEEK_ERROR );
|
|
+
|
|
+ ChkMem( *ppBuffer = ( DRM_BYTE * )Oem_MemAlloc( *pSize ) );
|
|
+
|
|
+ ChkBOOL( Oem_File_SetFilePointer( fp,
|
|
+ 0,
|
|
+ OEM_FILE_BEGIN,
|
|
+ NULL ), DRM_E_FILE_SEEK_ERROR );
|
|
+
|
|
+ ChkBOOL( Oem_File_Read( fp,
|
|
+ *ppBuffer,
|
|
+ *pSize,
|
|
+ &cbRead ), DRM_E_FILE_READ_ERROR );
|
|
+
|
|
+ ChkBOOL( cbRead == *pSize, DRM_E_FILE_READ_ERROR );
|
|
+
|
|
+ErrorExit:
|
|
+
|
|
+ if ( fp != OEM_INVALID_HANDLE_VALUE )
|
|
+ {
|
|
+ Oem_File_Close( fp );
|
|
+ }
|
|
+
|
|
+ return dr;
|
|
+
|
|
+}
|
|
+
|
|
+static DRM_RESULT GenerateLicenseChallege(DRM_APP_CONTEXT *AppContext, DRM_BYTE *pbHeader, DRM_DWORD cbHeader, DRM_BYTE **ppbChallenge, DRM_DWORD *pcbChallenge)
|
|
+{
|
|
+ DRM_RESULT dr = DRM_SUCCESS;
|
|
+ const DRM_CONST_STRING *rgpdstrRights[ 1 ] = { 0 };
|
|
+ DRM_BYTE *pbHeader2 = NULL;
|
|
+ DRM_STRING dstrHeader = DRM_EMPTY_DRM_STRING;
|
|
+ DRM_SUBSTRING dasstrHeader1 = DRM_EMPTY_DRM_SUBSTRING;
|
|
+ DRM_BYTE *pbChallenge = NULL;
|
|
+ DRM_DWORD cbChallenge = 0;
|
|
+ DRM_CHAR rgchURL[ 1024 ];
|
|
+ DRM_DWORD cchURL = 1024;
|
|
+
|
|
+ rgpdstrRights[ 0 ] = &g_dstrWMDRM_RIGHT_PLAYBACK;
|
|
+
|
|
+ ChkMem( pbHeader2 = (DRM_BYTE *)Oem_MemAlloc( cbHeader * sizeof( DRM_WCHAR ) ) );
|
|
+
|
|
+ DRM_DSTR_FROM_PB( &dstrHeader, pbHeader2, cbHeader * sizeof( DRM_WCHAR ) );
|
|
+
|
|
+ dasstrHeader1.m_cch = cbHeader;
|
|
+
|
|
+ DRM_UTL_PromoteASCIItoUNICODE( (const DRM_CHAR *)pbHeader,
|
|
+ &dasstrHeader1,
|
|
+ ( DRM_STRING * )&dstrHeader );
|
|
+
|
|
+ ChkDR( Drm_Content_SetProperty( AppContext,
|
|
+ DRM_CSP_AUTODETECT_HEADER,
|
|
+ DRM_PB_DSTR( &dstrHeader ),
|
|
+ DRM_CB_DSTR( &dstrHeader ) ) );
|
|
+
|
|
+ dr = Drm_LicenseAcq_GenerateChallenge( AppContext,
|
|
+ (const DRM_CONST_STRING **)rgpdstrRights,
|
|
+ DRM_NO_OF (rgpdstrRights),
|
|
+ NULL,
|
|
+ NULL,
|
|
+ 0,
|
|
+ rgchURL,
|
|
+ &cchURL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ &cbChallenge );
|
|
+
|
|
+ if ( dr != DRM_E_BUFFERTOOSMALL )
|
|
+ {
|
|
+ if (dr!= DRM_SUCCESS)
|
|
+ {
|
|
+ ChkDR( dr );
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ ChkDR( DRM_E_TEST_UNEXPECTED_OUTPUT);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ChkBOOL( cchURL <= 1024, DRM_E_FAIL );
|
|
+
|
|
+ ChkMem( pbChallenge = ( DRM_BYTE * )Oem_MemAlloc( cbChallenge ) );
|
|
+
|
|
+ ChkDR( Drm_LicenseAcq_GenerateChallenge( AppContext,
|
|
+ (const DRM_CONST_STRING **)rgpdstrRights,
|
|
+ DRM_NO_OF (rgpdstrRights),
|
|
+ NULL,
|
|
+ NULL,
|
|
+ 0,
|
|
+ rgchURL,
|
|
+ &cchURL,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ pbChallenge,
|
|
+ &cbChallenge ) );
|
|
+
|
|
+ *ppbChallenge = pbChallenge;
|
|
+ *pcbChallenge = cbChallenge;
|
|
+
|
|
+ErrorExit:
|
|
+
|
|
+ SAFE_OEM_FREE( pbHeader2 );
|
|
+ return dr;
|
|
+}
|
|
+
|
|
+DRM_RESULT DRM_CALL GenerateLicenseAck(
|
|
+ DRM_APP_CONTEXT *f_poAppContext,
|
|
+ DRM_LICENSE_RESPONSE *f_poLicResponse,
|
|
+ DRM_BYTE **f_ppbAcknowledgement,
|
|
+ DRM_DWORD *f_pcbAcknowledgement )
|
|
+{
|
|
+ DRM_RESULT dr = DRM_SUCCESS;
|
|
+
|
|
+ ChkArg( f_poAppContext != NULL );
|
|
+ ChkArg( f_poLicResponse != NULL );
|
|
+ ChkArg( f_ppbAcknowledgement != NULL );
|
|
+ ChkArg( f_pcbAcknowledgement != NULL );
|
|
+
|
|
+ dr = Drm_LicenseAcq_GenerateAck( f_poAppContext,
|
|
+ f_poLicResponse,
|
|
+ NULL,
|
|
+ f_pcbAcknowledgement);
|
|
+
|
|
+ if ( dr == DRM_E_BUFFERTOOSMALL )
|
|
+ {
|
|
+ ChkMem( *f_ppbAcknowledgement =
|
|
+ (DRM_BYTE *)Oem_MemAlloc( *f_pcbAcknowledgement ) );
|
|
+
|
|
+ ChkDR( Drm_LicenseAcq_GenerateAck( f_poAppContext,
|
|
+ f_poLicResponse,
|
|
+ *f_ppbAcknowledgement,
|
|
+ f_pcbAcknowledgement ) );
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ goto ErrorExit;
|
|
+ }
|
|
+
|
|
+ErrorExit:
|
|
+
|
|
+ return dr;
|
|
+}
|
|
+
|
|
+static inline void stringToWString(DRM_WCHAR *dst, char *src)
|
|
+{
|
|
+ while ((*dst++ = *src++));
|
|
+}
|
|
+
|
|
+static int doLicensing(const char *la_url)
|
|
+{
|
|
+ DRM_RESULT dr = DRM_SUCCESS;
|
|
+ DRM_APP_CONTEXT *AppContext = NULL;
|
|
+ DRM_BYTE *pbOpaqueBuffer = NULL;
|
|
+ DRM_BYTE *pbRevocationBuffer = NULL;
|
|
+ DRM_LICENSE_RESPONSE oLicResponse;
|
|
+
|
|
+ DRM_BYTE *pbHeader = NULL;
|
|
+ DRM_DWORD cbHeader = 0;
|
|
+ DRM_BYTE *pbChallenge1 = NULL;
|
|
+ DRM_DWORD cbChallenge1 = 0;
|
|
+ DRM_BYTE *pbChallenge2 = NULL;
|
|
+ DRM_DWORD cbChallenge2 = 0;
|
|
+ DRM_BYTE *pbResponse1 = NULL;
|
|
+ DRM_DWORD cbResponse1 = 0;
|
|
+ DRM_BYTE *pbResponse2 = NULL;
|
|
+ DRM_DWORD cbResponse2 = 0;
|
|
+
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+
|
|
+ // chl1.dat, mode = w+b
|
|
+ char chl1[] = PLAYREADY_TMPFILE_DIR "chl1.dat";
|
|
+ DRM_WCHAR wchl1[sizeof(chl1)] = {0};
|
|
+ stringToWString(wchl1, chl1);
|
|
+ const DRM_CONST_STRING strchl1 = DRM_CREATE_DRM_STRING(wchl1);
|
|
+
|
|
+ // chl2.dat, mode = w+b
|
|
+ char chl2[] = PLAYREADY_TMPFILE_DIR "chl2.dat";
|
|
+ DRM_WCHAR wchl2[sizeof(chl2)] = {0};
|
|
+ stringToWString(wchl2, chl2);
|
|
+ const DRM_CONST_STRING strchl2 = DRM_CREATE_DRM_STRING(wchl2);
|
|
+
|
|
+ // rsp1.dat, mode = w+b
|
|
+ char rsp1[] = PLAYREADY_TMPFILE_DIR "rsp1.dat";
|
|
+ DRM_WCHAR wrsp1[sizeof(rsp1)] = {0};
|
|
+ stringToWString(wrsp1, rsp1);
|
|
+ const DRM_CONST_STRING strrsp1 = DRM_CREATE_DRM_STRING(wrsp1);
|
|
+
|
|
+ // rsp2.dat, mode = w+b
|
|
+ char rsp2[] = PLAYREADY_TMPFILE_DIR "rsp2.dat";
|
|
+ DRM_WCHAR wrsp2[sizeof(rsp2)] = {0};
|
|
+ stringToWString(wrsp2, rsp2);
|
|
+ const DRM_CONST_STRING strrsp2 = DRM_CREATE_DRM_STRING(wrsp2);
|
|
+
|
|
+ // sstr.xml
|
|
+ char pin[] = PLAYREADY_TMPFILE_DIR "sstr.xml";
|
|
+ DRM_WCHAR wpin[sizeof(pin)] = {0};
|
|
+ stringToWString(wpin, pin);
|
|
+ DRM_CONST_STRING strin = DRM_CREATE_DRM_STRING(wpin);
|
|
+
|
|
+ // pr.hds
|
|
+ char prhds[] = PLAYREADY_TMPFILE_DIR "pr.hds";
|
|
+ DRM_WCHAR wprhds[sizeof(prhds)] = {0};
|
|
+ stringToWString(wprhds, prhds);
|
|
+ const DRM_CONST_STRING strhds = DRM_CREATE_DRM_STRING(wprhds);
|
|
+
|
|
+
|
|
+ // response.xml
|
|
+ char res[] = PLAYREADY_TMPFILE_DIR "response.xml";
|
|
+ DRM_WCHAR wres[sizeof(res)] = {0};
|
|
+ stringToWString(wres, res);
|
|
+ DRM_CONST_STRING strresponse = DRM_CREATE_DRM_STRING(wres);
|
|
+
|
|
+ memset(&oLicResponse, 0, sizeof(oLicResponse));
|
|
+ oLicResponse.m_eType = eUnknownProtocol;
|
|
+
|
|
+ ChkDR( KeyPath_Initialize(PLAYREADY_CERT_DIR) );
|
|
+ SetDbgAnalyzeFunction(DebugAnalyze);
|
|
+
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+ ChkDR( DRMTOOLS_DrmInitializeWithOpaqueBuffer(NULL, &strhds, TOOLS_DRM_BUFFER_SIZE, &pbOpaqueBuffer, &AppContext) );
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+ if( DRM_REVOCATION_IsRevocationSupported() )
|
|
+ {
|
|
+ ChkMem( pbRevocationBuffer = ( DRM_BYTE * )Oem_MemAlloc( REVOCATION_BUFFER_SIZE ) );
|
|
+
|
|
+ ChkDR( Drm_Revocation_SetBuffer( AppContext,
|
|
+ pbRevocationBuffer,
|
|
+ REVOCATION_BUFFER_SIZE ) );
|
|
+ }
|
|
+
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+ ChkDR( LoadFile(&strin, &pbHeader, &cbHeader) );
|
|
+
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+ ChkDR( GenerateLicenseChallege(AppContext, pbHeader, cbHeader, &pbChallenge1, &cbChallenge1) );
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+ ChkDR( DumpData(&strchl1, pbChallenge1, cbChallenge1) );
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+
|
|
+ ChkDR( DRM_TOOLS_NETIO_SendData( la_url,
|
|
+ eDRM_TOOLS_NET_LICGET,
|
|
+ pbChallenge1,
|
|
+ cbChallenge1,
|
|
+ &pbResponse1,
|
|
+ &cbResponse1,
|
|
+ NULL,
|
|
+ NULL ) );
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+ ChkDR( DumpData(&strrsp1, pbResponse1, cbResponse1) );
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+
|
|
+ oLicResponse.m_dwResult = DRM_SUCCESS;
|
|
+ ChkDR( Drm_LicenseAcq_ProcessResponse( AppContext,
|
|
+ DRM_PROCESS_LIC_RESPONSE_SIGNATURE_NOT_REQUIRED,
|
|
+ NULL,
|
|
+ NULL,
|
|
+ pbResponse1,
|
|
+ cbResponse1,
|
|
+ &oLicResponse ) );
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+ ChkDR( DumpData(&strresponse, pbResponse1, cbResponse1) );
|
|
+ GST_DEBUG("%s %d result: %lx", __func__, __LINE__, oLicResponse.m_dwResult);
|
|
+ if (!DRM_SUCCEEDED(oLicResponse.m_dwResult)) {
|
|
+ ChkDR(DRM_E_FAIL);
|
|
+ } else {
|
|
+ GST_DEBUG("transid:%d", oLicResponse.m_cbTransactionID);
|
|
+ if (oLicResponse.m_cbTransactionID > 0) {
|
|
+ ChkDR( GenerateLicenseAck( AppContext,
|
|
+ &oLicResponse,
|
|
+ &pbChallenge2,
|
|
+ &cbChallenge2) );
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+ ChkDR( DumpData(&strchl2, pbChallenge2, cbChallenge2) );
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+ ChkDR( DRM_TOOLS_NETIO_SendData( la_url,
|
|
+ eDRM_TOOLS_NET_LICACK,
|
|
+ pbChallenge2,
|
|
+ cbChallenge2,
|
|
+ &pbResponse2,
|
|
+ &cbResponse2,
|
|
+ NULL,
|
|
+ NULL ) );
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+ ChkDR( DumpData(&strrsp2, pbResponse2, cbResponse2) );
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+ DRM_RESULT dr1 = DRM_SUCCESS;
|
|
+ ChkDR( Drm_LicenseAcq_ProcessAckResponse( AppContext,
|
|
+ pbResponse2,
|
|
+ cbResponse2,
|
|
+ &dr1 ) );
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+ ChkDR(dr1);
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+
|
|
+ }
|
|
+ }
|
|
+
|
|
+ GST_DEBUG("%s %d", __func__, __LINE__);
|
|
+
|
|
+ErrorExit:
|
|
+
|
|
+ SAFE_OEM_FREE(pbRevocationBuffer);
|
|
+ DRMTOOLS_DrmUninitializeWithOpaqueBuffer( &pbOpaqueBuffer, &AppContext );
|
|
+ SAFE_OEM_FREE(pbHeader);
|
|
+ SAFE_OEM_FREE(pbChallenge1);
|
|
+ SAFE_OEM_FREE(pbChallenge2);
|
|
+ SAFE_OEM_FREE(pbResponse1);
|
|
+ SAFE_OEM_FREE(pbResponse2);
|
|
+ KeyPath_UnInitialize();
|
|
+ GST_DEBUG("end, dr=0x%x", (int)dr);
|
|
+ return (int)dr;
|
|
+}
|
|
+
|
|
+static DRM_RESULT PlayReadyOpenDrm(void)
|
|
+{
|
|
+ int index = 0;
|
|
+ DRM_RESULT dr = DRM_SUCCESS;
|
|
+
|
|
+ char prhds[] = PLAYREADY_TMPFILE_DIR "pr.hds";
|
|
+ DRM_WCHAR wprhds[sizeof(prhds)] = {0};
|
|
+ stringToWString(wprhds, prhds);
|
|
+ const DRM_CONST_STRING pdstrDataStoreFile = DRM_CREATE_DRM_STRING(wprhds);
|
|
+ const DRM_CONST_STRING *rights[1] = { &g_dstrWMDRM_RIGHT_PLAYBACK };
|
|
+
|
|
+ DRM_BYTE *pbHeader = NULL;
|
|
+ DRM_DWORD cbHeader = 0;
|
|
+ DRM_BYTE *pbHeader2 = NULL;
|
|
+ DRM_CONST_STRING dstrHeader = DRM_EMPTY_DRM_STRING;
|
|
+ DRM_SUBSTRING dasstrHeader1 = DRM_EMPTY_DRM_SUBSTRING;
|
|
+
|
|
+ char pin[] = PLAYREADY_TMPFILE_DIR "sstr.xml";
|
|
+ DRM_WCHAR wpin[sizeof(pin)] = {0};
|
|
+ stringToWString(wpin, pin);
|
|
+ const DRM_CONST_STRING strin = DRM_CREATE_DRM_STRING(wpin);
|
|
+
|
|
+ pthread_mutex_lock(&playready_mutex);
|
|
+
|
|
+ //SetDbgAnalyzeFunction(DebugAnalyze);
|
|
+
|
|
+ if (drminfo.IsInit == 0) {
|
|
+
|
|
+ memset(&drminfo, 0, sizeof(drminfo));
|
|
+
|
|
+ drminfo.IsInit = 1;
|
|
+ drminfo.IsDecryptInit = FALSE;
|
|
+
|
|
+ ChkDR( DRMTOOLS_DrmInitializeWithOpaqueBuffer(NULL,
|
|
+ &pdstrDataStoreFile,
|
|
+ TOOLS_DRM_BUFFER_SIZE,
|
|
+ &drminfo.OpaqueBuffer,
|
|
+ &drminfo.AppContext) );
|
|
+ if (DRM_REVOCATION_IsRevocationSupported())
|
|
+ {
|
|
+ ChkMem( drminfo.RevocationBuffer = (DRM_BYTE *)Oem_MemAlloc(REVOCATION_BUFFER_SIZE) );
|
|
+ ChkDR( Drm_Revocation_SetBuffer(drminfo.AppContext, drminfo.RevocationBuffer,
|
|
+ REVOCATION_BUFFER_SIZE) );
|
|
+ }
|
|
+
|
|
+ ChkDR( LoadFile(&strin, &pbHeader, &cbHeader) );
|
|
+ ChkMem( pbHeader2 = (DRM_BYTE *)Oem_MemAlloc( cbHeader * sizeof( DRM_WCHAR ) ) );
|
|
+ DRM_DSTR_FROM_PB( &dstrHeader, pbHeader2, cbHeader * sizeof( DRM_WCHAR ) );
|
|
+ dasstrHeader1.m_cch = cbHeader;
|
|
+
|
|
+ DRM_UTL_PromoteASCIItoUNICODE( (const DRM_CHAR *)pbHeader,
|
|
+ &dasstrHeader1,
|
|
+ ( DRM_STRING * )&dstrHeader );
|
|
+
|
|
+ ChkDR( Drm_Content_SetProperty( drminfo.AppContext,
|
|
+ DRM_CSP_AUTODETECT_HEADER,
|
|
+ DRM_PB_DSTR( &dstrHeader ),
|
|
+ DRM_CB_DSTR( &dstrHeader ) ) );
|
|
+
|
|
+ ChkDR( Drm_Reader_Bind(drminfo.AppContext,
|
|
+ rights,
|
|
+ DRM_NO_OF(rights),
|
|
+ (DRMPFNPOLICYCALLBACK)BindCallback,
|
|
+ NULL,
|
|
+ &drminfo.DecryptContext) );
|
|
+ drminfo.IsDecryptInit = TRUE;
|
|
+
|
|
+ ChkDR( Drm_Reader_Commit(drminfo.AppContext, NULL, NULL) );
|
|
+
|
|
+ SAFE_OEM_FREE(pbHeader);
|
|
+ SAFE_OEM_FREE(pbHeader2);
|
|
+ }
|
|
+
|
|
+ /* ref++ */
|
|
+ currPlayreadyRef++;
|
|
+ pthread_mutex_unlock(&playready_mutex);
|
|
+ return dr;
|
|
+ErrorExit:
|
|
+ GST_ERROR("OpenDrm failed:dr=0x%x", (int)dr);
|
|
+ if (drminfo.IsDecryptInit)
|
|
+ {
|
|
+ Drm_Reader_Close(&drminfo.DecryptContext);
|
|
+ }
|
|
+
|
|
+ DRMTOOLS_DrmUninitializeWithOpaqueBuffer(&drminfo.OpaqueBuffer, &drminfo.AppContext);
|
|
+ SAFE_OEM_FREE(drminfo.RevocationBuffer);
|
|
+ SAFE_OEM_FREE(pbHeader);
|
|
+ SAFE_OEM_FREE(pbHeader2);
|
|
+ drminfo.IsInit = 0;
|
|
+ pthread_mutex_unlock(&playready_mutex);
|
|
+ return dr;
|
|
+}
|
|
+
|
|
+static DRM_RESULT PlayReadyCloseDrm(void)
|
|
+{
|
|
+ DRM_RESULT dr = DRM_SUCCESS;
|
|
+
|
|
+ pthread_mutex_lock(&playready_mutex);
|
|
+ if(--currPlayreadyRef > 0){
|
|
+ pthread_mutex_unlock(&playready_mutex);
|
|
+ GST_WARNING("Playready is being used, not closed, currPlayreadyRef %u", currPlayreadyRef);
|
|
+ return dr;
|
|
+ }
|
|
+ pthread_mutex_unlock(&playready_mutex);
|
|
+
|
|
+ if (drminfo.IsInit == 1) {
|
|
+ if (drminfo.IsDecryptInit)
|
|
+ {
|
|
+ Drm_Reader_Close(&drminfo.DecryptContext);
|
|
+ }
|
|
+
|
|
+ DRMTOOLS_DrmUninitializeWithOpaqueBuffer(&drminfo.OpaqueBuffer, &drminfo.AppContext);
|
|
+ SAFE_OEM_FREE(drminfo.RevocationBuffer);
|
|
+ drminfo.IsInit = 0;
|
|
+
|
|
+ playready_doLicensed = FALSE;
|
|
+
|
|
+ GST_INFO("Release the playready DRM.");
|
|
+ }
|
|
+
|
|
+ return dr;
|
|
+}
|
|
+int doLicense(char *manifest)
|
|
+{
|
|
+ int success = -1;
|
|
+ unsigned int i,j;
|
|
+ FILE *fp = NULL;
|
|
+ char *currManifest = NULL;
|
|
+ int currManifestLength = 0;
|
|
+
|
|
+ if(!manifest)
|
|
+ return -1;
|
|
+
|
|
+ /* Get the certificate only once */
|
|
+ pthread_mutex_lock(&playready_mutex);
|
|
+ if(playready_doLicensed){
|
|
+ pthread_mutex_unlock(&playready_mutex);
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ int len = strlen((char*)manifest);
|
|
+ int str_len;
|
|
+
|
|
+ /* base64decode wrmheader */
|
|
+ if(strstr((char*)manifest,"=="))
|
|
+ str_len = len/4*3-2;
|
|
+ else if(strstr((char*)manifest,"="))
|
|
+ str_len = len/4*3-1;
|
|
+ else
|
|
+ str_len = len/4;
|
|
+
|
|
+ unsigned char* Header = malloc(str_len+1);
|
|
+ success = base64_decode(manifest, Header);
|
|
+
|
|
+ if(success <= 0){
|
|
+ GST_ERROR("base64 decode fail. %s %d", __func__, __LINE__);
|
|
+ free(Header);
|
|
+ goto ErrorExit;
|
|
+ }
|
|
+ /* drop all '\0',so we can get the real Header */
|
|
+ for(i=j=0;i< str_len;i++){
|
|
+ if(Header[i] != '\0'){
|
|
+ Header[j++] = Header[i];
|
|
+ }
|
|
+ }
|
|
+ Header[j] = '\0';
|
|
+
|
|
+ char * WRMHeader = strstr((char*)Header, "<WRMHEADER");
|
|
+ GST_DEBUG("WRMHeader=%s",WRMHeader);
|
|
+
|
|
+ /* save WRMHeader data in sstr.xml */
|
|
+ fp = fopen(WRMHeaderPath, "r" );
|
|
+ if(fp){
|
|
+ fclose(fp);
|
|
+ fp = NULL;
|
|
+ remove(WRMHeaderPath);
|
|
+ }
|
|
+ fp = fopen(WRMHeaderPath, "wb");
|
|
+ if (fp){
|
|
+ fwrite(WRMHeader, 1, strlen(WRMHeader), fp);
|
|
+ }
|
|
+ fclose(fp);
|
|
+
|
|
+ /* ack and process license using wrmheader. */
|
|
+ if (WRMHeader != NULL) {
|
|
+ int dolicense = 0;
|
|
+ /* get license acquire URL */
|
|
+ char* LA_URL = strstr(WRMHeader, "<LA_URL>");
|
|
+ LA_URL += 8;
|
|
+ char* LA_URL_END = strstr(WRMHeader, "</LA_URL>");
|
|
+ LA_URL_END[0] = '\0';
|
|
+ GST_DEBUG("LA_URL=%s",LA_URL);
|
|
+DOLICENSE:
|
|
+ success = -1;
|
|
+ success = doLicensing(LA_URL);
|
|
+ if ((success == DRM_E_FAIL) && (dolicense == 0)){
|
|
+ GST_ERROR("doLicensing return error,do it again.");
|
|
+ dolicense++;
|
|
+ goto DOLICENSE;
|
|
+ }
|
|
+ }
|
|
+ free(Header);
|
|
+
|
|
+ if (!success){
|
|
+ playready_doLicensed = TRUE;
|
|
+ pthread_mutex_unlock(&playready_mutex);
|
|
+ GST_INFO("license succeed.");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ErrorExit:
|
|
+ pthread_mutex_unlock(&playready_mutex);
|
|
+ GST_ERROR("license failed return=0x%x.", (int)success);
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int OpenPlayReadyDrm(void)
|
|
+{
|
|
+ if(PlayReadyOpenDrm() == DRM_SUCCESS){
|
|
+ GST_INFO("OpenDrm succeed.");
|
|
+ return 0;
|
|
+ }else
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+int ClosePlayReadyDrm(void)
|
|
+{
|
|
+ if(PlayReadyCloseDrm() == DRM_SUCCESS){
|
|
+ GST_INFO("CloseDrm succeed.");
|
|
+ return 0;
|
|
+ }else
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+gint PlayReadyDecrypt(GstBuffer *buf, guint64 iv, guint32 region_count, guint32 *regions)
|
|
+{
|
|
+ DRM_RESULT dr = DRM_SUCCESS;
|
|
+ GstMapInfo map;
|
|
+ guint8 *data;
|
|
+ guint size;
|
|
+ unsigned char *opaque_buf;
|
|
+ unsigned int opaque_size;
|
|
+ unsigned int decryptCount = 0;
|
|
+
|
|
+ gst_buffer_map (buf, &map, GST_MAP_READ);
|
|
+ data = map.data;
|
|
+ size = map.size;
|
|
+
|
|
+ if(region_count == 0){
|
|
+ region_count = 2;
|
|
+ regions[0] = 0;
|
|
+ regions[1] = size;
|
|
+ }
|
|
+
|
|
+ pthread_mutex_lock(&playready_mutex);
|
|
+DECRTPT:
|
|
+ decryptCount++;
|
|
+ /* decrypt */
|
|
+ dr = Drm_Reader_DecryptOpaque(&drminfo.DecryptContext,
|
|
+ region_count,
|
|
+ regions,
|
|
+ iv,
|
|
+ size,
|
|
+ data,
|
|
+ &opaque_size,
|
|
+ &opaque_buf);
|
|
+ if(dr != DRM_SUCCESS) {
|
|
+ GST_ERROR("decryptCount %u, decrypt fail:dr=0x%x", decryptCount, (int)dr);
|
|
+ if(opaque_buf && opaque_size > 0)
|
|
+ DRM_Reader_FreeOpaqueDecryptedContent(&drminfo.DecryptContext, opaque_size, opaque_buf);
|
|
+
|
|
+ if(decryptCount < DECRYPT_ERROR_NUM)
|
|
+ goto DECRTPT;
|
|
+
|
|
+ pthread_mutex_unlock(&playready_mutex);
|
|
+ gst_buffer_unmap (buf, &map);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ gst_buffer_unmap (buf, &map);
|
|
+
|
|
+ /* copy */
|
|
+ memset(&map, 0, sizeof(GstMapInfo));
|
|
+ gst_buffer_map (buf, &map, GST_MAP_WRITE);
|
|
+ data = map.data;
|
|
+ size = map.size;
|
|
+
|
|
+ memcpy(data, opaque_buf, size);
|
|
+
|
|
+ DRM_Reader_FreeOpaqueDecryptedContent(&drminfo.DecryptContext, opaque_size, opaque_buf);
|
|
+ gst_buffer_unmap (buf, &map);
|
|
+ pthread_mutex_unlock(&playready_mutex);
|
|
+
|
|
+ GST_DEBUG("decrypt succeed.");
|
|
+
|
|
+ return opaque_size;
|
|
+}
|
|
+
|
|
+#else
|
|
+int doLicense(char* manifest)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int OpenPlayReadyDrm(void)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+int ClosePlayReadyDrm(void)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+gint PlayReadyDecrypt(GstBuffer *buf, guint64 iv, guint32 region_count, guint32 *regions)
|
|
+{
|
|
+ return 0;
|
|
+}
|
|
+#endif
|
|
diff --git a/gst/isomp4/awPlayReadyLicense.h b/gst/isomp4/awPlayReadyLicense.h
|
|
new file mode 100755
|
|
index 0000000..c40ddb2
|
|
--- /dev/null
|
|
+++ b/gst/isomp4/awPlayReadyLicense.h
|
|
@@ -0,0 +1,11 @@
|
|
+#ifndef AW_PLAYREADY_LICENSE_H
|
|
+#define AW_PLAYREADY_LICENSE_H
|
|
+
|
|
+#include <glib.h>
|
|
+
|
|
+int doLicense(char* manifest);
|
|
+int OpenPlayReadyDrm(void);
|
|
+int ClosePlayReadyDrm(void);
|
|
+gint PlayReadyDecrypt(GstBuffer *buf, guint64 iv, guint32 region_count, guint32 *regions);
|
|
+
|
|
+#endif /* AW_PLAYREADY_LICENSE_H */
|
|
diff --git a/gst/isomp4/qtdemux.c b/gst/isomp4/qtdemux.c
|
|
index a6f870f..82da2f0 100644
|
|
--- a/gst/isomp4/qtdemux.c
|
|
+++ b/gst/isomp4/qtdemux.c
|
|
@@ -82,6 +82,8 @@
|
|
# include <zlib.h>
|
|
#endif
|
|
|
|
+#include "awPlayReadyLicense.h"
|
|
+
|
|
/* max. size considered 'sane' for non-mdat atoms */
|
|
#define QTDEMUX_MAX_ATOM_SIZE (25*1024*1024)
|
|
|
|
@@ -469,13 +471,15 @@ static GNode *qtdemux_tree_get_sibling_by_type_full (GNode * node,
|
|
guint32 fourcc, GstByteReader * parser);
|
|
|
|
static GstFlowReturn qtdemux_add_fragmented_samples (GstQTDemux * qtdemux);
|
|
-
|
|
+#define PLAYREADY_SYSTEM_ID "9A04F079-9840-4286-AB92-E65BE0885F95"
|
|
static GstStaticPadTemplate gst_qtdemux_sink_template =
|
|
GST_STATIC_PAD_TEMPLATE ("sink",
|
|
GST_PAD_SINK,
|
|
GST_PAD_ALWAYS,
|
|
GST_STATIC_CAPS ("video/quicktime; video/mj2; audio/x-m4a; "
|
|
- "application/x-3gp")
|
|
+ "application/x-3gp; "
|
|
+ "application/x-cenc, "
|
|
+ GST_PROTECTION_SYSTEM_ID_CAPS_FIELD "=(string)" PLAYREADY_SYSTEM_ID)
|
|
);
|
|
|
|
static GstStaticPadTemplate gst_qtdemux_videosrc_template =
|
|
@@ -615,7 +619,7 @@ gst_qtdemux_class_init (GstQTDemuxClass * klass)
|
|
gst_element_class_add_static_pad_template (gstelement_class,
|
|
&gst_qtdemux_subsrc_template);
|
|
gst_element_class_set_static_metadata (gstelement_class, "QuickTime demuxer",
|
|
- "Codec/Demuxer",
|
|
+ "Codec/Demuxer/Decryptor",
|
|
"Demultiplex a QuickTime file into audio and video streams",
|
|
"David Schleef <ds@schleef.org>, Wim Taymans <wim@fluendo.com>");
|
|
|
|
@@ -661,6 +665,8 @@ gst_qtdemux_init (GstQTDemux * qtdemux)
|
|
qtdemux->cenc_aux_info_sizes = NULL;
|
|
qtdemux->cenc_aux_sample_count = 0;
|
|
qtdemux->protection_system_ids = NULL;
|
|
+ qtdemux->protected_playready = FALSE;
|
|
+ qtdemux->playready_openDRM = FALSE;
|
|
g_queue_init (&qtdemux->protection_event_queue);
|
|
gst_segment_init (&qtdemux->segment, GST_FORMAT_TIME);
|
|
qtdemux->tag_list = gst_tag_list_new_empty ();
|
|
@@ -2381,10 +2387,38 @@ gst_qtdemux_handle_sink_event (GstPad * sinkpad, GstObject * parent,
|
|
case GST_EVENT_PROTECTION:
|
|
{
|
|
const gchar *system_id = NULL;
|
|
+ GstBuffer *data;
|
|
+ const gchar *origin;
|
|
|
|
- gst_event_parse_protection (event, &system_id, NULL, NULL);
|
|
+ gst_event_parse_protection (event, &system_id, &data, &origin);
|
|
GST_DEBUG_OBJECT (demux, "Received protection event for system ID %s",
|
|
system_id);
|
|
+
|
|
+ if ((g_ascii_strcasecmp (system_id, PLAYREADY_SYSTEM_ID) == 0) &&
|
|
+ (g_strcmp0 (origin, "smooth-streaming") == 0)) {
|
|
+ GstMapInfo info;
|
|
+ gchar *value;
|
|
+
|
|
+ demux->protected_playready = TRUE;
|
|
+ GST_DEBUG_OBJECT (demux, "Playready certificate acquisition operation");
|
|
+ /* extract WRMHeader */
|
|
+ gst_buffer_map (data, &info, GST_MAP_READ);
|
|
+
|
|
+ value = g_malloc (info.size + 1);
|
|
+ strncpy (value, (gchar *) info.data, info.size);
|
|
+ value[info.size] = 0;
|
|
+ gst_buffer_unmap (data, &info);
|
|
+
|
|
+ if (doLicense(value) == 0)
|
|
+ GST_DEBUG_OBJECT (demux, "Successful certification of playready");
|
|
+ else
|
|
+ GST_ERROR_OBJECT (demux, "playready certification failed");
|
|
+
|
|
+ g_free (value);
|
|
+
|
|
+ res = TRUE;
|
|
+ goto drop;
|
|
+ }
|
|
gst_qtdemux_append_protection_system_id (demux, system_id);
|
|
/* save the event for later, for source pads that have not been created */
|
|
g_queue_push_tail (&demux->protection_event_queue, gst_event_ref (event));
|
|
@@ -2520,6 +2554,12 @@ gst_qtdemux_stream_clear (GstQTDemux * qtdemux, QtDemuxStream * stream)
|
|
stream->redirect_uri = NULL;
|
|
stream->sent_eos = FALSE;
|
|
stream->protected = FALSE;
|
|
+ /* clear the playready decryption component */
|
|
+ if (qtdemux->protected_playready && qtdemux->playready_openDRM)
|
|
+ ClosePlayReadyDrm();
|
|
+ qtdemux->protected_playready = FALSE;
|
|
+ qtdemux->playready_openDRM = FALSE;
|
|
+
|
|
if (stream->protection_scheme_info) {
|
|
if (stream->protection_scheme_type == FOURCC_cenc) {
|
|
QtDemuxCencSampleSetInfo *info =
|
|
@@ -2671,15 +2711,22 @@ qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
|
|
stream = qtdemux->streams[0];
|
|
|
|
structure = gst_caps_get_structure (CUR_STREAM (stream)->caps, 0);
|
|
- if (!gst_structure_has_name (structure, "application/x-cenc")) {
|
|
+
|
|
+ /* playready encryption does not check this value */
|
|
+ if (!qtdemux->protected_playready &&
|
|
+ !gst_structure_has_name (structure, "application/x-cenc")) {
|
|
GST_WARNING_OBJECT (qtdemux,
|
|
"Attempting PIFF box parsing on an unencrypted stream.");
|
|
return;
|
|
}
|
|
|
|
- gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
|
|
- G_TYPE_STRING, &system_id, NULL);
|
|
- gst_qtdemux_append_protection_system_id (qtdemux, system_id);
|
|
+ /* playready encryption does not check this value */
|
|
+ if (!qtdemux->protected_playready) {
|
|
+ gst_structure_get (structure, GST_PROTECTION_SYSTEM_ID_CAPS_FIELD,
|
|
+ G_TYPE_STRING, &system_id, NULL);
|
|
+ gst_qtdemux_append_protection_system_id (qtdemux, system_id);
|
|
+ }else
|
|
+ gst_qtdemux_append_protection_system_id (qtdemux, PLAYREADY_SYSTEM_ID);
|
|
|
|
stream->protected = TRUE;
|
|
stream->protection_scheme_type = FOURCC_cenc;
|
|
@@ -2784,6 +2831,7 @@ qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
|
|
gst_structure_free (properties);
|
|
return;
|
|
}
|
|
+
|
|
buf = gst_buffer_new_wrapped (data, iv_size);
|
|
gst_structure_set (properties, "iv", GST_TYPE_BUFFER, buf, NULL);
|
|
gst_buffer_unref (buf);
|
|
@@ -2799,6 +2847,7 @@ qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
|
|
return;
|
|
}
|
|
GST_LOG_OBJECT (qtdemux, "subsample count: %u", n_subsamples);
|
|
+
|
|
if (!gst_byte_reader_dup_data (&br, n_subsamples * 6, &data)) {
|
|
GST_ERROR_OBJECT (qtdemux, "failed to get subsample data for sample %u",
|
|
i);
|
|
@@ -2816,6 +2865,15 @@ qtdemux_parse_piff (GstQTDemux * qtdemux, const guint8 * buffer, gint length,
|
|
|
|
g_ptr_array_add (ss_info->crypto_info, properties);
|
|
}
|
|
+
|
|
+ /* open playready drm */
|
|
+ if (!qtdemux->playready_openDRM) {
|
|
+ if (OpenPlayReadyDrm() == 0)
|
|
+ qtdemux->playready_openDRM = TRUE;
|
|
+ else
|
|
+ GST_ERROR_OBJECT (qtdemux, "open playready drm failed");
|
|
+ }
|
|
+
|
|
}
|
|
|
|
static void
|
|
@@ -5544,7 +5602,6 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
|
|
stream->on_keyframe = TRUE;
|
|
}
|
|
|
|
-
|
|
GST_LOG_OBJECT (qtdemux,
|
|
"Pushing buffer with dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT
|
|
", duration %" GST_TIME_FORMAT " on pad %s", GST_TIME_ARGS (dts),
|
|
@@ -5574,11 +5631,72 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
|
|
if (G_LIKELY (index >= 0 && index < info->crypto_info->len)) {
|
|
/* steal structure from array */
|
|
crypto_info = g_ptr_array_index (info->crypto_info, index);
|
|
- g_ptr_array_index (info->crypto_info, index) = NULL;
|
|
- GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
|
|
- info->crypto_info->len);
|
|
- if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
|
|
- GST_ERROR_OBJECT (qtdemux, "failed to attach cenc metadata to buffer");
|
|
+
|
|
+ /* playready Decrypt */
|
|
+ if (qtdemux->protected_playready) {
|
|
+ const GValue *value;
|
|
+ guint subsample_count = 0;
|
|
+ GstBuffer *buffer = NULL;
|
|
+ GstMapInfo map;
|
|
+ guint8 *data;
|
|
+ guint size;
|
|
+ guint i;
|
|
+ guint ret = 0;
|
|
+ guint64 iv = 0;
|
|
+ guint32 region_count = 0;
|
|
+ guint32 regions[32]; /* 16 pairs max */
|
|
+
|
|
+ /* Get decryption information */
|
|
+ if (gst_structure_has_field_typed (crypto_info, "iv", GST_TYPE_BUFFER)) {
|
|
+ gst_structure_get (crypto_info, "iv", GST_TYPE_BUFFER, &buffer, NULL);
|
|
+ gst_buffer_map (buffer, &map, GST_MAP_READ);
|
|
+ data = map.data;
|
|
+ size = map.size;
|
|
+ iv = QT_UINT64(data);
|
|
+ GST_DEBUG_OBJECT (qtdemux, "info->crypto_info index %u iv = %"G_GUINT64_FORMAT, index, iv);
|
|
+ gst_buffer_unmap (buffer, &map);
|
|
+ }
|
|
+ if (gst_structure_has_field_typed (crypto_info, "subsample_count", G_TYPE_UINT)) {
|
|
+ value = gst_structure_get_value (crypto_info, "subsample_count");
|
|
+ subsample_count = g_value_get_uint (value);
|
|
+ region_count = subsample_count * 2;
|
|
+ GST_DEBUG_OBJECT (qtdemux, "info->crypto_info index %d region_count %u", index, region_count);
|
|
+ }
|
|
+ if (gst_structure_has_field_typed (crypto_info, "subsamples", GST_TYPE_BUFFER)) {
|
|
+ gst_structure_get (crypto_info, "subsamples", GST_TYPE_BUFFER, &buffer, NULL);
|
|
+ gst_buffer_map (buffer, &map, GST_MAP_READ);
|
|
+ data = map.data;
|
|
+ size = map.size;
|
|
+ if (size < subsample_count*6){
|
|
+ GST_ERROR_OBJECT (qtdemux, "subsamples size error");
|
|
+ gst_buffer_unmap (buffer, &map);
|
|
+ goto exit;
|
|
+ }
|
|
+ for(i = 0; i < subsample_count; ++i){
|
|
+ if (i < 16) {
|
|
+ regions[i*2] = QT_UINT16(data);
|
|
+ regions[i*2+1] = QT_UINT32(data+2);
|
|
+ }
|
|
+ GST_DEBUG_OBJECT (qtdemux, "info->crypto_info index %u subsamples regions[%d] = %u", index, i*2, regions[i*2]);
|
|
+ GST_DEBUG_OBJECT (qtdemux, "info->crypto_info index %u subsamples regions[%d] = %u", index, i*2+1, regions[i*2+1]);
|
|
+ data += 6;
|
|
+ }
|
|
+ gst_buffer_unmap (buffer, &map);
|
|
+ }
|
|
+
|
|
+ /* Decrypt */
|
|
+ GST_LOG_OBJECT (qtdemux, "Will be decrypted");
|
|
+ ret = PlayReadyDecrypt(buf, iv, region_count, regions);
|
|
+ if (ret < 0)
|
|
+ GST_ERROR_OBJECT (qtdemux, "decrypt failed");
|
|
+ } else {
|
|
+ g_ptr_array_index (info->crypto_info, index) = NULL;
|
|
+
|
|
+ GST_LOG_OBJECT (qtdemux, "attaching cenc metadata [%u/%u]", index,
|
|
+ info->crypto_info->len);
|
|
+ if (!crypto_info || !gst_buffer_add_protection_meta (buf, crypto_info))
|
|
+ GST_ERROR_OBJECT (qtdemux, "failed to attach cenc metadata to buffer");
|
|
+ }
|
|
} else {
|
|
GST_INFO_OBJECT (qtdemux, "No crypto info with index %d and sample %d",
|
|
index, stream->sample_index);
|
|
@@ -7980,7 +8098,7 @@ gst_qtdemux_configure_stream (GstQTDemux * qtdemux, QtDemuxStream * stream)
|
|
|
|
gst_pad_use_fixed_caps (stream->pad);
|
|
|
|
- if (stream->protected) {
|
|
+ if (stream->protected && !qtdemux->protected_playready) {
|
|
if (!gst_qtdemux_configure_protected_caps (qtdemux, stream)) {
|
|
GST_ERROR_OBJECT (qtdemux,
|
|
"Failed to configure protected stream caps.");
|
|
diff --git a/gst/isomp4/qtdemux.h b/gst/isomp4/qtdemux.h
|
|
index ad4da3e..8474027 100644
|
|
--- a/gst/isomp4/qtdemux.h
|
|
+++ b/gst/isomp4/qtdemux.h
|
|
@@ -151,7 +151,8 @@ struct _GstQTDemux {
|
|
guint64 cenc_aux_info_offset;
|
|
guint8 *cenc_aux_info_sizes;
|
|
guint32 cenc_aux_sample_count;
|
|
-
|
|
+ gboolean playready_openDRM; /* Record whether the DRM has been opened */
|
|
+ gboolean protected_playready; /* playready encryption flag */
|
|
|
|
/*
|
|
* ALL VARIABLES BELOW ARE ONLY USED IN PUSH-BASED MODE
|