ScriptECMAArray::ScriptECMAArray(std::istream& s):frameRate(0) { //numVar is an 'approximation' of array size UI32 numVar; s >> numVar; numVar.bswap(); while(1) { ScriptDataString varName(s); //cout << varName.getString() << endl; UI8 Type; s >> Type; switch(Type) { case 0: //double (big-endian) { uint64_t tmp; s.read((char*)&tmp,8); tmp=be64toh(tmp); double d=*(reinterpret_cast<double*>(&tmp)); //cout << d << endl; //HACK, extract fps information if(varName.getString()=="framerate") frameRate=d; break; } case 1: { UI8 b; s >> b; //cout << (int)b << endl; break; } case 2: { ScriptDataString String(s); //cout << String.getString() << endl; break; } case 9: //End of array { return; } default: throw ParseException("Unexpected type in FLV"); //cout << (int)Type << endl; } } }
FLV_HEADER::FLV_HEADER(std::istream& in):dataOffset(0),_hasAudio(false),_hasVideo(false) { UI8 Signature[3]; UI8 Version; UI32 DataOffset; in >> Signature[0] >> Signature[1] >> Signature[2] >> Version; version=Version; if(Signature[0]=='F' && Signature[1]=='L' && Signature[2]=='V') { LOG(LOG_NO_INFO, "FLV file: Version " << (int)Version); valid=true; } else { LOG(LOG_NO_INFO,"No FLV file signature found"); valid=false; return; } BitStream bs(in); if(UB(5, bs)!=0) { valid=false; return; } _hasAudio=UB(1, bs); if(UB(1, bs)!=0) { valid=false; return; } _hasVideo=UB(1, bs); in >> DataOffset; DataOffset.bswap(); dataOffset = DataOffset; assert_and_throw(dataOffset==9); }
void NetStream::execute() { if(downloader->hasFailed()) { sys->currentVm->addEvent(this,Class<Event>::getInstanceS("ioError")); sys->downloadManager->destroy(downloader); return; } //The downloader hasn't failed yet at this point //mutex access to downloader istream s(downloader); s.exceptions ( istream::eofbit | istream::failbit | istream::badbit ); ThreadProfile* profile=sys->allocateProfiler(RGB(0,0,200)); profile->setTag("NetStream"); //We need to catch possible EOF and other error condition in the non reliable stream uint32_t decodedAudioBytes=0; uint32_t decodedVideoFrames=0; //The decoded time is computed from the decodedAudioBytes to avoid drifts uint32_t decodedTime=0; bool waitForFlush=true; try { ScriptDataTag tag; Chronometer chronometer; STREAM_TYPE t=classifyStream(s); if(t==FLV_STREAM) { FLV_HEADER h(s); if(!h.isValid()) throw ParseException("FLV is not valid"); unsigned int prevSize=0; bool done=false; do { //Check if threadAbort has been called, if so, stop this loop if(closed) done = true; UI32 PreviousTagSize; s >> PreviousTagSize; PreviousTagSize.bswap(); assert_and_throw(PreviousTagSize==prevSize); //Check tag type and read it UI8 TagType; s >> TagType; switch(TagType) { case 8: { AudioDataTag tag(s); prevSize=tag.getTotalLen(); if(audioDecoder==NULL) { audioCodec=tag.SoundFormat; switch(tag.SoundFormat) { case AAC: assert_and_throw(tag.isHeader()) #ifdef ENABLE_LIBAVCODEC audioDecoder=new FFMpegAudioDecoder(tag.SoundFormat, tag.packetData, tag.packetLen); #else audioDecoder=new NullAudioDecoder(); #endif tag.releaseBuffer(); break; case MP3: #ifdef ENABLE_LIBAVCODEC audioDecoder=new FFMpegAudioDecoder(tag.SoundFormat,NULL,0); #else audioDecoder=new NullAudioDecoder(); #endif decodedAudioBytes+= audioDecoder->decodeData(tag.packetData,tag.packetLen,decodedTime); //Adjust timing decodedTime=decodedAudioBytes/audioDecoder->getBytesPerMSec(); break; default: throw RunTimeException("Unsupported SoundFormat"); } if(audioDecoder->isValid() && sys->audioManager->pluginLoaded()) audioStream=sys->audioManager->createStreamPlugin(audioDecoder); } else { assert_and_throw(audioCodec==tag.SoundFormat); decodedAudioBytes+= audioDecoder->decodeData(tag.packetData,tag.packetLen,decodedTime); if(audioStream==0 && audioDecoder->isValid() && sys->audioManager->pluginLoaded()) audioStream=sys->audioManager->createStreamPlugin(audioDecoder); //Adjust timing decodedTime=decodedAudioBytes/audioDecoder->getBytesPerMSec(); } break; } case 9: { VideoDataTag tag(s); prevSize=tag.getTotalLen(); //If the framerate is known give the right timing, otherwise use decodedTime from audio uint32_t frameTime=(frameRate!=0.0)?(decodedVideoFrames*1000/frameRate):decodedTime; if(videoDecoder==NULL) { //If the isHeader flag is on then the decoder becomes the owner of the data if(tag.isHeader()) { //The tag is the header, initialize decoding #ifdef ENABLE_LIBAVCODEC videoDecoder= new FFMpegVideoDecoder(tag.codec,tag.packetData,tag.packetLen, frameRate); #else videoDecoder=new NullVideoDecoder(); #endif tag.releaseBuffer(); } else if(videoDecoder==NULL) { //First packet but no special handling #ifdef ENABLE_LIBAVCODEC videoDecoder=new FFMpegVideoDecoder(tag.codec,NULL,0,frameRate); #else videoDecoder=new NullVideoDecoder(); #endif videoDecoder->decodeData(tag.packetData,tag.packetLen, frameTime); decodedVideoFrames++; } Event* status=Class<NetStatusEvent>::getInstanceS("status", "NetStream.Play.Start"); getVm()->addEvent(this, status); status->decRef(); status=Class<NetStatusEvent>::getInstanceS("status", "NetStream.Buffer.Full"); getVm()->addEvent(this, status); status->decRef(); } else { videoDecoder->decodeData(tag.packetData,tag.packetLen, frameTime); decodedVideoFrames++; } break; } case 18: { tag = ScriptDataTag(s); prevSize=tag.getTotalLen(); //The frameRate of the container overrides the stream if(tag.metadataDouble.find("framerate") != tag.metadataDouble.end()) frameRate=tag.metadataDouble["framerate"]; break; } default: LOG(LOG_ERROR,_("Unexpected tag type ") << (int)TagType << _(" in FLV")); threadAbort(); } if(!tickStarted && isReady()) { { multiname onMetaDataName; onMetaDataName.name_type=multiname::NAME_STRING; onMetaDataName.name_s="onMetaData"; onMetaDataName.ns.push_back(nsNameAndKind("",NAMESPACE)); ASObject* callback = client->getVariableByMultiname(onMetaDataName); if(callback && callback->getObjectType() == T_FUNCTION) { ASObject* callbackArgs[1]; ASObject* metadata = Class<ASObject>::getInstanceS(); if(tag.metadataDouble.find("width") != tag.metadataDouble.end()) metadata->setVariableByQName("width", "", abstract_d(tag.metadataDouble["width"])); else metadata->setVariableByQName("width", "", abstract_d(getVideoWidth())); if(tag.metadataDouble.find("height") != tag.metadataDouble.end()) metadata->setVariableByQName("height", "", abstract_d(tag.metadataDouble["height"])); else metadata->setVariableByQName("height", "", abstract_d(getVideoHeight())); if(tag.metadataDouble.find("framerate") != tag.metadataDouble.end()) metadata->setVariableByQName("framerate", "", abstract_d(tag.metadataDouble["framerate"])); if(tag.metadataDouble.find("duration") != tag.metadataDouble.end()) metadata->setVariableByQName("duration", "", abstract_d(tag.metadataDouble["duration"])); if(tag.metadataInteger.find("canseekontime") != tag.metadataInteger.end()) metadata->setVariableByQName("canSeekToEnd", "", abstract_b(tag.metadataInteger["canseekontime"] == 1)); if(tag.metadataDouble.find("audiodatarate") != tag.metadataDouble.end()) metadata->setVariableByQName("audiodatarate", "", abstract_d(tag.metadataDouble["audiodatarate"])); if(tag.metadataDouble.find("videodatarate") != tag.metadataDouble.end()) metadata->setVariableByQName("videodatarate", "", abstract_d(tag.metadataDouble["videodatarate"])); //TODO: missing: audiocodecid (Number), cuePoints (Object[]), //videocodecid (Number), custommetadata's callbackArgs[0] = metadata; client->incRef(); metadata->incRef(); FunctionEvent* event = new FunctionEvent(static_cast<IFunction*>(callback), client, callbackArgs, 1); getVm()->addEvent(NULL,event); event->decRef(); } } tickStarted=true; if(frameRate==0) { assert(videoDecoder->frameRate); frameRate=videoDecoder->frameRate; } sys->addTick(1000/frameRate,this); //Also ask for a render rate equal to the video one (capped at 24) float localRenderRate=dmin(frameRate,24); sys->setRenderRate(localRenderRate); } profile->accountTime(chronometer.checkpoint()); if(aborting) { throw JobTerminationException(); } } while(!done); } else threadAbort(); }
void NetStream::execute() { //mutex access to downloader istream s(downloader); s.exceptions ( istream::eofbit | istream::failbit | istream::badbit ); ThreadProfile* profile=sys->allocateProfiler(RGB(0,0,200)); profile->setTag("NetStream"); //We need to catch possible EOF and other error condition in the non reliable stream try { Chronometer chronometer; STREAM_TYPE t=classifyStream(s); if(t==FLV_STREAM) { FLV_HEADER h(s); if(!h.isValid()) threadAbort(); unsigned int prevSize=0; bool done=false; do { UI32 PreviousTagSize; s >> PreviousTagSize; PreviousTagSize.bswap(); assert(PreviousTagSize==prevSize); //Check tag type and read it UI8 TagType; s >> TagType; switch(TagType) { case 8: { AudioDataTag tag(s); prevSize=tag.getTotalLen(); break; } case 9: { VideoDataTag tag(s); prevSize=tag.getTotalLen(); assert(tag.codecId==7); if(tag.isHeader()) { //The tag is the header, initialize decoding assert(decoder==NULL); //The decoder can be set only once //NOTE: there is not need to mutex the decoder, as an async transition from NULL to //valid is not critical decoder=new FFMpegDecoder(tag.packetData,tag.packetLen); assert(decoder); assert(frameRate!=0); //Now that the decoder is valid, let's start the ticking sys->addTick(1000/frameRate,this); //sys->setRenderRate(frameRate); tag.releaseBuffer(); } else decoder->decodeData(tag.packetData,tag.packetLen); break; } case 18: { ScriptDataTag tag(s); prevSize=tag.getTotalLen(); //HACK: initialize frameRate from the container frameRate=tag.frameRate; break; } default: cout << (int)TagType << endl; threadAbort(); } profile->accountTime(chronometer.checkpoint()); if(aborting) throw JobTerminationException(); } while(!done); } else threadAbort(); }
void NetStream::execute() { //mutex access to downloader istream s(downloader); s.exceptions ( istream::eofbit | istream::failbit | istream::badbit ); ThreadProfile* profile=sys->allocateProfiler(RGB(0,0,200)); profile->setTag("NetStream"); //We need to catch possible EOF and other error condition in the non reliable stream uint32_t decodedTime=0; uint32_t videoFrameCount=0; try { Chronometer chronometer; STREAM_TYPE t=classifyStream(s); if(t==FLV_STREAM) { FLV_HEADER h(s); if(!h.isValid()) threadAbort(); unsigned int prevSize=0; bool done=false; do { UI32 PreviousTagSize; s >> PreviousTagSize; PreviousTagSize.bswap(); assert_and_throw(PreviousTagSize==prevSize); //Check tag type and read it UI8 TagType; s >> TagType; switch(TagType) { case 8: { AudioDataTag tag(s); prevSize=tag.getTotalLen(); #ifdef ENABLE_SOUND if(audioDecoder) { assert_and_throw(audioCodec==tag.SoundFormat); uint32_t decodedBytes=audioDecoder->decodeData(tag.packetData,tag.packetLen,decodedTime); if(soundStreamId==0 && audioDecoder->isValid()) soundStreamId=sys->soundManager->createStream(audioDecoder); //Adjust timing decodedTime+=decodedBytes/audioDecoder->getBytesPerMSec(); } else { audioCodec=tag.SoundFormat; switch(tag.SoundFormat) { case AAC: assert_and_throw(tag.isHeader()) #ifdef ENABLE_LIBAVCODEC audioDecoder=new FFMpegAudioDecoder(tag.SoundFormat, tag.packetData, tag.packetLen); #else audioDecoder=new NullAudioDecoder(); #endif break; default: throw RunTimeException("Unsupported SoundFormat"); } if(audioDecoder->isValid()) soundStreamId=sys->soundManager->createStream(audioDecoder); } #endif break; } case 9: { VideoDataTag tag(s); prevSize=tag.getTotalLen(); //Reset the current time, the video flow driver the stream decodedTime=videoFrameCount*1000/frameRate; videoFrameCount++; assert_and_throw(tag.codecId==7); if(tag.isHeader()) { //The tag is the header, initialize decoding assert_and_throw(videoDecoder==NULL); //The decoder can be set only once #ifdef ENABLE_LIBAVCODEC videoDecoder=new FFMpegVideoDecoder(tag.packetData,tag.packetLen); #else videoDecoder=new NullVideoDecoder(); #endif tag.releaseBuffer(); Event* status=Class<NetStatusEvent>::getInstanceS("status", "NetStream.Play.Start"); getVm()->addEvent(this, status); status->decRef(); status=Class<NetStatusEvent>::getInstanceS("status", "NetStream.Buffer.Full"); getVm()->addEvent(this, status); status->decRef(); } else videoDecoder->decodeData(tag.packetData,tag.packetLen, decodedTime); break; } case 18: { ScriptDataTag tag(s); prevSize=tag.getTotalLen(); //The frameRate of the container overrides the stream if(tag.frameRate) frameRate=tag.frameRate; break; } default: LOG(LOG_ERROR,"Unexpected tag type " << (int)TagType << " in FLV"); threadAbort(); } if(!tickStarted && isReady()) { tickStarted=true; if(frameRate==0) { assert(videoDecoder->frameRate); frameRate=videoDecoder->frameRate; } sys->addTick(1000/frameRate,this); } profile->accountTime(chronometer.checkpoint()); if(aborting) throw JobTerminationException(); } while(!done); } else threadAbort(); }