Merge m-c to fx-team. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-07-11 16:43:23 -04:00
commit 58c90485f3
454 changed files with 10746 additions and 5266 deletions

View File

@ -19,13 +19,13 @@
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/> <project name="gaia.git" path="gaia" remote="mozillaorg" revision="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bf9aaf39dd5a6491925a022db167c460f8207d34"/> <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/> <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
<!-- Stock Android things --> <!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/> <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/> <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -17,10 +17,10 @@
</project> </project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/> <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things --> <!-- Stock Android things -->

View File

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1"> <project name="platform_build" path="build" remote="b2g" revision="276ce45e78b09c4a4ee643646f691d22804754c1">
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/> <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
@ -23,7 +23,7 @@
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
<!-- Stock Android things --> <!-- Stock Android things -->
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/> <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/> <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>

View File

@ -19,13 +19,13 @@
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/> <project name="gaia.git" path="gaia" remote="mozillaorg" revision="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/> <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bf9aaf39dd5a6491925a022db167c460f8207d34"/> <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="bf9aaf39dd5a6491925a022db167c460f8207d34"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/> <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
<!-- Stock Android things --> <!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/> <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/> <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>

View File

@ -17,10 +17,10 @@
</project> </project>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/> <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/> <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things --> <!-- Stock Android things -->

View File

@ -4,6 +4,6 @@
"remote": "", "remote": "",
"branch": "" "branch": ""
}, },
"revision": "ccd8b59df6230fc0eb5d47e9c617e58ddb4673dd", "revision": "f705a3f96020c7d7aa5ec63bf3417db29e1ab2a2",
"repo_path": "/integration/gaia-central" "repo_path": "/integration/gaia-central"
} }

View File

@ -17,12 +17,12 @@
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/> <project name="gaia.git" path="gaia" remote="mozillaorg" revision="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/> <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/> <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
<!-- Stock Android things --> <!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/> <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/> <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>

View File

@ -15,7 +15,7 @@
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/> <project name="gaia.git" path="gaia" remote="mozillaorg" revision="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/> <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>

View File

@ -17,10 +17,10 @@
</project> </project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/> <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/> <project name="gaia" path="gaia" remote="mozillaorg" revision="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/> <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/> <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/> <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
<!-- Stock Android things --> <!-- Stock Android things -->

View File

@ -17,12 +17,12 @@
<copyfile dest="Makefile" src="core/root.mk"/> <copyfile dest="Makefile" src="core/root.mk"/>
</project> </project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/> <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2580a49ddeb99f4bdaaae6716ea99c9547cb6d9f"/> <project name="gaia.git" path="gaia" remote="mozillaorg" revision="40cac290f0a3253d31242d7f50b1d2ddd2f47cda"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/> <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9ff55cd0aefea23e4c60e5844c155c6ebc2e632b"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/> <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/> <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/> <project name="moztt" path="external/moztt" remote="b2g" revision="dc5ca96695cab87b4c2fcd7c9f046ae3415a70a5"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="ee6e7320bb83409ebd4685fbd87a8ae033704182"/> <project name="apitrace" path="external/apitrace" remote="apitrace" revision="aebf432f334ec0b48eb358569b9dfbfbead48017"/>
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/> <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
<!-- Stock Android things --> <!-- Stock Android things -->
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/> <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>

File diff suppressed because it is too large Load Diff

View File

@ -9,13 +9,19 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm"); Cu.import("resource://gre/modules/Promise.jsm");
let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console; Cu.import("resource://gre/modules/osfile.jsm", this);
this.EXPORTED_SYMBOLS = ["MozLoopService"]; this.EXPORTED_SYMBOLS = ["MozLoopService"];
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "injectLoopAPI", XPCOMUtils.defineLazyModuleGetter(this, "injectLoopAPI",
"resource:///modules/loop/MozLoopAPI.jsm"); "resource:///modules/loop/MozLoopAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "convertToRTCStatsReport",
"resource://gre/modules/media/RTCStatsReport.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Chat", "resource:///modules/Chat.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Chat", "resource:///modules/Chat.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils", XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
@ -33,6 +39,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "deriveHawkCredentials",
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler", XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
"resource:///modules/loop/MozLoopPushHandler.jsm"); "resource:///modules/loop/MozLoopPushHandler.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
/** /**
* Internal helper methods and state * Internal helper methods and state
* *
@ -303,6 +313,72 @@ let MozLoopServiceInternal = {
return this._localizedStrings = map; return this._localizedStrings = map;
}, },
/**
* Saves loop logs to the saved-telemetry-pings folder.
*
* @param {Object} pc The peerConnection in question.
*/
stageForTelemetryUpload: function(window, pc) {
window.WebrtcGlobalInformation.getAllStats(allStats => {
let internalFormat = allStats.reports[0]; // filtered on pc.id
window.WebrtcGlobalInformation.getLogging('', logs => {
let report = convertToRTCStatsReport(internalFormat);
let logStr = "";
logs.forEach(s => { logStr += s + "\n"; });
// We have stats and logs.
// Create worker job. ping = saved telemetry ping file header + payload
//
// Prepare payload according to https://wiki.mozilla.org/Loop/Telemetry
let ai = Services.appinfo;
let uuid = uuidgen.generateUUID().toString();
uuid = uuid.substr(1,uuid.length-2); // remove uuid curly braces
let directory = OS.Path.join(OS.Constants.Path.profileDir,
"saved-telemetry-pings");
let job = {
directory: directory,
filename: uuid + ".json",
ping: {
reason: "loop",
slug: uuid,
payload: {
ver: 1,
info: {
appUpdateChannel: ai.defaultUpdateChannel,
appBuildID: ai.appBuildID,
appName: ai.name,
appVersion: ai.version,
reason: "loop",
OS: ai.OS,
version: Services.sysinfo.getProperty("version")
},
report: "ice failure",
connectionstate: pc.iceConnectionState,
stats: report,
localSdp: internalFormat.localSdp,
remoteSdp: internalFormat.remoteSdp,
log: logStr
}
}
};
// Send job to worker to do log sanitation, transcoding and saving to
// disk for pickup by telemetry on next startup, which then uploads it.
let worker = new ChromeWorker("MozLoopWorker.js");
worker.onmessage = function(e) {
console.log(e.data.ok ?
"Successfully staged loop report for telemetry upload." :
("Failed to stage loop report. Error: " + e.data.fail));
}
worker.postMessage(job);
});
}, pc.id);
},
/** /**
* Opens the chat window * Opens the chat window
* *
@ -310,9 +386,8 @@ let MozLoopServiceInternal = {
* be null. * be null.
* @param {String} title The title of the chat window. * @param {String} title The title of the chat window.
* @param {String} url The page to load in the chat window. * @param {String} url The page to load in the chat window.
* @param {String} mode May be "minimized" or undefined.
*/ */
openChatWindow: function(contentWindow, title, url, mode) { openChatWindow: function(contentWindow, title, url) {
// So I guess the origin is the loop server!? // So I guess the origin is the loop server!?
let origin = this.loopServerUri; let origin = this.loopServerUri;
url = url.spec || url; url = url.spec || url;
@ -332,8 +407,33 @@ let MozLoopServiceInternal = {
return; return;
} }
chatbox.removeEventListener("DOMContentLoaded", loaded, true); chatbox.removeEventListener("DOMContentLoaded", loaded, true);
injectLoopAPI(chatbox.contentWindow);
}, true); let window = chatbox.contentWindow;
injectLoopAPI(window);
let ourID = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
let onPCLifecycleChange = (pc, winID, type) => {
if (winID != ourID) {
return;
}
if (type == "iceconnectionstatechange") {
switch(pc.iceConnectionState) {
case "failed":
case "disconnected":
if (Services.telemetry.canSend ||
Services.prefs.getBoolPref("toolkit.telemetry.test")) {
this.stageForTelemetryUpload(window, pc);
}
break;
}
}
};
let pc_static = new window.mozRTCPeerConnectionStatic();
pc_static.registerPeerConnectionLifecycleCallback(onPCLifecycleChange);
}.bind(this), true);
}; };
Chat.open(contentWindow, origin, title, url, undefined, undefined, callback); Chat.open(contentWindow, origin, title, url, undefined, undefined, callback);

View File

@ -0,0 +1,161 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
/**
* A worker dedicated to loop-report sanitation and writing for MozLoopService.
*/
"use strict";
importScripts("resource://gre/modules/osfile.jsm");
let File = OS.File;
let Encoder = new TextEncoder();
let Counter = 0;
const MAX_LOOP_LOGS = 5;
/**
* Communications with the controller.
*
* Accepts messages:
* { path: filepath, ping: data }
*
* Sends messages:
* { ok: true }
* { fail: serialized_form_of_OS.File.Error }
*/
onmessage = function(e) {
if (++Counter > MAX_LOOP_LOGS) {
postMessage({
fail: "Maximum " + MAX_LOOP_LOGS + "loop reports reached for this session"
});
return;
}
let directory = e.data.directory;
let filename = e.data.filename;
let ping = e.data.ping;
// Anonymize data
resetIpMask();
ping.payload.localSdp = redactSdp(ping.payload.localSdp);
ping.payload.remoteSdp = redactSdp(ping.payload.remoteSdp);
ping.payload.log = sanitizeLogs(ping.payload.log);
let pingStr = anonymizeIPv4(sanitizeUrls(JSON.stringify(ping)));
// Save to disk
let array = Encoder.encode(pingStr);
try {
File.makeDir(directory,
{ unixMode: OS.Constants.S_IRWXU, ignoreExisting: true });
File.writeAtomic(OS.Path.join(directory, filename), array);
postMessage({ ok: true });
} catch (ex if ex instanceof File.Error) {
// Instances of OS.File.Error know how to serialize themselves
postMessage({fail: File.Error.toMsg(ex)});
}
};
/**
* Mask upper 24-bits of ip address with fake numbers. Call resetIpMask() first.
*/
let IpMap = {};
let IpCount = 0;
function resetIpMask() {
IpMap = {};
IpCount = Math.floor(Math.random() * 16777215) + 1;
}
/**
* Masks upper 24-bits of ip address with fake numbers. Grunt function.
*
* @param {DOMString} ip address
*/
function maskIp(ip) {
let isInvalidOrRfc1918or3927 = function(p1, p2, p3, p4) {
let invalid = octet => octet < 0 || octet > 255;
return invalid(p1) || invalid(p2) || invalid(p3) || invalid(p4) ||
(p1 == 10) ||
(p1 == 172 && p2 >= 16 && p2 <= 31) ||
(p1 == 192 && p2 == 168) ||
(p1 == 169 && p2 == 254);
};
let [p1, p2, p3, p4] = ip.split(".");
if (isInvalidOrRfc1918or3927(p1, p2, p3, p4)) {
return ip;
}
let key = [p1, p2, p3].join();
if (!IpMap[key]) {
do {
IpCount = (IpCount + 1049039) % 16777216; // + prime % 2^24
p1 = (IpCount >> 16) % 256;
p2 = (IpCount >> 8) % 256;
p3 = IpCount % 256;
} while (isInvalidOrRfc1918or3927(p1, p2, p3, p4));
IpMap[key] = p1 + "." + p2 + "." + p3;
}
return IpMap[key] + "." + p4;
}
/**
* Partially masks ip numbers in input text.
*
* @param {DOMString} text Input text containing IP numbers as words.
*/
function anonymizeIPv4(text) {
return text.replace(/\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g,
maskIp.bind(this));
}
/**
* Sanitizes any urls of session information, like
*
* - (id=31 url=https://call.services.mozilla.com/#call/ongoing/AQHYjqH_...)
* + (id=31 url=https://call.services.mozilla.com/#call/xxxx)
*
* - (id=35 url=about:loopconversation#incoming/1403134352854)
* + (id=35 url=about:loopconversation#incoming/xxxx)
*
* @param {DOMString} text The text.
*/
function sanitizeUrls(text) {
let trimUrl = url => url.replace(/(#call|#incoming).*/g,
(match, type) => type + "/xxxx");
return text.replace(/\(id=(\d+) url=([^\)]+)\)/g,
(match, id, url) =>
"(id=" + id + " url=" + trimUrl(url) + ")");
}
/**
* Removes privacy sensitive information from SDP input text outright, like
*
* a=fingerprint:sha-256 E9:DE:6A:FE:2A:2F:05: etc.
* a=identity ...
*
* Redacts lines from match to EOL. Assumes \r\n\ linebreaks.
*
* @param {DOMString} sdp The sdp text.
*/
let redactSdp = sdp => sdp.replace(/\r\na=(fingerprint|identity):.*?\r\n/g,
"\r\n");
/**
* Sanitizes log text of sensitive information, like
*
* - srflx(IP4:192.168.1.3:60348/UDP|turn402-oak.tokbox.com:3478)
* + srflx(IP4:192.168.1.3:60348/UDP|xxxx.xxx)
*
* @param {DOMString} log The log text.
*/
function sanitizeLogs(log) {
let rex = /(srflx|relay)\(IP4:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}\/(UDP|TCP)\|[^\)]+\)/g;
return log.replace(rex, match => match.replace(/\|[^\)]+\)/, "|xxxx.xxx)"));
}

View File

@ -14,4 +14,5 @@ EXTRA_JS_MODULES += [
'MozLoopAPI.jsm', 'MozLoopAPI.jsm',
'MozLoopPushHandler.jsm', 'MozLoopPushHandler.jsm',
'MozLoopService.jsm', 'MozLoopService.jsm',
'MozLoopWorker.js',
] ]

View File

@ -134,45 +134,39 @@ this.WebappManager = {
let manifestURL = aData.app.manifestURL; let manifestURL = aData.app.manifestURL;
let cleanup = () => { let nativeApp = new NativeApp(aData.app, jsonManifest,
aData.app.categories);
this.installations[manifestURL] = Promise.defer();
this.installations[manifestURL].promise.then(() => {
notifyInstallSuccess(aData.app, nativeApp, bundle);
}, (error) => {
Cu.reportError("Error installing webapp: " + error);
}).then(() => {
popupProgressContent.removeChild(progressMeter); popupProgressContent.removeChild(progressMeter);
delete this.installations[manifestURL]; delete this.installations[manifestURL];
if (Object.getOwnPropertyNames(this.installations).length == 0) { if (Object.getOwnPropertyNames(this.installations).length == 0) {
notification.remove(); notification.remove();
} }
};
this.installations[manifestURL] = Promise.defer();
this.installations[manifestURL].promise.then(null, (error) => {
Cu.reportError("Error installing webapp: " + error);
cleanup();
}); });
let nativeApp = new NativeApp(aData.app, jsonManifest,
aData.app.categories);
let localDir; let localDir;
try { try {
localDir = nativeApp.createProfile(); localDir = nativeApp.createProfile();
} catch (ex) { } catch (ex) {
Cu.reportError("Error installing webapp: " + ex);
DOMApplicationRegistry.denyInstall(aData); DOMApplicationRegistry.denyInstall(aData);
cleanup();
return; return;
} }
DOMApplicationRegistry.confirmInstall(aData, localDir, DOMApplicationRegistry.confirmInstall(aData, localDir,
(aApp, aManifest, aZipPath) => Task.spawn((function*() { Task.async(function*(aApp, aManifest, aZipPath) {
try { try {
yield nativeApp.install(aApp, aManifest, aZipPath); yield nativeApp.install(aApp, aManifest, aZipPath);
yield this.installations[manifestURL].promise;
notifyInstallSuccess(aApp, nativeApp, bundle);
} catch (ex) { } catch (ex) {
Cu.reportError("Error installing webapp: " + ex); Cu.reportError("Error installing webapp: " + ex);
// TODO: Notify user that the installation has failed throw ex;
} finally {
cleanup();
} }
}).bind(this)) })
); );
} }
}; };

View File

@ -88,13 +88,9 @@ if test -z "$BUILDING_JS" -o -n "$JS_STANDALONE"; then
# Use a separate cache file for libffi, since it does things differently # Use a separate cache file for libffi, since it does things differently
# from our configure. # from our configure.
mkdir -p $_objdir/js/src/ctypes/libffi
old_cache_file=$cache_file
cache_file=$_objdir/js/src/ctypes/libffi/config.cache
old_config_files=$CONFIG_FILES old_config_files=$CONFIG_FILES
unset CONFIG_FILES unset CONFIG_FILES
AC_OUTPUT_SUBDIRS(js/src/ctypes/libffi) AC_OUTPUT_SUBDIRS(js/src/ctypes/libffi)
cache_file=$old_cache_file
ac_configure_args="$_SUBDIR_CONFIG_ARGS" ac_configure_args="$_SUBDIR_CONFIG_ARGS"
CONFIG_FILES=$old_config_files CONFIG_FILES=$old_config_files
fi fi

View File

@ -18,33 +18,45 @@ MOZ_CONFIG_LOG_TRAP
]) ])
dnl Disable the trap when running sub-configures. dnl Disable the trap when running sub-configures.
define([_MOZ_AC_OUTPUT_SUBDIRS], defn([AC_OUTPUT_SUBDIRS])) define(GEN_MOZ_AC_OUTPUT_SUBDIRS, [
define([MOZ_SUBCONFIGURE_WRAP], define([_MOZ_AC_OUTPUT_SUBDIRS], [
[ _CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} patsubst($@, [$srcdir/$ac_config_dir], [$srcdir/$moz_config_srcdir])
case "$host" in ])
*-mingw*) ])
GEN_MOZ_AC_OUTPUT_SUBDIRS(defn([AC_OUTPUT_SUBDIRS]))
define([AC_OUTPUT_SUBDIRS],
[trap '' EXIT
for moz_config_dir in $1; do
case "$moz_config_dir" in
*:*)
moz_config_srcdir=$(echo $moz_config_dir | awk -F: '{print [$]1}')
moz_config_dir=$(echo $moz_config_dir | awk -F: '{print [$]2}')
;;
*)
moz_config_srcdir=$moz_config_dir
;;
esac
_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh}
case "$host" in
*-mingw*)
_CONFIG_SHELL=$(cd $(dirname $_CONFIG_SHELL); pwd -W)/$(basename $_CONFIG_SHELL) _CONFIG_SHELL=$(cd $(dirname $_CONFIG_SHELL); pwd -W)/$(basename $_CONFIG_SHELL)
if test ! -e "$_CONFIG_SHELL" -a -e "${_CONFIG_SHELL}.exe"; then if test ! -e "$_CONFIG_SHELL" -a -e "${_CONFIG_SHELL}.exe"; then
_CONFIG_SHELL="${_CONFIG_SHELL}.exe" _CONFIG_SHELL="${_CONFIG_SHELL}.exe"
fi fi
;; ;;
esac esac
if test -d "$1"; then if test -d "$moz_config_dir"; then
(cd "$1"; $PYTHON $_topsrcdir/build/subconfigure.py dump "$_CONFIG_SHELL") (cd "$moz_config_dir"; eval $PYTHON $_topsrcdir/build/subconfigure.py dump "$_CONFIG_SHELL" $ac_configure_args)
else else
mkdir -p "$1" mkdir -p "$moz_config_dir"
fi fi
$2 _save_cache_file="$cache_file"
(cd "$1"; $PYTHON $_topsrcdir/build/subconfigure.py adjust $ac_sub_configure) ifelse($2,,cache_file="$moz_config_dir/config.cache",cache_file="$2")
]) _MOZ_AC_OUTPUT_SUBDIRS($moz_config_dir)
cache_file="$_save_cache_file"
define([AC_OUTPUT_SUBDIRS], (cd "$moz_config_dir"; $PYTHON $_topsrcdir/build/subconfigure.py adjust $ac_sub_configure)
[trap '' EXIT
for moz_config_dir in $1; do
MOZ_SUBCONFIGURE_WRAP([$moz_config_dir],[
_MOZ_AC_OUTPUT_SUBDIRS($moz_config_dir)
])
done done
MOZ_CONFIG_LOG_TRAP MOZ_CONFIG_LOG_TRAP

View File

@ -154,27 +154,8 @@ if test -z "$BUILDING_JS" -o -n "$JS_STANDALONE"; then
ICU_CPPFLAGS="$ICU_CPPFLAGS -I$icudir/common -I$icudir/i18n" ICU_CPPFLAGS="$ICU_CPPFLAGS -I$icudir/common -I$icudir/i18n"
ICU_CROSS_BUILD_OPT="" ICU_CROSS_BUILD_OPT=""
ICU_SRCDIR=""
if test "$HOST_OS_ARCH" = "WINNT"; then
ICU_SRCDIR="--srcdir=$(cd $srcdir/intl/icu/source; pwd -W)"
fi
if test "$CROSS_COMPILE"; then if test "$CROSS_COMPILE"; then
# Building host tools. It is necessary to build target binary.
case "$HOST_OS_ARCH" in
Darwin)
ICU_TARGET=MacOSX
;;
Linux)
ICU_TARGET=Linux
;;
WINNT)
ICU_TARGET=MSYS/MSVC
;;
DragonFly|FreeBSD|NetBSD|OpenBSD|GNU_kFreeBSD)
ICU_TARGET=BSD
;;
esac
# Remove _DEPEND_CFLAGS from HOST_FLAGS to avoid configure error # Remove _DEPEND_CFLAGS from HOST_FLAGS to avoid configure error
HOST_ICU_CFLAGS="$HOST_CFLAGS" HOST_ICU_CFLAGS="$HOST_CFLAGS"
HOST_ICU_CXXFLAGS="$HOST_CXXFLAGS" HOST_ICU_CXXFLAGS="$HOST_CXXFLAGS"
@ -196,24 +177,20 @@ if test -z "$BUILDING_JS" -o -n "$JS_STANDALONE"; then
abs_srcdir=`(cd $srcdir; pwd)` abs_srcdir=`(cd $srcdir; pwd)`
mkdir -p $_objdir/intl/icu/host mkdir -p $_objdir/intl/icu/host
(cd $_objdir/intl/icu/host (export AR="$HOST_AR"
MOZ_SUBCONFIGURE_WRAP([.],[ export RANLIB="$HOST_RANLIB"
AR="$HOST_AR" RANLIB="$HOST_RANLIB" \ export CC="$HOST_CC"
CC="$HOST_CC" CXX="$HOST_CXX" LD="$HOST_LD" \ export CXX="$HOST_CXX"
CFLAGS="$HOST_ICU_CFLAGS $HOST_OPTIMIZE_FLAGS" \ export CPP="$HOST_CPP"
CPPFLAGS="$ICU_CPPFLAGS" \ export LD="$HOST_LD"
CXXFLAGS="$HOST_ICU_CXXFLAGS $HOST_OPTIMIZE_FLAGS" \ export CFLAGS="$HOST_ICU_CFLAGS $HOST_OPTIMIZE_FLAGS"
LDFLAGS="$HOST_LDFLAGS" \ export CPPFLAGS="$ICU_CPPFLAGS"
$SHELL $abs_srcdir/intl/icu/source/runConfigureICU \ export CXXFLAGS="$HOST_ICU_CXXFLAGS $HOST_OPTIMIZE_FLAGS"
$HOST_ICU_BUILD_OPTS \ export LDFLAGS="$HOST_LDFLAGS"
$ICU_TARGET \ ac_configure_args="$HOST_ICU_BUILD_OPTS"
dnl Shell quoting is fun. ac_configure_args="$ac_configure_args --enable-static --disable-shared --enable-extras=no --enable-icuio=no --enable-layout=no --enable-tests=no --enable-samples=no"
${ICU_SRCDIR+"$ICU_SRCDIR"} \ AC_OUTPUT_SUBDIRS(intl/icu/source:intl/icu/host)
--enable-static --disable-shared \ ) || exit 1
--enable-extras=no --enable-icuio=no --enable-layout=no \
--enable-tests=no --enable-samples=no || exit 1
])
) || exit 1
# generate config/icucross.mk # generate config/icucross.mk
$GMAKE -C $_objdir/intl/icu/host/ config/icucross.mk $GMAKE -C $_objdir/intl/icu/host/ config/icucross.mk
@ -308,29 +285,18 @@ if test -z "$BUILDING_JS" -o -n "$JS_STANDALONE"; then
fi fi
fi fi
# We cannot use AC_OUTPUT_SUBDIRS since ICU tree is out of spidermonkey. (export AR="$AR"
# When using AC_OUTPUT_SUBDIRS, objdir of ICU is out of objdir export CC="$CC"
# due to relative path. export CXX="$CXX"
# If building ICU moves into root of mozilla tree, we can use export LD="$LD"
# AC_OUTPUT_SUBDIR instead. export ARFLAGS="$ARFLAGS"
mkdir -p $_objdir/intl/icu/target export CPPFLAGS="$ICU_CPPFLAGS $CPPFLAGS"
(cd $_objdir/intl/icu/target export CFLAGS="$ICU_CFLAGS"
MOZ_SUBCONFIGURE_WRAP([.],[ export CXXFLAGS="$ICU_CXXFLAGS"
AR="$AR" CC="$CC" CXX="$CXX" LD="$LD" \ export LDFLAGS="$ICU_LDFLAGS $LDFLAGS"
ARFLAGS="$ARFLAGS" \ ac_configure_args="$ICU_BUILD_OPTS $ICU_CROSS_BUILD_OPT $ICU_LINK_OPTS $ICU_TARGET_OPT"
CPPFLAGS="$ICU_CPPFLAGS $CPPFLAGS" \ ac_configure_args="$ac_configure_args --disable-extras --disable-icuio --disable-layout --disable-tests --disable-samples"
CFLAGS="$ICU_CFLAGS" \ AC_OUTPUT_SUBDIRS(intl/icu/source:intl/icu/target)
CXXFLAGS="$ICU_CXXFLAGS" \
LDFLAGS="$ICU_LDFLAGS $LDFLAGS" \
$SHELL $_topsrcdir/intl/icu/source/configure \
$ICU_BUILD_OPTS \
$ICU_CROSS_BUILD_OPT \
$ICU_LINK_OPTS \
${ICU_SRCDIR+"$ICU_SRCDIR"} \
$ICU_TARGET_OPT \
--disable-extras --disable-icuio --disable-layout \
--disable-tests --disable-samples || exit 1
])
) || exit 1 ) || exit 1
fi fi

View File

@ -65,9 +65,6 @@ leak:AllocateArrayBufferContents
# Bug 1022010 - Small leak under _render_glyph_outline. bc1 # Bug 1022010 - Small leak under _render_glyph_outline. bc1
leak:_render_glyph_outline leak:_render_glyph_outline
# Bug 1022954 - ScriptSource leaks sourceMapURL_ sometimes. dt
leak:ScriptSource::setSourceMapURL
# Bug 1023548 - Small leak under SECITEM_AllocItem_Util. bc1, bc3 # Bug 1023548 - Small leak under SECITEM_AllocItem_Util. bc1, bc3
leak:SECITEM_AllocItem_Util leak:SECITEM_AllocItem_Util

View File

