Bug 991037 - Part 0: Stop sync waiting until GMP is ready, since this happens on main now and will deadlock. r=mt

This commit is contained in:
Byron Campen [:bwc] 2014-08-06 12:36:19 -07:00
Родитель b6553fb9e5
Коммит f038acb4d1
5 изменённых файлов: 139 добавлений и 70 удалений

Просмотреть файл

@ -239,75 +239,43 @@ int VcmSIPCCBinding::getVideoCodecs()
return VcmSIPCCBinding::gVideoCodecMask;
}
static void GMPDummy() {};
bool VcmSIPCCBinding::scanForGmpCodecs()
{
if (!gSelf) {
return false;
}
if (!gSelf->mGMPService) {
gSelf->mGMPService = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (!gSelf->mGMPService) {
return false;
}
}
// XXX find a way to a) do this earlier, b) not block mainthread
// Perhaps fire on first RTCPeerconnection creation, and block
// processing (async) CreateOffer or CreateAnswer's until it has returned.
// Since they're already async, it's easy to avoid starting them there.
// However, we might like to do it even earlier, perhaps.
// XXX We shouldn't be blocking MainThread on the GMP thread!
// This initiates the scan for codecs
nsCOMPtr<nsIThread> thread;
nsresult rv = gSelf->mGMPService->GetThread(getter_AddRefs(thread));
if (NS_FAILED(rv)) {
return false;
}
// presumes that all GMP dir scans have been queued for the GMPThread
mozilla::SyncRunnable::DispatchToThread(thread,
WrapRunnableNM(&GMPDummy));
return true;
}
int VcmSIPCCBinding::getVideoCodecsGmp()
{
if (!gInitGmpCodecs) {
if (scanForGmpCodecs()) {
gInitGmpCodecs = true;
}
// This code assumes that GMP has been initted, specifically
// by PeerConnectionCtx::initGMP()
if (!gSelf->mGMPService) {
gSelf->mGMPService = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
}
if (gInitGmpCodecs) {
if (!gSelf->mGMPService) {
gSelf->mGMPService = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
}
if (gSelf->mGMPService) {
// XXX I'd prefer if this was all known ahead of time...
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
// H.264 only for now
bool has_gmp;
nsresult rv;
rv = gSelf->mGMPService->HasPluginForAPI(NS_LITERAL_STRING(""),
NS_LITERAL_CSTRING("encode-video"),
&tags,
&has_gmp);
if (NS_SUCCEEDED(rv) && has_gmp) {
rv = gSelf->mGMPService->HasPluginForAPI(NS_LITERAL_STRING(""),
NS_LITERAL_CSTRING("decode-video"),
&tags,
&has_gmp);
if (NS_SUCCEEDED(rv) && has_gmp) {
return VCM_CODEC_RESOURCE_H264;
}
}
}
if (!gSelf->mGMPService) {
return 0;
}
return 0;
// XXX I'd prefer if this was all known ahead of time...
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
// H.264 only for now
bool has_gmp;
nsresult rv;
rv = gSelf->mGMPService->HasPluginForAPI(NS_LITERAL_STRING(""),
NS_LITERAL_CSTRING("encode-video"),
&tags,
&has_gmp);
if (NS_FAILED(rv) || !has_gmp) {
return 0;
}
rv = gSelf->mGMPService->HasPluginForAPI(NS_LITERAL_STRING(""),
NS_LITERAL_CSTRING("decode-video"),
&tags,
&has_gmp);
if (NS_FAILED(rv) || !has_gmp) {
return 0;
}
return VCM_CODEC_RESOURCE_H264;
}
int VcmSIPCCBinding::getVideoCodecsHw()

Просмотреть файл

@ -78,7 +78,6 @@ namespace CSF
static int gVideoCodecGmpMask;
private:
static bool scanForGmpCodecs();
void CandidateReady(mozilla::NrIceMediaStream* stream,
const std::string& candidate);

Просмотреть файл

@ -280,7 +280,7 @@ FreeOnMain_m(nsAutoPtr<RTCStatsQueries> aQueryList) {
static void
EverySecondTelemetryCallback_s(nsAutoPtr<RTCStatsQueries> aQueryList) {
using namespace Telemetry;
if(!PeerConnectionCtx::isActive()) {
return;
}
@ -391,6 +391,8 @@ PeerConnectionCtx::EverySecondTelemetryCallback_m(nsITimer* timer, void *closure
#endif
nsresult PeerConnectionCtx::Initialize() {
initGMP();
mCCM = CSF::CallControlManager::create();
NS_ENSURE_TRUE(mCCM.get(), NS_ERROR_FAILURE);
@ -465,9 +467,49 @@ nsresult PeerConnectionCtx::Initialize() {
return NS_OK;
}
static void GMPReady_m() {
if (PeerConnectionCtx::isActive()) {
PeerConnectionCtx::GetInstance()->onGMPReady();
}
};
static void GMPReady() {
PeerConnectionCtx::gMainThread->Dispatch(WrapRunnableNM(&GMPReady_m),
NS_DISPATCH_NORMAL);
};
void PeerConnectionCtx::initGMP()
{
mGMPService = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (!mGMPService) {
CSFLogError(logTag, "%s failed to get the gecko-media-plugin-service",
__FUNCTION__);
return;
}
nsCOMPtr<nsIThread> thread;
nsresult rv = mGMPService->GetThread(getter_AddRefs(thread));
if (NS_FAILED(rv)) {
mGMPService = nullptr;
CSFLogError(logTag,
"%s failed to get the gecko-media-plugin thread, err=%u",
__FUNCTION__,
static_cast<unsigned>(rv));
return;
}
// presumes that all GMP dir scans have been queued for the GMPThread
thread->Dispatch(WrapRunnableNM(&GMPReady), NS_DISPATCH_NORMAL);
}
nsresult PeerConnectionCtx::Cleanup() {
CSFLogDebug(logTag, "%s", __FUNCTION__);
mQueuedJSEPOperations.Clear();
mGMPService = nullptr;
mCCM->destroy();
mCCM->removeCCObserver(this);
return NS_OK;
@ -487,6 +529,18 @@ CSF::CC_CallPtr PeerConnectionCtx::createCall() {
return mDevice->createCall();
}
void PeerConnectionCtx::queueJSEPOperation(nsRefPtr<nsIRunnable> aOperation) {
mQueuedJSEPOperations.AppendElement(aOperation);
}
void PeerConnectionCtx::onGMPReady() {
mGMPReady = true;
for (size_t i = 0; i < mQueuedJSEPOperations.Length(); ++i) {
mQueuedJSEPOperations[i]->Run();
}
mQueuedJSEPOperations.Clear();
}
void PeerConnectionCtx::onDeviceEvent(ccapi_device_event_e aDeviceEvent,
CSF::CC_DevicePtr aDevice,
CSF::CC_DeviceInfoPtr aInfo ) {

Просмотреть файл

@ -21,6 +21,7 @@
#include "StaticPtr.h"
#include "PeerConnectionImpl.h"
#include "mozIGeckoMediaPluginService.h"
namespace mozilla {
class PeerConnectionCtxShutdown;
@ -75,6 +76,17 @@ class PeerConnectionCtx : public CSF::CC_Observer {
mozilla::dom::PCImplSipccState sipcc_state() { return mSipccState; }
bool isReady() {
// If mGMPService is not set, we aren't using GMP.
if (mGMPService) {
return mGMPReady;
}
return true;
}
void queueJSEPOperation(nsRefPtr<nsIRunnable> aJSEPOperation);
void onGMPReady();
// Make these classes friend so that they can access mPeerconnections.
friend class PeerConnectionImpl;
friend class PeerConnectionWrapper;
@ -92,7 +104,7 @@ class PeerConnectionCtx : public CSF::CC_Observer {
std::map<const std::string, PeerConnectionImpl *> mPeerConnections;
PeerConnectionCtx() : mSipccState(mozilla::dom::PCImplSipccState::Idle),
mCCM(nullptr), mDevice(nullptr) {}
mCCM(nullptr), mDevice(nullptr), mGMPReady(false) {}
// This is a singleton, so don't copy construct it, etc.
PeerConnectionCtx(const PeerConnectionCtx& other) MOZ_DELETE;
void operator=(const PeerConnectionCtx& other) MOZ_DELETE;
@ -105,6 +117,8 @@ class PeerConnectionCtx : public CSF::CC_Observer {
mSipccState = aState;
}
void initGMP();
static void
EverySecondTelemetryCallback_m(nsITimer* timer, void *);
@ -113,6 +127,7 @@ class PeerConnectionCtx : public CSF::CC_Observer {
int mConnectionCounter;
nsCOMPtr<nsITimer> mTelemetryTimer;
public:
// TODO(jib): If we ever enable move semantics on std::map...
//std::map<nsString,nsAutoPtr<mozilla::dom::RTCStatsReportInternal>> mLastReports;
@ -125,6 +140,15 @@ private:
CSF::CallControlManagerPtr mCCM;
CSF::CC_DevicePtr mDevice;
// We cannot form offers/answers properly until the Gecko Media Plugin stuff
// has been initted, which is a complicated mess of thread dispatches,
// including sync dispatches to main. So, we need to be able to queue up
// offer creation (or SetRemote, when we're the answerer) until all of this is
// ready to go, since blocking on this init is just begging for deadlock.
nsCOMPtr<mozIGeckoMediaPluginService> mGMPService;
bool mGMPReady;
nsTArray<nsRefPtr<nsIRunnable>> mQueuedJSEPOperations;
static PeerConnectionCtx *gInstance;
public:
static nsIThread *gMainThread;

Просмотреть файл

@ -1218,7 +1218,18 @@ PeerConnectionImpl::CreateOffer(const SipccOfferOptions& aOptions)
cc_media_options_t* cc_options = aOptions.build();
NS_ENSURE_TRUE(cc_options, NS_ERROR_UNEXPECTED);
mInternal->mCall->createOffer(cc_options, tc);
if (!PeerConnectionCtx::GetInstance()->isReady()) {
// Uh oh. We're not ready yet. Enqueue this operation.
PeerConnectionCtx::GetInstance()->queueJSEPOperation(
WrapRunnable(mInternal->mCall,
&CSF::CC_Call::createOffer,
cc_options,
tc));
} else {
mInternal->mCall->createOffer(cc_options, tc);
}
return NS_OK;
}
@ -1275,8 +1286,21 @@ PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
STAMP_TIMECARD(tc, "Set Remote Description");
mRemoteRequestedSDP = aSDP;
mInternal->mCall->setRemoteDescription((cc_jsep_action_t)action,
mRemoteRequestedSDP, tc);
if (!PeerConnectionCtx::GetInstance()->isReady()) {
// Uh oh. We're not ready yet. Enqueue this operation. (This must be a
// remote offer, or else we would not have gotten this far)
PeerConnectionCtx::GetInstance()->queueJSEPOperation(
WrapRunnable(mInternal->mCall,
&CSF::CC_Call::setRemoteDescription,
(cc_jsep_action_t)action,
mRemoteRequestedSDP,
tc));
} else {
mInternal->mCall->setRemoteDescription((cc_jsep_action_t)action,
mRemoteRequestedSDP, tc);
}
return NS_OK;
}