Fixed progressive MP4 track selection.
This commit is contained in:
		
							parent
							
								
									b5ff0eb0e4
								
							
						
					
					
						commit
						8844f8691a
					
				
					 3 changed files with 440 additions and 87 deletions
				
			
		|  | @ -12,6 +12,7 @@ | ||||||
| #include <mist/timing.h> | #include <mist/timing.h> | ||||||
| #include <mist/procs.h> | #include <mist/procs.h> | ||||||
| #include <mist/stream.h> | #include <mist/stream.h> | ||||||
|  | #include <mist/defines.h> | ||||||
| 
 | 
 | ||||||
| //under cygwin, recv blocks for ~15ms if no data is available.
 | //under cygwin, recv blocks for ~15ms if no data is available.
 | ||||||
| //This is a hack to keep performance decent with that bug present.
 | //This is a hack to keep performance decent with that bug present.
 | ||||||
|  | @ -250,9 +251,7 @@ int main(int argc, char** argv){ | ||||||
|         playing = 0; |         playing = 0; | ||||||
|       } |       } | ||||||
|       if (playing == 0){ |       if (playing == 0){ | ||||||
| #if DEBUG >= 4 |         DEBUG_MSG(DLVL_DEVEL, "Completed VoD request in MistPlayer (%d ms)", (Util::getMS() - bench)); | ||||||
|         std::cerr << "Completed VoD request in MistPlayer (" << (Util::getMS() - bench) << "ms)" << std::endl; |  | ||||||
| #endif |  | ||||||
|         pausemark["time"] = source.getJSON()["time"]; |         pausemark["time"] = source.getJSON()["time"]; | ||||||
|         pausemark.sendTo(in_out); |         pausemark.sendTo(in_out); | ||||||
|         in_out.setBlocking(true); |         in_out.setBlocking(true); | ||||||
|  | @ -266,12 +265,5 @@ int main(int argc, char** argv){ | ||||||
|   } |   } | ||||||
|   StatsSocket.close(); |   StatsSocket.close(); | ||||||
|   in_out.close(); |   in_out.close(); | ||||||
| #if DEBUG >= 5 |  | ||||||
|   if (Util::epoch() - lasttime < 60){ |  | ||||||
|     std::cerr << "MistPlayer exited (disconnect)." << std::endl; |  | ||||||
|   }else{ |  | ||||||
|     std::cerr << "MistPlayer exited (command timeout)." << std::endl; |  | ||||||
|   } |  | ||||||
| #endif |  | ||||||
|   return 0; |   return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -16,32 +16,379 @@ | ||||||
| #include <mist/http_parser.h> | #include <mist/http_parser.h> | ||||||
| #include <mist/dtsc.h> | #include <mist/dtsc.h> | ||||||
| #include <mist/mp4.h> | #include <mist/mp4.h> | ||||||
|  | #include <mist/mp4_generic.h> | ||||||
| #include <mist/amf.h> | #include <mist/amf.h> | ||||||
| #include <mist/config.h> | #include <mist/config.h> | ||||||
| #include <mist/stream.h> | #include <mist/stream.h> | ||||||
| #include <mist/timing.h> | #include <mist/timing.h> | ||||||
|  | #include <mist/defines.h> | ||||||
| 
 | 
 | ||||||
| ///\brief Holds everything unique to HTTP Connectors.
 | ///\brief Holds everything unique to HTTP Connectors.
 | ||||||
| namespace Connector_HTTP { | namespace Connector_HTTP { | ||||||
|  |    | ||||||
|  |   struct keyPart{ | ||||||
|  |   public: | ||||||
|  |     bool operator < (const keyPart& rhs) const { | ||||||
|  |       if (time < rhs.time){ | ||||||
|  |         return true; | ||||||
|  |       } | ||||||
|  |       if (time == rhs.time){ | ||||||
|  |         if (trackID < rhs.trackID){ | ||||||
|  |           return true; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     long unsigned int trackID; | ||||||
|  |     long unsigned int size; | ||||||
|  |     long long unsigned int time; | ||||||
|  |     long long unsigned int endTime; | ||||||
|  |     long unsigned int index; | ||||||
|  |   }; | ||||||
|  |    | ||||||
|  |   std::string DTSCMeta2MP4Header(DTSC::Meta & metaData, std::set<int> & tracks){ | ||||||
|  |     std::stringstream header; | ||||||
|  |     //ftyp box
 | ||||||
|  |     /// \todo fill ftyp with non hardcoded values from file
 | ||||||
|  |     MP4::FTYP ftypBox; | ||||||
|  |     ftypBox.setMajorBrand(0x6D703431);//mp41
 | ||||||
|  |     ftypBox.setMinorVersion(0); | ||||||
|  |     ftypBox.setCompatibleBrands(0x69736f6d,0); | ||||||
|  |     ftypBox.setCompatibleBrands(0x69736f32,1); | ||||||
|  |     ftypBox.setCompatibleBrands(0x61766331,2); | ||||||
|  |     ftypBox.setCompatibleBrands(0x6D703431,3); | ||||||
|  |     header << std::string(ftypBox.asBox(),ftypBox.boxedSize()); | ||||||
|  |      | ||||||
|  |     uint64_t mdatSize = 0; | ||||||
|  |     //moov box
 | ||||||
|  |     MP4::MOOV moovBox;{ | ||||||
|  |       //calculating longest duration
 | ||||||
|  |       long long int fileDuration = 0; | ||||||
|  |       long long int firstms = -1; | ||||||
|  |       long long int lastms = -1; | ||||||
|  |       for (std::set<int>::iterator it = tracks.begin(); it != tracks.end(); it++) { | ||||||
|  |         if (lastms == -1 || lastms < metaData.tracks[*it].lastms){ | ||||||
|  |           lastms = metaData.tracks[*it].lastms; | ||||||
|  |         } | ||||||
|  |         if (firstms == -1 || firstms > metaData.tracks[*it].firstms){ | ||||||
|  |           firstms = metaData.tracks[*it].firstms; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       fileDuration = lastms - firstms; | ||||||
|  |       //MP4::MVHD mvhdBox(fileDuration);
 | ||||||
|  |       MP4::MVHD mvhdBox; | ||||||
|  |       mvhdBox.setVersion(0); | ||||||
|  |       mvhdBox.setCreationTime(0); | ||||||
|  |       mvhdBox.setModificationTime(0); | ||||||
|  |       mvhdBox.setTimeScale(1000); | ||||||
|  |       mvhdBox.setRate(0x10000); | ||||||
|  |       mvhdBox.setDuration(fileDuration); | ||||||
|  |       mvhdBox.setTrackID(0); | ||||||
|  |       mvhdBox.setVolume(256); | ||||||
|  |       mvhdBox.setMatrix(0x00010000,0); | ||||||
|  |       mvhdBox.setMatrix(0,1); | ||||||
|  |       mvhdBox.setMatrix(0,2); | ||||||
|  |       mvhdBox.setMatrix(0,3); | ||||||
|  |       mvhdBox.setMatrix(0x00010000,4); | ||||||
|  |       mvhdBox.setMatrix(0,5); | ||||||
|  |       mvhdBox.setMatrix(0,6); | ||||||
|  |       mvhdBox.setMatrix(0,7); | ||||||
|  |       mvhdBox.setMatrix(0x40000000,8); | ||||||
|  |       moovBox.setContent(mvhdBox, 0); | ||||||
|  |     } | ||||||
|  |     {//start arbitrary track addition for headeri
 | ||||||
|  |       int boxOffset = 1; | ||||||
|  |       for (std::set<int>::iterator it = tracks.begin(); it != tracks.end(); it++) { | ||||||
|  |         int timescale = 0; | ||||||
|  |         MP4::TRAK trakBox; | ||||||
|  |         { | ||||||
|  |           { | ||||||
|  |             MP4::TKHD tkhdBox; | ||||||
|  |             tkhdBox.setVersion(0); | ||||||
|  |             tkhdBox.setFlags(15); | ||||||
|  |             tkhdBox.setTrackID(*it); | ||||||
|  |             tkhdBox.setDuration(metaData.tracks[*it].lastms - metaData.tracks[*it].firstms); | ||||||
|  |              | ||||||
|  |             if (metaData.tracks[*it].type == "video"){ | ||||||
|  |               tkhdBox.setWidth(metaData.tracks[*it].width << 16); | ||||||
|  |               tkhdBox.setHeight(metaData.tracks[*it].height << 16); | ||||||
|  |               tkhdBox.setVolume(0); | ||||||
|  |             }else{ | ||||||
|  |               tkhdBox.setVolume(256); | ||||||
|  |               tkhdBox.setAlternateGroup(1); | ||||||
|  |             } | ||||||
|  |             tkhdBox.setMatrix(0x00010000,0); | ||||||
|  |             tkhdBox.setMatrix(0,1); | ||||||
|  |             tkhdBox.setMatrix(0,2); | ||||||
|  |             tkhdBox.setMatrix(0,3); | ||||||
|  |             tkhdBox.setMatrix(0x00010000,4); | ||||||
|  |             tkhdBox.setMatrix(0,5); | ||||||
|  |             tkhdBox.setMatrix(0,6); | ||||||
|  |             tkhdBox.setMatrix(0,7); | ||||||
|  |             tkhdBox.setMatrix(0x40000000,8); | ||||||
|  |             trakBox.setContent(tkhdBox, 0); | ||||||
|  |           }{ | ||||||
|  |             MP4::MDIA mdiaBox; | ||||||
|  |             { | ||||||
|  |               MP4::MDHD mdhdBox(0);/// \todo fix constructor mdhd in lib
 | ||||||
|  |               mdhdBox.setCreationTime(0); | ||||||
|  |               mdhdBox.setModificationTime(0); | ||||||
|  |               //Calculating media time based on sampledelta. Probably cheating, but it works...
 | ||||||
|  |               timescale = ((double)(42 * metaData.tracks[*it].parts.size() ) / (metaData.tracks[*it].lastms - metaData.tracks[*it].firstms)) *  1000; | ||||||
|  |               mdhdBox.setTimeScale(timescale); | ||||||
|  |               mdhdBox.setDuration((metaData.tracks[*it].lastms - metaData.tracks[*it].firstms) * ((double)timescale / 1000)); | ||||||
|  |               mdiaBox.setContent(mdhdBox, 0); | ||||||
|  |             }//MDHD box
 | ||||||
|  |             { | ||||||
|  |               MP4::HDLR hdlrBox;/// \todo fix constructor hdlr in lib
 | ||||||
|  |               if (metaData.tracks[*it].type == "video"){ | ||||||
|  |                 hdlrBox.setHandlerType(0x76696465);//vide
 | ||||||
|  |               }else if (metaData.tracks[*it].type == "audio"){ | ||||||
|  |                 hdlrBox.setHandlerType(0x736F756E);//soun
 | ||||||
|  |               } | ||||||
|  |               hdlrBox.setName(metaData.tracks[*it].getIdentifier()); | ||||||
|  |               mdiaBox.setContent(hdlrBox, 1); | ||||||
|  |             }//hdlr box
 | ||||||
|  |             { | ||||||
|  |               MP4::MINF minfBox; | ||||||
|  |               if (metaData.tracks[*it].type== "video"){ | ||||||
|  |                 MP4::VMHD vmhdBox; | ||||||
|  |                 vmhdBox.setFlags(1); | ||||||
|  |                 minfBox.setContent(vmhdBox,0); | ||||||
|  |               }else if (metaData.tracks[*it].type == "audio"){ | ||||||
|  |                 MP4::SMHD smhdBox; | ||||||
|  |                 minfBox.setContent(smhdBox,0); | ||||||
|  |               }//type box
 | ||||||
|  |               { | ||||||
|  |                 MP4::DINF dinfBox; | ||||||
|  |                 MP4::DREF drefBox;/// \todo fix constructor dref in lib
 | ||||||
|  |                 drefBox.setVersion(0); | ||||||
|  |                 MP4::URL urlBox; | ||||||
|  |                 urlBox.setFlags(1); | ||||||
|  |                 drefBox.setDataEntry(urlBox,0); | ||||||
|  |                 dinfBox.setContent(drefBox,0); | ||||||
|  |                 minfBox.setContent(dinfBox,1); | ||||||
|  |               }//dinf box
 | ||||||
|  |               { | ||||||
|  |                 MP4::STBL stblBox; | ||||||
|  |                 { | ||||||
|  |                   MP4::STSD stsdBox; | ||||||
|  |                   stsdBox.setVersion(0); | ||||||
|  |                   if (metaData.tracks[*it].type == "video"){//boxname = codec
 | ||||||
|  |                     MP4::VisualSampleEntry vse; | ||||||
|  |                     if (metaData.tracks[*it].codec == "H264"){ | ||||||
|  |                       vse.setCodec("avc1"); | ||||||
|  |                     } | ||||||
|  |                     vse.setDataReferenceIndex(1); | ||||||
|  |                     vse.setWidth(metaData.tracks[*it].width); | ||||||
|  |                     vse.setHeight(metaData.tracks[*it].height); | ||||||
|  |                     MP4::AVCC avccBox; | ||||||
|  |                     avccBox.setPayload(metaData.tracks[*it].init); | ||||||
|  |                     vse.setCLAP(avccBox); | ||||||
|  |                     stsdBox.setEntry(vse,0); | ||||||
|  |                   }else if(metaData.tracks[*it].type == "audio"){//boxname = codec
 | ||||||
|  |                     MP4::AudioSampleEntry ase; | ||||||
|  |                     if (metaData.tracks[*it].codec == "AAC"){ | ||||||
|  |                       ase.setCodec("mp4a"); | ||||||
|  |                       ase.setDataReferenceIndex(1); | ||||||
|  |                     } | ||||||
|  |                     ase.setSampleRate(metaData.tracks[*it].rate); | ||||||
|  |                     ase.setChannelCount(metaData.tracks[*it].channels); | ||||||
|  |                     ase.setSampleSize(metaData.tracks[*it].size); | ||||||
|  |                     //MP4::ESDS esdsBox(metaData.tracks[*it].init, metaData.tracks[*it].bps);
 | ||||||
|  |                     MP4::ESDS esdsBox; | ||||||
|  |                      | ||||||
|  |                     //outputting these values first, so malloc isn't called as often.
 | ||||||
|  |                     esdsBox.setESHeaderStartCodes(metaData.tracks[*it].init); | ||||||
|  |                     esdsBox.setSLValue(2); | ||||||
|  |                      | ||||||
|  |                     esdsBox.setESDescriptorTypeLength(32+metaData.tracks[*it].init.size()); | ||||||
|  |                     esdsBox.setESID(2); | ||||||
|  |                     esdsBox.setStreamPriority(0); | ||||||
|  |                     esdsBox.setDecoderConfigDescriptorTypeLength(18 + metaData.tracks[*it].init.size()); | ||||||
|  |                     esdsBox.setByteObjectTypeID(0x40); | ||||||
|  |                     esdsBox.setStreamType(5); | ||||||
|  |                     esdsBox.setReservedFlag(1); | ||||||
|  |                     esdsBox.setBufferSize(1250000); | ||||||
|  |                     esdsBox.setMaximumBitRate(10000000); | ||||||
|  |                     esdsBox.setAverageBitRate(metaData.tracks[*it].bps * 8); | ||||||
|  |                     esdsBox.setConfigDescriptorTypeLength(5); | ||||||
|  |                     esdsBox.setSLConfigDescriptorTypeTag(0x6); | ||||||
|  |                     esdsBox.setSLConfigExtendedDescriptorTypeTag(0x808080); | ||||||
|  |                     esdsBox.setSLDescriptorTypeLength(1); | ||||||
|  |                     ase.setCodecBox(esdsBox); | ||||||
|  |                     stsdBox.setEntry(ase,0); | ||||||
|  |                   } | ||||||
|  |                   stblBox.setContent(stsdBox,0); | ||||||
|  |                 }//stsd box
 | ||||||
|  |                 /// \todo update following stts lines
 | ||||||
|  |                 { | ||||||
|  |                   MP4::STTS sttsBox;//current version probably causes problems
 | ||||||
|  |                   sttsBox.setVersion(0); | ||||||
|  |                   MP4::STTSEntry newEntry; | ||||||
|  |                   newEntry.sampleCount = metaData.tracks[*it].parts.size(); | ||||||
|  |                   //42, Used as magic number for timescale calculation
 | ||||||
|  |                   newEntry.sampleDelta = 42; | ||||||
|  |                   sttsBox.setSTTSEntry(newEntry, 0); | ||||||
|  |                   stblBox.setContent(sttsBox,1); | ||||||
|  |                 }//stts box
 | ||||||
|  |                 if (metaData.tracks[*it].type == "video"){ | ||||||
|  |                   //STSS Box here
 | ||||||
|  |                   MP4::STSS stssBox; | ||||||
|  |                   stssBox.setVersion(0); | ||||||
|  |                   int tmpCount = 1; | ||||||
|  |                   int tmpItCount = 0; | ||||||
|  |                   for ( std::deque< DTSC::Key>::iterator tmpIt = metaData.tracks[*it].keys.begin(); tmpIt != metaData.tracks[*it].keys.end(); tmpIt ++) { | ||||||
|  |                     stssBox.setSampleNumber(tmpCount,tmpItCount); | ||||||
|  |                     tmpCount += tmpIt->getParts(); | ||||||
|  |                     tmpItCount ++; | ||||||
|  |                   } | ||||||
|  |                   stblBox.setContent(stssBox,2); | ||||||
|  |                 }//stss box
 | ||||||
|  |                  | ||||||
|  |                 int offset = (metaData.tracks[*it].type == "video"); | ||||||
|  |                 { | ||||||
|  |                   MP4::STSC stscBox; | ||||||
|  |                   stscBox.setVersion(0); | ||||||
|  |                   MP4::STSCEntry stscEntry; | ||||||
|  |                   stscEntry.firstChunk = 1; | ||||||
|  |                   stscEntry.samplesPerChunk = 1; | ||||||
|  |                   stscEntry.sampleDescriptionIndex = 1; | ||||||
|  |                   stscBox.setSTSCEntry(stscEntry, 0); | ||||||
|  |                   stblBox.setContent(stscBox,2 + offset); | ||||||
|  |                 }//stsc box
 | ||||||
|  |                 { | ||||||
|  |                   uint32_t total = 0; | ||||||
|  |                   MP4::STSZ stszBox; | ||||||
|  |                   stszBox.setVersion(0); | ||||||
|  |                   total = 0; | ||||||
|  |                   for (std::deque< DTSC::Part>::iterator partIt = metaData.tracks[*it].parts.begin(); partIt != metaData.tracks[*it].parts.end(); partIt ++) { | ||||||
|  |                     stszBox.setEntrySize(partIt->getSize(), total);//in bytes in file
 | ||||||
|  |                     total++; | ||||||
|  |                   } | ||||||
|  |                   stblBox.setContent(stszBox,3 + offset); | ||||||
|  |                 }//stsz box
 | ||||||
|  |                 //add STCO boxes here
 | ||||||
|  |                 { | ||||||
|  |                   MP4::STCO stcoBox; | ||||||
|  |                   stcoBox.setVersion(1); | ||||||
|  |                   //Inserting empty values on purpose here, will be fixed later.
 | ||||||
|  |                   if (metaData.tracks[*it].parts.size() != 0){ | ||||||
|  |                     stcoBox.setChunkOffset(0, metaData.tracks[*it].parts.size() - 1);//this inserts all empty entries at once
 | ||||||
|  |                   } | ||||||
|  |                   stblBox.setContent(stcoBox,4 + offset); | ||||||
|  |                 }//stco box
 | ||||||
|  |                 minfBox.setContent(stblBox,2); | ||||||
|  |               }//stbl box
 | ||||||
|  |               mdiaBox.setContent(minfBox, 2); | ||||||
|  |             }//minf box
 | ||||||
|  |             trakBox.setContent(mdiaBox, 1); | ||||||
|  |           } | ||||||
|  |         }//trak Box
 | ||||||
|  |         moovBox.setContent(trakBox, boxOffset); | ||||||
|  |         boxOffset++; | ||||||
|  |       } | ||||||
|  |     }//end arbitrary track addition
 | ||||||
|  |     //initial offset length ftyp, length moov + 8
 | ||||||
|  |     unsigned long long int byteOffset = ftypBox.boxedSize() + moovBox.boxedSize() + 8; | ||||||
|  |     //update all STCO from the following map;
 | ||||||
|  |     std::map <int, MP4::STCO> checkStcoBoxes; | ||||||
|  |     //for all tracks
 | ||||||
|  |     for (unsigned int i = 1; i < moovBox.getContentCount(); i++){ | ||||||
|  |       //10 lines to get the STCO box.
 | ||||||
|  |       MP4::TRAK checkTrakBox; | ||||||
|  |       MP4::MDIA checkMdiaBox; | ||||||
|  |       MP4::TKHD checkTkhdBox; | ||||||
|  |       MP4::MINF checkMinfBox; | ||||||
|  |       MP4::STBL checkStblBox; | ||||||
|  |       //MP4::STCO checkStcoBox;
 | ||||||
|  |       checkTrakBox = ((MP4::TRAK&)moovBox.getContent(i)); | ||||||
|  |       for (unsigned int j = 0; j < checkTrakBox.getContentCount(); j++){ | ||||||
|  |         if (checkTrakBox.getContent(j).isType("mdia")){ | ||||||
|  |           checkMdiaBox = ((MP4::MDIA&)checkTrakBox.getContent(j)); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |         if (checkTrakBox.getContent(j).isType("tkhd")){ | ||||||
|  |           checkTkhdBox = ((MP4::TKHD&)checkTrakBox.getContent(j)); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       for (unsigned int j = 0; j < checkMdiaBox.getContentCount(); j++){ | ||||||
|  |         if (checkMdiaBox.getContent(j).isType("minf")){ | ||||||
|  |           checkMinfBox = ((MP4::MINF&)checkMdiaBox.getContent(j)); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       for (unsigned int j = 0; j < checkMinfBox.getContentCount(); j++){ | ||||||
|  |         if (checkMinfBox.getContent(j).isType("stbl")){ | ||||||
|  |           checkStblBox = ((MP4::STBL&)checkMinfBox.getContent(j)); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       for (unsigned int j = 0; j < checkStblBox.getContentCount(); j++){ | ||||||
|  |         if (checkStblBox.getContent(j).isType("stco")){ | ||||||
|  |           checkStcoBoxes.insert( std::pair<int, MP4::STCO>(checkTkhdBox.getTrackID(), ((MP4::STCO&)checkStblBox.getContent(j)) )); | ||||||
|  |           break; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |     //inserting right values in the STCO box header
 | ||||||
|  |     //total = 0;
 | ||||||
|  |     long long unsigned int totalByteOffset = 0; | ||||||
|  |     //Current values are actual byte offset without header-sized offset
 | ||||||
|  |     std::set <keyPart> sortSet;//filling sortset for interleaving parts
 | ||||||
|  |     for (std::set<int>::iterator subIt = tracks.begin(); subIt != tracks.end(); subIt++) { | ||||||
|  |       keyPart temp; | ||||||
|  |       temp.trackID = *subIt; | ||||||
|  |       temp.time = metaData.tracks[*subIt].firstms;//timeplace of frame
 | ||||||
|  |       temp.endTime = metaData.tracks[*subIt].firstms + metaData.tracks[*subIt].parts[0].getDuration(); | ||||||
|  |       temp.size = metaData.tracks[*subIt].parts[0].getSize();//bytesize of frame (alle parts all together)
 | ||||||
|  |       temp.index = 0; | ||||||
|  |       sortSet.insert(temp); | ||||||
|  |     } | ||||||
|  |     while (!sortSet.empty()){ | ||||||
|  |       //setting the right STCO size in the STCO box
 | ||||||
|  |       checkStcoBoxes[sortSet.begin()->trackID].setChunkOffset(totalByteOffset + byteOffset, sortSet.begin()->index); | ||||||
|  |       totalByteOffset += sortSet.begin()->size; | ||||||
|  |       //add keyPart to sortSet
 | ||||||
|  |       keyPart temp; | ||||||
|  |       temp.index = sortSet.begin()->index + 1; | ||||||
|  |       temp.trackID = sortSet.begin()->trackID; | ||||||
|  |       if(temp.index < metaData.tracks[temp.trackID].parts.size() ){//only insert when there are parts left
 | ||||||
|  |         temp.time = sortSet.begin()->endTime;//timeplace of frame
 | ||||||
|  |         temp.endTime = sortSet.begin()->endTime + metaData.tracks[temp.trackID].parts[temp.index].getDuration(); | ||||||
|  |         temp.size = metaData.tracks[temp.trackID].parts[temp.index].getSize();//bytesize of frame 
 | ||||||
|  |         sortSet.insert(temp); | ||||||
|  |       } | ||||||
|  |       //remove highest keyPart
 | ||||||
|  |       sortSet.erase(sortSet.begin()); | ||||||
|  |     } | ||||||
|  |     //calculating the offset where the STCO box will be in the main MOOV box
 | ||||||
|  |     //needed for probable optimise
 | ||||||
|  |     mdatSize = totalByteOffset; | ||||||
|  |      | ||||||
|  |     header << std::string(moovBox.asBox(),moovBox.boxedSize()); | ||||||
|  |      | ||||||
|  |     header << (char)((mdatSize>>24) & 0x000000FF) << (char)((mdatSize>>16) & 0x000000FF) << (char)((mdatSize>>8) & 0x000000FF) << (char)(mdatSize & 0x000000FF) << "mdat"; | ||||||
|  |     //end of header
 | ||||||
|  |      | ||||||
|  |     return header.str(); | ||||||
|  |   } | ||||||
|  |    | ||||||
|   ///\brief Main function for the HTTP Progressive Connector
 |   ///\brief Main function for the HTTP Progressive Connector
 | ||||||
|   ///\param conn A socket describing the connection the client.
 |   ///\param conn A socket describing the connection the client.
 | ||||||
|   ///\return The exit code of the connector.
 |   ///\return The exit code of the connector.
 | ||||||
|   int progressiveConnector(Socket::Connection & conn){ |   int progressiveConnector(Socket::Connection & conn){ | ||||||
|     bool ready4data = false; //Set to true when streaming is to begin.
 |  | ||||||
|     DTSC::Stream Strm; //Incoming stream buffer.
 |     DTSC::Stream Strm; //Incoming stream buffer.
 | ||||||
|     HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender.
 |     HTTP::Parser HTTP_R, HTTP_S;//HTTP Receiver en HTTP Sender.
 | ||||||
|     bool inited = false;//Whether the stream is initialized
 |     bool inited = false;//Whether the stream is initialized
 | ||||||
|     Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream.
 |     Socket::Connection ss( -1);//The Stream Socket, used to connect to the desired stream.
 | ||||||
|     std::string streamname;//Will contain the name of the stream.
 |     std::string streamname;//Will contain the name of the stream.
 | ||||||
|  |     #if DEBUG >= DLVL_DEVEL | ||||||
|  |     std::set <keyPart> sortSet;//filling sortset for interleaving parts
 | ||||||
|  |     #endif | ||||||
| 
 | 
 | ||||||
|     //MP4 specific variables
 |  | ||||||
|     MP4::DTSC2MP4Converter Conv; |  | ||||||
|      |  | ||||||
|     unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket.
 |     unsigned int lastStats = 0;//Indicates the last time that we have sent stats to the server socket.
 | ||||||
|      |      | ||||||
|     int videoID = -1; |  | ||||||
|     int audioID = -1; |  | ||||||
| 
 |  | ||||||
|     while (conn.connected()){ |     while (conn.connected()){ | ||||||
|       //Only attempt to parse input when not yet init'ed.
 |       //Only attempt to parse input when not yet init'ed.
 | ||||||
|       if ( !inited){ |       if ( !inited){ | ||||||
|  | @ -51,73 +398,72 @@ namespace Connector_HTTP { | ||||||
|             std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; |             std::cout << "Received request: " << HTTP_R.getUrl() << std::endl; | ||||||
| #endif | #endif | ||||||
|             conn.setHost(HTTP_R.GetHeader("X-Origin")); |             conn.setHost(HTTP_R.GetHeader("X-Origin")); | ||||||
|             //we assume the URL is the stream name with a 3 letter extension
 |             streamname = HTTP_R.GetHeader("X-Stream"); | ||||||
|             streamname = HTTP_R.getUrl().substr(1); |             //we are ready, connect the socket!
 | ||||||
|             size_t extDot = streamname.rfind('.'); |             ss = Util::Stream::getStream(streamname); | ||||||
|             if (extDot != std::string::npos){ |             Strm.waitForMeta(ss); | ||||||
|               streamname.resize(extDot); |              | ||||||
|             } //strip the extension
 |             int videoID = -1; | ||||||
|             ready4data = true; |             int audioID = -1; | ||||||
|  |             if (HTTP_R.GetVar("audio") != ""){ | ||||||
|  |               audioID = JSON::Value(HTTP_R.GetVar("audio")).asInt(); | ||||||
|  |             } | ||||||
|  |             if (HTTP_R.GetVar("video") != ""){ | ||||||
|  |               videoID = JSON::Value(HTTP_R.GetVar("video")).asInt(); | ||||||
|  |             } | ||||||
|  |             for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){ | ||||||
|  |               if (videoID == -1 && it->second.type == "video" && it->second.codec == "H264"){ | ||||||
|  |                 videoID = it->first; | ||||||
|  |               } | ||||||
|  |               if (audioID == -1 && it->second.type == "audio" && it->second.codec == "AAC"){ | ||||||
|  |                 audioID = it->first; | ||||||
|  |               } | ||||||
|  |             } | ||||||
|  |              | ||||||
|  |             std::set<int> tracks; | ||||||
|  |             if (videoID > 0){tracks.insert(videoID);} | ||||||
|  |             if (audioID > 0){tracks.insert(audioID);} | ||||||
|  |              | ||||||
|  |             HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers
 | ||||||
|  |             HTTP_S.SetHeader("Content-Type", "video/MP4"); //Send the correct content-type for FLV files
 | ||||||
|  |             HTTP_S.protocol = "HTTP/1.0"; | ||||||
|  |             conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file
 | ||||||
|  |             conn.SendNow(DTSCMeta2MP4Header(Strm.metadata, tracks));//SENDING MP4HEADER
 | ||||||
|             HTTP_R.Clean(); //clean for any possible next requests
 |             HTTP_R.Clean(); //clean for any possible next requests
 | ||||||
|  |             {//using scope to have cmd not declared after action
 | ||||||
|  |               std::stringstream cmd; | ||||||
|  |               cmd << "t"; | ||||||
|  |               for (std::set<int>::iterator it = tracks.begin(); it != tracks.end(); it++) { | ||||||
|  |                 cmd << " " << *it; | ||||||
|  |               } | ||||||
|  |               cmd << "\ns 0\np\n"; | ||||||
|  |               ss.SendNow(cmd.str()); | ||||||
|  |             } | ||||||
|  |             #if DEBUG >= DLVL_DEVEL | ||||||
|  |             for (std::set<int>::iterator subIt = tracks.begin(); subIt != tracks.end(); subIt++) { | ||||||
|  |               keyPart temp; | ||||||
|  |               temp.trackID = *subIt; | ||||||
|  |               temp.time = Strm.metadata.tracks[*subIt].firstms;//timeplace of frame
 | ||||||
|  |               temp.endTime = Strm.metadata.tracks[*subIt].firstms + Strm.metadata.tracks[*subIt].parts[0].getDuration(); | ||||||
|  |               temp.size = Strm.metadata.tracks[*subIt].parts[0].getSize();//bytesize of frame (alle parts all together)
 | ||||||
|  |               temp.index = 0; | ||||||
|  |               sortSet.insert(temp); | ||||||
|  |             } | ||||||
|  |             #endif | ||||||
|  |             if ( !ss.connected()){ | ||||||
|  |               #if DEBUG >= 1 | ||||||
|  |               fprintf(stderr, "Could not connect to server for %s!\n", streamname.c_str()); | ||||||
|  |               #endif | ||||||
|  |               ss.close(); | ||||||
|  |               HTTP_S.Clean(); | ||||||
|  |               HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); | ||||||
|  |               conn.SendNow(HTTP_S.BuildResponse("404", "Not found")); | ||||||
|  |               continue; | ||||||
|  |             } | ||||||
|  |             inited = true; | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       } |       }else{ | ||||||
|       if (ready4data){ |  | ||||||
|         if ( !inited){ |  | ||||||
|           //we are ready, connect the socket!
 |  | ||||||
|           ss = Util::Stream::getStream(streamname); |  | ||||||
|           Strm.waitForMeta(ss); |  | ||||||
|           //build header here and set iterator
 |  | ||||||
|           HTTP_S.Clean(); //make sure no parts of old requests are left in any buffers
 |  | ||||||
|           HTTP_S.SetHeader("Content-Type", "video/MP4"); //Send the correct content-type for FLV files
 |  | ||||||
|           HTTP_S.protocol = "HTTP/1.0"; |  | ||||||
|           conn.SendNow(HTTP_S.BuildResponse("200", "OK")); //no SetBody = unknown length - this is intentional, we will stream the entire file
 |  | ||||||
|           conn.SendNow(Conv.DTSCMeta2MP4Header(Strm.metadata));//SENDING MP4HEADER
 |  | ||||||
|           {//using scope to have cmd not declared after action
 |  | ||||||
|             std::stringstream cmd; |  | ||||||
|             cmd << "t 1 2"; |  | ||||||
|             cmd << "\ns 0"; |  | ||||||
|             cmd << "\np\n"; |  | ||||||
|             ss.SendNow(cmd.str()); |  | ||||||
|           } |  | ||||||
|           if ( !ss.connected()){ |  | ||||||
| #if DEBUG >= 1 |  | ||||||
|             fprintf(stderr, "Could not connect to server for %s!\n", streamname.c_str()); |  | ||||||
| #endif |  | ||||||
|             ss.close(); |  | ||||||
|             HTTP_S.Clean(); |  | ||||||
|             HTTP_S.SetBody("No such stream is available on the system. Please try again.\n"); |  | ||||||
|             conn.SendNow(HTTP_S.BuildResponse("404", "Not found")); |  | ||||||
|             ready4data = false; |  | ||||||
|             continue; |  | ||||||
|           } |  | ||||||
|           //wait until we have a header
 |  | ||||||
|           while ( !Strm.metadata && ss.connected()){ |  | ||||||
|             if (ss.spool()){ |  | ||||||
|               Strm.parsePacket(ss.Received()); //read the metadata
 |  | ||||||
|             }else{ |  | ||||||
|               Util::sleep(5); |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|           int byterate = 0; |  | ||||||
|           for (std::map<int,DTSC::Track>::iterator it = Strm.metadata.tracks.begin(); it != Strm.metadata.tracks.end(); it++){ |  | ||||||
|             if (videoID == -1 && it->second.type == "video"){ |  | ||||||
|               videoID = it->second.trackID; |  | ||||||
|             } |  | ||||||
|             if (audioID == -1 && it->second.type == "audio"){ |  | ||||||
|               audioID = it->second.trackID; |  | ||||||
|             } |  | ||||||
|           } |  | ||||||
|           if (videoID != -1){ |  | ||||||
|             byterate += Strm.metadata.tracks[videoID].bps; |  | ||||||
|           } |  | ||||||
|           if (audioID != -1){ |  | ||||||
|             byterate += Strm.metadata.tracks[audioID].bps; |  | ||||||
|           } |  | ||||||
|           if ( !byterate){byterate = 1;} |  | ||||||
|            |  | ||||||
|           inited = true; |  | ||||||
|         } |  | ||||||
|         unsigned int now = Util::epoch(); |         unsigned int now = Util::epoch(); | ||||||
|         if (now != lastStats){ |         if (now != lastStats){ | ||||||
|           lastStats = now; |           lastStats = now; | ||||||
|  | @ -128,18 +474,34 @@ namespace Connector_HTTP { | ||||||
|             if (Strm.lastType() == DTSC::PAUSEMARK){ |             if (Strm.lastType() == DTSC::PAUSEMARK){ | ||||||
|               conn.close(); |               conn.close(); | ||||||
|             }else if(Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){ |             }else if(Strm.lastType() == DTSC::AUDIO || Strm.lastType() == DTSC::VIDEO){ | ||||||
|               //parse DTSC to MP4 here
 |               #if DEBUG >= DLVL_DEVEL | ||||||
|  |               if (!sortSet.empty()){ | ||||||
|  |                 if (sortSet.begin()->trackID != Strm.getPacket()["trackid"].asInt() || sortSet.begin()->time != Strm.getPacket()["time"].asInt()){ | ||||||
|  |                   DEBUG_MSG(DLVL_DEVEL, "Set[%d, %d] => Real[%d, %d]", sortSet.begin()->trackID, sortSet.begin()->time, Strm.getPacket()["trackid"].asInt(), Strm.getPacket()["time"].asInt()); | ||||||
|  |                 } | ||||||
|  |                 //add keyPart to sortSet
 | ||||||
|  |                 keyPart temp; | ||||||
|  |                 temp.index = sortSet.begin()->index + 1; | ||||||
|  |                 temp.trackID = sortSet.begin()->trackID; | ||||||
|  |                 if(temp.index < Strm.metadata.tracks[temp.trackID].parts.size() ){//only insert when there are parts left
 | ||||||
|  |                   temp.time = sortSet.begin()->endTime;//timeplace of frame
 | ||||||
|  |                   temp.endTime = sortSet.begin()->endTime + Strm.metadata.tracks[temp.trackID].parts[temp.index].getDuration(); | ||||||
|  |                   temp.size = Strm.metadata.tracks[temp.trackID].parts[temp.index].getSize();//bytesize of frame 
 | ||||||
|  |                   sortSet.insert(temp); | ||||||
|  |                 } | ||||||
|  |                 //remove highest keyPart
 | ||||||
|  |                 sortSet.erase(sortSet.begin()); | ||||||
|  |               } | ||||||
|  |               #endif | ||||||
|               conn.SendNow(Strm.lastData());//send out and clear Converter buffer
 |               conn.SendNow(Strm.lastData());//send out and clear Converter buffer
 | ||||||
|             } |             } | ||||||
|             if (Strm.lastType() == DTSC::INVALID){ |             if (Strm.lastType() == DTSC::INVALID){ | ||||||
|               #if DEBUG >= 3 |               DEBUG_MSG(DLVL_FAIL, "Invalid packet received - closing connection"); | ||||||
|               fprintf(stderr, "Invalid packet received - closing connection.\n"); |  | ||||||
|               #endif |  | ||||||
|               conn.close(); |               conn.close(); | ||||||
|             } |             } | ||||||
|           } |           } | ||||||
|         }else{ |         }else{ | ||||||
|           Util::sleep(1); |           Util::sleep(10); | ||||||
|         } |         } | ||||||
|         if ( !ss.connected()){ |         if ( !ss.connected()){ | ||||||
|           break; |           break; | ||||||
|  |  | ||||||
|  | @ -125,7 +125,6 @@ namespace Converters{ | ||||||
|                   //if we are in the vicinity of a new keyframe
 |                   //if we are in the vicinity of a new keyframe
 | ||||||
|                     if (trackData[sNum].idHeader.parseGranuleUpper(trackData[sNum].lastGran) != trackData[sNum].idHeader.parseGranuleUpper(temp)){ |                     if (trackData[sNum].idHeader.parseGranuleUpper(trackData[sNum].lastGran) != trackData[sNum].idHeader.parseGranuleUpper(temp)){ | ||||||
|                     //try to mark right
 |                     //try to mark right
 | ||||||
|                       long long unsigned int temper = trackData[sNum].idHeader.parseGranuleUpper(temp) - trackData[sNum].idHeader.parseGranuleUpper(trackData[sNum].lastGran); |  | ||||||
|                       DTSCOut["keyframe"] = 1; |                       DTSCOut["keyframe"] = 1; | ||||||
|                       trackData[sNum].lastGran = temp; |                       trackData[sNum].lastGran = temp; | ||||||
|                     }else{ |                     }else{ | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Thulinma
						Thulinma