@ -5,6 +5,7 @@
# This script is used to capture the content of config.status-generated # This script is used to capture the content of config.status-generated
# files and subsequently restore their timestamp if they haven't changed. # files and subsequently restore their timestamp if they haven't changed.
import argparse
import os import os
import re import re
import subprocess import subprocess
@ -60,24 +61,54 @@ PRECIOUS_VARS = set([
# There's no reason not to do the latter automatically instead of failing, # There's no reason not to do the latter automatically instead of failing,
# doing the cleanup (which, on buildbots means a full clobber), and # doing the cleanup (which, on buildbots means a full clobber), and
# restarting from scratch. # restarting from scratch.
def maybe_clear_cache(): def maybe_clear_cache(args):
parser = argparse.ArgumentParser()
parser.add_argument('--target', type=str)
parser.add_argument('--host', type=str)
parser.add_argument('--build', type=str)
args, others = parser.parse_known_args(args)
env = dict(os.environ)
for kind in ('target', 'host', 'build'):
arg = getattr(args, kind)
if arg is not None:
env['%s_alias' % kind] = arg
# configure can take variables assignments in its arguments, and that
# overrides whatever is in the environment.
for arg in others:
if arg[:1] != '-' and '=' in arg:
key, value = arg.split('=', 1)
env[key] = value
comment = re.compile(r'^\s+#') comment = re.compile(r'^\s+#')
cache = {} cache = {}
with open('config.cache') as f: with open('config.cache') as f:
for line in f.readlines(): for line in f:
if not comment.match(line) and '=' in line: if not comment.match(line) and '=' in line:
key, value = line.split('=', 1) key, value = line.rstrip(os.linesep).split('=', 1)
# If the value is quoted, unquote it
if value[:1] == "'":
value = value[1:-1].replace("'\\''", "'")
cache[key] = value cache[key] = value
for precious in PRECIOUS_VARS: for precious in PRECIOUS_VARS:
entry = 'ac_cv_env_%s_value' % precious # If there is no entry at all for that precious variable, then
if entry in cache and (not precious in os.environ or os.environ[precious] != cache[entry]): # its value is not precious for that particular configure.
if 'ac_cv_env_%s_set' % precious not in cache:
continue
is_set = cache.get('ac_cv_env_%s_set' % precious) == 'set'
value = cache.get('ac_cv_env_%s_value' % precious) if is_set else None
if value != env.get(precious):
print 'Removing config.cache because of %s value change from:' \
% precious
print ' %s' % (value if value is not None else 'undefined')
print 'to:'
print ' %s' % env.get(precious, 'undefined')
os.remove('config.cache') os.remove('config.cache')
return return
def dump(dump_file, shell): def dump(dump_file, shell, args):
if os.path.exists('config.cache'): if os.path.exists('config.cache'):
maybe_clear_cache() maybe_clear_cache(args)
if not os.path.exists('config.status'): if not os.path.exists('config.status'):
if os.path.exists(dump_file): if os.path.exists(dump_file):
os.remove(dump_file) os.remove(dump_file)
@ -132,6 +163,6 @@ CONFIG_DUMP = 'config_files.pkl'
if __name__ == '__main__': if __name__ == '__main__':
if sys.argv[1] == 'dump': if sys.argv[1] == 'dump':
dump(CONFIG_DUMP, sys.argv[2]) dump(CONFIG_DUMP, sys.argv[2], sys.argv[3:])
elif sys.argv[1] == 'adjust': elif sys.argv[1] == 'adjust':
adjust(CONFIG_DUMP, sys.argv[2] if len(sys.argv) > 2 else None) adjust(CONFIG_DUMP, sys.argv[2] if len(sys.argv) > 2 else None)

View File

@ -126,6 +126,7 @@ class MachCommands(MachCommandBase):
valgrind_args.append('--suppressions=' + supps_file2) valgrind_args.append('--suppressions=' + supps_file2)
exitcode = None exitcode = None
timeout = 1100
try: try:
runner = FirefoxRunner(profile=profile, runner = FirefoxRunner(profile=profile,
binary=self.get_binary_path(), binary=self.get_binary_path(),
@ -133,14 +134,17 @@ class MachCommands(MachCommandBase):
env=env, env=env,
process_args=kp_kwargs) process_args=kp_kwargs)
runner.start(debug_args=valgrind_args) runner.start(debug_args=valgrind_args)
exitcode = runner.wait() # This timeout is slightly less than the no-output timeout on
# TBPL, so we'll timeout here first and give an informative
# message.
exitcode = runner.wait(timeout=timeout)
finally: finally:
errs = outputHandler.error_count errs = outputHandler.error_count
supps = outputHandler.suppression_count supps = outputHandler.suppression_count
if errs != supps: if errs != supps:
status = 1 # turns the TBPL job orange status = 1 # turns the TBPL job orange
print('TEST-UNEXPECTED-FAILURE | valgrind-test | error parsing:', errs, "errors seen, but", supps, "generated suppressions seen") print('TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {} errors seen, but {} generated suppressions seen'.format(errs, supps))
elif errs == 0: elif errs == 0:
status = 0 status = 0
@ -149,7 +153,10 @@ class MachCommands(MachCommandBase):
status = 1 # turns the TBPL job orange status = 1 # turns the TBPL job orange
# We've already printed details of the errors. # We've already printed details of the errors.
if exitcode != 0: if exitcode == None:
status = 2 # turns the TBPL job red
print('TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out (reached {} second limit)'.format(timeout))
elif exitcode != 0:
status = 2 # turns the TBPL job red status = 2 # turns the TBPL job red
print('TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind') print('TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind')

View File

@ -74,6 +74,7 @@ _MOZBUILD_EXTERNAL_VARIABLES := \
_DEPRECATED_VARIABLES := \ _DEPRECATED_VARIABLES := \
ANDROID_RESFILES \ ANDROID_RESFILES \
EXPORT_LIBRARY \
LIBXUL_LIBRARY \ LIBXUL_LIBRARY \
MOCHITEST_A11Y_FILES \ MOCHITEST_A11Y_FILES \
MOCHITEST_BROWSER_FILES \ MOCHITEST_BROWSER_FILES \
@ -345,7 +346,6 @@ endif
ifdef XPI_NAME ifdef XPI_NAME
ifdef IS_COMPONENT ifdef IS_COMPONENT
EXPORT_LIBRARY=
FORCE_STATIC_LIB= FORCE_STATIC_LIB=
FORCE_SHARED_LIB=1 FORCE_SHARED_LIB=1
endif endif

View File

@ -5,19 +5,6 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
ifdef EXPORT_LIBRARY
ifeq ($(EXPORT_LIBRARY),1)
ifdef IS_COMPONENT
EXPORT_LIBRARY = $(DEPTH)/staticlib/components
else
EXPORT_LIBRARY = $(DEPTH)/staticlib
endif
else
# If EXPORT_LIBRARY has a value, we'll be installing there. We also need to cleanup there
GARBAGE += $(foreach lib,$(LIBRARY),$(EXPORT_LIBRARY)/$(lib))
endif
endif # EXPORT_LIBRARY
binaries libs:: $(SUBMAKEFILES) $(TARGETS) binaries libs:: $(SUBMAKEFILES) $(TARGETS)
ifndef NO_DIST_INSTALL ifndef NO_DIST_INSTALL
ifdef SHARED_LIBRARY ifdef SHARED_LIBRARY
@ -41,12 +28,6 @@ INSTALL_TARGETS += PROGRAMS
endif endif
ifdef LIBRARY ifdef LIBRARY
ifdef EXPORT_LIBRARY
LIBRARY_FILES = $(LIBRARY)
LIBRARY_DEST ?= $(EXPORT_LIBRARY)
LIBRARY_TARGET = binaries libs
INSTALL_TARGETS += LIBRARY
endif
ifdef DIST_INSTALL ifdef DIST_INSTALL
ifdef IS_COMPONENT ifdef IS_COMPONENT
$(error Shipping static component libs makes no sense.) $(error Shipping static component libs makes no sense.)

View File

@ -833,7 +833,7 @@ $(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
$(filter-out %.$(LIB_SUFFIX),$(LIBRARY)): $(filter %.$(LIB_SUFFIX),$(LIBRARY)) $(OBJS) $(EXTRA_DEPS) $(GLOBAL_DEPS) $(filter-out %.$(LIB_SUFFIX),$(LIBRARY)): $(filter %.$(LIB_SUFFIX),$(LIBRARY)) $(OBJS) $(EXTRA_DEPS) $(GLOBAL_DEPS)
# When we only build a library descriptor, blow out any existing library # When we only build a library descriptor, blow out any existing library
$(REPORT_BUILD) $(REPORT_BUILD)
$(if $(filter %.$(LIB_SUFFIX),$(LIBRARY)),,$(RM) $(REAL_LIBRARY) $(EXPORT_LIBRARY:%=%/$(REAL_LIBRARY))) $(if $(filter %.$(LIB_SUFFIX),$(LIBRARY)),,$(RM) $(REAL_LIBRARY))
$(EXPAND_LIBS_GEN) -o $@ $(OBJS) $(SHARED_LIBRARY_LIBS) $(EXPAND_LIBS_GEN) -o $@ $(OBJS) $(SHARED_LIBRARY_LIBS)
ifeq ($(OS_ARCH),WINNT) ifeq ($(OS_ARCH),WINNT)

View File

@ -8783,7 +8783,6 @@ AC_SUBST(CC_VERSION)
AC_SUBST(CXX_VERSION) AC_SUBST(CXX_VERSION)
AC_SUBST(MSMANIFEST_TOOL) AC_SUBST(MSMANIFEST_TOOL)
AC_SUBST(NS_ENABLE_TSF) AC_SUBST(NS_ENABLE_TSF)
AC_SUBST(MOZ_APP_COMPONENT_LIBS)
AC_SUBST(MOZ_APP_EXTRA_LIBS) AC_SUBST(MOZ_APP_EXTRA_LIBS)
AC_SUBST(MOZ_WAVE) AC_SUBST(MOZ_WAVE)
@ -9050,8 +9049,6 @@ if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -
if test "$CROSS_COMPILE"; then if test "$CROSS_COMPILE"; then
ac_configure_args="$ac_configure_args je_cv_static_page_shift=12" ac_configure_args="$ac_configure_args je_cv_static_page_shift=12"
fi fi
_save_cache_file="$cache_file"
cache_file=$_objdir/memory/jemalloc/src/config.cache
if ! test -e memory/jemalloc; then if ! test -e memory/jemalloc; then
mkdir -p memory/jemalloc mkdir -p memory/jemalloc
@ -9064,7 +9061,6 @@ if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -
srcdir=`cd $srcdir; pwd` srcdir=`cd $srcdir; pwd`
AC_OUTPUT_SUBDIRS(memory/jemalloc/src) AC_OUTPUT_SUBDIRS(memory/jemalloc/src)
srcdir="$_save_srcdir" srcdir="$_save_srcdir"
cache_file="$_save_cache_file"
ac_configure_args="$_SUBDIR_CONFIG_ARGS" ac_configure_args="$_SUBDIR_CONFIG_ARGS"
fi fi
@ -9088,7 +9084,7 @@ if test "$MOZ_TREE_FREETYPE"; then
mkdir modules mkdir modules
fi fi
AC_OUTPUT_SUBDIRS(modules/freetype2) AC_OUTPUT_SUBDIRS(modules/freetype2,$cache_file)
fi fi
if test -z "$direct_nspr_config"; then if test -z "$direct_nspr_config"; then
@ -9180,14 +9176,9 @@ if test -z "$MOZ_NATIVE_NSPR"; then
export LDFLAGS="$LDFLAGS $NSPR_LDFLAGS" export LDFLAGS="$LDFLAGS $NSPR_LDFLAGS"
export CFLAGS="$CFLAGS $MOZ_FRAMEPTR_FLAGS" export CFLAGS="$CFLAGS $MOZ_FRAMEPTR_FLAGS"
# Use a separate cache file for NSPR since it uses autoconf 2.68.
_save_cache_file="$cache_file"
cache_file=$_objdir/nsprpub/config.cache
AC_OUTPUT_SUBDIRS(nsprpub) AC_OUTPUT_SUBDIRS(nsprpub)
# .. and restore them # .. and restore them
cache_file="$_save_cache_file"
CFLAGS="$_SAVE_CFLAGS" CFLAGS="$_SAVE_CFLAGS"
CPPFLAGS="$_SAVE_CPPFLAGS" CPPFLAGS="$_SAVE_CPPFLAGS"
LDFLAGS="$_SAVE_LDFLAGS" LDFLAGS="$_SAVE_LDFLAGS"
@ -9277,7 +9268,7 @@ if ! test -e js; then
mkdir js mkdir js
fi fi
AC_OUTPUT_SUBDIRS(js/src) AC_OUTPUT_SUBDIRS(js/src,$cache_file)
ac_configure_args="$_SUBDIR_CONFIG_ARGS" ac_configure_args="$_SUBDIR_CONFIG_ARGS"
fi # COMPILE_ENVIRONMENT && !LIBXUL_SDK_DIR fi # COMPILE_ENVIRONMENT && !LIBXUL_SDK_DIR

View File

@ -477,8 +477,7 @@ public:
/** /**
* Get the ContentSecurityPolicy for a JS context. * Get the ContentSecurityPolicy for a JS context.
**/ **/
static bool GetContentSecurityPolicy(JSContext* aCx, static bool GetContentSecurityPolicy(nsIContentSecurityPolicy** aCSP);
nsIContentSecurityPolicy** aCSP);
// Returns the subject principal. Guaranteed to return non-null. May only // Returns the subject principal. Guaranteed to return non-null. May only
// be called when nsContentUtils is initialized. // be called when nsContentUtils is initialized.

View File

@ -6425,11 +6425,9 @@ nsContentUtils::FindInternalContentViewer(const char* aType,
} }
bool bool
nsContentUtils::GetContentSecurityPolicy(JSContext* aCx, nsContentUtils::GetContentSecurityPolicy(nsIContentSecurityPolicy** aCSP)
nsIContentSecurityPolicy** aCSP)
{ {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
MOZ_ASSERT(aCx == GetCurrentJSContext());
nsCOMPtr<nsIContentSecurityPolicy> csp; nsCOMPtr<nsIContentSecurityPolicy> csp;
nsresult rv = SubjectPrincipal()->GetCsp(getter_AddRefs(csp)); nsresult rv = SubjectPrincipal()->GetCsp(getter_AddRefs(csp));

View File

@ -940,7 +940,7 @@ public:
} else { } else {
SHA1Sum sha1; SHA1Sum sha1;
sha1.update(owner->mData, owner->mLength); sha1.update(owner->mData, owner->mLength);
uint8_t digest[SHA1Sum::HashSize]; // SHA1 digests are 20 bytes long. uint8_t digest[SHA1Sum::kHashSize]; // SHA1 digests are 20 bytes long.
sha1.finish(digest); sha1.finish(digest);
nsAutoCString digestString; nsAutoCString digestString;

View File

@ -1471,9 +1471,11 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
} }
nsAutoCString sourceMapURL; nsAutoCString sourceMapURL;
httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"), sourceMapURL); rv = httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("X-SourceMap"), sourceMapURL);
aRequest->mHasSourceMapURL = true; if (NS_SUCCEEDED(rv)) {
aRequest->mSourceMapURL = NS_ConvertUTF8toUTF16(sourceMapURL); aRequest->mHasSourceMapURL = true;
aRequest->mSourceMapURL = NS_ConvertUTF8toUTF16(sourceMapURL);
}
} }
nsCOMPtr<nsIChannel> channel = do_QueryInterface(req); nsCOMPtr<nsIChannel> channel = do_QueryInterface(req);

View File

@ -1,4 +1,4 @@
//@ sourceMappingURL=bar.js.map //# sourceMappingURL=bar.js.map
// Define a single function to prevent script source from being gc'd // Define a single function to prevent script source from being gc'd
function foo() {} function foo() {}

View File

@ -13,6 +13,7 @@
#include <stdint.h> #include <stdint.h>
#include "MediaDecoderStateMachine.h" #include "MediaDecoderStateMachine.h"
#include "MediaDecoderStateMachineScheduler.h"
#include "AudioSink.h" #include "AudioSink.h"
#include "nsTArray.h" #include "nsTArray.h"
#include "MediaDecoder.h" #include "MediaDecoder.h"
@ -168,8 +169,11 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
MediaDecoderReader* aReader, MediaDecoderReader* aReader,
bool aRealTime) : bool aRealTime) :
mDecoder(aDecoder), mDecoder(aDecoder),
mScheduler(new MediaDecoderStateMachineScheduler(
aDecoder->GetReentrantMonitor(),
&MediaDecoderStateMachine::TimeoutExpired,
MOZ_THIS_IN_INITIALIZER_LIST(), aRealTime)),
mState(DECODER_STATE_DECODING_METADATA), mState(DECODER_STATE_DECODING_METADATA),
mInRunningStateMachine(false),
mSyncPointInMediaStream(-1), mSyncPointInMediaStream(-1),
mSyncPointInDecodedStream(-1), mSyncPointInDecodedStream(-1),
mPlayDuration(0), mPlayDuration(0),
@ -198,30 +202,24 @@ MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
mQuickBuffering(false), mQuickBuffering(false),
mMinimizePreroll(false), mMinimizePreroll(false),
mDecodeThreadWaiting(false), mDecodeThreadWaiting(false),
mRealTime(aRealTime),
mDispatchedDecodeMetadataTask(false), mDispatchedDecodeMetadataTask(false),
mDropAudioUntilNextDiscontinuity(false), mDropAudioUntilNextDiscontinuity(false),
mDropVideoUntilNextDiscontinuity(false), mDropVideoUntilNextDiscontinuity(false),
mDecodeToSeekTarget(false), mDecodeToSeekTarget(false),
mCurrentTimeBeforeSeek(0), mCurrentTimeBeforeSeek(0),
mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED), mLastFrameStatus(MediaDecoderOwner::NEXT_FRAME_UNINITIALIZED)
mTimerId(0)
{ {
MOZ_COUNT_CTOR(MediaDecoderStateMachine); MOZ_COUNT_CTOR(MediaDecoderStateMachine);
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
// Only enable realtime mode when "media.realtime_decoder.enabled" is true.
if (Preferences::GetBool("media.realtime_decoder.enabled", false) == false)
mRealTime = false;
mAmpleVideoFrames = mAmpleVideoFrames =
std::max<uint32_t>(Preferences::GetUint("media.video-queue.default-size", 10), 3); std::max<uint32_t>(Preferences::GetUint("media.video-queue.default-size", 10), 3);
mBufferingWait = mRealTime ? 0 : BUFFERING_WAIT_S; mBufferingWait = mScheduler->IsRealTime() ? 0 : BUFFERING_WAIT_S;
mLowDataThresholdUsecs = mRealTime ? 0 : LOW_DATA_THRESHOLD_USECS; mLowDataThresholdUsecs = mScheduler->IsRealTime() ? 0 : LOW_DATA_THRESHOLD_USECS;
mVideoPrerollFrames = mRealTime ? 0 : mAmpleVideoFrames / 2; mVideoPrerollFrames = mScheduler->IsRealTime() ? 0 : mAmpleVideoFrames / 2;
mAudioPrerollUsecs = mRealTime ? 0 : LOW_AUDIO_USECS * 2; mAudioPrerollUsecs = mScheduler->IsRealTime() ? 0 : LOW_AUDIO_USECS * 2;
#ifdef XP_WIN #ifdef XP_WIN
// Ensure high precision timers are enabled on Windows, otherwise the state // Ensure high precision timers are enabled on Windows, otherwise the state
@ -241,8 +239,6 @@ MediaDecoderStateMachine::~MediaDecoderStateMachine()
"WakeDecoder should have been revoked already"); "WakeDecoder should have been revoked already");
MOZ_ASSERT(!mDecodeTaskQueue, "Should be released in SHUTDOWN"); MOZ_ASSERT(!mDecodeTaskQueue, "Should be released in SHUTDOWN");
// No need to cancel the timer here for we've done that in SHUTDOWN.
MOZ_ASSERT(!mTimer, "Should be released in SHUTDOWN");
mReader = nullptr; mReader = nullptr;
#ifdef XP_WIN #ifdef XP_WIN
@ -1063,10 +1059,6 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
RefPtr<SharedThreadPool> decodePool(GetMediaDecodeThreadPool()); RefPtr<SharedThreadPool> decodePool(GetMediaDecodeThreadPool());
NS_ENSURE_TRUE(decodePool, NS_ERROR_FAILURE); NS_ENSURE_TRUE(decodePool, NS_ERROR_FAILURE);
RefPtr<SharedThreadPool> stateMachinePool(
SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1));
NS_ENSURE_TRUE(stateMachinePool, NS_ERROR_FAILURE);
mDecodeTaskQueue = new MediaTaskQueue(decodePool.forget()); mDecodeTaskQueue = new MediaTaskQueue(decodePool.forget());
NS_ENSURE_TRUE(mDecodeTaskQueue, NS_ERROR_FAILURE); NS_ENSURE_TRUE(mDecodeTaskQueue, NS_ERROR_FAILURE);
@ -1075,12 +1067,7 @@ nsresult MediaDecoderStateMachine::Init(MediaDecoderStateMachine* aCloneDonor)
cloneReader = aCloneDonor->mReader; cloneReader = aCloneDonor->mReader;
} }
mStateMachineThreadPool = stateMachinePool; nsresult rv = mScheduler->Init();
nsresult rv;
mTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = mTimer->SetTarget(GetStateMachineThread());
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
// Note: This creates a cycle, broken in shutdown. // Note: This creates a cycle, broken in shutdown.
@ -1342,8 +1329,8 @@ void MediaDecoderStateMachine::Shutdown()
// Change state before issuing shutdown request to threads so those // Change state before issuing shutdown request to threads so those
// threads can start exiting cleanly during the Shutdown call. // threads can start exiting cleanly during the Shutdown call.
DECODER_LOG(PR_LOG_DEBUG, "Changed state to SHUTDOWN"); DECODER_LOG(PR_LOG_DEBUG, "Changed state to SHUTDOWN");
ScheduleStateMachine();
mState = DECODER_STATE_SHUTDOWN; mState = DECODER_STATE_SHUTDOWN;
mScheduler->ScheduleAndShutdown();
if (mAudioSink) { if (mAudioSink) {
mAudioSink->PrepareToShutdown(); mAudioSink->PrepareToShutdown();
} }
@ -1753,6 +1740,7 @@ MediaDecoderStateMachine::StartAudioThread()
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
DECODER_LOG(PR_LOG_WARNING, "Changed state to SHUTDOWN because audio sink initialization failed"); DECODER_LOG(PR_LOG_WARNING, "Changed state to SHUTDOWN because audio sink initialization failed");
mState = DECODER_STATE_SHUTDOWN; mState = DECODER_STATE_SHUTDOWN;
mScheduler->ScheduleAndShutdown();
return rv; return rv;
} }
@ -1830,8 +1818,8 @@ MediaDecoderStateMachine::DecodeError()
// and the HTMLMediaElement, so that our pipeline can start exiting // and the HTMLMediaElement, so that our pipeline can start exiting
// cleanly during the sync dispatch below. // cleanly during the sync dispatch below.
DECODER_LOG(PR_LOG_WARNING, "Decode error, changed state to SHUTDOWN due to error"); DECODER_LOG(PR_LOG_WARNING, "Decode error, changed state to SHUTDOWN due to error");
ScheduleStateMachine();
mState = DECODER_STATE_SHUTDOWN; mState = DECODER_STATE_SHUTDOWN;
mScheduler->ScheduleAndShutdown();
mDecoder->GetReentrantMonitor().NotifyAll(); mDecoder->GetReentrantMonitor().NotifyAll();
// Dispatch the event to call DecodeError synchronously. This ensures // Dispatch the event to call DecodeError synchronously. This ensures
@ -1911,7 +1899,7 @@ nsresult MediaDecoderStateMachine::DecodeMetadata()
VideoQueue().AddPopListener(decodeTask, mDecodeTaskQueue); VideoQueue().AddPopListener(decodeTask, mDecodeTaskQueue);
} }
if (mRealTime) { if (mScheduler->IsRealTime()) {
SetStartTime(0); SetStartTime(0);
res = FinishDecodeMetadata(); res = FinishDecodeMetadata();
NS_ENSURE_SUCCESS(res, res); NS_ENSURE_SUCCESS(res, res);
@ -1940,7 +1928,7 @@ MediaDecoderStateMachine::FinishDecodeMetadata()
return NS_ERROR_FAILURE; return NS_ERROR_FAILURE;
} }
if (!mRealTime) { if (!mScheduler->IsRealTime()) {
const VideoData* v = VideoQueue().PeekFront(); const VideoData* v = VideoQueue().PeekFront();
const AudioData* a = AudioQueue().PeekFront(); const AudioData* a = AudioQueue().PeekFront();
@ -2174,6 +2162,9 @@ MediaDecoderStateMachine::SeekCompleted()
// Try to decode another frame to detect if we're at the end... // Try to decode another frame to detect if we're at the end...
DECODER_LOG(PR_LOG_DEBUG, "Seek completed, mCurrentFrameTime=%lld", mCurrentFrameTime); DECODER_LOG(PR_LOG_DEBUG, "Seek completed, mCurrentFrameTime=%lld", mCurrentFrameTime);
// Prevent changes in playback position before 'seeked' is fired for we
// expect currentTime equals seek target in 'seeked' callback.
mScheduler->FreezeScheduling();
{ {
ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC); NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC);
@ -2185,6 +2176,7 @@ MediaDecoderStateMachine::SeekCompleted()
mQuickBuffering = false; mQuickBuffering = false;
ScheduleStateMachine(); ScheduleStateMachine();
mScheduler->ThawScheduling();
} }
// Runnable to dispose of the decoder and state machine on the main thread. // Runnable to dispose of the decoder and state machine on the main thread.
@ -2292,8 +2284,6 @@ nsresult MediaDecoderStateMachine::RunStateMachine()
GetStateMachineThread()->Dispatch( GetStateMachineThread()->Dispatch(
new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL); new nsDispatchDisposeEvent(mDecoder, this), NS_DISPATCH_NORMAL);
mTimer->Cancel();
mTimer = nullptr;
return NS_OK; return NS_OK;
} }
@ -2589,7 +2579,7 @@ void MediaDecoderStateMachine::AdvanceFrame()
#endif #endif
if (VideoQueue().GetSize() > 0) { if (VideoQueue().GetSize() > 0) {
VideoData* frame = VideoQueue().PeekFront(); VideoData* frame = VideoQueue().PeekFront();
while (mRealTime || clock_time >= frame->mTime) { while (mScheduler->IsRealTime() || clock_time >= frame->mTime) {
mVideoFrameEndTime = frame->GetEndTime(); mVideoFrameEndTime = frame->GetEndTime();
currentFrame = frame; currentFrame = frame;
#ifdef PR_LOGGING #ifdef PR_LOGGING
@ -2929,24 +2919,13 @@ nsresult MediaDecoderStateMachine::CallRunStateMachine()
StopAudioThread(); StopAudioThread();
} }
MOZ_ASSERT(!mInRunningStateMachine, "State machine cycles must run in sequence!"); return RunStateMachine();
mTimeout = TimeStamp();
mInRunningStateMachine = true;
nsresult res = RunStateMachine();
mInRunningStateMachine = false;
return res;
} }
nsresult MediaDecoderStateMachine::TimeoutExpired(int aTimerId) nsresult MediaDecoderStateMachine::TimeoutExpired(void* aClosure)
{ {
ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); MediaDecoderStateMachine* p = static_cast<MediaDecoderStateMachine*>(aClosure);
NS_ASSERTION(OnStateMachineThread(), "Must be on state machine thread"); return p->CallRunStateMachine();
mTimer->Cancel();
if (mTimerId == aTimerId) {
return CallRunStateMachine();
} else {
return NS_OK;
}
} }
void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() { void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
@ -2955,75 +2934,8 @@ void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder() {
DispatchVideoDecodeTaskIfNeeded(); DispatchVideoDecodeTaskIfNeeded();
} }
class TimerEvent : public nsITimerCallback, public nsRunnable {
NS_DECL_THREADSAFE_ISUPPORTS
public:
TimerEvent(MediaDecoderStateMachine* aStateMachine, int aTimerId)
: mStateMachine(aStateMachine), mTimerId(aTimerId) {}
NS_IMETHOD Run() MOZ_OVERRIDE {
return mStateMachine->TimeoutExpired(mTimerId);
}
NS_IMETHOD Notify(nsITimer* aTimer) {
return mStateMachine->TimeoutExpired(mTimerId);
}
private:
~TimerEvent() {}
const nsRefPtr<MediaDecoderStateMachine> mStateMachine;
int mTimerId;
};
NS_IMPL_ISUPPORTS(TimerEvent, nsITimerCallback, nsIRunnable);
nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) { nsresult MediaDecoderStateMachine::ScheduleStateMachine(int64_t aUsecs) {
AssertCurrentThreadInMonitor(); return mScheduler->Schedule(aUsecs);
NS_ABORT_IF_FALSE(GetStateMachineThread(),
"Must have a state machine thread to schedule");
if (mState == DECODER_STATE_SHUTDOWN) {
return NS_ERROR_FAILURE;
}
aUsecs = std::max<int64_t>(aUsecs, 0);
TimeStamp timeout = TimeStamp::Now() + UsecsToDuration(aUsecs);
if (!mTimeout.IsNull() && timeout >= mTimeout) {
// We've already scheduled a timer set to expire at or before this time,
// or have an event dispatched to run the state machine.
return NS_OK;
}
uint32_t ms = static_cast<uint32_t>((aUsecs / USECS_PER_MS) & 0xFFFFFFFF);
if (mRealTime && ms > 40) {
ms = 40;
}
// Don't cancel the timer here for this function will be called from
// different threads.
nsresult rv = NS_ERROR_FAILURE;
nsRefPtr<TimerEvent> event = new TimerEvent(this, mTimerId+1);
if (ms == 0) {
// Dispatch a runnable to the state machine thread when delay is 0.
// It will has less latency than dispatching a runnable to the state
// machine thread which will then schedule a zero-delay timer.
rv = GetStateMachineThread()->Dispatch(event, NS_DISPATCH_NORMAL);
} else if (OnStateMachineThread()) {
rv = mTimer->InitWithCallback(event, ms, nsITimer::TYPE_ONE_SHOT);
} else {
MOZ_ASSERT(false, "non-zero delay timer should be only scheduled in state machine thread");
}
if (NS_SUCCEEDED(rv)) {
mTimeout = timeout;
++mTimerId;
} else {
NS_WARNING("Failed to schedule state machine");
}
return rv;
} }
bool MediaDecoderStateMachine::OnDecodeThread() const bool MediaDecoderStateMachine::OnDecodeThread() const
@ -3033,14 +2945,17 @@ bool MediaDecoderStateMachine::OnDecodeThread() const
bool MediaDecoderStateMachine::OnStateMachineThread() const bool MediaDecoderStateMachine::OnStateMachineThread() const
{ {
bool rv = false; return mScheduler->OnStateMachineThread();
mStateMachineThreadPool->IsOnCurrentThread(&rv);
return rv;
} }
nsIEventTarget* MediaDecoderStateMachine::GetStateMachineThread() nsIEventTarget* MediaDecoderStateMachine::GetStateMachineThread() const
{ {
return mStateMachineThreadPool->GetEventTarget(); return mScheduler->GetStateMachineThread();
}
bool MediaDecoderStateMachine::IsStateMachineScheduled() const
{
return mScheduler->IsScheduled();
} }
void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate) void MediaDecoderStateMachine::SetPlaybackRate(double aPlaybackRate)

View File

@ -100,6 +100,7 @@ class VideoSegment;
class MediaTaskQueue; class MediaTaskQueue;
class SharedThreadPool; class SharedThreadPool;
class AudioSink; class AudioSink;
class MediaDecoderStateMachineScheduler;
// GetCurrentTime is defined in winbase.h as zero argument macro forwarding to // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
// GetTickCount() and conflicts with MediaDecoderStateMachine::GetCurrentTime // GetTickCount() and conflicts with MediaDecoderStateMachine::GetCurrentTime
@ -279,7 +280,7 @@ public:
} }
// Returns the shared state machine thread. // Returns the shared state machine thread.
nsIEventTarget* GetStateMachineThread(); nsIEventTarget* GetStateMachineThread() const;
// Calls ScheduleStateMachine() after taking the decoder lock. Also // Calls ScheduleStateMachine() after taking the decoder lock. Also
// notifies the decoder thread in case it's waiting on the decoder lock. // notifies the decoder thread in case it's waiting on the decoder lock.
@ -290,8 +291,9 @@ public:
// earlier, in which case the request is discarded. // earlier, in which case the request is discarded.
nsresult ScheduleStateMachine(int64_t aUsecs = 0); nsresult ScheduleStateMachine(int64_t aUsecs = 0);
// Timer function to implement ScheduleStateMachine(aUsecs). // Callback function registered with MediaDecoderStateMachineScheduler
nsresult TimeoutExpired(int aGeneration); // to run state machine cycles.
static nsresult TimeoutExpired(void* aClosure);
// Set the media fragment end time. aEndTime is in microseconds. // Set the media fragment end time. aEndTime is in microseconds.
void SetFragmentEndTime(int64_t aEndTime); void SetFragmentEndTime(int64_t aEndTime);
@ -447,7 +449,7 @@ protected:
// Orders the Reader to stop decoding, and blocks until the Reader // Orders the Reader to stop decoding, and blocks until the Reader
// has stopped decoding and finished delivering samples, then calls // has stopped decoding and finished delivering samples, then calls
// ResetPlayback() to discard all enqueued data. // ResetPlayback() to discard all enqueued data.
void FlushDecoding(); void FlushDecoding();
// Returns the audio clock, if we have audio, or -1 if we don't. // Returns the audio clock, if we have audio, or -1 if we don't.
@ -610,10 +612,7 @@ protected:
// periodically via timer to ensure the video stays in sync. // periodically via timer to ensure the video stays in sync.
nsresult RunStateMachine(); nsresult RunStateMachine();
bool IsStateMachineScheduled() const { bool IsStateMachineScheduled() const;
AssertCurrentThreadInMonitor();
return !mTimeout.IsNull();
}
// Returns true if we're not playing and the decode thread has filled its // Returns true if we're not playing and the decode thread has filled its
// decode buffers and is waiting. We can shut the decode thread down in this // decode buffers and is waiting. We can shut the decode thread down in this
@ -650,6 +649,10 @@ protected:
// state machine, audio and main threads. // state machine, audio and main threads.
nsRefPtr<MediaDecoder> mDecoder; nsRefPtr<MediaDecoder> mDecoder;
// Used to schedule state machine cycles. This should never outlive
// the life cycle of the state machine.
const nsAutoPtr<MediaDecoderStateMachineScheduler> mScheduler;
// Time at which the last video sample was requested. If it takes too long // Time at which the last video sample was requested. If it takes too long
// before the sample arrives, we will increase the amount of audio we buffer. // before the sample arrives, we will increase the amount of audio we buffer.
// This is necessary for legacy synchronous decoders to prevent underruns. // This is necessary for legacy synchronous decoders to prevent underruns.
@ -674,19 +677,6 @@ protected:
// thread every time they're called. // thread every time they're called.
RefPtr<MediaTaskQueue> mDecodeTaskQueue; RefPtr<MediaTaskQueue> mDecodeTaskQueue;
RefPtr<SharedThreadPool> mStateMachineThreadPool;
// Timer to run the state machine cycles. Used by
// ScheduleStateMachine(). Access protected by decoder monitor.
nsCOMPtr<nsITimer> mTimer;
// Timestamp at which the next state machine cycle will run.
// Access protected by decoder monitor.
TimeStamp mTimeout;
// Used to check if there are state machine cycles are running in sequence.
DebugOnly<bool> mInRunningStateMachine;
// The time that playback started from the system clock. This is used for // The time that playback started from the system clock. This is used for
// timing the presentation of video frames when there's no audio. // timing the presentation of video frames when there's no audio.
// Accessed only via the state machine thread. Must be set via SetPlayStartTime. // Accessed only via the state machine thread. Must be set via SetPlayStartTime.
@ -911,9 +901,6 @@ protected:
// by the decoder monitor. // by the decoder monitor.
bool mDecodeThreadWaiting; bool mDecodeThreadWaiting;
// True is we are decoding a realtime stream, like a camera stream
bool mRealTime;
// True if we've dispatched a task to the decode task queue to call // True if we've dispatched a task to the decode task queue to call
// ReadMetadata on the reader. We maintain a flag to ensure that we don't // ReadMetadata on the reader. We maintain a flag to ensure that we don't
// dispatch multiple tasks to re-do the metadata loading. // dispatch multiple tasks to re-do the metadata loading.
@ -942,9 +929,6 @@ protected:
mozilla::MediaMetadataManager mMetadataManager; mozilla::MediaMetadataManager mMetadataManager;
MediaDecoderOwner::NextFrameStatus mLastFrameStatus; MediaDecoderOwner::NextFrameStatus mLastFrameStatus;
// The id of timer tasks, used to ignore tasks that are scheduled previously.
int mTimerId;
}; };
} // namespace mozilla; } // namespace mozilla;

