This is an experimental nightly build of SeaMonkey for OS/2. It was made from code in comm-central and mozilla-1.9.1 dated 2009-08-01 05:14:14 PDT (changesets 99a2ac5bc318 and 0274a35f0e16, resp.). But it contains code that is not yet in the Mercurial repositories. This code is partly needed to complete the build (with GCC 3.3.5, bug 451278 and bug 453705) and partly to enable new features that are available on other platforms but not yet on OS/2 (media support, bug 448918/bug 495352), fixes to plugins and sound (bug 500654, bug 503744). See all patches against mozilla-1.9.1 below. ================================================================================ [OS/2] Bug 453705: workaround to fix builds break when compiling dom_quickstubs.cpp diff --git a/js/src/xpconnect/src/qsgen.py b/js/src/xpconnect/src/qsgen.py --- a/js/src/xpconnect/src/qsgen.py +++ b/js/src/xpconnect/src/qsgen.py @@ -472,6 +472,12 @@ "XPCVariant::newVariant(ccx, ${argVal})));\n" " if (!${name})\n" " return JS_FALSE;\n") + if os.name == 'os2': # Workaround for GCC 3.3.x bug. + template = ( + " nsCOMPtr ${name}(" + "XPCVariant::newVariant(ccx, ${argVal}));\n" + " if (!${name})\n" + " return JS_FALSE;\n") f.write(substitute(template, params)) return rvdeclared elif type.name == 'nsIAtom': # HG changeset patch # User Peter Weilbacher # Date 1219360007 -10800 # Node ID 6cf68fab7f3c45c35ef3a710c602398f66a0be63 # Parent 73967cc9e4ee07b66a5a3f9a35958027a51507d5 Bug 451278: work around GCC 3.3 bug that breaks building on OS/2 diff --git a/xpcom/string/public/nsTString.h b/xpcom/string/public/nsTString.h --- a/xpcom/string/public/nsTString.h +++ b/xpcom/string/public/nsTString.h @@ -444,9 +444,14 @@ * the length of the string already contained in the buffer */ +#ifdef XP_OS2 /* Workaround for GCC 3.3.x bug. */ + nsTFixedString_CharT( char_type* data, size_type storageSize ) NS_COM; + nsTFixedString_CharT( char_type* data, size_type storageSize, size_type length ) NS_COM; +#else NS_COM nsTFixedString_CharT( char_type* data, size_type storageSize ); NS_COM nsTFixedString_CharT( char_type* data, size_type storageSize, size_type length ); +#endif // |operator=| does not inherit, so we must define our own self_type& operator=( char_type c ) { Assign(c); return *this; } diff --git a/xpcom/string/public/nsTSubstring.h b/xpcom/string/public/nsTSubstring.h --- a/xpcom/string/public/nsTSubstring.h +++ b/xpcom/string/public/nsTSubstring.h @@ -496,7 +496,11 @@ * this is public to support automatic conversion of tuple to string * base type, which helps avoid converting to nsTAString. */ +#ifdef XP_OS2 /* Workaround for GCC 3.3.x bug. */ + nsTSubstring_CharT(const substring_tuple_type& tuple) NS_COM; +#else NS_COM nsTSubstring_CharT(const substring_tuple_type& tuple); +#endif /** * allows for direct initialization of a nsTSubstring object. @@ -522,7 +526,11 @@ PRUint32 mFlags; // default initialization +#ifdef XP_OS2 /* Workaround for GCC 3.3.x bug. */ + nsTSubstring_CharT() NS_COM; +#else NS_COM nsTSubstring_CharT(); +#endif // version of constructor that leaves mData and mLength uninitialized explicit @@ -530,7 +538,11 @@ // copy-constructor, constructs as dependent on given object // (NOTE: this is for internal use only) +#ifdef XP_OS2 /* Workaround for GCC 3.3.x bug. */ + nsTSubstring_CharT( const self_type& str ) NS_COM; +#else NS_COM nsTSubstring_CharT( const self_type& str ); +#endif /** * this function releases mData and does not change the value of diff --git a/media/liboggplay/README_MOZILLA b/media/liboggplay/README_MOZILLA --- a/media/liboggplay/README_MOZILLA +++ b/media/liboggplay/README_MOZILLA @@ -39,5 +39,9 @@ bug495129b.patch: Fix from liboggplay commit 3602bf. bug487519.patch: Fix for bug 487519. + +oggplay_os2.patch: Bug 448918 - add OS/2 support (this patch should be + removed when OS/2 support is added upstream) + bug498815.patch: Fix for bug 498815. bug498824.patch: Fix for bug 498824. diff --git a/media/liboggplay/oggplay_os2.patch b/media/liboggplay/oggplay_os2.patch new file mode 100644 --- /dev/null +++ b/media/liboggplay/oggplay_os2.patch @@ -0,0 +1,304 @@ +diff --git a/media/liboggplay/src/liboggplay/oggplay_private.h b/media/liboggplay/src/liboggplay/oggplay_private.h +--- a/media/liboggplay/src/liboggplay/oggplay_private.h ++++ b/media/liboggplay/src/liboggplay/oggplay_private.h +@@ -62,16 +62,22 @@ + + #ifdef WIN32 + + #ifdef HAVE_WINSOCK2 + #include + #else + #include + #endif ++#endif ++ ++#ifdef OS2 ++#define INCL_DOSSEMAPHORES ++#define INCL_DOSPROCESS ++#include + #endif + + // for Win32 has to be included last + #include "std_semaphore.h" + + /** + * + * has_been_presented: 0 until the data has been added as a "required" element, +diff --git a/media/liboggplay/src/liboggplay/oggplay_tools.c b/media/liboggplay/src/liboggplay/oggplay_tools.c +--- a/media/liboggplay/src/liboggplay/oggplay_tools.c ++++ b/media/liboggplay/src/liboggplay/oggplay_tools.c +@@ -53,15 +53,17 @@ oggplay_sys_time_in_ms(void) { + return (ogg_int64_t)tv.tv_sec * 1000 + (ogg_int64_t)tv.tv_usec / 1000; + #endif + } + + void + oggplay_millisleep(long ms) { + #ifdef WIN32 + Sleep(ms); ++#elif defined(OS2) ++ DosSleep(ms); + #else + struct timespec ts = {0, (ogg_int64_t)ms * 1000000LL}; + nanosleep(&ts, NULL); + #endif + } + + +diff --git a/media/liboggplay/src/liboggplay/os2_semaphore.c b/media/liboggplay/src/liboggplay/os2_semaphore.c +new file mode 100644 +--- /dev/null ++++ b/media/liboggplay/src/liboggplay/os2_semaphore.c +@@ -0,0 +1,163 @@ ++/* ***** BEGIN LICENSE BLOCK ***** ++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 ++ * ++ * The contents of this file are subject to the Mozilla Public License Version ++ * 1.1 (the "License"); you may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at ++ * http://www.mozilla.org/MPL/ ++ * ++ * Software distributed under the License is distributed on an "AS IS" basis, ++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License ++ * for the specific language governing rights and limitations under the ++ * License. ++ * ++ * The Original Code is the Mozilla browser. ++ * ++ * The Initial Developer of the Original Code is ++ * Richard Walsh ++ * Portions created by the Initial Developer are Copyright (c) 2008 ++ * the Initial Developer. All Rights Reserved. ++ * ++ * Contributor(s): ++ * ++ * Alternatively, the contents of this file may be used under the terms of ++ * either the GNU General Public License Version 2 or later (the "GPL"), or ++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), ++ * in which case the provisions of the GPL or the LGPL are applicable instead ++ * of those above. If you wish to allow use of your version of this file only ++ * under the terms of either the GPL or the LGPL, and not to allow others to ++ * use your version of this file under the terms of the MPL, indicate your ++ * decision by deleting the provisions above and replace them with the notice ++ * and other provisions required by the GPL or the LGPL. If you do not delete ++ * the provisions above, a recipient may use your version of this file under ++ * the terms of any one of the MPL, the GPL or the LGPL. ++ * ++ * ***** END LICENSE BLOCK ***** * ++ */ ++/*****************************************************************************/ ++/* ++ * This is a conservative implementation of a posix counting semaphore. ++ * It relies on the state of the underlying OS/2 event semaphore to ++ * control whether sem_wait() blocks or returns immediately, and only ++ * uses its count to change the sem's state from posted to reset. ++ * (A more "activist" approach would use the count to decide whether ++ * to return immediately or to call DosWaitEventSem().) ++ * ++ */ ++/*****************************************************************************/ ++ ++#include ++#define INCL_DOS ++#include ++#include "os2_semaphore.h" ++ ++#ifndef ERROR_SEM_BUSY ++#define ERROR_SEM_BUSY 301 ++#endif ++ ++#define SEM_WAIT 20000 ++ ++/*****************************************************************************/ ++ ++int sem_init(sem_t *sem, int pshared, unsigned value) ++{ ++ OS2SEM * psem; ++ ++ if (!sem) ++ return -1; ++ *sem = 0; ++ ++ psem = (OS2SEM*)malloc(sizeof(OS2SEM)); ++ if (!psem) ++ return -1; ++ ++ if (DosCreateMutexSem(0, &psem->hmtx, 0, 0)) { ++ free(psem); ++ return -1; ++ } ++ ++ if (DosCreateEventSem(0, &psem->hev, 0, (value ? 1 : 0))) { ++ DosCloseMutexSem(psem->hmtx); ++ free(psem); ++ return -1; ++ } ++ ++ psem->cnt = value; ++ *sem = psem; ++ return 0; ++} ++ ++/*****************************************************************************/ ++ ++int sem_wait(sem_t *sem) ++{ ++ OS2SEM * psem; ++ ULONG cnt; ++ ++ if (!sem || !*sem) ++ return -1; ++ psem = *sem; ++ ++ if (DosWaitEventSem(psem->hev, -1)) ++ return -1; ++ ++ if (DosRequestMutexSem(psem->hmtx, SEM_WAIT)) ++ return -1; ++ ++ if (psem->cnt) ++ psem->cnt--; ++ if (!psem->cnt) ++ DosResetEventSem(psem->hev, &cnt); ++ DosReleaseMutexSem(psem->hmtx); ++ ++ return 0; ++} ++ ++/*****************************************************************************/ ++ ++int sem_post(sem_t *sem) ++{ ++ OS2SEM * psem; ++ ++ if (!sem || !*sem) ++ return -1; ++ psem = *sem; ++ ++ if (!DosRequestMutexSem(psem->hmtx, SEM_WAIT)) { ++ psem->cnt++; ++ DosPostEventSem(psem->hev); ++ DosReleaseMutexSem(psem->hmtx); ++ return 0; ++ } ++ ++ return -1; ++} ++ ++/*****************************************************************************/ ++ ++int sem_destroy(sem_t *sem) ++{ ++ OS2SEM * psem; ++ ++ if (!sem || !*sem) ++ return -1; ++ psem = *sem; ++ ++ if (DosCloseMutexSem(psem->hmtx) == ERROR_SEM_BUSY) { ++ DosRequestMutexSem(psem->hmtx, SEM_WAIT); ++ DosReleaseMutexSem(psem->hmtx); ++ DosCloseMutexSem(psem->hmtx); ++ } ++ ++ if (DosCloseEventSem(psem->hev) == ERROR_SEM_BUSY) { ++ DosPostEventSem(psem->hev); ++ DosSleep(1); ++ DosCloseEventSem(psem->hev); ++ } ++ ++ free(psem); ++ return 0; ++} ++ ++/*****************************************************************************/ ++ +diff --git a/media/liboggplay/src/liboggplay/os2_semaphore.h b/media/liboggplay/src/liboggplay/os2_semaphore.h +new file mode 100644 +--- /dev/null ++++ b/media/liboggplay/src/liboggplay/os2_semaphore.h +@@ -0,0 +1,57 @@ ++/* ***** BEGIN LICENSE BLOCK ***** ++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 ++ * ++ * The contents of this file are subject to the Mozilla Public License Version ++ * 1.1 (the "License"); you may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at ++ * http://www.mozilla.org/MPL/ ++ * ++ * Software distributed under the License is distributed on an "AS IS" basis, ++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License ++ * for the specific language governing rights and limitations under the ++ * License. ++ * ++ * The Original Code is the Mozilla browser. ++ * ++ * The Initial Developer of the Original Code is ++ * Richard Walsh ++ * Portions created by the Initial Developer are Copyright (c) 2008 ++ * the Initial Developer. All Rights Reserved. ++ * ++ * Contributor(s): ++ * ++ * Alternatively, the contents of this file may be used under the terms of ++ * either the GNU General Public License Version 2 or later (the "GPL"), or ++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), ++ * in which case the provisions of the GPL or the LGPL are applicable instead ++ * of those above. If you wish to allow use of your version of this file only ++ * under the terms of either the GPL or the LGPL, and not to allow others to ++ * use your version of this file under the terms of the MPL, indicate your ++ * decision by deleting the provisions above and replace them with the notice ++ * and other provisions required by the GPL or the LGPL. If you do not delete ++ * the provisions above, a recipient may use your version of this file under ++ * the terms of any one of the MPL, the GPL or the LGPL. ++ * ++ * ***** END LICENSE BLOCK ***** * ++ */ ++/*****************************************************************************/ ++ ++#ifndef _os2_semaphore_ ++#define _os2_semaphore_ ++ ++typedef struct _OS2SEM { ++ HMTX hmtx; ++ HEV hev; ++ ULONG cnt; ++} OS2SEM; ++ ++typedef OS2SEM * sem_t; ++ ++int sem_init(sem_t *sem, int pshared, unsigned value); ++int sem_wait(sem_t *sem); ++int sem_post(sem_t *sem); ++int sem_destroy(sem_t *sem); ++ ++#endif ++/*****************************************************************************/ ++ +diff --git a/media/liboggplay/src/liboggplay/std_semaphore.h b/media/liboggplay/src/liboggplay/std_semaphore.h +--- a/media/liboggplay/src/liboggplay/std_semaphore.h ++++ b/media/liboggplay/src/liboggplay/std_semaphore.h +@@ -83,16 +83,23 @@ typedef sem_t semaphore; + typedef sem_t semaphore; + #elif defined(WIN32) + #include + #define SEM_CREATE(p,s) (!(p = CreateSemaphore(NULL, (long)(s), (long)(s), NULL))) + #define SEM_SIGNAL(p) (!ReleaseSemaphore(p, 1, NULL)) + #define SEM_WAIT(p) WaitForSingleObject(p, INFINITE) + #define SEM_CLOSE(p) (!CloseHandle(p)) + typedef HANDLE semaphore; ++#elif defined(OS2) ++#include "os2_semaphore.h" ++#define SEM_CREATE(p,s) sem_init(&(p), 1, s) ++#define SEM_SIGNAL(p) sem_post(&(p)) ++#define SEM_WAIT(p) sem_wait(&(p)) ++#define SEM_CLOSE(p) sem_destroy(&(p)) ++typedef sem_t semaphore; + #elif defined(__APPLE__) + #include + #define SEM_CREATE(p,s) MPCreateSemaphore(s, s, &(p)) + #define SEM_SIGNAL(p) MPSignalSemaphore(p) + #define SEM_WAIT(p) MPWaitOnSemaphore(p, kDurationForever) + #define SEM_CLOSE(p) MPDeleteSemaphore(p) + typedef MPSemaphoreID semaphore; + #endif diff --git a/media/liboggplay/src/liboggplay/Makefile.in b/media/liboggplay/src/liboggplay/Makefile.in --- a/media/liboggplay/src/liboggplay/Makefile.in +++ b/media/liboggplay/src/liboggplay/Makefile.in @@ -68,4 +68,14 @@ oggplay_tools.c \ $(NULL) +ifeq ($(OS_ARCH),OS2) +EXPORTS += \ + os2_semaphore.h \ + $(NULL) + +CSRCS += \ + os2_semaphore.c \ + $(NULL) +endif + include $(topsrcdir)/config/rules.mk diff --git a/media/liboggplay/src/liboggplay/oggplay_private.h b/media/liboggplay/src/liboggplay/oggplay_private.h --- a/media/liboggplay/src/liboggplay/oggplay_private.h +++ b/media/liboggplay/src/liboggplay/oggplay_private.h @@ -67,6 +67,12 @@ #else #include #endif +#endif + +#ifdef OS2 +#define INCL_DOSSEMAPHORES +#define INCL_DOSPROCESS +#include #endif // for Win32 has to be included last diff --git a/media/liboggplay/src/liboggplay/oggplay_tools.c b/media/liboggplay/src/liboggplay/oggplay_tools.c --- a/media/liboggplay/src/liboggplay/oggplay_tools.c +++ b/media/liboggplay/src/liboggplay/oggplay_tools.c @@ -58,6 +58,8 @@ oggplay_millisleep(long ms) { #ifdef WIN32 Sleep(ms); +#elif defined(OS2) + DosSleep(ms); #else struct timespec ts = {0, (ogg_int64_t)ms * 1000000LL}; nanosleep(&ts, NULL); diff --git a/media/liboggplay/src/liboggplay/os2_semaphore.c b/media/liboggplay/src/liboggplay/os2_semaphore.c new file mode 100644 --- /dev/null +++ b/media/liboggplay/src/liboggplay/os2_semaphore.c @@ -0,0 +1,163 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Mozilla browser. + * + * The Initial Developer of the Original Code is + * Richard Walsh + * Portions created by the Initial Developer are Copyright (c) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** * + */ +/*****************************************************************************/ +/* + * This is a conservative implementation of a posix counting semaphore. + * It relies on the state of the underlying OS/2 event semaphore to + * control whether sem_wait() blocks or returns immediately, and only + * uses its count to change the sem's state from posted to reset. + * (A more "activist" approach would use the count to decide whether + * to return immediately or to call DosWaitEventSem().) + * + */ +/*****************************************************************************/ + +#include +#define INCL_DOS +#include +#include "os2_semaphore.h" + +#ifndef ERROR_SEM_BUSY +#define ERROR_SEM_BUSY 301 +#endif + +#define SEM_WAIT 20000 + +/*****************************************************************************/ + +int sem_init(sem_t *sem, int pshared, unsigned value) +{ + OS2SEM * psem; + + if (!sem) + return -1; + *sem = 0; + + psem = (OS2SEM*)malloc(sizeof(OS2SEM)); + if (!psem) + return -1; + + if (DosCreateMutexSem(0, &psem->hmtx, 0, 0)) { + free(psem); + return -1; + } + + if (DosCreateEventSem(0, &psem->hev, 0, (value ? 1 : 0))) { + DosCloseMutexSem(psem->hmtx); + free(psem); + return -1; + } + + psem->cnt = value; + *sem = psem; + return 0; +} + +/*****************************************************************************/ + +int sem_wait(sem_t *sem) +{ + OS2SEM * psem; + ULONG cnt; + + if (!sem || !*sem) + return -1; + psem = *sem; + + if (DosWaitEventSem(psem->hev, -1)) + return -1; + + if (DosRequestMutexSem(psem->hmtx, SEM_WAIT)) + return -1; + + if (psem->cnt) + psem->cnt--; + if (!psem->cnt) + DosResetEventSem(psem->hev, &cnt); + DosReleaseMutexSem(psem->hmtx); + + return 0; +} + +/*****************************************************************************/ + +int sem_post(sem_t *sem) +{ + OS2SEM * psem; + + if (!sem || !*sem) + return -1; + psem = *sem; + + if (!DosRequestMutexSem(psem->hmtx, SEM_WAIT)) { + psem->cnt++; + DosPostEventSem(psem->hev); + DosReleaseMutexSem(psem->hmtx); + return 0; + } + + return -1; +} + +/*****************************************************************************/ + +int sem_destroy(sem_t *sem) +{ + OS2SEM * psem; + + if (!sem || !*sem) + return -1; + psem = *sem; + + if (DosCloseMutexSem(psem->hmtx) == ERROR_SEM_BUSY) { + DosRequestMutexSem(psem->hmtx, SEM_WAIT); + DosReleaseMutexSem(psem->hmtx); + DosCloseMutexSem(psem->hmtx); + } + + if (DosCloseEventSem(psem->hev) == ERROR_SEM_BUSY) { + DosPostEventSem(psem->hev); + DosSleep(1); + DosCloseEventSem(psem->hev); + } + + free(psem); + return 0; +} + +/*****************************************************************************/ + diff --git a/media/liboggplay/src/liboggplay/os2_semaphore.h b/media/liboggplay/src/liboggplay/os2_semaphore.h new file mode 100644 --- /dev/null +++ b/media/liboggplay/src/liboggplay/os2_semaphore.h @@ -0,0 +1,57 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Mozilla browser. + * + * The Initial Developer of the Original Code is + * Richard Walsh + * Portions created by the Initial Developer are Copyright (c) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** * + */ +/*****************************************************************************/ + +#ifndef _os2_semaphore_ +#define _os2_semaphore_ + +typedef struct _OS2SEM { + HMTX hmtx; + HEV hev; + ULONG cnt; +} OS2SEM; + +typedef OS2SEM * sem_t; + +int sem_init(sem_t *sem, int pshared, unsigned value); +int sem_wait(sem_t *sem); +int sem_post(sem_t *sem); +int sem_destroy(sem_t *sem); + +#endif +/*****************************************************************************/ + diff --git a/media/liboggplay/src/liboggplay/std_semaphore.h b/media/liboggplay/src/liboggplay/std_semaphore.h --- a/media/liboggplay/src/liboggplay/std_semaphore.h +++ b/media/liboggplay/src/liboggplay/std_semaphore.h @@ -88,6 +88,13 @@ #define SEM_WAIT(p) WaitForSingleObject(p, INFINITE) #define SEM_CLOSE(p) (!CloseHandle(p)) typedef HANDLE semaphore; +#elif defined(OS2) +#include "os2_semaphore.h" +#define SEM_CREATE(p,s) sem_init(&(p), 1, s) +#define SEM_SIGNAL(p) sem_post(&(p)) +#define SEM_WAIT(p) sem_wait(&(p)) +#define SEM_CLOSE(p) sem_destroy(&(p)) +typedef sem_t semaphore; #elif defined(__APPLE__) #include #define SEM_CREATE(p,s) MPCreateSemaphore(s, s, &(p)) diff --git a/media/liboggplay/update.sh b/media/liboggplay/update.sh --- a/media/liboggplay/update.sh +++ b/media/liboggplay/update.sh @@ -59,5 +59,6 @@ patch -p3 < bug495129a.patch patch -p3 < bug495129b.patch patch -p3 < bug487519.patch +patch -p3 < oggplay_os2.patch patch -p3 < bug498815.patch patch -p3 < bug498824.patch diff --git a/media/liboggz/README_MOZILLA b/media/liboggz/README_MOZILLA --- a/media/liboggz/README_MOZILLA +++ b/media/liboggz/README_MOZILLA @@ -21,3 +21,7 @@ bug487519.patch: Fix for bug 487519. bug496063.patch: Fix for infinite loop during seek while shutting down. + +oggz_os2.patch: Bug 448918 - add OS/2 support (this patch should be + removed when OS/2 support is added upstream) + diff --git a/media/liboggz/include/oggz/oggz_off_t_generated.h b/media/liboggz/include/oggz/oggz_off_t_generated.h --- a/media/liboggz/include/oggz/oggz_off_t_generated.h +++ b/media/liboggz/include/oggz/oggz_off_t_generated.h @@ -59,7 +59,7 @@ #include -#if defined(__APPLE__) || defined(SOLARIS) +#if defined(__APPLE__) || defined(SOLARIS) || defined(OS2) typedef off_t oggz_off_t; #else typedef loff_t oggz_off_t; diff --git a/media/liboggz/oggz_os2.patch b/media/liboggz/oggz_os2.patch new file mode 100644 --- /dev/null +++ b/media/liboggz/oggz_os2.patch @@ -0,0 +1,22 @@ +diff --git a/media/liboggz/include/oggz/oggz_off_t_generated.h b/media/liboggz/include/oggz/oggz_off_t_generated.h +--- a/media/liboggz/include/oggz/oggz_off_t_generated.h ++++ b/media/liboggz/include/oggz/oggz_off_t_generated.h +@@ -54,17 +54,17 @@ +
+    echo "gcc -E oggz.h | grep oggz_off_t
+  
+ * + */ + + #include + +-#if defined(__APPLE__) || defined(SOLARIS) ++#if defined(__APPLE__) || defined(SOLARIS) || defined(OS2) + typedef off_t oggz_off_t; + #else + typedef loff_t oggz_off_t; + #endif + + #define PRI_OGGZ_OFF_T "PRId64" + + #endif /* __OGGZ_OFF_T_GENERATED__ */ diff --git a/media/liboggz/update.sh b/media/liboggz/update.sh --- a/media/liboggz/update.sh +++ b/media/liboggz/update.sh @@ -51,3 +51,4 @@ patch -p3 +#include +#include +#include +#include "sydney_audio.h" + +#define INCL_DOS +#define INCL_MCIOS2 +#include +#include + +/*****************************************************************************/ + +/* this will have to be changed to a variable + * if other than 16-bit samples are ever supported */ +#define SAOS2_SAMPLE_SIZE 2 + +/* the number of buffers to allocate - the ogg decoder typically + * writes 8k at a time, so this works out to roughly 1/2 second */ +#define SAOS2_BUF_CNT 11 + +/* this could be as large as 65535 but making it smaller helps + * avoid having the DART event thread think it's running out of + * buffers if the decoder sends larger chunks of data less often */ +#define SAOS2_RAW_BUFSIZE 16384 + +/* playback states */ +#define SAOS2_INIT 0 +#define SAOS2_RECOVER 1 +#define SAOS2_PLAY 2 +#define SAOS2_EXIT 3 + +/* an indefinite wait invites a hung thread */ +#define SAOS2_SEM_WAIT 5000 + +/* the only 2 return codes we care about */ +#ifndef INCL_DOSERRORS +#define ERROR_ALREADY_POSTED 299 +#define ERROR_ALREADY_RESET 300 +#endif + +/*****************************************************************************/ +/* Debug */ + +#ifdef DEBUG + #ifndef SAOS2_ERROR + #define SAOS2_ERROR + #endif +#endif + +#ifdef SAOS2_ERROR + static int os2_error_msg(int rtn, char * func, char * msg, uint32_t err); + #define os2_error(rtn, func, msg, err) os2_error_msg(rtn, func, msg, err) +#else + #define os2_error(rtn, func, msg, err) rtn +#endif + +/*****************************************************************************/ +/* OS/2 implementation of sa_stream_t */ + +struct sa_stream { + + /* audio format info */ + const char * client_name; + sa_mode_t mode; + sa_pcm_format_t format; + uint32_t rate; + uint32_t nchannels; + + /* device info */ + uint16_t hwDeviceID; + uint32_t hwMixHandle; + PMIXERPROC hwWriteProc; + + /* buffer allocations */ + int32_t bufCnt; + size_t bufSize; + PMCI_MIX_BUFFER bufList; + + /* buffer usage tracking */ + HEV freeSem; + int32_t freeCnt; + int32_t freeNdx; + int32_t readyCnt; + int32_t readyNdx; + HEV usedSem; + volatile int32_t usedCnt; + + /* miscellaneous */ + volatile int32_t state; + int64_t writePos; + /* workaround for Bug 495352 */ + uint32_t zeroCnt; +}; + +/*****************************************************************************/ +/* Private (static) Functions */ + +static int32_t os2_mixer_event(uint32_t ulStatus, PMCI_MIX_BUFFER pBuffer, + uint32_t ulFlags); +static int os2_write_to_device(sa_stream_t *s); +static void os2_stop_device(uint16_t hwDeviceID); +static int os2_pause_device(uint16_t hwDeviceID, uint32_t release); +static int os2_get_free_count(sa_stream_t *s, int32_t count); + +/*****************************************************************************/ +/* Mozilla-specific Additions */ + +/* reset the decode thread's priority */ +static void os2_set_priority(void); + +/* load mdm.dll on demand */ +static int os2_load_mdm(void); + +/* invoke mciSendCommand() via a static variable */ +typedef ULONG _System MCISENDCOMMAND(USHORT, USHORT, ULONG, PVOID, USHORT); +static MCISENDCOMMAND * _mciSendCommand = 0; + +/*****************************************************************************/ +/* Sydney Audio Functions */ +/*****************************************************************************/ + +/** Normal way to open a PCM device */ + +int sa_stream_create_pcm(sa_stream_t ** s, + const char * client_name, + sa_mode_t mode, + sa_pcm_format_t format, + unsigned int rate, + unsigned int nchannels) +{ + uint32_t status = SA_SUCCESS; + uint32_t size; + uint32_t rc; + sa_stream_t * sTemp = 0; + + /* this do{}while(0) "loop" makes it easy to ensure + * resources are freed on exit if there's an error */ +do { + /* load mdm.dll if it isn't already loaded */ + if (os2_load_mdm() != SA_SUCCESS) + return SA_ERROR_SYSTEM; + + if (mode != SA_MODE_WRONLY || format != SA_PCM_FORMAT_S16_LE) + return os2_error(SA_ERROR_NOT_SUPPORTED, "sa_stream_create_pcm", + "invalid mode or format", 0); + + if (!s) + return os2_error(SA_ERROR_INVALID, "sa_stream_create_pcm", + "s is null", 0); + *s = 0; + + /* the MCI_MIX_BUFFERs must be in low memory or terrible things will + * happen! - since there's extra space, put 'sa_stream' there too */ + size = sizeof(sa_stream_t) + sizeof(PMCI_MIX_BUFFER) * SAOS2_BUF_CNT; + rc = DosAllocMem((void**)&sTemp, size, + PAG_COMMIT | PAG_READ | PAG_WRITE); + if (rc) { + status = os2_error(SA_ERROR_OOM, "sa_stream_create_pcm", + "DosAllocMem - rc=", rc); + break; + } + + memset(sTemp, 0, size); + sTemp->bufList = (PMCI_MIX_BUFFER)&sTemp[1]; + + /* set the number of buffers; round the buffer + * size down to the nearest multiple of a frame; */ + sTemp->bufCnt = SAOS2_BUF_CNT; + sTemp->bufSize = SAOS2_RAW_BUFSIZE - + (SAOS2_RAW_BUFSIZE % (SAOS2_SAMPLE_SIZE * nchannels)); + + /* create event semaphores to signal free buffers */ + rc = DosCreateEventSem(0, &sTemp->freeSem, 0, FALSE); + if (!rc) + rc = DosCreateEventSem(0, &sTemp->usedSem, 0, FALSE); + if (rc) { + status = os2_error(SA_ERROR_SYSTEM, "sa_stream_create_pcm", + "DosCreateEventSem - rc=", rc); + break; + } + + /* fill in the miscellanea */ + sTemp->client_name = client_name; + sTemp->mode = mode; + sTemp->format = format; + sTemp->rate = rate; + sTemp->nchannels = nchannels; + + *s = sTemp; + +} while (0); + + /* on error, free any allocations */ + if (status != SA_SUCCESS && sTemp) { + if (sTemp->freeSem) + DosCloseEventSem(sTemp->freeSem); + if (sTemp->usedSem) + DosCloseEventSem(sTemp->usedSem); + if (sTemp) + DosFreeMem(sTemp); + } + + return status; +} + +/*****************************************************************************/ + +/** Initialise the device */ + +int sa_stream_open(sa_stream_t *s) +{ + int status = SA_SUCCESS; + uint32_t rc; + int32_t ctr; + uint32_t bufCntRequested; + MCI_AMP_OPEN_PARMS AmpOpenParms; + MCI_MIXSETUP_PARMS MixSetupParms; + MCI_BUFFER_PARMS BufferParms; + + if (!s) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_open", "s is null", 0); + +do { + /* set this thread's priority to 2-08 */ + os2_set_priority(); + + /* s->bufCnt will be restored after successfully allocating buffers */ + bufCntRequested = s->bufCnt; + s->bufCnt = 0; + + /* open the Amp-Mixer using the default device in shared mode */ + memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS)); + AmpOpenParms.pszDeviceType = (PSZ)(MCI_DEVTYPE_AUDIO_AMPMIX | 0); + + rc = _mciSendCommand(0, MCI_OPEN, + MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE, + (void*)&AmpOpenParms, 0); + if (LOUSHORT(rc)) { + status = os2_error(SA_ERROR_NO_DEVICE, "sa_stream_open", + "MCI_OPEN - rc=", LOUSHORT(rc)); + break; + } + + /* save the device ID */ + s->hwDeviceID = AmpOpenParms.usDeviceID; + + /* setup the Amp-Mixer to play wave data */ + memset(&MixSetupParms, 0, sizeof(MCI_MIXSETUP_PARMS)); + MixSetupParms.ulBitsPerSample = 16; + MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM; + MixSetupParms.ulFormatMode = MCI_PLAY; + MixSetupParms.ulSamplesPerSec = s->rate; + MixSetupParms.ulChannels = s->nchannels; + MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; + MixSetupParms.pmixEvent = (MIXEREVENT*)os2_mixer_event; + + rc = _mciSendCommand(s->hwDeviceID, MCI_MIXSETUP, + MCI_WAIT | MCI_MIXSETUP_INIT, + (void*)&MixSetupParms, 0); + if (LOUSHORT(rc)) { + status = os2_error(SA_ERROR_NOT_SUPPORTED, "sa_stream_open", + "MCI_MIXSETUP - rc=", LOUSHORT(rc)); + break; + } + + /* save hw info we'll need later */ + s->hwMixHandle = MixSetupParms.ulMixHandle; + s->hwWriteProc = MixSetupParms.pmixWrite; + + /* allocate device buffers from the Amp-Mixer */ + BufferParms.ulStructLength = sizeof(MCI_BUFFER_PARMS); + BufferParms.ulNumBuffers = bufCntRequested; + BufferParms.ulBufferSize = s->bufSize; + BufferParms.pBufList = s->bufList; + + rc = _mciSendCommand(s->hwDeviceID, MCI_BUFFER, + MCI_WAIT | MCI_ALLOCATE_MEMORY, + (void*)&BufferParms, 0); + if (LOUSHORT(rc)) { + status = os2_error(SA_ERROR_OOM, "sa_stream_open", + "MCI_ALLOCATE_MEMORY - rc=", LOUSHORT(rc)); + break; + } + + /* MCI_ALLOCATE_MEMORY may have decreased the, + * number of buffers, so update the counts */ + s->bufCnt = BufferParms.ulNumBuffers; + s->freeCnt = BufferParms.ulNumBuffers; + + /* sa_stream_write() & os2_mixer_event() require these initializations */ + for (ctr = 0; ctr < s->bufCnt; ctr++) { + s->bufList[ctr].ulStructLength = sizeof(MCI_MIX_BUFFER); + s->bufList[ctr].ulBufferLength = 0; + s->bufList[ctr].ulUserParm = (uint32_t)s; + } + +} while (0); + + return status; +} + +/*****************************************************************************/ + +/** Close/destroy everything */ + +int sa_stream_destroy(sa_stream_t *s) +{ + int status = SA_SUCCESS; + uint32_t rc; + MCI_GENERIC_PARMS GenericParms = { 0 }; + MCI_BUFFER_PARMS BufferParms; + + if (!s) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_destroy", "s is null", 0); + + /* if the device was opened, close it */ + if (s->hwDeviceID) { + + /* prevent os2_mixer_event() from reacting to a buffer under-run */ + s->state = SAOS2_EXIT; + + /* stop the device (which may not actually be playing) */ + os2_stop_device(s->hwDeviceID); + + /* if hardware buffers were allocated, free them */ + if (s->bufCnt) { + BufferParms.hwndCallback = 0; + BufferParms.ulStructLength = sizeof(MCI_BUFFER_PARMS); + BufferParms.ulNumBuffers = s->bufCnt; + BufferParms.ulBufferSize = s->bufSize; + BufferParms.pBufList = s->bufList; + + rc = _mciSendCommand(s->hwDeviceID, MCI_BUFFER, + MCI_WAIT | MCI_DEALLOCATE_MEMORY, + (void*)&BufferParms, 0); + if (LOUSHORT(rc)) + status = os2_error(SA_ERROR_SYSTEM, "sa_stream_destroy", + "MCI_DEALLOCATE_MEMORY - rc=", LOUSHORT(rc)); + } + + rc = _mciSendCommand(s->hwDeviceID, MCI_CLOSE, + MCI_WAIT, + (void*)&GenericParms, 0); + if (LOUSHORT(rc)) + status = os2_error(SA_ERROR_SYSTEM, "sa_stream_destroy", + "MCI_CLOSE - rc=", LOUSHORT(rc)); + } + + /* free other resources we allocated */ + if (s->freeSem) + DosCloseEventSem(s->freeSem); + if (s->usedSem) + DosCloseEventSem(s->usedSem); + DosFreeMem(s); + + return status; +} + +/*****************************************************************************/ + +/** Interleaved playback function */ + +int sa_stream_write(sa_stream_t * s, const void * data, size_t nbytes) +{ + uint32_t rc; + size_t cnt; + PMCI_MIX_BUFFER pHW; + + if (!s) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_write", "s is null", 0); + if (!data) + return os2_error(SA_ERROR_INVALID, "sa_stream_write", "data is null", 0); + + /* exit if no data */ + /* Bug 495352 - this function may get called repeatedly with no data; + * as a workaround to prevent as many as 30,000 such calls between valid + * writes (and 100% CPU usage), give up the remainder of the current + * time-slice every time 16 consecutive zero-byte writes are detected */ + if (!nbytes) { + s->zeroCnt++; + if (!(s->zeroCnt & 0x0f)) { + s->zeroCnt = 0; + DosSleep(1); + } + return SA_SUCCESS; + } + + /* This should only loop on the last write before sa_stream_drain() + * is called; at other times, 'nbytes' won't exceed 'bufSize'. */ + while (nbytes) { + + /* get the count of free buffers, wait until at least one + * is available (in practice, this should never block) */ + if (os2_get_free_count(s, 1)) + return SA_ERROR_SYSTEM; + + /* copy as much as will fit into the buffer */ + pHW = &(s->bufList[s->freeNdx]); + cnt = (nbytes > s->bufSize) ? s->bufSize : nbytes; + memcpy(pHW->pBuffer, (char*)data, cnt); + pHW->ulBufferLength = cnt; + nbytes -= cnt; + data = (char*)data + cnt; + + /* adjust cnts & indices, then send the buffer to the device */ + s->freeCnt--; + s->freeNdx = (s->freeNdx + 1) % s->bufCnt; + s->readyCnt++; + if (os2_write_to_device(s)) + return SA_ERROR_SYSTEM; + } + + return SA_SUCCESS; +} + +/*****************************************************************************/ + +/** sync/timing */ + +int sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) +{ + uint32_t rc; + + if (!s || !pos) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_get_position", + "s or pos is null", 0); + + if (position != SA_POSITION_WRITE_SOFTWARE) + return os2_error(SA_ERROR_NOT_SUPPORTED, "sa_stream_get_position", + "unsupported postion type=", position); + + /* this is the nbr of bytes that are known to have been played + * already; the MCI command to get stream position isn't usable - + * it returns a time value that resets when the stream is paused */ + *pos = s->writePos; + + return SA_SUCCESS; +} + +/*****************************************************************************/ + +/** Resume playing after a pause */ + +int sa_stream_resume(sa_stream_t *s) +{ + uint32_t rc; + MCI_GENERIC_PARMS GenericParms = { 0 }; + + if (!s) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_resume", + "s is null", 0); + + rc = _mciSendCommand(s->hwDeviceID, MCI_ACQUIREDEVICE, + MCI_WAIT, + (void*)&GenericParms, 0); + if (LOUSHORT(rc)) + return os2_error(SA_ERROR_SYSTEM, "sa_stream_resume", + "MCI_ACQUIREDEVICE - rc=", LOUSHORT(rc)); + + rc = _mciSendCommand(s->hwDeviceID, MCI_RESUME, + MCI_WAIT, + (void*)&GenericParms, 0); + if (LOUSHORT(rc)) + return os2_error(SA_ERROR_SYSTEM, "sa_stream_resume", + "MCI_RESUME - rc=", LOUSHORT(rc)); + + return SA_SUCCESS; +} + +/*****************************************************************************/ + +/** Pause audio playback (do not empty the buffer) */ + +int sa_stream_pause(sa_stream_t *s) +{ + if (!s) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_pause", "s is null", 0); + + /* pause & release device */ + return os2_pause_device(s->hwDeviceID, TRUE); +} + +/*****************************************************************************/ + +/** Block until all audio has been played */ + +int sa_stream_drain(sa_stream_t *s) +{ + int status = SA_SUCCESS; + char buf[32]; + + if (!s) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_drain", "s is null", 0); + + /* keep os2_mixer_event() from reacting to buffer under-runs */ + s->state = SAOS2_EXIT; + + /* DART won't start playing until 2 buffers have been + * written, so write a dummy 2nd buffer just in case */ + memset(buf, 0, sizeof(buf)); + sa_stream_write(s, buf, s->nchannels * SAOS2_SAMPLE_SIZE); + + /* write all remaining buffers to the device */ + if (s->readyCnt) + status = os2_write_to_device(s); + + /* wait for all buffers to become free */ + if (!status) + status = os2_get_free_count(s, s->bufCnt); + + /* stop the device so it doesn't misbehave due to an under-run */ + os2_stop_device(s->hwDeviceID); + + return status; +} + +/*****************************************************************************/ + +/** Query how much can be written without blocking */ + +int sa_stream_get_write_size(sa_stream_t *s, size_t *size) +{ + if (!s) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_get_write_size", + "s is null", 0); + + /* return a non-zero value here in case the upstream code ignores + * the return code - if so, sa_stream_write() will fail instead */ + if (os2_get_free_count(s, 0)) { + *size = s->bufSize; + return SA_ERROR_SYSTEM; + } + + /* limiting each write to a single buffer + * produces smoother results in some cases */ + *size = s->freeCnt ? s->bufSize : 0; + + return SA_SUCCESS; +} + +/*****************************************************************************/ + +/** set absolute volume using a value ranging from 0.0 to 1.0 */ + +int sa_stream_set_volume_abs(sa_stream_t *s, float vol) +{ + uint32_t rc; + MCI_SET_PARMS SetParms; + + if (!s) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_set_volume_abs", + "s is null", 0); + + /* convert f.p. value to an integer value ranging + * from 0 to 100 and apply to both channels */ + SetParms.ulLevel = (vol * 100); + SetParms.ulAudio = MCI_SET_AUDIO_ALL; + + rc = _mciSendCommand(s->hwDeviceID, MCI_SET, + MCI_WAIT | MCI_SET_AUDIO | MCI_SET_VOLUME, + (void*)&SetParms, 0); + if (LOUSHORT(rc)) + return os2_error(SA_ERROR_SYSTEM, "sa_stream_set_volume_abs", + "MCI_SET_VOLUME - rc=", LOUSHORT(rc)); + + return SA_SUCCESS; +} + +/*****************************************************************************/ + +/** get absolute volume as a value ranging from 0.0 to 1.0 */ + +int sa_stream_get_volume_abs(sa_stream_t *s, float *vol) +{ + int status = SA_SUCCESS; + uint32_t rc; + MCI_STATUS_PARMS StatusParms; + + if (!s || !vol) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_get_volume_abs", + "s or vol is null", 0); + + memset(&StatusParms, 0, sizeof(MCI_STATUS_PARMS)); + StatusParms.ulItem = MCI_STATUS_VOLUME; + + rc = _mciSendCommand(s->hwDeviceID, MCI_STATUS, + MCI_WAIT | MCI_STATUS_ITEM, + (void*)&StatusParms, 0); + if (LOUSHORT(rc)) { + /* if there's an error, return a reasonable value */ + StatusParms.ulReturn = (50 | 50 << 16); + status = os2_error(SA_ERROR_SYSTEM, "sa_stream_get_volume_abs", + "MCI_STATUS_VOLUME - rc=", LOUSHORT(rc)); + } + + /* left channel is the low-order word, right channel is the + * high-order word - convert the average of the channels from + * an integer (range 0 - 100) to a floating point value */ + + *vol = (LOUSHORT(StatusParms.ulReturn) + + HIUSHORT(StatusParms.ulReturn)) / 200.0; + + return status; +} + +/*****************************************************************************/ +/* Private (static) Functions */ +/*****************************************************************************/ + +/** signal the decode thread that a buffer is available - + ** this runs on a separate high-priority thread created by DART */ + +static int32_t os2_mixer_event(uint32_t ulStatus, PMCI_MIX_BUFFER pBuffer, + uint32_t ulFlags) +{ + uint32_t rc; + int32_t posted; + sa_stream_t * s; + + /* check for errors */ + if (ulFlags & MIX_STREAM_ERROR) + rc = os2_error(0, "os2_mixer_event", "MIX_STREAM_ERROR - status=", ulStatus); + + if (!(ulFlags & MIX_WRITE_COMPLETE)) + return os2_error(TRUE, "os2_mixer_event", + "unexpected event - flag=", ulFlags); + + if (!pBuffer || !pBuffer->ulUserParm) + return os2_error(TRUE, "os2_mixer_event", "null pointer", 0); + + /* Note: this thread doesn't use a mutex to avoid a deadlock with the one + * DART uses to prevent MCI operations while this function is running */ + s = (sa_stream_t *)pBuffer->ulUserParm; + + /* update the number of buffers that are now in use */ + rc = DosResetEventSem(s->usedSem, (unsigned long*)&posted); + if (rc && rc != ERROR_ALREADY_RESET) { + posted = 0; + rc = os2_error(rc, "os2_mixer_event", "DosResetEventSem - rc=", rc); + } + s->usedCnt += posted - 1; + + /* if fewer than 2 buffers are in use, enter recovery mode - + * if we wait until they're all free, it's often too late; */ + if (s->usedCnt < 2 && s->state == SAOS2_PLAY) { + s->state = SAOS2_RECOVER; + os2_pause_device(s->hwDeviceID, FALSE); + rc = os2_error(rc, "os2_mixer_event", + "too few buffers in use - recovering", 0); + } + + /* setting the write position after the buffer has been played yields + * far more accurate timing than setting it in sa_stream_write() */ + s->writePos = s->writePos + (int64_t)pBuffer->ulBufferLength; + pBuffer->ulBufferLength = 0; + + /* signal the decode thread that a buffer is available */ + rc = DosPostEventSem(s->freeSem); + if (rc && rc != ERROR_ALREADY_POSTED) + rc = os2_error(rc, "os2_mixer_event", "DosPostEventSem - rc=", rc); + + return TRUE; +} + +/*****************************************************************************/ + +/** write as many buffers as available to the device */ + +static int os2_write_to_device(sa_stream_t *s) +{ + uint32_t rc; + int32_t cnt; + int32_t ctr; + + /* this executes twice if bufList wraps, otherwise just once */ + while (s->readyCnt) { + + /* deal with wrap */ + cnt = (s->readyNdx + s->readyCnt > s->bufCnt) ? + (s->bufCnt - s->readyNdx) : s->readyCnt; + + /* if the write fails, abort */ + rc = s->hwWriteProc(s->hwMixHandle, &(s->bufList[s->readyNdx]), cnt); + if (LOUSHORT(rc)) + return os2_error(SA_ERROR_SYSTEM, "os2_write_to_device", + "mixWrite - rc=", LOUSHORT(rc)); + + /* signal the event thread that 'cnt' buffers are now in use */ + for (ctr = 0; ctr < cnt; ctr++) { + rc = DosPostEventSem(s->usedSem); + if (rc && rc != ERROR_ALREADY_POSTED) + return os2_error(SA_ERROR_SYSTEM, "os2_write_to_device", + "DosPostEventSem - rc=", rc); + } + + /* advance to the next entry */ + s->readyNdx = (s->readyNdx + cnt) % s->bufCnt; + s->readyCnt -= cnt; + } + + /* if state is INIT or RECOVER, change to PLAY */ + if (s->state < SAOS2_PLAY) + s->state = SAOS2_PLAY; + + return SA_SUCCESS; +} + +/*****************************************************************************/ + +/** stop playback */ + +static void os2_stop_device(uint16_t hwDeviceID) +{ + uint32_t rc; + MCI_GENERIC_PARMS GenericParms = { 0 }; + + rc = _mciSendCommand(hwDeviceID, MCI_STOP, + MCI_WAIT, + (void*)&GenericParms, 0); + if (LOUSHORT(rc)) + os2_error(0, "os2_stop_device", "MCI_STOP - rc=", LOUSHORT(rc)); + + return; +} + +/*****************************************************************************/ + +/** pause playback and optionally release device */ + +static int os2_pause_device(uint16_t hwDeviceID, uint32_t release) +{ + uint32_t rc; + MCI_GENERIC_PARMS GenericParms = { 0 }; + + rc = _mciSendCommand(hwDeviceID, MCI_PAUSE, + MCI_WAIT, + (void*)&GenericParms, 0); + if (LOUSHORT(rc)) + return os2_error(SA_ERROR_SYSTEM, "os2_pause_device", + "MCI_PAUSE - rc=", LOUSHORT(rc)); + + if (release) + _mciSendCommand(hwDeviceID, MCI_RELEASEDEVICE, + MCI_WAIT, + (void*)&GenericParms, 0); + + return SA_SUCCESS; +} + +/*****************************************************************************/ + +/** update the count of free buffers, returning when 'count' are available */ + +static int os2_get_free_count(sa_stream_t *s, int32_t count) +{ + uint32_t rc; + int32_t posted; + + while (1) { + rc = DosResetEventSem(s->freeSem, (unsigned long*)&posted); + if (rc && rc != ERROR_ALREADY_RESET) + return os2_error(SA_ERROR_SYSTEM, "os2_get_free_count", + "DosResetEventSem - rc=", rc); + + s->freeCnt += posted; + if (s->freeCnt >= count) + break; + + rc = DosWaitEventSem(s->freeSem, SAOS2_SEM_WAIT); + if (rc) + return os2_error(SA_ERROR_SYSTEM, "os2_get_free_count", + "DosWaitEventSem - rc=", rc); + } + + return SA_SUCCESS; +} + +/*****************************************************************************/ + +#ifdef SAOS2_ERROR + +/** display an error message & return whatever value was passed in */ + +static int os2_error_msg(int rtn, char * func, char * msg, uint32_t err) +{ + if (!err) + fprintf(stderr, "sa_os2 error - %s: %s\n", func, msg); + else + fprintf(stderr, "sa_os2 error - %s: %s %u\n", func, msg, err); + fflush(stderr); + + return rtn; +} + +#endif + +/*****************************************************************************/ +/* Mozilla-specific Functions */ +/*****************************************************************************/ + +/** load mdm.dll & get the entrypoint for mciSendCommand() */ + +static int os2_load_mdm(void) +{ + uint32_t rc; + HMODULE hmod; + char text[32]; + + if (_mciSendCommand) + return SA_SUCCESS; + + rc = DosLoadModule(text, sizeof(text), "MDM", &hmod); + if (rc) + return os2_error(SA_ERROR_SYSTEM, "os2_load_mdm", + "DosLoadModule - rc=", rc); + + /* the ordinal for mciSendCommand is '1' */ + rc = DosQueryProcAddr(hmod, 1, 0, (PFN*)&_mciSendCommand); + if (rc) { + _mciSendCommand = 0; + return os2_error(SA_ERROR_SYSTEM, "os2_load_mdm", + "DosQueryProcAddr - rc=", rc); + } + + return SA_SUCCESS; +} + +/*****************************************************************************/ + +/** adjust the decode thread's priority */ + +static void os2_set_priority(void) +{ + uint32_t rc; + uint32_t priority; + int32_t delta; + int32_t newdelta; + PTIB ptib; + PPIB ppib; + +#define SAOS2_PRIORITY 8 + + DosGetInfoBlocks(&ptib, &ppib); + priority = ptib->tib_ptib2->tib2_ulpri; + delta = priority & 0xff; + priority >>= 8; + + /* if the current priority class is other than "regular" (priority 2), + * don't change anything - otherwise, calculate a delta that will set + * the priority to SAOS2_PRIORITY */ + if (priority != PRTYC_REGULAR) + newdelta = 0; + else + newdelta = SAOS2_PRIORITY - delta; + + if (newdelta) { + rc = DosSetPriority(PRTYS_THREAD, PRTYC_NOCHANGE, newdelta, 0); + if (rc) + rc = os2_error(rc, "os2_set_priority", "DosSetPriority - rc=", rc); + } + + return; +} + +/*****************************************************************************/ +/* Not Implemented / Not Supported */ +/*****************************************************************************/ + +#define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; } + +UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec)) +UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size)) +UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size)) +UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size)) +UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size)) +UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n)) +UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode)) +UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable)) +UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable)) +UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver)) +UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback)) +UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s)) +UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name)) +UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n)) +UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n)) +UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate)) +UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size)) +UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value)) +UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction)) +UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction)) +UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction)) +UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction)) +UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode)) +UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size)) +UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format)) +UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate)) +UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels)) +UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value)) +UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size)) +UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size)) +UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size)) +UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size)) +UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n)) +UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode)) +UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled)) +UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled)) +UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size)) +UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size)) +UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n)) +UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n)) +UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size)) +UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction)) +UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction)) +UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction)) +UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction)) +UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state)) +UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error)) +UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify)) +UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes)) +UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes)) +UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes)) +UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence)) +UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence)) +UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size)) + +const char *sa_strerror(int code) { return NULL; } + +/*****************************************************************************/ + diff --git a/media/libsydneyaudio/sydney_os2_base.patch b/media/libsydneyaudio/sydney_os2_base.patch new file mode 100644 --- /dev/null +++ b/media/libsydneyaudio/sydney_os2_base.patch @@ -0,0 +1,918 @@ +diff --git a/media/libsydneyaudio/include/sydney_audio.h b/media/libsydneyaudio/include/sydney_audio.h +--- a/media/libsydneyaudio/include/sydney_audio.h ++++ b/media/libsydneyaudio/include/sydney_audio.h +@@ -95,18 +95,18 @@ typedef __int32 int32_t; + #endif + #if !defined(int64_t) + typedef __int64 int64_t; + #endif + #endif + + typedef struct sa_stream sa_stream_t; + +-#if defined(WIN32) +-// (left << 16 | right) (16 bits per channel) ++#if defined(WIN32) || defined(OS2) ++/* (left << 16 | right) (16 bits per channel) */ + #define SA_VOLUME_MUTED ((int32_t) (0x00000000)) + #else + /** Volume that corresponds to muted in/out */ + #define SA_VOLUME_MUTED ((int32_t) (-0x80000000)) + #endif + + /** Ways to express seek offsets for pread/pwrite */ + typedef enum { +diff --git a/media/libsydneyaudio/src/sydney_audio_os2.c b/media/libsydneyaudio/src/sydney_audio_os2.c +new file mode 100644 +--- /dev/null ++++ b/media/libsydneyaudio/src/sydney_audio_os2.c +@@ -0,0 +1,889 @@ ++/* ***** BEGIN LICENSE BLOCK ***** ++ * Version: MPL 1.1/GPL 2.0/LGPL 2.1 ++ * ++ * The contents of this file are subject to the Mozilla Public License Version ++ * 1.1 (the "License"); you may not use this file except in compliance with ++ * the License. You may obtain a copy of the License at ++ * http://www.mozilla.org/MPL/ ++ * ++ * Software distributed under the License is distributed on an "AS IS" basis, ++ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License ++ * for the specific language governing rights and limitations under the ++ * License. ++ * ++ * The Initial Developers of the Original Code are ++ * Andrew Zabolotny (libdart) - Copyright (C) 1998 ++ * CSIRO (libsydneyaudio)- Copyright (C) 2007 ++ * Richard Walsh (OS/2 implementation) - Copyright (C) 2008 ++ * Portions created by the Initial Developers are Copyright (c) 1998,2007,2008, ++ * the Initial Developers. All Rights Reserved. ++ * ++ * Contributor(s): ++ * ++ * Alternatively, the contents of this file may be used under the terms of ++ * either the GNU General Public License Version 2 or later (the "GPL"), or ++ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), ++ * in which case the provisions of the GPL or the LGPL are applicable instead ++ * of those above. If you wish to allow use of your version of this file only ++ * under the terms of either the GPL or the LGPL, and not to allow others to ++ * use your version of this file under the terms of the MPL, indicate your ++ * decision by deleting the provisions above and replace them with the notice ++ * and other provisions required by the GPL or the LGPL. If you do not delete ++ * the provisions above, a recipient may use your version of this file under ++ * the terms of any one of the MPL, the GPL or the LGPL. ++ * ++ * ***** END LICENSE BLOCK ***** * ++ */ ++ ++/*****************************************************************************/ ++/* OVERVIEW ++ * ++ * Unlike other DART implementations which pull data into the backend ++ * as needed, this one relies on the upstream code to provide sufficient ++ * data in a well regulated stream. If other activities in the system ++ * interrupt that stream, the sound device may run out of data. While ++ * it should simply pause until more data is available, on some machines ++ * a buffer underrun causes the device to stop responding and to ignore ++ * new data until an MCI_STOP or MCI_PAUSE command is issued. ++ * ++ * The solution used here is to track the number of buffers in use and ++ * to pause the device when the count falls below a threshold. Writing ++ * a new buffer to the device causes playback to resume automatically. ++ * To support this scheme, the code uses 2 event semaphores to pass ++ * buffer counts between its two threads (the app's decode thread and ++ * DART's event thread). It also has the event thread do as little as ++ * possible to ensure it's not busy when a buffer-free event occurs. ++ * ++ */ ++/*****************************************************************************/ ++ ++#include ++#include ++#include ++#include ++#include "sydney_audio.h" ++ ++#define INCL_DOS ++#define INCL_MCIOS2 ++#include ++#include ++ ++/*****************************************************************************/ ++ ++/* this will have to be changed to a variable ++ * if other than 16-bit samples are ever supported */ ++#define SAOS2_SAMPLE_SIZE 2 ++ ++/* the number of buffers to allocate - the ogg decoder typically ++ * writes 8k at a time, so this works out to roughly 1/2 second */ ++#define SAOS2_BUF_CNT 11 ++ ++/* this could be as large as 65535 but making it smaller helps ++ * avoid having the DART event thread think it's running out of ++ * buffers if the decoder sends larger chunks of data less often */ ++#define SAOS2_RAW_BUFSIZE 16384 ++ ++/* playback states */ ++#define SAOS2_INIT 0 ++#define SAOS2_RECOVER 1 ++#define SAOS2_PLAY 2 ++#define SAOS2_EXIT 3 ++ ++/* an indefinite wait invites a hung thread */ ++#define SAOS2_SEM_WAIT 5000 ++ ++/* the only 2 return codes we care about */ ++#ifndef INCL_DOSERRORS ++#define ERROR_ALREADY_POSTED 299 ++#define ERROR_ALREADY_RESET 300 ++#endif ++ ++/*****************************************************************************/ ++/* Debug */ ++ ++#ifdef DEBUG ++ #ifndef SAOS2_ERROR ++ #define SAOS2_ERROR ++ #endif ++#endif ++ ++#ifdef SAOS2_ERROR ++ static int os2_error_msg(int rtn, char * func, char * msg, uint32_t err); ++ #define os2_error(rtn, func, msg, err) os2_error_msg(rtn, func, msg, err) ++#else ++ #define os2_error(rtn, func, msg, err) rtn ++#endif ++ ++/*****************************************************************************/ ++/* OS/2 implementation of sa_stream_t */ ++ ++struct sa_stream { ++ ++ /* audio format info */ ++ const char * client_name; ++ sa_mode_t mode; ++ sa_pcm_format_t format; ++ uint32_t rate; ++ uint32_t nchannels; ++ ++ /* device info */ ++ uint16_t hwDeviceID; ++ uint32_t hwMixHandle; ++ PMIXERPROC hwWriteProc; ++ ++ /* buffer allocations */ ++ int32_t bufCnt; ++ size_t bufSize; ++ PMCI_MIX_BUFFER bufList; ++ ++ /* buffer usage tracking */ ++ HEV freeSem; ++ int32_t freeCnt; ++ int32_t freeNdx; ++ int32_t readyCnt; ++ int32_t readyNdx; ++ HEV usedSem; ++ volatile int32_t usedCnt; ++ ++ /* miscellaneous */ ++ volatile int32_t state; ++ int64_t writePos; ++}; ++ ++/*****************************************************************************/ ++/* Private (static) Functions */ ++ ++static int32_t os2_mixer_event(uint32_t ulStatus, PMCI_MIX_BUFFER pBuffer, ++ uint32_t ulFlags); ++static int os2_write_to_device(sa_stream_t *s); ++static void os2_stop_device(uint16_t hwDeviceID); ++static int os2_pause_device(uint16_t hwDeviceID, uint32_t release); ++static int os2_get_free_count(sa_stream_t *s, int32_t count); ++ ++/*****************************************************************************/ ++/* Sydney Audio Functions */ ++/*****************************************************************************/ ++ ++/** Normal way to open a PCM device */ ++ ++int sa_stream_create_pcm(sa_stream_t ** s, ++ const char * client_name, ++ sa_mode_t mode, ++ sa_pcm_format_t format, ++ unsigned int rate, ++ unsigned int nchannels) ++{ ++ uint32_t status = SA_SUCCESS; ++ uint32_t size; ++ uint32_t rc; ++ sa_stream_t * sTemp = 0; ++ ++ /* this do{}while(0) "loop" makes it easy to ensure ++ * resources are freed on exit if there's an error */ ++do { ++ if (mode != SA_MODE_WRONLY || format != SA_PCM_FORMAT_S16_LE) ++ return os2_error(SA_ERROR_NOT_SUPPORTED, "sa_stream_create_pcm", ++ "invalid mode or format", 0); ++ ++ if (!s) ++ return os2_error(SA_ERROR_INVALID, "sa_stream_create_pcm", ++ "s is null", 0); ++ *s = 0; ++ ++ /* the MCI_MIX_BUFFERs must be in low memory or terrible things will ++ * happen! - since there's extra space, put 'sa_stream' there too */ ++ size = sizeof(sa_stream_t) + sizeof(PMCI_MIX_BUFFER) * SAOS2_BUF_CNT; ++ rc = DosAllocMem((void**)&sTemp, size, ++ PAG_COMMIT | PAG_READ | PAG_WRITE); ++ if (rc) { ++ status = os2_error(SA_ERROR_OOM, "sa_stream_create_pcm", ++ "DosAllocMem - rc=", rc); ++ break; ++ } ++ ++ memset(sTemp, 0, size); ++ sTemp->bufList = (PMCI_MIX_BUFFER)&sTemp[1]; ++ ++ /* set the number of buffers; round the buffer ++ * size down to the nearest multiple of a frame; */ ++ sTemp->bufCnt = SAOS2_BUF_CNT; ++ sTemp->bufSize = SAOS2_RAW_BUFSIZE - ++ (SAOS2_RAW_BUFSIZE % (SAOS2_SAMPLE_SIZE * nchannels)); ++ ++ /* create event semaphores to signal free buffers */ ++ rc = DosCreateEventSem(0, &sTemp->freeSem, 0, FALSE); ++ if (!rc) ++ rc = DosCreateEventSem(0, &sTemp->usedSem, 0, FALSE); ++ if (rc) { ++ status = os2_error(SA_ERROR_SYSTEM, "sa_stream_create_pcm", ++ "DosCreateEventSem - rc=", rc); ++ break; ++ } ++ ++ /* fill in the miscellanea */ ++ sTemp->client_name = client_name; ++ sTemp->mode = mode; ++ sTemp->format = format; ++ sTemp->rate = rate; ++ sTemp->nchannels = nchannels; ++ ++ *s = sTemp; ++ ++} while (0); ++ ++ /* on error, free any allocations */ ++ if (status != SA_SUCCESS && sTemp) { ++ if (sTemp->freeSem) ++ DosCloseEventSem(sTemp->freeSem); ++ if (sTemp->usedSem) ++ DosCloseEventSem(sTemp->usedSem); ++ if (sTemp) ++ DosFreeMem(sTemp); ++ } ++ ++ return status; ++} ++ ++/*****************************************************************************/ ++ ++/** Initialise the device */ ++ ++int sa_stream_open(sa_stream_t *s) ++{ ++ int status = SA_SUCCESS; ++ uint32_t rc; ++ int32_t ctr; ++ uint32_t bufCntRequested; ++ MCI_AMP_OPEN_PARMS AmpOpenParms; ++ MCI_MIXSETUP_PARMS MixSetupParms; ++ MCI_BUFFER_PARMS BufferParms; ++ ++ if (!s) ++ return os2_error(SA_ERROR_NO_INIT, "sa_stream_open", "s is null", 0); ++ ++do { ++ /* s->bufCnt will be restored after successfully allocating buffers */ ++ bufCntRequested = s->bufCnt; ++ s->bufCnt = 0; ++ ++ /* open the Amp-Mixer using the default device in shared mode */ ++ memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS)); ++ AmpOpenParms.pszDeviceType = (PSZ)(MCI_DEVTYPE_AUDIO_AMPMIX | 0); ++ ++ rc = mciSendCommand(0, MCI_OPEN, ++ MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE, ++ (void*)&AmpOpenParms, 0); ++ if (LOUSHORT(rc)) { ++ status = os2_error(SA_ERROR_NO_DEVICE, "sa_stream_open", ++ "MCI_OPEN - rc=", LOUSHORT(rc)); ++ break; ++ } ++ ++ /* save the device ID */ ++ s->hwDeviceID = AmpOpenParms.usDeviceID; ++ ++ /* setup the Amp-Mixer to play wave data */ ++ memset(&MixSetupParms, 0, sizeof(MCI_MIXSETUP_PARMS)); ++ MixSetupParms.ulBitsPerSample = 16; ++ MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM; ++ MixSetupParms.ulFormatMode = MCI_PLAY; ++ MixSetupParms.ulSamplesPerSec = s->rate; ++ MixSetupParms.ulChannels = s->nchannels; ++ MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; ++ MixSetupParms.pmixEvent = (MIXEREVENT*)os2_mixer_event; ++ ++ rc = mciSendCommand(s->hwDeviceID, MCI_MIXSETUP, ++ MCI_WAIT | MCI_MIXSETUP_INIT, ++ (void*)&MixSetupParms, 0); ++ if (LOUSHORT(rc)) { ++ status = os2_error(SA_ERROR_NOT_SUPPORTED, "sa_stream_open", ++ "MCI_MIXSETUP - rc=", LOUSHORT(rc)); ++ break; ++ } ++ ++ /* save hw info we'll need later */ ++ s->hwMixHandle = MixSetupParms.ulMixHandle; ++ s->hwWriteProc = MixSetupParms.pmixWrite; ++ ++ /* allocate device buffers from the Amp-Mixer */ ++ BufferParms.ulStructLength = sizeof(MCI_BUFFER_PARMS); ++ BufferParms.ulNumBuffers = bufCntRequested; ++ BufferParms.ulBufferSize = s->bufSize; ++ BufferParms.pBufList = s->bufList; ++ ++ rc = mciSendCommand(s->hwDeviceID, MCI_BUFFER, ++ MCI_WAIT | MCI_ALLOCATE_MEMORY, ++ (void*)&BufferParms, 0); ++ if (LOUSHORT(rc)) { ++ status = os2_error(SA_ERROR_OOM, "sa_stream_open", ++ "MCI_ALLOCATE_MEMORY - rc=", LOUSHORT(rc)); ++ break; ++ } ++ ++ /* MCI_ALLOCATE_MEMORY may have decreased the, ++ * number of buffers, so update the counts */ ++ s->bufCnt = BufferParms.ulNumBuffers; ++ s->freeCnt = BufferParms.ulNumBuffers; ++ ++ /* sa_stream_write() & os2_mixer_event() require these initializations */ ++ for (ctr = 0; ctr < s->bufCnt; ctr++) { ++ s->bufList[ctr].ulStructLength = sizeof(MCI_MIX_BUFFER); ++ s->bufList[ctr].ulBufferLength = 0; ++ s->bufList[ctr].ulUserParm = (uint32_t)s; ++ } ++ ++} while (0); ++ ++ return status; ++} ++ ++/*****************************************************************************/ ++ ++/** Close/destroy everything */ ++ ++int sa_stream_destroy(sa_stream_t *s) ++{ ++ int status = SA_SUCCESS; ++ uint32_t rc; ++ MCI_GENERIC_PARMS GenericParms = { 0 }; ++ MCI_BUFFER_PARMS BufferParms; ++ ++ if (!s) ++ return os2_error(SA_ERROR_NO_INIT, "sa_stream_destroy", "s is null", 0); ++ ++ /* if the device was opened, close it */ ++ if (s->hwDeviceID) { ++ ++ /* prevent os2_mixer_event() from reacting to a buffer under-run */ ++ s->state = SAOS2_EXIT; ++ ++ /* stop the device (which may not actually be playing) */ ++ os2_stop_device(s->hwDeviceID); ++ ++ /* if hardware buffers were allocated, free them */ ++ if (s->bufCnt) { ++ BufferParms.hwndCallback = 0; ++ BufferParms.ulStructLength = sizeof(MCI_BUFFER_PARMS); ++ BufferParms.ulNumBuffers = s->bufCnt; ++ BufferParms.ulBufferSize = s->bufSize; ++ BufferParms.pBufList = s->bufList; ++ ++ rc = mciSendCommand(s->hwDeviceID, MCI_BUFFER, ++ MCI_WAIT | MCI_DEALLOCATE_MEMORY, ++ (void*)&BufferParms, 0); ++ if (LOUSHORT(rc)) ++ status = os2_error(SA_ERROR_SYSTEM, "sa_stream_destroy", ++ "MCI_DEALLOCATE_MEMORY - rc=", LOUSHORT(rc)); ++ } ++ ++ rc = mciSendCommand(s->hwDeviceID, MCI_CLOSE, ++ MCI_WAIT, ++ (void*)&GenericParms, 0); ++ if (LOUSHORT(rc)) ++ status = os2_error(SA_ERROR_SYSTEM, "sa_stream_destroy", ++ "MCI_CLOSE - rc=", LOUSHORT(rc)); ++ } ++ ++ /* free other resources we allocated */ ++ if (s->freeSem) ++ DosCloseEventSem(s->freeSem); ++ if (s->usedSem) ++ DosCloseEventSem(s->usedSem); ++ DosFreeMem(s); ++ ++ return status; ++} ++ ++/*****************************************************************************/ ++ ++/** Interleaved playback function */ ++ ++int sa_stream_write(sa_stream_t * s, const void * data, size_t nbytes) ++{ ++ uint32_t rc; ++ size_t cnt; ++ PMCI_MIX_BUFFER pHW; ++ ++ if (!s) ++ return os2_error(SA_ERROR_NO_INIT, "sa_stream_write", "s is null", 0); ++ if (!data) ++ return os2_error(SA_ERROR_INVALID, "sa_stream_write", "data is null", 0); ++ ++ /* exit if no data */ ++ if (!nbytes) ++ return SA_SUCCESS; ++ ++ /* This should only loop on the last write before sa_stream_drain() ++ * is called; at other times, 'nbytes' won't exceed 'bufSize'. */ ++ while (nbytes) { ++ ++ /* get the count of free buffers, wait until at least one ++ * is available (in practice, this should never block) */ ++ if (os2_get_free_count(s, 1)) ++ return SA_ERROR_SYSTEM; ++ ++ /* copy as much as will fit into the buffer */ ++ pHW = &(s->bufList[s->freeNdx]); ++ cnt = (nbytes > s->bufSize) ? s->bufSize : nbytes; ++ memcpy(pHW->pBuffer, (char*)data, cnt); ++ pHW->ulBufferLength = cnt; ++ nbytes -= cnt; ++ data = (char*)data + cnt; ++ ++ /* adjust cnts & indices, then send the buffer to the device */ ++ s->freeCnt--; ++ s->freeNdx = (s->freeNdx + 1) % s->bufCnt; ++ s->readyCnt++; ++ if (os2_write_to_device(s)) ++ return SA_ERROR_SYSTEM; ++ } ++ ++ return SA_SUCCESS; ++} ++ ++/*****************************************************************************/ ++ ++/** sync/timing */ ++ ++int sa_stream_get_position(sa_stream_t *s, sa_position_t position, int64_t *pos) ++{ ++ uint32_t rc; ++ ++ if (!s || !pos) ++ return os2_error(SA_ERROR_NO_INIT, "sa_stream_get_position", ++ "s or pos is null", 0); ++ ++ if (position != SA_POSITION_WRITE_SOFTWARE) ++ return os2_error(SA_ERROR_NOT_SUPPORTED, "sa_stream_get_position", ++ "unsupported postion type=", position); ++ ++ /* this is the nbr of bytes that are known to have been played ++ * already; the MCI command to get stream position isn't usable - ++ * it returns a time value that resets when the stream is paused */ ++ *pos = s->writePos; ++ ++ return SA_SUCCESS; ++} ++ ++/*****************************************************************************/ ++ ++/** Resume playing after a pause */ ++ ++int sa_stream_resume(sa_stream_t *s) ++{ ++ uint32_t rc; ++ MCI_GENERIC_PARMS GenericParms = { 0 }; ++ ++ if (!s) ++ return os2_error(SA_ERROR_NO_INIT, "sa_stream_resume", ++ "s is null", 0); ++ ++ rc = mciSendCommand(s->hwDeviceID, MCI_ACQUIREDEVICE, ++ MCI_WAIT, ++ (void*)&GenericParms, 0); ++ if (LOUSHORT(rc)) ++ return os2_error(SA_ERROR_SYSTEM, "sa_stream_resume", ++ "MCI_ACQUIREDEVICE - rc=", LOUSHORT(rc)); ++ ++ rc = mciSendCommand(s->hwDeviceID, MCI_RESUME, ++ MCI_WAIT, ++ (void*)&GenericParms, 0); ++ if (LOUSHORT(rc)) ++ return os2_error(SA_ERROR_SYSTEM, "sa_stream_resume", ++ "MCI_RESUME - rc=", LOUSHORT(rc)); ++ ++ return SA_SUCCESS; ++} ++ ++/*****************************************************************************/ ++ ++/** Pause audio playback (do not empty the buffer) */ ++ ++int sa_stream_pause(sa_stream_t *s) ++{ ++ if (!s) ++ return os2_error(SA_ERROR_NO_INIT, "sa_stream_pause", "s is null", 0); ++ ++ /* pause & release device */ ++ return os2_pause_device(s->hwDeviceID, TRUE); ++} ++ ++/*****************************************************************************/ ++ ++/** Block until all audio has been played */ ++ ++int sa_stream_drain(sa_stream_t *s) ++{ ++ int status = SA_SUCCESS; ++ char buf[32]; ++ ++ if (!s) ++ return os2_error(SA_ERROR_NO_INIT, "sa_stream_drain", "s is null", 0); ++ ++ /* keep os2_mixer_event() from reacting to buffer under-runs */ ++ s->state = SAOS2_EXIT; ++ ++ /* DART won't start playing until 2 buffers have been ++ * written, so write a dummy 2nd buffer just in case */ ++ memset(buf, 0, sizeof(buf)); ++ sa_stream_write(s, buf, s->nchannels * SAOS2_SAMPLE_SIZE); ++ ++ /* write all remaining buffers to the device */ ++ if (s->readyCnt) ++ status = os2_write_to_device(s); ++ ++ /* wait for all buffers to become free */ ++ if (!status) ++ status = os2_get_free_count(s, s->bufCnt); ++ ++ /* stop the device so it doesn't misbehave due to an under-run */ ++ os2_stop_device(s->hwDeviceID); ++ ++ return status; ++} ++ ++/*****************************************************************************/ ++ ++/** Query how much can be written without blocking */ ++ ++int sa_stream_get_write_size(sa_stream_t *s, size_t *size) ++{ ++ if (!s) ++ return os2_error(SA_ERROR_NO_INIT, "sa_stream_get_write_size", ++ "s is null", 0); ++ ++ /* return a non-zero value here in case the upstream code ignores ++ * the return code - if so, sa_stream_write() will fail instead */ ++ if (os2_get_free_count(s, 0)) { ++ *size = s->bufSize; ++ return SA_ERROR_SYSTEM; ++ } ++ ++ /* limiting each write to a single buffer ++ * produces smoother results in some cases */ ++ *size = s->freeCnt ? s->bufSize : 0; ++ ++ return SA_SUCCESS; ++} ++ ++/*****************************************************************************/ ++ ++/** set absolute volume using a value ranging from 0.0 to 1.0 */ ++ ++int sa_stream_set_volume_abs(sa_stream_t *s, float vol) ++{ ++ uint32_t rc; ++ MCI_SET_PARMS SetParms; ++ ++ if (!s) ++ return os2_error(SA_ERROR_NO_INIT, "sa_stream_set_volume_abs", ++ "s is null", 0); ++ ++ /* convert f.p. value to an integer value ranging ++ * from 0 to 100 and apply to both channels */ ++ SetParms.ulLevel = (vol * 100); ++ SetParms.ulAudio = MCI_SET_AUDIO_ALL; ++ ++ rc = mciSendCommand(s->hwDeviceID, MCI_SET, ++ MCI_WAIT | MCI_SET_AUDIO | MCI_SET_VOLUME, ++ (void*)&SetParms, 0); ++ if (LOUSHORT(rc)) ++ return os2_error(SA_ERROR_SYSTEM, "sa_stream_set_volume_abs", ++ "MCI_SET_VOLUME - rc=", LOUSHORT(rc)); ++ ++ return SA_SUCCESS; ++} ++ ++/*****************************************************************************/ ++ ++/** get absolute volume as a value ranging from 0.0 to 1.0 */ ++ ++int sa_stream_get_volume_abs(sa_stream_t *s, float *vol) ++{ ++ int status = SA_SUCCESS; ++ uint32_t rc; ++ MCI_STATUS_PARMS StatusParms; ++ ++ if (!s || !vol) ++ return os2_error(SA_ERROR_NO_INIT, "sa_stream_get_volume_abs", ++ "s or vol is null", 0); ++ ++ memset(&StatusParms, 0, sizeof(MCI_STATUS_PARMS)); ++ StatusParms.ulItem = MCI_STATUS_VOLUME; ++ ++ rc = mciSendCommand(s->hwDeviceID, MCI_STATUS, ++ MCI_WAIT | MCI_STATUS_ITEM, ++ (void*)&StatusParms, 0); ++ if (LOUSHORT(rc)) { ++ /* if there's an error, return a reasonable value */ ++ StatusParms.ulReturn = (50 | 50 << 16); ++ status = os2_error(SA_ERROR_SYSTEM, "sa_stream_get_volume_abs", ++ "MCI_STATUS_VOLUME - rc=", LOUSHORT(rc)); ++ } ++ ++ /* left channel is the low-order word, right channel is the ++ * high-order word - convert the average of the channels from ++ * an integer (range 0 - 100) to a floating point value */ ++ ++ *vol = (LOUSHORT(StatusParms.ulReturn) + ++ HIUSHORT(StatusParms.ulReturn)) / 200.0; ++ ++ return status; ++} ++ ++/*****************************************************************************/ ++/* Private (static) Functions */ ++/*****************************************************************************/ ++ ++/** signal the decode thread that a buffer is available - ++ ** this runs on a separate high-priority thread created by DART */ ++ ++static int32_t os2_mixer_event(uint32_t ulStatus, PMCI_MIX_BUFFER pBuffer, ++ uint32_t ulFlags) ++{ ++ uint32_t rc; ++ int32_t posted; ++ sa_stream_t * s; ++ ++ /* check for errors */ ++ if (ulFlags & MIX_STREAM_ERROR) ++ rc = os2_error(0, "os2_mixer_event", "MIX_STREAM_ERROR - status=", ulStatus); ++ ++ if (!(ulFlags & MIX_WRITE_COMPLETE)) ++ return os2_error(TRUE, "os2_mixer_event", ++ "unexpected event - flag=", ulFlags); ++ ++ if (!pBuffer || !pBuffer->ulUserParm) ++ return os2_error(TRUE, "os2_mixer_event", "null pointer", 0); ++ ++ /* Note: this thread doesn't use a mutex to avoid a deadlock with the one ++ * DART uses to prevent MCI operations while this function is running */ ++ s = (sa_stream_t *)pBuffer->ulUserParm; ++ ++ /* update the number of buffers that are now in use */ ++ rc = DosResetEventSem(s->usedSem, (unsigned long*)&posted); ++ if (rc && rc != ERROR_ALREADY_RESET) { ++ posted = 0; ++ rc = os2_error(rc, "os2_mixer_event", "DosResetEventSem - rc=", rc); ++ } ++ s->usedCnt += posted - 1; ++ ++ /* if fewer than 2 buffers are in use, enter recovery mode - ++ * if we wait until they're all free, it's often too late; */ ++ if (s->usedCnt < 2 && s->state == SAOS2_PLAY) { ++ s->state = SAOS2_RECOVER; ++ os2_pause_device(s->hwDeviceID, FALSE); ++ rc = os2_error(rc, "os2_mixer_event", ++ "too few buffers in use - recovering", 0); ++ } ++ ++ /* setting the write position after the buffer has been played yields ++ * far more accurate timing than setting it in sa_stream_write() */ ++ s->writePos = s->writePos + (int64_t)pBuffer->ulBufferLength; ++ pBuffer->ulBufferLength = 0; ++ ++ /* signal the decode thread that a buffer is available */ ++ rc = DosPostEventSem(s->freeSem); ++ if (rc && rc != ERROR_ALREADY_POSTED) ++ rc = os2_error(rc, "os2_mixer_event", "DosPostEventSem - rc=", rc); ++ ++ return TRUE; ++} ++ ++/*****************************************************************************/ ++ ++/** write as many buffers as available to the device */ ++ ++static int os2_write_to_device(sa_stream_t *s) ++{ ++ uint32_t rc; ++ int32_t cnt; ++ int32_t ctr; ++ ++ /* this executes twice if bufList wraps, otherwise just once */ ++ while (s->readyCnt) { ++ ++ /* deal with wrap */ ++ cnt = (s->readyNdx + s->readyCnt > s->bufCnt) ? ++ (s->bufCnt - s->readyNdx) : s->readyCnt; ++ ++ /* if the write fails, abort */ ++ rc = s->hwWriteProc(s->hwMixHandle, &(s->bufList[s->readyNdx]), cnt); ++ if (LOUSHORT(rc)) ++ return os2_error(SA_ERROR_SYSTEM, "os2_write_to_device", ++ "mixWrite - rc=", LOUSHORT(rc)); ++ ++ /* signal the event thread that 'cnt' buffers are now in use */ ++ for (ctr = 0; ctr < cnt; ctr++) { ++ rc = DosPostEventSem(s->usedSem); ++ if (rc && rc != ERROR_ALREADY_POSTED) ++ return os2_error(SA_ERROR_SYSTEM, "os2_write_to_device", ++ "DosPostEventSem - rc=", rc); ++ } ++ ++ /* advance to the next entry */ ++ s->readyNdx = (s->readyNdx + cnt) % s->bufCnt; ++ s->readyCnt -= cnt; ++ } ++ ++ /* if state is INIT or RECOVER, change to PLAY */ ++ if (s->state < SAOS2_PLAY) ++ s->state = SAOS2_PLAY; ++ ++ return SA_SUCCESS; ++} ++ ++/*****************************************************************************/ ++ ++/** stop playback */ ++ ++static void os2_stop_device(uint16_t hwDeviceID) ++{ ++ uint32_t rc; ++ MCI_GENERIC_PARMS GenericParms = { 0 }; ++ ++ rc = mciSendCommand(hwDeviceID, MCI_STOP, ++ MCI_WAIT, ++ (void*)&GenericParms, 0); ++ if (LOUSHORT(rc)) ++ os2_error(0, "os2_stop_device", "MCI_STOP - rc=", LOUSHORT(rc)); ++ ++ return; ++} ++ ++/*****************************************************************************/ ++ ++/** pause playback and optionally release device */ ++ ++static int os2_pause_device(uint16_t hwDeviceID, uint32_t release) ++{ ++ uint32_t rc; ++ MCI_GENERIC_PARMS GenericParms = { 0 }; ++ ++ rc = mciSendCommand(hwDeviceID, MCI_PAUSE, ++ MCI_WAIT, ++ (void*)&GenericParms, 0); ++ if (LOUSHORT(rc)) ++ return os2_error(SA_ERROR_SYSTEM, "os2_pause_device", ++ "MCI_PAUSE - rc=", LOUSHORT(rc)); ++ ++ if (release) ++ mciSendCommand(hwDeviceID, MCI_RELEASEDEVICE, ++ MCI_WAIT, ++ (void*)&GenericParms, 0); ++ ++ return SA_SUCCESS; ++} ++ ++/*****************************************************************************/ ++ ++/** update the count of free buffers, returning when 'count' are available */ ++ ++static int os2_get_free_count(sa_stream_t *s, int32_t count) ++{ ++ uint32_t rc; ++ int32_t posted; ++ ++ while (1) { ++ rc = DosResetEventSem(s->freeSem, (unsigned long*)&posted); ++ if (rc && rc != ERROR_ALREADY_RESET) ++ return os2_error(SA_ERROR_SYSTEM, "os2_get_free_count", ++ "DosResetEventSem - rc=", rc); ++ ++ s->freeCnt += posted; ++ if (s->freeCnt >= count) ++ break; ++ ++ rc = DosWaitEventSem(s->freeSem, SAOS2_SEM_WAIT); ++ if (rc) ++ return os2_error(SA_ERROR_SYSTEM, "os2_get_free_count", ++ "DosWaitEventSem - rc=", rc); ++ } ++ ++ return SA_SUCCESS; ++} ++ ++/*****************************************************************************/ ++ ++#ifdef SAOS2_ERROR ++ ++/** display an error message & return whatever value was passed in */ ++ ++static int os2_error_msg(int rtn, char * func, char * msg, uint32_t err) ++{ ++ if (!err) ++ fprintf(stderr, "sa_os2 error - %s: %s\n", func, msg); ++ else ++ fprintf(stderr, "sa_os2 error - %s: %s %u\n", func, msg, err); ++ fflush(stderr); ++ ++ return rtn; ++} ++ ++#endif ++ ++/*****************************************************************************/ ++/* Not Implemented / Not Supported */ ++/*****************************************************************************/ ++ ++#define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; } ++ ++UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec)) ++UNSUPPORTED(int sa_stream_set_write_lower_watermark(sa_stream_t *s, size_t size)) ++UNSUPPORTED(int sa_stream_set_read_lower_watermark(sa_stream_t *s, size_t size)) ++UNSUPPORTED(int sa_stream_set_write_upper_watermark(sa_stream_t *s, size_t size)) ++UNSUPPORTED(int sa_stream_set_read_upper_watermark(sa_stream_t *s, size_t size)) ++UNSUPPORTED(int sa_stream_set_channel_map(sa_stream_t *s, const sa_channel_t map[], unsigned int n)) ++UNSUPPORTED(int sa_stream_set_xrun_mode(sa_stream_t *s, sa_xrun_mode_t mode)) ++UNSUPPORTED(int sa_stream_set_non_interleaved(sa_stream_t *s, int enable)) ++UNSUPPORTED(int sa_stream_set_dynamic_rate(sa_stream_t *s, int enable)) ++UNSUPPORTED(int sa_stream_set_driver(sa_stream_t *s, const char *driver)) ++UNSUPPORTED(int sa_stream_start_thread(sa_stream_t *s, sa_event_callback_t callback)) ++UNSUPPORTED(int sa_stream_stop_thread(sa_stream_t *s)) ++UNSUPPORTED(int sa_stream_change_device(sa_stream_t *s, const char *device_name)) ++UNSUPPORTED(int sa_stream_change_read_volume(sa_stream_t *s, const int32_t vol[], unsigned int n)) ++UNSUPPORTED(int sa_stream_change_write_volume(sa_stream_t *s, const int32_t vol[], unsigned int n)) ++UNSUPPORTED(int sa_stream_change_rate(sa_stream_t *s, unsigned int rate)) ++UNSUPPORTED(int sa_stream_change_meta_data(sa_stream_t *s, const char *name, const void *data, size_t size)) ++UNSUPPORTED(int sa_stream_change_user_data(sa_stream_t *s, const void *value)) ++UNSUPPORTED(int sa_stream_set_adjust_rate(sa_stream_t *s, sa_adjust_t direction)) ++UNSUPPORTED(int sa_stream_set_adjust_nchannels(sa_stream_t *s, sa_adjust_t direction)) ++UNSUPPORTED(int sa_stream_set_adjust_pcm_format(sa_stream_t *s, sa_adjust_t direction)) ++UNSUPPORTED(int sa_stream_set_adjust_watermarks(sa_stream_t *s, sa_adjust_t direction)) ++UNSUPPORTED(int sa_stream_get_mode(sa_stream_t *s, sa_mode_t *access_mode)) ++UNSUPPORTED(int sa_stream_get_codec(sa_stream_t *s, char *codec, size_t *size)) ++UNSUPPORTED(int sa_stream_get_pcm_format(sa_stream_t *s, sa_pcm_format_t *format)) ++UNSUPPORTED(int sa_stream_get_rate(sa_stream_t *s, unsigned int *rate)) ++UNSUPPORTED(int sa_stream_get_nchannels(sa_stream_t *s, int *nchannels)) ++UNSUPPORTED(int sa_stream_get_user_data(sa_stream_t *s, void **value)) ++UNSUPPORTED(int sa_stream_get_write_lower_watermark(sa_stream_t *s, size_t *size)) ++UNSUPPORTED(int sa_stream_get_read_lower_watermark(sa_stream_t *s, size_t *size)) ++UNSUPPORTED(int sa_stream_get_write_upper_watermark(sa_stream_t *s, size_t *size)) ++UNSUPPORTED(int sa_stream_get_read_upper_watermark(sa_stream_t *s, size_t *size)) ++UNSUPPORTED(int sa_stream_get_channel_map(sa_stream_t *s, sa_channel_t map[], unsigned int *n)) ++UNSUPPORTED(int sa_stream_get_xrun_mode(sa_stream_t *s, sa_xrun_mode_t *mode)) ++UNSUPPORTED(int sa_stream_get_non_interleaved(sa_stream_t *s, int *enabled)) ++UNSUPPORTED(int sa_stream_get_dynamic_rate(sa_stream_t *s, int *enabled)) ++UNSUPPORTED(int sa_stream_get_driver(sa_stream_t *s, char *driver_name, size_t *size)) ++UNSUPPORTED(int sa_stream_get_device(sa_stream_t *s, char *device_name, size_t *size)) ++UNSUPPORTED(int sa_stream_get_read_volume(sa_stream_t *s, int32_t vol[], unsigned int *n)) ++UNSUPPORTED(int sa_stream_get_write_volume(sa_stream_t *s, int32_t vol[], unsigned int *n)) ++UNSUPPORTED(int sa_stream_get_meta_data(sa_stream_t *s, const char *name, void*data, size_t *size)) ++UNSUPPORTED(int sa_stream_get_adjust_rate(sa_stream_t *s, sa_adjust_t *direction)) ++UNSUPPORTED(int sa_stream_get_adjust_nchannels(sa_stream_t *s, sa_adjust_t *direction)) ++UNSUPPORTED(int sa_stream_get_adjust_pcm_format(sa_stream_t *s, sa_adjust_t *direction)) ++UNSUPPORTED(int sa_stream_get_adjust_watermarks(sa_stream_t *s, sa_adjust_t *direction)) ++UNSUPPORTED(int sa_stream_get_state(sa_stream_t *s, sa_state_t *state)) ++UNSUPPORTED(int sa_stream_get_event_error(sa_stream_t *s, sa_error_t *error)) ++UNSUPPORTED(int sa_stream_get_event_notify(sa_stream_t *s, sa_notify_t *notify)) ++UNSUPPORTED(int sa_stream_read(sa_stream_t *s, void *data, size_t nbytes)) ++UNSUPPORTED(int sa_stream_read_ni(sa_stream_t *s, unsigned int channel, void *data, size_t nbytes)) ++UNSUPPORTED(int sa_stream_write_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes)) ++UNSUPPORTED(int sa_stream_pwrite(sa_stream_t *s, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence)) ++UNSUPPORTED(int sa_stream_pwrite_ni(sa_stream_t *s, unsigned int channel, const void *data, size_t nbytes, int64_t offset, sa_seek_t whence)) ++UNSUPPORTED(int sa_stream_get_read_size(sa_stream_t *s, size_t *size)) ++ ++const char *sa_strerror(int code) { return NULL; } ++ ++/*****************************************************************************/ ++ diff --git a/media/libsydneyaudio/sydney_os2_moz.patch b/media/libsydneyaudio/sydney_os2_moz.patch new file mode 100644 --- /dev/null +++ b/media/libsydneyaudio/sydney_os2_moz.patch @@ -0,0 +1,390 @@ +diff --git a/media/libsydneyaudio/src/sydney_audio_os2.c b/media/libsydneyaudio/src/sydney_audio_os2.c +--- a/media/libsydneyaudio/src/sydney_audio_os2.c ++++ b/media/libsydneyaudio/src/sydney_audio_os2.c +@@ -143,27 +143,42 @@ struct sa_stream { + int32_t readyCnt; + int32_t readyNdx; + HEV usedSem; + volatile int32_t usedCnt; + + /* miscellaneous */ + volatile int32_t state; + int64_t writePos; ++ /* workaround for Bug 495352 */ ++ uint32_t zeroCnt; + }; + + /*****************************************************************************/ + /* Private (static) Functions */ + + static int32_t os2_mixer_event(uint32_t ulStatus, PMCI_MIX_BUFFER pBuffer, + uint32_t ulFlags); + static int os2_write_to_device(sa_stream_t *s); + static void os2_stop_device(uint16_t hwDeviceID); + static int os2_pause_device(uint16_t hwDeviceID, uint32_t release); + static int os2_get_free_count(sa_stream_t *s, int32_t count); ++ ++/*****************************************************************************/ ++/* Mozilla-specific Additions */ ++ ++/* reset the decode thread's priority */ ++static void os2_set_priority(void); ++ ++/* load mdm.dll on demand */ ++static int os2_load_mdm(void); ++ ++/* invoke mciSendCommand() via a static variable */ ++typedef ULONG _System MCISENDCOMMAND(USHORT, USHORT, ULONG, PVOID, USHORT); ++static MCISENDCOMMAND * _mciSendCommand = 0; + + /*****************************************************************************/ + /* Sydney Audio Functions */ + /*****************************************************************************/ + + /** Normal way to open a PCM device */ + + int sa_stream_create_pcm(sa_stream_t ** s, +@@ -176,16 +191,20 @@ int sa_stream_create_pcm(sa_stream_t + uint32_t status = SA_SUCCESS; + uint32_t size; + uint32_t rc; + sa_stream_t * sTemp = 0; + + /* this do{}while(0) "loop" makes it easy to ensure + * resources are freed on exit if there's an error */ + do { ++ /* load mdm.dll if it isn't already loaded */ ++ if (os2_load_mdm() != SA_SUCCESS) ++ return SA_ERROR_SYSTEM; ++ + if (mode != SA_MODE_WRONLY || format != SA_PCM_FORMAT_S16_LE) + return os2_error(SA_ERROR_NOT_SUPPORTED, "sa_stream_create_pcm", + "invalid mode or format", 0); + + if (!s) + return os2_error(SA_ERROR_INVALID, "sa_stream_create_pcm", + "s is null", 0); + *s = 0; +@@ -257,25 +276,28 @@ int sa_stream_open(sa_stream_t *s) + MCI_AMP_OPEN_PARMS AmpOpenParms; + MCI_MIXSETUP_PARMS MixSetupParms; + MCI_BUFFER_PARMS BufferParms; + + if (!s) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_open", "s is null", 0); + + do { ++ /* set this thread's priority to 2-08 */ ++ os2_set_priority(); ++ + /* s->bufCnt will be restored after successfully allocating buffers */ + bufCntRequested = s->bufCnt; + s->bufCnt = 0; + + /* open the Amp-Mixer using the default device in shared mode */ + memset(&AmpOpenParms, 0, sizeof(MCI_AMP_OPEN_PARMS)); + AmpOpenParms.pszDeviceType = (PSZ)(MCI_DEVTYPE_AUDIO_AMPMIX | 0); + +- rc = mciSendCommand(0, MCI_OPEN, ++ rc = _mciSendCommand(0, MCI_OPEN, + MCI_WAIT | MCI_OPEN_TYPE_ID | MCI_OPEN_SHAREABLE, + (void*)&AmpOpenParms, 0); + if (LOUSHORT(rc)) { + status = os2_error(SA_ERROR_NO_DEVICE, "sa_stream_open", + "MCI_OPEN - rc=", LOUSHORT(rc)); + break; + } + +@@ -287,17 +309,17 @@ do { + MixSetupParms.ulBitsPerSample = 16; + MixSetupParms.ulFormatTag = MCI_WAVE_FORMAT_PCM; + MixSetupParms.ulFormatMode = MCI_PLAY; + MixSetupParms.ulSamplesPerSec = s->rate; + MixSetupParms.ulChannels = s->nchannels; + MixSetupParms.ulDeviceType = MCI_DEVTYPE_WAVEFORM_AUDIO; + MixSetupParms.pmixEvent = (MIXEREVENT*)os2_mixer_event; + +- rc = mciSendCommand(s->hwDeviceID, MCI_MIXSETUP, ++ rc = _mciSendCommand(s->hwDeviceID, MCI_MIXSETUP, + MCI_WAIT | MCI_MIXSETUP_INIT, + (void*)&MixSetupParms, 0); + if (LOUSHORT(rc)) { + status = os2_error(SA_ERROR_NOT_SUPPORTED, "sa_stream_open", + "MCI_MIXSETUP - rc=", LOUSHORT(rc)); + break; + } + +@@ -306,17 +328,17 @@ do { + s->hwWriteProc = MixSetupParms.pmixWrite; + + /* allocate device buffers from the Amp-Mixer */ + BufferParms.ulStructLength = sizeof(MCI_BUFFER_PARMS); + BufferParms.ulNumBuffers = bufCntRequested; + BufferParms.ulBufferSize = s->bufSize; + BufferParms.pBufList = s->bufList; + +- rc = mciSendCommand(s->hwDeviceID, MCI_BUFFER, ++ rc = _mciSendCommand(s->hwDeviceID, MCI_BUFFER, + MCI_WAIT | MCI_ALLOCATE_MEMORY, + (void*)&BufferParms, 0); + if (LOUSHORT(rc)) { + status = os2_error(SA_ERROR_OOM, "sa_stream_open", + "MCI_ALLOCATE_MEMORY - rc=", LOUSHORT(rc)); + break; + } + +@@ -363,25 +385,25 @@ int sa_stream_destroy(sa_stream_t *s + /* if hardware buffers were allocated, free them */ + if (s->bufCnt) { + BufferParms.hwndCallback = 0; + BufferParms.ulStructLength = sizeof(MCI_BUFFER_PARMS); + BufferParms.ulNumBuffers = s->bufCnt; + BufferParms.ulBufferSize = s->bufSize; + BufferParms.pBufList = s->bufList; + +- rc = mciSendCommand(s->hwDeviceID, MCI_BUFFER, ++ rc = _mciSendCommand(s->hwDeviceID, MCI_BUFFER, + MCI_WAIT | MCI_DEALLOCATE_MEMORY, + (void*)&BufferParms, 0); + if (LOUSHORT(rc)) + status = os2_error(SA_ERROR_SYSTEM, "sa_stream_destroy", + "MCI_DEALLOCATE_MEMORY - rc=", LOUSHORT(rc)); + } + +- rc = mciSendCommand(s->hwDeviceID, MCI_CLOSE, ++ rc = _mciSendCommand(s->hwDeviceID, MCI_CLOSE, + MCI_WAIT, + (void*)&GenericParms, 0); + if (LOUSHORT(rc)) + status = os2_error(SA_ERROR_SYSTEM, "sa_stream_destroy", + "MCI_CLOSE - rc=", LOUSHORT(rc)); + } + + /* free other resources we allocated */ +@@ -405,18 +427,28 @@ int sa_stream_write(sa_stream_t * s, + PMCI_MIX_BUFFER pHW; + + if (!s) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_write", "s is null", 0); + if (!data) + return os2_error(SA_ERROR_INVALID, "sa_stream_write", "data is null", 0); + + /* exit if no data */ +- if (!nbytes) ++ /* Bug 495352 - this function may get called repeatedly with no data; ++ * as a workaround to prevent as many as 30,000 such calls between valid ++ * writes (and 100% CPU usage), give up the remainder of the current ++ * time-slice every time 16 consecutive zero-byte writes are detected */ ++ if (!nbytes) { ++ s->zeroCnt++; ++ if (!(s->zeroCnt & 0x0f)) { ++ s->zeroCnt = 0; ++ DosSleep(1); ++ } + return SA_SUCCESS; ++ } + + /* This should only loop on the last write before sa_stream_drain() + * is called; at other times, 'nbytes' won't exceed 'bufSize'. */ + while (nbytes) { + + /* get the count of free buffers, wait until at least one + * is available (in practice, this should never block) */ + if (os2_get_free_count(s, 1)) +@@ -473,24 +505,24 @@ int sa_stream_resume(sa_stream_t *s) + { + uint32_t rc; + MCI_GENERIC_PARMS GenericParms = { 0 }; + + if (!s) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_resume", + "s is null", 0); + +- rc = mciSendCommand(s->hwDeviceID, MCI_ACQUIREDEVICE, ++ rc = _mciSendCommand(s->hwDeviceID, MCI_ACQUIREDEVICE, + MCI_WAIT, + (void*)&GenericParms, 0); + if (LOUSHORT(rc)) + return os2_error(SA_ERROR_SYSTEM, "sa_stream_resume", + "MCI_ACQUIREDEVICE - rc=", LOUSHORT(rc)); + +- rc = mciSendCommand(s->hwDeviceID, MCI_RESUME, ++ rc = _mciSendCommand(s->hwDeviceID, MCI_RESUME, + MCI_WAIT, + (void*)&GenericParms, 0); + if (LOUSHORT(rc)) + return os2_error(SA_ERROR_SYSTEM, "sa_stream_resume", + "MCI_RESUME - rc=", LOUSHORT(rc)); + + return SA_SUCCESS; + } +@@ -579,17 +611,17 @@ int sa_stream_set_volume_abs(sa_stre + return os2_error(SA_ERROR_NO_INIT, "sa_stream_set_volume_abs", + "s is null", 0); + + /* convert f.p. value to an integer value ranging + * from 0 to 100 and apply to both channels */ + SetParms.ulLevel = (vol * 100); + SetParms.ulAudio = MCI_SET_AUDIO_ALL; + +- rc = mciSendCommand(s->hwDeviceID, MCI_SET, ++ rc = _mciSendCommand(s->hwDeviceID, MCI_SET, + MCI_WAIT | MCI_SET_AUDIO | MCI_SET_VOLUME, + (void*)&SetParms, 0); + if (LOUSHORT(rc)) + return os2_error(SA_ERROR_SYSTEM, "sa_stream_set_volume_abs", + "MCI_SET_VOLUME - rc=", LOUSHORT(rc)); + + return SA_SUCCESS; + } +@@ -606,17 +638,17 @@ int sa_stream_get_volume_abs(sa_stre + + if (!s || !vol) + return os2_error(SA_ERROR_NO_INIT, "sa_stream_get_volume_abs", + "s or vol is null", 0); + + memset(&StatusParms, 0, sizeof(MCI_STATUS_PARMS)); + StatusParms.ulItem = MCI_STATUS_VOLUME; + +- rc = mciSendCommand(s->hwDeviceID, MCI_STATUS, ++ rc = _mciSendCommand(s->hwDeviceID, MCI_STATUS, + MCI_WAIT | MCI_STATUS_ITEM, + (void*)&StatusParms, 0); + if (LOUSHORT(rc)) { + /* if there's an error, return a reasonable value */ + StatusParms.ulReturn = (50 | 50 << 16); + status = os2_error(SA_ERROR_SYSTEM, "sa_stream_get_volume_abs", + "MCI_STATUS_VOLUME - rc=", LOUSHORT(rc)); + } +@@ -737,17 +769,17 @@ static int os2_write_to_device(sa_strea + + /** stop playback */ + + static void os2_stop_device(uint16_t hwDeviceID) + { + uint32_t rc; + MCI_GENERIC_PARMS GenericParms = { 0 }; + +- rc = mciSendCommand(hwDeviceID, MCI_STOP, ++ rc = _mciSendCommand(hwDeviceID, MCI_STOP, + MCI_WAIT, + (void*)&GenericParms, 0); + if (LOUSHORT(rc)) + os2_error(0, "os2_stop_device", "MCI_STOP - rc=", LOUSHORT(rc)); + + return; + } + +@@ -755,25 +787,25 @@ static void os2_stop_device(uint16_t hwD + + /** pause playback and optionally release device */ + + static int os2_pause_device(uint16_t hwDeviceID, uint32_t release) + { + uint32_t rc; + MCI_GENERIC_PARMS GenericParms = { 0 }; + +- rc = mciSendCommand(hwDeviceID, MCI_PAUSE, ++ rc = _mciSendCommand(hwDeviceID, MCI_PAUSE, + MCI_WAIT, + (void*)&GenericParms, 0); + if (LOUSHORT(rc)) + return os2_error(SA_ERROR_SYSTEM, "os2_pause_device", + "MCI_PAUSE - rc=", LOUSHORT(rc)); + + if (release) +- mciSendCommand(hwDeviceID, MCI_RELEASEDEVICE, ++ _mciSendCommand(hwDeviceID, MCI_RELEASEDEVICE, + MCI_WAIT, + (void*)&GenericParms, 0); + + return SA_SUCCESS; + } + + /*****************************************************************************/ + +@@ -816,16 +848,84 @@ static int os2_error_msg(int rtn, char + else + fprintf(stderr, "sa_os2 error - %s: %s %u\n", func, msg, err); + fflush(stderr); + + return rtn; + } + + #endif ++ ++/*****************************************************************************/ ++/* Mozilla-specific Functions */ ++/*****************************************************************************/ ++ ++/** load mdm.dll & get the entrypoint for mciSendCommand() */ ++ ++static int os2_load_mdm(void) ++{ ++ uint32_t rc; ++ HMODULE hmod; ++ char text[32]; ++ ++ if (_mciSendCommand) ++ return SA_SUCCESS; ++ ++ rc = DosLoadModule(text, sizeof(text), "MDM", &hmod); ++ if (rc) ++ return os2_error(SA_ERROR_SYSTEM, "os2_load_mdm", ++ "DosLoadModule - rc=", rc); ++ ++ /* the ordinal for mciSendCommand is '1' */ ++ rc = DosQueryProcAddr(hmod, 1, 0, (PFN*)&_mciSendCommand); ++ if (rc) { ++ _mciSendCommand = 0; ++ return os2_error(SA_ERROR_SYSTEM, "os2_load_mdm", ++ "DosQueryProcAddr - rc=", rc); ++ } ++ ++ return SA_SUCCESS; ++} ++ ++/*****************************************************************************/ ++ ++/** adjust the decode thread's priority */ ++ ++static void os2_set_priority(void) ++{ ++ uint32_t rc; ++ uint32_t priority; ++ int32_t delta; ++ int32_t newdelta; ++ PTIB ptib; ++ PPIB ppib; ++ ++#define SAOS2_PRIORITY 8 ++ ++ DosGetInfoBlocks(&ptib, &ppib); ++ priority = ptib->tib_ptib2->tib2_ulpri; ++ delta = priority & 0xff; ++ priority >>= 8; ++ ++ /* if the current priority class is other than "regular" (priority 2), ++ * don't change anything - otherwise, calculate a delta that will set ++ * the priority to SAOS2_PRIORITY */ ++ if (priority != PRTYC_REGULAR) ++ newdelta = 0; ++ else ++ newdelta = SAOS2_PRIORITY - delta; ++ ++ if (newdelta) { ++ rc = DosSetPriority(PRTYS_THREAD, PRTYC_NOCHANGE, newdelta, 0); ++ if (rc) ++ rc = os2_error(rc, "os2_set_priority", "DosSetPriority - rc=", rc); ++ } ++ ++ return; ++} + + /*****************************************************************************/ + /* Not Implemented / Not Supported */ + /*****************************************************************************/ + + #define UNSUPPORTED(func) func { return SA_ERROR_NOT_SUPPORTED; } + + UNSUPPORTED(int sa_stream_create_opaque(sa_stream_t **s, const char *client_name, sa_mode_t mode, const char *codec)) diff --git a/media/libsydneyaudio/update.sh b/media/libsydneyaudio/update.sh --- a/media/libsydneyaudio/update.sh +++ b/media/libsydneyaudio/update.sh @@ -6,3 +6,5 @@ cp $1/src/*.c src/ cp $1/AUTHORS ./AUTHORS patch -p4 mTime) { - mon.Wait(PR_MillisecondsToInterval(PRInt64((frame->mTime - time)*1000))); - if (mState == DECODER_STATE_SHUTDOWN) - return; - continue; - } - break; + // Is it time for the next frame? Using an integer here avoids f.p. + // rounding errors that can cause multiple 0ms waits (Bug 495352) + PRInt64 wait = PRInt64((frame->mTime - time)*1000); + if (wait <= 0) + break; + mon.Wait(PR_MillisecondsToInterval(wait)); + if (mState == DECODER_STATE_SHUTDOWN) + return; } mDecodedFrames.Pop(); From: Rich Walsh [OS/2] Bug 500654: sync OS/2 plugin code with Windows version, to hopefully fix some Flash-related crashes (1.9.1 version), r=pweilbacher diff --git a/modules/plugin/base/src/nsPluginNativeWindowOS2.cpp b/modules/plugin/base/src/nsPluginNativeWindowOS2.cpp --- a/modules/plugin/base/src/nsPluginNativeWindowOS2.cpp +++ b/modules/plugin/base/src/nsPluginNativeWindowOS2.cpp @@ -37,12 +37,16 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ + +/*****************************************************************************/ + #define INCL_WIN #include "os2.h" #include "nsDebug.h" #include "nsIPluginInstancePeer.h" +#include "nsIPluginInstanceInternal.h" #include "nsPluginSafety.h" #include "nsPluginNativeWindow.h" #include "nsThreadUtils.h" @@ -52,6 +56,11 @@ static NS_DEFINE_CID(kCPluginManagerCID, NS_PLUGINMANAGER_CID); // needed for NS_TRY_SAFE_CALL #define NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION "MozillaPluginWindowPropertyAssociation" +#define NS_PLUGIN_CUSTOM_MSG_ID "MozFlashUserRelay" +#define WM_USER_FLASH WM_USER+1 +#ifndef WM_FOCUSCHANGED +#define WM_FOCUSCHANGED 0x000E +#endif typedef nsTWeakRef PluginWindowWeakRef; @@ -64,10 +73,17 @@ PVOID pvData, ULONG ulFlags); } +/*****************************************************************************/ + +static ULONG sWM_FLASHBOUNCEMSG = 0; + +/*****************************************************************************/ /** * PLEvent handling code */ -class PluginWindowEvent : public nsRunnable { + +class PluginWindowEvent : public nsRunnable +{ public: PluginWindowEvent(); void Init(const PluginWindowWeakRef &ref, HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2); @@ -110,8 +126,30 @@ mLParam = mp2; } +/*****************************************************************************/ + +class nsDelayedPopupsEnabledEvent : public nsRunnable +{ +public: + nsDelayedPopupsEnabledEvent(nsIPluginInstanceInternal *inst) + : mInst(inst) + {} + + NS_DECL_NSIRUNNABLE + +private: + nsCOMPtr mInst; +}; + +NS_IMETHODIMP nsDelayedPopupsEnabledEvent::Run() +{ + mInst->PushPopupsEnabledState(PR_FALSE); + return NS_OK; +} + +/*****************************************************************************/ /** - * nsPluginNativeWindow Windows specific class declaration + * nsPluginNativeWindow OS/2-specific class declaration */ typedef enum { @@ -121,7 +159,8 @@ nsPluginType_Other } nsPluginType; -class nsPluginNativeWindowOS2 : public nsPluginNativeWindow { +class nsPluginNativeWindowOS2 : public nsPluginNativeWindow +{ public: nsPluginNativeWindowOS2(); virtual ~nsPluginNativeWindowOS2(); @@ -134,6 +173,7 @@ public: // locals + PFNWP GetPrevWindowProc(); PFNWP GetWindowProc(); PluginWindowEvent* GetPluginWindowEvent(HWND aWnd, ULONG aMsg, @@ -141,6 +181,7 @@ MPARAM mp2); private: + PFNWP mPrevWinProc; PFNWP mPluginWinProc; PluginWindowWeakRef mWeakRef; nsRefPtr mCachedPluginWindowEvent; @@ -149,12 +190,24 @@ nsPluginType mPluginType; }; -static PRBool ProcessFlashMessageDelayed(nsPluginNativeWindowOS2 * aWin, - HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) +/*****************************************************************************/ + +static PRBool ProcessFlashMessageDelayed(nsPluginNativeWindowOS2 * aWin, + nsIPluginInstance * aInst, + HWND hWnd, ULONG msg, + MPARAM mp1, MPARAM mp2) { NS_ENSURE_TRUE(aWin, NS_ERROR_NULL_POINTER); + NS_ENSURE_TRUE(aInst, NS_ERROR_NULL_POINTER); - if (msg != WM_USER+1) + if (msg == sWM_FLASHBOUNCEMSG) { + // See PluginWindowEvent::Run() below. + NS_TRY_SAFE_CALL_VOID((aWin->GetWindowProc())(hWnd, WM_USER_FLASH, mp1, mp2), + nsnull, inst); + return TRUE; + } + + if (msg != WM_USER_FLASH) return PR_FALSE; // no need to delay // do stuff @@ -166,20 +219,26 @@ return PR_FALSE; } +/*****************************************************************************/ /** * New plugin window procedure */ -MRESULT EXPENTRY PluginWndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) +static MRESULT EXPENTRY PluginWndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) { - nsPluginNativeWindowOS2 * win = (nsPluginNativeWindowOS2 *)::WinQueryProperty(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION); + nsPluginNativeWindowOS2 * win = (nsPluginNativeWindowOS2 *) + ::WinQueryProperty(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION); if (!win) return (MRESULT)TRUE; - // check plugin mime type and cache whether it is Flash or java-vm or not + // The DispatchEvent(NS_PLUGIN_ACTIVATE) below can trigger a reentrant focus + // event which might destroy us. Hold a strong ref on the plugin instance + // to prevent that, bug 374229. + nsCOMPtr inst; + win->GetPluginInstance(inst); + + // check plugin mime type and cache whether it is Flash or java-vm or not; // flash and java-vm will need special treatment later if (win->mPluginType == nsPluginType_Unknown) { - nsCOMPtr inst; - win->GetPluginInstance(inst); if (inst) { nsCOMPtr pip; inst->GetPeer(getter_AddRefs(pip)); @@ -198,31 +257,112 @@ } } + PRBool enablePopups = PR_FALSE; + + // Activate/deactivate mouse capture on the plugin widget + // here, before we pass the Windows event to the plugin + // because its possible our widget won't get paired events + // (see bug 131007) and we'll look frozen. Note that this + // is also done in ChildWindow::DispatchMouseEvent. + switch (msg) { + case WM_BUTTON1DOWN: + case WM_BUTTON2DOWN: + case WM_BUTTON3DOWN: { + nsCOMPtr widget; + win->GetPluginWidget(getter_AddRefs(widget)); + if (widget) + widget->CaptureMouse(PR_TRUE); + break; + } + + case WM_BUTTON1UP: + case WM_BUTTON2UP: + case WM_BUTTON3UP: { + if (msg == WM_BUTTON1UP) + enablePopups = PR_TRUE; + + nsCOMPtr widget; + win->GetPluginWidget(getter_AddRefs(widget)); + if (widget) + widget->CaptureMouse(PR_FALSE); + break; + } + + case WM_CHAR: + // Ignore repeating keydown messages... + if (SHORT1FROMMP(mp1) & KC_PREVDOWN) + break; + enablePopups = PR_TRUE; + break; + + case WM_SETFOCUS: + case WM_FOCUSCHANGE: + case WM_FOCUSCHANGED: + case WM_ACTIVATE: { + // Make sure setfocus and focuschange get through + // even if they are eaten by the plugin + PFNWP prevWndProc = win->GetPrevWindowProc(); + if (prevWndProc) + prevWndProc(hWnd, msg, mp1, mp2); + break; + } + } + // Macromedia Flash plugin may flood the message queue with some special messages // (WM_USER+1) causing 100% CPU consumption and GUI freeze, see mozilla bug 132759; // we can prevent this from happening by delaying the processing such messages; if (win->mPluginType == nsPluginType_Flash) { - if (ProcessFlashMessageDelayed(win, hWnd, msg, mp1, mp2)) + if (ProcessFlashMessageDelayed(win, inst, hWnd, msg, mp1, mp2)) return (MRESULT)TRUE; } + nsCOMPtr instInternal; + + if (enablePopups) { + nsCOMPtr tmp = do_QueryInterface(inst); + + if (tmp && !nsVersionOK(tmp->GetPluginAPIVersion(), + NP_POPUP_API_VERSION)) { + tmp.swap(instInternal); + + instInternal->PushPopupsEnabledState(PR_TRUE); + } + } + MRESULT res = (MRESULT)TRUE; + if (win->mPluginType == nsPluginType_Java_vm) + NS_TRY_SAFE_CALL_RETURN(res, ::WinDefWindowProc(hWnd, msg, mp1, mp2), nsnull, inst); + else + NS_TRY_SAFE_CALL_RETURN(res, (win->GetWindowProc())(hWnd, msg, mp1, mp2), nsnull, inst); - nsCOMPtr inst; - win->GetPluginInstance(inst); + if (instInternal) { + // Popups are enabled (were enabled before the call to + // CallWindowProc()). Some plugins (at least the flash player) + // post messages from their key handlers etc that delay the actual + // processing, so we need to delay the disabling of popups so that + // popups remain enabled when the flash player ends up processing + // the actual key handlers. We do this by posting an event that + // does the disabling, this way our disabling will happen after + // the handlers in the plugin are done. - if (win->mPluginType == nsPluginType_Java_vm) { - NS_TRY_SAFE_CALL_RETURN(res, WinDefWindowProc(hWnd, msg, mp1, mp2), nsnull, inst); - } else { - NS_TRY_SAFE_CALL_RETURN(res, (win->GetWindowProc())(hWnd, msg, mp1, mp2), nsnull, inst); + // Note that it's not fatal if any of this fails (which won't + // happen unless we're out of memory anyways) since the plugin + // code will pop any popup state pushed by this plugin on + // destruction. + + nsCOMPtr event = new nsDelayedPopupsEnabledEvent(instInternal); + if (event) + NS_DispatchToCurrentThread(event); } return res; } +/*****************************************************************************/ /** * nsPluginNativeWindowOS2 implementation */ + nsPluginNativeWindowOS2::nsPluginNativeWindowOS2() : nsPluginNativeWindow() { // initialize the struct fields @@ -232,8 +372,18 @@ width = 0; height = 0; + mPrevWinProc = NULL; mPluginWinProc = NULL; mPluginType = nsPluginType_Unknown; + + // once the atom has been added, it won't be deleted + if (!sWM_FLASHBOUNCEMSG) { + sWM_FLASHBOUNCEMSG = ::WinFindAtom(WinQuerySystemAtomTable(), + NS_PLUGIN_CUSTOM_MSG_ID); + if (!sWM_FLASHBOUNCEMSG) + sWM_FLASHBOUNCEMSG = ::WinAddAtom(WinQuerySystemAtomTable(), + NS_PLUGIN_CUSTOM_MSG_ID); + } } nsPluginNativeWindowOS2::~nsPluginNativeWindowOS2() @@ -241,6 +391,11 @@ // clear weak reference to self to prevent any pending events from // dereferencing this. mWeakRef.forget(); +} + +PFNWP nsPluginNativeWindowOS2::GetPrevWindowProc() +{ + return mPrevWinProc; } PFNWP nsPluginNativeWindowOS2::GetWindowProc() @@ -337,9 +492,11 @@ if (!mPluginWinProc) return NS_ERROR_FAILURE; +#ifdef DEBUG nsPluginNativeWindowOS2 * win = (nsPluginNativeWindowOS2 *)::WinQueryProperty(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION); NS_ASSERTION(!win || (win == this), "plugin window already has property and this is not us"); - +#endif + if (!::WinSetProperty(hWnd, NS_PLUGIN_WINDOW_PROPERTY_ASSOCIATION, (PVOID)this, 0)) return NS_ERROR_FAILURE; [OS/2] Bug 503744: do not unload MDM to fix MMOS2 destruction diff --git a/widget/src/os2/nsSound.cpp b/widget/src/os2/nsSound.cpp --- a/widget/src/os2/nsSound.cpp +++ b/widget/src/os2/nsSound.cpp @@ -70,7 +70,6 @@ static int sInitialized = 0; static PRBool sMMPMInstalled = PR_FALSE; static HMODULE sHModMMIO = NULLHANDLE; -static HMODULE sHModMDM = NULLHANDLE; // function pointer definitions, include underscore (work around redef. warning) HMMIO (*APIENTRY _mmioOpen)(PSZ, PMMIOINFO, ULONG); @@ -100,9 +99,10 @@ { ULONG ulrc = 0; char LoadError[CCHMAXPATH]; + HMODULE hModMDM = NULLHANDLE; ulrc = DosLoadModule(LoadError, CCHMAXPATH, "MMIO", &sHModMMIO); - ulrc += DosLoadModule(LoadError, CCHMAXPATH, "MDM", &sHModMDM); + ulrc += DosLoadModule(LoadError, CCHMAXPATH, "MDM", &hModMDM); if (ulrc == NO_ERROR) { #ifdef DEBUG printf("InitGlobals: MMOS2 is installed, both DLLs loaded\n"); @@ -114,12 +114,12 @@ ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioClose", (PFN *)&_mmioClose); ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioGetFormats", (PFN *)&_mmioGetFormats); // mci functions are in MDM.DLL - ulrc += DosQueryProcAddr(sHModMDM, 0L, "mciSendCommand", (PFN *)&_mciSendCommand); + ulrc += DosQueryProcAddr(hModMDM, 0L, "mciSendCommand", (PFN *)&_mciSendCommand); #ifdef DEBUG ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioGetLastError", (PFN *)&_mmioGetLastError); ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioQueryFormatCount", (PFN *)&_mmioQueryFormatCount); ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioGetFormatName", (PFN *)&_mmioGetFormatName); - ulrc += DosQueryProcAddr(sHModMDM, 0L, "mciGetErrorString", (PFN *)&_mciGetErrorString); + ulrc += DosQueryProcAddr(hModMDM, 0L, "mciGetErrorString", (PFN *)&_mciGetErrorString); #endif ulrc += DosQueryProcAddr(sHModMMIO, 0L, "mmioIniFileHandler", (PFN *)&_mmioIniFileHandler); @@ -398,7 +398,7 @@ #endif ULONG ulrc; ulrc = DosFreeModule(sHModMMIO); - ulrc += DosFreeModule(sHModMDM); + // do not free MDM.DLL because it doesn't like to be unloaded repeatedly if (ulrc != NO_ERROR) { NS_WARNING("DosFreeModule did not work"); }