bool VideoPlayerBackend::start(const QUrl &url)
{
Q_ASSERT(!m_pipeline);
if (state() == PermanentError || m_pipeline)
return false;
if (!m_sink)
{
setError(true, QLatin1String("Internal error: improper usage"));
return false;
}
/* Pipeline */
m_pipeline = gst_pipeline_new("stream");
if (!m_pipeline)
{
setError(true, tr("Failed to create video pipeline (%1)").arg(QLatin1String("stream")));
return false;
}
/* Buffered HTTP source */
setVideoBuffer(new VideoHttpBuffer(url));
GstElement *source = m_videoBuffer->setupSrcElement(m_pipeline);
if (!source)
{
setError(true, tr("Failed to create video pipeline (%1)").arg(QLatin1String("source")));
setVideoBuffer(0);
return false;
}
m_videoBuffer->startBuffering();
/* Decoder */
GstElement *decoder = gst_element_factory_make("decodebin2", "decoder");
if (!decoder)
{
setError(true, tr("Failed to create video pipeline (%1)").arg(QLatin1String("decoder")));
return false;
}
g_object_set(G_OBJECT(decoder),
"use-buffering", TRUE,
"max-size-time", 10 * GST_SECOND,
NULL);
g_signal_connect(decoder, "new-decoded-pad", G_CALLBACK(staticDecodePadReady), this);
/* Colorspace conversion (no-op if unnecessary) */
GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "colorspace");
if (!colorspace)
{
setError(true, tr("Failed to create video pipeline (%1)").arg(QLatin1String("colorspace")));
return false;
}
gst_bin_add_many(GST_BIN(m_pipeline), decoder, colorspace, m_sink, NULL);
if (!gst_element_link(source, decoder))
{
setError(true, tr("Failed to create video pipeline (%1)").arg(QLatin1String("link decoder")));
return false;
}
if (!gst_element_link(colorspace, m_sink))
{
setError(true, tr("Failed to create video pipeline (%1)").arg(QLatin1String("link sink")));
return false;
}
/* This is the element that is linked to the decoder for video output; it will be linked when decodePadReady
* gives us the video pad. */
m_videoLink = colorspace;
m_playbackSpeed = 1.0;
/* We handle all messages in the sync handler, because we can't run a glib event loop.
* Although linux does use glib's loop (and we could take advantage of that), it's better
* to handle everything this way for windows and mac support. */
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(m_pipeline));
Q_ASSERT(bus);
gst_bus_enable_sync_message_emission(bus);
gst_bus_set_sync_handler(bus, staticBusHandler, this);
gst_object_unref(bus);
/* Move the pipeline into the PLAYING state. This call may block for a very long time
* (up to several seconds), because it will block until the pipeline has completed that move. */
gst_element_set_state(m_pipeline, GST_STATE_READY);
return true;
}
HRESULT StorageController::setPortCount(ULONG aPortCount)
{
/* the machine needs to be mutable */
AutoMutableStateDependency adep(m->pParent);
if (FAILED(adep.rc())) return adep.rc();
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
switch (m->bd->storageBus)
{
case StorageBus_SATA:
{
/* AHCI SATA supports a maximum of 30 ports. */
if (aPortCount < 1 || aPortCount > 30)
return setError(E_INVALIDARG,
tr("Invalid port count: %lu (must be in range [%lu, %lu])"),
aPortCount, 1, 30);
break;
}
case StorageBus_SCSI:
{
/*
* SCSI does not support setting different ports.
* (doesn't make sense here either).
* The maximum and minimum is 16 and unless the callee
* tries to set a different value we return an error.
*/
if (aPortCount != 16)
return setError(E_INVALIDARG,
tr("Invalid port count: %lu (must be in range [%lu, %lu])"),
aPortCount, 16, 16);
break;
}
case StorageBus_IDE:
{
/*
* The port count is fixed to 2.
*/
if (aPortCount != 2)
return setError(E_INVALIDARG,
tr("Invalid port count: %lu (must be in range [%lu, %lu])"),
aPortCount, 2, 2);
break;
}
case StorageBus_Floppy:
{
/*
* The port count is fixed to 1.
*/
if (aPortCount != 1)
return setError(E_INVALIDARG,
tr("Invalid port count: %lu (must be in range [%lu, %lu])"),
aPortCount, 1, 1);
break;
}
case StorageBus_SAS:
{
/* SAS supports a maximum of 255 ports. */
if (aPortCount < 1 || aPortCount > 255)
return setError(E_INVALIDARG,
tr("Invalid port count: %lu (must be in range [%lu, %lu])"),
aPortCount, 1, 255);
break;
}
case StorageBus_USB:
{
/*
* The port count is fixed to 8.
*/
if (aPortCount != 8)
return setError(E_INVALIDARG,
tr("Invalid port count: %lu (must be in range [%lu, %lu])"),
aPortCount, 8, 8);
break;
}
case StorageBus_PCIe:
{
/*
* PCIe (NVMe in particular) supports theoretically 2^32 - 1
* different namespaces, limit the amount artifically here.
*/
if (aPortCount < 1 || aPortCount > 255)
return setError(E_INVALIDARG,
tr("Invalid port count: %lu (must be in range [%lu, %lu])"),
aPortCount, 1, 255);
break;
}
default:
AssertMsgFailed(("Invalid controller type %d\n", m->bd->storageBus));
}
if (m->bd->ulPortCount != aPortCount)
{
m->bd.backup();
m->bd->ulPortCount = aPortCount;
alock.release();
AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS); // m->pParent is const, needs no locking
m->pParent->i_setModified(Machine::IsModified_Storage);
mlock.release();
//.........这里部分代码省略.........
/**
* Do the teleporter.
*
* @returns VBox status code.
* @param pState The teleporter state.
*/
HRESULT
Console::teleporterSrc(TeleporterStateSrc *pState)
{
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
/*
* Wait for Console::Teleport to change the state.
*/
{
AutoWriteLock autoLock(this COMMA_LOCKVAL_SRC_POS);
}
BOOL fCanceled = TRUE;
HRESULT hrc = pState->mptrProgress->COMGETTER(Canceled)(&fCanceled);
if (FAILED(hrc))
return hrc;
if (fCanceled)
return setError(E_FAIL, tr("canceled"));
/*
* Try connect to the destination machine, disable Nagle.
* (Note. The caller cleans up mhSocket, so we can return without worries.)
*/
int vrc = RTTcpClientConnect(pState->mstrHostname.c_str(), pState->muPort, &pState->mhSocket);
if (RT_FAILURE(vrc))
return setError(E_FAIL, tr("Failed to connect to port %u on '%s': %Rrc"),
pState->muPort, pState->mstrHostname.c_str(), vrc);
vrc = RTTcpSetSendCoalescing(pState->mhSocket, false /*fEnable*/);
AssertRC(vrc);
/* Read and check the welcome message. */
char szLine[RT_MAX(128, sizeof(g_szWelcome))];
RT_ZERO(szLine);
vrc = RTTcpRead(pState->mhSocket, szLine, sizeof(g_szWelcome) - 1, NULL);
if (RT_FAILURE(vrc))
return setError(E_FAIL, tr("Failed to read welcome message: %Rrc"), vrc);
if (strcmp(szLine, g_szWelcome))
return setError(E_FAIL, tr("Unexpected welcome %.*Rhxs"), sizeof(g_szWelcome) - 1, szLine);
/* password */
pState->mstrPassword.append('\n');
vrc = RTTcpWrite(pState->mhSocket, pState->mstrPassword.c_str(), pState->mstrPassword.length());
if (RT_FAILURE(vrc))
return setError(E_FAIL, tr("Failed to send password: %Rrc"), vrc);
/* ACK */
hrc = teleporterSrcReadACK(pState, "password", tr("Invalid password"));
if (FAILED(hrc))
return hrc;
/*
* Start loading the state.
*
* Note! The saved state includes vital configuration data which will be
* verified against the VM config on the other end. This is all done
* in the first pass, so we should fail pretty promptly on misconfig.
*/
hrc = teleporterSrcSubmitCommand(pState, "load");
if (FAILED(hrc))
return hrc;
RTSocketRetain(pState->mhSocket);
void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(pState));
vrc = VMR3Teleport(VMR3GetVM(pState->mpUVM),
pState->mcMsMaxDowntime,
&g_teleporterTcpOps, pvUser,
teleporterProgressCallback, pvUser,
&pState->mfSuspendedByUs);
RTSocketRelease(pState->mhSocket);
if (RT_FAILURE(vrc))
{
if ( vrc == VERR_SSM_CANCELLED
&& RT_SUCCESS(RTTcpSelectOne(pState->mhSocket, 1)))
{
hrc = teleporterSrcReadACK(pState, "load-complete");
if (FAILED(hrc))
return hrc;
}
return setError(E_FAIL, tr("VMR3Teleport -> %Rrc"), vrc);
}
hrc = teleporterSrcReadACK(pState, "load-complete");
if (FAILED(hrc))
return hrc;
/*
* We're at the point of no return.
*/
if (!pState->mptrProgress->notifyPointOfNoReturn())
{
teleporterSrcSubmitCommand(pState, "cancel", false /*fWaitForAck*/);
return E_FAIL;
}
//.........这里部分代码省略.........
/**
* Creates a TCP server that listens for the source machine and passes control
* over to Console::teleporterTrgServeConnection().
*
* @returns VBox status code.
* @param pUVM The user-mode VM handle
* @param pMachine The IMachine for the virtual machine.
* @param pErrorMsg Pointer to the error string for VMSetError.
* @param fStartPaused Whether to start it in the Paused (true) or
* Running (false) state,
* @param pProgress Pointer to the progress object.
* @param pfPowerOffOnFailure Whether the caller should power off
* the VM on failure.
*
* @remarks The caller expects error information to be set on failure.
* @todo Check that all the possible failure paths sets error info...
*/
HRESULT
Console::teleporterTrg(PUVM pUVM, IMachine *pMachine, Utf8Str *pErrorMsg, bool fStartPaused,
Progress *pProgress, bool *pfPowerOffOnFailure)
{
LogThisFunc(("pUVM=%p pMachine=%p fStartPaused=%RTbool pProgress=%p\n", pUVM, pMachine, fStartPaused, pProgress));
*pfPowerOffOnFailure = true;
/*
* Get the config.
*/
ULONG uPort;
HRESULT hrc = pMachine->COMGETTER(TeleporterPort)(&uPort);
if (FAILED(hrc))
return hrc;
ULONG const uPortOrg = uPort;
Bstr bstrAddress;
hrc = pMachine->COMGETTER(TeleporterAddress)(bstrAddress.asOutParam());
if (FAILED(hrc))
return hrc;
Utf8Str strAddress(bstrAddress);
const char *pszAddress = strAddress.isEmpty() ? NULL : strAddress.c_str();
Bstr bstrPassword;
hrc = pMachine->COMGETTER(TeleporterPassword)(bstrPassword.asOutParam());
if (FAILED(hrc))
return hrc;
Utf8Str strPassword(bstrPassword);
strPassword.append('\n'); /* To simplify password checking. */
/*
* Create the TCP server.
*/
int vrc;
PRTTCPSERVER hServer;
if (uPort)
vrc = RTTcpServerCreateEx(pszAddress, uPort, &hServer);
else
{
for (int cTries = 10240; cTries > 0; cTries--)
{
uPort = RTRandU32Ex(cTries >= 8192 ? 49152 : 1024, 65534);
vrc = RTTcpServerCreateEx(pszAddress, uPort, &hServer);
if (vrc != VERR_NET_ADDRESS_IN_USE)
break;
}
if (RT_SUCCESS(vrc))
{
hrc = pMachine->COMSETTER(TeleporterPort)(uPort);
if (FAILED(hrc))
{
RTTcpServerDestroy(hServer);
return hrc;
}
}
}
if (RT_FAILURE(vrc))
return setError(E_FAIL, tr("RTTcpServerCreateEx failed with status %Rrc"), vrc);
/*
* Create a one-shot timer for timing out after 5 mins.
*/
RTTIMERLR hTimerLR;
vrc = RTTimerLRCreateEx(&hTimerLR, 0 /*ns*/, RTTIMER_FLAGS_CPU_ANY, teleporterDstTimeout, hServer);
if (RT_SUCCESS(vrc))
{
vrc = RTTimerLRStart(hTimerLR, 5*60*UINT64_C(1000000000) /*ns*/);
if (RT_SUCCESS(vrc))
{
/*
* Do the job, when it returns we're done.
*/
TeleporterStateTrg theState(this, pUVM, pProgress, pMachine, mControl, &hTimerLR, fStartPaused);
theState.mstrPassword = strPassword;
theState.mhServer = hServer;
void *pvUser = static_cast<void *>(static_cast<TeleporterState *>(&theState));
if (pProgress->setCancelCallback(teleporterProgressCancelCallback, pvUser))
{
LogRel(("Teleporter: Waiting for incoming VM...\n"));
hrc = pProgress->SetNextOperation(Bstr(tr("Waiting for incoming VM")).raw(), 1);
if (SUCCEEDED(hrc))
//.........这里部分代码省略.........
请发表评论