View File

@ -0,0 +1,233 @@
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "MediaDecoderStateMachineScheduler.h"
#include "SharedThreadPool.h"
#include "mozilla/Preferences.h"
#include "mozilla/ReentrantMonitor.h"
#include "nsITimer.h"
#include "nsComponentManagerUtils.h"
#include "VideoUtils.h"
namespace {
class TimerEvent : public nsITimerCallback, public nsRunnable {
typedef mozilla::MediaDecoderStateMachineScheduler Scheduler;
NS_DECL_THREADSAFE_ISUPPORTS
public:
TimerEvent(Scheduler* aScheduler, int aTimerId)
: mScheduler(aScheduler), mTimerId(aTimerId) {}
NS_IMETHOD Run() MOZ_OVERRIDE {
return mScheduler->TimeoutExpired(mTimerId);
}
NS_IMETHOD Notify(nsITimer* aTimer) MOZ_OVERRIDE {
return mScheduler->TimeoutExpired(mTimerId);
}
private:
~TimerEvent() {}
Scheduler* const mScheduler;
const int mTimerId;
};
NS_IMPL_ISUPPORTS(TimerEvent, nsITimerCallback, nsIRunnable);
} // anonymous namespace
static already_AddRefed<nsIEventTarget>
CreateStateMachineThread()
{
using mozilla::SharedThreadPool;
using mozilla::RefPtr;
RefPtr<SharedThreadPool> threadPool(
SharedThreadPool::Get(NS_LITERAL_CSTRING("Media State Machine"), 1));
nsCOMPtr<nsIEventTarget> rv = threadPool.get();
return rv.forget();
}
namespace mozilla {
MediaDecoderStateMachineScheduler::MediaDecoderStateMachineScheduler(
ReentrantMonitor& aMonitor,
nsresult (*aTimeoutCallback)(void*),
void* aClosure, bool aRealTime)
: mTimeoutCallback(aTimeoutCallback)
, mClosure(aClosure)
// Only enable realtime mode when "media.realtime_decoder.enabled" is true.
, mRealTime(aRealTime &&
Preferences::GetBool("media.realtime_decoder.enabled", false))
, mMonitor(aMonitor)
, mEventTarget(CreateStateMachineThread())
, mTimer(do_CreateInstance("@mozilla.org/timer;1"))
, mTimerId(0)
, mState(SCHEDULER_STATE_NONE)
, mInRunningStateMachine(false)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_CTOR(MediaDecoderStateMachineScheduler);
}
MediaDecoderStateMachineScheduler::~MediaDecoderStateMachineScheduler()
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_DTOR(MediaDecoderStateMachineScheduler);
}
nsresult
MediaDecoderStateMachineScheduler::Init()
{
MOZ_ASSERT(NS_IsMainThread());
NS_ENSURE_TRUE(mEventTarget, NS_ERROR_FAILURE);
nsresult rv = mTimer->SetTarget(mEventTarget);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
MediaDecoderStateMachineScheduler::Schedule(int64_t aUsecs)
{
mMonitor.AssertCurrentThreadIn();
switch(mState) {
case SCHEDULER_STATE_SHUTDOWN:
return NS_ERROR_FAILURE;
case SCHEDULER_STATE_FROZEN:
mState = SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK;
case SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK:
return NS_OK;
case SCHEDULER_STATE_NONE:
break;
}
aUsecs = std::max<int64_t>(aUsecs, 0);
TimeStamp timeout = TimeStamp::Now() +
TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
if (!mTimeout.IsNull() && timeout >= mTimeout) {
// We've already scheduled a timer set to expire at or before this time,
// or have an event dispatched to run the state machine.
return NS_OK;
}
uint32_t ms = static_cast<uint32_t>((aUsecs / USECS_PER_MS) & 0xFFFFFFFF);
if (IsRealTime() && ms > 40) {
ms = 40;
}
// Don't cancel the timer here for this function will be called from
// different threads.
nsresult rv = NS_ERROR_FAILURE;
nsRefPtr<TimerEvent> event = new TimerEvent(this, mTimerId+1);
if (ms == 0) {
// Dispatch a runnable to the state machine thread when delay is 0.
// It will has less latency than dispatching a runnable to the state
// machine thread which will then schedule a zero-delay timer.
rv = mEventTarget->Dispatch(event, NS_DISPATCH_NORMAL);
} else if (OnStateMachineThread()) {
rv = mTimer->InitWithCallback(event, ms, nsITimer::TYPE_ONE_SHOT);
} else {
MOZ_ASSERT(false, "non-zero delay timer should be only "
"scheduled in state machine thread");
}
if (NS_SUCCEEDED(rv)) {
mTimeout = timeout;
++mTimerId;
} else {
NS_WARNING("Failed to schedule state machine");
}
return rv;
}
nsresult
MediaDecoderStateMachineScheduler::TimeoutExpired(int aTimerId)
{
ReentrantMonitorAutoEnter mon(mMonitor);
MOZ_ASSERT(OnStateMachineThread());
MOZ_ASSERT(!mInRunningStateMachine,
"State machine cycles must run in sequence!");
mInRunningStateMachine = true;
// Only run state machine cycles when id matches.
nsresult rv = NS_OK;
if (mTimerId == aTimerId) {
ResetTimer();
rv = mTimeoutCallback(mClosure);
}
mInRunningStateMachine = false;
return rv;
}
void
MediaDecoderStateMachineScheduler::ScheduleAndShutdown()
{
mMonitor.AssertCurrentThreadIn();
if (IsFrozen()) {
ThawScheduling();
}
// Schedule next cycle to handle SHUTDOWN in state machine thread.
Schedule();
// This must be set after calling Schedule()
// which does nothing in shutdown state.
mState = SCHEDULER_STATE_SHUTDOWN;
}
bool
MediaDecoderStateMachineScheduler::OnStateMachineThread() const
{
bool rv = false;
mEventTarget->IsOnCurrentThread(&rv);
return rv;
}
bool
MediaDecoderStateMachineScheduler::IsScheduled() const
{
mMonitor.AssertCurrentThreadIn();
return !mTimeout.IsNull();
}
void
MediaDecoderStateMachineScheduler::ResetTimer()
{
mMonitor.AssertCurrentThreadIn();
mTimer->Cancel();
mTimeout = TimeStamp();
}
void MediaDecoderStateMachineScheduler::FreezeScheduling()
{
mMonitor.AssertCurrentThreadIn();
if (mState == SCHEDULER_STATE_SHUTDOWN) {
return;
}
MOZ_ASSERT(mState == SCHEDULER_STATE_NONE);
mState = !IsScheduled() ? SCHEDULER_STATE_FROZEN :
SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK;
// Nullify pending timer task if any.
++mTimerId;
mTimeout = TimeStamp();
}
void MediaDecoderStateMachineScheduler::ThawScheduling()
{
mMonitor.AssertCurrentThreadIn();
if (mState == SCHEDULER_STATE_SHUTDOWN) {
return;
}
// We should be in frozen state and no pending timer task.
MOZ_ASSERT(IsFrozen() && !IsScheduled());
bool pendingTask = mState == SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK;
mState = SCHEDULER_STATE_NONE;
if (pendingTask) {
Schedule();
}
}
} // namespace mozilla

View File

@ -0,0 +1,86 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef MediaDecoderStateMachineScheduler_h__
#define MediaDecoderStateMachineScheduler_h__
#include "nsCOMPtr.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/DebugOnly.h"
class nsITimer;
class nsIEventTarget;
namespace mozilla {
class ReentrantMonitor;
class MediaDecoderStateMachineScheduler {
enum State {
SCHEDULER_STATE_NONE,
SCHEDULER_STATE_FROZEN,
SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK,
SCHEDULER_STATE_SHUTDOWN
};
public:
MediaDecoderStateMachineScheduler(ReentrantMonitor& aMonitor,
nsresult (*aTimeoutCallback)(void*),
void* aClosure, bool aRealTime);
~MediaDecoderStateMachineScheduler();
nsresult Init();
nsresult Schedule(int64_t aUsecs = 0);
void ScheduleAndShutdown();
nsresult TimeoutExpired(int aTimerId);
void FreezeScheduling();
void ThawScheduling();
bool OnStateMachineThread() const;
bool IsScheduled() const;
bool IsRealTime() const {
return mRealTime;
}
nsIEventTarget* GetStateMachineThread() const {
return mEventTarget;
}
bool IsFrozen() const {
return mState == SCHEDULER_STATE_FROZEN ||
mState == SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK;
}
private:
void ResetTimer();
// Callback function provided by MediaDecoderStateMachine to run
// state machine cycles.
nsresult (*const mTimeoutCallback)(void*);
// Since StateMachineScheduler will never outlive the state machine,
// it is safe to keep a raw pointer only to avoid reference cycles.
void* const mClosure;
// True is we are decoding a realtime stream, like a camera stream
const bool mRealTime;
// Monitor of the decoder
ReentrantMonitor& mMonitor;
// State machine thread
const nsCOMPtr<nsIEventTarget> mEventTarget;
// Timer to schedule callbacks to run the state machine cycles.
nsCOMPtr<nsITimer> mTimer;
// Timestamp at which the next state machine cycle will run.
TimeStamp mTimeout;
// The id of timer tasks, timer callback will only run if id matches.
int mTimerId;
// No more state machine cycles in shutdown state.
State mState;
// Used to check if state machine cycles are running in sequence.
DebugOnly<bool> mInRunningStateMachine;
};
} // namespace mozilla
#endif // MediaDecoderStateMachineScheduler_h__

View File

@ -147,6 +147,10 @@ public:
mEnd = 0; mEnd = 0;
} }
bool Contains(const MediaByteRange& aByteRange) const {
return aByteRange.mStart >= mStart && aByteRange.mEnd <= mEnd;
}
int64_t mStart, mEnd; int64_t mStart, mEnd;
}; };

View File

@ -13,6 +13,7 @@
#include "Layers.h" #include "Layers.h"
#include "SharedThreadPool.h" #include "SharedThreadPool.h"
#include "mozilla/Preferences.h" #include "mozilla/Preferences.h"
#include "mozilla/dom/TimeRanges.h"
using mozilla::layers::Image; using mozilla::layers::Image;
using mozilla::layers::LayerManager; using mozilla::layers::LayerManager;
@ -553,4 +554,23 @@ MP4Reader::Seek(int64_t aTime,
return NS_OK; return NS_OK;
} }
nsresult
MP4Reader::GetBuffered(dom::TimeRanges* aBuffered, int64_t aStartTime)
{
nsTArray<MediaByteRange> ranges;
if (NS_FAILED(mDecoder->GetResource()->GetCachedRanges(ranges))) {
return NS_ERROR_FAILURE;
}
nsTArray<Interval<Microseconds>> timeRanges;
mDemuxer->ConvertByteRangesToTime(ranges, &timeRanges);
for (size_t i = 0; i < timeRanges.Length(); i++) {
aBuffered->Add((timeRanges[i].start - aStartTime) / 1000000.0,
(timeRanges[i].end - aStartTime) / 1000000.0);
}
return NS_OK;
}
} // namespace mozilla } // namespace mozilla

View File

@ -52,6 +52,9 @@ public:
virtual bool IsMediaSeekable() MOZ_OVERRIDE; virtual bool IsMediaSeekable() MOZ_OVERRIDE;
virtual nsresult GetBuffered(dom::TimeRanges* aBuffered,
int64_t aStartTime) MOZ_OVERRIDE;
private: private:
// Destroys all decoder resources. // Destroys all decoder resources.

View File

@ -71,6 +71,7 @@ FFmpegAACDecoder<LIBAV_VER>::DecodePacket(MP4Sample* aSample)
AVPacket packet; AVPacket packet;
av_init_packet(&packet); av_init_packet(&packet);
aSample->Pad(FF_INPUT_BUFFER_PADDING_SIZE);
packet.data = aSample->data; packet.data = aSample->data;
packet.size = aSample->size; packet.size = aSample->size;
packet.pos = aSample->byte_offset; packet.pos = aSample->byte_offset;

View File

@ -85,11 +85,14 @@ FFmpegDataDecoder<LIBAV_VER>::Init()
mCodecContext.get_format = ChoosePixelFormat; mCodecContext.get_format = ChoosePixelFormat;
mCodecContext.thread_count = PR_GetNumberOfProcessors(); mCodecContext.thread_count = PR_GetNumberOfProcessors();
mCodecContext.thread_type = FF_THREAD_FRAME; mCodecContext.thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
mCodecContext.thread_safe_callbacks = false; mCodecContext.thread_safe_callbacks = false;
mCodecContext.extradata = mExtraData.begin();
mCodecContext.extradata_size = mExtraData.length(); mCodecContext.extradata_size = mExtraData.length();
for (int i = 0; i < FF_INPUT_BUFFER_PADDING_SIZE; i++) {
mExtraData.append(0);
}
mCodecContext.extradata = mExtraData.begin();
AVDictionary* opts = nullptr; AVDictionary* opts = nullptr;
if (avcodec_open2(&mCodecContext, codec, &opts) < 0) { if (avcodec_open2(&mCodecContext, codec, &opts) < 0) {

View File

@ -53,6 +53,7 @@ FFmpegH264Decoder<LIBAV_VER>::DecodeFrame(mp4_demuxer::MP4Sample* aSample)
AVPacket packet; AVPacket packet;
av_init_packet(&packet); av_init_packet(&packet);
aSample->Pad(FF_INPUT_BUFFER_PADDING_SIZE);
packet.data = aSample->data; packet.data = aSample->data;
packet.size = aSample->size; packet.size = aSample->size;
packet.pts = aSample->composition_timestamp; packet.pts = aSample->composition_timestamp;
@ -137,7 +138,11 @@ FFmpegH264Decoder<LIBAV_VER>::AllocateBufferCb(AVCodecContext* aCodecContext,
FFmpegH264Decoder<LIBAV_VER>::ReleaseBufferCb(AVCodecContext* aCodecContext, FFmpegH264Decoder<LIBAV_VER>::ReleaseBufferCb(AVCodecContext* aCodecContext,
AVFrame* aFrame) AVFrame* aFrame)
{ {
reinterpret_cast<Image*>(aFrame->opaque)->Release(); Image* image = reinterpret_cast<Image*>(aFrame->opaque);
avcodec_default_release_buffer(aCodecContext, aFrame);
if (image) {
image->Release();
}
} }
int int

View File

@ -7,9 +7,24 @@
#define GMPMessageUtils_h_ #define GMPMessageUtils_h_
#include "gmp-video-codec.h" #include "gmp-video-codec.h"
#include "gmp-video-frame-encoded.h"
namespace IPC { namespace IPC {
template <>
struct ParamTraits<GMPErr>
: public ContiguousEnumSerializer<GMPErr,
GMPNoErr,
GMPLastErr>
{};
template <>
struct ParamTraits<GMPVideoFrameType>
: public ContiguousEnumSerializer<GMPVideoFrameType,
kGMPKeyFrame,
kGMPSkipFrame>
{};
template <> template <>
struct ParamTraits<GMPVideoCodecComplexity> struct ParamTraits<GMPVideoCodecComplexity>
: public ContiguousEnumSerializer<GMPVideoCodecComplexity, : public ContiguousEnumSerializer<GMPVideoCodecComplexity,
@ -39,57 +54,11 @@ struct ParamTraits<GMPVideoCodecMode>
{}; {};
template <> template <>
struct ParamTraits<GMPVideoCodecVP8> struct ParamTraits<GMPBufferType>
{ : public ContiguousEnumSerializer<GMPBufferType,
typedef GMPVideoCodecVP8 paramType; GMP_BufferSingle,
GMP_BufferInvalid>
static void Write(Message* aMsg, const paramType& aParam) {};
{
WriteParam(aMsg, aParam.mPictureLossIndicationOn);
WriteParam(aMsg, aParam.mFeedbackModeOn);
WriteParam(aMsg, aParam.mComplexity);
WriteParam(aMsg, aParam.mResilience);
WriteParam(aMsg, aParam.mNumberOfTemporalLayers);
WriteParam(aMsg, aParam.mDenoisingOn);
WriteParam(aMsg, aParam.mErrorConcealmentOn);
WriteParam(aMsg, aParam.mAutomaticResizeOn);
WriteParam(aMsg, aParam.mFrameDroppingOn);
WriteParam(aMsg, aParam.mKeyFrameInterval);
}
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{
if (ReadParam(aMsg, aIter, &(aResult->mPictureLossIndicationOn)) &&
ReadParam(aMsg, aIter, &(aResult->mFeedbackModeOn)) &&
ReadParam(aMsg, aIter, &(aResult->mComplexity)) &&
ReadParam(aMsg, aIter, &(aResult->mResilience)) &&
ReadParam(aMsg, aIter, &(aResult->mNumberOfTemporalLayers)) &&
ReadParam(aMsg, aIter, &(aResult->mDenoisingOn)) &&
ReadParam(aMsg, aIter, &(aResult->mErrorConcealmentOn)) &&
ReadParam(aMsg, aIter, &(aResult->mAutomaticResizeOn)) &&
ReadParam(aMsg, aIter, &(aResult->mFrameDroppingOn)) &&
ReadParam(aMsg, aIter, &(aResult->mKeyFrameInterval))) {
return true;
}
return false;
}
static void Log(const paramType& aParam, std::wstring* aLog)
{
aLog->append(StringPrintf(L"[%d, %d, %d, %d, %u, %d, %d, %d, %d, %d]",
aParam.mPictureLossIndicationOn,
aParam.mFeedbackModeOn,
aParam.mComplexity,
aParam.mResilience,
aParam.mNumberOfTemporalLayers,
aParam.mDenoisingOn,
aParam.mErrorConcealmentOn,
aParam.mAutomaticResizeOn,
aParam.mFrameDroppingOn,
aParam.mKeyFrameInterval));
}
};
template <> template <>
struct ParamTraits<GMPSimulcastStream> struct ParamTraits<GMPSimulcastStream>
@ -136,6 +105,7 @@ struct ParamTraits<GMPVideoCodec>
static void Write(Message* aMsg, const paramType& aParam) static void Write(Message* aMsg, const paramType& aParam)
{ {
WriteParam(aMsg, aParam.mGMPApiVersion);
WriteParam(aMsg, aParam.mCodecType); WriteParam(aMsg, aParam.mCodecType);
WriteParam(aMsg, nsAutoCString(aParam.mPLName)); WriteParam(aMsg, nsAutoCString(aParam.mPLName));
WriteParam(aMsg, aParam.mPLType); WriteParam(aMsg, aParam.mPLType);
@ -145,11 +115,8 @@ struct ParamTraits<GMPVideoCodec>
WriteParam(aMsg, aParam.mMaxBitrate); WriteParam(aMsg, aParam.mMaxBitrate);
WriteParam(aMsg, aParam.mMinBitrate); WriteParam(aMsg, aParam.mMinBitrate);
WriteParam(aMsg, aParam.mMaxFramerate); WriteParam(aMsg, aParam.mMaxFramerate);
if (aParam.mCodecType == kGMPVideoCodecVP8) { WriteParam(aMsg, aParam.mFrameDroppingOn);
WriteParam(aMsg, aParam.mCodecSpecific.mVP8); WriteParam(aMsg, aParam.mKeyFrameInterval);
} else {
MOZ_ASSERT(false, "Serializing unknown codec type!");
}
WriteParam(aMsg, aParam.mQPMax); WriteParam(aMsg, aParam.mQPMax);
WriteParam(aMsg, aParam.mNumberOfSimulcastStreams); WriteParam(aMsg, aParam.mNumberOfSimulcastStreams);
for (uint32_t i = 0; i < aParam.mNumberOfSimulcastStreams; i++) { for (uint32_t i = 0; i < aParam.mNumberOfSimulcastStreams; i++) {
@ -160,6 +127,11 @@ struct ParamTraits<GMPVideoCodec>
static bool Read(const Message* aMsg, void** aIter, paramType* aResult) static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{ {
// NOTE: make sure this matches any versions supported
if (!ReadParam(aMsg, aIter, &(aResult->mGMPApiVersion)) ||
aResult->mGMPApiVersion != kGMPVersion33) {
return false;
}
if (!ReadParam(aMsg, aIter, &(aResult->mCodecType))) { if (!ReadParam(aMsg, aIter, &(aResult->mCodecType))) {
return false; return false;
} }
@ -178,16 +150,9 @@ struct ParamTraits<GMPVideoCodec>
!ReadParam(aMsg, aIter, &(aResult->mStartBitrate)) || !ReadParam(aMsg, aIter, &(aResult->mStartBitrate)) ||
!ReadParam(aMsg, aIter, &(aResult->mMaxBitrate)) || !ReadParam(aMsg, aIter, &(aResult->mMaxBitrate)) ||
!ReadParam(aMsg, aIter, &(aResult->mMinBitrate)) || !ReadParam(aMsg, aIter, &(aResult->mMinBitrate)) ||
!ReadParam(aMsg, aIter, &(aResult->mMaxFramerate))) { !ReadParam(aMsg, aIter, &(aResult->mMaxFramerate)) ||
return false; !ReadParam(aMsg, aIter, &(aResult->mFrameDroppingOn)) ||
} !ReadParam(aMsg, aIter, &(aResult->mKeyFrameInterval))) {
if (aResult->mCodecType == kGMPVideoCodecVP8) {
if (!ReadParam(aMsg, aIter, &(aResult->mCodecSpecific.mVP8))) {
return false;
}
} else {
MOZ_ASSERT(false, "De-serializing unknown codec type!");
return false; return false;
} }
@ -226,104 +191,6 @@ struct ParamTraits<GMPVideoCodec>
} }
}; };
template <>
struct ParamTraits<GMPCodecSpecificInfoVP8>
{
typedef GMPCodecSpecificInfoVP8 paramType;
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, aParam.mHasReceivedSLI);
WriteParam(aMsg, aParam.mPictureIdSLI);
WriteParam(aMsg, aParam.mHasReceivedRPSI);
WriteParam(aMsg, aParam.mPictureIdRPSI);
WriteParam(aMsg, aParam.mPictureId);
WriteParam(aMsg, aParam.mNonReference);
WriteParam(aMsg, aParam.mSimulcastIdx);
WriteParam(aMsg, aParam.mTemporalIdx);
WriteParam(aMsg, aParam.mLayerSync);
WriteParam(aMsg, aParam.mTL0PicIdx);
WriteParam(aMsg, aParam.mKeyIdx);
}
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{
if (ReadParam(aMsg, aIter, &(aResult->mHasReceivedSLI)) &&
ReadParam(aMsg, aIter, &(aResult->mPictureIdSLI)) &&
ReadParam(aMsg, aIter, &(aResult->mHasReceivedRPSI)) &&
ReadParam(aMsg, aIter, &(aResult->mPictureIdRPSI)) &&
ReadParam(aMsg, aIter, &(aResult->mPictureId)) &&
ReadParam(aMsg, aIter, &(aResult->mNonReference)) &&
ReadParam(aMsg, aIter, &(aResult->mSimulcastIdx)) &&
ReadParam(aMsg, aIter, &(aResult->mTemporalIdx)) &&
ReadParam(aMsg, aIter, &(aResult->mLayerSync)) &&
ReadParam(aMsg, aIter, &(aResult->mTL0PicIdx)) &&
ReadParam(aMsg, aIter, &(aResult->mKeyIdx))) {
return true;
}
return false;
}
static void Log(const paramType& aParam, std::wstring* aLog)
{
aLog->append(StringPrintf(L"[%d, %u, %d, %u, %d, %d, %u, %u, %d, %d, %d]",
aParam.mHasReceivedSLI,
aParam.mPictureIdSLI,
aParam.mHasReceivedRPSI,
aParam.mPictureIdRPSI,
aParam.mPictureId,
aParam.mNonReference,
aParam.mSimulcastIdx,
aParam.mTemporalIdx,
aParam.mLayerSync,
aParam.mTL0PicIdx,
aParam.mKeyIdx));
}
};
template <>
struct ParamTraits<GMPCodecSpecificInfo>
{
typedef GMPCodecSpecificInfo paramType;
static void Write(Message* aMsg, const paramType& aParam)
{
WriteParam(aMsg, aParam.mCodecType);
if (aParam.mCodecType == kGMPVideoCodecVP8) {
WriteParam(aMsg, aParam.mCodecSpecific.mVP8);
} else {
MOZ_ASSERT(false, "Serializing unknown codec type!");
}
}
static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
{
if (!ReadParam(aMsg, aIter, &(aResult->mCodecType))) {
return false;
}
if (aResult->mCodecType == kGMPVideoCodecVP8) {
if (!ReadParam(aMsg, aIter, &(aResult->mCodecSpecific.mVP8))) {
return false;
}
} else {
MOZ_ASSERT(false, "De-serializing unknown codec type!");
return false;
}
return true;
}
static void Log(const paramType& aParam, std::wstring* aLog)
{
const char* codecName = nullptr;
if (aParam.mCodecType == kGMPVideoCodecVP8) {
codecName = "VP8";
}
aLog->append(StringPrintf(L"[%s]", codecName));
}
};
} // namespace IPC } // namespace IPC
#endif // GMPMessageUtils_h_ #endif // GMPMessageUtils_h_

View File

@ -16,6 +16,7 @@
#include "nsString.h" #include "nsString.h"
#include "nsTArray.h" #include "nsTArray.h"
#include "nsIFile.h" #include "nsIFile.h"
#include "ThreadSafeRefcountingWithMainThreadDestruction.h"
class nsILineInputStream; class nsILineInputStream;
class nsIThread; class nsIThread;
@ -38,7 +39,7 @@ enum GMPState {
class GMPParent MOZ_FINAL : public PGMPParent class GMPParent MOZ_FINAL : public PGMPParent
{ {
public: public:
NS_INLINE_DECL_REFCOUNTING(GMPParent) NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_MAIN_THREAD_DESTRUCTION(GMPParent)
GMPParent(); GMPParent();

View File

@ -27,6 +27,7 @@ public:
void Run() void Run()
{ {
mTask->Run(); mTask->Run();
mTask->Destroy();
mTask = nullptr; mTask = nullptr;
} }
@ -35,7 +36,7 @@ private:
{ {
} }
nsAutoPtr<GMPTask> mTask; GMPTask* mTask;
}; };
class SyncRunnable MOZ_FINAL class SyncRunnable MOZ_FINAL
@ -71,6 +72,7 @@ public:
void Run() void Run()
{ {
mTask->Run(); mTask->Run();
mTask->Destroy();
mTask = nullptr; mTask = nullptr;
MonitorAutoLock lock(mMonitor); MonitorAutoLock lock(mMonitor);
mDone = true; mDone = true;
@ -83,7 +85,7 @@ private:
} }
bool mDone; bool mDone;
nsAutoPtr<GMPTask> mTask; GMPTask* mTask;
MessageLoop* mMessageLoop; MessageLoop* mMessageLoop;
Monitor mMonitor; Monitor mMonitor;
}; };
@ -108,7 +110,6 @@ RunOnMainThread(GMPTask* aTask)
} }
nsRefPtr<Runnable> r = new Runnable(aTask); nsRefPtr<Runnable> r = new Runnable(aTask);
sMainLoop->PostTask(FROM_HERE, NewRunnableMethod(r.get(), &Runnable::Run)); sMainLoop->PostTask(FROM_HERE, NewRunnableMethod(r.get(), &Runnable::Run));
return GMPNoErr; return GMPNoErr;
@ -152,6 +153,9 @@ InitPlatformAPI(GMPPlatformAPI& aPlatformAPI)
aPlatformAPI.runonmainthread = &RunOnMainThread; aPlatformAPI.runonmainthread = &RunOnMainThread;
aPlatformAPI.syncrunonmainthread = &SyncRunOnMainThread; aPlatformAPI.syncrunonmainthread = &SyncRunOnMainThread;
aPlatformAPI.createmutex = &CreateMutex; aPlatformAPI.createmutex = &CreateMutex;
aPlatformAPI.createrecord = nullptr;
aPlatformAPI.settimer = nullptr;
aPlatformAPI.getcurrenttime = nullptr;
} }
GMPThreadImpl::GMPThreadImpl() GMPThreadImpl::GMPThreadImpl()

View File

