diff --git a/lib/dtsc.cpp b/lib/dtsc.cpp index fb5966ad..4f25c07c 100644 --- a/lib/dtsc.cpp +++ b/lib/dtsc.cpp @@ -10,531 +10,6 @@ char DTSC::Magic_Header[] = "DTSC"; char DTSC::Magic_Packet[] = "DTPD"; char DTSC::Magic_Packet2[] = "DTP2"; -/// Initializes a DTSC::Stream with only one packet buffer. -DTSC::Stream::Stream() { - datapointertype = DTSC::INVALID; - buffercount = 1; - buffertime = 0; -} - -/// Initializes a DTSC::Stream with a minimum of rbuffers packet buffers. -/// The actual buffer count may not at all times be the requested amount. -DTSC::Stream::Stream(unsigned int rbuffers, unsigned int bufferTime) { - datapointertype = DTSC::INVALID; - if (rbuffers < 1) { - rbuffers = 1; - } - buffercount = rbuffers; - buffertime = bufferTime; -} - -/// This function does nothing, it's supposed to be overridden. -/// It will be called right before a buffer position is deleted. -void DTSC::Stream::deletionCallback(livePos deleting) {} - -/// Returns the time in milliseconds of the last received packet. -/// This is _not_ the time this packet was received, only the stored time. -unsigned int DTSC::Stream::getTime() { - if (!buffers.size()) { - return 0; - } - return buffers.rbegin()->second["time"].asInt(); -} - -/// Attempts to parse a packet from the given std::string buffer. -/// Returns true if successful, removing the parsed part from the buffer string. -/// Returns false if invalid or not enough data is in the buffer. -/// \arg buffer The std::string buffer to attempt to parse. -bool DTSC::Stream::parsePacket(std::string & buffer) { - uint32_t len; - static bool syncing = false; - if (buffer.length() > 8) { - if (memcmp(buffer.c_str(), DTSC::Magic_Header, 4) == 0) { - len = ntohl(((uint32_t *)buffer.c_str())[1]); - if (buffer.length() < len + 8) { - return false; - } - unsigned int i = 0; - JSON::Value meta; - JSON::fromDTMI((unsigned char *)buffer.c_str() + 8, len, i, meta); - metadata = Meta(meta); - buffer.erase(0, len + 8); - if (buffer.length() <= 8) { - return false; - } - } - int version = 0; - if (memcmp(buffer.c_str(), DTSC::Magic_Packet, 4) == 0) { - version = 1; - } - if (memcmp(buffer.c_str(), DTSC::Magic_Packet2, 4) == 0) { - version = 2; - } - if (version) { - len = ntohl(((uint32_t *)buffer.c_str())[1]); - if (buffer.length() < len + 8) { - return false; - } - JSON::Value newPack; - unsigned int i = 0; - if (version == 1) { - JSON::fromDTMI((unsigned char *)buffer.c_str() + 8, len, i, newPack); - } - if (version == 2) { - JSON::fromDTMI2((unsigned char *)buffer.c_str() + 8, len, i, newPack); - } - buffer.erase(0, len + 8); - addPacket(newPack); - syncing = false; - return true; - } -#if DEBUG >= DLVL_WARN - if (!syncing) { - DEBUG_MSG(DLVL_WARN, "Invalid DTMI data detected - re-syncing"); - syncing = true; - } -#endif - size_t magic_search = buffer.find(Magic_Packet); - size_t magic_search2 = buffer.find(Magic_Packet2); - if (magic_search2 == std::string::npos) { - if (magic_search == std::string::npos) { - buffer.clear(); - } else { - buffer.erase(0, magic_search); - } - } else { - buffer.erase(0, magic_search2); - } - } - return false; -} - -/// Attempts to parse a packet from the given Socket::Buffer. -/// Returns true if successful, removing the parsed part from the buffer. -/// Returns false if invalid or not enough data is in the buffer. -/// \arg buffer The Socket::Buffer to attempt to parse. -bool DTSC::Stream::parsePacket(Socket::Buffer & buffer) { - uint32_t len; - static bool syncing = false; - if (buffer.available(8)) { - std::string header_bytes = buffer.copy(8); - if (memcmp(header_bytes.c_str(), DTSC::Magic_Header, 4) == 0) { - len = ntohl(((uint32_t *)header_bytes.c_str())[1]); - if (!buffer.available(len + 8)) { - return false; - } - unsigned int i = 0; - std::string wholepacket = buffer.remove(len + 8); - JSON::Value meta; - JSON::fromDTMI((unsigned char *)wholepacket.c_str() + 8, len, i, meta); - addMeta(meta); - //recursively calls itself until failure or data packet instead of header - return parsePacket(buffer); - } - int version = 0; - if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet, 4) == 0) { - version = 1; - } - if (memcmp(header_bytes.c_str(), DTSC::Magic_Packet2, 4) == 0) { - version = 2; - } - if (version) { - len = ntohl(((uint32_t *)header_bytes.c_str())[1]); - if (!buffer.available(len + 8)) { - return false; - } - JSON::Value newPack; - unsigned int i = 0; - std::string wholepacket = buffer.remove(len + 8); - if (version == 1) { - JSON::fromDTMI((unsigned char *)wholepacket.c_str() + 8, len, i, newPack); - } - if (version == 2) { - JSON::fromDTMI2((unsigned char *)wholepacket.c_str() + 8, len, i, newPack); - } - addPacket(newPack); - syncing = false; - return true; - } -#if DEBUG >= DLVL_WARN - if (!syncing) { - DEBUG_MSG(DLVL_WARN, "Invalid DTMI data detected - syncing"); - syncing = true; - } -#endif - buffer.get().clear(); - } - return false; -} - -/// Adds a keyframe packet to all tracks, so the stream can be fully played. -void DTSC::Stream::endStream() { - if (!metadata.tracks.size()) { - return; - } - for (std::map::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++) { - JSON::Value newPack; - newPack["time"] = (long long)it->second.lastms; - newPack["trackid"] = it->first; - newPack["keyframe"] = 1ll; - newPack["data"] = ""; - addPacket(newPack); - } -} - -/// Blocks until either the stream has metadata available or the sourceSocket errors. -/// This function is intended to be run before any commands are sent and thus will not throw away anything important. -/// It will time out after 3 seconds, disconnecting the sourceSocket. -void DTSC::Stream::waitForMeta(Socket::Connection & sourceSocket, bool closeOnError){ - bool wasBlocking = sourceSocket.isBlocking(); - sourceSocket.setBlocking(false); - //cancel the attempt after 5000 milliseconds - long long int start = Util::getMS(); - while (!metadata && sourceSocket.connected() && Util::getMS() - start < 3000) { - //we have data? attempt to read header - if (sourceSocket.Received().size()) { - //return value is ignored because we're not interested in data packets, just metadata. - parsePacket(sourceSocket.Received()); - } - //still no header? check for more data - if (!metadata) { - if (sourceSocket.spool()) { - //more received? attempt to read - //return value is ignored because we're not interested in data packets, just metadata. - parsePacket(sourceSocket.Received()); - } else { - //nothing extra to receive? wait a bit and retry - Util::sleep(10); - } - } - } - sourceSocket.setBlocking(wasBlocking); - //if the timeout has passed, close the socket - if (Util::getMS() - start >= 3000 && closeOnError){ - sourceSocket.close(); - //and optionally print a debug message that this happened - DEBUG_MSG(DLVL_DEVEL, "Timing out while waiting for metadata"); - } -} - -/// Blocks until either the stream encounters a pause mark or the sourceSocket errors. -/// This function is intended to be run after the 'q' command is sent, throwing away superfluous packets. -/// It will time out after 5 seconds, disconnecting the sourceSocket. -void DTSC::Stream::waitForPause(Socket::Connection & sourceSocket) { - bool wasBlocking = sourceSocket.isBlocking(); - sourceSocket.setBlocking(false); - //cancel the attempt after 5000 milliseconds - long long int start = Util::getMS(); - while (lastType() != DTSC::PAUSEMARK && sourceSocket.connected() && Util::getMS() - start < 5000) { - //we have data? parse it - if (sourceSocket.Received().size()) { - //return value is ignored because we're not interested. - parsePacket(sourceSocket.Received()); - } - //still no pause mark? check for more data - if (lastType() != DTSC::PAUSEMARK) { - if (sourceSocket.spool()) { - //more received? attempt to read - //return value is ignored because we're not interested in data packets, just metadata. - parsePacket(sourceSocket.Received()); - } else { - //nothing extra to receive? wait a bit and retry - Util::sleep(10); - } - } - } - sourceSocket.setBlocking(wasBlocking); - //if the timeout has passed, close the socket - if (Util::getMS() - start >= 5000) { - sourceSocket.close(); - //and optionally print a debug message that this happened - DEBUG_MSG(DLVL_DEVEL, "Timing out while waiting for pause break"); - } -} - -/// Resets the stream by clearing the buffers and keyframes, making sure to call the deletionCallback first. -void DTSC::Stream::resetStream() { - for (std::map::iterator it = buffers.begin(); it != buffers.end(); it++) { - deletionCallback(it->first); - } - buffers.clear(); - keyframes.clear(); -} - -/// Adds a set of metadata to the steam. -/// This is implemented by simply replacing the current metadata. -/// This effectively resets the stream. -void DTSC::Stream::addMeta(JSON::Value & newMeta) { - metadata = Meta(newMeta); -} - -/// Adds a single DTSC packet to the stream, updating the internal metadata if needed. -void DTSC::Stream::addPacket(JSON::Value & newPack) { - livePos newPos; - newPos.trackID = newPack["trackid"].asInt(); - newPos.seekTime = newPack["time"].asInt(); - if (!metadata.tracks.count(newPos.trackID) && (!newPack.isMember("mark") || newPack["mark"].asStringRef() != "pause")) { - return; - } - if (buffercount > 1 && metadata.tracks[newPos.trackID].keys.size() > 1 && newPos.seekTime < (long long unsigned int)metadata.tracks[newPos.trackID].keys.rbegin()->getTime()) { - resetStream(); - } - while (buffers.count(newPos) > 0) { - newPos.seekTime++; - } - while (buffercount == 1 && buffers.size() > 0) { - cutOneBuffer(); - } - buffers[newPos] = newPack; - datapointertype = INVALID; - std::string tmp = ""; - if (newPack.isMember("trackid") && newPack["trackid"].asInt() > 0) { - tmp = metadata.tracks[newPack["trackid"].asInt()].type; - } - if (newPack.isMember("datatype")) { - tmp = newPack["datatype"].asStringRef(); - } - if (tmp == "video") { - datapointertype = VIDEO; - } - if (tmp == "audio") { - datapointertype = AUDIO; - } - if (tmp == "meta") { - datapointertype = META; - } - if (tmp == "pause_marker" || (newPack.isMember("mark") && newPack["mark"].asStringRef() == "pause")) { - datapointertype = PAUSEMARK; - } - if (buffercount > 1) { - metadata.update(newPack); - if (newPack.isMember("keyframe") || (long long unsigned int)metadata.tracks[newPos.trackID].keys.rbegin()->getTime() == newPos.seekTime) { - keyframes[newPos.trackID].insert(newPos); - } - metadata.live = true; - //throw away buffers if buffer time is met - int trid = buffers.begin()->first.trackID; - int firstTime = buffers.begin()->first.seekTime; - int lastTime = buffers.rbegin()->first.seekTime - buffertime; - while ((!metadata.tracks[trid].keys.size() && firstTime < lastTime) || (metadata.tracks[trid].keys.size() && metadata.tracks[trid].keys.rbegin()->getTime() < lastTime) || (metadata.tracks[trid].keys.size() > 2 && metadata.tracks[trid].keys.rbegin()->getTime() - firstTime > buffertime)) { - cutOneBuffer(); - trid = buffers.begin()->first.trackID; - firstTime = buffers.begin()->first.seekTime; - } - metadata.bufferWindow = buffertime; - } - -} - -/// Deletes a the first part of the buffer, updating the keyframes list and metadata as required. -/// Will print a warning if a track has less than 2 keyframes left because of this. -void DTSC::Stream::cutOneBuffer() { - if (!buffers.size()) { - return; - } - int trid = buffers.begin()->first.trackID; - long long unsigned int delTime = buffers.begin()->first.seekTime; - if (buffercount > 1) { - while (keyframes[trid].size() > 0 && keyframes[trid].begin()->seekTime <= delTime) { - keyframes[trid].erase(keyframes[trid].begin()); - } - while (metadata.tracks[trid].keys.size() && (long long unsigned int)metadata.tracks[trid].keys[0].getTime() <= delTime) { - for (int i = 0; i < metadata.tracks[trid].keys[0].getParts(); i++) { - metadata.tracks[trid].parts.pop_front(); - } - metadata.tracks[trid].keys.pop_front(); - } - if (metadata.tracks[trid].keys.size()) { - metadata.tracks[trid].firstms = metadata.tracks[trid].keys[0].getTime(); - //delete fragments of which the beginning can no longer be reached - while (metadata.tracks[trid].fragments.size() && metadata.tracks[trid].fragments[0].getNumber() < metadata.tracks[trid].keys[0].getNumber()) { - metadata.tracks[trid].fragments.pop_front(); - //increase the missed fragments counter - metadata.tracks[trid].missedFrags++; - } - } else { - metadata.tracks[trid].fragments.clear(); - } - } - deletionCallback(buffers.begin()->first); - buffers.erase(buffers.begin()); -} - -/// Returns a direct pointer to the data attribute of the last received packet, if available. -/// Returns NULL if no valid pointer or packet is available. -std::string & DTSC::Stream::lastData() { - return buffers.rbegin()->second["data"].strVal; -} - -/// Returns the packet in this buffer number. -/// \arg num Buffer number. -JSON::Value & DTSC::Stream::getPacket(livePos num) { - static JSON::Value empty; - if (buffers.find(num) == buffers.end()) { - return empty; - } - return buffers[num]; -} - -JSON::Value & DTSC::Stream::getPacket() { - return buffers.begin()->second; -} - -/// Returns the type of the last received packet. -DTSC::datatype DTSC::Stream::lastType() { - return datapointertype; -} - -/// Returns true if the current stream contains at least one video track. -bool DTSC::Stream::hasVideo() { - for (std::map::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++) { - if (it->second.type == "video") { - return true; - } - } - return false; -} - -/// Returns true if the current stream contains at least one audio track. -bool DTSC::Stream::hasAudio() { - for (std::map::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++) { - if (it->second.type == "audio") { - return true; - } - } - return false; -} - -void DTSC::Stream::setBufferTime(unsigned int ms) { - buffertime = ms; -} - -std::string & DTSC::Stream::outPacket() { - static std::string emptystring; - if (!buffers.size() || !buffers.rbegin()->second.isObject()) { - return emptystring; - } - return buffers.rbegin()->second.toNetPacked(); -} - -/// Returns a packed DTSC packet, ready to sent over the network. -std::string & DTSC::Stream::outPacket(livePos num) { - static std::string emptystring; - if (buffers.find(num) == buffers.end() || !buffers[num].isObject()) return emptystring; - return buffers[num].toNetPacked(); -} - -/// Returns a packed DTSC header, ready to sent over the network. -std::string & DTSC::Stream::outHeader() { - return metadata.toJSON().toNetPacked(); -} - -/// Constructs a new Ring, at the given buffer position. -/// \arg v Position for buffer. -DTSC::Ring::Ring(livePos v) { - b = v; - waiting = false; - starved = false; - updated = false; - playCount = 0; -} - -/// Requests a new Ring, which will be created and added to the internal Ring list. -/// This Ring will be kept updated so it always points to valid data or has the starved boolean set. -/// Don't forget to call dropRing() for all requested Ring classes that are no longer neccessary! -DTSC::Ring * DTSC::Stream::getRing() { - livePos tmp = buffers.begin()->first; - std::map >::iterator it; - for (it = keyframes.begin(); it != keyframes.end(); it++) { - if ((*it->second.begin()).seekTime > tmp.seekTime) { - tmp = *it->second.begin(); - } - } - return new DTSC::Ring(tmp); -} - -/// Deletes a given out Ring class from memory and internal Ring list. -/// Checks for NULL pointers and invalid pointers, silently discarding them. -void DTSC::Stream::dropRing(DTSC::Ring * ptr) { - if (ptr) { - delete ptr; - } -} - -/// Returns 0 if seeking is possible, -1 if the wanted frame is too old, 1 if the wanted frame is too new. -/// This function looks in the header - not in the buffered data itself. -int DTSC::Stream::canSeekms(unsigned int ms) { - bool too_late = false; - //no tracks? Frame too new by definition. - if (!metadata.tracks.size()) { - return 1; - } - //loop trough all the tracks - for (std::map::iterator it = metadata.tracks.begin(); it != metadata.tracks.end(); it++) { - if (it->second.keys.size()) { - if (it->second.keys[0].getTime() <= ms && it->second.keys[it->second.keys.size() - 1].getTime() >= ms) { - return 0; - } - if (it->second.keys[0].getTime() > ms) { - too_late = true; - } - } - } - //did we spot a track already past this point? return too late. - if (too_late) { - return -1; - } - //otherwise, assume not available yet - return 1; -} - -DTSC::livePos DTSC::Stream::msSeek(unsigned int ms, std::set & allowedTracks) { - std::set seekTracks = allowedTracks; - livePos result = buffers.begin()->first; - for (std::set::iterator it = allowedTracks.begin(); it != allowedTracks.end(); it++) { - if (metadata.tracks[*it].type == "video") { - int trackNo = *it; - seekTracks.clear(); - seekTracks.insert(trackNo); - break; - } - } - for (std::map::iterator bIt = buffers.begin(); bIt != buffers.end(); bIt++) { - if (seekTracks.find(bIt->first.trackID) != seekTracks.end()) { - // if (bIt->second.isMember("keyframe")){ - result = bIt->first; - if (bIt->first.seekTime >= ms) { - return result; - } - //} - } - } - return result; -} - -/// Returns whether the current position is the last currently available position within allowedTracks. -/// Simply returns the result of getNext(pos, allowedTracks) == pos -bool DTSC::Stream::isNewest(DTSC::livePos & pos, std::set & allowedTracks) { - return getNext(pos, allowedTracks) == pos; -} - -/// Returns the next available position within allowedTracks, or the current position if no next is availble. -DTSC::livePos DTSC::Stream::getNext(DTSC::livePos & pos, std::set & allowedTracks) { - std::map::iterator iter = buffers.upper_bound(pos); - while (iter != buffers.end()) { - if (allowedTracks.count(iter->first.trackID)) { - return iter->first; - } - iter++; - } - return pos; -} - -/// Properly cleans up the object for erasing. -/// Drops all Ring classes that have been given out. -DTSC::Stream::~Stream() { -} - DTSC::File::File() { F = 0; buffer = malloc(4); diff --git a/lib/dtsc.h b/lib/dtsc.h index 0707f424..cf6752f1 100644 --- a/lib/dtsc.h +++ b/lib/dtsc.h @@ -166,19 +166,6 @@ namespace DTSC { unsigned int trackID; }; - /// A part from the DTSC::Stream ringbuffer. - /// Holds information about a buffer that will stay consistent - class Ring { - public: - Ring(livePos v); - livePos b; - //volatile unsigned int b; ///< Holds current number of buffer. May and is intended to change unexpectedly! - volatile bool waiting; ///< If true, this Ring is currently waiting for a buffer fill. - volatile bool starved; ///< If true, this Ring can no longer receive valid data. - volatile bool updated; ///< If true, this Ring should write a new header. - volatile int playCount; - }; - ///\brief Basic class for storage of data associated with single DTSC packets, a.k.a. parts. class Part { @@ -374,49 +361,5 @@ namespace DTSC { }; //FileWriter - /// Holds temporary data for a DTSC stream and provides functions to utilize it. - /// Optionally also acts as a ring buffer of a certain requested size. - /// If ring buffering mode is enabled, it will automatically grow in size to always contain at least one keyframe. - class Stream { - public: - Stream(); - virtual ~Stream(); - Stream(unsigned int buffers, unsigned int bufferTime = 0); - Meta metadata; - JSON::Value & getPacket(); - JSON::Value & getPacket(livePos num); - datatype lastType(); - std::string & lastData(); - bool hasVideo(); - bool hasAudio(); - bool parsePacket(std::string & buffer); - bool parsePacket(Socket::Buffer & buffer); - std::string & outPacket(); - std::string & outPacket(livePos num); - std::string & outHeader(); - Ring * getRing(); - unsigned int getTime(); - void dropRing(Ring * ptr); - int canSeekms(unsigned int ms); - livePos msSeek(unsigned int ms, std::set & allowedTracks); - void setBufferTime(unsigned int ms); - bool isNewest(DTSC::livePos & pos, std::set & allowedTracks); - DTSC::livePos getNext(DTSC::livePos & pos, std::set & allowedTracks); - void endStream(); - void waitForMeta(Socket::Connection & sourceSocket, bool closeOnError = true); - void waitForPause(Socket::Connection & sourceSocket); - protected: - void cutOneBuffer(); - void resetStream(); - std::map buffers; - std::map > keyframes; - virtual void addPacket(JSON::Value & newPack); - virtual void addMeta(JSON::Value & newMeta); - datatype datapointertype; - unsigned int buffercount; - unsigned int buffertime; - std::map trackMapping; - virtual void deletionCallback(livePos deleting); - }; } diff --git a/lib/dtscmeta.cpp b/lib/dtscmeta.cpp index ff8f598a..4125c47c 100644 --- a/lib/dtscmeta.cpp +++ b/lib/dtscmeta.cpp @@ -646,7 +646,7 @@ namespace DTSC { return ""; } - /// Sets result to a pointer to the string, and strlen to the lenght of it. + /// Sets result to a pointer to the string, and strlen to the length of it. /// Sets both to zero if this isn't a DTSC string value. /// Attempts absolutely no conversion. void Scan::getString(char *& result, unsigned int & strlen) { diff --git a/lib/flv_tag.cpp b/lib/flv_tag.cpp index 0dc418ca..9d2289e5 100644 --- a/lib/flv_tag.cpp +++ b/lib/flv_tag.cpp @@ -360,14 +360,6 @@ FLV::Tag & FLV::Tag::operator=(const FLV::Tag & O) { return *this; } //assignment operator -/// FLV loader function from DTSC. -/// Takes the DTSC data and makes it into FLV. -bool FLV::Tag::DTSCLoader(DTSC::Stream & S) { - std::string tmp = S.getPacket().toNetPacked(); - DTSC::Packet tmpPack(tmp.data(), tmp.size()); - return DTSCLoader(tmpPack, S.metadata.tracks[S.getPacket()["trackid"].asInt()]); -} - bool FLV::Tag::DTSCLoader(DTSC::Packet & packData, DTSC::Track & track) { std::string meta_str; len = 0; @@ -698,124 +690,6 @@ bool FLV::Tag::DTSCMetaInit(DTSC::Meta & M, std::set & selTra return true; } -/// FLV metadata loader function from DTSC. -/// Takes the DTSC metadata and makes it into FLV. -/// Assumes metadata is available - so check before calling! -bool FLV::Tag::DTSCMetaInit(DTSC::Stream & S, DTSC::Track & videoRef, DTSC::Track & audioRef) { - //Unknown? Assume AAC. - if (audioRef.codec == "?") { - audioRef.codec = "AAC"; - } - //Unknown? Assume H264. - if (videoRef.codec == "?") { - videoRef.codec = "H264"; - } - - AMF::Object amfdata("root", AMF::AMF0_DDV_CONTAINER); - - amfdata.addContent(AMF::Object("", "onMetaData")); - amfdata.addContent(AMF::Object("", AMF::AMF0_ECMA_ARRAY)); - if (S.metadata.vod) { - amfdata.getContentP(1)->addContent(AMF::Object("duration", videoRef.lastms / 1000, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("moovPosition", 40, AMF::AMF0_NUMBER)); - AMF::Object keys("keyframes", AMF::AMF0_OBJECT); - keys.addContent(AMF::Object("filepositions", AMF::AMF0_STRICT_ARRAY)); - keys.addContent(AMF::Object("times", AMF::AMF0_STRICT_ARRAY)); - int total_byterate = 0; - if (videoRef.trackID > 0) { - total_byterate += videoRef.bps; - } - if (audioRef.trackID > 0) { - total_byterate += audioRef.bps; - } - for (unsigned long long i = 0; i < videoRef.lastms / 1000; ++i) { //for each second in the file - keys.getContentP(0)->addContent(AMF::Object("", i * total_byterate, AMF::AMF0_NUMBER)); //multiply by byterate for fake byte positions - keys.getContentP(1)->addContent(AMF::Object("", i, AMF::AMF0_NUMBER)); //seconds - } - amfdata.getContentP(1)->addContent(keys); - } - if (videoRef.trackID > 0) { - amfdata.getContentP(1)->addContent(AMF::Object("hasVideo", 1, AMF::AMF0_BOOL)); - if (videoRef.codec == "H264") { - amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", (std::string)"avc1")); - } - if (videoRef.codec == "VP6") { - amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 4, AMF::AMF0_NUMBER)); - } - if (videoRef.codec == "H263") { - amfdata.getContentP(1)->addContent(AMF::Object("videocodecid", 2, AMF::AMF0_NUMBER)); - } - amfdata.getContentP(1)->addContent(AMF::Object("width", videoRef.width, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("height", videoRef.height, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("videoframerate", (double)videoRef.fpks / 1000.0, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("videodatarate", (double)videoRef.bps * 128.0, AMF::AMF0_NUMBER)); - } - if (audioRef.trackID > 0) { - amfdata.getContentP(1)->addContent(AMF::Object("hasAudio", 1, AMF::AMF0_BOOL)); - amfdata.getContentP(1)->addContent(AMF::Object("audiodelay", 0, AMF::AMF0_NUMBER)); - if (audioRef.codec == "AAC") { - amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp4a")); - } - if (audioRef.codec == "MP3") { - amfdata.getContentP(1)->addContent(AMF::Object("audiocodecid", (std::string)"mp3")); - } - amfdata.getContentP(1)->addContent(AMF::Object("audiochannels", audioRef.channels, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplerate", audioRef.rate, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("audiosamplesize", audioRef.size, AMF::AMF0_NUMBER)); - amfdata.getContentP(1)->addContent(AMF::Object("audiodatarate", (double)audioRef.bps * 128.0, AMF::AMF0_NUMBER)); - } - AMF::Object trinfo = AMF::Object("trackinfo", AMF::AMF0_STRICT_ARRAY); - int i = 0; - if (audioRef) { - trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); - trinfo.getContentP(i)->addContent(AMF::Object("length", ((double)audioRef.lastms) * ((double)audioRef.rate), AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->addContent(AMF::Object("timescale", audioRef.rate, AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); - if (audioRef.codec == "AAC") { - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp4a")); - } - if (audioRef.codec == "MP3") { - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"mp3")); - } - ++i; - } - if (videoRef) { - trinfo.addContent(AMF::Object("", AMF::AMF0_OBJECT)); - trinfo.getContentP(i)->addContent( - AMF::Object("length", ((double)videoRef.lastms / 1000) * ((double)videoRef.fpks / 1000.0), AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->addContent(AMF::Object("timescale", ((double)videoRef.fpks / 1000.0), AMF::AMF0_NUMBER)); - trinfo.getContentP(i)->addContent(AMF::Object("sampledescription", AMF::AMF0_STRICT_ARRAY)); - if (videoRef.codec == "H264") { - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"avc1")); - } - if (videoRef.codec == "VP6") { - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"vp6")); - } - if (videoRef.codec == "H263") { - trinfo.getContentP(i)->getContentP(2)->addContent(AMF::Object("sampletype", (std::string)"h263")); - } - ++i; - } - amfdata.getContentP(1)->addContent(trinfo); - - std::string tmp = amfdata.Pack(); - len = tmp.length() + 15; - if (len <= 0 || !checkBufferSize()) { - return false; - } - memcpy(data + 11, tmp.data(), len - 15); - setLen(); - data[0] = 0x12; - data[1] = ((len - 15) >> 16) & 0xFF; - data[2] = ((len - 15) >> 8) & 0xFF; - data[3] = (len - 15) & 0xFF; - data[8] = 0; - data[9] = 0; - data[10] = 0; - tagTime(0); - return true; -} - /// FLV loader function from chunk. /// Copies the contents and wraps it in a FLV header. bool FLV::Tag::ChunkLoader(const RTMPStream::Chunk & O) { diff --git a/lib/flv_tag.h b/lib/flv_tag.h index 15837753..b91fe53e 100644 --- a/lib/flv_tag.h +++ b/lib/flv_tag.h @@ -47,12 +47,10 @@ namespace FLV { ~Tag(); ///< Generic destructor. //loader functions bool ChunkLoader(const RTMPStream::Chunk & O); - bool DTSCLoader(DTSC::Stream & S); bool DTSCLoader(DTSC::Packet & packData, DTSC::Track & track); bool DTSCVideoInit(DTSC::Track & video); bool DTSCAudioInit(DTSC::Track & audio); bool DTSCMetaInit(DTSC::Meta & M, std::set & selTracks); - bool DTSCMetaInit(DTSC::Stream & S, DTSC::Track & videoRef, DTSC::Track & audioRef); JSON::Value toJSON(DTSC::Meta & metadata, AMF::Object & amf_storage, unsigned int reTrack = 0); bool MemLoader(char * D, unsigned int S, unsigned int & P); bool FileLoader(FILE * f); diff --git a/lib/json.h b/lib/json.h index 958ee5d6..37390033 100644 --- a/lib/json.h +++ b/lib/json.h @@ -8,11 +8,6 @@ #include #include "socket.h" -//empty definition of DTSC::Stream so it can be a friend. -namespace DTSC { - class Stream; -} - /// JSON-related classes and functions namespace JSON { @@ -33,8 +28,6 @@ namespace JSON { std::deque arrVal; std::map objVal; public: - //friends - friend class DTSC::Stream; //for access to strVal //constructors/destructors Value(); ~Value();