void NetConnection_as::connect(const std::string& uri) { // Close any current connections. (why?) Because that's what happens. close(); // TODO: check for other kind of invalidities here... if ( uri.empty() ) { _isConnected = false; notifyStatus(CONNECT_FAILED); return; } URL url(uri, getRunResources(owner()).baseURL()); if ((url.protocol() != "rtmp") && (url.protocol() != "rtmpt") && (url.protocol() != "rtmpts") && (url.protocol() != "https") && (url.protocol() != "http")) { IF_VERBOSE_ASCODING_ERRORS( log_aserror("NetConnection.connect(%s): invalid connection " "protocol", url); );
Video::Video(as_object* object, const SWF::DefineVideoStreamTag* def, DisplayObject* parent) : DisplayObject(getRoot(*object), object, parent), m_def(def), _ns(nullptr), _embeddedStream(m_def), _lastDecodedVideoFrameNum(-1), _lastDecodedVideoFrame(), _smoothing(false) { assert(object); assert(def); media::MediaHandler* mh = getRunResources(*object).mediaHandler(); if (!mh) { LOG_ONCE(log_error(_("No Media handler registered, " "won't be able to decode embedded video")) ); return; } media::VideoInfo* info = m_def->getVideoInfo(); if (!info) return; try { _decoder = mh->createVideoDecoder(*info); } catch (const MediaException& e) { log_error(_("Could not create Video Decoder: %s"), e.what()); } }
Sound_as::Sound_as(as_object* owner) : ActiveRelay(owner), _attachedCharacter(0), soundId(-1), externalSound(false), isStreaming(false), _soundHandler(getRunResources(*owner).soundHandler()), _mediaHandler(getRunResources(*owner).mediaHandler()), _startTime(0), _leftOverData(), _leftOverPtr(0), _leftOverSize(0), _inputStream(0), remainingLoops(0), _soundCompleted(false), _soundLoaded(false) { }
/// FIXME: this should not use _uri, but rather take a URL argument. /// Validation should probably be done on connect() only and return a /// bool indicating validity. That can be used to return a failure /// for invalid or blocked URLs. std::string NetConnection_as::validateURL() const { URL uri(_uri, getRunResources(owner()).baseURL()); std::string uriStr(uri.str()); assert(uriStr.find("://") != std::string::npos); // Check if we're allowed to open url if (!URLAccessManager::allow(uri)) { log_security(_("Gnash is not allowed to open this url: %s"), uriStr); return ""; } log_debug(_("Connection to movie: %s"), uriStr); return uriStr; }
bool HTTPRemotingHandler::advance() { #ifdef GNASH_DEBUG_REMOTING log_debug("advancing HTTPRemotingHandler"); #endif if (_connection) { #ifdef GNASH_DEBUG_REMOTING log_debug("have connection"); #endif // Fill last chunk before reading in the next size_t toRead = reply.capacity() - reply.size(); if (! toRead) toRead = NCCALLREPLYCHUNK; #ifdef GNASH_DEBUG_REMOTING log_debug("Attempt to read %d bytes", toRead); #endif // See if we need to allocate more bytes for the next // read chunk if (reply.capacity() < reply.size() + toRead) { // if _connection->size() >= 0, reserve for it, so // if HTTP Content-Length response header is correct // we'll be allocating only once for all. const size_t newCapacity = reply.size() + toRead; #ifdef GNASH_DEBUG_REMOTING log_debug("NetConnection.call: reply buffer capacity (%d) " "is too small to accept next %d bytes of chunk " "(current size is %d). Reserving %d bytes.", reply.capacity(), toRead, reply.size(), newCapacity); #endif reply.reserve(newCapacity); #ifdef GNASH_DEBUG_REMOTING log_debug(" after reserve, new capacity is %d", reply.capacity()); #endif } const int read = _connection->readNonBlocking(reply.data() + reply.size(), toRead); if (read > 0) { #ifdef GNASH_DEBUG_REMOTING log_debug("read '%1%' bytes: %2%", read, hexify(reply.data() + reply.size(), read, false)); #endif reply.resize(reply.size() + read); } // There is no way to tell if we have a whole amf reply without // parsing everything // // The reply format has a header field which specifies the // number of bytes in the reply, but potlatch sends 0xffffffff // and works fine in the proprietary player // // For now we just wait until we have the full reply. // // FIXME make this parse on other conditions, including: 1) when // the buffer is full, 2) when we have a "length in bytes" value // thas is satisfied if (_connection->bad()) { log_debug("connection is in error condition, calling " "NetConnection.onStatus"); reply.resize(0); reply_start = 0; // reset connection before calling the callback _connection.reset(); // This is just a guess, but is better than sending // 'undefined' _nc.notifyStatus(NetConnection_as::CALL_FAILED); } else if (_connection->eof()) { if (reply.size() > 8) { #ifdef GNASH_DEBUG_REMOTING log_debug("hit eof"); #endif boost::uint16_t li; const boost::uint8_t *b = reply.data() + reply_start; const boost::uint8_t *end = reply.data() + reply.size(); amf::Reader rd(b, end, getGlobal(_nc.owner())); // parse header b += 2; // skip version indicator and client id // NOTE: this looks much like parsing of an OBJECT_AMF0 boost::int16_t si = readNetworkShort(b); b += 2; // number of headers uint8_t headers_ok = 1; if (si != 0) { #ifdef GNASH_DEBUG_REMOTING log_debug("NetConnection::call(): amf headers " "section parsing"); #endif as_value tmp; for (size_t i = si; i > 0; --i) { if(b + 2 > end) { headers_ok = 0; break; } si = readNetworkShort(b); b += 2; // name length if(b + si > end) { headers_ok = 0; break; } std::string headerName((char*)b, si); // end-b); #ifdef GNASH_DEBUG_REMOTING log_debug("Header name %s", headerName); #endif b += si; if ( b + 5 > end ) { headers_ok = 0; break; } b += 5; // skip past bool and length long if(!rd(tmp)) { headers_ok = 0; break; } #ifdef GNASH_DEBUG_REMOTING log_debug("Header value %s", tmp); #endif { // method call for each header // FIXME: it seems to me that the call should happen VM& vm = getVM(_nc.owner()); string_table& st = vm.getStringTable(); string_table::key key = st.find(headerName); #ifdef GNASH_DEBUG_REMOTING log_debug("Calling NetConnection.%s(%s)", headerName, tmp); #endif callMethod(&_nc.owner(), key, tmp); } } } if(headers_ok == 1) { si = readNetworkShort(b); b += 2; // number of replies // TODO consider counting number of replies we // actually parse and doing something if it // doesn't match this value (does it matter? if(si > 0) { // parse replies until we get a parse error or we reach the end of the buffer while(b < end) { if(b + 2 > end) break; si = readNetworkShort(b); b += 2; // reply length if(si < 4) { // shorted valid response is '/1/a' log_error("NetConnection::call(): reply message name too short"); break; } if(b + si > end) break; // Reply message is: '/id/methodName' int ns = 1; // next slash position while (ns<si-1 && *(b+ns) != '/') ++ns; if ( ns >= si-1 ) { std::string msg( reinterpret_cast<const char*>(b), si); log_error("NetConnection::call(): invalid " "reply message name (%s)", msg); break; } std::string id(reinterpret_cast<const char*>(b), ns); std::string methodName( reinterpret_cast<const char*>(b+ns+1), si-ns-1); b += si; // parse past unused string in header if(b + 2 > end) break; si = readNetworkShort(b); b += 2; // reply length if(b + si > end) break; b += si; // this field is supposed to hold the // total number of bytes in the rest of // this particular reply value, but // openstreetmap.org (which works great // in the adobe player) sends // 0xffffffff. So we just ignore it if(b + 4 > end) break; li = readNetworkLong(b); b += 4; // reply length #ifdef GNASH_DEBUG_REMOTING log_debug("about to parse amf value"); #endif // this updates b to point to the next unparsed byte as_value replyval; if (!rd(replyval)) { log_error("parse amf failed"); // this will happen if we get // bogus data, or if the data runs // off the end of the buffer // provided, or if we get data we // don't know how to parse break; } #ifdef GNASH_DEBUG_REMOTING log_debug("parsed amf"); #endif // update variable to show how much we've parsed reply_start = b - reply.data(); // if actionscript specified a callback object, // call it as_object* callback = pop_callback(id); if (callback) { string_table::key methodKey; if ( methodName == "onResult" ) { methodKey = NSV::PROP_ON_RESULT; } else if (methodName == "onStatus") { methodKey = NSV::PROP_ON_STATUS; } else { // NOTE: the pp is known to actually // invoke the custom method, but with 7 // undefined arguments (?) log_error("Unsupported HTTP Remoting " "response callback: '%s' " "(size %d)", methodName, methodName.size()); continue; } #ifdef GNASH_DEBUG_REMOTING log_debug("calling onResult callback"); #endif // FIXME check if above line can fail and we // have to react callMethod(callback, methodKey, replyval); #ifdef GNASH_DEBUG_REMOTING log_debug("callback called"); #endif } else { log_error("Unknown HTTP Remoting response identifier '%s'", id); } } } } } else { log_error("Response from remoting service < 8 bytes"); } #ifdef GNASH_DEBUG_REMOTING log_debug("deleting connection"); #endif _connection.reset(); reply.resize(0); reply_start = 0; } } if(!_connection && queued_count > 0) { //#ifdef GNASH_DEBUG_REMOTING log_debug("creating connection"); //#endif // set the "number of bodies" header (reinterpret_cast<boost::uint16_t*>(_postdata.data() + 4))[0] = htons(queued_count); std::string postdata_str(reinterpret_cast<char*>(_postdata.data()), _postdata.size()); #ifdef GNASH_DEBUG_REMOTING log_debug("NetConnection.call(): encoded args from %1% calls: %2%", queued_count, hexify(postdata.data(), postdata.size(), false)); #endif queued_count = 0; // TODO: it might be useful for a Remoting Handler to have a // StreamProvider member const StreamProvider& sp = getRunResources(_nc.owner()).streamProvider(); _connection.reset(sp.getStream(_url, postdata_str, _headers).release()); _postdata.resize(6); #ifdef GNASH_DEBUG_REMOTING log_debug("connection created"); #endif } if (_connection == 0) { // nothing more to do return false; } return true; }
void Sound_as::loadSound(const std::string& file, bool streaming) { if (!_mediaHandler || !_soundHandler) { log_debug("No media or sound handlers, won't load any sound"); return; } /// If we are already streaming stop doing so as we'll replace /// the media parser if (_inputStream) { _soundHandler->unplugInputStream(_inputStream); _inputStream = 0; } /// Mark sound as not being loaded // TODO: should we check for _soundLoaded == true? _soundLoaded = false; /// Delete any media parser being used (make sure we have detached!) _mediaParser.reset(); /// Start at offset 0, in case a previous ::start() call /// changed that. _startTime = 0; const RunResources& rr = getRunResources(owner()); URL url(file, rr.streamProvider().baseURL()); const RcInitFile& rcfile = RcInitFile::getDefaultInstance(); const StreamProvider& streamProvider = rr.streamProvider(); std::auto_ptr<IOChannel> inputStream(streamProvider.getStream(url, rcfile.saveStreamingMedia())); if (!inputStream.get()) { log_error(_("Gnash could not open this URL: %s"), url ); // dispatch onLoad (false) callMethod(&owner(), NSV::PROP_ON_LOAD, false); return; } externalSound = true; isStreaming = streaming; _mediaParser.reset(_mediaHandler->createMediaParser(inputStream).release()); if (!_mediaParser) { log_error(_("Unable to create parser for Sound at %s"), url); // not necessarely correct, the stream might have been found... // dispatch onLoad (false) callMethod(&owner(), NSV::PROP_ON_LOAD, false); return; } // TODO: use global _soundbuftime if (isStreaming) { _mediaParser->setBufferTime(60000); // one minute buffer... should be fine } else { // If this is an event sound, we must not limit buffering (parsing), // because onLoad will not be called until we have finished doing so. _mediaParser->setBufferTime(std::numeric_limits<boost::uint64_t>::max()); } startProbeTimer(); owner().set_member(NSV::PROP_DURATION, getDuration()); owner().set_member(NSV::PROP_POSITION, getPosition()); }