@ -13,6 +13,8 @@
namespace mozilla { namespace mozilla {
namespace gmp { namespace gmp {
class GMPChild;
void InitPlatformAPI(GMPPlatformAPI& aPlatformAPI); void InitPlatformAPI(GMPPlatformAPI& aPlatformAPI);
class GMPThreadImpl : public GMPThread class GMPThreadImpl : public GMPThread

View File

@ -183,7 +183,7 @@ NS_IMETHODIMP
GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags, GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
const nsAString& aOrigin, const nsAString& aOrigin,
GMPVideoHost** aOutVideoHost, GMPVideoHost** aOutVideoHost,
GMPVideoDecoder** aGMPVD) GMPVideoDecoderProxy** aGMPVD)
{ {
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0); NS_ENSURE_ARG(aTags && aTags->Length() > 0);
@ -217,7 +217,7 @@ NS_IMETHODIMP
GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags, GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
const nsAString& aOrigin, const nsAString& aOrigin,
GMPVideoHost** aOutVideoHost, GMPVideoHost** aOutVideoHost,
GMPVideoEncoder** aGMPVE) GMPVideoEncoderProxy** aGMPVE)
{ {
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread); MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0); NS_ENSURE_ARG(aTags && aTags->Length() > 0);
@ -364,6 +364,19 @@ GeckoMediaPluginService::SelectPluginForAPI(const nsAString& aOrigin,
return nullptr; return nullptr;
} }
class CreateGMPParentTask : public nsRunnable {
public:
NS_IMETHOD Run() {
MOZ_ASSERT(NS_IsMainThread());
mParent = new GMPParent();
return NS_OK;
}
already_AddRefed<GMPParent> GetParent() {
return mParent.forget();
}
private:
nsRefPtr<GMPParent> mParent;
};
void void
GeckoMediaPluginService::AddOnGMPThread(const nsAString& aDirectory) GeckoMediaPluginService::AddOnGMPThread(const nsAString& aDirectory)
@ -376,9 +389,16 @@ GeckoMediaPluginService::AddOnGMPThread(const nsAString& aDirectory)
return; return;
} }
nsRefPtr<GMPParent> gmp = new GMPParent(); // The GMPParent inherits from IToplevelProtocol, which must be created
// on the main thread to be threadsafe. See Bug 1035653.
nsRefPtr<CreateGMPParentTask> task(new CreateGMPParentTask());
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
MOZ_ASSERT(mainThread);
mozilla::SyncRunnable::DispatchToThread(mainThread, task);
nsRefPtr<GMPParent> gmp = task->GetParent();
rv = gmp->Init(directory); rv = gmp->Init(directory);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
NS_WARNING("Can't Create GMPParent");
return; return;
} }

View File

@ -6,6 +6,7 @@
#ifndef GMPService_h_ #ifndef GMPService_h_
#define GMPService_h_ #define GMPService_h_
#include "nsString.h"
#include "mozIGeckoMediaPluginService.h" #include "mozIGeckoMediaPluginService.h"
#include "nsIObserver.h" #include "nsIObserver.h"
#include "nsTArray.h" #include "nsTArray.h"

View File

@ -0,0 +1,116 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "GMPSharedMemManager.h"
#include "GMPMessageUtils.h"
#include "mozilla/ipc/SharedMemory.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/ClearOnShutdown.h"
namespace mozilla {
namespace gmp {
// Really one set of pools on each side of the plugin API.
// YUV buffers go from Encoder parent to child; pool there, and then return
// with Decoded() frames to the Decoder parent and goes into the parent pool.
// Compressed (encoded) data goes from the Decoder parent to the child;
// pool there, and then return with Encoded() frames and goes into the parent
// pool.
static StaticAutoPtr<nsTArray<ipc::Shmem>> sGmpFreelist[GMPSharedMemManager::kGMPNumTypes];
static uint32_t sGMPShmemManagerCount = 0;
GMPSharedMemManager::GMPSharedMemManager()
{
if (!sGMPShmemManagerCount) {
for (uint32_t i = 0; i < GMPSharedMemManager::kGMPNumTypes; i++) {
sGmpFreelist[i] = new nsTArray<ipc::Shmem>();
}
}
sGMPShmemManagerCount++;
}
GMPSharedMemManager::~GMPSharedMemManager()
{
MOZ_ASSERT(sGMPShmemManagerCount > 0);
sGMPShmemManagerCount--;
if (!sGMPShmemManagerCount) {
for (uint32_t i = 0; i < GMPSharedMemManager::kGMPNumTypes; i++) {
sGmpFreelist[i] = nullptr;
}
}
}
static nsTArray<ipc::Shmem>&
GetGmpFreelist(GMPSharedMemManager::GMPMemoryClasses aTypes)
{
return *(sGmpFreelist[aTypes]);
}
static uint32_t sGmpAllocated[GMPSharedMemManager::kGMPNumTypes]; // 0's
bool
GMPSharedMemManager::MgrAllocShmem(GMPMemoryClasses aClass, size_t aSize,
ipc::Shmem::SharedMemory::SharedMemoryType aType,
ipc::Shmem* aMem)
{
CheckThread();
// first look to see if we have a free buffer large enough
for (uint32_t i = 0; i < GetGmpFreelist(aClass).Length(); i++) {
MOZ_ASSERT(GetGmpFreelist(aClass)[i].IsWritable());
if (aSize <= GetGmpFreelist(aClass)[i].Size<uint8_t>()) {
*aMem = GetGmpFreelist(aClass)[i];
GetGmpFreelist(aClass).RemoveElementAt(i);
return true;
}
}
// Didn't find a buffer free with enough space; allocate one
size_t pagesize = ipc::SharedMemory::SystemPageSize();
aSize = (aSize + (pagesize-1)) & ~(pagesize-1); // round up to page size
bool retval = Alloc(aSize, aType, aMem);
if (retval) {
sGmpAllocated[aClass]++;
}
return retval;
}
bool
GMPSharedMemManager::MgrDeallocShmem(GMPMemoryClasses aClass, ipc::Shmem& aMem)
{
CheckThread();
size_t size = aMem.Size<uint8_t>();
size_t total = 0;
// XXX This works; there are better pool algorithms. We need to avoid
// "falling off a cliff" with too low a number
if (GetGmpFreelist(aClass).Length() > 10) {
Dealloc(GetGmpFreelist(aClass)[0]);
GetGmpFreelist(aClass).RemoveElementAt(0);
// The allocation numbers will be fubar on the Child!
sGmpAllocated[aClass]--;
}
for (uint32_t i = 0; i < GetGmpFreelist(aClass).Length(); i++) {
MOZ_ASSERT(GetGmpFreelist(aClass)[i].IsWritable());
total += GetGmpFreelist(aClass)[i].Size<uint8_t>();
if (size < GetGmpFreelist(aClass)[i].Size<uint8_t>()) {
GetGmpFreelist(aClass).InsertElementAt(i, aMem);
return true;
}
}
GetGmpFreelist(aClass).AppendElement(aMem);
return true;
}
uint32_t
GMPSharedMemManager::NumInUse(GMPMemoryClasses aClass)
{
return sGmpAllocated[aClass] - GetGmpFreelist(aClass).Length();
}
}
}

View File

