def test_create_script_tag(self): s = tags.create_script_tag('onMetaData', {'silly': True}) self.assertEquals( s, ('\x12\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00' + '\x02\x00\x0aonMetaData\x08\x00\x00\x00\x01' + '\x00\x05silly\x01\x01\x00\x00\x09' + '\x00\x00\x00\x29'))
def test_create_script_tag(self): s = tags.create_script_tag('onMetaData', {'silly': True}) self.assertEquals(s, ('\x12\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00' + '\x02\x00\x0aonMetaData\x08\x00\x00\x00\x01' + '\x00\x05silly\x01\x01\x00\x00\x09' + '\x00\x00\x00\x29'))
def test_create_script_tag(self): s = tags.create_script_tag("onMetaData", {"silly": True}) self.assertEquals( s, ( "\x12\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00" + "\x02\x00\x0aonMetaData\x08\x00\x00\x00\x01" + "\x00\x05silly\x01\x01\x00\x00\x09" + "\x00\x00\x00\x29" ), )
def onMetaData(self, data): self.debug("Meta-data: %r, %s", data, type(data)) self._component.clear_sizes() if not data: self.debug("We have been asked to clear metadata, we will do " "it as soon as we get new metadata") return if not self._published: self._internalError('Meta-data received for an unpublished stream') return if self._started and self._metadata is None: # Metadata received too late self.debug("Dropping late meta-data") return if self._creationdate is None: self._creationdate = data.get("creationdate", None) # Force the creation date to the first one seen, # to be able to compare metadata. if self._creationdate: data["creationdate"] = self._creationdate if self._metadata and self._metadata != data: self.debug("RTMP stream meta-data changed.") self._clear() if self._metadata is None: self._metadata = data self._component.got_metadata(data.copy()) # a framerate of 1000 sounds unlikely, but seems to happen a lot # when ffmpeg streams an .flv file. Adjust it. if self._metadata and self._metadata.get('framerate', None) == 1000: self.warning( 'Client claims framerate is 1000 fps. Adjusting to 25 fps.') self._metadata['framerate'] = 25 if self._started: self.debug("Dropping unchanged meta-data tag") else: bin = tags.create_script_tag('onMetaData', self._metadata) self._addHeader(bin) self._tryStarting()
def filepositions_difference(metadata, original_metadata_size): test_payload = create_script_tag('onMetaData', metadata) payload_size = len(test_payload) difference = payload_size - original_metadata_size return test_payload, difference
metadata['duration'] = duration metadata['keyframes'] = keyframes metadata['metadatacreator'] = 'flvlib %s' % __versionstr__ # we're going to write new metadata, so we need to shift the # filepositions by the amount of bytes that we're going to add to # the metadata tag test_payload, difference = filepositions_difference(metadata, original_metadata_size) if difference: new_filepositions = [pos + difference for pos in keyframes.filepositions] metadata['keyframes'].filepositions = new_filepositions payload = create_script_tag('onMetaData', metadata) else: log.debug("The file `%s' metadata size did not change.", inpath) payload = test_payload if outpath: try: fo = open(outpath, 'wb') except IOError, (errno, strerror): log.error("Failed to open `%s': %s", outpath, strerror) return False else: try: fd, temppath = tempfile.mkstemp() # preserve the permission bits shutil.copymode(inpath, temppath)
def index_file(inpath, outpath=None): out_text = (outpath and ("into file `%s'" % outpath)) or "and overwriting" log.debug("Indexing file `%s' %s", inpath, out_text) try: f = open(inpath, 'rb') except IOError as e5: (errno, strerror) = e5.args log.error("Failed to open `%s': %s", inpath, strerror) return False flv = IndexingFLV(f) tag_iterator = flv.iter_tags() last_tag = None try: while True: tag = next(tag_iterator) # some buggy software, like gstreamer's flvmux, puts a metadata tag # at the end of the file with timestamp 0, and we don't want to # base our duration computation on that if tag.timestamp != 0: last_tag = tag except MalformedFLV as e: message = e.args[0] % e.args[1:] log.error("The file `%s' is not a valid FLV file: %s", inpath, message) return False except EndOfFile: log.error("Unexpected end of file on file `%s'", inpath) return False except StopIteration: pass if not flv.first_media_tag_offset: log.error("The file `%s' does not have any media content", inpath) return False if not last_tag: log.error( "The file `%s' does not have any content with a " "non-zero timestamp", inpath) return False metadata = flv.metadata or {} if flv.metadata_tag_start: original_metadata_size = flv.metadata_tag_end - flv.metadata_tag_start else: log.debug("The file `%s' has no metadata", inpath) original_metadata_size = 0 keyframes = flv.keyframes if flv.no_video: log.info("The file `%s' has no video, using audio seekpoints info", inpath) keyframes = flv.audio_seekpoints duration = metadata.get('duration') if not duration: # A duration of 0 is nonsensical, yet some tools put it like that. In # that case (or when there is no such field) update the duration value. duration = last_tag.timestamp / 1000.0 metadata['duration'] = duration metadata['keyframes'] = keyframes metadata['metadatacreator'] = 'flvlib %s' % __versionstr__ # we're going to write new metadata, so we need to shift the # filepositions by the amount of bytes that we're going to add to # the metadata tag test_payload, difference = filepositions_difference( metadata, original_metadata_size) if difference: new_filepositions = [ pos + difference for pos in keyframes.filepositions ] metadata['keyframes'].filepositions = new_filepositions payload = create_script_tag('onMetaData', metadata) else: log.debug("The file `%s' metadata size did not change.", inpath) payload = test_payload if outpath: try: fo = open(outpath, 'wb') except IOError as e2: (errno, strerror) = e2.args log.error("Failed to open `%s': %s", outpath, strerror) return False else: try: fd, temppath = tempfile.mkstemp() # preserve the permission bits shutil.copymode(inpath, temppath) fo = os.fdopen(fd, 'wb') except EnvironmentError as e3: (errno, strerror) = e3.args log.error("Failed to create temporary file: %s", strerror) return False log.debug("Creating the output file") try: fo.write( create_flv_header(has_audio=flv.has_audio, has_video=flv.has_video)) fo.write(payload) f.seek(flv.first_media_tag_offset) shutil.copyfileobj(f, fo) except IOError as e6: (errno, strerror) = e6.args log.error("Failed to create the indexed file: %s", strerror) if not outpath: # remove the temporary file force_remove(temppath) return False f.close() fo.close() if not outpath: # If we were not writing directly to the output file # we need to overwrite the original try: shutil.move(temppath, inpath) except EnvironmentError as e4: (errno, strerror) = e4.args log.error( "Failed to overwrite the original file " "with the indexed version: %s", strerror) return False return True
def main(): PY3K = sys.version_info >= (3, 0) if PY3K: source = sys.stdin.buffer else: if sys.platform == "win32": import os, msvcrt msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY) source = sys.stdin header = read_bytes(source, 3) if header != "FLV": print("Not a valid FLV file") return write(header) # Skip rest of FLV header write(read_bytes(source, 6)) i = 0 while True: header = read_bytes(source, 15) if len(header) != 15: write(header) return # Packet structure from Wikipedia: # # Size of previous packet uint32_be 0 For first packet set to NULL # Packet Type uint8 18 For first packet set to AMF Metadata # Payload Size uint24_be varies Size of packet data only # Timestamp Lower uint24_be 0 For first packet set to NULL # Timestamp Upper uint8 0 Extension to create a uint32_be value # Stream ID uint24_be 0 For first stream of same type set to NULL # Payload Data freeform varies Data as defined by packet type # Get payload size to know how many bytes to read high, low = struct.unpack(">BH", header[5:8]) payload_size = (high << 16) + low # Get timestamp to inject into clock sync tag low_high = header[8:12] combined = low_high[3] + low_high[:3] timestamp = struct.unpack(">i", combined)[0] if i % 3: data = astypes.FLVObject() data["streamClock"] = int(timestamp) data["streamClockBase"] = 0 data["wallClock"] = time.time() * 1000 # Inject clock sync packet write(primitives.make_ui32(payload_size + 15)) write(tags.create_script_tag("onClockSync", data, timestamp)) # Write rest of original packet minus previous packet size write(header[4:]) write(read_bytes(source, payload_size)) else: # Write normal packet write(header) write(read_bytes(source, payload_size)) i += 1
def index_file(inpath, outpath=None): out_text = (outpath and ("into file `%s'" % outpath)) or "and overwriting" log.debug("Indexing file `%s' %s", inpath, out_text) try: f = open(inpath, 'rb') except IOError as e5: (errno, strerror) = e5.args log.error("Failed to open `%s': %s", inpath, strerror) return False flv = IndexingFLV(f) tag_iterator = flv.iter_tags() last_tag = None try: while True: tag = next(tag_iterator) # some buggy software, like gstreamer's flvmux, puts a metadata tag # at the end of the file with timestamp 0, and we don't want to # base our duration computation on that if tag.timestamp != 0: last_tag = tag except MalformedFLV as e: message = e.args[0] % e.args[1:] log.error("The file `%s' is not a valid FLV file: %s", inpath, message) return False except EndOfFile: log.error("Unexpected end of file on file `%s'", inpath) return False except StopIteration: pass if not flv.first_media_tag_offset: log.error("The file `%s' does not have any media content", inpath) return False if not last_tag: log.error("The file `%s' does not have any content with a " "non-zero timestamp", inpath) return False metadata = flv.metadata or {} if flv.metadata_tag_start: original_metadata_size = flv.metadata_tag_end - flv.metadata_tag_start else: log.debug("The file `%s' has no metadata", inpath) original_metadata_size = 0 keyframes = flv.keyframes if flv.no_video: log.info("The file `%s' has no video, using audio seekpoints info", inpath) keyframes = flv.audio_seekpoints duration = metadata.get('duration') if not duration: # A duration of 0 is nonsensical, yet some tools put it like that. In # that case (or when there is no such field) update the duration value. duration = last_tag.timestamp / 1000.0 metadata['duration'] = duration metadata['keyframes'] = keyframes metadata['metadatacreator'] = 'flvlib %s' % __versionstr__ # we're going to write new metadata, so we need to shift the # filepositions by the amount of bytes that we're going to add to # the metadata tag test_payload, difference = filepositions_difference(metadata, original_metadata_size) if difference: new_filepositions = [pos + difference for pos in keyframes.filepositions] metadata['keyframes'].filepositions = new_filepositions payload = create_script_tag('onMetaData', metadata) else: log.debug("The file `%s' metadata size did not change.", inpath) payload = test_payload if outpath: try: fo = open(outpath, 'wb') except IOError as e2: (errno, strerror) = e2.args log.error("Failed to open `%s': %s", outpath, strerror) return False else: try: fd, temppath = tempfile.mkstemp() # preserve the permission bits shutil.copymode(inpath, temppath) fo = os.fdopen(fd, 'wb') except EnvironmentError as e3: (errno, strerror) = e3.args log.error("Failed to create temporary file: %s", strerror) return False log.debug("Creating the output file") try: fo.write(create_flv_header(has_audio=flv.has_audio, has_video=flv.has_video)) fo.write(payload) f.seek(flv.first_media_tag_offset) shutil.copyfileobj(f, fo) except IOError as e6: (errno, strerror) = e6.args log.error("Failed to create the indexed file: %s", strerror) if not outpath: # remove the temporary file force_remove(temppath) return False f.close() fo.close() if not outpath: # If we were not writing directly to the output file # we need to overwrite the original try: shutil.move(temppath, inpath) except EnvironmentError as e4: (errno, strerror) = e4.args log.error("Failed to overwrite the original file " "with the indexed version: %s", strerror) return False return True