@ -7,6 +7,7 @@
#define GMPSharedMemManager_h_ #define GMPSharedMemManager_h_
#include "mozilla/ipc/Shmem.h" #include "mozilla/ipc/Shmem.h"
#include "nsTArray.h"
namespace mozilla { namespace mozilla {
namespace gmp { namespace gmp {
@ -14,10 +15,37 @@ namespace gmp {
class GMPSharedMemManager class GMPSharedMemManager
{ {
public: public:
virtual bool MgrAllocShmem(size_t aSize, typedef enum {
kGMPFrameData = 0,
kGMPEncodedData,
kGMPNumTypes
} GMPMemoryClasses;
// This is a heuristic - max of 10 free in the Child pool, plus those
// in-use for the encoder and decoder at the given moment and not yet
// returned to the parent pool (which is not included). If more than
// this are needed, we presume the client has either crashed or hung
// (perhaps temporarily).
static const uint32_t kGMPBufLimit = 20;
GMPSharedMemManager();
virtual ~GMPSharedMemManager();
virtual bool MgrAllocShmem(GMPMemoryClasses aClass, size_t aSize,
ipc::Shmem::SharedMemory::SharedMemoryType aType, ipc::Shmem::SharedMemory::SharedMemoryType aType,
ipc::Shmem* aMem) = 0; ipc::Shmem* aMem);
virtual bool MgrDeallocShmem(ipc::Shmem& aMem) = 0; virtual bool MgrDeallocShmem(GMPMemoryClasses aClass, ipc::Shmem& aMem);
// So we can know if data is "piling up" for the plugin - I.e. it's hung or crashed
virtual uint32_t NumInUse(GMPMemoryClasses aClass);
// Parent and child impls will differ here
virtual void CheckThread() = 0;
// These have to be implemented using the AllocShmem/etc provided by the IPDL-generated interfaces,
// so have the Parent/Child implement them.
virtual bool Alloc(size_t aSize, ipc::Shmem::SharedMemory::SharedMemoryType aType, ipc::Shmem* aMem) = 0;
virtual void Dealloc(ipc::Shmem& aMem) = 0;
}; };
} // namespace gmp } // namespace gmp

View File

@ -3,17 +3,20 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
using GMPBufferType from "gmp-video-codec.h";
namespace mozilla { namespace mozilla {
namespace gmp { namespace gmp {
struct GMPVideoEncodedFrameData struct GMPVideoEncodedFrameData
{ {
int64_t mCaptureTime_ms;
uint32_t mEncodedWidth; uint32_t mEncodedWidth;
uint32_t mEncodedHeight; uint32_t mEncodedHeight;
uint32_t mTimeStamp; uint64_t mTimestamp; // microseconds
uint64_t mDuration; // microseconds
uint32_t mFrameType; uint32_t mFrameType;
uint32_t mSize; uint32_t mSize;
GMPBufferType mBufferType;
Shmem mBuffer; Shmem mBuffer;
bool mCompleteFrame; bool mCompleteFrame;
}; };
@ -32,8 +35,8 @@ struct GMPVideoi420FrameData
GMPPlaneData mVPlane; GMPPlaneData mVPlane;
int32_t mWidth; int32_t mWidth;
int32_t mHeight; int32_t mHeight;
uint32_t mTimestamp; uint64_t mTimestamp; // microseconds
int64_t mRenderTime_ms; uint64_t mDuration; // microseconds
}; };
} }

View File

@ -80,26 +80,31 @@ GMPVideoDecoderChild::InputDataExhausted()
SendInputDataExhausted(); SendInputDataExhausted();
} }
bool void
GMPVideoDecoderChild::MgrAllocShmem(size_t aSize, GMPVideoDecoderChild::DrainComplete()
ipc::Shmem::SharedMemory::SharedMemoryType aType,
ipc::Shmem* aMem)
{ {
MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
return AllocShmem(aSize, aType, aMem); SendDrainComplete();
} }
bool void
GMPVideoDecoderChild::MgrDeallocShmem(Shmem& aMem) GMPVideoDecoderChild::ResetComplete()
{ {
MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
return DeallocShmem(aMem); SendResetComplete();
}
void
GMPVideoDecoderChild::CheckThread()
{
MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
} }
bool bool
GMPVideoDecoderChild::RecvInitDecode(const GMPVideoCodec& aCodecSettings, GMPVideoDecoderChild::RecvInitDecode(const GMPVideoCodec& aCodecSettings,
const nsTArray<uint8_t>& aCodecSpecific,
const int32_t& aCoreCount) const int32_t& aCoreCount)
{ {
if (!mVideoDecoder) { if (!mVideoDecoder) {
@ -107,15 +112,18 @@ GMPVideoDecoderChild::RecvInitDecode(const GMPVideoCodec& aCodecSettings,
} }
// Ignore any return code. It is OK for this to fail without killing the process. // Ignore any return code. It is OK for this to fail without killing the process.
mVideoDecoder->InitDecode(aCodecSettings, this, aCoreCount); mVideoDecoder->InitDecode(aCodecSettings,
aCodecSpecific.Elements(),
aCodecSpecific.Length(),
this,
aCoreCount);
return true; return true;
} }
bool bool
GMPVideoDecoderChild::RecvDecode(const GMPVideoEncodedFrameData& aInputFrame, GMPVideoDecoderChild::RecvDecode(const GMPVideoEncodedFrameData& aInputFrame,
const bool& aMissingFrames, const bool& aMissingFrames,
const GMPCodecSpecificInfo& aCodecSpecificInfo, const nsTArray<uint8_t>& aCodecSpecificInfo,
const int64_t& aRenderTimeMs) const int64_t& aRenderTimeMs)
{ {
if (!mVideoDecoder) { if (!mVideoDecoder) {
@ -125,11 +133,25 @@ GMPVideoDecoderChild::RecvDecode(const GMPVideoEncodedFrameData& aInputFrame,
auto f = new GMPVideoEncodedFrameImpl(aInputFrame, &mVideoHost); auto f = new GMPVideoEncodedFrameImpl(aInputFrame, &mVideoHost);
// Ignore any return code. It is OK for this to fail without killing the process. // Ignore any return code. It is OK for this to fail without killing the process.
mVideoDecoder->Decode(f, aMissingFrames, aCodecSpecificInfo, aRenderTimeMs); mVideoDecoder->Decode(f,
aMissingFrames,
aCodecSpecificInfo.Elements(),
aCodecSpecificInfo.Length(),
aRenderTimeMs);
return true; return true;
} }
bool
GMPVideoDecoderChild::RecvChildShmemForPool(Shmem& aFrameBuffer)
{
if (aFrameBuffer.IsWritable()) {
mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPFrameData,
aFrameBuffer);
}
return true;
}
bool bool
GMPVideoDecoderChild::RecvReset() GMPVideoDecoderChild::RecvReset()
{ {

View File

@ -11,6 +11,7 @@
#include "gmp-video-decode.h" #include "gmp-video-decode.h"
#include "GMPSharedMemManager.h" #include "GMPSharedMemManager.h"
#include "GMPVideoHost.h" #include "GMPVideoHost.h"
#include "mozilla/gmp/GMPTypes.h"
namespace mozilla { namespace mozilla {
namespace gmp { namespace gmp {
@ -18,7 +19,7 @@ namespace gmp {
class GMPChild; class GMPChild;
class GMPVideoDecoderChild : public PGMPVideoDecoderChild, class GMPVideoDecoderChild : public PGMPVideoDecoderChild,
public GMPDecoderCallback, public GMPVideoDecoderCallback,
public GMPSharedMemManager public GMPSharedMemManager
{ {
public: public:
@ -28,26 +29,47 @@ public:
void Init(GMPVideoDecoder* aDecoder); void Init(GMPVideoDecoder* aDecoder);
GMPVideoHostImpl& Host(); GMPVideoHostImpl& Host();
// GMPDecoderCallback // GMPVideoDecoderCallback
virtual void Decoded(GMPVideoi420Frame* decodedFrame) MOZ_OVERRIDE; virtual void Decoded(GMPVideoi420Frame* decodedFrame) MOZ_OVERRIDE;
virtual void ReceivedDecodedReferenceFrame(const uint64_t pictureId) MOZ_OVERRIDE; virtual void ReceivedDecodedReferenceFrame(const uint64_t pictureId) MOZ_OVERRIDE;
virtual void ReceivedDecodedFrame(const uint64_t pictureId) MOZ_OVERRIDE; virtual void ReceivedDecodedFrame(const uint64_t pictureId) MOZ_OVERRIDE;
virtual void InputDataExhausted() MOZ_OVERRIDE; virtual void InputDataExhausted() MOZ_OVERRIDE;
virtual void DrainComplete() MOZ_OVERRIDE;
virtual void ResetComplete() MOZ_OVERRIDE;
// GMPSharedMemManager // GMPSharedMemManager
virtual bool MgrAllocShmem(size_t aSize, virtual void CheckThread();
ipc::Shmem::SharedMemory::SharedMemoryType aType, virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem)
ipc::Shmem* aMem) MOZ_OVERRIDE; {
virtual bool MgrDeallocShmem(Shmem& aMem) MOZ_OVERRIDE; #ifndef SHMEM_ALLOC_IN_CHILD
return CallNeedShmem(aSize, aMem);
#else
#ifdef GMP_SAFE_SHMEM
return AllocShmem(aSize, aType, aMem);
#else
return AllocUnsafeShmem(aSize, aType, aMem);
#endif
#endif
}
virtual void Dealloc(Shmem& aMem)
{
#ifndef SHMEM_ALLOC_IN_CHILD
SendParentShmemForPool(aMem);
#else
DeallocShmem(aMem);
#endif
}
private: private:
// PGMPVideoDecoderChild // PGMPVideoDecoderChild
virtual bool RecvInitDecode(const GMPVideoCodec& codecSettings, virtual bool RecvInitDecode(const GMPVideoCodec& aCodecSettings,
const int32_t& coreCount) MOZ_OVERRIDE; const nsTArray<uint8_t>& aCodecSpecific,
virtual bool RecvDecode(const GMPVideoEncodedFrameData& inputFrame, const int32_t& aCoreCount) MOZ_OVERRIDE;
const bool& missingFrames, virtual bool RecvDecode(const GMPVideoEncodedFrameData& aInputFrame,
const GMPCodecSpecificInfo& codecSpecificInfo, const bool& aMissingFrames,
const int64_t& renderTimeMs) MOZ_OVERRIDE; const nsTArray<uint8_t>& aCodecSpecificInfo,
const int64_t& aRenderTimeMs) MOZ_OVERRIDE;
virtual bool RecvChildShmemForPool(Shmem& aFrameBuffer) MOZ_OVERRIDE;
virtual bool RecvReset() MOZ_OVERRIDE; virtual bool RecvReset() MOZ_OVERRIDE;
virtual bool RecvDrain() MOZ_OVERRIDE; virtual bool RecvDrain() MOZ_OVERRIDE;
virtual bool RecvDecodingComplete() MOZ_OVERRIDE; virtual bool RecvDecodingComplete() MOZ_OVERRIDE;

View File

@ -12,6 +12,7 @@
#include "GMPMessageUtils.h" #include "GMPMessageUtils.h"
#include "nsAutoRef.h" #include "nsAutoRef.h"
#include "nsThreadUtils.h" #include "nsThreadUtils.h"
#include "mozilla/gmp/GMPTypes.h"
template <> template <>
class nsAutoRefTraits<GMPVideoEncodedFrame> : public nsPointerRefTraits<GMPVideoEncodedFrame> class nsAutoRefTraits<GMPVideoEncodedFrame> : public nsPointerRefTraits<GMPVideoEncodedFrame>
@ -42,60 +43,43 @@ GMPVideoDecoderParent::Host()
return mVideoHost; return mVideoHost;
} }
bool nsresult
GMPVideoDecoderParent::MgrAllocShmem(size_t aSize,
ipc::Shmem::SharedMemory::SharedMemoryType aType,
ipc::Shmem* aMem)
{
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
return AllocShmem(aSize, aType, aMem);
}
bool
GMPVideoDecoderParent::MgrDeallocShmem(Shmem& aMem)
{
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
return DeallocShmem(aMem);
}
GMPVideoErr
GMPVideoDecoderParent::InitDecode(const GMPVideoCodec& aCodecSettings, GMPVideoDecoderParent::InitDecode(const GMPVideoCodec& aCodecSettings,
GMPDecoderCallback* aCallback, const nsTArray<uint8_t>& aCodecSpecific,
GMPVideoDecoderCallback* aCallback,
int32_t aCoreCount) int32_t aCoreCount)
{ {
if (!mCanSendMessages) { if (!mCanSendMessages) {
NS_WARNING("Trying to use an invalid GMP video decoder!"); NS_WARNING("Trying to use an invalid GMP video decoder!");
return GMPVideoGenericErr; return NS_ERROR_FAILURE;
} }
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
if (!aCallback) { if (!aCallback) {
return GMPVideoGenericErr; return NS_ERROR_FAILURE;
} }
mCallback = aCallback; mCallback = aCallback;
if (!SendInitDecode(aCodecSettings, aCoreCount)) { if (!SendInitDecode(aCodecSettings, aCodecSpecific, aCoreCount)) {
return GMPVideoGenericErr; return NS_ERROR_FAILURE;
} }
// Async IPC, we don't have access to a return value. // Async IPC, we don't have access to a return value.
return GMPVideoNoErr; return NS_OK;
} }
GMPVideoErr nsresult
GMPVideoDecoderParent::Decode(GMPVideoEncodedFrame* aInputFrame, GMPVideoDecoderParent::Decode(GMPVideoEncodedFrame* aInputFrame,
bool aMissingFrames, bool aMissingFrames,
const GMPCodecSpecificInfo& aCodecSpecificInfo, const nsTArray<uint8_t>& aCodecSpecificInfo,
int64_t aRenderTimeMs) int64_t aRenderTimeMs)
{ {
nsAutoRef<GMPVideoEncodedFrame> autoDestroy(aInputFrame); nsAutoRef<GMPVideoEncodedFrame> autoDestroy(aInputFrame);
if (!mCanSendMessages) { if (!mCanSendMessages) {
NS_WARNING("Trying to use an invalid GMP video decoder!"); NS_WARNING("Trying to use an invalid GMP video decoder!");
return GMPVideoGenericErr; return NS_ERROR_FAILURE;
} }
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
@ -105,60 +89,68 @@ GMPVideoDecoderParent::Decode(GMPVideoEncodedFrame* aInputFrame,
GMPVideoEncodedFrameData frameData; GMPVideoEncodedFrameData frameData;
inputFrameImpl->RelinquishFrameData(frameData); inputFrameImpl->RelinquishFrameData(frameData);
// Very rough kill-switch if the plugin stops processing. If it's merely
// hung and continues, we'll come back to life eventually.
// 3* is because we're using 3 buffers per frame for i420 data for now.
if (NumInUse(kGMPFrameData) > 3*GMPSharedMemManager::kGMPBufLimit ||
NumInUse(kGMPEncodedData) > GMPSharedMemManager::kGMPBufLimit) {
return NS_ERROR_FAILURE;
}
if (!SendDecode(frameData, if (!SendDecode(frameData,
aMissingFrames, aMissingFrames,
aCodecSpecificInfo, aCodecSpecificInfo,
aRenderTimeMs)) { aRenderTimeMs)) {
return GMPVideoGenericErr; return NS_ERROR_FAILURE;
} }
// Async IPC, we don't have access to a return value. // Async IPC, we don't have access to a return value.
return GMPVideoNoErr; return NS_OK;
} }
GMPVideoErr nsresult
GMPVideoDecoderParent::Reset() GMPVideoDecoderParent::Reset()
{ {
if (!mCanSendMessages) { if (!mCanSendMessages) {
NS_WARNING("Trying to use an invalid GMP video decoder!"); NS_WARNING("Trying to use an invalid GMP video decoder!");
return GMPVideoGenericErr; return NS_ERROR_FAILURE;
} }
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
if (!SendReset()) { if (!SendReset()) {
return GMPVideoGenericErr; return NS_ERROR_FAILURE;
} }
// Async IPC, we don't have access to a return value. // Async IPC, we don't have access to a return value.
return GMPVideoNoErr; return NS_OK;
} }
GMPVideoErr nsresult
GMPVideoDecoderParent::Drain() GMPVideoDecoderParent::Drain()
{ {
if (!mCanSendMessages) { if (!mCanSendMessages) {
NS_WARNING("Trying to use an invalid GMP video decoder!"); NS_WARNING("Trying to use an invalid GMP video decoder!");
return GMPVideoGenericErr; return NS_ERROR_FAILURE;
} }
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
if (!SendDrain()) { if (!SendDrain()) {
return GMPVideoGenericErr; return NS_ERROR_FAILURE;
} }
// Async IPC, we don't have access to a return value. // Async IPC, we don't have access to a return value.
return GMPVideoNoErr; return NS_OK;
} }
// Note: Consider keeping ActorDestroy sync'd up when making changes here. // Note: Consider keeping ActorDestroy sync'd up when making changes here.
void nsresult
GMPVideoDecoderParent::DecodingComplete() GMPVideoDecoderParent::DecodingComplete()
{ {
if (!mCanSendMessages) { if (!mCanSendMessages) {
NS_WARNING("Trying to use an invalid GMP video decoder!"); NS_WARNING("Trying to use an invalid GMP video decoder!");
return; return NS_ERROR_FAILURE;
} }
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
@ -170,6 +162,8 @@ GMPVideoDecoderParent::DecodingComplete()
mVideoHost.DoneWithAPI(); mVideoHost.DoneWithAPI();
unused << SendDecodingComplete(); unused << SendDecodingComplete();
return NS_OK;
} }
// Note: Keep this sync'd up with DecodingComplete // Note: Keep this sync'd up with DecodingComplete
@ -186,6 +180,12 @@ GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
mVideoHost.ActorDestroyed(); mVideoHost.ActorDestroyed();
} }
void
GMPVideoDecoderParent::CheckThread()
{
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
}
bool bool
GMPVideoDecoderParent::RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame) GMPVideoDecoderParent::RecvDecoded(const GMPVideoi420FrameData& aDecodedFrame)
{ {
@ -240,6 +240,59 @@ GMPVideoDecoderParent::RecvInputDataExhausted()
return true; return true;
} }
bool
GMPVideoDecoderParent::RecvDrainComplete()
{
if (!mCallback) {
return false;
}
// Ignore any return code. It is OK for this to fail without killing the process.
mCallback->DrainComplete();
return true;
}
bool
GMPVideoDecoderParent::RecvResetComplete()
{
if (!mCallback) {
return false;
}
// Ignore any return code. It is OK for this to fail without killing the process.
mCallback->ResetComplete();
return true;
}
bool
GMPVideoDecoderParent::RecvParentShmemForPool(Shmem& aEncodedBuffer)
{
if (aEncodedBuffer.IsWritable()) {
mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPEncodedData,
aEncodedBuffer);
}
return true;
}
bool
GMPVideoDecoderParent::AnswerNeedShmem(const uint32_t& aFrameBufferSize,
Shmem* aMem)
{
ipc::Shmem mem;
if (!mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPFrameData,
aFrameBufferSize,
ipc::SharedMemory::TYPE_BASIC, &mem))
{
return false;
}
*aMem = mem;
mem = ipc::Shmem();
return true;
}
bool bool
GMPVideoDecoderParent::Recv__delete__() GMPVideoDecoderParent::Recv__delete__()
{ {

View File

@ -12,15 +12,16 @@
#include "GMPMessageUtils.h" #include "GMPMessageUtils.h"
#include "GMPSharedMemManager.h" #include "GMPSharedMemManager.h"
#include "GMPVideoHost.h" #include "GMPVideoHost.h"
#include "GMPVideoDecoderProxy.h"
namespace mozilla { namespace mozilla {
namespace gmp { namespace gmp {
class GMPParent; class GMPParent;
class GMPVideoDecoderParent MOZ_FINAL : public GMPVideoDecoder class GMPVideoDecoderParent MOZ_FINAL : public PGMPVideoDecoderParent
, public PGMPVideoDecoderParent
, public GMPSharedMemManager , public GMPSharedMemManager
, public GMPVideoDecoderProxy
{ {
public: public:
NS_INLINE_DECL_REFCOUNTING(GMPVideoDecoderParent) NS_INLINE_DECL_REFCOUNTING(GMPVideoDecoderParent)
@ -29,23 +30,33 @@ public:
GMPVideoHostImpl& Host(); GMPVideoHostImpl& Host();
// GMPSharedMemManager
virtual bool MgrAllocShmem(size_t aSize,
ipc::Shmem::SharedMemory::SharedMemoryType aType,
ipc::Shmem* aMem) MOZ_OVERRIDE;
virtual bool MgrDeallocShmem(Shmem& aMem) MOZ_OVERRIDE;
// GMPVideoDecoder // GMPVideoDecoder
virtual GMPVideoErr InitDecode(const GMPVideoCodec& aCodecSettings, virtual nsresult InitDecode(const GMPVideoCodec& aCodecSettings,
GMPDecoderCallback* aCallback, const nsTArray<uint8_t>& aCodecSpecific,
int32_t aCoreCount) MOZ_OVERRIDE; GMPVideoDecoderCallback* aCallback,
virtual GMPVideoErr Decode(GMPVideoEncodedFrame* aInputFrame, int32_t aCoreCount) MOZ_OVERRIDE;
bool aMissingFrames, virtual nsresult Decode(GMPVideoEncodedFrame* aInputFrame,
const GMPCodecSpecificInfo& aCodecSpecificInfo, bool aMissingFrames,
int64_t aRenderTimeMs = -1) MOZ_OVERRIDE; const nsTArray<uint8_t>& aCodecSpecificInfo,
virtual GMPVideoErr Reset() MOZ_OVERRIDE; int64_t aRenderTimeMs = -1) MOZ_OVERRIDE;
virtual GMPVideoErr Drain() MOZ_OVERRIDE; virtual nsresult Reset() MOZ_OVERRIDE;
virtual void DecodingComplete() MOZ_OVERRIDE; virtual nsresult Drain() MOZ_OVERRIDE;
virtual nsresult DecodingComplete() MOZ_OVERRIDE;
// GMPSharedMemManager
virtual void CheckThread();
virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem)
{
#ifdef GMP_SAFE_SHMEM
return AllocShmem(aSize, aType, aMem);
#else
return AllocUnsafeShmem(aSize, aType, aMem);
#endif
}
virtual void Dealloc(Shmem& aMem)
{
DeallocShmem(aMem);
}
private: private:
~GMPVideoDecoderParent(); ~GMPVideoDecoderParent();
@ -56,11 +67,16 @@ private:
virtual bool RecvReceivedDecodedReferenceFrame(const uint64_t& aPictureId) MOZ_OVERRIDE; virtual bool RecvReceivedDecodedReferenceFrame(const uint64_t& aPictureId) MOZ_OVERRIDE;
virtual bool RecvReceivedDecodedFrame(const uint64_t& aPictureId) MOZ_OVERRIDE; virtual bool RecvReceivedDecodedFrame(const uint64_t& aPictureId) MOZ_OVERRIDE;
virtual bool RecvInputDataExhausted() MOZ_OVERRIDE; virtual bool RecvInputDataExhausted() MOZ_OVERRIDE;
virtual bool RecvDrainComplete() MOZ_OVERRIDE;
virtual bool RecvResetComplete() MOZ_OVERRIDE;
virtual bool RecvParentShmemForPool(Shmem& aEncodedBuffer) MOZ_OVERRIDE;
virtual bool AnswerNeedShmem(const uint32_t& aFrameBufferSize,
Shmem* aMem) MOZ_OVERRIDE;
virtual bool Recv__delete__() MOZ_OVERRIDE; virtual bool Recv__delete__() MOZ_OVERRIDE;
bool mCanSendMessages; bool mCanSendMessages;
GMPParent* mPlugin; GMPParent* mPlugin;
GMPDecoderCallback* mCallback; GMPVideoDecoderCallback* mCallback;
GMPVideoHostImpl mVideoHost; GMPVideoHostImpl mVideoHost;
}; };

View File

@ -0,0 +1,33 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GMPVideoDecoderProxy_h_
#define GMPVideoDecoderProxy_h_
#include "nsTArray.h"
#include "gmp-video-decode.h"
#include "gmp-video-frame-i420.h"
#include "gmp-video-frame-encoded.h"
// A proxy to GMPVideoDecoder in the child process.
// GMPVideoDecoderParent exposes this to users the GMP.
// This enables Gecko to pass nsTArrays to the child GMP and avoid
// an extra copy when doing so.
class GMPVideoDecoderProxy {
public:
virtual nsresult InitDecode(const GMPVideoCodec& aCodecSettings,
const nsTArray<uint8_t>& aCodecSpecific,
GMPVideoDecoderCallback* aCallback,
int32_t aCoreCount) = 0;
virtual nsresult Decode(GMPVideoEncodedFrame* aInputFrame,
bool aMissingFrames,
const nsTArray<uint8_t>& aCodecSpecificInfo,
int64_t aRenderTimeMs = -1) = 0;
virtual nsresult Reset() = 0;
virtual nsresult Drain() = 0;
virtual nsresult DecodingComplete() = 0;
};
#endif

View File

@ -14,12 +14,13 @@ namespace gmp {
GMPVideoEncodedFrameImpl::GMPVideoEncodedFrameImpl(GMPVideoHostImpl* aHost) GMPVideoEncodedFrameImpl::GMPVideoEncodedFrameImpl(GMPVideoHostImpl* aHost)
: mEncodedWidth(0), : mEncodedWidth(0),
mEncodedHeight(0), mEncodedHeight(0),
mTimeStamp(0), mTimeStamp(0ll),
mCaptureTime_ms(0), mDuration(0ll),
mFrameType(kGMPDeltaFrame), mFrameType(kGMPDeltaFrame),
mSize(0), mSize(0),
mCompleteFrame(false), mCompleteFrame(false),
mHost(aHost) mHost(aHost),
mBufferType(GMP_BufferSingle)
{ {
MOZ_ASSERT(aHost); MOZ_ASSERT(aHost);
aHost->EncodedFrameCreated(this); aHost->EncodedFrameCreated(this);
@ -29,13 +30,14 @@ GMPVideoEncodedFrameImpl::GMPVideoEncodedFrameImpl(const GMPVideoEncodedFrameDat
GMPVideoHostImpl* aHost) GMPVideoHostImpl* aHost)
: mEncodedWidth(aFrameData.mEncodedWidth()), : mEncodedWidth(aFrameData.mEncodedWidth()),
mEncodedHeight(aFrameData.mEncodedHeight()), mEncodedHeight(aFrameData.mEncodedHeight()),
mTimeStamp(aFrameData.mTimeStamp()), mTimeStamp(aFrameData.mTimestamp()),
mCaptureTime_ms(aFrameData.mCaptureTime_ms()), mDuration(aFrameData.mDuration()),
mFrameType(static_cast<GMPVideoFrameType>(aFrameData.mFrameType())), mFrameType(static_cast<GMPVideoFrameType>(aFrameData.mFrameType())),
mSize(aFrameData.mSize()), mSize(aFrameData.mSize()),
mCompleteFrame(aFrameData.mCompleteFrame()), mCompleteFrame(aFrameData.mCompleteFrame()),
mHost(aHost), mHost(aHost),
mBuffer(aFrameData.mBuffer()) mBuffer(aFrameData.mBuffer()),
mBufferType(aFrameData.mBufferType())
{ {
MOZ_ASSERT(aHost); MOZ_ASSERT(aHost);
aHost->EncodedFrameCreated(this); aHost->EncodedFrameCreated(this);
@ -49,6 +51,12 @@ GMPVideoEncodedFrameImpl::~GMPVideoEncodedFrameImpl()
} }
} }
const GMPEncryptedBufferData*
GMPVideoEncodedFrameImpl::GetDecryptionData() const
{
return nullptr;
}
GMPVideoFrameFormat GMPVideoFrameFormat
GMPVideoEncodedFrameImpl::GetFrameFormat() GMPVideoEncodedFrameImpl::GetFrameFormat()
{ {
@ -80,12 +88,13 @@ GMPVideoEncodedFrameImpl::RelinquishFrameData(GMPVideoEncodedFrameData& aFrameDa
{ {
aFrameData.mEncodedWidth() = mEncodedWidth; aFrameData.mEncodedWidth() = mEncodedWidth;
aFrameData.mEncodedHeight() = mEncodedHeight; aFrameData.mEncodedHeight() = mEncodedHeight;
aFrameData.mTimeStamp() = mTimeStamp; aFrameData.mTimestamp() = mTimeStamp;
aFrameData.mCaptureTime_ms() = mCaptureTime_ms; aFrameData.mDuration() = mDuration;
aFrameData.mFrameType() = mFrameType; aFrameData.mFrameType() = mFrameType;
aFrameData.mSize() = mSize; aFrameData.mSize() = mSize;
aFrameData.mCompleteFrame() = mCompleteFrame; aFrameData.mCompleteFrame() = mCompleteFrame;
aFrameData.mBuffer() = mBuffer; aFrameData.mBuffer() = mBuffer;
aFrameData.mBufferType() = mBufferType;
// This method is called right before Shmem is sent to another process. // This method is called right before Shmem is sent to another process.
// We need to effectively zero out our member copy so that we don't // We need to effectively zero out our member copy so that we don't
@ -99,36 +108,37 @@ void
GMPVideoEncodedFrameImpl::DestroyBuffer() GMPVideoEncodedFrameImpl::DestroyBuffer()
{ {
if (mHost && mBuffer.IsWritable()) { if (mHost && mBuffer.IsWritable()) {
mHost->SharedMemMgr()->MgrDeallocShmem(mBuffer); mHost->SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPEncodedData, mBuffer);
} }
mBuffer = ipc::Shmem(); mBuffer = ipc::Shmem();
} }
GMPVideoErr GMPErr
GMPVideoEncodedFrameImpl::CreateEmptyFrame(uint32_t aSize) GMPVideoEncodedFrameImpl::CreateEmptyFrame(uint32_t aSize)
{ {
DestroyBuffer(); if (aSize == 0) {
DestroyBuffer();
if (aSize != 0) { } else if (aSize > AllocatedSize()) {
if (!mHost->SharedMemMgr()->MgrAllocShmem(aSize, ipc::SharedMemory::TYPE_BASIC, &mBuffer) || DestroyBuffer();
if (!mHost->SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPEncodedData, aSize,
ipc::SharedMemory::TYPE_BASIC, &mBuffer) ||
!Buffer()) { !Buffer()) {
return GMPVideoAllocErr; return GMPAllocErr;
} }
} }
mSize = aSize; mSize = aSize;
return GMPVideoNoErr; return GMPNoErr;
} }
GMPVideoErr GMPErr
GMPVideoEncodedFrameImpl::CopyFrame(const GMPVideoEncodedFrame& aFrame) GMPVideoEncodedFrameImpl::CopyFrame(const GMPVideoEncodedFrame& aFrame)
{ {
auto& f = static_cast<const GMPVideoEncodedFrameImpl&>(aFrame); auto& f = static_cast<const GMPVideoEncodedFrameImpl&>(aFrame);
if (f.mSize != 0) { if (f.mSize != 0) {
GMPVideoErr err = CreateEmptyFrame(f.mSize); GMPErr err = CreateEmptyFrame(f.mSize);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
memcpy(Buffer(), f.Buffer(), f.mSize); memcpy(Buffer(), f.Buffer(), f.mSize);
@ -136,13 +146,14 @@ GMPVideoEncodedFrameImpl::CopyFrame(const GMPVideoEncodedFrame& aFrame)
mEncodedWidth = f.mEncodedWidth; mEncodedWidth = f.mEncodedWidth;
mEncodedHeight = f.mEncodedHeight; mEncodedHeight = f.mEncodedHeight;
mTimeStamp = f.mTimeStamp; mTimeStamp = f.mTimeStamp;
mCaptureTime_ms = f.mCaptureTime_ms; mDuration = f.mDuration;
mFrameType = f.mFrameType; mFrameType = f.mFrameType;
mSize = f.mSize; mSize = f.mSize; // already set...
mCompleteFrame = f.mCompleteFrame; mCompleteFrame = f.mCompleteFrame;
mBufferType = f.mBufferType;
// Don't copy host, that should have been set properly on object creation via host. // Don't copy host, that should have been set properly on object creation via host.
return GMPVideoNoErr; return GMPNoErr;
} }
void void
@ -170,27 +181,27 @@ GMPVideoEncodedFrameImpl::EncodedHeight()
} }
void void
GMPVideoEncodedFrameImpl::SetTimeStamp(uint32_t aTimeStamp) GMPVideoEncodedFrameImpl::SetTimeStamp(uint64_t aTimeStamp)
{ {
mTimeStamp = aTimeStamp; mTimeStamp = aTimeStamp;
} }
uint32_t uint64_t
GMPVideoEncodedFrameImpl::TimeStamp() GMPVideoEncodedFrameImpl::TimeStamp()
{ {
return mTimeStamp; return mTimeStamp;
} }
void void
GMPVideoEncodedFrameImpl::SetCaptureTime(int64_t aCaptureTime) GMPVideoEncodedFrameImpl::SetDuration(uint64_t aDuration)
{ {
mCaptureTime_ms = aCaptureTime; mDuration = aDuration;
} }
int64_t uint64_t
GMPVideoEncodedFrameImpl::CaptureTime() GMPVideoEncodedFrameImpl::Duration() const
{ {
return mCaptureTime_ms; return mDuration;
} }
void void
@ -217,7 +228,8 @@ GMPVideoEncodedFrameImpl::SetAllocatedSize(uint32_t aNewSize)
} }
ipc::Shmem new_mem; ipc::Shmem new_mem;
if (!mHost->SharedMemMgr()->MgrAllocShmem(aNewSize, ipc::SharedMemory::TYPE_BASIC, &new_mem) || if (!mHost->SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPEncodedData, aNewSize,
ipc::SharedMemory::TYPE_BASIC, &new_mem) ||
!new_mem.get<uint8_t>()) { !new_mem.get<uint8_t>()) {
return; return;
} }
@ -282,5 +294,17 @@ GMPVideoEncodedFrameImpl::Destroy()
delete this; delete this;
} }
GMPBufferType
GMPVideoEncodedFrameImpl::BufferType() const
{
return mBufferType;
}
void
GMPVideoEncodedFrameImpl::SetBufferType(GMPBufferType aBufferType)
{
mBufferType = aBufferType;
}
} // namespace gmp } // namespace gmp
} // namespace mozilla } // namespace mozilla

View File

@ -31,9 +31,10 @@
#ifndef GMPVideoEncodedFrameImpl_h_ #ifndef GMPVideoEncodedFrameImpl_h_
#define GMPVideoEncodedFrameImpl_h_ #define GMPVideoEncodedFrameImpl_h_
#include "gmp-video-errors.h" #include "gmp-errors.h"
#include "gmp-video-frame.h" #include "gmp-video-frame.h"
#include "gmp-video-frame-encoded.h" #include "gmp-video-frame-encoded.h"
#include "gmp-decryption.h"
#include "mozilla/ipc/Shmem.h" #include "mozilla/ipc/Shmem.h"
namespace mozilla { namespace mozilla {
@ -63,16 +64,21 @@ public:
virtual void Destroy() MOZ_OVERRIDE; virtual void Destroy() MOZ_OVERRIDE;
// GMPVideoEncodedFrame // GMPVideoEncodedFrame
virtual GMPVideoErr CreateEmptyFrame(uint32_t aSize) MOZ_OVERRIDE; virtual GMPErr CreateEmptyFrame(uint32_t aSize) MOZ_OVERRIDE;
virtual GMPVideoErr CopyFrame(const GMPVideoEncodedFrame& aFrame) MOZ_OVERRIDE; virtual GMPErr CopyFrame(const GMPVideoEncodedFrame& aFrame) MOZ_OVERRIDE;
virtual void SetEncodedWidth(uint32_t aEncodedWidth) MOZ_OVERRIDE; virtual void SetEncodedWidth(uint32_t aEncodedWidth) MOZ_OVERRIDE;
virtual uint32_t EncodedWidth() MOZ_OVERRIDE; virtual uint32_t EncodedWidth() MOZ_OVERRIDE;
virtual void SetEncodedHeight(uint32_t aEncodedHeight) MOZ_OVERRIDE; virtual void SetEncodedHeight(uint32_t aEncodedHeight) MOZ_OVERRIDE;
virtual uint32_t EncodedHeight() MOZ_OVERRIDE; virtual uint32_t EncodedHeight() MOZ_OVERRIDE;
virtual void SetTimeStamp(uint32_t aTimeStamp) MOZ_OVERRIDE; // Microseconds
virtual uint32_t TimeStamp() MOZ_OVERRIDE; virtual void SetTimeStamp(uint64_t aTimeStamp) MOZ_OVERRIDE;
virtual void SetCaptureTime(int64_t aCaptureTime) MOZ_OVERRIDE; virtual uint64_t TimeStamp() MOZ_OVERRIDE;
virtual int64_t CaptureTime() MOZ_OVERRIDE; // Set frame duration (microseconds)
// NOTE: next-frame's Timestamp() != this-frame's TimeStamp()+Duration()
// depending on rounding to avoid having to track roundoff errors
// and dropped/missing frames(!) (which may leave a large gap)
virtual void SetDuration(uint64_t aDuration) MOZ_OVERRIDE;
virtual uint64_t Duration() const MOZ_OVERRIDE;
virtual void SetFrameType(GMPVideoFrameType aFrameType) MOZ_OVERRIDE; virtual void SetFrameType(GMPVideoFrameType aFrameType) MOZ_OVERRIDE;
virtual GMPVideoFrameType FrameType() MOZ_OVERRIDE; virtual GMPVideoFrameType FrameType() MOZ_OVERRIDE;
virtual void SetAllocatedSize(uint32_t aNewSize) MOZ_OVERRIDE; virtual void SetAllocatedSize(uint32_t aNewSize) MOZ_OVERRIDE;
@ -83,19 +89,23 @@ public:
virtual bool CompleteFrame() MOZ_OVERRIDE; virtual bool CompleteFrame() MOZ_OVERRIDE;
virtual const uint8_t* Buffer() const MOZ_OVERRIDE; virtual const uint8_t* Buffer() const MOZ_OVERRIDE;
virtual uint8_t* Buffer() MOZ_OVERRIDE; virtual uint8_t* Buffer() MOZ_OVERRIDE;
virtual GMPBufferType BufferType() const MOZ_OVERRIDE;
virtual void SetBufferType(GMPBufferType aBufferType) MOZ_OVERRIDE;
virtual const GMPEncryptedBufferData* GetDecryptionData() const MOZ_OVERRIDE;
private: private:
void DestroyBuffer(); void DestroyBuffer();
uint32_t mEncodedWidth; uint32_t mEncodedWidth;
uint32_t mEncodedHeight; uint32_t mEncodedHeight;
uint32_t mTimeStamp; uint64_t mTimeStamp;
int64_t mCaptureTime_ms; uint64_t mDuration;
GMPVideoFrameType mFrameType; GMPVideoFrameType mFrameType;
uint32_t mSize; uint32_t mSize;
bool mCompleteFrame; bool mCompleteFrame;
GMPVideoHostImpl* mHost; GMPVideoHostImpl* mHost;
ipc::Shmem mBuffer; ipc::Shmem mBuffer;
GMPBufferType mBufferType;
}; };
} // namespace gmp } // namespace gmp

View File

@ -40,7 +40,8 @@ GMPVideoEncoderChild::Host()
void void
GMPVideoEncoderChild::Encoded(GMPVideoEncodedFrame* aEncodedFrame, GMPVideoEncoderChild::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
const GMPCodecSpecificInfo& aCodecSpecificInfo) const uint8_t* aCodecSpecificInfo,
uint32_t aCodecSpecificInfoLength)
{ {
MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
@ -49,31 +50,22 @@ GMPVideoEncoderChild::Encoded(GMPVideoEncodedFrame* aEncodedFrame,
GMPVideoEncodedFrameData frameData; GMPVideoEncodedFrameData frameData;
ef->RelinquishFrameData(frameData); ef->RelinquishFrameData(frameData);
SendEncoded(frameData, aCodecSpecificInfo); nsTArray<uint8_t> codecSpecific;
codecSpecific.AppendElements(aCodecSpecificInfo, aCodecSpecificInfoLength);
SendEncoded(frameData, codecSpecific);
aEncodedFrame->Destroy(); aEncodedFrame->Destroy();
} }
bool void
GMPVideoEncoderChild::MgrAllocShmem(size_t aSize, GMPVideoEncoderChild::CheckThread()
ipc::Shmem::SharedMemory::SharedMemoryType aType,
ipc::Shmem* aMem)
{ {
MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current()); MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
return AllocShmem(aSize, aType, aMem);
}
bool
GMPVideoEncoderChild::MgrDeallocShmem(Shmem& aMem)
{
MOZ_ASSERT(mPlugin->GMPMessageLoop() == MessageLoop::current());
return DeallocShmem(aMem);
} }
bool bool
GMPVideoEncoderChild::RecvInitEncode(const GMPVideoCodec& aCodecSettings, GMPVideoEncoderChild::RecvInitEncode(const GMPVideoCodec& aCodecSettings,
const nsTArray<uint8_t>& aCodecSpecific,
const int32_t& aNumberOfCores, const int32_t& aNumberOfCores,
const uint32_t& aMaxPayloadSize) const uint32_t& aMaxPayloadSize)
{ {
@ -82,15 +74,20 @@ GMPVideoEncoderChild::RecvInitEncode(const GMPVideoCodec& aCodecSettings,
} }
// Ignore any return code. It is OK for this to fail without killing the process. // Ignore any return code. It is OK for this to fail without killing the process.
mVideoEncoder->InitEncode(aCodecSettings, this, aNumberOfCores, aMaxPayloadSize); mVideoEncoder->InitEncode(aCodecSettings,
aCodecSpecific.Elements(),
aCodecSpecific.Length(),
this,
aNumberOfCores,
aMaxPayloadSize);
return true; return true;
} }
bool bool
GMPVideoEncoderChild::RecvEncode(const GMPVideoi420FrameData& aInputFrame, GMPVideoEncoderChild::RecvEncode(const GMPVideoi420FrameData& aInputFrame,
const GMPCodecSpecificInfo& aCodecSpecificInfo, const nsTArray<uint8_t>& aCodecSpecificInfo,
const InfallibleTArray<int>& aFrameTypes) const nsTArray<GMPVideoFrameType>& aFrameTypes)
{ {
if (!mVideoEncoder) { if (!mVideoEncoder) {
return false; return false;
@ -98,17 +95,26 @@ GMPVideoEncoderChild::RecvEncode(const GMPVideoi420FrameData& aInputFrame,
auto f = new GMPVideoi420FrameImpl(aInputFrame, &mVideoHost); auto f = new GMPVideoi420FrameImpl(aInputFrame, &mVideoHost);
std::vector<GMPVideoFrameType> frameTypes(aFrameTypes.Length());
for (uint32_t i = 0; i < aFrameTypes.Length(); i++) {
frameTypes[i] = static_cast<GMPVideoFrameType>(aFrameTypes[i]);
}
// Ignore any return code. It is OK for this to fail without killing the process. // Ignore any return code. It is OK for this to fail without killing the process.
mVideoEncoder->Encode(f, aCodecSpecificInfo, frameTypes); mVideoEncoder->Encode(f,
aCodecSpecificInfo.Elements(),
aCodecSpecificInfo.Length(),
aFrameTypes.Elements(),
aFrameTypes.Length());
return true; return true;
} }
bool
GMPVideoEncoderChild::RecvChildShmemForPool(Shmem& aEncodedBuffer)
{
if (aEncodedBuffer.IsWritable()) {
mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPEncodedData,
aEncodedBuffer);
}
return true;
}
bool bool
GMPVideoEncoderChild::RecvSetChannelParameters(const uint32_t& aPacketLoss, GMPVideoEncoderChild::RecvSetChannelParameters(const uint32_t& aPacketLoss,
const uint32_t& aRTT) const uint32_t& aRTT)

View File

@ -18,7 +18,7 @@ namespace gmp {
class GMPChild; class GMPChild;
class GMPVideoEncoderChild : public PGMPVideoEncoderChild, class GMPVideoEncoderChild : public PGMPVideoEncoderChild,
public GMPEncoderCallback, public GMPVideoEncoderCallback,
public GMPSharedMemManager public GMPSharedMemManager
{ {
public: public:
@ -28,24 +28,44 @@ public:
void Init(GMPVideoEncoder* aEncoder); void Init(GMPVideoEncoder* aEncoder);
GMPVideoHostImpl& Host(); GMPVideoHostImpl& Host();
// GMPEncoderCallback // GMPVideoEncoderCallback
virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame, virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
const GMPCodecSpecificInfo& aCodecSpecificInfo) MOZ_OVERRIDE; const uint8_t* aCodecSpecificInfo,
uint32_t aCodecSpecificInfoLength) MOZ_OVERRIDE;
// GMPSharedMemManager // GMPSharedMemManager
virtual bool MgrAllocShmem(size_t aSize, virtual void CheckThread();
ipc::Shmem::SharedMemory::SharedMemoryType aType, virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem)
ipc::Shmem* aMem) MOZ_OVERRIDE; {
virtual bool MgrDeallocShmem(Shmem& aMem) MOZ_OVERRIDE; #ifndef SHMEM_ALLOC_IN_CHILD
return CallNeedShmem(aSize, aMem);
#else
#ifdef GMP_SAFE_SHMEM
return AllocShmem(aSize, aType, aMem);
#else
return AllocUnsafeShmem(aSize, aType, aMem);
#endif
#endif
}
virtual void Dealloc(Shmem& aMem)
{
#ifndef SHMEM_ALLOC_IN_CHILD
SendParentShmemForPool(aMem);
#else
DeallocShmem(aMem);
#endif
}
private: private:
// PGMPVideoEncoderChild // PGMPVideoEncoderChild
virtual bool RecvInitEncode(const GMPVideoCodec& aCodecSettings, virtual bool RecvInitEncode(const GMPVideoCodec& aCodecSettings,
const nsTArray<uint8_t>& aCodecSpecific,
const int32_t& aNumberOfCores, const int32_t& aNumberOfCores,
const uint32_t& aMaxPayloadSize) MOZ_OVERRIDE; const uint32_t& aMaxPayloadSize) MOZ_OVERRIDE;
virtual bool RecvEncode(const GMPVideoi420FrameData& aInputFrame, virtual bool RecvEncode(const GMPVideoi420FrameData& aInputFrame,
const GMPCodecSpecificInfo& aCodecSpecificInfo, const nsTArray<uint8_t>& aCodecSpecificInfo,
const InfallibleTArray<int>& aFrameTypes) MOZ_OVERRIDE; const nsTArray<GMPVideoFrameType>& aFrameTypes) MOZ_OVERRIDE;
virtual bool RecvChildShmemForPool(Shmem& aEncodedBuffer) MOZ_OVERRIDE;
virtual bool RecvSetChannelParameters(const uint32_t& aPacketLoss, virtual bool RecvSetChannelParameters(const uint32_t& aPacketLoss,
const uint32_t& aRTT) MOZ_OVERRIDE; const uint32_t& aRTT) MOZ_OVERRIDE;
virtual bool RecvSetRates(const uint32_t& aNewBitRate, virtual bool RecvSetRates(const uint32_t& aNewBitRate,

View File

@ -43,60 +43,43 @@ GMPVideoEncoderParent::Host()
return mVideoHost; return mVideoHost;
} }
bool GMPErr
GMPVideoEncoderParent::MgrAllocShmem(size_t aSize,
ipc::Shmem::SharedMemory::SharedMemoryType aType,
ipc::Shmem* aMem)
{
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
return AllocShmem(aSize, aType, aMem);
}
bool
GMPVideoEncoderParent::MgrDeallocShmem(Shmem& aMem)
{
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
return DeallocShmem(aMem);
}
GMPVideoErr
GMPVideoEncoderParent::InitEncode(const GMPVideoCodec& aCodecSettings, GMPVideoEncoderParent::InitEncode(const GMPVideoCodec& aCodecSettings,
GMPEncoderCallback* aCallback, const nsTArray<uint8_t>& aCodecSpecific,
GMPVideoEncoderCallbackProxy* aCallback,
int32_t aNumberOfCores, int32_t aNumberOfCores,
uint32_t aMaxPayloadSize) uint32_t aMaxPayloadSize)
{ {
if (!mCanSendMessages) { if (!mCanSendMessages) {
NS_WARNING("Trying to use an invalid GMP video encoder!"); NS_WARNING("Trying to use an invalid GMP video encoder!");
return GMPVideoGenericErr; return GMPGenericErr;
} }
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
if (!aCallback) { if (!aCallback) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
mCallback = aCallback; mCallback = aCallback;
if (!SendInitEncode(aCodecSettings, aNumberOfCores, aMaxPayloadSize)) { if (!SendInitEncode(aCodecSettings, aCodecSpecific, aNumberOfCores, aMaxPayloadSize)) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
// Async IPC, we don't have access to a return value. // Async IPC, we don't have access to a return value.
return GMPVideoNoErr; return GMPNoErr;
} }
GMPVideoErr GMPErr
GMPVideoEncoderParent::Encode(GMPVideoi420Frame* aInputFrame, GMPVideoEncoderParent::Encode(GMPVideoi420Frame* aInputFrame,
const GMPCodecSpecificInfo& aCodecSpecificInfo, const nsTArray<uint8_t>& aCodecSpecificInfo,
const std::vector<GMPVideoFrameType>& aFrameTypes) const nsTArray<GMPVideoFrameType>& aFrameTypes)
{ {
nsAutoRef<GMPVideoi420Frame> frameRef(aInputFrame); nsAutoRef<GMPVideoi420Frame> frameRef(aInputFrame);
if (!mCanSendMessages) { if (!mCanSendMessages) {
NS_WARNING("Trying to use an invalid GMP video encoder!"); NS_WARNING("Trying to use an invalid GMP video encoder!");
return GMPVideoGenericErr; return GMPGenericErr;
} }
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
@ -106,74 +89,76 @@ GMPVideoEncoderParent::Encode(GMPVideoi420Frame* aInputFrame,
GMPVideoi420FrameData frameData; GMPVideoi420FrameData frameData;
inputFrameImpl->InitFrameData(frameData); inputFrameImpl->InitFrameData(frameData);
InfallibleTArray<int> frameTypes; // Very rough kill-switch if the plugin stops processing. If it's merely
frameTypes.SetCapacity(aFrameTypes.size()); // hung and continues, we'll come back to life eventually.
for (std::vector<int>::size_type i = 0; i != aFrameTypes.size(); i++) { // 3* is because we're using 3 buffers per frame for i420 data for now.
frameTypes.AppendElement(static_cast<int>(aFrameTypes[i])); if (NumInUse(kGMPFrameData) > 3*GMPSharedMemManager::kGMPBufLimit ||
NumInUse(kGMPEncodedData) > GMPSharedMemManager::kGMPBufLimit) {
return GMPGenericErr;
} }
if (!SendEncode(frameData, if (!SendEncode(frameData,
aCodecSpecificInfo, aCodecSpecificInfo,
frameTypes)) { aFrameTypes)) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
// Async IPC, we don't have access to a return value. // Async IPC, we don't have access to a return value.
return GMPVideoNoErr; return GMPNoErr;
} }
GMPVideoErr GMPErr
GMPVideoEncoderParent::SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) GMPVideoEncoderParent::SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT)
{ {
if (!mCanSendMessages) { if (!mCanSendMessages) {
NS_WARNING("Trying to use an invalid GMP video encoder!"); NS_WARNING("Trying to use an invalid GMP video encoder!");
return GMPVideoGenericErr; return GMPGenericErr;
} }
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
if (!SendSetChannelParameters(aPacketLoss, aRTT)) { if (!SendSetChannelParameters(aPacketLoss, aRTT)) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
// Async IPC, we don't have access to a return value. // Async IPC, we don't have access to a return value.
return GMPVideoNoErr; return GMPNoErr;
} }
GMPVideoErr GMPErr
GMPVideoEncoderParent::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) GMPVideoEncoderParent::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate)
{ {
if (!mCanSendMessages) { if (!mCanSendMessages) {
NS_WARNING("Trying to use an invalid GMP video encoder!"); NS_WARNING("Trying to use an invalid GMP video encoder!");
return GMPVideoGenericErr; return GMPGenericErr;
} }
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
if (!SendSetRates(aNewBitRate, aFrameRate)) { if (!SendSetRates(aNewBitRate, aFrameRate)) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
// Async IPC, we don't have access to a return value. // Async IPC, we don't have access to a return value.
return GMPVideoNoErr; return GMPNoErr;
} }
GMPVideoErr GMPErr
GMPVideoEncoderParent::SetPeriodicKeyFrames(bool aEnable) GMPVideoEncoderParent::SetPeriodicKeyFrames(bool aEnable)
{ {
if (!mCanSendMessages) { if (!mCanSendMessages) {
NS_WARNING("Trying to use an invalid GMP video encoder!"); NS_WARNING("Trying to use an invalid GMP video encoder!");
return GMPVideoGenericErr; return GMPGenericErr;
} }
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread()); MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
if (!SendSetPeriodicKeyFrames(aEnable)) { if (!SendSetPeriodicKeyFrames(aEnable)) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
// Async IPC, we don't have access to a return value. // Async IPC, we don't have access to a return value.
return GMPVideoNoErr; return GMPNoErr;
} }
// Note: Consider keeping ActorDestroy sync'd up when making changes here. // Note: Consider keeping ActorDestroy sync'd up when making changes here.
@ -210,9 +195,15 @@ GMPVideoEncoderParent::ActorDestroy(ActorDestroyReason aWhy)
mVideoHost.ActorDestroyed(); mVideoHost.ActorDestroyed();
} }
void
GMPVideoEncoderParent::CheckThread()
{
MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
}
bool bool
GMPVideoEncoderParent::RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame, GMPVideoEncoderParent::RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame,
const GMPCodecSpecificInfo& aCodecSpecificInfo) const nsTArray<uint8_t>& aCodecSpecificInfo)
{ {
if (!mCallback) { if (!mCallback) {
return false; return false;
@ -223,6 +214,35 @@ GMPVideoEncoderParent::RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame
// Ignore any return code. It is OK for this to fail without killing the process. // Ignore any return code. It is OK for this to fail without killing the process.
mCallback->Encoded(f, aCodecSpecificInfo); mCallback->Encoded(f, aCodecSpecificInfo);
// Return SHM to sender to recycle
//SendEncodedReturn(aEncodedFrame, aCodecSpecificInfo);
return true;
}
bool
GMPVideoEncoderParent::RecvParentShmemForPool(Shmem& aFrameBuffer)
{
if (aFrameBuffer.IsWritable()) {
mVideoHost.SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPFrameData,
aFrameBuffer);
}
return true;
}
bool
GMPVideoEncoderParent::AnswerNeedShmem(const uint32_t& aEncodedBufferSize,
Shmem* aMem)
{
ipc::Shmem mem;
if (!mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPEncodedData,
aEncodedBufferSize,
ipc::SharedMemory::TYPE_BASIC, &mem))
{
return false;
}
*aMem = mem;
mem = ipc::Shmem();
return true; return true;
} }

View File

@ -12,13 +12,14 @@
#include "GMPMessageUtils.h" #include "GMPMessageUtils.h"
#include "GMPSharedMemManager.h" #include "GMPSharedMemManager.h"
#include "GMPVideoHost.h" #include "GMPVideoHost.h"
#include "GMPVideoEncoderProxy.h"
namespace mozilla { namespace mozilla {
namespace gmp { namespace gmp {
class GMPParent; class GMPParent;
class GMPVideoEncoderParent : public GMPVideoEncoder, class GMPVideoEncoderParent : public GMPVideoEncoderProxy,
public PGMPVideoEncoderParent, public PGMPVideoEncoderParent,
public GMPSharedMemManager public GMPSharedMemManager
{ {
@ -29,37 +30,50 @@ public:
GMPVideoHostImpl& Host(); GMPVideoHostImpl& Host();
// GMPSharedMemManager // GMPVideoEncoderProxy
virtual bool MgrAllocShmem(size_t aSize, virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings,
ipc::Shmem::SharedMemory::SharedMemoryType aType, const nsTArray<uint8_t>& aCodecSpecific,
ipc::Shmem* aMem) MOZ_OVERRIDE; GMPVideoEncoderCallbackProxy* aCallback,
virtual bool MgrDeallocShmem(Shmem& aMem) MOZ_OVERRIDE; int32_t aNumberOfCores,
uint32_t aMaxPayloadSize) MOZ_OVERRIDE;
// GMPVideoEncoder virtual GMPErr Encode(GMPVideoi420Frame* aInputFrame,
virtual GMPVideoErr InitEncode(const GMPVideoCodec& aCodecSettings, const nsTArray<uint8_t>& aCodecSpecificInfo,
GMPEncoderCallback* aCallback, const nsTArray<GMPVideoFrameType>& aFrameTypes) MOZ_OVERRIDE;
int32_t aNumberOfCores, virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) MOZ_OVERRIDE;
uint32_t aMaxPayloadSize) MOZ_OVERRIDE; virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) MOZ_OVERRIDE;
virtual GMPVideoErr Encode(GMPVideoi420Frame* aInputFrame, virtual GMPErr SetPeriodicKeyFrames(bool aEnable) MOZ_OVERRIDE;
const GMPCodecSpecificInfo& aCodecSpecificInfo,
const std::vector<GMPVideoFrameType>& aFrameTypes) MOZ_OVERRIDE;
virtual GMPVideoErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) MOZ_OVERRIDE;
virtual GMPVideoErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) MOZ_OVERRIDE;
virtual GMPVideoErr SetPeriodicKeyFrames(bool aEnable) MOZ_OVERRIDE;
virtual void EncodingComplete() MOZ_OVERRIDE; virtual void EncodingComplete() MOZ_OVERRIDE;
// GMPSharedMemManager
virtual void CheckThread();
virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem)
{
#ifdef GMP_SAFE_SHMEM
return AllocShmem(aSize, aType, aMem);
#else
return AllocUnsafeShmem(aSize, aType, aMem);
#endif
}
virtual void Dealloc(Shmem& aMem)
{
DeallocShmem(aMem);
}
private: private:
virtual ~GMPVideoEncoderParent(); virtual ~GMPVideoEncoderParent();
// PGMPVideoEncoderParent // PGMPVideoEncoderParent
virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE; virtual void ActorDestroy(ActorDestroyReason aWhy) MOZ_OVERRIDE;
virtual bool RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame, virtual bool RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame,
const GMPCodecSpecificInfo& aCodecSpecificInfo) MOZ_OVERRIDE; const nsTArray<uint8_t>& aCodecSpecificInfo) MOZ_OVERRIDE;
virtual bool RecvParentShmemForPool(Shmem& aFrameBuffer) MOZ_OVERRIDE;
virtual bool AnswerNeedShmem(const uint32_t& aEncodedBufferSize,
Shmem* aMem) MOZ_OVERRIDE;
virtual bool Recv__delete__() MOZ_OVERRIDE; virtual bool Recv__delete__() MOZ_OVERRIDE;
bool mCanSendMessages; bool mCanSendMessages;
GMPParent* mPlugin; GMPParent* mPlugin;
GMPEncoderCallback* mCallback; GMPVideoEncoderCallbackProxy* mCallback;
GMPVideoHostImpl mVideoHost; GMPVideoHostImpl mVideoHost;
}; };

View File

@ -0,0 +1,40 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GMPVideoEncoderProxy_h_
#define GMPVideoEncoderProxy_h_
#include "nsTArray.h"
#include "gmp-video-encode.h"
#include "gmp-video-frame-i420.h"
#include "gmp-video-frame-encoded.h"
class GMPVideoEncoderCallbackProxy {
public:
virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
const nsTArray<uint8_t>& aCodecSpecificInfo) = 0;
};
// A proxy to GMPVideoEncoder in the child process.
// GMPVideoEncoderParent exposes this to users the GMP.
// This enables Gecko to pass nsTArrays to the child GMP and avoid
// an extra copy when doing so.
class GMPVideoEncoderProxy {
public:
virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings,
const nsTArray<uint8_t>& aCodecSpecific,
GMPVideoEncoderCallbackProxy* aCallback,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize) = 0;
virtual GMPErr Encode(GMPVideoi420Frame* aInputFrame,
const nsTArray<uint8_t>& aCodecSpecificInfo,
const nsTArray<GMPVideoFrameType>& aFrameTypes) = 0;
virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) = 0;
virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) = 0;
virtual GMPErr SetPeriodicKeyFrames(bool aEnable) = 0;
virtual void EncodingComplete() = 0;
};
#endif // GMPVideoEncoderProxy_h_

View File

@ -20,41 +20,41 @@ GMPVideoHostImpl::~GMPVideoHostImpl()
{ {
} }
GMPVideoErr GMPErr
GMPVideoHostImpl::CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame) GMPVideoHostImpl::CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame)
{ {
if (!mSharedMemMgr) { if (!mSharedMemMgr) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
if (!aFrame) { if (!aFrame) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
*aFrame = nullptr; *aFrame = nullptr;
switch (aFormat) { switch (aFormat) {
case kGMPI420VideoFrame: case kGMPI420VideoFrame:
*aFrame = new GMPVideoi420FrameImpl(this); *aFrame = new GMPVideoi420FrameImpl(this);
return GMPVideoNoErr; return GMPNoErr;
case kGMPEncodedVideoFrame: case kGMPEncodedVideoFrame:
*aFrame = new GMPVideoEncodedFrameImpl(this); *aFrame = new GMPVideoEncodedFrameImpl(this);
return GMPVideoNoErr; return GMPNoErr;
default: default:
NS_NOTREACHED("Unknown frame format!"); NS_NOTREACHED("Unknown frame format!");
} }
return GMPVideoGenericErr; return GMPGenericErr;
} }
GMPVideoErr GMPErr
GMPVideoHostImpl::CreatePlane(GMPPlane** aPlane) GMPVideoHostImpl::CreatePlane(GMPPlane** aPlane)
{ {
if (!mSharedMemMgr) { if (!mSharedMemMgr) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
if (!aPlane) { if (!aPlane) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
*aPlane = nullptr; *aPlane = nullptr;
@ -62,7 +62,7 @@ GMPVideoHostImpl::CreatePlane(GMPPlane** aPlane)
*aPlane = p; *aPlane = p;
return GMPVideoNoErr; return GMPNoErr;
} }
GMPSharedMemManager* GMPSharedMemManager*

View File

@ -35,8 +35,8 @@ public:
void EncodedFrameDestroyed(GMPVideoEncodedFrameImpl* aFrame); void EncodedFrameDestroyed(GMPVideoEncodedFrameImpl* aFrame);
// GMPVideoHost // GMPVideoHost
virtual GMPVideoErr CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame) MOZ_OVERRIDE; virtual GMPErr CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame) MOZ_OVERRIDE;
virtual GMPVideoErr CreatePlane(GMPPlane** aPlane) MOZ_OVERRIDE; virtual GMPErr CreatePlane(GMPPlane** aPlane) MOZ_OVERRIDE;
private: private:
// All shared memory allocations have to be made by an IPDL actor. // All shared memory allocations have to be made by an IPDL actor.

View File

@ -73,20 +73,21 @@ GMPPlaneImpl::InitPlaneData(GMPPlaneData& aPlaneData)
return true; return true;
} }
GMPVideoErr GMPErr
GMPPlaneImpl::MaybeResize(int32_t aNewSize) { GMPPlaneImpl::MaybeResize(int32_t aNewSize) {
if (aNewSize <= AllocatedSize()) { if (aNewSize <= AllocatedSize()) {
return GMPVideoNoErr; return GMPNoErr;
} }
if (!mHost) { if (!mHost) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
ipc::Shmem new_mem; ipc::Shmem new_mem;
if (!mHost->SharedMemMgr()->MgrAllocShmem(aNewSize, ipc::SharedMemory::TYPE_BASIC, &new_mem) || if (!mHost->SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPFrameData, aNewSize,
ipc::SharedMemory::TYPE_BASIC, &new_mem) ||
!new_mem.get<uint8_t>()) { !new_mem.get<uint8_t>()) {
return GMPVideoAllocErr; return GMPAllocErr;
} }
if (mBuffer.IsReadable()) { if (mBuffer.IsReadable()) {
@ -97,43 +98,43 @@ GMPPlaneImpl::MaybeResize(int32_t aNewSize) {
mBuffer = new_mem; mBuffer = new_mem;
return GMPVideoNoErr; return GMPNoErr;
} }
void void
GMPPlaneImpl::DestroyBuffer() GMPPlaneImpl::DestroyBuffer()
{ {
if (mHost && mBuffer.IsWritable()) { if (mHost && mBuffer.IsWritable()) {
mHost->SharedMemMgr()->MgrDeallocShmem(mBuffer); mHost->SharedMemMgr()->MgrDeallocShmem(GMPSharedMemManager::kGMPFrameData, mBuffer);
} }
mBuffer = ipc::Shmem(); mBuffer = ipc::Shmem();
} }
GMPVideoErr GMPErr
GMPPlaneImpl::CreateEmptyPlane(int32_t aAllocatedSize, int32_t aStride, int32_t aPlaneSize) GMPPlaneImpl::CreateEmptyPlane(int32_t aAllocatedSize, int32_t aStride, int32_t aPlaneSize)
{ {
if (aAllocatedSize < 1 || aStride < 1 || aPlaneSize < 1) { if (aAllocatedSize < 1 || aStride < 1 || aPlaneSize < 1) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
GMPVideoErr err = MaybeResize(aAllocatedSize); GMPErr err = MaybeResize(aAllocatedSize);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
mSize = aPlaneSize; mSize = aPlaneSize;
mStride = aStride; mStride = aStride;
return GMPVideoNoErr; return GMPNoErr;
} }
GMPVideoErr GMPErr
GMPPlaneImpl::Copy(const GMPPlane& aPlane) GMPPlaneImpl::Copy(const GMPPlane& aPlane)
{ {
auto& planeimpl = static_cast<const GMPPlaneImpl&>(aPlane); auto& planeimpl = static_cast<const GMPPlaneImpl&>(aPlane);
GMPVideoErr err = MaybeResize(planeimpl.mSize); GMPErr err = MaybeResize(planeimpl.mSize);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
@ -144,14 +145,14 @@ GMPPlaneImpl::Copy(const GMPPlane& aPlane)
mSize = planeimpl.mSize; mSize = planeimpl.mSize;
mStride = planeimpl.mStride; mStride = planeimpl.mStride;
return GMPVideoNoErr; return GMPNoErr;
} }
GMPVideoErr GMPErr
GMPPlaneImpl::Copy(int32_t aSize, int32_t aStride, const uint8_t* aBuffer) GMPPlaneImpl::Copy(int32_t aSize, int32_t aStride, const uint8_t* aBuffer)
{ {
GMPVideoErr err = MaybeResize(aSize); GMPErr err = MaybeResize(aSize);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
@ -162,7 +163,7 @@ GMPPlaneImpl::Copy(int32_t aSize, int32_t aStride, const uint8_t* aBuffer)
mSize = aSize; mSize = aSize;
mStride = aStride; mStride = aStride;
return GMPVideoNoErr; return GMPNoErr;
} }
void void

View File

@ -34,13 +34,13 @@ public:
bool InitPlaneData(GMPPlaneData& aPlaneData); bool InitPlaneData(GMPPlaneData& aPlaneData);
// GMPPlane // GMPPlane
virtual GMPVideoErr CreateEmptyPlane(int32_t aAllocatedSize, virtual GMPErr CreateEmptyPlane(int32_t aAllocatedSize,
int32_t aStride, int32_t aStride,
int32_t aPlaneSize) MOZ_OVERRIDE; int32_t aPlaneSize) MOZ_OVERRIDE;
virtual GMPVideoErr Copy(const GMPPlane& aPlane) MOZ_OVERRIDE; virtual GMPErr Copy(const GMPPlane& aPlane) MOZ_OVERRIDE;
virtual GMPVideoErr Copy(int32_t aSize, virtual GMPErr Copy(int32_t aSize,
int32_t aStride, int32_t aStride,
const uint8_t* aBuffer) MOZ_OVERRIDE; const uint8_t* aBuffer) MOZ_OVERRIDE;
virtual void Swap(GMPPlane& aPlane) MOZ_OVERRIDE; virtual void Swap(GMPPlane& aPlane) MOZ_OVERRIDE;
virtual int32_t AllocatedSize() const MOZ_OVERRIDE; virtual int32_t AllocatedSize() const MOZ_OVERRIDE;
virtual void ResetSize() MOZ_OVERRIDE; virtual void ResetSize() MOZ_OVERRIDE;
@ -51,7 +51,7 @@ public:
virtual void Destroy() MOZ_OVERRIDE; virtual void Destroy() MOZ_OVERRIDE;
private: private:
GMPVideoErr MaybeResize(int32_t aNewSize); GMPErr MaybeResize(int32_t aNewSize);
void DestroyBuffer(); void DestroyBuffer();
ipc::Shmem mBuffer; ipc::Shmem mBuffer;

View File

@ -15,8 +15,8 @@ GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(GMPVideoHostImpl* aHost)
mVPlane(aHost), mVPlane(aHost),
mWidth(0), mWidth(0),
mHeight(0), mHeight(0),
mTimestamp(0), mTimestamp(0ll),
mRenderTime_ms(0) mDuration(0ll)
{ {
MOZ_ASSERT(aHost); MOZ_ASSERT(aHost);
} }
@ -29,7 +29,7 @@ GMPVideoi420FrameImpl::GMPVideoi420FrameImpl(const GMPVideoi420FrameData& aFrame
mWidth(aFrameData.mWidth()), mWidth(aFrameData.mWidth()),
mHeight(aFrameData.mHeight()), mHeight(aFrameData.mHeight()),
mTimestamp(aFrameData.mTimestamp()), mTimestamp(aFrameData.mTimestamp()),
mRenderTime_ms(aFrameData.mRenderTime_ms()) mDuration(aFrameData.mDuration())
{ {
MOZ_ASSERT(aHost); MOZ_ASSERT(aHost);
} }
@ -47,7 +47,7 @@ GMPVideoi420FrameImpl::InitFrameData(GMPVideoi420FrameData& aFrameData)
aFrameData.mWidth() = mWidth; aFrameData.mWidth() = mWidth;
aFrameData.mHeight() = mHeight; aFrameData.mHeight() = mHeight;
aFrameData.mTimestamp() = mTimestamp; aFrameData.mTimestamp() = mTimestamp;
aFrameData.mRenderTime_ms() = mRenderTime_ms; aFrameData.mDuration() = mDuration;
return true; return true;
} }
@ -106,12 +106,12 @@ GMPVideoi420FrameImpl::GetPlane(GMPPlaneType aType) {
return nullptr; return nullptr;
} }
GMPVideoErr GMPErr
GMPVideoi420FrameImpl::CreateEmptyFrame(int32_t aWidth, int32_t aHeight, GMPVideoi420FrameImpl::CreateEmptyFrame(int32_t aWidth, int32_t aHeight,
int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) int32_t aStride_y, int32_t aStride_u, int32_t aStride_v)
{ {
if (!CheckDimensions(aWidth, aHeight, aStride_y, aStride_u, aStride_v)) { if (!CheckDimensions(aWidth, aHeight, aStride_y, aStride_u, aStride_v)) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
int32_t size_y = aStride_y * aHeight; int32_t size_y = aStride_y * aHeight;
@ -119,28 +119,28 @@ GMPVideoi420FrameImpl::CreateEmptyFrame(int32_t aWidth, int32_t aHeight,
int32_t size_u = aStride_u * half_height; int32_t size_u = aStride_u * half_height;
int32_t size_v = aStride_v * half_height; int32_t size_v = aStride_v * half_height;
GMPVideoErr err = mYPlane.CreateEmptyPlane(size_y, aStride_y, size_y); GMPErr err = mYPlane.CreateEmptyPlane(size_y, aStride_y, size_y);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
err = mUPlane.CreateEmptyPlane(size_u, aStride_u, size_u); err = mUPlane.CreateEmptyPlane(size_u, aStride_u, size_u);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
err = mVPlane.CreateEmptyPlane(size_v, aStride_v, size_v); err = mVPlane.CreateEmptyPlane(size_v, aStride_v, size_v);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
mWidth = aWidth; mWidth = aWidth;
mHeight = aHeight; mHeight = aHeight;
mTimestamp = 0; mTimestamp = 0ll;
mRenderTime_ms = 0; mDuration = 0ll;
return GMPVideoNoErr; return GMPNoErr;
} }
GMPVideoErr GMPErr
GMPVideoi420FrameImpl::CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y, GMPVideoi420FrameImpl::CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y,
int32_t aSize_u, const uint8_t* aBuffer_u, int32_t aSize_u, const uint8_t* aBuffer_u,
int32_t aSize_v, const uint8_t* aBuffer_v, int32_t aSize_v, const uint8_t* aBuffer_v,
@ -152,58 +152,58 @@ GMPVideoi420FrameImpl::CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y,
MOZ_ASSERT(aBuffer_v); MOZ_ASSERT(aBuffer_v);
if (aSize_y < 1 || aSize_u < 1 || aSize_v < 1) { if (aSize_y < 1 || aSize_u < 1 || aSize_v < 1) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
if (!CheckDimensions(aWidth, aHeight, aStride_y, aStride_u, aStride_v)) { if (!CheckDimensions(aWidth, aHeight, aStride_y, aStride_u, aStride_v)) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
GMPVideoErr err = mYPlane.Copy(aSize_y, aStride_y, aBuffer_y); GMPErr err = mYPlane.Copy(aSize_y, aStride_y, aBuffer_y);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
err = mUPlane.Copy(aSize_u, aStride_u, aBuffer_u); err = mUPlane.Copy(aSize_u, aStride_u, aBuffer_u);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
err = mVPlane.Copy(aSize_v, aStride_v, aBuffer_v); err = mVPlane.Copy(aSize_v, aStride_v, aBuffer_v);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
mWidth = aWidth; mWidth = aWidth;
mHeight = aHeight; mHeight = aHeight;
return GMPVideoNoErr; return GMPNoErr;
} }
GMPVideoErr GMPErr
GMPVideoi420FrameImpl::CopyFrame(const GMPVideoi420Frame& aFrame) GMPVideoi420FrameImpl::CopyFrame(const GMPVideoi420Frame& aFrame)
{ {
auto& f = static_cast<const GMPVideoi420FrameImpl&>(aFrame); auto& f = static_cast<const GMPVideoi420FrameImpl&>(aFrame);
GMPVideoErr err = mYPlane.Copy(f.mYPlane); GMPErr err = mYPlane.Copy(f.mYPlane);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
err = mUPlane.Copy(f.mUPlane); err = mUPlane.Copy(f.mUPlane);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
err = mVPlane.Copy(f.mVPlane); err = mVPlane.Copy(f.mVPlane);
if (err != GMPVideoNoErr) { if (err != GMPNoErr) {
return err; return err;
} }
mWidth = f.mWidth; mWidth = f.mWidth;
mHeight = f.mHeight; mHeight = f.mHeight;
mTimestamp = f.mTimestamp; mTimestamp = f.mTimestamp;
mRenderTime_ms = f.mRenderTime_ms; mDuration = f.mDuration;
return GMPVideoNoErr; return GMPNoErr;
} }
void void
@ -216,7 +216,7 @@ GMPVideoi420FrameImpl::SwapFrame(GMPVideoi420Frame* aFrame)
std::swap(mWidth, f->mWidth); std::swap(mWidth, f->mWidth);
std::swap(mHeight, f->mHeight); std::swap(mHeight, f->mHeight);
std::swap(mTimestamp, f->mTimestamp); std::swap(mTimestamp, f->mTimestamp);
std::swap(mRenderTime_ms, f->mRenderTime_ms); std::swap(mDuration, f->mDuration);
} }
uint8_t* uint8_t*
@ -259,28 +259,28 @@ GMPVideoi420FrameImpl::Stride(GMPPlaneType aType) const
return -1; return -1;
} }
GMPVideoErr GMPErr
GMPVideoi420FrameImpl::SetWidth(int32_t aWidth) GMPVideoi420FrameImpl::SetWidth(int32_t aWidth)
{ {
if (!CheckDimensions(aWidth, mHeight, if (!CheckDimensions(aWidth, mHeight,
mYPlane.Stride(), mUPlane.Stride(), mYPlane.Stride(), mUPlane.Stride(),
mVPlane.Stride())) { mVPlane.Stride())) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
mWidth = aWidth; mWidth = aWidth;
return GMPVideoNoErr; return GMPNoErr;
} }
GMPVideoErr GMPErr
GMPVideoi420FrameImpl::SetHeight(int32_t aHeight) GMPVideoi420FrameImpl::SetHeight(int32_t aHeight)
{ {
if (!CheckDimensions(mWidth, aHeight, if (!CheckDimensions(mWidth, aHeight,
mYPlane.Stride(), mUPlane.Stride(), mYPlane.Stride(), mUPlane.Stride(),
mVPlane.Stride())) { mVPlane.Stride())) {
return GMPVideoGenericErr; return GMPGenericErr;
} }
mHeight = aHeight; mHeight = aHeight;
return GMPVideoNoErr; return GMPNoErr;
} }
int32_t int32_t
@ -296,27 +296,27 @@ GMPVideoi420FrameImpl::Height() const
} }
void void
GMPVideoi420FrameImpl::SetTimestamp(uint32_t aTimestamp) GMPVideoi420FrameImpl::SetTimestamp(uint64_t aTimestamp)
{ {
mTimestamp = aTimestamp; mTimestamp = aTimestamp;
} }
uint32_t uint64_t
GMPVideoi420FrameImpl::Timestamp() const GMPVideoi420FrameImpl::Timestamp() const
{ {
return mTimestamp; return mTimestamp;
} }
void void
GMPVideoi420FrameImpl::SetRenderTime_ms(int64_t aRenderTime_ms) GMPVideoi420FrameImpl::SetDuration(uint64_t aDuration)
{ {
mRenderTime_ms = aRenderTime_ms; mDuration = aDuration;
} }
int64_t uint64_t
GMPVideoi420FrameImpl::RenderTime_ms() const GMPVideoi420FrameImpl::Duration() const
{ {
return mRenderTime_ms; return mDuration;
} }
bool bool

View File

@ -32,33 +32,33 @@ public:
virtual void Destroy() MOZ_OVERRIDE; virtual void Destroy() MOZ_OVERRIDE;
// GMPVideoi420Frame // GMPVideoi420Frame
virtual GMPVideoErr CreateEmptyFrame(int32_t aWidth, virtual GMPErr CreateEmptyFrame(int32_t aWidth,
int32_t aHeight,
int32_t aStride_y,
int32_t aStride_u,
int32_t aStride_v) MOZ_OVERRIDE;
virtual GMPVideoErr CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y,
int32_t aSize_u, const uint8_t* aBuffer_u,
int32_t aSize_v, const uint8_t* aBuffer_v,
int32_t aWidth,
int32_t aHeight, int32_t aHeight,
int32_t aStride_y, int32_t aStride_y,
int32_t aStride_u, int32_t aStride_u,
int32_t aStride_v) MOZ_OVERRIDE; int32_t aStride_v) MOZ_OVERRIDE;
virtual GMPVideoErr CopyFrame(const GMPVideoi420Frame& aFrame) MOZ_OVERRIDE; virtual GMPErr CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y,
int32_t aSize_u, const uint8_t* aBuffer_u,
int32_t aSize_v, const uint8_t* aBuffer_v,
int32_t aWidth,
int32_t aHeight,
int32_t aStride_y,
int32_t aStride_u,
int32_t aStride_v) MOZ_OVERRIDE;
virtual GMPErr CopyFrame(const GMPVideoi420Frame& aFrame) MOZ_OVERRIDE;
virtual void SwapFrame(GMPVideoi420Frame* aFrame) MOZ_OVERRIDE; virtual void SwapFrame(GMPVideoi420Frame* aFrame) MOZ_OVERRIDE;
virtual uint8_t* Buffer(GMPPlaneType aType) MOZ_OVERRIDE; virtual uint8_t* Buffer(GMPPlaneType aType) MOZ_OVERRIDE;
virtual const uint8_t* Buffer(GMPPlaneType aType) const MOZ_OVERRIDE; virtual const uint8_t* Buffer(GMPPlaneType aType) const MOZ_OVERRIDE;
virtual int32_t AllocatedSize(GMPPlaneType aType) const MOZ_OVERRIDE; virtual int32_t AllocatedSize(GMPPlaneType aType) const MOZ_OVERRIDE;
virtual int32_t Stride(GMPPlaneType aType) const MOZ_OVERRIDE; virtual int32_t Stride(GMPPlaneType aType) const MOZ_OVERRIDE;
virtual GMPVideoErr SetWidth(int32_t aWidth) MOZ_OVERRIDE; virtual GMPErr SetWidth(int32_t aWidth) MOZ_OVERRIDE;
virtual GMPVideoErr SetHeight(int32_t aHeight) MOZ_OVERRIDE; virtual GMPErr SetHeight(int32_t aHeight) MOZ_OVERRIDE;
virtual int32_t Width() const MOZ_OVERRIDE; virtual int32_t Width() const MOZ_OVERRIDE;
virtual int32_t Height() const MOZ_OVERRIDE; virtual int32_t Height() const MOZ_OVERRIDE;
virtual void SetTimestamp(uint32_t aTimestamp) MOZ_OVERRIDE; virtual void SetTimestamp(uint64_t aTimestamp) MOZ_OVERRIDE;
virtual uint32_t Timestamp() const MOZ_OVERRIDE; virtual uint64_t Timestamp() const MOZ_OVERRIDE;
virtual void SetRenderTime_ms(int64_t aRenderTime_ms) MOZ_OVERRIDE; virtual void SetDuration(uint64_t aDuration) MOZ_OVERRIDE;
virtual int64_t RenderTime_ms() const MOZ_OVERRIDE; virtual uint64_t Duration() const MOZ_OVERRIDE;
virtual bool IsZeroSize() const MOZ_OVERRIDE; virtual bool IsZeroSize() const MOZ_OVERRIDE;
virtual void ResetSize() MOZ_OVERRIDE; virtual void ResetSize() MOZ_OVERRIDE;
@ -71,8 +71,8 @@ private:
GMPPlaneImpl mVPlane; GMPPlaneImpl mVPlane;
int32_t mWidth; int32_t mWidth;
int32_t mHeight; int32_t mHeight;
uint32_t mTimestamp; uint64_t mTimestamp;
int64_t mRenderTime_ms; uint64_t mDuration;
}; };
} // namespace gmp } // namespace gmp

View File

@ -9,13 +9,13 @@ include protocol PGMPVideoEncoder;
namespace mozilla { namespace mozilla {
namespace gmp { namespace gmp {
async protocol PGMP intr protocol PGMP
{ {
manages PGMPVideoDecoder; manages PGMPVideoDecoder;
manages PGMPVideoEncoder; manages PGMPVideoEncoder;
child: child:
PGMPVideoDecoder(); async PGMPVideoDecoder();
PGMPVideoEncoder(); async PGMPVideoEncoder();
}; };
} // namespace gmp } // namespace gmp

View File

@ -7,32 +7,40 @@ include protocol PGMP;
include GMPTypes; include GMPTypes;
using GMPVideoCodec from "gmp-video-codec.h"; using GMPVideoCodec from "gmp-video-codec.h";
using GMPCodecSpecificInfo from "gmp-video-codec.h";
include "GMPMessageUtils.h"; include "GMPMessageUtils.h";
namespace mozilla { namespace mozilla {
namespace gmp { namespace gmp {
async protocol PGMPVideoDecoder intr protocol PGMPVideoDecoder
{ {
manager PGMP; manager PGMP;
child: child:
InitDecode(GMPVideoCodec aCodecSettings, async InitDecode(GMPVideoCodec aCodecSettings,
int32_t aCoreCount); uint8_t[] aCodecSpecific,
Decode(GMPVideoEncodedFrameData aInputFrame, int32_t aCoreCount);
bool aMissingFrames, async Decode(GMPVideoEncodedFrameData aInputFrame,
GMPCodecSpecificInfo aCodecSpecificInfo, bool aMissingFrames,
int64_t aRenderTimeMs); uint8_t[] aCodecSpecificInfo,
Reset(); int64_t aRenderTimeMs);
Drain(); async Reset();
DecodingComplete(); async Drain();
async DecodingComplete();
async ChildShmemForPool(Shmem aFrameBuffer);
parent: parent:
__delete__(); async __delete__();
Decoded(GMPVideoi420FrameData aDecodedFrame); async Decoded(GMPVideoi420FrameData aDecodedFrame);
ReceivedDecodedReferenceFrame(uint64_t aPictureId); async ReceivedDecodedReferenceFrame(uint64_t aPictureId);
ReceivedDecodedFrame(uint64_t aPictureId); async ReceivedDecodedFrame(uint64_t aPictureId);
InputDataExhausted(); async InputDataExhausted();
async DrainComplete();
async ResetComplete();
async ParentShmemForPool(Shmem aEncodedBuffer);
// MUST be intr - if sync and we create a new Shmem, when the returned
// Shmem is received in the Child it will fail to Deserialize
intr NeedShmem(uint32_t aFrameBufferSize) returns (Shmem aMem);
}; };
} // namespace gmp } // namespace gmp

View File

@ -7,32 +7,38 @@ include protocol PGMP;
include GMPTypes; include GMPTypes;
using GMPVideoCodec from "gmp-video-codec.h"; using GMPVideoCodec from "gmp-video-codec.h";
using GMPCodecSpecificInfo from "gmp-video-codec.h"; using GMPVideoFrameType from "gmp-video-frame-encoded.h";
include "GMPMessageUtils.h"; include "GMPMessageUtils.h";
namespace mozilla { namespace mozilla {
namespace gmp { namespace gmp {
async protocol PGMPVideoEncoder intr protocol PGMPVideoEncoder
{ {
manager PGMP; manager PGMP;
child: child:
InitEncode(GMPVideoCodec aCodecSettings, async InitEncode(GMPVideoCodec aCodecSettings,
int32_t aNumberOfCores, uint8_t[] aCodecSpecific,
uint32_t aMaxPayloadSize); int32_t aNumberOfCores,
Encode(GMPVideoi420FrameData aInputFrame, uint32_t aMaxPayloadSize);
GMPCodecSpecificInfo aCodecSpecificInfo, async Encode(GMPVideoi420FrameData aInputFrame,
int[] aFrameTypes); uint8_t[] aCodecSpecificInfo,
SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT); GMPVideoFrameType[] aFrameTypes);
SetRates(uint32_t aNewBitRate, uint32_t aFrameRate); async SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT);
SetPeriodicKeyFrames(bool aEnable); async SetRates(uint32_t aNewBitRate, uint32_t aFrameRate);
EncodingComplete(); async SetPeriodicKeyFrames(bool aEnable);
async EncodingComplete();
async ChildShmemForPool(Shmem aEncodedBuffer);
parent: parent:
__delete__(); async __delete__();
Encoded(GMPVideoEncodedFrameData aEncodedFrame, async Encoded(GMPVideoEncodedFrameData aEncodedFrame,
GMPCodecSpecificInfo aCodecSpecificInfo); uint8_t[] aCodecSpecificInfo);
async ParentShmemForPool(Shmem aFrameBuffer);
// MUST be intr - if sync and we create a new Shmem, when the returned
// Shmem is received in the Child it will fail to Deserialize
intr NeedShmem(uint32_t aEncodedBufferSize) returns (Shmem aMem);
}; };
} // namespace gmp } // namespace gmp

View File

@ -0,0 +1,52 @@
/*
* Copyright 2013, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GMP_ASYNC_SHUTDOWN_H_
#define GMP_ASYNC_SHUTDOWN_H_
// API exposed by the plugin library to manage asynchronous shutdown.
// Some plugins require special cleanup which may need to make calls
// to host services and wait for async responses.
//
// To enable a plugins to block shutdown until its async shutdown is
// complete, implement the GMPAsyncShutdown interface and return it when
// your plugin's GMPGetAPI function is called with "async-shutdown".
// When your GMPAsyncShutdown's BeginShutdown() implementation is called
// by the GMP host, you should initate your async shutdown process.
// Once you have completed shutdown, call the ShutdownComplete() function
// of the GMPAsyncShutdownHost that is passed as the host argument to the
// GMPGetAPI() call.
//
// Note: Your GMP's GMPShutdown function will still be called after your
// call to ShutdownComplete().
//
// API name: "async-shutdown"
// Host API: GMPAsyncShutdownHost
class GMPAsyncShutdown {
public:
virtual ~GMPAsyncShutdown() {}
virtual void BeginShutdown() = 0;
};
class GMPAsyncShutdownHost {
public:
virtual ~GMPAsyncShutdownHost() {}
virtual void ShutdownComplete() = 0;
};
#endif // GMP_ASYNC_SHUTDOWN_H_

View File

@ -0,0 +1,43 @@
/*
* Copyright 2013, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GMP_AUDIO_CODEC_h_
#define GMP_AUDIO_CODEC_h_
#include <stdint.h>
enum GMPAudioCodecType
{
kGMPAudioCodecAAC,
kGMPAudioCodecVorbis,
kGMPAudioCodecInvalid // Should always be last.
};
struct GMPAudioCodec
{
GMPAudioCodecType mCodecType;
uint32_t mChannelCount;
uint32_t mBitsPerChannel;
uint32_t mSamplesPerSecond;
// Codec extra data, such as vorbis setup header, or
// AAC AudioSpecificConfig.
// These are null/0 if not externally negotiated
const uint8_t* mExtraData;
size_t mExtraDataLen;
};
#endif // GMP_AUDIO_CODEC_h_

View File

@ -0,0 +1,72 @@
/*
* Copyright 2013, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GMP_AUDIO_DECODE_h_
#define GMP_AUDIO_DECODE_h_
#include "gmp-errors.h"
#include "gmp-audio-samples.h"
#include "gmp-audio-codec.h"
#include <stdint.h>
// ALL METHODS MUST BE CALLED ON THE MAIN THREAD
class GMPAudioDecoderCallback
{
public:
virtual ~GMPAudioDecoderCallback() {}
virtual void Decoded(GMPAudioSamples* aDecodedSamples) = 0;
virtual void InputDataExhausted() = 0;
virtual void DrainComplete() = 0;
virtual void ResetComplete() = 0;
};
// ALL METHODS MUST BE CALLED ON THE MAIN THREAD
class GMPAudioDecoder
{
public:
virtual ~GMPAudioDecoder() {}
// aCallback: Subclass should retain reference to it until DecodingComplete
// is called. Do not attempt to delete it, host retains ownership.
// TODO: Pass AudioHost so decoder can create GMPAudioEncodedFrame objects?
virtual GMPErr InitDecode(const GMPAudioCodec& aCodecSettings,
GMPAudioDecoderCallback* aCallback) = 0;
// Decode encoded audio frames (as a part of an audio stream). The decoded
// frames must be returned to the user through the decode complete callback.
virtual GMPErr Decode(GMPAudioSamples* aEncodedSamples) = 0;
// Reset decoder state and prepare for a new call to Decode(...).
// Flushes the decoder pipeline.
// The decoder should enqueue a task to run ResetComplete() on the main
// thread once the reset has finished.
virtual GMPErr Reset() = 0;
// Output decoded frames for any data in the pipeline, regardless of ordering.
// All remaining decoded frames should be immediately returned via callback.
// The decoder should enqueue a task to run DrainComplete() on the main
// thread once the reset has finished.
virtual GMPErr Drain() = 0;
// May free decoder memory.
virtual void DecodingComplete() = 0;
};
#endif // GMP_VIDEO_DECODE_h_

View File

@ -0,0 +1,32 @@
/*
* Copyright 2013, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GMP_AUDIO_HOST_h_
#define GMP_AUDIO_HOST_h_
#include "gmp-errors.h"
#include "gmp-audio-samples.h"
class GMPAudioHost
{
public:
// Construct various Audio API objects. Host does not retain reference,
// caller is owner and responsible for deleting.
virtual GMPErr CreateSamples(GMPAudioFormat aFormat,
GMPAudioSamples** aSamples) = 0;
};
#endif // GMP_AUDIO_HOST_h_

View File

@ -0,0 +1,57 @@
/*
* Copyright 2013, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GMP_AUDIO_FRAME_h_
#define GMP_AUDIO_FRAME_h_
#include <stdint.h>
#include "gmp-errors.h"
#include "gmp-decryption.h"
enum GMPAudioFormat
{
kGMPAudioEncodedSamples, // Raw compressed data, i.e. an AAC/Vorbis packet.
kGMPAudioIS16Samples, // Interleaved int16_t PCM samples.
kGMPAudioSamplesFormatInvalid // Should always be last.
};
class GMPAudioSamples {
public:
// The format of the buffer.
virtual GMPAudioFormat GetFormat() = 0;
virtual void Destroy() = 0;
// MAIN THREAD ONLY
// Buffer size must be exactly what's required to contain all samples in
// the buffer; every byte is assumed to be part of a sample.
virtual GMPErr SetBufferSize(uint32_t aSize) = 0;
// Size of the buffer in bytes.
virtual uint32_t Size() = 0;
// Timestamps are in microseconds, and are the playback start time of the
// first sample in the buffer.
virtual void SetTimeStamp(uint64_t aTimeStamp) = 0;
virtual uint64_t TimeStamp() = 0;
virtual const uint8_t* Buffer() const = 0;
virtual uint8_t* Buffer() = 0;
// Get data describing how this frame is encrypted, or nullptr if the
// buffer is not encrypted.
virtual const GMPEncryptedBufferData* GetDecryptionData() const = 0;
};
#endif // GMP_AUDIO_FRAME_h_

View File

@ -0,0 +1,208 @@
/*
* Copyright 2013, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GMP_DECRYPTION_h_
#define GMP_DECRYPTION_h_
#include "gmp-platform.h"
class GMPEncryptedBufferData {
public:
// Key ID to identify the decryption key.
virtual const uint8_t* KeyId() const = 0;
// Size (in bytes) of |KeyId()|.
virtual uint32_t KeyIdSize() const = 0;
// Initialization vector.
virtual const uint8_t* IV() const = 0;
// Size (in bytes) of |IV|.
virtual uint32_t IVSize() const = 0;
// Number of enties returned by ClearBytes and CipherBytes().
virtual uint32_t NumSubsamples() const = 0;
virtual const uint32_t* ClearBytes() const = 0;
virtual const uint32_t* CipherBytes() const = 0;
};
// These match to the DOMException codes as per:
// http://www.w3.org/TR/dom/#domexception
enum GMPDOMException {
kGMPNoModificationAllowedError = 7,
kGMPNotFoundError = 8,
kGMPNotSupportedError = 9,
kGMPInvalidStateError = 11,
kGMPSyntaxError = 12,
kGMPInvalidModificationError = 13,
kGMPInvalidAccessError = 15,
kGMPSecurityError = 18,
kGMPAbortError = 20,
kGMPQuotaExceededError = 22,
kGMPTimeoutError = 23
};
// Time in milliseconds, as offset from epoch, 1 Jan 1970.
typedef int64_t GMPTimestamp;
class GMPDecryptorCallback {
public:
// Resolves a promise for a session created or loaded.
// Passes the session id to be exposed to JavaScript.
// Must be called before OnSessionMessage().
// aSessionId must be null terminated.
virtual void OnResolveNewSessionPromise(uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdLength) = 0;
// Called to resolve a specified promise with "undefined".
virtual void OnResolvePromise(uint32_t aPromiseId) = 0;
// Called to reject a promise with a DOMException.
// aMessage is logged to the WebConsole.
// aMessage is optional, but if present must be null terminated.
virtual void OnRejectPromise(uint32_t aPromiseId,
GMPDOMException aException,
const char* aMessage,
uint32_t aMessageLength) = 0;
// Called by the CDM when it has a message for session |session_id|.
// Length parameters should not include null termination.
// aSessionId must be null terminated.
virtual void OnSessionMessage(const char* aSessionId,
uint32_t aSessionIdLength,
const uint8_t* aMessage,
uint32_t aMessageLength,
const char* aDestinationURL,
uint32_t aDestinationURLLength) = 0;
// aSessionId must be null terminated.
virtual void OnExpirationChange(const char* aSessionId,
uint32_t aSessionIdLength,
GMPTimestamp aExpiryTime) = 0;
// Called by the GMP when a session is closed. All file IO
// that a session requires should be complete before calling this.
// aSessionId must be null terminated.
virtual void OnSessionClosed(const char* aSessionId,
uint32_t aSessionIdLength) = 0;
// Called by the GMP when an error occurs in a session.
// aSessionId must be null terminated.
// aMessage is logged to the WebConsole.
// aMessage is optional, but if present must be null terminated.
virtual void OnSessionError(const char* aSessionId,
uint32_t aSessionIdLength,
GMPDOMException aException,
uint32_t aSystemCode,
const char* aMessage,
uint32_t aMessageLength) = 0;
virtual void OnKeyIdUsable(const char* aSessionId,
uint32_t aSessionIdLength,
const uint8_t* aKeyId,
uint32_t aKeyIdLength) = 0;
// Marks a key as no longer usable.
// Note: Keys are assumed to be not usable when a session is closed or removed.
virtual void OnKeyIdNotUsable(const char* aSessionId,
uint32_t aSessionIdLength,
const uint8_t* aKeyId,
uint32_t aKeyIdLength) = 0;
};
// Host interface, passed to GetAPIFunc(), with "decrypt".
class GMPDecryptorHost {
public:
// Returns an origin specific string uniquely identifying the device.
// The node id contains a random component, and is consistent between
// plugin instantiations, unless the user clears it.
// Different origins have different node ids.
// The node id pointer returned here remains valid for the until shutdown
// begins.
// *aOutNodeId is null terminated.
virtual void GetNodeId(const char** aOutNodeId,
uint32_t* aOutNodeIdLength) = 0;
virtual void GetSandboxVoucher(const uint8_t** aVoucher,
uint8_t* aVoucherLength) = 0;
virtual void GetPluginVoucher(const uint8_t** aVoucher,
uint8_t* aVoucherLength) = 0;
};
enum GMPSessionType {
kGMPTemporySession = 0,
kGMPPersistentSession = 1
};
// API exposed by plugin library to manage decryption sessions.
// When the Host requests this by calling GMPGetAPIFunc().
//
// API name: "eme-decrypt".
// Host API: GMPDecryptorHost
class GMPDecryptor {
public:
// Sets the callback to use with the decryptor to return results
// to Gecko.
virtual void Init(GMPDecryptorCallback* aCallback) = 0;
// Requests the creation of a session given |aType| and |aInitData|.
// Decryptor should callback GMPDecryptorCallback::OnSessionCreated()
// with the web session ID on success, or OnSessionError() on failure,
// and then call OnSessionReady() once all keys for that session are
// available.
virtual void CreateSession(uint32_t aPromiseId,
const char* aInitDataType,
uint32_t aInitDataTypeSize,
const uint8_t* aInitData,
uint32_t aInitDataSize,
GMPSessionType aSessionType) = 0;
// Loads a previously loaded persistent session.
virtual void LoadSession(uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdLength) = 0;
// Updates the session with |aResponse|.
virtual void UpdateSession(uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdLength,
const uint8_t* aResponse,
uint32_t aResponseSize) = 0;
// Releases the resources (keys) for the specified session.
virtual void CloseSession(uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdLength) = 0;
// Removes the resources (keys) for the specified session.
virtual void RemoveSession(uint32_t aPromiseId,
const char* aSessionId,
uint32_t aSessionIdLength) = 0;
// Resolve/reject promise on completion.
virtual void SetServerCertificate(uint32_t aPromiseId,
const uint8_t* aServerCert,
uint32_t aServerCertSize) = 0;
};
#endif // GMP_DECRYPTION_h_

View File

@ -35,7 +35,16 @@
typedef enum { typedef enum {
GMPNoErr = 0, GMPNoErr = 0,
GMPGenericErr = 1 GMPGenericErr = 1,
GMPClosedErr = 2,
GMPAllocErr = 3,
GMPNotImplementedErr = 4,
GMPNotClosedErr = 5,
GMPQuotaExceededErr = 6,
GMPLastErr // Placeholder, must be last. This enum's values must remain consecutive!
} GMPErr; } GMPErr;
#define GMP_SUCCEEDED(x) ((x) == GMPNoErr)
#define GMP_FAILED(x) ((x) != GMPNoErr)
#endif // GMP_ERRORS_h_ #endif // GMP_ERRORS_h_

View File

@ -34,12 +34,14 @@
#define GMP_PLATFORM_h_ #define GMP_PLATFORM_h_
#include "gmp-errors.h" #include "gmp-errors.h"
#include "gmp-storage.h"
#include <stdint.h> #include <stdint.h>
/* Platform helper API. */ /* Platform helper API. */
class GMPTask { class GMPTask {
public: public:
virtual void Destroy() = 0;
virtual ~GMPTask() {} virtual ~GMPTask() {}
virtual void Run() = 0; virtual void Run() = 0;
}; };
@ -58,10 +60,20 @@ public:
virtual void Release() = 0; virtual void Release() = 0;
}; };
// Time is defined as the number of milliseconds since the
// Epoch (00:00:00 UTC, January 1, 1970).
typedef int64_t GMPTimestamp;
typedef GMPErr (*GMPCreateThreadPtr)(GMPThread** aThread); typedef GMPErr (*GMPCreateThreadPtr)(GMPThread** aThread);
typedef GMPErr (*GMPRunOnMainThreadPtr)(GMPTask* aTask); typedef GMPErr (*GMPRunOnMainThreadPtr)(GMPTask* aTask);
typedef GMPErr (*GMPSyncRunOnMainThreadPtr)(GMPTask* aTask); typedef GMPErr (*GMPSyncRunOnMainThreadPtr)(GMPTask* aTask);
typedef GMPErr (*GMPCreateMutexPtr)(GMPMutex** aMutex); typedef GMPErr (*GMPCreateMutexPtr)(GMPMutex** aMutex);
typedef GMPErr (*GMPCreateRecordPtr)(const char* aRecordName,
uint32_t aRecordNameSize,
GMPRecord** aOutRecord,
GMPRecordClient* aClient);
typedef GMPErr (*GMPSetTimerOnMainThreadPtr)(GMPTask* aTask, int64_t aTimeoutMS);
typedef GMPErr (*GMPGetCurrentTimePtr)(GMPTimestamp* aOutTime);
struct GMPPlatformAPI { struct GMPPlatformAPI {
// Increment the version when things change. Can only add to the struct, // Increment the version when things change. Can only add to the struct,
@ -74,6 +86,9 @@ struct GMPPlatformAPI {
GMPRunOnMainThreadPtr runonmainthread; GMPRunOnMainThreadPtr runonmainthread;
GMPSyncRunOnMainThreadPtr syncrunonmainthread; GMPSyncRunOnMainThreadPtr syncrunonmainthread;
GMPCreateMutexPtr createmutex; GMPCreateMutexPtr createmutex;
GMPCreateRecordPtr createrecord;
GMPSetTimerOnMainThreadPtr settimer;
GMPGetCurrentTimePtr getcurrenttime;
}; };
#endif // GMP_PLATFORM_h_ #endif // GMP_PLATFORM_h_

View File

@ -0,0 +1,90 @@
/*
* Copyright 2013, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GMP_STORAGE_h_
#define GMP_STORAGE_h_
#include "gmp-errors.h"
#include <stdint.h>
// Provides basic per-origin storage for CDMs. GMPRecord instances can be
// retrieved by calling GMPPlatformAPI->openstorage. Multiple GMPRecord
// can be open at once. This interface is asynchronous, with results
// being returned via callbacks to the GMPRecordClient pointer provided
// to the GMPPlatformAPI->openstorage call, on the main thread.
class GMPRecord {
public:
// Opens the record. Calls OnOpenComplete() once the record is open.
// Note: OnReadComplete() is only called if this returns GMPNoErr.
virtual GMPErr Open() = 0;
// Reads the entire contents of the file, and calls
// GMPRecordClient::OnReadComplete() once the operation is complete.
// Note: OnReadComplete() is only called if this returns GMPNoErr.
virtual GMPErr Read() = 0;
// Writes aDataSize bytes of aData into the file, overwritting the contents
// of the file. Overwriting with 0 bytes "deletes" the file.
// Write 0 bytes to "delete" a file.
// Note: OnWriteComplete is only called if this returns GMPNoErr.
virtual GMPErr Write(const uint8_t* aData, uint32_t aDataSize) = 0;
// Closes a file. File must not be used after this is called. Cancels all
// callbacks.
virtual GMPErr Close() = 0;
virtual ~GMPRecord() {}
};
// Callback object that receives the results of GMPRecord calls. Callbacks
// run asynchronously to the GMPRecord call, on the main thread.
class GMPRecordClient {
public:
// Response to a GMPRecord::Open() call with the open |status|.
// aStatus values:
// - GMPNoErr - File opened successfully. File may be empty.
// - GMPFileInUse - There file is in use by another client.
// - GMPGenericErr - Unspecified error.
// Do not use the GMPRecord if aStatus is not GMPNoErr.
virtual void OnOpenComplete(GMPErr aStatus) = 0;
// Response to a GMPRecord::Read() call, where aData is the file contents,
// of length aDataSize.
// aData is only valid for the duration of the call to OnReadComplete.
// Copy it if you want to hang onto it!
// aStatus values:
// - GMPNoErr - File contents read successfully, aDataSize 0 means file
// is empty.
// - GMPFileInUse - There are other operations or clients in use on this file.
// - GMPGenericErr - Unspecified error.
// Do not continue to use the GMPRecord if aStatus is not GMPNoErr.
virtual void OnReadComplete(GMPErr aStatus,
const uint8_t* aData,
uint32_t aDataSize) = 0;
// Response to a GMPRecord::Write() call.
// - GMPNoErr - File contents written successfully.
// - GMPFileInUse - There are other operations or clients in use on this file.
// - GMPGenericErr - Unspecified error. File should be regarded as corrupt.
// Do not continue to use the GMPRecord if aStatus is not GMPNoErr.
virtual void OnWriteComplete(GMPErr aStatus) = 0;
virtual ~GMPRecordClient() {}
};
#endif // GMP_STORAGE_h_

View File

@ -72,39 +72,50 @@ struct GMPVideoCodecVP8
bool mDenoisingOn; bool mDenoisingOn;
bool mErrorConcealmentOn; bool mErrorConcealmentOn;
bool mAutomaticResizeOn; bool mAutomaticResizeOn;
bool mFrameDroppingOn;
int32_t mKeyFrameInterval;
}; };
// H264 specific // H264 specific
struct GMPVideoCodecH264
// Needs to match a binary spec for this structure.
// Note: the mSPS at the end of this structure is variable length.
struct GMPVideoCodecH264AVCC
{ {
uint8_t mProfile; uint8_t mVersion; // == 0x01
uint8_t mProfile; // these 3 are profile_level_id
uint8_t mConstraints; uint8_t mConstraints;
uint8_t mLevel; uint8_t mLevel;
uint8_t mLengthSizeMinusOne; // lower 2 bits (== GMPBufferType-1). Top 6 reserved (1's)
// SPS/PPS will not generally be present for interactive use unless SDP
// parameter-sets are used.
uint8_t mNumSPS; // lower 5 bits; top 5 reserved (1's)
/*** uint8_t mSPS[]; (Not defined due to compiler warnings and warnings-as-errors ...) **/
// Following mNumSPS is a variable number of bytes, which is the SPS and PPS.
// Each SPS == 16 bit size, ("N"), then "N" bytes,
// then uint8_t mNumPPS, then each PPS == 16 bit size ("N"), then "N" bytes.
};
// Codec specific data for H.264 decoding/encoding.
// Cast the "aCodecSpecific" parameter of GMPVideoDecoder::InitDecode() and
// GMPVideoEncoder::InitEncode() to this structure.
struct GMPVideoCodecH264
{
uint8_t mPacketizationMode; // 0 or 1 uint8_t mPacketizationMode; // 0 or 1
bool mFrameDroppingOn; struct GMPVideoCodecH264AVCC mAVCC; // holds a variable-sized struct GMPVideoCodecH264AVCC mAVCC;
int32_t mKeyFrameInterval;
// These are null/0 if not externally negotiated
const uint8_t* mSPSData;
size_t mSPSLen;
const uint8_t* mPPSData;
size_t mPPSLen;
}; };
enum GMPVideoCodecType enum GMPVideoCodecType
{ {
kGMPVideoCodecVP8, kGMPVideoCodecVP8,
// Encoded frames are in AVCC format; NAL length field of 4 bytes, followed
// by frame data. May be multiple NALUs per sample. Codec specific extra data
// is the AVCC extra data (in AVCC format).
kGMPVideoCodecH264, kGMPVideoCodecH264,
kGMPVideoCodecInvalid // Should always be last. kGMPVideoCodecInvalid // Should always be last.
}; };
union GMPVideoCodecUnion
{
GMPVideoCodecVP8 mVP8;
GMPVideoCodecH264 mH264;
};
// Simulcast is when the same stream is encoded multiple times with different // Simulcast is when the same stream is encoded multiple times with different
// settings such as resolution. // settings such as resolution.
struct GMPSimulcastStream struct GMPSimulcastStream
@ -121,11 +132,19 @@ struct GMPSimulcastStream
enum GMPVideoCodecMode { enum GMPVideoCodecMode {
kGMPRealtimeVideo, kGMPRealtimeVideo,
kGMPScreensharing, kGMPScreensharing,
kGMPStreamingVideo,
kGMPCodecModeInvalid // Should always be last. kGMPCodecModeInvalid // Should always be last.
}; };
enum GMPApiVersion {
kGMPVersion32 = 1, // leveraging that V32 had mCodecType first, and only supported H264
kGMPVersion33 = 33,
};
struct GMPVideoCodec struct GMPVideoCodec
{ {
uint32_t mGMPApiVersion;
GMPVideoCodecType mCodecType; GMPVideoCodecType mCodecType;
char mPLName[kGMPPayloadNameSize]; // Must be NULL-terminated! char mPLName[kGMPPayloadNameSize]; // Must be NULL-terminated!
uint32_t mPLType; uint32_t mPLType;
@ -138,7 +157,8 @@ struct GMPVideoCodec
uint32_t mMinBitrate; // kilobits/sec. uint32_t mMinBitrate; // kilobits/sec.
uint32_t mMaxFramerate; uint32_t mMaxFramerate;
GMPVideoCodecUnion mCodecSpecific; bool mFrameDroppingOn;
int32_t mKeyFrameInterval;
uint32_t mQPMax; uint32_t mQPMax;
uint32_t mNumberOfSimulcastStreams; uint32_t mNumberOfSimulcastStreams;
@ -157,6 +177,7 @@ enum GMPBufferType {
GMP_BufferLength16, GMP_BufferLength16,
GMP_BufferLength24, GMP_BufferLength24,
GMP_BufferLength32, GMP_BufferLength32,
GMP_BufferInvalid,
}; };
struct GMPCodecSpecificInfoGeneric { struct GMPCodecSpecificInfoGeneric {
@ -188,6 +209,7 @@ union GMPCodecSpecificInfoUnion
{ {
GMPCodecSpecificInfoGeneric mGeneric; GMPCodecSpecificInfoGeneric mGeneric;
GMPCodecSpecificInfoVP8 mVP8; GMPCodecSpecificInfoVP8 mVP8;
GMPCodecSpecificInfoH264 mH264;
}; };
// Note: if any pointers are added to this struct or its sub-structs, it // Note: if any pointers are added to this struct or its sub-structs, it

View File

@ -34,17 +34,17 @@
#ifndef GMP_VIDEO_DECODE_h_ #ifndef GMP_VIDEO_DECODE_h_
#define GMP_VIDEO_DECODE_h_ #define GMP_VIDEO_DECODE_h_
#include "gmp-video-errors.h" #include "gmp-errors.h"
#include "gmp-video-frame-i420.h" #include "gmp-video-frame-i420.h"
#include "gmp-video-frame-encoded.h" #include "gmp-video-frame-encoded.h"
#include "gmp-video-codec.h" #include "gmp-video-codec.h"
#include <stdint.h> #include <stdint.h>
// ALL METHODS MUST BE CALLED ON THE MAIN THREAD // ALL METHODS MUST BE CALLED ON THE MAIN THREAD
class GMPDecoderCallback class GMPVideoDecoderCallback
{ {
public: public:
virtual ~GMPDecoderCallback() {} virtual ~GMPVideoDecoderCallback() {}
virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) = 0; virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) = 0;
@ -53,6 +53,10 @@ public:
virtual void ReceivedDecodedFrame(const uint64_t aPictureId) = 0; virtual void ReceivedDecodedFrame(const uint64_t aPictureId) = 0;
virtual void InputDataExhausted() = 0; virtual void InputDataExhausted() = 0;
virtual void DrainComplete() = 0;
virtual void ResetComplete() = 0;
}; };
// ALL METHODS MUST BE CALLED ON THE MAIN THREAD // ALL METHODS MUST BE CALLED ON THE MAIN THREAD
@ -61,37 +65,48 @@ class GMPVideoDecoder
public: public:
virtual ~GMPVideoDecoder() {} virtual ~GMPVideoDecoder() {}
// aCallback: Subclass should retain reference to it until DecodingComplete // - aCodecSettings: Details of decoder to create.
// is called. Do not attempt to delete it, host retains ownership. // - aCodecSpecific: codec specific data, cast to a GMPVideoCodecXXX struct
virtual GMPVideoErr InitDecode(const GMPVideoCodec& aCodecSettings, // to get codec specific config data.
GMPDecoderCallback* aCallback, // - aCodecSpecificLength: number of bytes in aCodecSpecific.
int32_t aCoreCount) = 0; // - aCallback: Subclass should retain reference to it until DecodingComplete
// is called. Do not attempt to delete it, host retains ownership.
// aCoreCount: number of CPU cores.
virtual GMPErr InitDecode(const GMPVideoCodec& aCodecSettings,
const uint8_t* aCodecSpecific,
uint32_t aCodecSpecificLength,
GMPVideoDecoderCallback* aCallback,
int32_t aCoreCount) = 0;
// Decode encoded frame (as a part of a video stream). The decoded frame // Decode encoded frame (as a part of a video stream). The decoded frame
// will be returned to the user through the decode complete callback. // will be returned to the user through the decode complete callback.
// //
// inputFrame: Frame to decode. // - aInputFrame: Frame to decode. Call Destroy() on frame when it's decoded.
// // - aMissingFrames: True if one or more frames have been lost since the
// missingFrames: True if one or more frames have been lost since the previous decode call. // previous decode call.
// // - aCodecSpecificInfo : codec specific data, pointer to a
// fragmentation: Specifies where the encoded frame can be split into separate fragments. // GMPCodecSpecificInfo structure appropriate for
// The meaning of fragment is codec specific, but often means that each // this codec type.
// fragment is decodable by itself. // - aCodecSpecificInfoLength : number of bytes in aCodecSpecificInfo
// // - renderTimeMs : System time to render in milliseconds. Only used by
// codecSpecificInfo: Codec-specific data // decoders with internal rendering.
// virtual GMPErr Decode(GMPVideoEncodedFrame* aInputFrame,
// renderTimeMs : System time to render in milliseconds. Only used by decoders with internal bool aMissingFrames,
// rendering. const uint8_t* aCodecSpecificInfo,
virtual GMPVideoErr Decode(GMPVideoEncodedFrame* aInputFrame, uint32_t aCodecSpecificInfoLength,
bool aMissingFrames, int64_t aRenderTimeMs = -1) = 0;
const GMPCodecSpecificInfo& aCodecSpecificInfo,
int64_t aRenderTimeMs = -1) = 0;
// Reset decoder state and prepare for a new call to Decode(...). Flushes the decoder pipeline. // Reset decoder state and prepare for a new call to Decode(...).
virtual GMPVideoErr Reset() = 0; // Flushes the decoder pipeline.
// The decoder should enqueue a task to run ResetComplete() on the main
// thread once the reset has finished.
virtual GMPErr Reset() = 0;
// Output decoded frames for any data in the pipeline, regardless of ordering. // Output decoded frames for any data in the pipeline, regardless of ordering.
virtual GMPVideoErr Drain() = 0; // All remaining decoded frames should be immediately returned via callback.
// The decoder should enqueue a task to run DrainComplete() on the main
// thread once the reset has finished.
virtual GMPErr Drain() = 0;
// May free decoder memory. // May free decoder memory.
virtual void DecodingComplete() = 0; virtual void DecodingComplete() = 0;

View File

@ -37,19 +37,20 @@
#include <vector> #include <vector>
#include <stdint.h> #include <stdint.h>
#include "gmp-video-errors.h" #include "gmp-errors.h"
#include "gmp-video-frame-i420.h" #include "gmp-video-frame-i420.h"
#include "gmp-video-frame-encoded.h" #include "gmp-video-frame-encoded.h"
#include "gmp-video-codec.h" #include "gmp-video-codec.h"
// ALL METHODS MUST BE CALLED ON THE MAIN THREAD // ALL METHODS MUST BE CALLED ON THE MAIN THREAD
class GMPEncoderCallback class GMPVideoEncoderCallback
{ {
public: public:
virtual ~GMPEncoderCallback() {} virtual ~GMPVideoEncoderCallback() {}
virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame, virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
const GMPCodecSpecificInfo& aCodecSpecificInfo) = 0; const uint8_t* aCodecSpecificInfo,
uint32_t aCodecSpecificInfoLength) = 0;
}; };
// ALL METHODS MUST BE CALLED ON THE MAIN THREAD // ALL METHODS MUST BE CALLED ON THE MAIN THREAD
@ -62,26 +63,38 @@ public:
// //
// Input: // Input:
// - codecSettings : Codec settings // - codecSettings : Codec settings
// - aCodecSpecific : codec specific data, pointer to a
// GMPCodecSpecific structure appropriate for
// this codec type.
// - aCodecSpecificLength : number of bytes in aCodecSpecific
// - aCallback: Subclass should retain reference to it until EncodingComplete // - aCallback: Subclass should retain reference to it until EncodingComplete
// is called. Do not attempt to delete it, host retains ownership. // is called. Do not attempt to delete it, host retains ownership.
// - numberOfCores : Number of cores available for the encoder // - aNnumberOfCores : Number of cores available for the encoder
// - maxPayloadSize : The maximum size each payload is allowed // - aMaxPayloadSize : The maximum size each payload is allowed
// to have. Usually MTU - overhead. // to have. Usually MTU - overhead.
virtual GMPVideoErr InitEncode(const GMPVideoCodec& aCodecSettings, virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings,
GMPEncoderCallback* aCallback, const uint8_t* aCodecSpecific,
int32_t aNumberOfCores, uint32_t aCodecSpecificLength,
uint32_t aMaxPayloadSize) = 0; GMPVideoEncoderCallback* aCallback,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize) = 0;
// Encode an I420 frame (as a part of a video stream). The encoded frame // Encode an I420 frame (as a part of a video stream). The encoded frame
// will be returned to the user through the encode complete callback. // will be returned to the user through the encode complete callback.
// //
// Input: // Input:
// - inputFrame : Frame to be encoded // - aInputFrame : Frame to be encoded
// - codecSpecificInfo : Pointer to codec specific data // - aCodecSpecificInfo : codec specific data, pointer to a
// - frame_types : The frame type to encode // GMPCodecSpecificInfo structure appropriate for
virtual GMPVideoErr Encode(GMPVideoi420Frame* aInputFrame, // this codec type.
const GMPCodecSpecificInfo& aCodecSpecificInfo, // - aCodecSpecificInfoLength : number of bytes in aCodecSpecific
const std::vector<GMPVideoFrameType>& aFrameTypes) = 0; // - aFrameTypes : The frame type to encode
// - aFrameTypesLength : The number of elements in aFrameTypes array.
virtual GMPErr Encode(GMPVideoi420Frame* aInputFrame,
const uint8_t* aCodecSpecificInfo,
uint32_t aCodecSpecificInfoLength,
const GMPVideoFrameType* aFrameTypes,
uint32_t aFrameTypesLength) = 0;
// Inform the encoder about the packet loss and round trip time on the // Inform the encoder about the packet loss and round trip time on the
// network used to decide the best pattern and signaling. // network used to decide the best pattern and signaling.
@ -89,19 +102,19 @@ public:
// - packetLoss : Fraction lost (loss rate in percent = // - packetLoss : Fraction lost (loss rate in percent =
// 100 * packetLoss / 255) // 100 * packetLoss / 255)
// - rtt : Round-trip time in milliseconds // - rtt : Round-trip time in milliseconds
virtual GMPVideoErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) = 0; virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) = 0;
// Inform the encoder about the new target bit rate. // Inform the encoder about the new target bit rate.
// //
// - newBitRate : New target bit rate // - newBitRate : New target bit rate
// - frameRate : The target frame rate // - frameRate : The target frame rate
virtual GMPVideoErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) = 0; virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) = 0;
// Use this function to enable or disable periodic key frames. Can be useful for codecs // Use this function to enable or disable periodic key frames. Can be useful for codecs
// which have other ways of stopping error propagation. // which have other ways of stopping error propagation.
// //
// - enable : Enable or disable periodic key frames // - enable : Enable or disable periodic key frames
virtual GMPVideoErr SetPeriodicKeyFrames(bool aEnable) = 0; virtual GMPErr SetPeriodicKeyFrames(bool aEnable) = 0;
// May free Encoder memory. // May free Encoder memory.
virtual void EncodingComplete() = 0; virtual void EncodingComplete() = 0;

View File

@ -1,43 +0,0 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* Copyright (c) 2011, The WebRTC project authors. All rights reserved.
* Copyright (c) 2014, Mozilla
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
** Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
** Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
** Neither the name of Google nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef GMP_VIDEO_ERRORS_h_
#define GMP_VIDEO_ERRORS_h_
enum GMPVideoErr {
GMPVideoNoErr = 0,
GMPVideoGenericErr = 1,
GMPVideoAllocErr = 2
};
#endif // GMP_VIDEO_ERRORS_h_

View File

@ -35,6 +35,9 @@
#define GMP_VIDEO_FRAME_ENCODED_h_ #define GMP_VIDEO_FRAME_ENCODED_h_
#include <stdint.h> #include <stdint.h>
#include "gmp-decryption.h"
#include "gmp-video-frame.h"
#include "gmp-video-codec.h"
enum GMPVideoFrameType enum GMPVideoFrameType
{ {
@ -58,17 +61,22 @@ class GMPVideoEncodedFrame : public GMPVideoFrame
{ {
public: public:
// MAIN THREAD ONLY // MAIN THREAD ONLY
virtual GMPVideoErr CreateEmptyFrame(uint32_t aSize) = 0; virtual GMPErr CreateEmptyFrame(uint32_t aSize) = 0;
// MAIN THREAD ONLY // MAIN THREAD ONLY
virtual GMPVideoErr CopyFrame(const GMPVideoEncodedFrame& aVideoFrame) = 0; virtual GMPErr CopyFrame(const GMPVideoEncodedFrame& aVideoFrame) = 0;
virtual void SetEncodedWidth(uint32_t aEncodedWidth) = 0; virtual void SetEncodedWidth(uint32_t aEncodedWidth) = 0;
virtual uint32_t EncodedWidth() = 0; virtual uint32_t EncodedWidth() = 0;
virtual void SetEncodedHeight(uint32_t aEncodedHeight) = 0; virtual void SetEncodedHeight(uint32_t aEncodedHeight) = 0;
virtual uint32_t EncodedHeight() = 0; virtual uint32_t EncodedHeight() = 0;
virtual void SetTimeStamp(uint32_t aTimeStamp) = 0; // Microseconds
virtual uint32_t TimeStamp() = 0; virtual void SetTimeStamp(uint64_t aTimeStamp) = 0;
virtual void SetCaptureTime(int64_t aCaptureTime) = 0; virtual uint64_t TimeStamp() = 0;
virtual int64_t CaptureTime() = 0; // Set frame duration (microseconds)
// NOTE: next-frame's Timestamp() != this-frame's TimeStamp()+Duration()
// depending on rounding to avoid having to track roundoff errors
// and dropped/missing frames(!) (which may leave a large gap)
virtual void SetDuration(uint64_t aDuration) = 0;
virtual uint64_t Duration() const = 0;
virtual void SetFrameType(GMPVideoFrameType aFrameType) = 0; virtual void SetFrameType(GMPVideoFrameType aFrameType) = 0;
virtual GMPVideoFrameType FrameType() = 0; virtual GMPVideoFrameType FrameType() = 0;
virtual void SetAllocatedSize(uint32_t aNewSize) = 0; virtual void SetAllocatedSize(uint32_t aNewSize) = 0;
@ -79,6 +87,12 @@ public:
virtual bool CompleteFrame() = 0; virtual bool CompleteFrame() = 0;
virtual const uint8_t* Buffer() const = 0; virtual const uint8_t* Buffer() const = 0;
virtual uint8_t* Buffer() = 0; virtual uint8_t* Buffer() = 0;
virtual GMPBufferType BufferType() const = 0;
virtual void SetBufferType(GMPBufferType aBufferType) = 0;
// Get data describing how this frame is encrypted, or nullptr if the
// frame is not encrypted.
virtual const GMPEncryptedBufferData* GetDecryptionData() const = 0;
}; };
#endif // GMP_VIDEO_FRAME_ENCODED_h_ #endif // GMP_VIDEO_FRAME_ENCODED_h_

View File

@ -34,7 +34,7 @@
#ifndef GMP_VIDEO_FRAME_I420_h_ #ifndef GMP_VIDEO_FRAME_I420_h_
#define GMP_VIDEO_FRAME_I420_h_ #define GMP_VIDEO_FRAME_I420_h_
#include "gmp-video-errors.h" #include "gmp-errors.h"
#include "gmp-video-frame.h" #include "gmp-video-frame.h"
#include "gmp-video-plane.h" #include "gmp-video-plane.h"
@ -63,22 +63,22 @@ public:
// on set dimensions - height and plane stride. // on set dimensions - height and plane stride.
// If required size is bigger than the allocated one, new buffers of adequate // If required size is bigger than the allocated one, new buffers of adequate
// size will be allocated. // size will be allocated.
virtual GMPVideoErr CreateEmptyFrame(int32_t aWidth, int32_t aHeight, virtual GMPErr CreateEmptyFrame(int32_t aWidth, int32_t aHeight,
int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) = 0; int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) = 0;
// MAIN THREAD ONLY // MAIN THREAD ONLY
// CreateFrame: Sets the frame's members and buffers. If required size is // CreateFrame: Sets the frame's members and buffers. If required size is
// bigger than allocated one, new buffers of adequate size will be allocated. // bigger than allocated one, new buffers of adequate size will be allocated.
virtual GMPVideoErr CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y, virtual GMPErr CreateFrame(int32_t aSize_y, const uint8_t* aBuffer_y,
int32_t aSize_u, const uint8_t* aBuffer_u, int32_t aSize_u, const uint8_t* aBuffer_u,
int32_t aSize_v, const uint8_t* aBuffer_v, int32_t aSize_v, const uint8_t* aBuffer_v,
int32_t aWidth, int32_t aHeight, int32_t aWidth, int32_t aHeight,
int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) = 0; int32_t aStride_y, int32_t aStride_u, int32_t aStride_v) = 0;
// MAIN THREAD ONLY // MAIN THREAD ONLY
// Copy frame: If required size is bigger than allocated one, new buffers of // Copy frame: If required size is bigger than allocated one, new buffers of
// adequate size will be allocated. // adequate size will be allocated.
virtual GMPVideoErr CopyFrame(const GMPVideoi420Frame& aVideoFrame) = 0; virtual GMPErr CopyFrame(const GMPVideoi420Frame& aVideoFrame) = 0;
// Swap Frame. // Swap Frame.
virtual void SwapFrame(GMPVideoi420Frame* aVideoFrame) = 0; virtual void SwapFrame(GMPVideoi420Frame* aVideoFrame) = 0;
@ -96,10 +96,10 @@ public:
virtual int32_t Stride(GMPPlaneType aType) const = 0; virtual int32_t Stride(GMPPlaneType aType) const = 0;
// Set frame width. // Set frame width.
virtual GMPVideoErr SetWidth(int32_t aWidth) = 0; virtual GMPErr SetWidth(int32_t aWidth) = 0;
// Set frame height. // Set frame height.
virtual GMPVideoErr SetHeight(int32_t aHeight) = 0; virtual GMPErr SetHeight(int32_t aHeight) = 0;
// Get frame width. // Get frame width.
virtual int32_t Width() const = 0; virtual int32_t Width() const = 0;
@ -107,17 +107,20 @@ public:
// Get frame height. // Get frame height.
virtual int32_t Height() const = 0; virtual int32_t Height() const = 0;
// Set frame timestamp (90kHz). // Set frame timestamp (microseconds)
virtual void SetTimestamp(uint32_t aTimestamp) = 0; virtual void SetTimestamp(uint64_t aTimestamp) = 0;
// Get frame timestamp (90kHz). // Get frame timestamp (microseconds)
virtual uint32_t Timestamp() const = 0; virtual uint64_t Timestamp() const = 0;
// Set render time in miliseconds. // Set frame duration (microseconds)
virtual void SetRenderTime_ms(int64_t aRenderTime_ms) = 0; // NOTE: next-frame's Timestamp() != this-frame's TimeStamp()+Duration()
// depending on rounding to avoid having to track roundoff errors
// and dropped/missing frames(!) (which may leave a large gap)
virtual void SetDuration(uint64_t aDuration) = 0;
// Get render time in miliseconds. // Get frame duration (microseconds)
virtual int64_t RenderTime_ms() const = 0; virtual uint64_t Duration() const = 0;
// Return true if underlying plane buffers are of zero size, false if not. // Return true if underlying plane buffers are of zero size, false if not.
virtual bool IsZeroSize() const = 0; virtual bool IsZeroSize() const = 0;

View File

@ -34,7 +34,6 @@
#ifndef GMP_VIDEO_FRAME_h_ #ifndef GMP_VIDEO_FRAME_h_
#define GMP_VIDEO_FRAME_h_ #define GMP_VIDEO_FRAME_h_
#include "gmp-video-errors.h"
#include "gmp-video-plane.h" #include "gmp-video-plane.h"
enum GMPVideoFrameFormat { enum GMPVideoFrameFormat {

View File

@ -34,7 +34,7 @@
#ifndef GMP_VIDEO_HOST_h_ #ifndef GMP_VIDEO_HOST_h_
#define GMP_VIDEO_HOST_h_ #define GMP_VIDEO_HOST_h_
#include "gmp-video-errors.h" #include "gmp-errors.h"
#include "gmp-video-frame-i420.h" #include "gmp-video-frame-i420.h"
#include "gmp-video-frame-encoded.h" #include "gmp-video-frame-encoded.h"
#include "gmp-video-codec.h" #include "gmp-video-codec.h"
@ -44,8 +44,8 @@ class GMPVideoHost
public: public:
// Construct various video API objects. Host does not retain reference, // Construct various video API objects. Host does not retain reference,
// caller is owner and responsible for deleting. // caller is owner and responsible for deleting.
virtual GMPVideoErr CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame) = 0; virtual GMPErr CreateFrame(GMPVideoFrameFormat aFormat, GMPVideoFrame** aFrame) = 0;
virtual GMPVideoErr CreatePlane(GMPPlane** aPlane) = 0; virtual GMPErr CreatePlane(GMPPlane** aPlane) = 0;
}; };
#endif // GMP_VIDEO_HOST_h_ #endif // GMP_VIDEO_HOST_h_

View File

@ -34,7 +34,7 @@
#ifndef GMP_VIDEO_PLANE_h_ #ifndef GMP_VIDEO_PLANE_h_
#define GMP_VIDEO_PLANE_h_ #define GMP_VIDEO_PLANE_h_
#include "gmp-video-errors.h" #include "gmp-errors.h"
#include <stdint.h> #include <stdint.h>
// The implementation backing this interface uses shared memory for the // The implementation backing this interface uses shared memory for the
@ -52,18 +52,18 @@ public:
// CreateEmptyPlane - set allocated size, actual plane size and stride: // CreateEmptyPlane - set allocated size, actual plane size and stride:
// If current size is smaller than current size, then a buffer of sufficient // If current size is smaller than current size, then a buffer of sufficient
// size will be allocated. // size will be allocated.
virtual GMPVideoErr CreateEmptyPlane(int32_t aAllocatedSize, virtual GMPErr CreateEmptyPlane(int32_t aAllocatedSize,
int32_t aStride, int32_t aStride,
int32_t aPlaneSize) = 0; int32_t aPlaneSize) = 0;
// MAIN THREAD ONLY // MAIN THREAD ONLY
// Copy the entire plane data. // Copy the entire plane data.
virtual GMPVideoErr Copy(const GMPPlane& aPlane) = 0; virtual GMPErr Copy(const GMPPlane& aPlane) = 0;
// MAIN THREAD ONLY // MAIN THREAD ONLY
// Copy buffer: If current size is smaller // Copy buffer: If current size is smaller
// than current size, then a buffer of sufficient size will be allocated. // than current size, then a buffer of sufficient size will be allocated.
virtual GMPVideoErr Copy(int32_t aSize, int32_t aStride, const uint8_t* aBuffer) = 0; virtual GMPErr Copy(int32_t aSize, int32_t aStride, const uint8_t* aBuffer) = 0;
// Swap plane data. // Swap plane data.
virtual void Swap(GMPPlane& aPlane) = 0; virtual void Swap(GMPPlane& aPlane) = 0;

View File

@ -11,13 +11,19 @@ XPIDL_SOURCES += [
] ]
EXPORTS += [ EXPORTS += [
'gmp-api/gmp-async-shutdown.h',
'gmp-api/gmp-audio-codec.h',
'gmp-api/gmp-audio-decode.h',
'gmp-api/gmp-audio-host.h',
'gmp-api/gmp-audio-samples.h',
'gmp-api/gmp-decryption.h',
'gmp-api/gmp-entrypoints.h', 'gmp-api/gmp-entrypoints.h',
'gmp-api/gmp-errors.h', 'gmp-api/gmp-errors.h',
'gmp-api/gmp-platform.h', 'gmp-api/gmp-platform.h',
'gmp-api/gmp-storage.h',
'gmp-api/gmp-video-codec.h', 'gmp-api/gmp-video-codec.h',
'gmp-api/gmp-video-decode.h', 'gmp-api/gmp-video-decode.h',
'gmp-api/gmp-video-encode.h', 'gmp-api/gmp-video-encode.h',
'gmp-api/gmp-video-errors.h',
'gmp-api/gmp-video-frame-encoded.h', 'gmp-api/gmp-video-frame-encoded.h',
'gmp-api/gmp-video-frame-i420.h', 'gmp-api/gmp-video-frame-i420.h',
'gmp-api/gmp-video-frame.h', 'gmp-api/gmp-video-frame.h',
@ -33,9 +39,11 @@ EXPORTS += [
'GMPSharedMemManager.h', 'GMPSharedMemManager.h',
'GMPVideoDecoderChild.h', 'GMPVideoDecoderChild.h',
'GMPVideoDecoderParent.h', 'GMPVideoDecoderParent.h',
'GMPVideoDecoderProxy.h',
'GMPVideoEncodedFrameImpl.h', 'GMPVideoEncodedFrameImpl.h',
'GMPVideoEncoderChild.h', 'GMPVideoEncoderChild.h',
'GMPVideoEncoderParent.h', 'GMPVideoEncoderParent.h',
'GMPVideoEncoderProxy.h',
'GMPVideoHost.h', 'GMPVideoHost.h',
'GMPVideoi420FrameImpl.h', 'GMPVideoi420FrameImpl.h',
'GMPVideoPlaneImpl.h', 'GMPVideoPlaneImpl.h',
@ -48,6 +56,7 @@ UNIFIED_SOURCES += [
'GMPProcessChild.cpp', 'GMPProcessChild.cpp',
'GMPProcessParent.cpp', 'GMPProcessParent.cpp',
'GMPService.cpp', 'GMPService.cpp',
'GMPSharedMemManager.cpp',
'GMPVideoDecoderChild.cpp', 'GMPVideoDecoderChild.cpp',
'GMPVideoDecoderParent.cpp', 'GMPVideoDecoderParent.cpp',
'GMPVideoEncodedFrameImpl.cpp', 'GMPVideoEncodedFrameImpl.cpp',
@ -70,6 +79,9 @@ LIBRARY_NAME = 'mozgmp'
if CONFIG['GKMEDIAS_SHARED_LIBRARY']: if CONFIG['GKMEDIAS_SHARED_LIBRARY']:
NO_VISIBILITY_FLAGS = True NO_VISIBILITY_FLAGS = True
# comment this out to use Unsafe Shmem for more performance
DEFINES['GMP_SAFE_SHMEM'] = True
FAIL_ON_WARNINGS = True FAIL_ON_WARNINGS = True
include('/ipc/chromium/chromium-config.mozbuild') include('/ipc/chromium/chromium-config.mozbuild')

View File

@ -10,18 +10,18 @@
%{C++ %{C++
#include "nsTArray.h" #include "nsTArray.h"
#include "nsStringGlue.h" #include "nsStringGlue.h"
class GMPVideoDecoder; class GMPVideoDecoderProxy;
class GMPVideoEncoder; class GMPVideoEncoderProxy;
class GMPVideoHost; class GMPVideoHost;
%} %}
[ptr] native GMPVideoDecoder(GMPVideoDecoder); [ptr] native GMPVideoDecoderProxy(GMPVideoDecoderProxy);
[ptr] native GMPVideoEncoder(GMPVideoEncoder); [ptr] native GMPVideoEncoderProxy(GMPVideoEncoderProxy);
[ptr] native GMPVideoHost(GMPVideoHost); [ptr] native GMPVideoHost(GMPVideoHost);
[ptr] native MessageLoop(MessageLoop); [ptr] native MessageLoop(MessageLoop);
[ptr] native TagArray(nsTArray<nsCString>); [ptr] native TagArray(nsTArray<nsCString>);
[scriptable, uuid(63fc797f-9d01-43f4-8b93-5b1fe713c2f8)] [scriptable, uuid(7cef50ca-7a0f-41f2-9560-47abf709f0d7)]
interface mozIGeckoMediaPluginService : nsISupports interface mozIGeckoMediaPluginService : nsISupports
{ {
/** /**
@ -36,9 +36,9 @@ interface mozIGeckoMediaPluginService : nsISupports
* Callable only on GMP thread. * Callable only on GMP thread.
*/ */
[noscript] [noscript]
GMPVideoDecoder getGMPVideoDecoder(in TagArray tags, GMPVideoDecoderProxy getGMPVideoDecoder(in TagArray tags,
[optional] in AString origin, [optional] in AString origin,
out GMPVideoHost outVideoHost); out GMPVideoHost outVideoHost);
/** /**
* Get a video encoder that supports the specified tags. * Get a video encoder that supports the specified tags.
@ -47,9 +47,9 @@ interface mozIGeckoMediaPluginService : nsISupports
* Callable only on GMP thread. * Callable only on GMP thread.
*/ */
[noscript] [noscript]
GMPVideoEncoder getGMPVideoEncoder(in TagArray tags, GMPVideoEncoderProxy getGMPVideoEncoder(in TagArray tags,
[optional] in AString origin, [optional] in AString origin,
out GMPVideoHost outVideoHost); out GMPVideoHost outVideoHost);
/** /**
* Add a directory to scan for gecko media plugins. * Add a directory to scan for gecko media plugins.

View File

@ -18,8 +18,6 @@ if CONFIG['MOZ_WEBM_ENCODER']:
'TestWebMWriter.cpp', 'TestWebMWriter.cpp',
] ]
EXPORT_LIBRARY = True
include('/ipc/chromium/chromium-config.mozbuild') include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [ LOCAL_INCLUDES += [

View File

@ -144,6 +144,7 @@ UNIFIED_SOURCES += [
'MediaDecoder.cpp', 'MediaDecoder.cpp',
'MediaDecoderReader.cpp', 'MediaDecoderReader.cpp',
'MediaDecoderStateMachine.cpp', 'MediaDecoderStateMachine.cpp',
'MediaDecoderStateMachineScheduler.cpp',
'MediaRecorder.cpp', 'MediaRecorder.cpp',
'MediaResource.cpp', 'MediaResource.cpp',
'MediaShutdownManager.cpp', 'MediaShutdownManager.cpp',

View File

@ -472,4 +472,3 @@ skip-if = wave
run-if = wave run-if = wave
[test_fragment_play.html] [test_fragment_play.html]
run-if = wave run-if = wave
skip-if = buildapp == 'b2g' || toolkit == 'android' # Intermittent failures - Bug 996465 & bug 924246

View File

@ -119,11 +119,12 @@ SVGMatrix::Multiply(SVGMatrix& aMatrix)
already_AddRefed<SVGMatrix> already_AddRefed<SVGMatrix>
SVGMatrix::Inverse(ErrorResult& rv) SVGMatrix::Inverse(ErrorResult& rv)
{ {
if (GetMatrix().IsSingular()) { gfxMatrix mat = GetMatrix();
if (!mat.Invert()) {
rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); rv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return nullptr; return nullptr;
} }
nsRefPtr<SVGMatrix> matrix = new SVGMatrix(gfxMatrix(GetMatrix()).Invert()); nsRefPtr<SVGMatrix> matrix = new SVGMatrix(mat);
return matrix.forget(); return matrix.forget();
} }

View File

@ -367,6 +367,17 @@ this.PermissionsTable = { geolocation: {
privileged: PROMPT_ACTION, privileged: PROMPT_ACTION,
certified: ALLOW_ACTION, certified: ALLOW_ACTION,
access: ["read", "write", "create"] access: ["read", "write", "create"]
},
"firefox-accounts": {
app: DENY_ACTION,
privileged: DENY_ACTION,
certified: ALLOW_ACTION
},
"moz-firefox-accounts": {
app: DENY_ACTION,
privileged: PROMPT_ACTION,
certified: ALLOW_ACTION,
substitute: ["firefox-accounts"]
} }
}; };

View File

@ -2552,6 +2552,19 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject }); this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject });
if (!aData.isPackage) {
this.updateAppHandlers(null, app.manifest, app);
if (aInstallSuccessCallback) {
try {
yield aInstallSuccessCallback(app, app.manifest);
} catch (e) {
// Ignore exceptions during the local installation of
// an app. If it fails, the app will anyway be considered
// as not installed because isLaunchable will return false.
}
}
}
// The presence of a requestID means that we have a page to update. // The presence of a requestID means that we have a page to update.
if (aData.isPackage && aData.apkInstall && !aData.requestID) { if (aData.isPackage && aData.apkInstall && !aData.requestID) {
// Skip directly to onInstallSuccessAck, since there isn't // Skip directly to onInstallSuccessAck, since there isn't
@ -2565,13 +2578,6 @@ this.DOMApplicationRegistry = {
this.broadcastMessage("Webapps:Install:Return:OK", aData); this.broadcastMessage("Webapps:Install:Return:OK", aData);
} }
if (!aData.isPackage) {
this.updateAppHandlers(null, app.manifest, app);
if (aInstallSuccessCallback) {
aInstallSuccessCallback(app, app.manifest);
}
}
Services.obs.notifyObservers(null, "webapps-installed", Services.obs.notifyObservers(null, "webapps-installed",
JSON.stringify({ manifestURL: app.manifestURL })); JSON.stringify({ manifestURL: app.manifestURL }));
@ -2642,6 +2648,16 @@ this.DOMApplicationRegistry = {
this.updateDataStore(this.webapps[aId].localId, aNewApp.origin, this.updateDataStore(this.webapps[aId].localId, aNewApp.origin,
aNewApp.manifestURL, aManifest); aNewApp.manifestURL, aManifest);
if (aInstallSuccessCallback) {
try {
yield aInstallSuccessCallback(aNewApp, aManifest, zipFile.path);
} catch (e) {
// Ignore exceptions during the local installation of
// an app. If it fails, the app will anyway be considered
// as not installed because isLaunchable will return false.
}
}
this.broadcastMessage("Webapps:UpdateState", { this.broadcastMessage("Webapps:UpdateState", {
app: app, app: app,
manifest: aManifest, manifest: aManifest,
@ -2655,10 +2671,6 @@ this.DOMApplicationRegistry = {
eventType: ["downloadsuccess", "downloadapplied"], eventType: ["downloadsuccess", "downloadapplied"],
manifestURL: aNewApp.manifestURL manifestURL: aNewApp.manifestURL
}); });
if (aInstallSuccessCallback) {
aInstallSuccessCallback(aNewApp, aManifest, zipFile.path);
}
}), }),
_nextLocalId: function() { _nextLocalId: function() {

View File

@ -1242,7 +1242,9 @@ nsDOMWindowUtils::SendKeyEvent(const nsAString& aType,
event.refPoint.x = event.refPoint.y = 0; event.refPoint.x = event.refPoint.y = 0;
event.time = PR_IntervalNow(); event.time = PR_IntervalNow();
event.mFlags.mIsSynthesizedForTests = true; if (!(aAdditionalFlags & KEY_FLAG_NOT_SYNTHESIZED_FOR_TESTS)) {
event.mFlags.mIsSynthesizedForTests = true;
}
if (aAdditionalFlags & KEY_FLAG_PREVENT_DEFAULT) { if (aAdditionalFlags & KEY_FLAG_PREVENT_DEFAULT) {
event.mFlags.mDefaultPrevented = true; event.mFlags.mDefaultPrevented = true;

View File

@ -715,7 +715,6 @@ static const char js_strict_option_str[] = JS_OPTIONS_DOT_STR "strict";
#ifdef DEBUG #ifdef DEBUG
static const char js_strict_debug_option_str[] = JS_OPTIONS_DOT_STR "strict.debug"; static const char js_strict_debug_option_str[] = JS_OPTIONS_DOT_STR "strict.debug";
#endif #endif
static const char js_werror_option_str[] = JS_OPTIONS_DOT_STR "werror";
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal"; static const char js_zeal_option_str[] = JS_OPTIONS_DOT_STR "gczeal";
static const char js_zeal_frequency_str[] = JS_OPTIONS_DOT_STR "gczeal.frequency"; static const char js_zeal_frequency_str[] = JS_OPTIONS_DOT_STR "gczeal.frequency";
@ -753,8 +752,6 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
} }
#endif #endif
JS::ContextOptionsRef(cx).setWerror(Preferences::GetBool(js_werror_option_str));
#ifdef JS_GC_ZEAL #ifdef JS_GC_ZEAL
int32_t zeal = Preferences::GetInt(js_zeal_option_str, -1); int32_t zeal = Preferences::GetInt(js_zeal_option_str, -1);
int32_t frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ); int32_t frequency = Preferences::GetInt(js_zeal_frequency_str, JS_DEFAULT_ZEAL_FREQ);

View File

@ -1903,11 +1903,10 @@ InterfaceHasInstance(JSContext* cx, JS::Handle<JSObject*> obj,
return true; return true;
} }
JS::Rooted<JSObject*> unwrapped(cx, js::CheckedUnwrap(instance, true)); if (jsipc::IsWrappedCPOW(instance)) {
if (unwrapped && jsipc::IsCPOW(unwrapped)) {
bool boolp = false; bool boolp = false;
if (!jsipc::DOMInstanceOf(cx, unwrapped, clasp->mPrototypeID, if (!jsipc::DOMInstanceOf(cx, js::CheckedUnwrap(instance), clasp->mPrototypeID,
clasp->mDepth, &boolp)) { clasp->mDepth, &boolp)) {
return false; return false;
} }
*bp = boolp; *bp = boolp;

View File

@ -145,8 +145,7 @@ public:
MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(!NS_IsMainThread());
MOZ_ASSERT(!mShuttingDownOnIOThread); MOZ_ASSERT(!mShuttingDownOnIOThread);
RemoveWatchers(READ_WATCHER | WRITE_WATCHER); Close(); // will also remove fd from I/O loop
mShuttingDownOnIOThread = true; mShuttingDownOnIOThread = true;
} }

Some files were not shown because too many files have changed in this diff Show More